[PATCH 26/44] Enable 802.11i pre-authentication with full dynamic vlans

michael-dev at fami-braun.de michael-dev at fami-braun.de
Wed Feb 24 03:53:32 PST 2016


From: Michael Braun <michael-dev at fami-braun.de>

Using pre-auth with fully dynamic vlans currently fails as the target AP
does not listen for preauthentication packets in the target vlan, because
he doesn't known about the vlan yet.

This patch solves this by snooping preauth packets from local stations and
sending them in a different vlan/bridge instead. This way the target AP
can receive the requests by listening in a common vlan/bridge. Replies are
also snooped and copied back.

Signed-off-by: Michael Braun <michael-dev at fami-braun.de>
---
 hostapd/Makefile                   |   5 +
 hostapd/config_file.c              |   5 +
 hostapd/defconfig                  |   3 +
 hostapd/hostapd.conf               |   9 ++
 src/ap/ap_config.h                 |   6 +
 src/ap/hostapd.h                   |   6 +
 src/ap/l2_snoop.h                  |  72 +++++++++++
 src/ap/l2_snoop_pcap.c             | 134 ++++++++++++++++++++
 src/ap/preauth_auth.c              | 242 ++++++++++++++++++++++++++++++++++++-
 src/ap/preauth_auth.h              |  19 +++
 src/ap/vlan_init.c                 |  31 +++++
 tests/hwsim/example-hostapd.config |   1 +
 12 files changed, 531 insertions(+), 2 deletions(-)
 create mode 100644 src/ap/l2_snoop.h
 create mode 100644 src/ap/l2_snoop_pcap.c

diff --git a/hostapd/Makefile b/hostapd/Makefile
index 47587e6..368523b 100644
--- a/hostapd/Makefile
+++ b/hostapd/Makefile
@@ -237,6 +237,11 @@ endif
 ifdef CONFIG_RSN_PREAUTH
 CFLAGS += -DCONFIG_RSN_PREAUTH
 CONFIG_L2_PACKET=y
+ifdef CONFIG_RSN_PREAUTH_COPY
+CFLAGS += -DCONFIG_RSN_PREAUTH_COPY
+OBJS += ../src/ap/l2_snoop_pcap.o
+LIBS += -lpcap
+endif
 endif
 
 ifdef CONFIG_PEERKEY
