From: Benjamin Berg <benjamin.berg@xxxxxxxxxxxxx> In the case that two hostapd instances run on the same bridge the two instances may not be able to communicate with each other directly. This is because the packets from the other instance are marked as PACKET_OUTGOING and are filtered by the kernel. This patch adds a new initializer that binds twice to grab both incoming and outgoing packets. Signed-off-by: Benjamin Berg <benjamin.berg@xxxxxxxxxxxxx> --- src/ap/wpa_auth_glue.c | 2 +- src/l2_packet/l2_packet.h | 14 +++++++++ src/l2_packet/l2_packet_linux.c | 70 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 85 insertions(+), 1 deletion(-) diff --git a/src/ap/wpa_auth_glue.c b/src/ap/wpa_auth_glue.c index 4981c49..7f64b1a 100644 --- a/src/ap/wpa_auth_glue.c +++ b/src/ap/wpa_auth_glue.c @@ -669,7 +669,7 @@ int hostapd_setup_wpa(struct hostapd_data *hapd) #ifdef CONFIG_IEEE80211R if (!hostapd_drv_none(hapd) && wpa_key_mgmt_ft(hapd->conf->wpa_key_mgmt)) { - hapd->l2 = l2_packet_init(hapd->conf->bridge[0] ? + hapd->l2 = l2_packet_init_outgoing(hapd->conf->bridge[0] ? hapd->conf->bridge : hapd->conf->iface, NULL, ETH_P_RRB, hostapd_rrb_receive, hapd, 1); diff --git a/src/l2_packet/l2_packet.h b/src/l2_packet/l2_packet.h index 2a45245..f78bf8c 100644 --- a/src/l2_packet/l2_packet.h +++ b/src/l2_packet/l2_packet.h @@ -68,6 +68,20 @@ struct l2_packet_data * l2_packet_init( void *rx_callback_ctx, int l2_hdr); /** + * l2_packet_init_outgoing - Like l2_packet_init() but with outgoing packets included + * + * This version of l2_packet_init() can be used to also listen for outgoing + * packets (on a bridge interface). This is important if multiple hostapd + * instances run on the same interface and need to communicate with each other + * (i.e. for 802.11r). + */ +struct l2_packet_data * l2_packet_init_outgoing( + const char *ifname, const u8 *own_addr, unsigned short protocol, + void (*rx_callback)(void *ctx, const u8 *src_addr, + const u8 *buf, size_t len), + void *rx_callback_ctx, int l2_hdr); + +/** * l2_packet_init_bridge - Like l2_packet_init() but with bridge workaround * * This version of l2_packet_init() can be used to enable a workaround for Linux diff --git a/src/l2_packet/l2_packet_linux.c b/src/l2_packet/l2_packet_linux.c index a7a300e..41caa23 100644 --- a/src/l2_packet/l2_packet_linux.c +++ b/src/l2_packet/l2_packet_linux.c @@ -21,6 +21,7 @@ struct l2_packet_data { int fd; /* packet socket for EAPOL frames */ + int fd_outgoing; /* socket for hostapd instances on same bridge */ char ifname[IFNAMSIZ + 1]; int ifindex; u8 own_addr[ETH_ALEN]; @@ -259,6 +260,7 @@ struct l2_packet_data * l2_packet_init( l2->rx_callback = rx_callback; l2->rx_callback_ctx = rx_callback_ctx; l2->l2_hdr = l2_hdr; + l2->fd_outgoing = -1; #ifndef CONFIG_NO_LINUX_PACKET_SOCKET_WAR l2->fd_br_rx = -1; #endif /* CONFIG_NO_LINUX_PACKET_SOCKET_WAR */ @@ -309,6 +311,69 @@ struct l2_packet_data * l2_packet_init( } +struct l2_packet_data * l2_packet_init_outgoing( + const char *ifname, const u8 *own_addr, unsigned short protocol, + void (*rx_callback)(void *ctx, const u8 *src_addr, + const u8 *buf, size_t len), + void *rx_callback_ctx, int l2_hdr) +{ + struct ifreq ifr; + struct l2_packet_data *l2; + struct sockaddr_ll ll; + + l2 = l2_packet_init(ifname, own_addr, protocol, rx_callback, + rx_callback_ctx, l2_hdr); + if (!l2) + return NULL; + + l2->fd_outgoing = socket(PF_PACKET, l2_hdr ? SOCK_RAW : SOCK_DGRAM, + htons(protocol)); + if (l2->fd_outgoing < 0) { + wpa_printf(MSG_ERROR, "%s: socket(PF_PACKET): %s", + __func__, strerror(errno)); + l2_packet_deinit(l2); + return NULL; + } + os_memset(&ifr, 0, sizeof(ifr)); + os_strlcpy(ifr.ifr_name, l2->ifname, sizeof(ifr.ifr_name)); + if (ioctl(l2->fd_outgoing, SIOCGIFINDEX, &ifr) < 0) { + wpa_printf(MSG_ERROR, "%s: ioctl[SIOCGIFINDEX]: %s", + __func__, strerror(errno)); + close(l2->fd_outgoing); + l2->fd_outgoing = -1; + l2_packet_deinit(l2); + return NULL; + } + + os_memset(&ll, 0, sizeof(ll)); + ll.sll_family = PF_PACKET; + ll.sll_ifindex = ifr.ifr_ifindex; + ll.sll_protocol = htons(protocol); + ll.sll_pkttype = PACKET_OUTGOING; + if (bind(l2->fd_outgoing, (struct sockaddr *) &ll, sizeof(ll)) < 0) { + wpa_printf(MSG_ERROR, "%s: bind[PF_PACKET]: %s", + __func__, strerror(errno)); + close(l2->fd_outgoing); + l2->fd_outgoing = -1; + l2_packet_deinit(l2); + return NULL; + } + + if (ioctl(l2->fd_outgoing, SIOCGIFHWADDR, &ifr) < 0) { + wpa_printf(MSG_ERROR, "%s: ioctl[SIOCGIFHWADDR]: %s", + __func__, strerror(errno)); + close(l2->fd_outgoing); + l2->fd_outgoing = -1; + l2_packet_deinit(l2); + return NULL; + } + + eloop_register_read_sock(l2->fd_outgoing, l2_packet_receive, l2, NULL); + + return l2; +} + + struct l2_packet_data * l2_packet_init_bridge( const char *br_ifname, const char *ifname, const u8 *own_addr, unsigned short protocol, @@ -404,6 +469,11 @@ void l2_packet_deinit(struct l2_packet_data *l2) close(l2->fd); } + if (l2->fd_outgoing >= 0) { + eloop_unregister_read_sock(l2->fd_outgoing); + close(l2->fd_outgoing); + } + #ifndef CONFIG_NO_LINUX_PACKET_SOCKET_WAR if (l2->fd_br_rx >= 0) { eloop_unregister_read_sock(l2->fd_br_rx); -- 2.9.3 _______________________________________________ Hostap mailing list Hostap@xxxxxxxxxxxxxxxxxxx http://lists.infradead.org/mailman/listinfo/hostap