diff --git a/hostapd/hostapd.conf b/hostapd/hostapd.conf index 2521b7440..e7b5eab3e 100644 --- a/hostapd/hostapd.conf +++ b/hostapd/hostapd.conf @@ -5,9 +5,17 @@ # management frames); ath0 for madwifi interface=wlan0 -# In case of madwifi and nl80211 driver interfaces, an additional configuration -# parameter, bridge, must be used to notify hostapd if the interface is -# included in a bridge. This parameter is not used with Host AP driver. +# In case of madwifi, atheros, and nl80211 driver interfaces, an additional +# configuration parameter, bridge, may be used to notify hostapd if the +# interface is included in a bridge. This parameter is not used with Host AP +# driver. If the bridge parameter is not set, the drivers will automatically +# figure out the bridge interface (assuming sysfs is enabled and mounted to +# /sys) and this parameter may not be needed. +# +# For nl80211, this parameter can be used to request the AP interface to be +# added to the bridge automatically (brctl may refuse to do this before hostapd +# has been started to change the interface mode). If needed, the bridge +# interface is also created. #bridge=br0 # Driver interface type (hostap/wired/madwifi/test/none/nl80211/bsd); diff --git a/src/drivers/driver_atheros.c b/src/drivers/driver_atheros.c index 4428731bc..f24a40f59 100644 --- a/src/drivers/driver_atheros.c +++ b/src/drivers/driver_atheros.c @@ -1049,6 +1049,7 @@ madwifi_init(struct hostapd_data *hapd, struct wpa_init_params *params) struct madwifi_driver_data *drv; struct ifreq ifr; struct iwreq iwr; + char brname[IFNAMSIZ]; drv = os_zalloc(sizeof(struct madwifi_driver_data)); if (drv == NULL) { @@ -1086,6 +1087,13 @@ madwifi_init(struct hostapd_data *hapd, struct wpa_init_params *params) 1); if (drv->sock_recv == NULL) goto bad; + } else if (linux_br_get(brname, drv->iface) == 0) { + wpa_printf(MSG_DEBUG, "Interface in bridge %s; configure for " + "EAPOL receive", brname); + drv->sock_recv = l2_packet_init(brname, NULL, ETH_P_EAPOL, + handle_read, drv, 1); + if (drv->sock_recv == NULL) + goto bad; } else drv->sock_recv = drv->sock_xmit; diff --git a/src/drivers/driver_madwifi.c b/src/drivers/driver_madwifi.c index ad0b977f3..475334e1d 100644 --- a/src/drivers/driver_madwifi.c +++ b/src/drivers/driver_madwifi.c @@ -1119,6 +1119,7 @@ madwifi_init(struct hostapd_data *hapd, struct wpa_init_params *params) struct madwifi_driver_data *drv; struct ifreq ifr; struct iwreq iwr; + char brname[IFNAMSIZ]; drv = os_zalloc(sizeof(struct madwifi_driver_data)); if (drv == NULL) { @@ -1156,6 +1157,13 @@ madwifi_init(struct hostapd_data *hapd, struct wpa_init_params *params) 1); if (drv->sock_recv == NULL) goto bad; + } else if (linux_br_get(brname, drv->iface) == 0) { + wpa_printf(MSG_DEBUG, "Interface in bridge %s; configure for " + "EAPOL receive", brname); + drv->sock_recv = l2_packet_init(brname, NULL, ETH_P_EAPOL, + handle_read, drv, 1); + if (drv->sock_recv == NULL) + goto bad; } else drv->sock_recv = drv->sock_xmit; diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c index ec2dad6d3..d64b68080 100644 --- a/src/drivers/driver_nl80211.c +++ b/src/drivers/driver_nl80211.c @@ -68,6 +68,7 @@ struct wpa_driver_nl80211_data { struct netlink_data *netlink; int ioctl_sock; /* socket for ioctl() use */ char ifname[IFNAMSIZ + 1]; + char brname[IFNAMSIZ]; int ifindex; int if_removed; struct wpa_driver_capa capa; @@ -100,6 +101,8 @@ struct wpa_driver_nl80211_data { unsigned int beacon_set:1; unsigned int pending_remain_on_chan:1; + unsigned int added_bridge:1; + unsigned int added_if_into_bridge:1; u64 remain_on_chan_cookie; @@ -1271,6 +1274,20 @@ static void wpa_driver_nl80211_deinit(void *priv) { struct wpa_driver_nl80211_data *drv = priv; + if (drv->added_if_into_bridge) { + if (linux_br_del_if(drv->ioctl_sock, drv->brname, drv->ifname) + < 0) + wpa_printf(MSG_INFO, "nl80211: Failed to remove " + "interface %s from bridge %s: %s", + drv->ifname, drv->brname, strerror(errno)); + } + if (drv->added_bridge) { + if (linux_br_del(drv->ioctl_sock, drv->brname) < 0) + wpa_printf(MSG_INFO, "nl80211: Failed to remove " + "bridge %s: %s", + drv->brname, strerror(errno)); + } + nl80211_remove_monitor_interface(drv); if (drv->nlmode == NL80211_IFTYPE_AP) @@ -4246,11 +4263,66 @@ static int i802_sta_disassoc(void *priv, const u8 *own_addr, const u8 *addr, } +static int i802_check_bridge(struct wpa_driver_nl80211_data *drv, + const char *brname, const char *ifname) +{ + int ifindex; + char in_br[IFNAMSIZ]; + + os_strlcpy(drv->brname, brname, IFNAMSIZ); + ifindex = if_nametoindex(brname); + if (ifindex == 0) { + /* + * Bridge was configured, but the bridge device does + * not exist. Try to add it now. + */ + if (linux_br_add(drv->ioctl_sock, brname) < 0) { + wpa_printf(MSG_ERROR, "nl80211: Failed to add the " + "bridge interface %s: %s", + brname, strerror(errno)); + return -1; + } + drv->added_bridge = 1; + add_ifidx(drv, if_nametoindex(brname)); + } + + if (linux_br_get(in_br, ifname) == 0) { + if (os_strcmp(in_br, brname) == 0) + return 0; /* already in the bridge */ + + wpa_printf(MSG_DEBUG, "nl80211: Removing interface %s from " + "bridge %s", ifname, in_br); + if (linux_br_del_if(drv->ioctl_sock, in_br, ifname) < 0) { + wpa_printf(MSG_ERROR, "nl80211: Failed to " + "remove interface %s from bridge " + "%s: %s", + ifname, brname, strerror(errno)); + return -1; + } + } + + wpa_printf(MSG_DEBUG, "nl80211: Adding interface %s into bridge %s", + ifname, brname); + if (linux_br_add_if(drv->ioctl_sock, brname, ifname) < 0) { + wpa_printf(MSG_ERROR, "nl80211: Failed to add interface %s " + "into bridge %s: %s", + ifname, brname, strerror(errno)); + return -1; + } + drv->added_if_into_bridge = 1; + + return 0; +} + + static void *i802_init(struct hostapd_data *hapd, struct wpa_init_params *params) { struct wpa_driver_nl80211_data *drv; size_t i; + char brname[IFNAMSIZ]; + int ifindex, br_ifindex; + int br_added = 0; drv = wpa_driver_nl80211_init(hapd, params->ifname); if (drv == NULL) @@ -4258,12 +4330,29 @@ static void *i802_init(struct hostapd_data *hapd, drv->bss.ifindex = drv->ifindex; + if (linux_br_get(brname, params->ifname) == 0) { + wpa_printf(MSG_DEBUG, "nl80211: Interface %s is in bridge %s", + params->ifname, brname); + br_ifindex = if_nametoindex(brname); + } else { + brname[0] = '\0'; + br_ifindex = 0; + } + drv->num_if_indices = sizeof(drv->default_if_indices) / sizeof(int); drv->if_indices = drv->default_if_indices; for (i = 0; i < params->num_bridge; i++) { - if (params->bridge[i]) - add_ifidx(drv, if_nametoindex(params->bridge[i])); + if (params->bridge[i]) { + ifindex = if_nametoindex(params->bridge[i]); + if (ifindex) + add_ifidx(drv, ifindex); + if (ifindex == br_ifindex) + br_added = 1; + } } + if (!br_added && br_ifindex && + (params->num_bridge == 0 || !params->bridge[0])) + add_ifidx(drv, br_ifindex); /* start listening for EAPOL on the default AP interface */ add_ifidx(drv, drv->ifindex); @@ -4283,6 +4372,10 @@ static void *i802_init(struct hostapd_data *hapd, goto failed; } + if (params->num_bridge && params->bridge[0] && + i802_check_bridge(drv, params->bridge[0], params->ifname) < 0) + goto failed; + if (linux_set_iface_flags(drv->ioctl_sock, drv->ifname, 1)) goto failed; diff --git a/src/drivers/linux_ioctl.c b/src/drivers/linux_ioctl.c index 549ebb473..f8061670a 100644 --- a/src/drivers/linux_ioctl.c +++ b/src/drivers/linux_ioctl.c @@ -97,3 +97,101 @@ int linux_set_ifhwaddr(int sock, const char *ifname, const u8 *addr) return 0; } + + +#ifndef SIOCBRADDBR +#define SIOCBRADDBR 0x89a0 +#endif +#ifndef SIOCBRDELBR +#define SIOCBRDELBR 0x89a1 +#endif +#ifndef SIOCBRADDIF +#define SIOCBRADDIF 0x89a2 +#endif +#ifndef SIOCBRDELIF +#define SIOCBRDELIF 0x89a3 +#endif + + +int linux_br_add(int sock, const char *brname) +{ + if (ioctl(sock, SIOCBRADDBR, brname) < 0) { + wpa_printf(MSG_DEBUG, "Could not add bridge %s: %s", + brname, strerror(errno)); + return -1; + } + + return 0; +} + + +int linux_br_del(int sock, const char *brname) +{ + if (ioctl(sock, SIOCBRDELBR, brname) < 0) { + wpa_printf(MSG_DEBUG, "Could not remove bridge %s: %s", + brname, strerror(errno)); + return -1; + } + + return 0; +} + + +int linux_br_add_if(int sock, const char *brname, const char *ifname) +{ + struct ifreq ifr; + int ifindex; + + ifindex = if_nametoindex(ifname); + if (ifindex == 0) + return -1; + + os_memset(&ifr, 0, sizeof(ifr)); + os_strlcpy(ifr.ifr_name, brname, IFNAMSIZ); + ifr.ifr_ifindex = ifindex; + if (ioctl(sock, SIOCBRADDIF, &ifr) < 0) { + wpa_printf(MSG_DEBUG, "Could not add interface %s into bridge " + "%s: %s", ifname, brname, strerror(errno)); + return -1; + } + + return 0; +} + + +int linux_br_del_if(int sock, const char *brname, const char *ifname) +{ + struct ifreq ifr; + int ifindex; + + ifindex = if_nametoindex(ifname); + if (ifindex == 0) + return -1; + + os_memset(&ifr, 0, sizeof(ifr)); + os_strlcpy(ifr.ifr_name, brname, IFNAMSIZ); + ifr.ifr_ifindex = ifindex; + if (ioctl(sock, SIOCBRDELIF, &ifr) < 0) { + wpa_printf(MSG_DEBUG, "Could not remove interface %s from " + "bridge %s: %s", ifname, brname, strerror(errno)); + return -1; + } + + return 0; +} + + +int linux_br_get(char *brname, const char *ifname) +{ + char path[128], brlink[128], *pos; + os_snprintf(path, sizeof(path), "/sys/class/net/%s/brport/bridge", + ifname); + if (readlink(path, brlink, sizeof(brlink)) < 0) + return -1; + pos = os_strrchr(brlink, '/'); + if (pos == NULL) + return -1; + pos++; + os_strlcpy(brname, pos, IFNAMSIZ); + return 0; +} diff --git a/src/drivers/linux_ioctl.h b/src/drivers/linux_ioctl.h index 54142f563..a5557383d 100644 --- a/src/drivers/linux_ioctl.h +++ b/src/drivers/linux_ioctl.h @@ -18,5 +18,10 @@ int linux_set_iface_flags(int sock, const char *ifname, int dev_up); int linux_get_ifhwaddr(int sock, const char *ifname, u8 *addr); int linux_set_ifhwaddr(int sock, const char *ifname, const u8 *addr); +int linux_br_add(int sock, const char *brname); +int linux_br_del(int sock, const char *brname); +int linux_br_add_if(int sock, const char *brname, const char *ifname); +int linux_br_del_if(int sock, const char *brname, const char *ifname); +int linux_br_get(char *brname, const char *ifname); #endif /* LINUX_IOCTL_H */