diff --git a/hostapd/config_file.c b/hostapd/config_file.c
index d2a6406..2b9727b 100644
--- a/hostapd/config_file.c
+++ b/hostapd/config_file.c
@@ -2518,6 +2518,11 @@ static int hostapd_config_fill(struct hostapd_config *conf,
 		bss->rsn_preauth_interfaces = os_strdup(pos);
 	} else if (os_strcmp(buf, "rsn_preauth_autoconf_bridge") == 0) {
 		bss->rsn_preauth_autoconf_bridge = atoi(pos);
+#ifdef CONFIG_RSN_PREAUTH_COPY
+	} else if (os_strcmp(buf, "rsn_preauth_copy_iface") == 0) {
+		os_strlcpy(bss->rsn_preauth_copy_iface, pos,
+			   sizeof(bss->rsn_preauth_copy_iface));
+#endif /* CONFIG_RSN_PREAUTH_COPY */
 #endif /* CONFIG_RSN_PREAUTH */
 #ifdef CONFIG_PEERKEY
 	} else if (os_strcmp(buf, "peerkey") == 0) {
diff --git a/hostapd/defconfig b/hostapd/defconfig
index f7b60e0..5168829 100644
--- a/hostapd/defconfig
+++ b/hostapd/defconfig
@@ -50,6 +50,9 @@ CONFIG_IAPP=y
 # WPA2/IEEE 802.11i RSN pre-authentication
 CONFIG_RSN_PREAUTH=y
 
+# WPA2/IEEE 802.11i RSN pre-authentication full-dynamic packet copy
+CONFIG_RSN_PREAUTH_COPY=y
+
 # PeerKey handshake for Station to Station Link (IEEE 802.11e DLS)
 CONFIG_PEERKEY=y
 
diff --git a/hostapd/hostapd.conf b/hostapd/hostapd.conf
index e26add5..efc7a54 100644
--- a/hostapd/hostapd.conf
+++ b/hostapd/hostapd.conf
@@ -1189,6 +1189,15 @@ own_ip_addr=127.0.0.1
 # rsn_preauth_interfaces, they need to be bridges.
 # (default: 0 = disabled)
 #rsn_preauth_autoconf_bridge=1
+#
+# For pre-authentication with fully dynamic vlans, the packets from the local
+# station need to copied into a different netdev where they can reach the
+# target AP and similar in the opposite direction. This is the netdev where
+# packets from sta to remote ap are send to and packets from remote ap to sta
+# are gathered from.
+# ETH_P_PREAUTH packets can then be filtered from forwarding in the normal data
+# bridge.
+#rsn_preauth_copy_iface=brpreauth
 
 # peerkey: Whether PeerKey negotiation for direct links (IEEE 802.11e) is
 # allowed. This is only used with RSN/WPA2.
diff --git a/src/ap/ap_config.h b/src/ap/ap_config.h
index 8521e01..a2b36c3 100644
--- a/src/ap/ap_config.h
+++ b/src/ap/ap_config.h
@@ -120,6 +120,9 @@ struct hostapd_vlan {
 	char ifname[IFNAMSIZ + 1];
 	int configured;
 	int dynamic_vlan;
+#ifdef CONFIG_RSN_PREAUTH_COPY
+	void *rsn_preauth;
+#endif /* CONFIG_RSN_PREAUTH_COPY */
 #ifdef CONFIG_FULL_DYNAMIC_VLAN
 
 #define DVLAN_CLEAN_WLAN_PORT	0x8
@@ -319,6 +322,9 @@ struct hostapd_bss_config {
 	int rsn_preauth;
 	char *rsn_preauth_interfaces;
 	int rsn_preauth_autoconf_bridge;
+#ifdef CONFIG_RSN_PREAUTH_COPY
+	char rsn_preauth_copy_iface[IFNAMSIZ+1];
+#endif /* CONFIG_RSN_PREAUTH_COPY */
 	int peerkey;
 
 #ifdef CONFIG_IEEE80211R
diff --git a/src/ap/hostapd.h b/src/ap/hostapd.h
index d6d96db..4920b47 100644
--- a/src/ap/hostapd.h
+++ b/src/ap/hostapd.h
@@ -150,6 +150,12 @@ struct hostapd_data {
 	struct eapol_authenticator *eapol_auth;
 
 	struct rsn_preauth_interface *preauth_iface;
+#ifdef CONFIG_RSN_PREAUTH_COPY
+	/* ctx for snoop preauth packets for STA on BSS preauth_copy_iface */
+	struct rsn_preauth_copy_interface *preauth_copy_iface;
+	/* ctx for snoop preauth packets from STA on BSS conf->iface */
+	struct rsn_preauth_copy_interface *preauth_vlan0;
+#endif
 	struct os_reltime michael_mic_failure;
 	int michael_mic_failures;
 	int tkip_countermeasures;
diff --git a/src/ap/l2_snoop.h b/src/ap/l2_snoop.h
new file mode 100644
index 0000000..520911c
--- /dev/null
+++ b/src/ap/l2_snoop.h
@@ -0,0 +1,72 @@
+/*
+ * hostapd - Layer2 packet snooping interface definition
+ * Copyright (c) 2015, Michael Braun <michael-dev at fami-braun.de>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ *
+ * This file defines an interface for layer 2 (link layer) packet injecting
+ * and snooping.
+ */
+
+#ifndef L2_SNOOP_H
+#define L2_SNOOP_H
+
+/**
+ * struct l2_snoop_data - Internal l2_snoop data structure
+ *
+ * This structure is used by the l2_snoop implementation to store its private
+ * data. Other files use a pointer to this data when calling the l2_snoop
+ * functions, but the contents of this structure should not be used directly
+ * outside l2_snoop implementation.
+ */
+struct l2_snoop_data;
+
+#ifdef _MSC_VER
+#pragma pack(push, 1)
+#endif /* _MSC_VER */
+
+struct l2_snoop_ethhdr {
+	u8 h_dest[ETH_ALEN];
+	u8 h_source[ETH_ALEN];
+	be16 h_proto;
+} STRUCT_PACKED;
+
+#ifdef _MSC_VER
+#pragma pack(pop)
+#endif /* _MSC_VER */
+
+/**
+ * l2_snoop_init - Initialize l2_snoop interface
+ * @ifname: Interface name
+ * @protocol: Ethernet protocol number in host byte order
+ * @rx_callback: Callback function that will be called for each received packet
+ * @rx_callback_ctx: Callback data (ctx) for calls to rx_callback()
+ * Returns: Pointer to internal data or %NULL on failure
+ *
+ * rx_callback function will be called with src_addr pointing to the source
+ * address (MAC address) of the the packet. Buf points to payload including
+ * ethernet header.
+ */
+struct l2_snoop_data * l2_snoop_init(
+	const char *ifname, unsigned short protocol,
+	void (*rx_callback)(void *ctx, const u8 *src_addr,
+			    const u8 *buf, size_t len),
+	void *rx_callback_ctx);
+
+/**
+ * l2_snoop_deinit - Deinitialize l2_snoop interface
+ * @l2: Pointer to internal l2_snoop data from l2_snoop_init()
+ */
+void l2_snoop_deinit(struct l2_snoop_data *l2);
+
+/**
+ * l2_snoop_send - Send a packet
+ * @l2: Pointer to internal l2_snoop data from l2_snoop_init()
+ * @buf: Packet contents to be sent; including layer 2 header.
+ * @len: Length of the buffer (including l2 header)
+ * Returns: >=0 on success, <0 on failure
+ */
+int l2_snoop_send(struct l2_snoop_data *l2, const u8 *buf, size_t len);
+
+#endif /* L2_SNOOP_H */
diff --git a/src/ap/l2_snoop_pcap.c b/src/ap/l2_snoop_pcap.c
new file mode 100644
index 0000000..889c9b1
--- /dev/null
+++ b/src/ap/l2_snoop_pcap.c
@@ -0,0 +1,134 @@
+/*
+ * hostapd - Layer2 packet snooping interface definition
+ * Copyright (c) 2015, Michael Braun <michael-dev at fami-braun.de>
+ *
+ * Implementation based on l2_packet/l2_packet_pcap.c with modifications.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+#include <pcap.h>
+
+#include "common.h"
+#include "eloop.h"
+#include "l2_snoop.h"
+
+
+struct l2_snoop_data {
+	pcap_t *pcap;
+	char ifname[100];
+	void (*rx_callback)(void *ctx, const u8 *src_addr,
+			    const u8 *buf, size_t len);
+	void *rx_callback_ctx;
+};
+
+
+int l2_snoop_send(struct l2_snoop_data *l2, const u8 *buf, size_t len)
+{
+	int ret;
+
+	if (l2 == NULL)
+		return -1;
+
+	ret = pcap_sendpacket(l2->pcap, buf, len);
+
+	return ret;
+}
+
+
+static void l2_snoop_receive(int sock, void *eloop_ctx, void *sock_ctx)
+{
+	struct l2_snoop_data *l2 = eloop_ctx;
+	pcap_t *pcap = sock_ctx;
+	struct pcap_pkthdr hdr;
+	const u_char *packet;
+	struct l2_snoop_ethhdr *ethhdr;
+	unsigned char *buf;
+	size_t len;
+
+	packet = pcap_next(pcap, &hdr);
+
+	if (packet == NULL || hdr.caplen < sizeof(*ethhdr))
+		return;
+
+	ethhdr = (struct l2_snoop_ethhdr *) packet;
+	buf = (unsigned char *) ethhdr;
+	len = hdr.caplen;
+	l2->rx_callback(l2->rx_callback_ctx, ethhdr->h_source, buf, len);
+}
+
+
+static int l2_snoop_init_libpcap(struct l2_snoop_data *l2,
+				  unsigned short protocol)
+{
+	bpf_u_int32 pcap_maskp, pcap_netp;
+	char pcap_filter[200], pcap_err[PCAP_ERRBUF_SIZE];
+	struct bpf_program pcap_fp;
+
+	pcap_lookupnet(l2->ifname, &pcap_netp, &pcap_maskp, pcap_err);
+	l2->pcap = pcap_open_live(l2->ifname, 2500, 1, 10, pcap_err);
+	if (l2->pcap == NULL) {
+		fprintf(stderr, "pcap_open_live: %s\n", pcap_err);
+		fprintf(stderr, "ifname='%s'\n", l2->ifname);
+		return -1;
+	}
+	if (pcap_setnonblock(l2->pcap, 1, pcap_err) < 0)
+		fprintf(stderr, "pcap_setnonblock: %s\n",
+			pcap_geterr(l2->pcap));
+	os_snprintf(pcap_filter, sizeof(pcap_filter),
+		    "ether proto 0x%x", protocol);
+	if (pcap_compile(l2->pcap, &pcap_fp, pcap_filter, 1, pcap_netp) < 0) {
+		fprintf(stderr, "pcap_compile: %s\n", pcap_geterr(l2->pcap));
+		return -1;
+	}
+
+	if (pcap_setfilter(l2->pcap, &pcap_fp) < 0) {
+		fprintf(stderr, "pcap_setfilter: %s\n", pcap_geterr(l2->pcap));
+		return -1;
+	}
+
+	pcap_freecode(&pcap_fp);
+
+	eloop_register_read_sock(pcap_get_selectable_fd(l2->pcap),
+				 l2_snoop_receive, l2, l2->pcap);
+
+	return 0;
+}
+
+
+struct l2_snoop_data * l2_snoop_init(
+	const char *ifname, unsigned short protocol,
+	void (*rx_callback)(void *ctx, const u8 *src_addr,
+			    const u8 *buf, size_t len),
+	void *rx_callback_ctx)
+{
+	struct l2_snoop_data *l2;
+
+	l2 = os_zalloc(sizeof(struct l2_snoop_data));
+	if (l2 == NULL)
+		return NULL;
+	os_strlcpy(l2->ifname, ifname, sizeof(l2->ifname));
+	l2->rx_callback = rx_callback;
+	l2->rx_callback_ctx = rx_callback_ctx;
+
+	if (l2_snoop_init_libpcap(l2, protocol)) {
+		os_free(l2);
+		return NULL;
+	}
+
+	return l2;
+}
+
+
+void l2_snoop_deinit(struct l2_snoop_data *l2)
+{
+	if (l2 == NULL)
+		return;
+
+	eloop_unregister_read_sock(pcap_get_selectable_fd(l2->pcap));
+	if (l2->pcap)
+		pcap_close(l2->pcap);
+	os_free(l2);
+}
diff --git a/src/ap/preauth_auth.c b/src/ap/preauth_auth.c
index 2284b5d..7f12fa7 100644
--- a/src/ap/preauth_auth.c
+++ b/src/ap/preauth_auth.c
@@ -25,6 +25,9 @@
 #include "bridge.h"
 #include "dummy.h"
 #include "ifconfig.h"
+#ifdef CONFIG_RSN_PREAUTH_COPY
+#include "l2_snoop.h"
+#endif /* CONFIG_RSN_PREAUTH_COPY */
 
 #ifndef ETH_P_PREAUTH
 #define ETH_P_PREAUTH 0x88C7 /* IEEE 802.11i pre-authentication */
@@ -40,6 +43,23 @@ struct rsn_preauth_interface {
 	int ifindex;
 };
 
+#ifdef CONFIG_RSN_PREAUTH_COPY
+struct rsn_preauth_copy_interface {
+	struct l2_snoop_data *l2;
+	char ifname[IFNAMSIZ+1];
+	struct hostapd_data *hapd;
+};
+
+
+static struct rsn_preauth_copy_interface*
+rsn_preauth_snoop_init_cb(struct hostapd_data *hapd, char *ifname,
+			  void (*rx_callback)(void *ctx, const u8 *src_addr,
+						const u8 *buf, size_t len));
+static void rsn_preauth_snoop_from_sta(void *ctx, const u8 *src_addr,
+				       const u8 *buf, size_t len);
+static void rsn_preauth_snoop_to_sta(void *ctx, const u8 *src_addr,
+				     const u8 *buf, size_t len);
+#endif /* CONFIG_RSN_PREAUTH_COPY */
 
 static void rsn_preauth_receive(void *ctx, const u8 *src_addr,
 				const u8 *buf, size_t len)
@@ -153,6 +173,7 @@ fail1:
 
 void rsn_preauth_iface_deinit(struct hostapd_data *hapd)
 {
+	struct hostapd_bss_config *conf = hapd->conf;
 	struct rsn_preauth_interface *piface, *prev;
 #ifdef CONFIG_LIBNL3_ROUTE
 	char dummy_iface[IFNAMSIZ+1];
@@ -166,7 +187,7 @@ void rsn_preauth_iface_deinit(struct hostapd_data *hapd)
 		if (hapd->conf->rsn_preauth_autoconf_bridge) {
 #ifdef CONFIG_LIBNL3_ROUTE
 			snprintf(dummy_iface, sizeof(dummy_iface), "pre%s",
-				 hapd->conf->iface);
+				 conf->iface);
 			ifconfig_down(dummy_iface);
 			br_delif(prev->ifname, dummy_iface);
 			dummy_del(dummy_iface);
@@ -176,6 +197,14 @@ void rsn_preauth_iface_deinit(struct hostapd_data *hapd)
 		os_free(prev->ifname);
 		os_free(prev);
 	}
+
+#ifdef CONFIG_RSN_PREAUTH_COPY
+	rsn_preauth_snoop_deinit(hapd, conf->rsn_preauth_copy_iface,
+				 hapd->preauth_copy_iface);
+	hapd->preauth_copy_iface = NULL;
+	rsn_preauth_snoop_deinit(hapd, conf->iface, hapd->preauth_vlan0);
+	hapd->preauth_vlan0 = NULL;
+#endif /* CONFIG_RSN_PREAUTH_COPY */
 }
 
 
@@ -184,7 +213,7 @@ int rsn_preauth_iface_init(struct hostapd_data *hapd)
 	char *tmp, *start, *end;
 
 	if (hapd->conf->rsn_preauth_interfaces == NULL)
-		return 0;
+		goto skip_preauth_iface_init;
 
 	tmp = os_strdup(hapd->conf->rsn_preauth_interfaces);
 	if (tmp == NULL)
@@ -211,6 +240,20 @@ int rsn_preauth_iface_init(struct hostapd_data *hapd)
 			break;
 	}
 	os_free(tmp);
+
+skip_preauth_iface_init:
+#ifdef CONFIG_RSN_PREAUTH_COPY
+	if (hapd->preauth_copy_iface)
+		rsn_preauth_snoop_deinit(hapd,
+					 hapd->conf->rsn_preauth_copy_iface,
+					 rsn_preauth_snoop_to_sta);
+
+	hapd->preauth_copy_iface = rsn_preauth_snoop_init_cb(hapd,
+					   hapd->conf->rsn_preauth_copy_iface,
+					   rsn_preauth_snoop_to_sta);
+	hapd->preauth_vlan0 = rsn_preauth_snoop_init(hapd, hapd->conf->iface);
+#endif /* CONFIG_RSN_PREAUTH_COPY */
+
 	return 0;
 }
 
@@ -303,4 +346,199 @@ void rsn_preauth_free_station(struct hostapd_data *hapd, struct sta_info *sta)
 	eloop_cancel_timeout(rsn_preauth_finished_cb, hapd, sta);
 }
 
