aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJouni Malinen <j@w1.fi>2010-01-16 13:19:58 (GMT)
committerJouni Malinen <j@w1.fi>2010-01-16 13:19:58 (GMT)
commit94627f6cc8be8693b6c6a7a6407fcf94ba0a7edb (patch)
treea17034718c8d9d941a1cf7c39270ec5f42dea1f1
parentd455d0806ed30cd471937d445f4d840017f25758 (diff)
downloadhostap-94627f6cc8be8693b6c6a7a6407fcf94ba0a7edb.zip
hostap-94627f6cc8be8693b6c6a7a6407fcf94ba0a7edb.tar.gz
hostap-94627f6cc8be8693b6c6a7a6407fcf94ba0a7edb.tar.bz2
hostapd: Detect bridge interface automatically
This makes the bridge parameter unnecessary for cases where the interface is already in a bridge and sysfs is mounted to /sys so that the detection code works. For nl80211, the bridge 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.
-rw-r--r--hostapd/hostapd.conf14
-rw-r--r--src/drivers/driver_atheros.c8
-rw-r--r--src/drivers/driver_madwifi.c8
-rw-r--r--src/drivers/driver_nl80211.c97
-rw-r--r--src/drivers/linux_ioctl.c98
-rw-r--r--src/drivers/linux_ioctl.h5
6 files changed, 225 insertions, 5 deletions
diff --git a/hostapd/hostapd.conf b/hostapd/hostapd.conf
index 2521b74..e7b5eab 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 4428731..f24a40f 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 ad0b977..475334e 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 ec2dad6..d64b680 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 549ebb4..f806167 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 54142f5..a555738 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 */