AP: Add support for Proxy ARP, DHCP snooping mechanism
Proxy ARP allows the AP devices to keep track of the hardware address to IP address mapping of the STA devices within the BSS. When a request for such information is made (i.e., ARP request, Neighbor Solicitation), the AP will respond on behalf of the STA device within the BSS. Such requests could originate from a device within the BSS or also from the bridge. In the process of the AP replying to the request (i.e., ARP reply, Neighbor Advertisement), the AP will drop the original request frame. The relevant STA will not even know that such information was ever requested. This feature is a requirement for Hotspot 2.0, and is defined in IEEE Std 802.11-2012, 10.23.13. While the Proxy ARP support code mainly resides in the kernel bridge code, in order to optimize the performance and simplify kernel implementation, the DHCP snooping code was added to the hostapd. Signed-off-by: Kyeyoon Park <kyeyoonp@qca.qualcomm.com>
This commit is contained in:
parent
6897556455
commit
7d597d4617
12 changed files with 265 additions and 0 deletions
|
@ -188,6 +188,11 @@ endif
|
||||||
|
|
||||||
ifdef CONFIG_HS20
|
ifdef CONFIG_HS20
|
||||||
NEED_AES_OMAC1=y
|
NEED_AES_OMAC1=y
|
||||||
|
CONFIG_PROXYARP=y
|
||||||
|
endif
|
||||||
|
|
||||||
|
ifdef CONFIG_PROXYARP
|
||||||
|
CONFIG_L2_PACKET=y
|
||||||
endif
|
endif
|
||||||
|
|
||||||
ifdef CONFIG_IEEE80211W
|
ifdef CONFIG_IEEE80211W
|
||||||
|
@ -840,6 +845,11 @@ OBJS += ../src/common/gas.o
|
||||||
OBJS += ../src/ap/gas_serv.o
|
OBJS += ../src/ap/gas_serv.o
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
ifdef CONFIG_PROXYARP
|
||||||
|
CFLAGS += -DCONFIG_PROXYARP
|
||||||
|
OBJS += ../src/ap/dhcp_snoop.o
|
||||||
|
endif
|
||||||
|
|
||||||
OBJS += ../src/drivers/driver_common.o
|
OBJS += ../src/drivers/driver_common.o
|
||||||
|
|
||||||
ifdef CONFIG_WPA_CLI_EDIT
|
ifdef CONFIG_WPA_CLI_EDIT
|
||||||
|
|
|
@ -3008,6 +3008,8 @@ static int hostapd_config_fill(struct hostapd_config *conf,
|
||||||
bss->hs20 = atoi(pos);
|
bss->hs20 = atoi(pos);
|
||||||
} else if (os_strcmp(buf, "disable_dgaf") == 0) {
|
} else if (os_strcmp(buf, "disable_dgaf") == 0) {
|
||||||
bss->disable_dgaf = atoi(pos);
|
bss->disable_dgaf = atoi(pos);
|
||||||
|
} else if (os_strcmp(buf, "proxy_arp") == 0) {
|
||||||
|
bss->proxy_arp = atoi(pos);
|
||||||
} else if (os_strcmp(buf, "osen") == 0) {
|
} else if (os_strcmp(buf, "osen") == 0) {
|
||||||
bss->osen = atoi(pos);
|
bss->osen = atoi(pos);
|
||||||
} else if (os_strcmp(buf, "anqp_domain_id") == 0) {
|
} else if (os_strcmp(buf, "anqp_domain_id") == 0) {
|
||||||
|
|
|
@ -1453,6 +1453,11 @@ own_ip_addr=127.0.0.1
|
||||||
# 1 = enabled
|
# 1 = enabled
|
||||||
#bss_transition=1
|
#bss_transition=1
|
||||||
|
|
||||||
|
# Proxy ARP
|
||||||
|
# 0 = disabled (default)
|
||||||
|
# 1 = enabled
|
||||||
|
#proxy_arp=1
|
||||||
|
|
||||||
##### IEEE 802.11u-2011 #######################################################
|
##### IEEE 802.11u-2011 #######################################################
|
||||||
|
|
||||||
# Enable Interworking service
|
# Enable Interworking service
|
||||||
|
|
|
@ -484,6 +484,7 @@ struct hostapd_bss_config {
|
||||||
unsigned int qos_map_set_len;
|
unsigned int qos_map_set_len;
|
||||||
|
|
||||||
int osen;
|
int osen;
|
||||||
|
int proxy_arp;
|
||||||
#ifdef CONFIG_HS20
|
#ifdef CONFIG_HS20
|
||||||
int hs20;
|
int hs20;
|
||||||
int disable_dgaf;
|
int disable_dgaf;
|
||||||
|
|
196
src/ap/dhcp_snoop.c
Normal file
196
src/ap/dhcp_snoop.c
Normal file
|
@ -0,0 +1,196 @@
|
||||||
|
/*
|
||||||
|
* DHCP 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/filter.h>
|
||||||
|
#include <linux/ip.h>
|
||||||
|
#include <linux/udp.h>
|
||||||
|
|
||||||
|
#include "utils/common.h"
|
||||||
|
#include "l2_packet/l2_packet.h"
|
||||||
|
#include "hostapd.h"
|
||||||
|
#include "sta_info.h"
|
||||||
|
#include "ap_drv_ops.h"
|
||||||
|
#include "dhcp_snoop.h"
|
||||||
|
|
||||||
|
struct bootp_pkt {
|
||||||
|
struct iphdr iph;
|
||||||
|
struct udphdr udph;
|
||||||
|
u8 op;
|
||||||
|
u8 htype;
|
||||||
|
u8 hlen;
|
||||||
|
u8 hops;
|
||||||
|
be32 xid;
|
||||||
|
be16 secs;
|
||||||
|
be16 flags;
|
||||||
|
be32 client_ip;
|
||||||
|
be32 your_ip;
|
||||||
|
be32 server_ip;
|
||||||
|
be32 relay_ip;
|
||||||
|
u8 hw_addr[16];
|
||||||
|
u8 serv_name[64];
|
||||||
|
u8 boot_file[128];
|
||||||
|
u8 exten[312];
|
||||||
|
};
|
||||||
|
|
||||||
|
#define DHCPACK 5
|
||||||
|
static const u8 ic_bootp_cookie[] = { 99, 130, 83, 99 };
|
||||||
|
|
||||||
|
|
||||||
|
static void handle_dhcp(void *ctx, const u8 *src_addr, const u8 *buf,
|
||||||
|
size_t len)
|
||||||
|
{
|
||||||
|
struct hostapd_data *hapd = ctx;
|
||||||
|
const struct bootp_pkt *b;
|
||||||
|
struct sta_info *sta;
|
||||||
|
int exten_len;
|
||||||
|
const u8 *end, *pos;
|
||||||
|
int res, msgtype = 0, prefixlen = 32;
|
||||||
|
u32 subnet_mask = 0;
|
||||||
|
|
||||||
|
exten_len = len - ETH_HLEN - (sizeof(*b) - sizeof(b->exten));
|
||||||
|
if (exten_len < 4)
|
||||||
|
return;
|
||||||
|
|
||||||
|
b = (const struct bootp_pkt *) &buf[ETH_HLEN];
|
||||||
|
if (os_memcmp(b->exten, ic_bootp_cookie, ARRAY_SIZE(ic_bootp_cookie)))
|
||||||
|
return;
|
||||||
|
|
||||||
|
/* Parse DHCP options */
|
||||||
|
end = (const u8 *) b + ntohs(b->iph.tot_len);
|
||||||
|
pos = &b->exten[4];
|
||||||
|
while (pos < end && *pos != 0xff) {
|
||||||
|
const u8 *opt = pos++;
|
||||||
|
|
||||||
|
if (*opt == 0) /* padding */
|
||||||
|
continue;
|
||||||
|
|
||||||
|
pos += *pos + 1;
|
||||||
|
if (pos >= end)
|
||||||
|
break;
|
||||||
|
|
||||||
|
switch (*opt) {
|
||||||
|
case 1: /* subnet mask */
|
||||||
|
if (opt[1] == 4)
|
||||||
|
subnet_mask = WPA_GET_BE32(&opt[2]);
|
||||||
|
if (subnet_mask == 0)
|
||||||
|
return;
|
||||||
|
while (!(subnet_mask & 0x1)) {
|
||||||
|
subnet_mask >>= 1;
|
||||||
|
prefixlen--;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 53: /* message type */
|
||||||
|
if (opt[1])
|
||||||
|
msgtype = opt[2];
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (msgtype == DHCPACK) {
|
||||||
|
if (b->your_ip == 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
/* DHCPACK for DHCPREQUEST */
|
||||||
|
sta = ap_get_sta(hapd, b->hw_addr);
|
||||||
|
if (!sta)
|
||||||
|
return;
|
||||||
|
|
||||||
|
wpa_printf(MSG_DEBUG, "dhcp_snoop: Found DHCPACK for " MACSTR
|
||||||
|
" @ IPv4 address %X/%d",
|
||||||
|
MAC2STR(sta->addr), ntohl(b->your_ip), prefixlen);
|
||||||
|
|
||||||
|
if (sta->ipaddr == b->your_ip)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (sta->ipaddr != 0) {
|
||||||
|
wpa_printf(MSG_DEBUG,
|
||||||
|
"dhcp_snoop: Removing IPv4 address %X from the ip neigh table",
|
||||||
|
sta->ipaddr);
|
||||||
|
hostapd_drv_br_delete_ip_neigh(hapd, sta->ipaddr);
|
||||||
|
}
|
||||||
|
|
||||||
|
res = hostapd_drv_br_add_ip_neigh(hapd, b->your_ip, prefixlen,
|
||||||
|
sta->addr);
|
||||||
|
if (res) {
|
||||||
|
wpa_printf(MSG_DEBUG,
|
||||||
|
"dhcp_snoop: Adding ip neigh table failed: %d",
|
||||||
|
res);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
sta->ipaddr = b->your_ip;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int dhcp_snoop_init(struct hostapd_data *hapd)
|
||||||
|
{
|
||||||
|
struct hostapd_bss_config *conf = hapd->conf;
|
||||||
|
|
||||||
|
if (!conf->isolate) {
|
||||||
|
wpa_printf(MSG_DEBUG,
|
||||||
|
"dhcp_snoop: ap_isolate must be enabled for DHCP snooping");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (conf->bridge[0] == '\0') {
|
||||||
|
wpa_printf(MSG_DEBUG,
|
||||||
|
"dhcp_snoop: Bridge must be configured for DHCP snooping");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
hapd->sock_dhcp = l2_packet_init(conf->bridge, NULL, ETH_P_ALL,
|
||||||
|
handle_dhcp, hapd, 1);
|
||||||
|
if (hapd->sock_dhcp == NULL) {
|
||||||
|
wpa_printf(MSG_DEBUG,
|
||||||
|
"dhcp_snoop: Failed to initialize L2 packet processing for DHCP packet: %s",
|
||||||
|
strerror(errno));
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (l2_packet_set_packet_filter(hapd->sock_dhcp,
|
||||||
|
L2_PACKET_FILTER_DHCP)) {
|
||||||
|
wpa_printf(MSG_DEBUG,
|
||||||
|
"dhcp_snoop: Failed to set L2 packet filter for DHCP: %s",
|
||||||
|
strerror(errno));
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hostapd_drv_br_port_set_attr(hapd, DRV_BR_PORT_ATTR_HAIRPIN_MODE,
|
||||||
|
1)) {
|
||||||
|
wpa_printf(MSG_DEBUG,
|
||||||
|
"dhcp_snoop: Failed to enable hairpin_mode on the bridge port");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hostapd_drv_br_port_set_attr(hapd, DRV_BR_PORT_ATTR_PROXYARP, 1)) {
|
||||||
|
wpa_printf(MSG_DEBUG,
|
||||||
|
"dhcp_snoop: Failed to enable proxyarp on the bridge port");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hostapd_drv_br_set_net_param(hapd, DRV_BR_NET_PARAM_GARP_ACCEPT,
|
||||||
|
1)) {
|
||||||
|
wpa_printf(MSG_DEBUG,
|
||||||
|
"dhcp_snoop: Failed to enable accepting gratuitous ARP on the bridge");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void dhcp_snoop_deinit(struct hostapd_data *hapd)
|
||||||
|
{
|
||||||
|
hostapd_drv_br_set_net_param(hapd, DRV_BR_NET_PARAM_GARP_ACCEPT, 0);
|
||||||
|
hostapd_drv_br_port_set_attr(hapd, DRV_BR_PORT_ATTR_PROXYARP, 0);
|
||||||
|
hostapd_drv_br_port_set_attr(hapd, DRV_BR_PORT_ATTR_HAIRPIN_MODE, 0);
|
||||||
|
l2_packet_deinit(hapd->sock_dhcp);
|
||||||
|
}
|
30
src/ap/dhcp_snoop.h
Normal file
30
src/ap/dhcp_snoop.h
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
/*
|
||||||
|
* DHCP 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 DHCP_SNOOP_H
|
||||||
|
#define DHCP_SNOOP_H
|
||||||
|
|
||||||
|
#ifdef CONFIG_PROXYARP
|
||||||
|
|
||||||
|
int dhcp_snoop_init(struct hostapd_data *hapd);
|
||||||
|
void dhcp_snoop_deinit(struct hostapd_data *hapd);
|
||||||
|
|
||||||
|
#else /* CONFIG_PROXYARP */
|
||||||
|
|
||||||
|
static inline int dhcp_snoop_init(struct hostapd_data *hapd)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void dhcp_snoop_deinit(struct hostapd_data *hapd)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif /* CONFIG_PROXYARP */
|
||||||
|
|
||||||
|
#endif /* DHCP_SNOOP_H */
|
|
@ -36,6 +36,7 @@
|
||||||
#include "dfs.h"
|
#include "dfs.h"
|
||||||
#include "ieee802_11.h"
|
#include "ieee802_11.h"
|
||||||
#include "bss_load.h"
|
#include "bss_load.h"
|
||||||
|
#include "dhcp_snoop.h"
|
||||||
|
|
||||||
|
|
||||||
static int hostapd_flush_old_stations(struct hostapd_data *hapd, u16 reason);
|
static int hostapd_flush_old_stations(struct hostapd_data *hapd, u16 reason);
|
||||||
|
@ -312,6 +313,7 @@ static void hostapd_free_hapd_data(struct hostapd_data *hapd)
|
||||||
#endif /* CONFIG_INTERWORKING */
|
#endif /* CONFIG_INTERWORKING */
|
||||||
|
|
||||||
bss_load_update_deinit(hapd);
|
bss_load_update_deinit(hapd);
|
||||||
|
dhcp_snoop_deinit(hapd);
|
||||||
|
|
||||||
#ifdef CONFIG_SQLITE
|
#ifdef CONFIG_SQLITE
|
||||||
bin_clear_free(hapd->tmp_eap_user.identity,
|
bin_clear_free(hapd->tmp_eap_user.identity,
|
||||||
|
@ -891,6 +893,11 @@ static int hostapd_setup_bss(struct hostapd_data *hapd, int first)
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (conf->proxy_arp && dhcp_snoop_init(hapd)) {
|
||||||
|
wpa_printf(MSG_ERROR, "DHCP snooping initialization failed");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
if (!hostapd_drv_none(hapd) && vlan_init(hapd)) {
|
if (!hostapd_drv_none(hapd) && vlan_init(hapd)) {
|
||||||
wpa_printf(MSG_ERROR, "VLAN initialization failed.");
|
wpa_printf(MSG_ERROR, "VLAN initialization failed.");
|
||||||
return -1;
|
return -1;
|
||||||
|
|
|
@ -241,6 +241,9 @@ struct hostapd_data {
|
||||||
#ifdef CONFIG_INTERWORKING
|
#ifdef CONFIG_INTERWORKING
|
||||||
size_t gas_frag_limit;
|
size_t gas_frag_limit;
|
||||||
#endif /* CONFIG_INTERWORKING */
|
#endif /* CONFIG_INTERWORKING */
|
||||||
|
#ifdef CONFIG_PROXYARP
|
||||||
|
struct l2_packet_data *sock_dhcp;
|
||||||
|
#endif /* CONFIG_PROXYARP */
|
||||||
#ifdef CONFIG_MESH
|
#ifdef CONFIG_MESH
|
||||||
int num_plinks;
|
int num_plinks;
|
||||||
int max_plinks;
|
int max_plinks;
|
||||||
|
|
|
@ -1502,6 +1502,8 @@ static void handle_disassoc(struct hostapd_data *hapd,
|
||||||
* authenticated. */
|
* authenticated. */
|
||||||
accounting_sta_stop(hapd, sta);
|
accounting_sta_stop(hapd, sta);
|
||||||
ieee802_1x_free_station(sta);
|
ieee802_1x_free_station(sta);
|
||||||
|
if (sta->ipaddr)
|
||||||
|
hostapd_drv_br_delete_ip_neigh(hapd, sta->ipaddr);
|
||||||
hostapd_drv_sta_remove(hapd, sta->addr);
|
hostapd_drv_sta_remove(hapd, sta->addr);
|
||||||
|
|
||||||
if (sta->timeout_next == STA_NULLFUNC ||
|
if (sta->timeout_next == STA_NULLFUNC ||
|
||||||
|
|
|
@ -174,6 +174,8 @@ static void hostapd_ext_capab_byte(struct hostapd_data *hapd, u8 *pos, int idx)
|
||||||
*pos |= 0x01; /* Bit 0 - Coexistence management */
|
*pos |= 0x01; /* Bit 0 - Coexistence management */
|
||||||
break;
|
break;
|
||||||
case 1: /* Bits 8-15 */
|
case 1: /* Bits 8-15 */
|
||||||
|
if (hapd->conf->proxy_arp)
|
||||||
|
*pos |= 0x10; /* Bit 12 - Proxy ARP */
|
||||||
break;
|
break;
|
||||||
case 2: /* Bits 16-23 */
|
case 2: /* Bits 16-23 */
|
||||||
if (hapd->conf->wnm_sleep_mode)
|
if (hapd->conf->wnm_sleep_mode)
|
||||||
|
|
|
@ -156,6 +156,9 @@ void ap_free_sta(struct hostapd_data *hapd, struct sta_info *sta)
|
||||||
if (sta->flags & WLAN_STA_WDS)
|
if (sta->flags & WLAN_STA_WDS)
|
||||||
hostapd_set_wds_sta(hapd, NULL, sta->addr, sta->aid, 0);
|
hostapd_set_wds_sta(hapd, NULL, sta->addr, sta->aid, 0);
|
||||||
|
|
||||||
|
if (sta->ipaddr)
|
||||||
|
hostapd_drv_br_delete_ip_neigh(hapd, sta->ipaddr);
|
||||||
|
|
||||||
if (!hapd->iface->driver_ap_teardown &&
|
if (!hapd->iface->driver_ap_teardown &&
|
||||||
!(sta->flags & WLAN_STA_PREAUTH))
|
!(sta->flags & WLAN_STA_PREAUTH))
|
||||||
hostapd_drv_sta_remove(hapd, sta->addr);
|
hostapd_drv_sta_remove(hapd, sta->addr);
|
||||||
|
@ -605,6 +608,9 @@ static int ap_sta_remove(struct hostapd_data *hapd, struct sta_info *sta)
|
||||||
{
|
{
|
||||||
ieee802_1x_notify_port_enabled(sta->eapol_sm, 0);
|
ieee802_1x_notify_port_enabled(sta->eapol_sm, 0);
|
||||||
|
|
||||||
|
if (sta->ipaddr)
|
||||||
|
hostapd_drv_br_delete_ip_neigh(hapd, sta->ipaddr);
|
||||||
|
|
||||||
wpa_printf(MSG_DEBUG, "Removing STA " MACSTR " from kernel driver",
|
wpa_printf(MSG_DEBUG, "Removing STA " MACSTR " from kernel driver",
|
||||||
MAC2STR(sta->addr));
|
MAC2STR(sta->addr));
|
||||||
if (hostapd_drv_sta_remove(hapd, sta->addr) &&
|
if (hostapd_drv_sta_remove(hapd, sta->addr) &&
|
||||||
|
|
|
@ -46,6 +46,7 @@ struct sta_info {
|
||||||
struct sta_info *next; /* next entry in sta list */
|
struct sta_info *next; /* next entry in sta list */
|
||||||
struct sta_info *hnext; /* next entry in hash table list */
|
struct sta_info *hnext; /* next entry in hash table list */
|
||||||
u8 addr[6];
|
u8 addr[6];
|
||||||
|
be32 ipaddr;
|
||||||
u16 aid; /* STA's unique AID (1 .. 2007) or 0 if not yet assigned */
|
u16 aid; /* STA's unique AID (1 .. 2007) or 0 if not yet assigned */
|
||||||
u32 flags; /* Bitfield of WLAN_STA_* */
|
u32 flags; /* Bitfield of WLAN_STA_* */
|
||||||
u16 capability;
|
u16 capability;
|
||||||
|
|
Loading…
Reference in a new issue