From bd00c4311c0ea7cd6c731e799c9b1f09cf323bac Mon Sep 17 00:00:00 2001 From: Kyeyoon Park Date: Fri, 31 Oct 2014 23:33:41 -0700 Subject: [PATCH] AP: Add Neighbor Discovery snooping mechanism for Proxy ARP This commit establishes the infrastructure, and handles the Neighbor Solicitation and Neighbor Advertisement frames. This will be extended in the future to handle other frames. Signed-off-by: Kyeyoon Park --- hostapd/Makefile | 1 + src/ap/hostapd.c | 8 +++ src/ap/hostapd.h | 1 + src/ap/ieee802_11.c | 1 + src/ap/ndisc_snoop.c | 156 +++++++++++++++++++++++++++++++++++++++++++ src/ap/ndisc_snoop.h | 36 ++++++++++ src/ap/sta_info.c | 10 +++ src/ap/sta_info.h | 4 ++ 8 files changed, 217 insertions(+) create mode 100644 src/ap/ndisc_snoop.c create mode 100644 src/ap/ndisc_snoop.h diff --git a/hostapd/Makefile b/hostapd/Makefile index 317271c8b..aeea289a0 100644 --- a/hostapd/Makefile +++ b/hostapd/Makefile @@ -855,6 +855,7 @@ ifdef CONFIG_PROXYARP CFLAGS += -DCONFIG_PROXYARP OBJS += ../src/ap/x_snoop.o OBJS += ../src/ap/dhcp_snoop.o +OBJS += ../src/ap/ndisc_snoop.o endif OBJS += ../src/drivers/driver_common.o diff --git a/src/ap/hostapd.c b/src/ap/hostapd.c index 2ed16046e..73e939c85 100644 --- a/src/ap/hostapd.c +++ b/src/ap/hostapd.c @@ -38,6 +38,7 @@ #include "bss_load.h" #include "x_snoop.h" #include "dhcp_snoop.h" +#include "ndisc_snoop.h" static int hostapd_flush_old_stations(struct hostapd_data *hapd, u16 reason); @@ -314,6 +315,7 @@ static void hostapd_free_hapd_data(struct hostapd_data *hapd) #endif /* CONFIG_INTERWORKING */ bss_load_update_deinit(hapd); + ndisc_snoop_deinit(hapd); dhcp_snoop_deinit(hapd); x_snoop_deinit(hapd); @@ -907,6 +909,12 @@ static int hostapd_setup_bss(struct hostapd_data *hapd, int first) "DHCP snooping initialization failed"); return -1; } + + if (ndisc_snoop_init(hapd)) { + wpa_printf(MSG_ERROR, + "Neighbor Discovery snooping initialization failed"); + return -1; + } } if (!hostapd_drv_none(hapd) && vlan_init(hapd)) { diff --git a/src/ap/hostapd.h b/src/ap/hostapd.h index 7ee3c8771..ba169f499 100644 --- a/src/ap/hostapd.h +++ b/src/ap/hostapd.h @@ -243,6 +243,7 @@ struct hostapd_data { #endif /* CONFIG_INTERWORKING */ #ifdef CONFIG_PROXYARP struct l2_packet_data *sock_dhcp; + struct l2_packet_data *sock_ndisc; #endif /* CONFIG_PROXYARP */ #ifdef CONFIG_MESH int num_plinks; diff --git a/src/ap/ieee802_11.c b/src/ap/ieee802_11.c index 1982942dc..cca398412 100644 --- a/src/ap/ieee802_11.c +++ b/src/ap/ieee802_11.c @@ -1718,6 +1718,7 @@ static void handle_disassoc(struct hostapd_data *hapd, ieee802_1x_free_station(sta); if (sta->ipaddr) hostapd_drv_br_delete_ip_neigh(hapd, 4, (u8 *) &sta->ipaddr); + ap_sta_ip6addr_del(hapd, sta); hostapd_drv_sta_remove(hapd, sta->addr); if (sta->timeout_next == STA_NULLFUNC || diff --git a/src/ap/ndisc_snoop.c b/src/ap/ndisc_snoop.c new file mode 100644 index 000000000..81b9d8426 --- /dev/null +++ b/src/ap/ndisc_snoop.c @@ -0,0 +1,156 @@ +/* + * Neighbor Discovery snooping for Proxy ARP + * Copyright (c) 2014, Qualcomm Atheros, Inc. + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "utils/includes.h" +#include +#include + +#include "utils/common.h" +#include "l2_packet/l2_packet.h" +#include "hostapd.h" +#include "sta_info.h" +#include "ap_drv_ops.h" +#include "list.h" +#include "x_snoop.h" + +struct ip6addr { + struct in6_addr addr; + struct dl_list list; +}; + +struct icmpv6_ndmsg { + struct ipv6hdr ipv6h; + struct icmp6hdr icmp6h; + struct in6_addr target_addr; + u8 opt_type; + u8 len; + u8 opt_lladdr[0]; +}; + +#define NEIGHBOR_SOLICITATION 135 +#define NEIGHBOR_ADVERTISEMENT 136 +#define SOURCE_LL_ADDR 1 + +static int sta_ip6addr_add(struct sta_info *sta, struct in6_addr *addr) +{ + struct ip6addr *ip6addr; + + ip6addr = os_zalloc(sizeof(*ip6addr)); + if (!ip6addr) + return -1; + + os_memcpy(&ip6addr->addr, addr, sizeof(*addr)); + + dl_list_add_tail(&sta->ip6addr, &ip6addr->list); + + return 0; +} + + +void sta_ip6addr_del(struct hostapd_data *hapd, struct sta_info *sta) +{ + struct ip6addr *ip6addr, *prev; + + dl_list_for_each_safe(ip6addr, prev, &sta->ip6addr, struct ip6addr, + list) { + hostapd_drv_br_delete_ip_neigh(hapd, 6, (u8 *) &ip6addr->addr); + os_free(ip6addr); + } +} + + +static int sta_has_ip6addr(struct sta_info *sta, struct in6_addr *addr) +{ + struct ip6addr *ip6addr; + + dl_list_for_each(ip6addr, &sta->ip6addr, struct ip6addr, list) { + if (ip6addr->addr.s6_addr32[0] == addr->s6_addr32[0] && + ip6addr->addr.s6_addr32[1] == addr->s6_addr32[1] && + ip6addr->addr.s6_addr32[2] == addr->s6_addr32[2] && + ip6addr->addr.s6_addr32[3] == addr->s6_addr32[3]) + return 1; + } + + return 0; +} + + +static void handle_ndisc(void *ctx, const u8 *src_addr, const u8 *buf, + size_t len) +{ + struct hostapd_data *hapd = ctx; + struct icmpv6_ndmsg *msg; + struct in6_addr *saddr; + struct sta_info *sta; + int res; + + if (len < ETH_HLEN + sizeof(*msg)) + return; + msg = (struct icmpv6_ndmsg *) &buf[ETH_HLEN]; + switch (msg->icmp6h.icmp6_type) { + case NEIGHBOR_SOLICITATION: + if (msg->opt_type != SOURCE_LL_ADDR) + return; + + saddr = &msg->ipv6h.saddr; + if (!(saddr->s6_addr32[0] == 0 && saddr->s6_addr32[1] == 0 && + saddr->s6_addr32[2] == 0 && saddr->s6_addr32[3] == 0)) { + if (len < ETH_HLEN + sizeof(*msg) + ETH_ALEN) + return; + sta = ap_get_sta(hapd, msg->opt_lladdr); + if (!sta) + return; + + if (sta_has_ip6addr(sta, saddr)) + return; + + hostapd_drv_br_delete_ip_neigh(hapd, 6, (u8 *) saddr); + res = hostapd_drv_br_add_ip_neigh(hapd, 6, (u8 *) saddr, + 128, sta->addr); + if (res) { + wpa_printf(MSG_ERROR, + "ndisc_snoop: Adding ip neigh failed: %d", + res); + return; + } + + if (sta_ip6addr_add(sta, saddr)) + return; + } + break; + case NEIGHBOR_ADVERTISEMENT: + for (sta = hapd->sta_list; sta; sta = sta->next) { + x_snoop_mcast_to_ucast_convert_send(hapd, sta, + (u8 *) buf, len); + } + break; + default: + break; + } +} + + +int ndisc_snoop_init(struct hostapd_data *hapd) +{ + hapd->sock_ndisc = x_snoop_get_l2_packet(hapd, handle_ndisc, + L2_PACKET_FILTER_NDISC); + if (hapd->sock_ndisc == NULL) { + wpa_printf(MSG_DEBUG, + "ndisc_snoop: Failed to initialize L2 packet processing for NDISC packets: %s", + strerror(errno)); + return -1; + } + + return 0; +} + + +void ndisc_snoop_deinit(struct hostapd_data *hapd) +{ + l2_packet_deinit(hapd->sock_ndisc); +} diff --git a/src/ap/ndisc_snoop.h b/src/ap/ndisc_snoop.h new file mode 100644 index 000000000..921775653 --- /dev/null +++ b/src/ap/ndisc_snoop.h @@ -0,0 +1,36 @@ +/* + * Neighbor Discovery snooping for Proxy ARP + * Copyright (c) 2014, Qualcomm Atheros, Inc. + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef NDISC_SNOOP_H +#define NDISC_SNOOP_H + +#ifdef CONFIG_PROXYARP + +int ndisc_snoop_init(struct hostapd_data *hapd); +void ndisc_snoop_deinit(struct hostapd_data *hapd); +void sta_ip6addr_del(struct hostapd_data *hapd, struct sta_info *sta); + +#else /* CONFIG_PROXYARP */ + +static inline int ndisc_snoop_init(struct hostapd_data *hapd) +{ + return 0; +} + +static inline void ndisc_snoop_deinit(struct hostapd_data *hapd) +{ +} + +static inline void sta_ip6addr_del(struct hostapd_data *hapd, + struct sta_info *sta) +{ +} + +#endif /* CONFIG_PROXYARP */ + +#endif /* NDISC_SNOOP_H */ diff --git a/src/ap/sta_info.c b/src/ap/sta_info.c index 19ebe9ccf..059eda9c2 100644 --- a/src/ap/sta_info.c +++ b/src/ap/sta_info.c @@ -31,6 +31,7 @@ #include "ap_drv_ops.h" #include "gas_serv.h" #include "wnm_ap.h" +#include "ndisc_snoop.h" #include "sta_info.h" static void ap_sta_remove_in_other_bss(struct hostapd_data *hapd, @@ -144,6 +145,12 @@ static void ap_sta_hash_del(struct hostapd_data *hapd, struct sta_info *sta) } +void ap_sta_ip6addr_del(struct hostapd_data *hapd, struct sta_info *sta) +{ + sta_ip6addr_del(hapd, sta); +} + + void ap_free_sta(struct hostapd_data *hapd, struct sta_info *sta) { int set_beacon = 0; @@ -158,6 +165,7 @@ void ap_free_sta(struct hostapd_data *hapd, struct sta_info *sta) if (sta->ipaddr) hostapd_drv_br_delete_ip_neigh(hapd, 4, (u8 *) &sta->ipaddr); + ap_sta_ip6addr_del(hapd, sta); if (!hapd->iface->driver_ap_teardown && !(sta->flags & WLAN_STA_PREAUTH)) @@ -605,6 +613,7 @@ struct sta_info * ap_sta_add(struct hostapd_data *hapd, const u8 *addr) sta->ssid = &hapd->conf->ssid; ap_sta_remove_in_other_bss(hapd, sta); sta->last_seq_ctrl = WLAN_INVALID_MGMT_SEQ; + dl_list_init(&sta->ip6addr); return sta; } @@ -616,6 +625,7 @@ static int ap_sta_remove(struct hostapd_data *hapd, struct sta_info *sta) if (sta->ipaddr) hostapd_drv_br_delete_ip_neigh(hapd, 4, (u8 *) &sta->ipaddr); + ap_sta_ip6addr_del(hapd, sta); wpa_printf(MSG_DEBUG, "Removing STA " MACSTR " from kernel driver", MAC2STR(sta->addr)); diff --git a/src/ap/sta_info.h b/src/ap/sta_info.h index ac7269f6a..588a9e2f2 100644 --- a/src/ap/sta_info.h +++ b/src/ap/sta_info.h @@ -14,6 +14,8 @@ #include "common/defs.h" #endif /* CONFIG_MESH */ +#include "list.h" + /* STA flags */ #define WLAN_STA_AUTH BIT(0) #define WLAN_STA_ASSOC BIT(1) @@ -47,6 +49,7 @@ struct sta_info { struct sta_info *hnext; /* next entry in hash table list */ u8 addr[6]; be32 ipaddr; + struct dl_list ip6addr; /* list head for struct ip6addr */ u16 aid; /* STA's unique AID (1 .. 2007) or 0 if not yet assigned */ u32 flags; /* Bitfield of WLAN_STA_* */ u16 capability; @@ -193,6 +196,7 @@ struct sta_info * ap_get_sta(struct hostapd_data *hapd, const u8 *sta); struct sta_info * ap_get_sta_p2p(struct hostapd_data *hapd, const u8 *addr); void ap_sta_hash_add(struct hostapd_data *hapd, struct sta_info *sta); void ap_free_sta(struct hostapd_data *hapd, struct sta_info *sta); +void ap_sta_ip6addr_del(struct hostapd_data *hapd, struct sta_info *sta); void hostapd_free_stas(struct hostapd_data *hapd); void ap_handle_timer(void *eloop_ctx, void *timeout_ctx); void ap_sta_replenish_timeout(struct hostapd_data *hapd, struct sta_info *sta,