]> git.ipfire.org Git - thirdparty/hostap.git/commitdiff
AP: Add Neighbor Discovery snooping mechanism for Proxy ARP
authorKyeyoon Park <kyeyoonp@qca.qualcomm.com>
Sat, 1 Nov 2014 06:33:41 +0000 (23:33 -0700)
committerJouni Malinen <j@w1.fi>
Wed, 19 Nov 2014 14:47:06 +0000 (16:47 +0200)
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>
hostapd/Makefile
src/ap/hostapd.c
src/ap/hostapd.h
src/ap/ieee802_11.c
src/ap/ndisc_snoop.c [new file with mode: 0644]
src/ap/ndisc_snoop.h [new file with mode: 0644]
src/ap/sta_info.c
src/ap/sta_info.h

index 317271c8bfeda3e3f31a40bb3a69971368b1bcdb..aeea289a0723b56d0ae2ec89f8777010653de64d 100644 (file)
@@ -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
index 2ed16046ef164fc76821061d0946d1f9235c199e..73e939c85ea366215c17399f8473d8be799defa2 100644 (file)
@@ -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)) {
index 7ee3c8771fe8eebecda7026fd287b8e7da677a74..ba169f499ac78d9c1f926af1907af4ec1c53360f 100644 (file)
@@ -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;
index 1982942dc83f113c035c49a6807993fb7fa6bd15..cca39841294624590c59d3d66c2106bf3739ed6e 100644 (file)
@@ -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 (file)
index 0000000..81b9d84
--- /dev/null
@@ -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 (file)
index 0000000..9217756
--- /dev/null
@@ -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 */
index 19ebe9ccfc9292d0130a119804cd61d8cf5bbfb3..059eda9c2e6a38fe2938ecf0e4dcd31b35503a85 100644 (file)
@@ -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));
index ac7269f6ab9ae9f98a237e2fd9798214fe49f154..588a9e2f295ba06a74910e02081d030951f17dc5 100644 (file)
@@ -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,