aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorKyeyoon Park <kyeyoonp@qca.qualcomm.com>2014-09-26 05:32:55 (GMT)
committerJouni Malinen <j@w1.fi>2014-10-27 23:08:29 (GMT)
commit7d597d461777cd9af5d49dd678e29d0d400c27e1 (patch)
tree777fe5bcb84780fb125d9e50629cedf5ec1ccf0f /src
parent6897556455bd18f3e87b56636a06f8dc8a9336f8 (diff)
downloadhostap-7d597d461777cd9af5d49dd678e29d0d400c27e1.zip
hostap-7d597d461777cd9af5d49dd678e29d0d400c27e1.tar.gz
hostap-7d597d461777cd9af5d49dd678e29d0d400c27e1.tar.bz2
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>
Diffstat (limited to 'src')
-rw-r--r--src/ap/ap_config.h1
-rw-r--r--src/ap/dhcp_snoop.c196
-rw-r--r--src/ap/dhcp_snoop.h30
-rw-r--r--src/ap/hostapd.c7
-rw-r--r--src/ap/hostapd.h3
-rw-r--r--src/ap/ieee802_11.c2
-rw-r--r--src/ap/ieee802_11_shared.c2
-rw-r--r--src/ap/sta_info.c6
-rw-r--r--src/ap/sta_info.h1
9 files changed, 248 insertions, 0 deletions
diff --git a/src/ap/ap_config.h b/src/ap/ap_config.h
index 18538c9..8a44401 100644
--- a/src/ap/ap_config.h
+++ b/src/ap/ap_config.h
@@ -484,6 +484,7 @@ struct hostapd_bss_config {
unsigned int qos_map_set_len;
int osen;
+ int proxy_arp;
#ifdef CONFIG_HS20
int hs20;
int disable_dgaf;
diff --git a/src/ap/dhcp_snoop.c b/src/ap/dhcp_snoop.c
new file mode 100644
index 0000000..1bd4ba4
--- /dev/null
+++ b/src/ap/dhcp_snoop.c
@@ -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);
+}
diff --git a/src/ap/dhcp_snoop.h b/src/ap/dhcp_snoop.h
new file mode 100644
index 0000000..93d0050
--- /dev/null
+++ b/src/ap/dhcp_snoop.h
@@ -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 */
diff --git a/src/ap/hostapd.c b/src/ap/hostapd.c
index 9fda339..2871b9b 100644
--- a/src/ap/hostapd.c
+++ b/src/ap/hostapd.c
@@ -36,6 +36,7 @@
#include "dfs.h"
#include "ieee802_11.h"
#include "bss_load.h"
+#include "dhcp_snoop.h"
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 */
bss_load_update_deinit(hapd);
+ dhcp_snoop_deinit(hapd);
#ifdef CONFIG_SQLITE
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;
}
+ 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)) {
wpa_printf(MSG_ERROR, "VLAN initialization failed.");
return -1;
diff --git a/src/ap/hostapd.h b/src/ap/hostapd.h
index 3f79413..4567036 100644
--- a/src/ap/hostapd.h
+++ b/src/ap/hostapd.h
@@ -241,6 +241,9 @@ struct hostapd_data {
#ifdef CONFIG_INTERWORKING
size_t gas_frag_limit;
#endif /* CONFIG_INTERWORKING */
+#ifdef CONFIG_PROXYARP
+ struct l2_packet_data *sock_dhcp;
+#endif /* CONFIG_PROXYARP */
#ifdef CONFIG_MESH
int num_plinks;
int max_plinks;
diff --git a/src/ap/ieee802_11.c b/src/ap/ieee802_11.c
index 3eb9456..d59a2b4 100644
--- a/src/ap/ieee802_11.c
+++ b/src/ap/ieee802_11.c
@@ -1502,6 +1502,8 @@ static void handle_disassoc(struct hostapd_data *hapd,
* authenticated. */
accounting_sta_stop(hapd, 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);
if (sta->timeout_next == STA_NULLFUNC ||
diff --git a/src/ap/ieee802_11_shared.c b/src/ap/ieee802_11_shared.c
index 12403f9..d462ac8 100644
--- a/src/ap/ieee802_11_shared.c
+++ b/src/ap/ieee802_11_shared.c
@@ -174,6 +174,8 @@ static void hostapd_ext_capab_byte(struct hostapd_data *hapd, u8 *pos, int idx)
*pos |= 0x01; /* Bit 0 - Coexistence management */
break;
case 1: /* Bits 8-15 */
+ if (hapd->conf->proxy_arp)
+ *pos |= 0x10; /* Bit 12 - Proxy ARP */
break;
case 2: /* Bits 16-23 */
if (hapd->conf->wnm_sleep_mode)
diff --git a/src/ap/sta_info.c b/src/ap/sta_info.c
index ed63aa8..ec0e493 100644
--- a/src/ap/sta_info.c
+++ b/src/ap/sta_info.c
@@ -156,6 +156,9 @@ void ap_free_sta(struct hostapd_data *hapd, struct sta_info *sta)
if (sta->flags & WLAN_STA_WDS)
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 &&
!(sta->flags & WLAN_STA_PREAUTH))
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);
+ if (sta->ipaddr)
+ hostapd_drv_br_delete_ip_neigh(hapd, sta->ipaddr);
+
wpa_printf(MSG_DEBUG, "Removing STA " MACSTR " from kernel driver",
MAC2STR(sta->addr));
if (hostapd_drv_sta_remove(hapd, sta->addr) &&
diff --git a/src/ap/sta_info.h b/src/ap/sta_info.h
index c4c8825..25edd7f 100644
--- a/src/ap/sta_info.h
+++ b/src/ap/sta_info.h
@@ -46,6 +46,7 @@ struct sta_info {
struct sta_info *next; /* next entry in sta list */
struct sta_info *hnext; /* next entry in hash table list */
u8 addr[6];
+ be32 ipaddr;
u16 aid; /* STA's unique AID (1 .. 2007) or 0 if not yet assigned */
u32 flags; /* Bitfield of WLAN_STA_* */
u16 capability;