+#ifdef CONFIG_RSN_PREAUTH_COPY
+static struct rsn_preauth_copy_interface*
+rsn_preauth_snoop_init_cb(struct hostapd_data *hapd, char *ifname,
+			    void (*rx_callback)(void *ctx, const u8 *src_addr,
+						const u8 *buf, size_t len))
+{
+	struct rsn_preauth_copy_interface *ctx;
+
+	if (!hapd->conf->rsn_preauth_copy_iface[0])
+		return NULL;
+
+	ctx = os_zalloc(sizeof(*ctx));
+
+	if (ctx == NULL) {
+		wpa_printf(MSG_ERROR,
+			   "RSN: rsn_preauth_init_cb: Failed to alloc ctx");
+		goto fail;
+	}
+
+	ifconfig_up(ifname);
+
+	os_strlcpy(ctx->ifname, ifname, sizeof(ctx->ifname));
+	ctx->hapd = hapd;
+	ctx->l2 = l2_snoop_init(ifname, ETH_P_PREAUTH, rx_callback, ctx);
+
+	if (ctx->l2 == NULL) {
+		wpa_printf(MSG_ERROR, "RSN: failed to start L2 snooping on %s,"
+			   " error: %s", ifname, strerror(errno));
+		goto fail;
+	}
+
+	return ctx;
+fail:
+	if (ctx && ctx->l2)
+		l2_snoop_deinit(ctx->l2);
+	if (ctx)
+		os_free(ctx);
+	return NULL;
+}
+
+void * rsn_preauth_snoop_init(struct hostapd_data *hapd, char *ifname)
+{
+	return rsn_preauth_snoop_init_cb(hapd, ifname,
+					 rsn_preauth_snoop_from_sta);
+}
+
+void rsn_preauth_snoop_deinit(struct hostapd_data *hapd, char *ifname,
+			      void *ctx_ptr)
+{
+	struct rsn_preauth_copy_interface *ctx = ctx_ptr;
+
+	wpa_printf(MSG_DEBUG, "RSN: deinit pre-authentication snooping"
+			      " on %s", ifname);
+
+	if (ctx && ctx->l2)
+		l2_snoop_deinit(ctx->l2);
+	if (ctx)
+		os_free(ctx);
+}
+
+
+struct rsn_preauth_iter_data {
+	struct hostapd_data *src_hapd;
+	const u8 *src_addr;
+	const u8 *dst;
+	const u8 *data;
+	size_t data_len;
+	const char *dest_iface;
+};
+
+
+static int rsn_preauth_iter(struct hostapd_iface *iface, void *ctx)
+{
+	struct rsn_preauth_iter_data *idata = ctx;
+	struct hostapd_data *hapd;
+	unsigned int j;
+	struct rsn_preauth_interface *piface;
+
+	for (j = 0; j < iface->num_bss; j++) {
+		hapd = iface->bss[j];
+		if (hapd == idata->src_hapd)
+			continue;
+		if (os_memcmp(hapd->own_addr, idata->dst, ETH_ALEN) != 0)
+			continue;
+		for (piface = hapd->preauth_iface; piface;
+		     piface = piface->next) {
+			if (strcmp(piface->ifname, idata->dest_iface) != 0)
+				continue;
+			wpa_printf(MSG_DEBUG, "RSN: Send preauth data directly"
+				   " to locally managed BSS " MACSTR "@%s -> "
+				   MACSTR "@%s via %s",
+				   MAC2STR(idata->src_addr),
+				   idata->src_hapd->conf->iface,
+				   MAC2STR(hapd->own_addr), hapd->conf->iface,
+				   idata->dest_iface);
+			rsn_preauth_receive(piface, idata->src_addr,
+					    idata->data, idata->data_len);
+			return 1;
+		}
+	}
+
+	return 0;
+}
+
+static void rsn_preauth_snoop_from_sta(void *ctx, const u8 *src_addr,
+				       const u8 *buf, size_t len)
+{
+	struct rsn_preauth_copy_interface *l2ctx;
+	struct hostapd_data *hapd;
+	struct sta_info *sta;
+	struct l2_ethhdr *ethhdr;
+	struct rsn_preauth_iter_data idata;
+
+	l2ctx = (struct rsn_preauth_copy_interface *) ctx;
+	hapd = l2ctx->hapd;
+
+	if (!hapd->preauth_copy_iface)
+		return;
+
+	if (len < sizeof(*ethhdr))
+		return;
+	ethhdr = (struct l2_ethhdr *) buf;
+
+	sta = ap_get_sta(hapd, ethhdr->h_source);
+	if (!sta)
+		return;
+
+	wpa_printf(MSG_DEBUG, "RSN: preauth_snoop forward from sta " MACSTR
+		   " to ap " MACSTR " on from %s to %s",
+		   MAC2STR(ethhdr->h_source), MAC2STR(ethhdr->h_dest),
+		   l2ctx->ifname, hapd->preauth_copy_iface->ifname);
+
+	idata.src_hapd = hapd;
+	idata.dst = ethhdr->h_dest;
+	idata.data = buf;
+	idata.data_len = len;
+	idata.src_addr = src_addr;
+	idata.dest_iface = hapd->conf->rsn_preauth_copy_iface;
+	if (hapd->iface->interfaces->for_each_interface(
+	    hapd->iface->interfaces, rsn_preauth_iter,
+	    &idata))
+		return;
+	l2_snoop_send(hapd->preauth_copy_iface->l2, buf, len);
+}
+
+static void rsn_preauth_snoop_to_sta(void *ctx, const u8 *src_addr,
+				     const u8 *buf, size_t len)
+{
+	struct rsn_preauth_copy_interface *l2ctx;
+	struct hostapd_data *hapd;
+	struct rsn_preauth_copy_interface *destctx;
+	struct hostapd_vlan *vlan;
+	struct sta_info *sta;
+	struct l2_ethhdr *ethhdr;
+
+	l2ctx = (struct rsn_preauth_copy_interface *) ctx;
+	hapd = l2ctx->hapd;
+
+	if (!hapd->preauth_copy_iface)
+		return;
+
+	if (len < sizeof(*ethhdr))
+		return;
+	ethhdr = (struct l2_ethhdr *) buf;
+
+	sta = ap_get_sta(hapd, ethhdr->h_dest);
+	if (!sta)
+		return;
+
+	destctx = hapd->preauth_vlan0;
+#ifndef CONFIG_NO_VLAN
+	if (sta->vlan_id_bound) {
+		vlan = hapd->conf->vlan;
+		while (vlan) {
+			if (vlan->vlan_id == sta->vlan_id_bound)
+				break;
+			vlan = vlan->next;
+		}
+		if (!vlan)
+			return;
+		destctx = vlan->rsn_preauth;
+	}
+#endif /* CONFIG_NO_VLAN */
+	if (!destctx || !destctx->l2)
+		return;
+
+	wpa_printf(MSG_DEBUG, "RSN: preauth_snoop forward from ap " MACSTR
+		   " to sta " MACSTR " if from %s to %s",
+		   MAC2STR(ethhdr->h_source), MAC2STR(ethhdr->h_dest),
+		   l2ctx->ifname, destctx->ifname);
+
+	l2_snoop_send(destctx->l2, buf, len);
+}
+#endif /* CONFIG_RSN_PREAUTH_COPY */
+
 #endif /* CONFIG_RSN_PREAUTH */
