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>
This commit is contained in:
Kyeyoon Park 2014-10-31 23:33:41 -07:00 committed by Jouni Malinen
parent 5c58944d08
commit bd00c4311c
8 changed files with 217 additions and 0 deletions

View 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

View 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)) {

View 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;

View 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 ||

156
src/ap/ndisc_snoop.c Normal file
View File

@ -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);
}

36
src/ap/ndisc_snoop.h Normal file
View File

@ -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 */

View 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));

View 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,