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.
This commit is contained in:
Jouni Malinen 2010-01-16 15:19:58 +02:00
parent d455d0806e
commit 94627f6cc8
6 changed files with 225 additions and 5 deletions

View file

@ -5,9 +5,17 @@
# management frames); ath0 for madwifi # management frames); ath0 for madwifi
interface=wlan0 interface=wlan0
# In case of madwifi and nl80211 driver interfaces, an additional configuration # In case of madwifi, atheros, and nl80211 driver interfaces, an additional
# parameter, bridge, must be used to notify hostapd if the interface is # configuration parameter, bridge, may be used to notify hostapd if the
# included in a bridge. This parameter is not used with Host AP driver. # 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 #bridge=br0
# Driver interface type (hostap/wired/madwifi/test/none/nl80211/bsd); # Driver interface type (hostap/wired/madwifi/test/none/nl80211/bsd);

View file

@ -1049,6 +1049,7 @@ madwifi_init(struct hostapd_data *hapd, struct wpa_init_params *params)
struct madwifi_driver_data *drv; struct madwifi_driver_data *drv;
struct ifreq ifr; struct ifreq ifr;
struct iwreq iwr; struct iwreq iwr;
char brname[IFNAMSIZ];
drv = os_zalloc(sizeof(struct madwifi_driver_data)); drv = os_zalloc(sizeof(struct madwifi_driver_data));
if (drv == NULL) { if (drv == NULL) {
@ -1086,6 +1087,13 @@ madwifi_init(struct hostapd_data *hapd, struct wpa_init_params *params)
1); 1);
if (drv->sock_recv == NULL) if (drv->sock_recv == NULL)
goto bad; 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 } else
drv->sock_recv = drv->sock_xmit; drv->sock_recv = drv->sock_xmit;

View file

@ -1119,6 +1119,7 @@ madwifi_init(struct hostapd_data *hapd, struct wpa_init_params *params)
struct madwifi_driver_data *drv; struct madwifi_driver_data *drv;
struct ifreq ifr; struct ifreq ifr;
struct iwreq iwr; struct iwreq iwr;
char brname[IFNAMSIZ];
drv = os_zalloc(sizeof(struct madwifi_driver_data)); drv = os_zalloc(sizeof(struct madwifi_driver_data));
if (drv == NULL) { if (drv == NULL) {
@ -1156,6 +1157,13 @@ madwifi_init(struct hostapd_data *hapd, struct wpa_init_params *params)
1); 1);
if (drv->sock_recv == NULL) if (drv->sock_recv == NULL)
goto bad; 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 } else
drv->sock_recv = drv->sock_xmit; drv->sock_recv = drv->sock_xmit;

View file

@ -68,6 +68,7 @@ struct wpa_driver_nl80211_data {
struct netlink_data *netlink; struct netlink_data *netlink;
int ioctl_sock; /* socket for ioctl() use */ int ioctl_sock; /* socket for ioctl() use */
char ifname[IFNAMSIZ + 1]; char ifname[IFNAMSIZ + 1];
char brname[IFNAMSIZ];
int ifindex; int ifindex;
int if_removed; int if_removed;
struct wpa_driver_capa capa; struct wpa_driver_capa capa;
@ -100,6 +101,8 @@ struct wpa_driver_nl80211_data {
unsigned int beacon_set:1; unsigned int beacon_set:1;
unsigned int pending_remain_on_chan: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; u64 remain_on_chan_cookie;
@ -1271,6 +1274,20 @@ static void wpa_driver_nl80211_deinit(void *priv)
{ {
struct wpa_driver_nl80211_data *drv = 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); nl80211_remove_monitor_interface(drv);
if (drv->nlmode == NL80211_IFTYPE_AP) 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, static void *i802_init(struct hostapd_data *hapd,
struct wpa_init_params *params) struct wpa_init_params *params)
{ {
struct wpa_driver_nl80211_data *drv; struct wpa_driver_nl80211_data *drv;
size_t i; size_t i;
char brname[IFNAMSIZ];
int ifindex, br_ifindex;
int br_added = 0;
drv = wpa_driver_nl80211_init(hapd, params->ifname); drv = wpa_driver_nl80211_init(hapd, params->ifname);
if (drv == NULL) if (drv == NULL)
@ -4258,12 +4330,29 @@ static void *i802_init(struct hostapd_data *hapd,
drv->bss.ifindex = drv->ifindex; 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->num_if_indices = sizeof(drv->default_if_indices) / sizeof(int);
drv->if_indices = drv->default_if_indices; drv->if_indices = drv->default_if_indices;
for (i = 0; i < params->num_bridge; i++) { for (i = 0; i < params->num_bridge; i++) {
if (params->bridge[i]) if (params->bridge[i]) {
add_ifidx(drv, if_nametoindex(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 */ /* start listening for EAPOL on the default AP interface */
add_ifidx(drv, drv->ifindex); add_ifidx(drv, drv->ifindex);
@ -4283,6 +4372,10 @@ static void *i802_init(struct hostapd_data *hapd,
goto failed; 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)) if (linux_set_iface_flags(drv->ioctl_sock, drv->ifname, 1))
goto failed; goto failed;

View file

@ -97,3 +97,101 @@ int linux_set_ifhwaddr(int sock, const char *ifname, const u8 *addr)
return 0; 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;
}

View file

@ -18,5 +18,10 @@
int linux_set_iface_flags(int sock, const char *ifname, int dev_up); 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_get_ifhwaddr(int sock, const char *ifname, u8 *addr);
int linux_set_ifhwaddr(int sock, const char *ifname, const 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 */ #endif /* LINUX_IOCTL_H */