diff --git a/src/ap/preauth_auth.h b/src/ap/preauth_auth.h
index 69fb356..fa046d2 100644
--- a/src/ap/preauth_auth.h
+++ b/src/ap/preauth_auth.h
@@ -18,6 +18,12 @@ void rsn_preauth_finished(struct hostapd_data *hapd, struct sta_info *sta,
 void rsn_preauth_send(struct hostapd_data *hapd, struct sta_info *sta,
 		      u8 *buf, size_t len);
 void rsn_preauth_free_station(struct hostapd_data *hapd, struct sta_info *sta);
+#ifdef CONFIG_RSN_PREAUTH_COPY
+/* snoop packets from STA */
+void * rsn_preauth_snoop_init(struct hostapd_data *hapd, char *ifname);
+void rsn_preauth_snoop_deinit(struct hostapd_data *hapd, char *ifname,
+			      void *ctx);
+#endif /* CONFIG_RSN_PREAUTH_COPY */
 
 #else /* CONFIG_RSN_PREAUTH */
 
@@ -47,6 +53,19 @@ static inline void rsn_preauth_free_station(struct hostapd_data *hapd,
 {
 }
 
+#ifdef CONFIG_RSN_PREAUTH_COPY
+static inline void * rsn_preauth_snoop_init(struct hostapd_data *hapd,
+					   char *ifname)
+{
+	return NULL;
+}
+
+static inline void rsn_preauth_snoop_deinit(struct hostapd_data *hapd,
+					    char *ifname, void *ctx)
+{
+}
+#endif /* CONFIG_RSN_PREAUTH_COPY */
+
 #endif /* CONFIG_RSN_PREAUTH */
 
 #endif /* PREAUTH_H */
diff --git a/src/ap/vlan_init.c b/src/ap/vlan_init.c
index becf1dc..cd37db5 100644
--- a/src/ap/vlan_init.c
+++ b/src/ap/vlan_init.c
@@ -26,6 +26,11 @@
 #include <linux/if_bridge.h>
 #endif /* CONFIG_FULL_DYNAMIC_VLAN */
 
+#include "wpa_auth_glue.h"
+#ifdef CONFIG_RSN_PREAUTH_COPY
+#include "preauth_auth.h"
+#endif /* CONFIG_RSN_PREAUTH_COPY */
+
 
 #ifdef CONFIG_FULL_DYNAMIC_VLAN
 
@@ -157,6 +162,12 @@ static int vlan_if_add(struct hostapd_data *hapd, struct hostapd_vlan *vlan,
 
 	ifconfig_up(vlan->ifname); /* else wpa group will fail fatal */
 
+#ifdef CONFIG_RSN_PREAUTH_COPY
+	if (!vlan->rsn_preauth)
+		vlan->rsn_preauth = rsn_preauth_snoop_init(hapd,
+							   vlan->ifname);
+#endif /* CONFIG_RSN_PREAUTH_COPY */
+
 	if (hapd->wpa_auth)
 		ret = wpa_auth_ensure_group(hapd->wpa_auth, vlan->vlan_id);
 
@@ -168,6 +179,10 @@ static int vlan_if_add(struct hostapd_data *hapd, struct hostapd_vlan *vlan,
 	if (wpa_auth_release_group(hapd->wpa_auth, vlan->vlan_id))
 		wpa_printf(MSG_ERROR, "WPA deinit of %s failed", vlan->ifname);
 
+#ifdef CONFIG_RSN_PREAUTH_COPY
+	rsn_preauth_snoop_deinit(hapd, vlan->ifname, vlan->rsn_preauth);
+#endif /* CONFIG_RSN_PREAUTH_COPY */
+
 	/* group state machine setup failed */
 	if (hostapd_vlan_if_remove(hapd, vlan->ifname))
 		wpa_printf(MSG_ERROR, "Removal of %s failed", vlan->ifname);
@@ -186,6 +201,11 @@ static int vlan_if_remove(struct hostapd_data *hapd, struct hostapd_vlan *vlan)
 			   "WPA deinitialization for VLAN %d failed (%d)",
 			   vlan->vlan_id, ret);
 
+#ifdef CONFIG_RSN_PREAUTH_COPY
+	rsn_preauth_snoop_deinit(hapd, vlan->ifname, vlan->rsn_preauth);
+	vlan->rsn_preauth = NULL;
+#endif /* CONFIG_RSN_PREAUTH_COPY */
+
 	return hostapd_vlan_if_remove(hapd, vlan->ifname);
 }
 
@@ -450,6 +470,12 @@ static void vlan_newlink(const char *ifname, struct hostapd_data *hapd)
 	}
 
 	ifconfig_up(ifname);
+
+#ifdef CONFIG_RSN_PREAUTH_COPY
+	if (!vlan->rsn_preauth)
+		vlan->rsn_preauth = rsn_preauth_snoop_init(hapd,
+							   vlan->ifname);
+#endif /* CONFIG_RSN_PREAUTH_COPY */
 }
 
 
