aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-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,