aboutsummaryrefslogtreecommitdiffstats
path: root/src/l2_packet
diff options
context:
space:
mode:
authorJouni Malinen <j@w1.fi>2008-02-28 01:34:43 (GMT)
committerJouni Malinen <jm@jm.kir.nu>2008-02-28 01:34:43 (GMT)
commit6fc6879bd55a394f807cbbe927df736c190cb8ab (patch)
treecdf50da0c58f21510a808d53502a060d911ff243 /src/l2_packet
downloadhostap-6fc6879bd55a394f807cbbe927df736c190cb8ab.zip
hostap-6fc6879bd55a394f807cbbe927df736c190cb8ab.tar.gz
hostap-6fc6879bd55a394f807cbbe927df736c190cb8ab.tar.bz2
Re-initialize hostapd/wpa_supplicant git repository based on 0.6.3 release
Diffstat (limited to 'src/l2_packet')
-rw-r--r--src/l2_packet/.gitignore1
-rw-r--r--src/l2_packet/Makefile6
-rw-r--r--src/l2_packet/l2_packet.h130
-rw-r--r--src/l2_packet/l2_packet_freebsd.c285
-rw-r--r--src/l2_packet/l2_packet_linux.c199
-rw-r--r--src/l2_packet/l2_packet_ndis.c516
-rw-r--r--src/l2_packet/l2_packet_none.c123
-rw-r--r--src/l2_packet/l2_packet_pcap.c386
-rw-r--r--src/l2_packet/l2_packet_privsep.c267
-rw-r--r--src/l2_packet/l2_packet_winpcap.c340
10 files changed, 2253 insertions, 0 deletions
diff --git a/src/l2_packet/.gitignore b/src/l2_packet/.gitignore
new file mode 100644
index 0000000..a438335
--- /dev/null
+++ b/src/l2_packet/.gitignore
@@ -0,0 +1 @@
+*.d
diff --git a/src/l2_packet/Makefile b/src/l2_packet/Makefile
new file mode 100644
index 0000000..37d649c
--- /dev/null
+++ b/src/l2_packet/Makefile
@@ -0,0 +1,6 @@
+all:
+ @echo Nothing to be made.
+
+clean:
+ for d in $(SUBDIRS); do make -C $$d clean; done
+ rm -f *~ *.o *.d
diff --git a/src/l2_packet/l2_packet.h b/src/l2_packet/l2_packet.h
new file mode 100644
index 0000000..b4824c5
--- /dev/null
+++ b/src/l2_packet/l2_packet.h
@@ -0,0 +1,130 @@
+/*
+ * WPA Supplicant - Layer2 packet interface definition
+ * Copyright (c) 2003-2005, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ *
+ * This file defines an interface for layer 2 (link layer) packet sending and
+ * receiving. l2_packet_linux.c is one implementation for such a layer 2
+ * implementation using Linux packet sockets and l2_packet_pcap.c another one
+ * using libpcap and libdnet. When porting %wpa_supplicant to other operating
+ * systems, a new l2_packet implementation may need to be added.
+ */
+
+#ifndef L2_PACKET_H
+#define L2_PACKET_H
+
+/**
+ * struct l2_packet_data - Internal l2_packet data structure
+ *
+ * This structure is used by the l2_packet implementation to store its private
+ * data. Other files use a pointer to this data when calling the l2_packet
+ * functions, but the contents of this structure should not be used directly
+ * outside l2_packet implementation.
+ */
+struct l2_packet_data;
+
+#ifdef _MSC_VER
+#pragma pack(push, 1)
+#endif /* _MSC_VER */
+
+struct l2_ethhdr {
+ u8 h_dest[ETH_ALEN];
+ u8 h_source[ETH_ALEN];
+ u16 h_proto;
+} STRUCT_PACKED;
+
+#ifdef _MSC_VER
+#pragma pack(pop)
+#endif /* _MSC_VER */
+
+/**
+ * l2_packet_init - Initialize l2_packet interface
+ * @ifname: Interface name
+ * @own_addr: Optional own MAC address if available from driver interface or
+ * %NULL if not available
+ * @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()
+ * @l2_hdr: 1 = include layer 2 header, 0 = do not include header
+ * 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. If l2_hdr is set to 0, buf
+ * points to len bytes of the payload after the layer 2 header and similarly,
+ * TX buffers start with payload. This behavior can be changed by setting
+ * l2_hdr=1 to include the layer 2 header in the data buffer.
+ */
+struct l2_packet_data * l2_packet_init(
+ const char *ifname, const u8 *own_addr, unsigned short protocol,
+ void (*rx_callback)(void *ctx, const u8 *src_addr,
+ const u8 *buf, size_t len),
+ void *rx_callback_ctx, int l2_hdr);
+
+/**
+ * l2_packet_deinit - Deinitialize l2_packet interface
+ * @l2: Pointer to internal l2_packet data from l2_packet_init()
+ */
+void l2_packet_deinit(struct l2_packet_data *l2);
+
+/**
+ * l2_packet_get_own_addr - Get own layer 2 address
+ * @l2: Pointer to internal l2_packet data from l2_packet_init()
+ * @addr: Buffer for the own address (6 bytes)
+ * Returns: 0 on success, -1 on failure
+ */
+int l2_packet_get_own_addr(struct l2_packet_data *l2, u8 *addr);
+
+/**
+ * l2_packet_send - Send a packet
+ * @l2: Pointer to internal l2_packet data from l2_packet_init()
+ * @dst_addr: Destination address for the packet (only used if l2_hdr == 0)
+ * @proto: Protocol/ethertype for the packet in host byte order (only used if
+ * l2_hdr == 0)
+ * @buf: Packet contents to be sent; including layer 2 header if l2_hdr was
+ * set to 1 in l2_packet_init() call. Otherwise, only the payload of the packet
+ * is included.
+ * @len: Length of the buffer (including l2 header only if l2_hdr == 1)
+ * Returns: >=0 on success, <0 on failure
+ */
+int l2_packet_send(struct l2_packet_data *l2, const u8 *dst_addr, u16 proto,
+ const u8 *buf, size_t len);
+
+/**
+ * l2_packet_get_ip_addr - Get the current IP address from the interface
+ * @l2: Pointer to internal l2_packet data from l2_packet_init()
+ * @buf: Buffer for the IP address in text format
+ * @len: Maximum buffer length
+ * Returns: 0 on success, -1 on failure
+ *
+ * This function can be used to get the current IP address from the interface
+ * bound to the l2_packet. This is mainly for status information and the IP
+ * address will be stored as an ASCII string. This function is not essential
+ * for %wpa_supplicant operation, so full implementation is not required.
+ * l2_packet implementation will need to define the function, but it can return
+ * -1 if the IP address information is not available.
+ */
+int l2_packet_get_ip_addr(struct l2_packet_data *l2, char *buf, size_t len);
+
+
+/**
+ * l2_packet_notify_auth_start - Notify l2_packet about start of authentication
+ * @l2: Pointer to internal l2_packet data from l2_packet_init()
+ *
+ * This function is called when authentication is expected to start, e.g., when
+ * association has been completed, in order to prepare l2_packet implementation
+ * for EAPOL frames. This function is used mainly if the l2_packet code needs
+ * to do polling in which case it can increasing polling frequency. This can
+ * also be an empty function if the l2_packet implementation does not benefit
+ * from knowing about the starting authentication.
+ */
+void l2_packet_notify_auth_start(struct l2_packet_data *l2);
+
+#endif /* L2_PACKET_H */
diff --git a/src/l2_packet/l2_packet_freebsd.c b/src/l2_packet/l2_packet_freebsd.c
new file mode 100644
index 0000000..d1034aa
--- /dev/null
+++ b/src/l2_packet/l2_packet_freebsd.c
@@ -0,0 +1,285 @@
+/*
+ * WPA Supplicant - Layer2 packet handling with FreeBSD
+ * Copyright (c) 2003-2005, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2005, Sam Leffler <sam@errno.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#include "includes.h"
+#ifdef __APPLE__
+#include <net/bpf.h>
+#endif /* __APPLE__ */
+#include <pcap.h>
+
+#include <sys/ioctl.h>
+#include <sys/sysctl.h>
+
+#include <net/if.h>
+#include <net/if_dl.h>
+#include <net/route.h>
+#include <netinet/in.h>
+
+#include "common.h"
+#include "eloop.h"
+#include "l2_packet.h"
+
+
+static const u8 pae_group_addr[ETH_ALEN] =
+{ 0x01, 0x80, 0xc2, 0x00, 0x00, 0x03 };
+
+struct l2_packet_data {
+ pcap_t *pcap;
+ char ifname[100];
+ u8 own_addr[ETH_ALEN];
+ void (*rx_callback)(void *ctx, const u8 *src_addr,
+ const u8 *buf, size_t len);
+ void *rx_callback_ctx;
+ int l2_hdr; /* whether to include layer 2 (Ethernet) header data
+ * buffers */
+};
+
+
+int l2_packet_get_own_addr(struct l2_packet_data *l2, u8 *addr)
+{
+ os_memcpy(addr, l2->own_addr, ETH_ALEN);
+ return 0;
+}
+
+
+int l2_packet_send(struct l2_packet_data *l2, const u8 *dst_addr, u16 proto,
+ const u8 *buf, size_t len)
+{
+ if (!l2->l2_hdr) {
+ int ret;
+ struct l2_ethhdr *eth = os_malloc(sizeof(*eth) + len);
+ if (eth == NULL)
+ return -1;
+ os_memcpy(eth->h_dest, dst_addr, ETH_ALEN);
+ os_memcpy(eth->h_source, l2->own_addr, ETH_ALEN);
+ eth->h_proto = htons(proto);
+ os_memcpy(eth + 1, buf, len);
+ ret = pcap_inject(l2->pcap, (u8 *) eth, len + sizeof(*eth));
+ os_free(eth);
+ return ret;
+ } else
+ return pcap_inject(l2->pcap, buf, len);
+}
+
+
+static void l2_packet_receive(int sock, void *eloop_ctx, void *sock_ctx)
+{
+ struct l2_packet_data *l2 = eloop_ctx;
+ pcap_t *pcap = sock_ctx;
+ struct pcap_pkthdr hdr;
+ const u_char *packet;
+ struct l2_ethhdr *ethhdr;
+ unsigned char *buf;
+ size_t len;
+
+ packet = pcap_next(pcap, &hdr);
+
+ if (packet == NULL || hdr.caplen < sizeof(*ethhdr))
+ return;
+
+ ethhdr = (struct l2_ethhdr *) packet;
+ if (l2->l2_hdr) {
+ buf = (unsigned char *) ethhdr;
+ len = hdr.caplen;
+ } else {
+ buf = (unsigned char *) (ethhdr + 1);
+ len = hdr.caplen - sizeof(*ethhdr);
+ }
+ l2->rx_callback(l2->rx_callback_ctx, ethhdr->h_source, buf, len);
+}
+
+
+static int l2_packet_init_libpcap(struct l2_packet_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, 0, 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_datalink(l2->pcap) != DLT_EN10MB &&
+ pcap_set_datalink(l2->pcap, DLT_EN10MB) < 0) {
+ fprintf(stderr, "pcap_set_datalink(DLT_EN10MB): %s\n",
+ pcap_geterr(l2->pcap));
+ return -1;
+ }
+ os_snprintf(pcap_filter, sizeof(pcap_filter),
+ "not ether src " MACSTR " and "
+ "( ether dst " MACSTR " or ether dst " MACSTR " ) and "
+ "ether proto 0x%x",
+ MAC2STR(l2->own_addr), /* do not receive own packets */
+ MAC2STR(l2->own_addr), MAC2STR(pae_group_addr),
+ 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);
+ /*
+ * When libpcap uses BPF we must enable "immediate mode" to
+ * receive frames right away; otherwise the system may
+ * buffer them for us.
+ */
+ {
+ unsigned int on = 1;
+ if (ioctl(pcap_fileno(l2->pcap), BIOCIMMEDIATE, &on) < 0) {
+ fprintf(stderr, "%s: cannot enable immediate mode on "
+ "interface %s: %s\n",
+ __func__, l2->ifname, strerror(errno));
+ /* XXX should we fail? */
+ }
+ }
+
+ eloop_register_read_sock(pcap_get_selectable_fd(l2->pcap),
+ l2_packet_receive, l2, l2->pcap);
+
+ return 0;
+}
+
+
+static int eth_get(const char *device, u8 ea[ETH_ALEN])
+{
+ struct if_msghdr *ifm;
+ struct sockaddr_dl *sdl;
+ u_char *p, *buf;
+ size_t len;
+ int mib[] = { CTL_NET, AF_ROUTE, 0, AF_LINK, NET_RT_IFLIST, 0 };
+
+ if (sysctl(mib, 6, NULL, &len, NULL, 0) < 0)
+ return -1;
+ if ((buf = os_malloc(len)) == NULL)
+ return -1;
+ if (sysctl(mib, 6, buf, &len, NULL, 0) < 0) {
+ os_free(buf);
+ return -1;
+ }
+ for (p = buf; p < buf + len; p += ifm->ifm_msglen) {
+ ifm = (struct if_msghdr *)p;
+ sdl = (struct sockaddr_dl *)(ifm + 1);
+ if (ifm->ifm_type != RTM_IFINFO ||
+ (ifm->ifm_addrs & RTA_IFP) == 0)
+ continue;
+ if (sdl->sdl_family != AF_LINK || sdl->sdl_nlen == 0 ||
+ os_memcmp(sdl->sdl_data, device, sdl->sdl_nlen) != 0)
+ continue;
+ os_memcpy(ea, LLADDR(sdl), sdl->sdl_alen);
+ break;
+ }
+ os_free(buf);
+
+ if (p >= buf + len) {
+ errno = ESRCH;
+ return -1;
+ }
+ return 0;
+}
+
+
+struct l2_packet_data * l2_packet_init(
+ const char *ifname, const u8 *own_addr, unsigned short protocol,
+ void (*rx_callback)(void *ctx, const u8 *src_addr,
+ const u8 *buf, size_t len),
+ void *rx_callback_ctx, int l2_hdr)
+{
+ struct l2_packet_data *l2;
+
+ l2 = os_zalloc(sizeof(struct l2_packet_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;
+ l2->l2_hdr = l2_hdr;
+
+ if (eth_get(l2->ifname, l2->own_addr) < 0) {
+ fprintf(stderr, "Failed to get link-level address for "
+ "interface '%s'.\n", l2->ifname);
+ os_free(l2);
+ return NULL;
+ }
+
+ if (l2_packet_init_libpcap(l2, protocol)) {
+ os_free(l2);
+ return NULL;
+ }
+
+ return l2;
+}
+
+
+void l2_packet_deinit(struct l2_packet_data *l2)
+{
+ if (l2 != NULL) {
+ if (l2->pcap) {
+ eloop_unregister_read_sock(
+ pcap_get_selectable_fd(l2->pcap));
+ pcap_close(l2->pcap);
+ }
+ os_free(l2);
+ }
+}
+
+
+int l2_packet_get_ip_addr(struct l2_packet_data *l2, char *buf, size_t len)
+{
+ pcap_if_t *devs, *dev;
+ struct pcap_addr *addr;
+ struct sockaddr_in *saddr;
+ int found = 0;
+ char err[PCAP_ERRBUF_SIZE + 1];
+
+ if (pcap_findalldevs(&devs, err) < 0) {
+ wpa_printf(MSG_DEBUG, "pcap_findalldevs: %s\n", err);
+ return -1;
+ }
+
+ for (dev = devs; dev && !found; dev = dev->next) {
+ if (os_strcmp(dev->name, l2->ifname) != 0)
+ continue;
+
+ addr = dev->addresses;
+ while (addr) {
+ saddr = (struct sockaddr_in *) addr->addr;
+ if (saddr && saddr->sin_family == AF_INET) {
+ os_strlcpy(buf, inet_ntoa(saddr->sin_addr),
+ len);
+ found = 1;
+ break;
+ }
+ addr = addr->next;
+ }
+ }
+
+ pcap_freealldevs(devs);
+
+ return found ? 0 : -1;
+}
+
+
+void l2_packet_notify_auth_start(struct l2_packet_data *l2)
+{
+}
diff --git a/src/l2_packet/l2_packet_linux.c b/src/l2_packet/l2_packet_linux.c
new file mode 100644
index 0000000..9def7ff
--- /dev/null
+++ b/src/l2_packet/l2_packet_linux.c
@@ -0,0 +1,199 @@
+/*
+ * WPA Supplicant - Layer2 packet handling with Linux packet sockets
+ * Copyright (c) 2003-2005, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#include "includes.h"
+#include <sys/ioctl.h>
+#include <netpacket/packet.h>
+#include <net/if.h>
+
+#include "common.h"
+#include "eloop.h"
+#include "l2_packet.h"
+
+
+struct l2_packet_data {
+ int fd; /* packet socket for EAPOL frames */
+ char ifname[IFNAMSIZ + 1];
+ int ifindex;
+ u8 own_addr[ETH_ALEN];
+ void (*rx_callback)(void *ctx, const u8 *src_addr,
+ const u8 *buf, size_t len);
+ void *rx_callback_ctx;
+ int l2_hdr; /* whether to include layer 2 (Ethernet) header data
+ * buffers */
+};
+
+
+int l2_packet_get_own_addr(struct l2_packet_data *l2, u8 *addr)
+{
+ os_memcpy(addr, l2->own_addr, ETH_ALEN);
+ return 0;
+}
+
+
+int l2_packet_send(struct l2_packet_data *l2, const u8 *dst_addr, u16 proto,
+ const u8 *buf, size_t len)
+{
+ int ret;
+ if (l2 == NULL)
+ return -1;
+ if (l2->l2_hdr) {
+ ret = send(l2->fd, buf, len, 0);
+ if (ret < 0)
+ perror("l2_packet_send - send");
+ } else {
+ struct sockaddr_ll ll;
+ os_memset(&ll, 0, sizeof(ll));
+ ll.sll_family = AF_PACKET;
+ ll.sll_ifindex = l2->ifindex;
+ ll.sll_protocol = htons(proto);
+ ll.sll_halen = ETH_ALEN;
+ os_memcpy(ll.sll_addr, dst_addr, ETH_ALEN);
+ ret = sendto(l2->fd, buf, len, 0, (struct sockaddr *) &ll,
+ sizeof(ll));
+ if (ret < 0)
+ perror("l2_packet_send - sendto");
+ }
+ return ret;
+}
+
+
+static void l2_packet_receive(int sock, void *eloop_ctx, void *sock_ctx)
+{
+ struct l2_packet_data *l2 = eloop_ctx;
+ u8 buf[2300];
+ int res;
+ struct sockaddr_ll ll;
+ socklen_t fromlen;
+
+ os_memset(&ll, 0, sizeof(ll));
+ fromlen = sizeof(ll);
+ res = recvfrom(sock, buf, sizeof(buf), 0, (struct sockaddr *) &ll,
+ &fromlen);
+ if (res < 0) {
+ perror("l2_packet_receive - recvfrom");
+ return;
+ }
+
+ l2->rx_callback(l2->rx_callback_ctx, ll.sll_addr, buf, res);
+}
+
+
+struct l2_packet_data * l2_packet_init(
+ const char *ifname, const u8 *own_addr, unsigned short protocol,
+ void (*rx_callback)(void *ctx, const u8 *src_addr,
+ const u8 *buf, size_t len),
+ void *rx_callback_ctx, int l2_hdr)
+{
+ struct l2_packet_data *l2;
+ struct ifreq ifr;
+ struct sockaddr_ll ll;
+
+ l2 = os_zalloc(sizeof(struct l2_packet_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;
+ l2->l2_hdr = l2_hdr;
+
+ l2->fd = socket(PF_PACKET, l2_hdr ? SOCK_RAW : SOCK_DGRAM,
+ htons(protocol));
+ if (l2->fd < 0) {
+ perror("socket(PF_PACKET)");
+ os_free(l2);
+ return NULL;
+ }
+ os_strlcpy(ifr.ifr_name, l2->ifname, sizeof(ifr.ifr_name));
+ if (ioctl(l2->fd, SIOCGIFINDEX, &ifr) < 0) {
+ perror("ioctl[SIOCGIFINDEX]");
+ close(l2->fd);
+ os_free(l2);
+ return NULL;
+ }
+ l2->ifindex = ifr.ifr_ifindex;
+
+ os_memset(&ll, 0, sizeof(ll));
+ ll.sll_family = PF_PACKET;
+ ll.sll_ifindex = ifr.ifr_ifindex;
+ ll.sll_protocol = htons(protocol);
+ if (bind(l2->fd, (struct sockaddr *) &ll, sizeof(ll)) < 0) {
+ perror("bind[PF_PACKET]");
+ close(l2->fd);
+ os_free(l2);
+ return NULL;
+ }
+
+ if (ioctl(l2->fd, SIOCGIFHWADDR, &ifr) < 0) {
+ perror("ioctl[SIOCGIFHWADDR]");
+ close(l2->fd);
+ os_free(l2);
+ return NULL;
+ }
+ os_memcpy(l2->own_addr, ifr.ifr_hwaddr.sa_data, ETH_ALEN);
+
+ eloop_register_read_sock(l2->fd, l2_packet_receive, l2, NULL);
+
+ return l2;
+}
+
+
+void l2_packet_deinit(struct l2_packet_data *l2)
+{
+ if (l2 == NULL)
+ return;
+
+ if (l2->fd >= 0) {
+ eloop_unregister_read_sock(l2->fd);
+ close(l2->fd);
+ }
+
+ os_free(l2);
+}
+
+
+int l2_packet_get_ip_addr(struct l2_packet_data *l2, char *buf, size_t len)
+{
+ int s;
+ struct ifreq ifr;
+ struct sockaddr_in *saddr;
+ size_t res;
+
+ s = socket(PF_INET, SOCK_DGRAM, 0);
+ if (s < 0) {
+ perror("socket");
+ return -1;
+ }
+ os_memset(&ifr, 0, sizeof(ifr));
+ os_strlcpy(ifr.ifr_name, l2->ifname, sizeof(ifr.ifr_name));
+ if (ioctl(s, SIOCGIFADDR, &ifr) < 0) {
+ if (errno != EADDRNOTAVAIL)
+ perror("ioctl[SIOCGIFADDR]");
+ close(s);
+ return -1;
+ }
+ close(s);
+ saddr = (struct sockaddr_in *) &ifr.ifr_addr;
+ if (saddr->sin_family != AF_INET)
+ return -1;
+ res = os_strlcpy(buf, inet_ntoa(saddr->sin_addr), len);
+ if (res >= len)
+ return -1;
+ return 0;
+}
+
+
+void l2_packet_notify_auth_start(struct l2_packet_data *l2)
+{
+}
diff --git a/src/l2_packet/l2_packet_ndis.c b/src/l2_packet/l2_packet_ndis.c
new file mode 100644
index 0000000..7de5880
--- /dev/null
+++ b/src/l2_packet/l2_packet_ndis.c
@@ -0,0 +1,516 @@
+/*
+ * WPA Supplicant - Layer2 packet handling with Microsoft NDISUIO
+ * Copyright (c) 2003-2006, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ *
+ * This implementation requires Windows specific event loop implementation,
+ * i.e., eloop_win.c. In addition, the NDISUIO connection is shared with
+ * driver_ndis.c, so only that driver interface can be used and
+ * CONFIG_USE_NDISUIO must be defined.
+ *
+ * WinXP version of the code uses overlapped I/O and a single threaded design
+ * with callback functions from I/O code. WinCE version uses a separate RX
+ * thread that blocks on ReadFile() whenever the media status is connected.
+ */
+
+#include "includes.h"
+#include <winsock2.h>
+#include <ntddndis.h>
+
+#ifdef _WIN32_WCE
+#include <winioctl.h>
+#include <nuiouser.h>
+#endif /* _WIN32_WCE */
+
+#include "common.h"
+#include "eloop.h"
+#include "l2_packet.h"
+
+#ifndef _WIN32_WCE
+/* from nuiouser.h */
+#define FSCTL_NDISUIO_BASE FILE_DEVICE_NETWORK
+#define _NDISUIO_CTL_CODE(_Function, _Method, _Access) \
+ CTL_CODE(FSCTL_NDISUIO_BASE, _Function, _Method, _Access)
+#define IOCTL_NDISUIO_SET_ETHER_TYPE \
+ _NDISUIO_CTL_CODE(0x202, METHOD_BUFFERED, \
+ FILE_READ_ACCESS | FILE_WRITE_ACCESS)
+#endif /* _WIN32_WCE */
+
+/* From driver_ndis.c to shared the handle to NDISUIO */
+HANDLE driver_ndis_get_ndisuio_handle(void);
+
+/*
+ * NDISUIO supports filtering of only one ethertype at the time, so we must
+ * fake support for two (EAPOL and RSN pre-auth) by switching to pre-auth
+ * whenever wpa_supplicant is trying to pre-authenticate and then switching
+ * back to EAPOL when pre-authentication has been completed.
+ */
+
+struct l2_packet_data;
+
+struct l2_packet_ndisuio_global {
+ int refcount;
+ unsigned short first_proto;
+ struct l2_packet_data *l2[2];
+#ifdef _WIN32_WCE
+ HANDLE rx_thread;
+ HANDLE stop_request;
+ HANDLE ready_for_read;
+ HANDLE rx_processed;
+#endif /* _WIN32_WCE */
+};
+
+static struct l2_packet_ndisuio_global *l2_ndisuio_global = NULL;
+
+struct l2_packet_data {
+ char ifname[100];
+ u8 own_addr[ETH_ALEN];
+ void (*rx_callback)(void *ctx, const u8 *src_addr,
+ const u8 *buf, size_t len);
+ void *rx_callback_ctx;
+ int l2_hdr; /* whether to include layer 2 (Ethernet) header in calls to
+ * rx_callback and l2_packet_send() */
+ HANDLE rx_avail;
+#ifndef _WIN32_WCE
+ OVERLAPPED rx_overlapped;
+#endif /* _WIN32_WCE */
+ u8 rx_buf[1514];
+ DWORD rx_written;
+};
+
+
+int l2_packet_get_own_addr(struct l2_packet_data *l2, u8 *addr)
+{
+ os_memcpy(addr, l2->own_addr, ETH_ALEN);
+ return 0;
+}
+
+
+int l2_packet_send(struct l2_packet_data *l2, const u8 *dst_addr, u16 proto,
+ const u8 *buf, size_t len)
+{
+ BOOL res;
+ DWORD written;
+ struct l2_ethhdr *eth;
+#ifndef _WIN32_WCE
+ OVERLAPPED overlapped;
+#endif /* _WIN32_WCE */
+ OVERLAPPED *o;
+
+ if (l2 == NULL)
+ return -1;
+
+#ifdef _WIN32_WCE
+ o = NULL;
+#else /* _WIN32_WCE */
+ os_memset(&overlapped, 0, sizeof(overlapped));
+ o = &overlapped;
+#endif /* _WIN32_WCE */
+
+ if (l2->l2_hdr) {
+ res = WriteFile(driver_ndis_get_ndisuio_handle(), buf, len,
+ &written, o);
+ } else {
+ size_t mlen = sizeof(*eth) + len;
+ eth = os_malloc(mlen);
+ if (eth == NULL)
+ return -1;
+
+ os_memcpy(eth->h_dest, dst_addr, ETH_ALEN);
+ os_memcpy(eth->h_source, l2->own_addr, ETH_ALEN);
+ eth->h_proto = htons(proto);
+ os_memcpy(eth + 1, buf, len);
+ res = WriteFile(driver_ndis_get_ndisuio_handle(), eth, mlen,
+ &written, o);
+ os_free(eth);
+ }
+
+ if (!res) {
+ DWORD err = GetLastError();
+#ifndef _WIN32_WCE
+ if (err == ERROR_IO_PENDING) {
+ /* For now, just assume that the packet will be sent in
+ * time before the next write happens. This could be
+ * cleaned up at some point to actually wait for
+ * completion before starting new writes.
+ */
+ return 0;
+ }
+#endif /* _WIN32_WCE */
+ wpa_printf(MSG_DEBUG, "L2(NDISUIO): WriteFile failed: %d",
+ (int) GetLastError());
+ return -1;
+ }
+
+ return 0;
+}
+
+
+static void l2_packet_callback(struct l2_packet_data *l2);
+
+#ifdef _WIN32_WCE
+static void l2_packet_rx_thread_try_read(struct l2_packet_data *l2)
+{
+ HANDLE handles[2];
+
+ wpa_printf(MSG_MSGDUMP, "l2_packet_rx_thread: -> ReadFile");
+ if (!ReadFile(driver_ndis_get_ndisuio_handle(), l2->rx_buf,
+ sizeof(l2->rx_buf), &l2->rx_written, NULL)) {
+ DWORD err = GetLastError();
+ wpa_printf(MSG_DEBUG, "l2_packet_rx_thread: ReadFile failed: "
+ "%d", (int) err);
+ /*
+ * ReadFile on NDISUIO/WinCE returns ERROR_DEVICE_NOT_CONNECTED
+ * error whenever the connection is not up. Yield the thread to
+ * avoid triggering a busy loop. Connection event should stop
+ * us from looping for long, but we need to allow enough CPU
+ * for the main thread to process the media disconnection.
+ */
+ Sleep(100);
+ return;
+ }
+
+ wpa_printf(MSG_DEBUG, "l2_packet_rx_thread: Read %d byte packet",
+ (int) l2->rx_written);
+
+ /*
+ * Notify the main thread about the availability of a frame and wait
+ * for the frame to be processed.
+ */
+ SetEvent(l2->rx_avail);
+ handles[0] = l2_ndisuio_global->stop_request;
+ handles[1] = l2_ndisuio_global->rx_processed;
+ WaitForMultipleObjects(2, handles, FALSE, INFINITE);
+ ResetEvent(l2_ndisuio_global->rx_processed);
+}
+
+
+static DWORD WINAPI l2_packet_rx_thread(LPVOID arg)
+{
+ struct l2_packet_data *l2 = arg;
+ DWORD res;
+ HANDLE handles[2];
+ int run = 1;
+
+ wpa_printf(MSG_DEBUG, "L2(NDISUIO): RX thread started");
+ handles[0] = l2_ndisuio_global->stop_request;
+ handles[1] = l2_ndisuio_global->ready_for_read;
+
+ /*
+ * Unfortunately, NDISUIO on WinCE does not seem to support waiting
+ * on the handle. There do not seem to be anything else that we could
+ * wait for either. If one were to modify NDISUIO to set a named event
+ * whenever packets are available, this event could be used here to
+ * avoid having to poll for new packets or we could even move to use a
+ * single threaded design.
+ *
+ * In addition, NDISUIO on WinCE is returning
+ * ERROR_DEVICE_NOT_CONNECTED whenever ReadFile() is attempted while
+ * the adapter is not in connected state. For now, we are just using a
+ * local event to allow ReadFile calls only after having received NDIS
+ * media connect event. This event could be easily converted to handle
+ * another event if the protocol driver is replaced with somewhat more
+ * useful design.
+ */
+
+ while (l2_ndisuio_global && run) {
+ res = WaitForMultipleObjects(2, handles, FALSE, INFINITE);
+ switch (res) {
+ case WAIT_OBJECT_0:
+ wpa_printf(MSG_DEBUG, "l2_packet_rx_thread: Received "
+ "request to stop RX thread");
+ run = 0;
+ break;
+ case WAIT_OBJECT_0 + 1:
+ l2_packet_rx_thread_try_read(l2);
+ break;
+ case WAIT_FAILED:
+ default:
+ wpa_printf(MSG_DEBUG, "l2_packet_rx_thread: "
+ "WaitForMultipleObjects failed: %d",
+ (int) GetLastError());
+ run = 0;
+ break;
+ }
+ }
+
+ wpa_printf(MSG_DEBUG, "L2(NDISUIO): RX thread stopped");
+
+ return 0;
+}
+#else /* _WIN32_WCE */
+static int l2_ndisuio_start_read(struct l2_packet_data *l2, int recursive)
+{
+ os_memset(&l2->rx_overlapped, 0, sizeof(l2->rx_overlapped));
+ l2->rx_overlapped.hEvent = l2->rx_avail;
+ if (!ReadFile(driver_ndis_get_ndisuio_handle(), l2->rx_buf,
+ sizeof(l2->rx_buf), &l2->rx_written, &l2->rx_overlapped))
+ {
+ DWORD err = GetLastError();
+ if (err != ERROR_IO_PENDING) {
+ wpa_printf(MSG_DEBUG, "L2(NDISUIO): ReadFile failed: "
+ "%d", (int) err);
+ return -1;
+ }
+ /*
+ * Once read is completed, l2_packet_rx_event() will be
+ * called.
+ */
+ } else {
+ wpa_printf(MSG_DEBUG, "L2(NDISUIO): ReadFile returned data "
+ "without wait for completion");
+ if (!recursive)
+ l2_packet_callback(l2);
+ }
+
+ return 0;
+}
+#endif /* _WIN32_WCE */
+
+
+static void l2_packet_callback(struct l2_packet_data *l2)
+{
+ const u8 *rx_buf, *rx_src;
+ size_t rx_len;
+ struct l2_ethhdr *ethhdr = (struct l2_ethhdr *) l2->rx_buf;
+
+ wpa_printf(MSG_DEBUG, "L2(NDISUIO): Read %d bytes",
+ (int) l2->rx_written);
+
+ if (l2->l2_hdr || l2->rx_written < sizeof(*ethhdr)) {
+ rx_buf = (u8 *) ethhdr;
+ rx_len = l2->rx_written;
+ } else {
+ rx_buf = (u8 *) (ethhdr + 1);
+ rx_len = l2->rx_written - sizeof(*ethhdr);
+ }
+ rx_src = ethhdr->h_source;
+
+ l2->rx_callback(l2->rx_callback_ctx, rx_src, rx_buf, rx_len);
+#ifndef _WIN32_WCE
+ l2_ndisuio_start_read(l2, 1);
+#endif /* _WIN32_WCE */
+}
+
+
+static void l2_packet_rx_event(void *eloop_data, void *user_data)
+{
+ struct l2_packet_data *l2 = eloop_data;
+
+ if (l2_ndisuio_global)
+ l2 = l2_ndisuio_global->l2[l2_ndisuio_global->refcount - 1];
+
+ ResetEvent(l2->rx_avail);
+
+#ifndef _WIN32_WCE
+ if (!GetOverlappedResult(driver_ndis_get_ndisuio_handle(),
+ &l2->rx_overlapped, &l2->rx_written, FALSE)) {
+ wpa_printf(MSG_DEBUG, "L2(NDISUIO): GetOverlappedResult "
+ "failed: %d", (int) GetLastError());
+ return;
+ }
+#endif /* _WIN32_WCE */
+
+ l2_packet_callback(l2);
+
+#ifdef _WIN32_WCE
+ SetEvent(l2_ndisuio_global->rx_processed);
+#endif /* _WIN32_WCE */
+}
+
+
+static int l2_ndisuio_set_ether_type(unsigned short protocol)
+{
+ USHORT proto = htons(protocol);
+ DWORD written;
+
+ if (!DeviceIoControl(driver_ndis_get_ndisuio_handle(),
+ IOCTL_NDISUIO_SET_ETHER_TYPE, &proto,
+ sizeof(proto), NULL, 0, &written, NULL)) {
+ wpa_printf(MSG_ERROR, "L2(NDISUIO): "
+ "IOCTL_NDISUIO_SET_ETHER_TYPE failed: %d",
+ (int) GetLastError());
+ return -1;
+ }
+
+ return 0;
+}
+
+
+struct l2_packet_data * l2_packet_init(
+ const char *ifname, const u8 *own_addr, unsigned short protocol,
+ void (*rx_callback)(void *ctx, const u8 *src_addr,
+ const u8 *buf, size_t len),
+ void *rx_callback_ctx, int l2_hdr)
+{
+ struct l2_packet_data *l2;
+
+ if (l2_ndisuio_global == NULL) {
+ l2_ndisuio_global = os_zalloc(sizeof(*l2_ndisuio_global));
+ if (l2_ndisuio_global == NULL)
+ return NULL;
+ l2_ndisuio_global->first_proto = protocol;
+ }
+ if (l2_ndisuio_global->refcount >= 2) {
+ wpa_printf(MSG_ERROR, "L2(NDISUIO): Not more than two "
+ "simultaneous connections allowed");
+ return NULL;
+ }
+ l2_ndisuio_global->refcount++;
+
+ l2 = os_zalloc(sizeof(struct l2_packet_data));
+ if (l2 == NULL)
+ return NULL;
+ l2_ndisuio_global->l2[l2_ndisuio_global->refcount - 1] = l2;
+
+ os_strlcpy(l2->ifname, ifname, sizeof(l2->ifname));
+ l2->rx_callback = rx_callback;
+ l2->rx_callback_ctx = rx_callback_ctx;
+ l2->l2_hdr = l2_hdr;
+
+ if (own_addr)
+ os_memcpy(l2->own_addr, own_addr, ETH_ALEN);
+
+ if (l2_ndisuio_set_ether_type(protocol) < 0) {
+ os_free(l2);
+ return NULL;
+ }
+
+ if (l2_ndisuio_global->refcount > 1) {
+ wpa_printf(MSG_DEBUG, "L2(NDISUIO): Temporarily setting "
+ "filtering ethertype to %04x", protocol);
+ if (l2_ndisuio_global->l2[0])
+ l2->rx_avail = l2_ndisuio_global->l2[0]->rx_avail;
+ return l2;
+ }
+
+ l2->rx_avail = CreateEvent(NULL, TRUE, FALSE, NULL);
+ if (l2->rx_avail == NULL) {
+ os_free(l2);
+ return NULL;
+ }
+
+ eloop_register_event(l2->rx_avail, sizeof(l2->rx_avail),
+ l2_packet_rx_event, l2, NULL);
+
+#ifdef _WIN32_WCE
+ l2_ndisuio_global->stop_request = CreateEvent(NULL, TRUE, FALSE, NULL);
+ /*
+ * This event is being set based on media connect/disconnect
+ * notifications in driver_ndis.c.
+ */
+ l2_ndisuio_global->ready_for_read =
+ CreateEvent(NULL, TRUE, FALSE, TEXT("WpaSupplicantConnected"));
+ l2_ndisuio_global->rx_processed = CreateEvent(NULL, TRUE, FALSE, NULL);
+ if (l2_ndisuio_global->stop_request == NULL ||
+ l2_ndisuio_global->ready_for_read == NULL ||
+ l2_ndisuio_global->rx_processed == NULL) {
+ if (l2_ndisuio_global->stop_request) {
+ CloseHandle(l2_ndisuio_global->stop_request);
+ l2_ndisuio_global->stop_request = NULL;
+ }
+ if (l2_ndisuio_global->ready_for_read) {
+ CloseHandle(l2_ndisuio_global->ready_for_read);
+ l2_ndisuio_global->ready_for_read = NULL;
+ }
+ if (l2_ndisuio_global->rx_processed) {
+ CloseHandle(l2_ndisuio_global->rx_processed);
+ l2_ndisuio_global->rx_processed = NULL;
+ }
+ eloop_unregister_event(l2->rx_avail, sizeof(l2->rx_avail));
+ os_free(l2);
+ return NULL;
+ }
+
+ l2_ndisuio_global->rx_thread = CreateThread(NULL, 0,
+ l2_packet_rx_thread, l2, 0,
+ NULL);
+ if (l2_ndisuio_global->rx_thread == NULL) {
+ wpa_printf(MSG_INFO, "L2(NDISUIO): Failed to create RX "
+ "thread: %d", (int) GetLastError());
+ eloop_unregister_event(l2->rx_avail, sizeof(l2->rx_avail));
+ CloseHandle(l2_ndisuio_global->stop_request);
+ l2_ndisuio_global->stop_request = NULL;
+ os_free(l2);
+ return NULL;
+ }
+#else /* _WIN32_WCE */
+ l2_ndisuio_start_read(l2, 0);
+#endif /* _WIN32_WCE */
+
+ return l2;
+}
+
+
+void l2_packet_deinit(struct l2_packet_data *l2)
+{
+ if (l2 == NULL)
+ return;
+
+ if (l2_ndisuio_global) {
+ l2_ndisuio_global->refcount--;
+ l2_ndisuio_global->l2[l2_ndisuio_global->refcount] = NULL;
+ if (l2_ndisuio_global->refcount) {
+ wpa_printf(MSG_DEBUG, "L2(NDISUIO): restore filtering "
+ "ethertype to %04x",
+ l2_ndisuio_global->first_proto);
+ l2_ndisuio_set_ether_type(
+ l2_ndisuio_global->first_proto);
+ return;
+ }
+
+#ifdef _WIN32_WCE
+ wpa_printf(MSG_DEBUG, "L2(NDISUIO): Waiting for RX thread to "
+ "stop");
+ SetEvent(l2_ndisuio_global->stop_request);
+ /*
+ * Cancel pending ReadFile() in the RX thread (if we were still
+ * connected at this point).
+ */
+ if (!DeviceIoControl(driver_ndis_get_ndisuio_handle(),
+ IOCTL_CANCEL_READ, NULL, 0, NULL, 0, NULL,
+ NULL)) {
+ wpa_printf(MSG_DEBUG, "L2(NDISUIO): IOCTL_CANCEL_READ "
+ "failed: %d", (int) GetLastError());
+ /* RX thread will exit blocking ReadFile once NDISUIO
+ * notices that the adapter is disconnected. */
+ }
+ WaitForSingleObject(l2_ndisuio_global->rx_thread, INFINITE);
+ wpa_printf(MSG_DEBUG, "L2(NDISUIO): RX thread exited");
+ CloseHandle(l2_ndisuio_global->rx_thread);
+ CloseHandle(l2_ndisuio_global->stop_request);
+ CloseHandle(l2_ndisuio_global->ready_for_read);
+ CloseHandle(l2_ndisuio_global->rx_processed);
+#endif /* _WIN32_WCE */
+
+ os_free(l2_ndisuio_global);
+ l2_ndisuio_global = NULL;
+ }
+
+#ifndef _WIN32_WCE
+ CancelIo(driver_ndis_get_ndisuio_handle());
+#endif /* _WIN32_WCE */
+
+ eloop_unregister_event(l2->rx_avail, sizeof(l2->rx_avail));
+ CloseHandle(l2->rx_avail);
+ os_free(l2);
+}
+
+
+int l2_packet_get_ip_addr(struct l2_packet_data *l2, char *buf, size_t len)
+{
+ return -1;
+}
+
+
+void l2_packet_notify_auth_start(struct l2_packet_data *l2)
+{
+}
diff --git a/src/l2_packet/l2_packet_none.c b/src/l2_packet/l2_packet_none.c
new file mode 100644
index 0000000..5e3f6e9
--- /dev/null
+++ b/src/l2_packet/l2_packet_none.c
@@ -0,0 +1,123 @@
+/*
+ * WPA Supplicant - Layer2 packet handling example with dummy functions
+ * Copyright (c) 2003-2005, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ *
+ * This file can be used as a starting point for layer2 packet implementation.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "eloop.h"
+#include "l2_packet.h"
+
+
+struct l2_packet_data {
+ char ifname[17];
+ u8 own_addr[ETH_ALEN];
+ void (*rx_callback)(void *ctx, const u8 *src_addr,
+ const u8 *buf, size_t len);
+ void *rx_callback_ctx;
+ int l2_hdr; /* whether to include layer 2 (Ethernet) header data
+ * buffers */
+ int fd;
+};
+
+
+int l2_packet_get_own_addr(struct l2_packet_data *l2, u8 *addr)
+{
+ os_memcpy(addr, l2->own_addr, ETH_ALEN);
+ return 0;
+}
+
+
+int l2_packet_send(struct l2_packet_data *l2, const u8 *dst_addr, u16 proto,
+ const u8 *buf, size_t len)
+{
+ if (l2 == NULL)
+ return -1;
+
+ /*
+ * TODO: Send frame (may need different implementation depending on
+ * whether l2->l2_hdr is set).
+ */
+
+ return 0;
+}
+
+
+static void l2_packet_receive(int sock, void *eloop_ctx, void *sock_ctx)
+{
+ struct l2_packet_data *l2 = eloop_ctx;
+ u8 buf[2300];
+ int res;
+
+ /* TODO: receive frame (e.g., recv() using sock */
+ buf[0] = 0;
+ res = 0;
+
+ l2->rx_callback(l2->rx_callback_ctx, NULL /* TODO: src addr */,
+ buf, res);
+}
+
+
+struct l2_packet_data * l2_packet_init(
+ const char *ifname, const u8 *own_addr, unsigned short protocol,
+ void (*rx_callback)(void *ctx, const u8 *src_addr,
+ const u8 *buf, size_t len),
+ void *rx_callback_ctx, int l2_hdr)
+{
+ struct l2_packet_data *l2;
+
+ l2 = os_zalloc(sizeof(struct l2_packet_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;
+ l2->l2_hdr = l2_hdr;
+
+ /*
+ * TODO: open connection for receiving frames
+ */
+ l2->fd = -1;
+ eloop_register_read_sock(l2->fd, l2_packet_receive, l2, NULL);
+
+ return l2;
+}
+
+
+void l2_packet_deinit(struct l2_packet_data *l2)
+{
+ if (l2 == NULL)
+ return;
+
+ if (l2->fd >= 0) {
+ eloop_unregister_read_sock(l2->fd);
+ /* TODO: close connection */
+ }
+
+ os_free(l2);
+}
+
+
+int l2_packet_get_ip_addr(struct l2_packet_data *l2, char *buf, size_t len)
+{
+ /* TODO: get interface IP address */
+ return -1;
+}
+
+
+void l2_packet_notify_auth_start(struct l2_packet_data *l2)
+{
+ /* This function can be left empty */
+}
diff --git a/src/l2_packet/l2_packet_pcap.c b/src/l2_packet/l2_packet_pcap.c
new file mode 100644
index 0000000..8156e29
--- /dev/null
+++ b/src/l2_packet/l2_packet_pcap.c
@@ -0,0 +1,386 @@
+/*
+ * WPA Supplicant - Layer2 packet handling with libpcap/libdnet and WinPcap
+ * Copyright (c) 2003-2006, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#include "includes.h"
+#ifndef CONFIG_NATIVE_WINDOWS
+#include <sys/ioctl.h>
+#endif /* CONFIG_NATIVE_WINDOWS */
+#include <pcap.h>
+#ifndef CONFIG_WINPCAP
+#include <dnet.h>
+#endif /* CONFIG_WINPCAP */
+
+#include "common.h"
+#include "eloop.h"
+#include "l2_packet.h"
+
+
+static const u8 pae_group_addr[ETH_ALEN] =
+{ 0x01, 0x80, 0xc2, 0x00, 0x00, 0x03 };
+
+struct l2_packet_data {
+ pcap_t *pcap;
+#ifdef CONFIG_WINPCAP
+ unsigned int num_fast_poll;
+#else /* CONFIG_WINPCAP */
+ eth_t *eth;
+#endif /* CONFIG_WINPCAP */
+ char ifname[100];
+ u8 own_addr[ETH_ALEN];
+ void (*rx_callback)(void *ctx, const u8 *src_addr,
+ const u8 *buf, size_t len);
+ void *rx_callback_ctx;
+ int l2_hdr; /* whether to include layer 2 (Ethernet) header in calls
+ * to rx_callback */
+};
+
+
+int l2_packet_get_own_addr(struct l2_packet_data *l2, u8 *addr)
+{
+ os_memcpy(addr, l2->own_addr, ETH_ALEN);
+ return 0;
+}
+
+
+#ifndef CONFIG_WINPCAP
+static int l2_packet_init_libdnet(struct l2_packet_data *l2)
+{
+ eth_addr_t own_addr;
+
+ l2->eth = eth_open(l2->ifname);
+ if (!l2->eth) {
+ printf("Failed to open interface '%s'.\n", l2->ifname);
+ perror("eth_open");
+ return -1;
+ }
+
+ if (eth_get(l2->eth, &own_addr) < 0) {
+ printf("Failed to get own hw address from interface '%s'.\n",
+ l2->ifname);
+ perror("eth_get");
+ eth_close(l2->eth);
+ l2->eth = NULL;
+ return -1;
+ }
+ os_memcpy(l2->own_addr, own_addr.data, ETH_ALEN);
+
+ return 0;
+}
+#endif /* CONFIG_WINPCAP */
+
+
+int l2_packet_send(struct l2_packet_data *l2, const u8 *dst_addr, u16 proto,
+ const u8 *buf, size_t len)
+{
+ int ret;
+ struct l2_ethhdr *eth;
+
+ if (l2 == NULL)
+ return -1;
+
+ if (l2->l2_hdr) {
+#ifdef CONFIG_WINPCAP
+ ret = pcap_sendpacket(l2->pcap, buf, len);
+#else /* CONFIG_WINPCAP */
+ ret = eth_send(l2->eth, buf, len);
+#endif /* CONFIG_WINPCAP */
+ } else {
+ size_t mlen = sizeof(*eth) + len;
+ eth = os_malloc(mlen);
+ if (eth == NULL)
+ return -1;
+
+ os_memcpy(eth->h_dest, dst_addr, ETH_ALEN);
+ os_memcpy(eth->h_source, l2->own_addr, ETH_ALEN);
+ eth->h_proto = htons(proto);
+ os_memcpy(eth + 1, buf, len);
+
+#ifdef CONFIG_WINPCAP
+ ret = pcap_sendpacket(l2->pcap, (u8 *) eth, mlen);
+#else /* CONFIG_WINPCAP */
+ ret = eth_send(l2->eth, (u8 *) eth, mlen);
+#endif /* CONFIG_WINPCAP */
+
+ os_free(eth);
+ }
+
+ return ret;
+}
+
+
+#ifndef CONFIG_WINPCAP
+static void l2_packet_receive(int sock, void *eloop_ctx, void *sock_ctx)
+{
+ struct l2_packet_data *l2 = eloop_ctx;
+ pcap_t *pcap = sock_ctx;
+ struct pcap_pkthdr hdr;
+ const u_char *packet;
+ struct l2_ethhdr *ethhdr;
+ unsigned char *buf;
+ size_t len;
+
+ packet = pcap_next(pcap, &hdr);
+
+ if (packet == NULL || hdr.caplen < sizeof(*ethhdr))
+ return;
+
+ ethhdr = (struct l2_ethhdr *) packet;
+ if (l2->l2_hdr) {
+ buf = (unsigned char *) ethhdr;
+ len = hdr.caplen;
+ } else {
+ buf = (unsigned char *) (ethhdr + 1);
+ len = hdr.caplen - sizeof(*ethhdr);
+ }
+ l2->rx_callback(l2->rx_callback_ctx, ethhdr->h_source, buf, len);
+}
+#endif /* CONFIG_WINPCAP */
+
+
+#ifdef CONFIG_WINPCAP
+static void l2_packet_receive_cb(u_char *user, const struct pcap_pkthdr *hdr,
+ const u_char *pkt_data)
+{
+ struct l2_packet_data *l2 = (struct l2_packet_data *) user;
+ struct l2_ethhdr *ethhdr;
+ unsigned char *buf;
+ size_t len;
+
+ if (pkt_data == NULL || hdr->caplen < sizeof(*ethhdr))
+ return;
+
+ ethhdr = (struct l2_ethhdr *) pkt_data;
+ if (l2->l2_hdr) {
+ buf = (unsigned char *) ethhdr;
+ len = hdr->caplen;
+ } else {
+ buf = (unsigned char *) (ethhdr + 1);
+ len = hdr->caplen - sizeof(*ethhdr);
+ }
+ l2->rx_callback(l2->rx_callback_ctx, ethhdr->h_source, buf, len);
+ /*
+ * Use shorter poll interval for 3 seconds to reduce latency during key
+ * handshake.
+ */
+ l2->num_fast_poll = 3 * 50;
+}
+
+
+static void l2_packet_receive_timeout(void *eloop_ctx, void *timeout_ctx)
+{
+ struct l2_packet_data *l2 = eloop_ctx;
+ pcap_t *pcap = timeout_ctx;
+ int timeout;
+
+ if (l2->num_fast_poll > 0) {
+ timeout = 20000;
+ l2->num_fast_poll--;
+ } else
+ timeout = 100000;
+
+ /* Register new timeout before calling l2_packet_receive() since
+ * receive handler may free this l2_packet instance (which will
+ * cancel this timeout). */
+ eloop_register_timeout(0, timeout, l2_packet_receive_timeout,
+ l2, pcap);
+ pcap_dispatch(pcap, 10, l2_packet_receive_cb, (u_char *) l2);
+}
+#endif /* CONFIG_WINPCAP */
+
+
+static int l2_packet_init_libpcap(struct l2_packet_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;
+
+#ifdef CONFIG_WINPCAP
+ char ifname[128];
+ os_snprintf(ifname, sizeof(ifname), "\\Device\\NPF_%s", l2->ifname);
+ pcap_lookupnet(ifname, &pcap_netp, &pcap_maskp, pcap_err);
+ l2->pcap = pcap_open_live(ifname, 2500, 0, 10, pcap_err);
+ if (l2->pcap == NULL) {
+ fprintf(stderr, "pcap_open_live: %s\n", pcap_err);
+ fprintf(stderr, "ifname='%s'\n", ifname);
+ return -1;
+ }
+ if (pcap_setnonblock(l2->pcap, 1, pcap_err) < 0)
+ fprintf(stderr, "pcap_setnonblock: %s\n",
+ pcap_geterr(l2->pcap));
+#else /* CONFIG_WINPCAP */
+ pcap_lookupnet(l2->ifname, &pcap_netp, &pcap_maskp, pcap_err);
+ l2->pcap = pcap_open_live(l2->ifname, 2500, 0, 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_datalink(l2->pcap) != DLT_EN10MB &&
+ pcap_set_datalink(l2->pcap, DLT_EN10MB) < 0) {
+ fprintf(stderr, "pcap_set_datalink(DLT_EN10MB): %s\n",
+ pcap_geterr(l2->pcap));
+ return -1;
+ }
+#endif /* CONFIG_WINPCAP */
+ os_snprintf(pcap_filter, sizeof(pcap_filter),
+ "not ether src " MACSTR " and "
+ "( ether dst " MACSTR " or ether dst " MACSTR " ) and "
+ "ether proto 0x%x",
+ MAC2STR(l2->own_addr), /* do not receive own packets */
+ MAC2STR(l2->own_addr), MAC2STR(pae_group_addr),
+ 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);
+#ifdef BIOCIMMEDIATE
+ /*
+ * When libpcap uses BPF we must enable "immediate mode" to
+ * receive frames right away; otherwise the system may
+ * buffer them for us.
+ */
+ {
+ unsigned int on = 1;
+ if (ioctl(pcap_fileno(l2->pcap), BIOCIMMEDIATE, &on) < 0) {
+ fprintf(stderr, "%s: cannot enable immediate mode on "
+ "interface %s: %s\n",
+ __func__, l2->ifname, strerror(errno));
+ /* XXX should we fail? */
+ }
+ }
+#endif /* BIOCIMMEDIATE */
+
+#ifdef CONFIG_WINPCAP
+ eloop_register_timeout(0, 100000, l2_packet_receive_timeout,
+ l2, l2->pcap);
+#else /* CONFIG_WINPCAP */
+ eloop_register_read_sock(pcap_get_selectable_fd(l2->pcap),
+ l2_packet_receive, l2, l2->pcap);
+#endif /* CONFIG_WINPCAP */
+
+ return 0;
+}
+
+
+struct l2_packet_data * l2_packet_init(
+ const char *ifname, const u8 *own_addr, unsigned short protocol,
+ void (*rx_callback)(void *ctx, const u8 *src_addr,
+ const u8 *buf, size_t len),
+ void *rx_callback_ctx, int l2_hdr)
+{
+ struct l2_packet_data *l2;
+
+ l2 = os_zalloc(sizeof(struct l2_packet_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;
+ l2->l2_hdr = l2_hdr;
+
+#ifdef CONFIG_WINPCAP
+ if (own_addr)
+ os_memcpy(l2->own_addr, own_addr, ETH_ALEN);
+#else /* CONFIG_WINPCAP */
+ if (l2_packet_init_libdnet(l2))
+ return NULL;
+#endif /* CONFIG_WINPCAP */
+
+ if (l2_packet_init_libpcap(l2, protocol)) {
+#ifndef CONFIG_WINPCAP
+ eth_close(l2->eth);
+#endif /* CONFIG_WINPCAP */
+ os_free(l2);
+ return NULL;
+ }
+
+ return l2;
+}
+
+
+void l2_packet_deinit(struct l2_packet_data *l2)
+{
+ if (l2 == NULL)
+ return;
+
+#ifdef CONFIG_WINPCAP
+ eloop_cancel_timeout(l2_packet_receive_timeout, l2, l2->pcap);
+#else /* CONFIG_WINPCAP */
+ if (l2->eth)
+ eth_close(l2->eth);
+ eloop_unregister_read_sock(pcap_get_selectable_fd(l2->pcap));
+#endif /* CONFIG_WINPCAP */
+ if (l2->pcap)
+ pcap_close(l2->pcap);
+ os_free(l2);
+}
+
+
+int l2_packet_get_ip_addr(struct l2_packet_data *l2, char *buf, size_t len)
+{
+ pcap_if_t *devs, *dev;
+ struct pcap_addr *addr;
+ struct sockaddr_in *saddr;
+ int found = 0;
+ char err[PCAP_ERRBUF_SIZE + 1];
+
+ if (pcap_findalldevs(&devs, err) < 0) {
+ wpa_printf(MSG_DEBUG, "pcap_findalldevs: %s\n", err);
+ return -1;
+ }
+
+ for (dev = devs; dev && !found; dev = dev->next) {
+ if (os_strcmp(dev->name, l2->ifname) != 0)
+ continue;
+
+ addr = dev->addresses;
+ while (addr) {
+ saddr = (struct sockaddr_in *) addr->addr;
+ if (saddr && saddr->sin_family == AF_INET) {
+ os_strlcpy(buf, inet_ntoa(saddr->sin_addr),
+ len);
+ found = 1;
+ break;
+ }
+ addr = addr->next;
+ }
+ }
+
+ pcap_freealldevs(devs);
+
+ return found ? 0 : -1;
+}
+
+
+void l2_packet_notify_auth_start(struct l2_packet_data *l2)
+{
+#ifdef CONFIG_WINPCAP
+ /*
+ * Use shorter poll interval for 3 seconds to reduce latency during key
+ * handshake.
+ */
+ l2->num_fast_poll = 3 * 50;
+ eloop_cancel_timeout(l2_packet_receive_timeout, l2, l2->pcap);
+ eloop_register_timeout(0, 10000, l2_packet_receive_timeout,
+ l2, l2->pcap);
+#endif /* CONFIG_WINPCAP */
+}
diff --git a/src/l2_packet/l2_packet_privsep.c b/src/l2_packet/l2_packet_privsep.c
new file mode 100644
index 0000000..c0e7c49
--- /dev/null
+++ b/src/l2_packet/l2_packet_privsep.c
@@ -0,0 +1,267 @@
+/*
+ * WPA Supplicant - Layer2 packet handling with privilege separation
+ * Copyright (c) 2007, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#include "includes.h"
+#include <sys/un.h>
+
+#include "common.h"
+#include "eloop.h"
+#include "l2_packet.h"
+#include "privsep_commands.h"
+
+
+struct l2_packet_data {
+ int fd; /* UNIX domain socket for privsep access */
+ void (*rx_callback)(void *ctx, const u8 *src_addr,
+ const u8 *buf, size_t len);
+ void *rx_callback_ctx;
+ u8 own_addr[ETH_ALEN];
+ char *own_socket_path;
+ struct sockaddr_un priv_addr;
+};
+
+
+static int wpa_priv_cmd(struct l2_packet_data *l2, int cmd,
+ const void *data, size_t data_len)
+{
+ struct msghdr msg;
+ struct iovec io[2];
+
+ io[0].iov_base = &cmd;
+ io[0].iov_len = sizeof(cmd);
+ io[1].iov_base = (u8 *) data;
+ io[1].iov_len = data_len;
+
+ os_memset(&msg, 0, sizeof(msg));
+ msg.msg_iov = io;
+ msg.msg_iovlen = data ? 2 : 1;
+ msg.msg_name = &l2->priv_addr;
+ msg.msg_namelen = sizeof(l2->priv_addr);
+
+ if (sendmsg(l2->fd, &msg, 0) < 0) {
+ perror("L2: sendmsg(cmd)");
+ return -1;
+ }
+
+ return 0;
+}
+
+
+int l2_packet_get_own_addr(struct l2_packet_data *l2, u8 *addr)
+{
+ os_memcpy(addr, l2->own_addr, ETH_ALEN);
+ return 0;
+}
+
+
+int l2_packet_send(struct l2_packet_data *l2, const u8 *dst_addr, u16 proto,
+ const u8 *buf, size_t len)
+{
+ struct msghdr msg;
+ struct iovec io[4];
+ int cmd = PRIVSEP_CMD_L2_SEND;
+
+ io[0].iov_base = &cmd;
+ io[0].iov_len = sizeof(cmd);
+ io[1].iov_base = &dst_addr;
+ io[1].iov_len = ETH_ALEN;
+ io[2].iov_base = &proto;
+ io[2].iov_len = 2;
+ io[3].iov_base = (u8 *) buf;
+ io[3].iov_len = len;
+
+ os_memset(&msg, 0, sizeof(msg));
+ msg.msg_iov = io;
+ msg.msg_iovlen = 4;
+ msg.msg_name = &l2->priv_addr;
+ msg.msg_namelen = sizeof(l2->priv_addr);
+
+ if (sendmsg(l2->fd, &msg, 0) < 0) {
+ perror("L2: sendmsg(packet_send)");
+ return -1;
+ }
+
+ return 0;
+}
+
+
+static void l2_packet_receive(int sock, void *eloop_ctx, void *sock_ctx)
+{
+ struct l2_packet_data *l2 = eloop_ctx;
+ u8 buf[2300];
+ int res;
+ struct sockaddr_un from;
+ socklen_t fromlen = sizeof(from);
+
+ os_memset(&from, 0, sizeof(from));
+ res = recvfrom(sock, buf, sizeof(buf), 0, (struct sockaddr *) &from,
+ &fromlen);
+ if (res < 0) {
+ perror("l2_packet_receive - recvfrom");
+ return;
+ }
+ if (res < ETH_ALEN) {
+ wpa_printf(MSG_DEBUG, "L2: Too show packet received");
+ return;
+ }
+
+ if (from.sun_family != AF_UNIX ||
+ os_strncmp(from.sun_path, l2->priv_addr.sun_path,
+ sizeof(from.sun_path)) != 0) {
+ wpa_printf(MSG_DEBUG, "L2: Received message from unexpected "
+ "source");
+ return;
+ }
+
+ l2->rx_callback(l2->rx_callback_ctx, buf, buf + ETH_ALEN,
+ res - ETH_ALEN);
+}
+
+
+struct l2_packet_data * l2_packet_init(
+ const char *ifname, const u8 *own_addr, unsigned short protocol,
+ void (*rx_callback)(void *ctx, const u8 *src_addr,
+ const u8 *buf, size_t len),
+ void *rx_callback_ctx, int l2_hdr)
+{
+ struct l2_packet_data *l2;
+ char *own_dir = "/tmp";
+ char *priv_dir = "/var/run/wpa_priv";
+ size_t len;
+ static unsigned int counter = 0;
+ struct sockaddr_un addr;
+ fd_set rfds;
+ struct timeval tv;
+ int res;
+ u8 reply[ETH_ALEN + 1];
+ int reg_cmd[2];
+
+ l2 = os_zalloc(sizeof(struct l2_packet_data));
+ if (l2 == NULL)
+ return NULL;
+ l2->rx_callback = rx_callback;
+ l2->rx_callback_ctx = rx_callback_ctx;
+
+ len = os_strlen(own_dir) + 50;
+ l2->own_socket_path = os_malloc(len);
+ if (l2->own_socket_path == NULL) {
+ os_free(l2);
+ return NULL;
+ }
+ os_snprintf(l2->own_socket_path, len, "%s/wpa_privsep-l2-%d-%d",
+ own_dir, getpid(), counter++);
+
+ l2->priv_addr.sun_family = AF_UNIX;
+ os_snprintf(l2->priv_addr.sun_path, sizeof(l2->priv_addr.sun_path),
+ "%s/%s", priv_dir, ifname);
+
+ l2->fd = socket(PF_UNIX, SOCK_DGRAM, 0);
+ if (l2->fd < 0) {
+ perror("socket(PF_UNIX)");
+ os_free(l2->own_socket_path);
+ l2->own_socket_path = NULL;
+ os_free(l2);
+ return NULL;
+ }
+
+ os_memset(&addr, 0, sizeof(addr));
+ addr.sun_family = AF_UNIX;
+ os_strlcpy(addr.sun_path, l2->own_socket_path, sizeof(addr.sun_path));
+ if (bind(l2->fd, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+ perror("bind(PF_UNIX)");
+ goto fail;
+ }
+
+ reg_cmd[0] = protocol;
+ reg_cmd[1] = l2_hdr;
+ if (wpa_priv_cmd(l2, PRIVSEP_CMD_L2_REGISTER, reg_cmd, sizeof(reg_cmd))
+ < 0) {
+ wpa_printf(MSG_ERROR, "L2: Failed to register with wpa_priv");
+ goto fail;
+ }
+
+ FD_ZERO(&rfds);
+ FD_SET(l2->fd, &rfds);
+ tv.tv_sec = 5;
+ tv.tv_usec = 0;
+ res = select(l2->fd + 1, &rfds, NULL, NULL, &tv);
+ if (res < 0 && errno != EINTR) {
+ perror("select");
+ goto fail;
+ }
+
+ if (FD_ISSET(l2->fd, &rfds)) {
+ res = recv(l2->fd, reply, sizeof(reply), 0);
+ if (res < 0) {
+ perror("recv");
+ goto fail;
+ }
+ } else {
+ wpa_printf(MSG_DEBUG, "L2: Timeout while waiting for "
+ "registration reply");
+ goto fail;
+ }
+
+ if (res != ETH_ALEN) {
+ wpa_printf(MSG_DEBUG, "L2: Unexpected registration reply "
+ "(len=%d)", res);
+ }
+ os_memcpy(l2->own_addr, reply, ETH_ALEN);
+
+ eloop_register_read_sock(l2->fd, l2_packet_receive, l2, NULL);
+
+ return l2;
+
+fail:
+ close(l2->fd);
+ l2->fd = -1;
+ unlink(l2->own_socket_path);
+ os_free(l2->own_socket_path);
+ l2->own_socket_path = NULL;
+ os_free(l2);
+ return NULL;
+}
+
+
+void l2_packet_deinit(struct l2_packet_data *l2)
+{
+ if (l2 == NULL)
+ return;
+
+ if (l2->fd >= 0) {
+ wpa_priv_cmd(l2, PRIVSEP_CMD_L2_UNREGISTER, NULL, 0);
+ eloop_unregister_read_sock(l2->fd);
+ close(l2->fd);
+ }
+
+ if (l2->own_socket_path) {
+ unlink(l2->own_socket_path);
+ os_free(l2->own_socket_path);
+ }
+
+ os_free(l2);
+}
+
+
+int l2_packet_get_ip_addr(struct l2_packet_data *l2, char *buf, size_t len)
+{
+ /* TODO */
+ return -1;
+}
+
+
+void l2_packet_notify_auth_start(struct l2_packet_data *l2)
+{
+ wpa_priv_cmd(l2, PRIVSEP_CMD_L2_NOTIFY_AUTH_START, NULL, 0);
+}
diff --git a/src/l2_packet/l2_packet_winpcap.c b/src/l2_packet/l2_packet_winpcap.c
new file mode 100644
index 0000000..2879da7
--- /dev/null
+++ b/src/l2_packet/l2_packet_winpcap.c
@@ -0,0 +1,340 @@
+/*
+ * WPA Supplicant - Layer2 packet handling with WinPcap RX thread
+ * Copyright (c) 2003-2006, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ *
+ * This l2_packet implementation is explicitly for WinPcap and Windows events.
+ * l2_packet_pcap.c has support for WinPcap, but it requires polling to receive
+ * frames which means relatively long latency for EAPOL RX processing. The
+ * implementation here uses a separate thread to allow WinPcap to be receiving
+ * all the time to reduce latency for EAPOL receiving from about 100 ms to 3 ms
+ * when comparing l2_packet_pcap.c to l2_packet_winpcap.c. Extra sleep of 50 ms
+ * is added in to receive thread whenever no EAPOL frames has been received for
+ * a while. Whenever an EAPOL handshake is expected, this sleep is removed.
+ *
+ * The RX thread receives a frame and signals main thread through Windows event
+ * about the availability of a new frame. Processing the received frame is
+ * synchronized with pair of Windows events so that no extra buffer or queuing
+ * mechanism is needed. This implementation requires Windows specific event
+ * loop implementation, i.e., eloop_win.c.
+ *
+ * WinPcap has pcap_getevent() that could, in theory at least, be used to
+ * implement this kind of waiting with a simpler single-thread design. However,
+ * that event handle is not really signaled immediately when receiving each
+ * frame, so it does not really work for this kind of use.
+ */
+
+#include "includes.h"
+#include <pcap.h>
+
+#include "common.h"
+#include "eloop.h"
+#include "l2_packet.h"
+
+
+static const u8 pae_group_addr[ETH_ALEN] =
+{ 0x01, 0x80, 0xc2, 0x00, 0x00, 0x03 };
+
+/*
+ * Number of pcap_dispatch() iterations to do without extra wait after each
+ * received EAPOL packet or authentication notification. This is used to reduce
+ * latency for EAPOL receive.
+ */
+static const size_t no_wait_count = 750;
+
+struct l2_packet_data {
+ pcap_t *pcap;
+ unsigned int num_fast_poll;
+ char ifname[100];
+ u8 own_addr[ETH_ALEN];
+ void (*rx_callback)(void *ctx, const u8 *src_addr,
+ const u8 *buf, size_t len);
+ void *rx_callback_ctx;
+ int l2_hdr; /* whether to include layer 2 (Ethernet) header in calls to
+ * rx_callback and l2_packet_send() */
+ int running;
+ HANDLE rx_avail, rx_done, rx_thread, rx_thread_done, rx_notify;
+ u8 *rx_buf, *rx_src;
+ size_t rx_len;
+ size_t rx_no_wait;
+};
+
+
+int l2_packet_get_own_addr(struct l2_packet_data *l2, u8 *addr)
+{
+ os_memcpy(addr, l2->own_addr, ETH_ALEN);
+ return 0;
+}
+
+
+int l2_packet_send(struct l2_packet_data *l2, const u8 *dst_addr, u16 proto,
+ const u8 *buf, size_t len)
+{
+ int ret;
+ struct l2_ethhdr *eth;
+
+ if (l2 == NULL)
+ return -1;
+
+ if (l2->l2_hdr) {
+ ret = pcap_sendpacket(l2->pcap, buf, len);
+ } else {
+ size_t mlen = sizeof(*eth) + len;
+ eth = os_malloc(mlen);
+ if (eth == NULL)
+ return -1;
+
+ os_memcpy(eth->h_dest, dst_addr, ETH_ALEN);
+ os_memcpy(eth->h_source, l2->own_addr, ETH_ALEN);
+ eth->h_proto = htons(proto);
+ os_memcpy(eth + 1, buf, len);
+ ret = pcap_sendpacket(l2->pcap, (u8 *) eth, mlen);
+ os_free(eth);
+ }
+
+ return ret;
+}
+
+
+/* pcap_dispatch() callback for the RX thread */
+static void l2_packet_receive_cb(u_char *user, const struct pcap_pkthdr *hdr,
+ const u_char *pkt_data)
+{
+ struct l2_packet_data *l2 = (struct l2_packet_data *) user;
+ struct l2_ethhdr *ethhdr;
+
+ if (pkt_data == NULL || hdr->caplen < sizeof(*ethhdr))
+ return;
+
+ ethhdr = (struct l2_ethhdr *) pkt_data;
+ if (l2->l2_hdr) {
+ l2->rx_buf = (u8 *) ethhdr;
+ l2->rx_len = hdr->caplen;
+ } else {
+ l2->rx_buf = (u8 *) (ethhdr + 1);
+ l2->rx_len = hdr->caplen - sizeof(*ethhdr);
+ }
+ l2->rx_src = ethhdr->h_source;
+ SetEvent(l2->rx_avail);
+ WaitForSingleObject(l2->rx_done, INFINITE);
+ ResetEvent(l2->rx_done);
+ l2->rx_no_wait = no_wait_count;
+}
+
+
+/* main RX loop that is running in a separate thread */
+static DWORD WINAPI l2_packet_receive_thread(LPVOID arg)
+{
+ struct l2_packet_data *l2 = arg;
+
+ while (l2->running) {
+ pcap_dispatch(l2->pcap, 1, l2_packet_receive_cb,
+ (u_char *) l2);
+ if (l2->rx_no_wait > 0)
+ l2->rx_no_wait--;
+ if (WaitForSingleObject(l2->rx_notify,
+ l2->rx_no_wait ? 0 : 50) ==
+ WAIT_OBJECT_0) {
+ l2->rx_no_wait = no_wait_count;
+ ResetEvent(l2->rx_notify);
+ }
+ }
+ SetEvent(l2->rx_thread_done);
+ ExitThread(0);
+ return 0;
+}
+
+
+/* main thread RX event handler */
+static void l2_packet_rx_event(void *eloop_data, void *user_data)
+{
+ struct l2_packet_data *l2 = eloop_data;
+ l2->rx_callback(l2->rx_callback_ctx, l2->rx_src, l2->rx_buf,
+ l2->rx_len);
+ ResetEvent(l2->rx_avail);
+ SetEvent(l2->rx_done);
+}
+
+
+static int l2_packet_init_libpcap(struct l2_packet_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, 0, 1, pcap_err);
+ if (l2->pcap == NULL) {
+ fprintf(stderr, "pcap_open_live: %s\n", pcap_err);
+ fprintf(stderr, "ifname='%s'\n", l2->ifname);
+ return -1;
+ }
+ os_snprintf(pcap_filter, sizeof(pcap_filter),
+ "not ether src " MACSTR " and "
+ "( ether dst " MACSTR " or ether dst " MACSTR " ) and "
+ "ether proto 0x%x",
+ MAC2STR(l2->own_addr), /* do not receive own packets */
+ MAC2STR(l2->own_addr), MAC2STR(pae_group_addr),
+ 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);
+
+ return 0;
+}
+
+
+struct l2_packet_data * l2_packet_init(
+ const char *ifname, const u8 *own_addr, unsigned short protocol,
+ void (*rx_callback)(void *ctx, const u8 *src_addr,
+ const u8 *buf, size_t len),
+ void *rx_callback_ctx, int l2_hdr)
+{
+ struct l2_packet_data *l2;
+ DWORD thread_id;
+
+ l2 = os_zalloc(sizeof(struct l2_packet_data));
+ if (l2 == NULL)
+ return NULL;
+ if (os_strncmp(ifname, "\\Device\\NPF_", 12) == 0)
+ os_strlcpy(l2->ifname, ifname, sizeof(l2->ifname));
+ else
+ os_snprintf(l2->ifname, sizeof(l2->ifname), "\\Device\\NPF_%s",
+ ifname);
+ l2->rx_callback = rx_callback;
+ l2->rx_callback_ctx = rx_callback_ctx;
+ l2->l2_hdr = l2_hdr;
+
+ if (own_addr)
+ os_memcpy(l2->own_addr, own_addr, ETH_ALEN);
+
+ if (l2_packet_init_libpcap(l2, protocol)) {
+ os_free(l2);
+ return NULL;
+ }
+
+ l2->rx_avail = CreateEvent(NULL, TRUE, FALSE, NULL);
+ l2->rx_done = CreateEvent(NULL, TRUE, FALSE, NULL);
+ l2->rx_notify = CreateEvent(NULL, TRUE, FALSE, NULL);
+ if (l2->rx_avail == NULL || l2->rx_done == NULL ||
+ l2->rx_notify == NULL) {
+ CloseHandle(l2->rx_avail);
+ CloseHandle(l2->rx_done);
+ CloseHandle(l2->rx_notify);
+ pcap_close(l2->pcap);
+ os_free(l2);
+ return NULL;
+ }
+
+ eloop_register_event(l2->rx_avail, sizeof(l2->rx_avail),
+ l2_packet_rx_event, l2, NULL);
+
+ l2->running = 1;
+ l2->rx_thread = CreateThread(NULL, 0, l2_packet_receive_thread, l2, 0,
+ &thread_id);
+
+ return l2;
+}
+
+
+static void l2_packet_deinit_timeout(void *eloop_ctx, void *timeout_ctx)
+{
+ struct l2_packet_data *l2 = eloop_ctx;
+
+ if (l2->rx_thread_done &&
+ WaitForSingleObject(l2->rx_thread_done, 2000) != WAIT_OBJECT_0) {
+ wpa_printf(MSG_DEBUG, "l2_packet_winpcap: RX thread did not "
+ "exit - kill it\n");
+ TerminateThread(l2->rx_thread, 0);
+ }
+ CloseHandle(l2->rx_thread_done);
+ CloseHandle(l2->rx_thread);
+ if (l2->pcap)
+ pcap_close(l2->pcap);
+ eloop_unregister_event(l2->rx_avail, sizeof(l2->rx_avail));
+ CloseHandle(l2->rx_avail);
+ CloseHandle(l2->rx_done);
+ CloseHandle(l2->rx_notify);
+ os_free(l2);
+}
+
+
+void l2_packet_deinit(struct l2_packet_data *l2)
+{
+ if (l2 == NULL)
+ return;
+
+ l2->rx_thread_done = CreateEvent(NULL, TRUE, FALSE, NULL);
+
+ l2->running = 0;
+ pcap_breakloop(l2->pcap);
+
+ /*
+ * RX thread may be waiting in l2_packet_receive_cb() for l2->rx_done
+ * event and this event is set in l2_packet_rx_event(). However,
+ * l2_packet_deinit() may end up being called from l2->rx_callback(),
+ * so we need to return from here and complete deinitialization in
+ * a registered timeout to avoid having to forcefully kill the RX
+ * thread.
+ */
+ eloop_register_timeout(0, 0, l2_packet_deinit_timeout, l2, NULL);
+}
+
+
+int l2_packet_get_ip_addr(struct l2_packet_data *l2, char *buf, size_t len)
+{
+ pcap_if_t *devs, *dev;
+ struct pcap_addr *addr;
+ struct sockaddr_in *saddr;
+ int found = 0;
+ char err[PCAP_ERRBUF_SIZE + 1];
+
+ if (pcap_findalldevs(&devs, err) < 0) {
+ wpa_printf(MSG_DEBUG, "pcap_findalldevs: %s\n", err);
+ return -1;
+ }
+
+ for (dev = devs; dev && !found; dev = dev->next) {
+ if (os_strcmp(dev->name, l2->ifname) != 0)
+ continue;
+
+ addr = dev->addresses;
+ while (addr) {
+ saddr = (struct sockaddr_in *) addr->addr;
+ if (saddr && saddr->sin_family == AF_INET) {
+ os_strlcpy(buf, inet_ntoa(saddr->sin_addr),
+ len);
+ found = 1;
+ break;
+ }
+ addr = addr->next;
+ }
+ }
+
+ pcap_freealldevs(devs);
+
+ return found ? 0 : -1;
+}
+
+
+void l2_packet_notify_auth_start(struct l2_packet_data *l2)
+{
+ SetEvent(l2->rx_notify);
+}