@@ -523,6 +549,11 @@ static void vlan_dellink(const char *ifname, struct hostapd_data *hapd)
 		char br_name[IFNAMSIZ];
 		int i;
 
+#ifdef CONFIG_RSN_PREAUTH_COPY
+		rsn_preauth_snoop_deinit(hapd, vlan->ifname,
+					 vlan->rsn_preauth);
+#endif /* CONFIG_RSN_PREAUTH_COPY */
+
 		for (i = 0; i < MAX_NUM_TAGGED_VLAN && tagged[i]; i++) {
 			if (tagged[i] == untagged ||
 			    tagged[i] <= 0 || tagged[i] > MAX_VLAN_ID ||
diff --git a/tests/hwsim/example-hostapd.config b/tests/hwsim/example-hostapd.config
index 2b8ad7f..7802916 100644
--- a/tests/hwsim/example-hostapd.config
+++ b/tests/hwsim/example-hostapd.config
@@ -3,6 +3,7 @@
 CONFIG_DRIVER_NONE=y
 CONFIG_DRIVER_NL80211=y
 CONFIG_RSN_PREAUTH=y
+CONFIG_RSN_PREAUTH_COPY=y
 
 #CONFIG_TLS=internal
 #CONFIG_INTERNAL_LIBTOMMATH=y
-- 
1.9.1




More information about the Hostap mailing list