aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorKyeyoon Park <kyeyoonp@qca.qualcomm.com>2014-11-01 06:33:41 (GMT)
committerJouni Malinen <j@w1.fi>2014-11-19 14:47:06 (GMT)
commitbd00c4311c0ea7cd6c731e799c9b1f09cf323bac (patch)
tree67817377fab04ecc7099eca93da94e3ace71304d
parent5c58944d08718e7a8aefddd0e18825c288865604 (diff)
downloadhostap-bd00c4311c0ea7cd6c731e799c9b1f09cf323bac.zip
hostap-bd00c4311c0ea7cd6c731e799c9b1f09cf323bac.tar.gz
hostap-bd00c4311c0ea7cd6c731e799c9b1f09cf323bac.tar.bz2
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 <kyeyoonp@qca.qualcomm.com>
-rw-r--r--hostapd/Makefile1
-rw-r--r--src/ap/hostapd.c8
-rw-r--r--src/ap/hostapd.h1
-rw-r--r--src/ap/ieee802_11.c1
-rw-r--r--src/ap/ndisc_snoop.c156
-rw-r--r--src/ap/ndisc_snoop.h36
-rw-r--r--src/ap/sta_info.c10
-rw-r--r--src/ap/sta_info.h4
8 files changed, 217 insertions, 0 deletions
diff --git a/hostapd/Makefile b/hostapd/Makefile
index 317271c..aeea289 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 2ed1604..73e939c 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 7ee3c87..ba169f4 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 1982942..cca3984 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 0000000..81b9d84
--- /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 <linux/ipv6.h>
+#include <linux/icmpv6.h>
+
+#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 0000000..9217756
--- /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 19ebe9c..059eda9 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 ac7269f..588a9e2 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,