summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--usr.sbin/hostapd/Makefile14
-rw-r--r--usr.sbin/hostapd/apme.c168
-rw-r--r--usr.sbin/hostapd/hostapd.8163
-rw-r--r--usr.sbin/hostapd/hostapd.c453
-rw-r--r--usr.sbin/hostapd/hostapd.conf.595
-rw-r--r--usr.sbin/hostapd/hostapd.h191
-rw-r--r--usr.sbin/hostapd/iapp.c209
-rw-r--r--usr.sbin/hostapd/llc.c102
-rw-r--r--usr.sbin/hostapd/parse.y562
-rw-r--r--usr.sbin/hostapd/privsep.c484
10 files changed, 2441 insertions, 0 deletions
diff --git a/usr.sbin/hostapd/Makefile b/usr.sbin/hostapd/Makefile
new file mode 100644
index 00000000000..b34d30b6606
--- /dev/null
+++ b/usr.sbin/hostapd/Makefile
@@ -0,0 +1,14 @@
+# $OpenBSD: Makefile,v 1.1 2005/04/13 18:12:23 reyk Exp $
+
+PROG= hostapd
+SRCS= privsep.c apme.c iapp.c llc.c hostapd.c parse.y
+MAN= hostapd.8 hostapd.conf.5
+LDADD= ${LIBEVENT}
+CFLAGS+= -Wall -pedantic -I${.CURDIR}
+CFLAGS+= -Wstrict-prototypes -Wmissing-prototypes
+CFLAGS+= -Wmissing-declarations
+CFLAGS+= -Wshadow -Wpointer-arith -Wcast-qual
+CFLAGS+= -Wsign-compare
+CLEANFILES= y.tab.h
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/hostapd/apme.c b/usr.sbin/hostapd/apme.c
new file mode 100644
index 00000000000..e131924b471
--- /dev/null
+++ b/usr.sbin/hostapd/apme.c
@@ -0,0 +1,168 @@
+/* $OpenBSD: apme.c,v 1.1 2005/04/13 18:12:23 reyk Exp $ */
+
+/*
+ * Copyright (c) 2004, 2005 Reyk Floeter <reyk@vantronix.net>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <sys/param.h>
+#include <sys/ioctl.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/time.h>
+
+#include <net/if.h>
+#include <net/if_dl.h>
+#include <net/if_media.h>
+#include <net/if_arp.h>
+#include <net/if_llc.h>
+#include <net/bpf.h>
+
+#include <netinet/in.h>
+#include <netinet/if_ether.h>
+#include <arpa/inet.h>
+
+#include <fcntl.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "hostapd.h"
+
+void hostapd_apme_frame(struct hostapd_config *, u_int8_t *, u_int);
+
+void
+hostapd_apme_input(int fd, short sig, void *arg)
+{
+ struct hostapd_config *cfg = (struct hostapd_config *)arg;
+ u_int8_t buf[IAPP_MAXSIZE], *bp, *ep;
+ struct bpf_hdr *bph;
+ ssize_t len;
+
+ /* Ignore invalid signals */
+ if (sig != EV_READ)
+ return;
+
+ bzero(&buf, sizeof(buf));
+
+ if ((len = read(fd, buf, sizeof(buf))) <
+ (ssize_t)sizeof(struct ieee80211_frame))
+ return;
+
+ /*
+ * Loop through each frame.
+ */
+
+ bp = (u_int8_t *)&buf;
+ bph = (struct bpf_hdr *)bp;
+ ep = bp + len;
+
+ while (bp < ep) {
+ register int caplen, hdrlen;
+ caplen = bph->bh_caplen;
+ hdrlen = bph->bh_hdrlen;
+
+ /* Process frame */
+ hostapd_apme_frame(cfg, bp + hdrlen, caplen);
+
+ bp += BPF_WORDALIGN(caplen + hdrlen);
+ }
+}
+
+void
+hostapd_apme_frame(struct hostapd_config *cfg, u_int8_t *buf, u_int len)
+{
+ struct hostapd_node node;
+ struct ieee80211_frame *wh = (struct ieee80211_frame *)buf;
+
+ /* Ignore short frames or fragments */
+ if (len < sizeof(struct ieee80211_frame))
+ return;
+
+ /*
+ * Only accept local association response frames, ...
+ */
+ if (!((wh->i_fc[1] & IEEE80211_FC1_DIR_MASK) ==
+ IEEE80211_FC1_DIR_NODS &&
+ (wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK) ==
+ IEEE80211_FC0_TYPE_MGT &&
+ (wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK) ==
+ IEEE80211_FC0_SUBTYPE_ASSOC_RESP))
+ return;
+
+ /*
+ * ...sent by the Host AP (addr2) to our BSSID (addr3)
+ */
+ if (bcmp(wh->i_addr2, cfg->c_apme_bssid, IEEE80211_ADDR_LEN) != 0 ||
+ bcmp(wh->i_addr3, cfg->c_apme_bssid, IEEE80211_ADDR_LEN) != 0)
+ return;
+
+ cfg->c_stats.cn_rx_apme++;
+
+ /*
+ * Double-check if the station got associated to our Host AP
+ */
+ bcopy(wh->i_addr1, node.ni_macaddr, IEEE80211_ADDR_LEN);
+ if (hostapd_priv_apme_getnode(cfg, &node) != 0) {
+ hostapd_log(HOSTAPD_LOG_DEBUG,
+ "%s/%s: invalid association from %s on the Host AP\n",
+ cfg->c_apme_iface, cfg->c_iapp_iface,
+ ether_ntoa((struct ether_addr*)wh->i_addr1));
+ return;
+ }
+
+ /* Call ADD.notify handler */
+ hostapd_iapp_add_notify(cfg, &node);
+}
+
+void
+hostapd_apme_init(struct hostapd_config *cfg)
+{
+ u_int i, dlt;
+ struct ifreq ifr;
+
+ cfg->c_apme_raw = hostapd_bpf_open(O_RDONLY);
+
+ cfg->c_apme_rawlen = IAPP_MAXSIZE;
+ if (ioctl(cfg->c_apme_raw, BIOCSBLEN, &cfg->c_apme_rawlen) == -1)
+ hostapd_fatal("failed to set BPF buffer len \"%s\": %s\n",
+ cfg->c_apme_iface, strerror(errno));
+
+ i = 1;
+ if (ioctl(cfg->c_apme_raw, BIOCIMMEDIATE, &i) == -1)
+ hostapd_fatal("failed to set BPF immediate mode on \"%s\": %s\n",
+ cfg->c_apme_iface, strerror(errno));
+
+ bzero(&ifr, sizeof(struct ifreq));
+ strlcpy(ifr.ifr_name, cfg->c_apme_iface, sizeof(ifr.ifr_name));
+
+ /* This may fail, ignore it */
+ ioctl(cfg->c_apme_raw, BIOCPROMISC, NULL);
+
+ /* Associate the wireless network interface to the BPF descriptor */
+ if (ioctl(cfg->c_apme_raw, BIOCSETIF, &ifr) == -1)
+ hostapd_fatal("failed to set BPF interface \"%s\": %s\n",
+ cfg->c_apme_iface, strerror(errno));
+
+ dlt = IAPP_DLT;
+ if (ioctl(cfg->c_apme_raw, BIOCSDLT, &dlt) == -1)
+ hostapd_fatal("failed to set BPF link type on \"%s\": %s\n",
+ cfg->c_apme_iface, strerror(errno));
+
+ /* Lock the BPF descriptor, no further configuration */
+ if (ioctl(cfg->c_apme_raw, BIOCLOCK, NULL) == -1)
+ hostapd_fatal("failed to lock BPF interface on \"%s\": %s\n",
+ cfg->c_apme_iface, strerror(errno));
+}
diff --git a/usr.sbin/hostapd/hostapd.8 b/usr.sbin/hostapd/hostapd.8
new file mode 100644
index 00000000000..89f7ab43cdb
--- /dev/null
+++ b/usr.sbin/hostapd/hostapd.8
@@ -0,0 +1,163 @@
+.\" $OpenBSD: hostapd.8,v 1.1 2005/04/13 18:12:23 reyk Exp $
+.\"
+.\" Copyright (c) 2004, 2005 Reyk Floeter <reyk@vantronix.net>
+.\"
+.\" Permission to use, copy, modify, and distribute this software for any
+.\" purpose with or without fee is hereby granted, provided that the above
+.\" copyright notice and this permission notice appear in all copies.
+.\"
+.\" THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+.\" WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+.\" MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+.\" ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+.\" WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+.\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+.\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+.\"
+.Dd November 13, 2004
+.Dt HOSTAPD 8
+.Os
+.Sh NAME
+.Nm hostapd
+.Nd Host Access Point daemon
+.Sh SYNOPSIS
+.Nm hostapd
+.Op Fl dvb
+.Op Fl a Ar interface
+.Oo Xo
+.Fl D Ar macro Ns = Ns Ar value Oc
+.Xc
+.Op Fl f Ar file
+.Op Fl i Ar interface
+.Sh DESCRIPTION
+.Nm
+is an daemon which allows communication between different 802.11
+wireless access points running in
+.Pa Host AP
+mode.
+.Pp
+.Nm
+implements the Inter Access Points Protocol (IAPP).
+It's purpose is to exchange station association updates between access
+points in large wireless networks.
+IAPP has been designed to speed up roaming between different access
+points in the same
+.Pa BSS .
+IAPP is described in the
+.Pa IEEE 802.11f
+standard.
+.Pp
+.Nm
+additionally allows to monitor and to log stations associations on a
+non-hostap host which is receiving IAPP messages.
+.Pp
+.Nm
+uses two network interfaces as options on startup.
+The first interface
+.Pa ( Fl a Ar interface )
+is used to access the
+.Pa Host AP ,
+which is a wireless interface running in Host AP mode. Please use
+.Xr ifconfig 4
+to enable Host AP mode. The second interface
+.Pa ( Fl i Ar interface )
+is used to communicate with other
+.Nm
+in the same broadcast domain or multicast group.
+Usually a wired interface is used to communicate with other
+.Nm .
+.Pp
+The
+.Nm
+will send an
+.Pa ADD.notify
+IAPP message if a new station has been associated successfully to the
+.Pa Host AP .
+If the
+.Nm
+receives
+.Pa ADD.notify
+messages it will request the
+.Pa Host AP
+to remove a station which has been associated to another access point.
+.Pp
+The options are as follows:
+.Bl -tag -width Ds
+.It Fl a Ar interface
+Specify the 802.11 wireless network interface running in
+.Pa Host AP
+mode.
+This this option could be omitted to use the
+.Nm
+to log received
+.Pa IAPP
+messages.
+.It Fl b
+Use UDP broadcast instead of multicast group 224.0.1.178 for IAPP
+messages.
+.It Fl D Ar macro Ns = Ns Ar value
+Define
+.Ar macro
+to be set to
+.Ar value
+on the command line.
+Overrides the definition of
+.Ar macro
+in the configuration file.
+.It Fl d
+Do not daemonize and log to
+.Em stderr .
+.It Fl f Ar file
+Use
+.Ar file
+as the configuration file, instead of the default
+.Pa /etc/hostapd.conf .
+.It Fl i Ar interface
+Specify the network interface used to send
+.Pa IAPP
+messages.
+It is important that the IAPP interface is on a trusted
+network because there is no authentication and an attacker could force
+disassociation of selected stations on all listening access points.
+.It Fl v
+Produce more verbose output.
+.El
+.Sh FILES
+.Bl -tag -width "/etc/hostapd.confXXX" -compact
+.It Pa /etc/hostapd.conf
+default
+.Nm
+configuration file
+.El
+.Sh SEE ALSO
+.Xr hostapd.conf 5
+.Xr ifconfig 4
+.Rs
+.%R IEEE 802.11F
+.%T Inter Access Point Protocol
+.%D March 2001
+.Re
+.Sh CAVEATS
+.Nm
+depends on drivers using the
+.Pa net80211
+kernel wireless layer with support of
+.Pa Host AP mode.
+For traditional reasons,
+the
+.Xr wi 4
+driver still uses it's own Host AP code in
+.Pa if_wi_hostap
+which is not supported by
+.Nm .
+.Sh HISTORY
+The
+.Nm
+program first appeared on the 21th Chaos Communication Congress
+(http://www.ccc.de/congress/2004/) and later in
+.Ox 3.8 .
+.Sh AUTHORS
+The
+.Nm
+program was written by
+.An Reyk Floeter Aq reyk@openbsd.org .
diff --git a/usr.sbin/hostapd/hostapd.c b/usr.sbin/hostapd/hostapd.c
new file mode 100644
index 00000000000..05b2d8f71ae
--- /dev/null
+++ b/usr.sbin/hostapd/hostapd.c
@@ -0,0 +1,453 @@
+/* $OpenBSD: hostapd.c,v 1.1 2005/04/13 18:12:23 reyk Exp $ */
+
+/*
+ * Copyright (c) 2004, 2005 Reyk Floeter <reyk@vantronix.net>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <sys/param.h>
+#include <sys/ioctl.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/time.h>
+#include <sys/queue.h>
+#include <sys/stat.h>
+
+#include <net/if.h>
+#include <net/if_dl.h>
+#include <net/if_media.h>
+#include <net/if_arp.h>
+#include <net/if_llc.h>
+#include <net/bpf.h>
+
+#include <netinet/in.h>
+#include <netinet/if_ether.h>
+#include <arpa/inet.h>
+
+#include <errno.h>
+#include <event.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "hostapd.h"
+
+void hostapd_usage(void);
+void hostapd_udp_init(struct hostapd_config *);
+void hostapd_sig_handler(int);
+
+struct hostapd_config hostapd_cfg;
+
+extern char *__progname;
+
+/* defined in event(3) to terminate the event loop */
+extern volatile sig_atomic_t event_gotterm;
+
+void
+hostapd_usage(void)
+{
+ fprintf(stderr, "usage: %s [-dvb] [-a interface] [-D macro=value] "
+ "[-f file] [-i interface]\n",
+ __progname);
+ exit(EXIT_FAILURE);
+}
+
+void
+hostapd_log(u_int level, const char *fmt, ...)
+{
+ va_list ap;
+
+ if (level > hostapd_cfg.c_verbose)
+ return;
+
+ va_start(ap, fmt);
+ if (hostapd_cfg.c_debug)
+ vfprintf(stderr, fmt, ap);
+ else
+ vsyslog(LOG_INFO, fmt, ap);
+ fflush(stderr);
+ va_end(ap);
+}
+
+void
+hostapd_fatal(const char *fmt, ...)
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+ if (hostapd_cfg.c_debug)
+ vfprintf(stderr, fmt, ap);
+ else
+ vsyslog(LOG_ERR, fmt, ap);
+ fflush(stderr);
+ va_end(ap);
+
+ hostapd_cleanup(&hostapd_cfg);
+
+ exit(EXIT_FAILURE);
+}
+
+int
+hostapd_check_file_secrecy(int fd, const char *fname)
+{
+ struct stat st;
+
+ if (fstat(fd, &st)) {
+ hostapd_log(HOSTAPD_LOG,
+ "cannot stat %s\n", fname);
+ return (-1);
+ }
+
+ if (st.st_uid != 0 && st.st_uid != getuid()) {
+ hostapd_log(HOSTAPD_LOG,
+ "%s: owner not root or current user\n", fname);
+ return (-1);
+ }
+
+ if (st.st_mode & (S_IRWXG | S_IRWXO)) {
+ hostapd_log(HOSTAPD_LOG,
+ "%s: group/world readable/writeable\n", fname);
+ return (-1);
+ }
+
+ return (0);
+}
+
+int
+hostapd_bpf_open(u_int flags)
+{
+ u_int i;
+ int fd = -1;
+ char *dev;
+ struct bpf_version bpv;
+
+ /*
+ * Try to open the next available BPF device
+ */
+ for (i = 0; i < 255; i++) {
+ if (asprintf(&dev, "/dev/bpf%u", i) == -1)
+ hostapd_fatal("failed to allocate buffer\n");
+
+ if ((fd = open(dev, flags)) != -1)
+ break;
+
+ free(dev);
+ }
+
+ if (fd == -1) {
+ free(dev);
+ hostapd_fatal("unable to open BPF device\n");
+ }
+
+ free(dev);
+
+ /*
+ * Get and validate the BPF version
+ */
+
+ if (ioctl(fd, BIOCVERSION, &bpv) == -1)
+ hostapd_fatal("failed to get BPF version: %s\n",
+ strerror(errno));
+
+ if (bpv.bv_major != BPF_MAJOR_VERSION ||
+ bpv.bv_minor < BPF_MINOR_VERSION)
+ hostapd_fatal("invalid BPF version\n");
+
+ return (fd);
+}
+
+void
+hostapd_udp_init(struct hostapd_config *cfg)
+{
+ struct ifreq ifr;
+ struct sockaddr_in *addr, baddr;
+ struct ip_mreq mreq;
+ int brd = 1;
+
+ bzero(&ifr, sizeof(ifr));
+
+ /*
+ * Open a listening UDP socket
+ */
+
+ if ((cfg->c_iapp_udp = socket(AF_INET, SOCK_DGRAM, 0)) == -1)
+ hostapd_fatal("unable to open udp socket\n");
+
+ cfg->c_flags |= HOSTAPD_CFG_F_UDP;
+
+ strlcpy(ifr.ifr_name, cfg->c_iapp_iface, sizeof(ifr.ifr_name));
+
+ if (ioctl(cfg->c_iapp_udp, SIOCGIFADDR, &ifr) == -1)
+ hostapd_fatal("UDP ioctl %s on \"%s\" failed: %s\n",
+ "SIOCGIFADDR", ifr.ifr_name, strerror(errno));
+
+ addr = (struct sockaddr_in *)&ifr.ifr_addr;
+ cfg->c_iapp_addr.sin_family = AF_INET;
+ cfg->c_iapp_addr.sin_addr.s_addr = addr->sin_addr.s_addr;
+ cfg->c_iapp_addr.sin_port = htons(IAPP_PORT);
+
+ if (ioctl(cfg->c_iapp_udp, SIOCGIFBRDADDR, &ifr) == -1)
+ hostapd_fatal("UDP ioctl %s on \"%s\" failed: %s\n",
+ "SIOCGIFBRDADDR", ifr.ifr_name, strerror(errno));
+
+ addr = (struct sockaddr_in *)&ifr.ifr_addr;
+ cfg->c_iapp_broadcast.sin_family = AF_INET;
+ cfg->c_iapp_broadcast.sin_addr.s_addr = addr->sin_addr.s_addr;
+ cfg->c_iapp_broadcast.sin_port = htons(IAPP_PORT);
+
+ baddr.sin_family = AF_INET;
+ baddr.sin_addr.s_addr = htonl(INADDR_ANY);
+ baddr.sin_port = htons(IAPP_PORT);
+
+ if (bind(cfg->c_iapp_udp, (struct sockaddr *)&baddr,
+ sizeof(baddr)) == -1)
+ hostapd_fatal("failed to bind UDP socket: %s\n",
+ strerror(errno));
+
+ /*
+ * The revised 802.11F standard requires IAPP messages to be
+ * send via multicast to the group 224.0.1.178. Nevertheless,
+ * some implementations still use broadcasts for IAPP
+ * messages.
+ */
+ if (cfg->c_flags & HOSTAPD_CFG_F_BRDCAST) {
+ /*
+ * Enable broadcast
+ */
+
+ hostapd_log(HOSTAPD_LOG_DEBUG, "using broadcast mode\n");
+
+ if (setsockopt(cfg->c_iapp_udp, SOL_SOCKET, SO_BROADCAST,
+ &brd, sizeof(brd)) == -1)
+ hostapd_fatal("failed to enable broadcast on socket\n");
+ } else {
+ /*
+ * Enable multicast
+ */
+
+ hostapd_log(HOSTAPD_LOG_DEBUG, "using multicast mode\n");
+
+ bzero(&mreq, sizeof(mreq));
+
+ cfg->c_iapp_multicast.sin_family = AF_INET;
+ cfg->c_iapp_multicast.sin_addr.s_addr =
+ inet_addr(IAPP_MCASTADDR);
+ cfg->c_iapp_multicast.sin_port = htons(IAPP_PORT);
+
+ mreq.imr_multiaddr.s_addr =
+ cfg->c_iapp_multicast.sin_addr.s_addr;
+ mreq.imr_interface.s_addr =
+ cfg->c_iapp_addr.sin_addr.s_addr;
+
+ if (setsockopt(cfg->c_iapp_udp, IPPROTO_IP,
+ IP_ADD_MEMBERSHIP, &mreq, sizeof(mreq)) == -1)
+ hostapd_fatal("failed to add multicast membership to %s:"
+ " %s\n",
+ IAPP_MCASTADDR, strerror(errno));
+ }
+}
+
+void
+hostapd_sig_handler(int sig)
+{
+ switch (sig) {
+ case SIGALRM:
+ case SIGTERM:
+ case SIGQUIT:
+ case SIGINT:
+ /* This will terminate libevent's main loop */
+ event_gotterm = 1;
+ }
+}
+
+void
+hostapd_cleanup(struct hostapd_config *cfg)
+{
+ struct ip_mreq mreq;
+
+ if (cfg->c_flags & HOSTAPD_CFG_F_PRIV &&
+ (cfg->c_flags & HOSTAPD_CFG_F_BRDCAST) == 0 &&
+ cfg->c_apme_n == 0) {
+ /*
+ * Disable multicast and let the kernel unsubscribe
+ * from the multicast group.
+ */
+
+ bzero(&mreq, sizeof(mreq));
+
+ mreq.imr_multiaddr.s_addr =
+ inet_addr(IAPP_MCASTADDR);
+ mreq.imr_interface.s_addr =
+ cfg->c_iapp_addr.sin_addr.s_addr;
+
+ if (setsockopt(cfg->c_iapp_udp, IPPROTO_IP,
+ IP_DROP_MEMBERSHIP, &mreq, sizeof(mreq)) == -1)
+ hostapd_log(HOSTAPD_LOG, "failed to remove multicast"
+ " membership to %s: %s\n",
+ IAPP_MCASTADDR, strerror(errno));
+ }
+
+ if ((cfg->c_flags & HOSTAPD_CFG_F_PRIV) == 0 &&
+ cfg->c_flags & HOSTAPD_CFG_F_APME) {
+ /* Shutdown the Host AP protocol handler */
+ hostapd_iapp_term(&hostapd_cfg);
+ }
+
+ hostapd_log(HOSTAPD_LOG_VERBOSE, "bye!\n");
+
+ if (!cfg->c_debug)
+ closelog();
+
+ /* Close all open file descriptors and sockets */
+ closefrom(0);
+}
+
+int
+main(int argc, char *argv[])
+{
+ struct hostapd_config *cfg = &hostapd_cfg;
+ char *iapp_iface = NULL, *hostap_iface = NULL, *config = NULL;
+ int ch;
+
+ bzero(cfg, sizeof(struct hostapd_config));
+
+ /*
+ * Get and parse command line options
+ */
+ while ((ch = getopt(argc, argv, "a:bf:D:di:v")) != -1) {
+ switch (ch) {
+ case 'a':
+ hostap_iface = optarg;
+ cfg->c_flags |= HOSTAPD_CFG_F_APME;
+ break;
+ case 'b':
+ cfg->c_flags |= HOSTAPD_CFG_F_BRDCAST;
+ break;
+ case 'f':
+ config = optarg;
+ break;
+ case 'D':
+ if (hostapd_parse_symset(optarg) < 0)
+ hostapd_fatal("could not parse macro definition %s",
+ optarg);
+ break;
+ case 'd':
+ cfg->c_debug++;
+ break;
+ case 'i':
+ iapp_iface = optarg;
+ cfg->c_flags |= HOSTAPD_CFG_F_IAPP;
+ break;
+ case 'v':
+ cfg->c_verbose++;
+ break;
+ default:
+ hostapd_usage();
+ }
+ }
+
+ if (config == NULL)
+ strlcpy(cfg->c_config, HOSTAPD_CONFIG, sizeof(cfg->c_config));
+ else
+ strlcpy(cfg->c_config, config, sizeof(cfg->c_config));
+
+ if (iapp_iface != NULL)
+ strlcpy(cfg->c_iapp_iface, iapp_iface,
+ sizeof(cfg->c_iapp_iface));
+
+ if (hostap_iface != NULL)
+ strlcpy(cfg->c_apme_iface, hostap_iface,
+ sizeof(cfg->c_apme_iface));
+
+ if (!cfg->c_debug) {
+ openlog(__progname, LOG_PID | LOG_NDELAY, LOG_DAEMON);
+ daemon(0, 0);
+ }
+
+ /* Parse the configuration file */
+ hostapd_parse_file(cfg);
+
+ if ((cfg->c_flags & HOSTAPD_CFG_F_IAPP) == 0)
+ hostapd_usage();
+
+ if ((cfg->c_flags & HOSTAPD_CFG_F_APME) == 0)
+ strlcpy(cfg->c_apme_iface, "<none>", sizeof(cfg->c_apme_iface));
+
+ /*
+ * Setup the hostapd handlers
+ */
+ hostapd_udp_init(cfg);
+ hostapd_llc_init(cfg);
+
+ if (cfg->c_flags & HOSTAPD_CFG_F_APME)
+ hostapd_apme_init(cfg);
+ else
+ hostapd_log(HOSTAPD_LOG, "%s/%s: running without a Host AP\n",
+ cfg->c_apme_iface, cfg->c_iapp_iface);
+
+ /* Drop all privileges in an unprivileged child process */
+ hostapd_priv_init(cfg);
+
+ setproctitle("Host AP: %s, IAPP: %s",
+ cfg->c_apme_iface, cfg->c_iapp_iface);
+
+ /*
+ * Unprivileged child process
+ */
+
+ event_init();
+
+ /*
+ * Set signal handlers
+ */
+ signal(SIGALRM, hostapd_sig_handler);
+ signal(SIGTERM, hostapd_sig_handler);
+ signal(SIGQUIT, hostapd_sig_handler);
+ signal(SIGINT, hostapd_sig_handler);
+ signal(SIGHUP, SIG_IGN);
+ signal(SIGCHLD, SIG_IGN);
+
+ /* Initialize the IAPP protcol handler */
+ hostapd_iapp_init(cfg);
+
+ /*
+ * Schedule the Host AP listener
+ */
+ if (cfg->c_flags & HOSTAPD_CFG_F_APME) {
+ event_set(&cfg->c_apme_ev, cfg->c_apme_raw,
+ EV_READ | EV_PERSIST, hostapd_apme_input, cfg);
+ event_add(&cfg->c_apme_ev, NULL);
+ }
+
+ /*
+ * Schedule the IAPP listener
+ */
+ event_set(&cfg->c_iapp_udp_ev, cfg->c_iapp_udp, EV_READ | EV_PERSIST,
+ hostapd_iapp_input, cfg);
+ event_add(&cfg->c_iapp_udp_ev, NULL);
+
+ hostapd_log(HOSTAPD_LOG, "%s/%s: starting hostapd with pid %u\n",
+ cfg->c_apme_iface, cfg->c_iapp_iface, getpid());
+
+ /* Run event loop */
+ event_dispatch();
+
+ /* Executed after the event loop has been terminated */
+ hostapd_cleanup(cfg);
+
+ return(EXIT_SUCCESS);
+}
diff --git a/usr.sbin/hostapd/hostapd.conf.5 b/usr.sbin/hostapd/hostapd.conf.5
new file mode 100644
index 00000000000..6e3b1c9ba26
--- /dev/null
+++ b/usr.sbin/hostapd/hostapd.conf.5
@@ -0,0 +1,95 @@
+.\" $OpenBSD: hostapd.conf.5,v 1.1 2005/04/13 18:12:23 reyk Exp $
+.\"
+.\" Copyright (c) 2004, 2005 Reyk Floeter <reyk@vantronix.net>
+.\"
+.\" Permission to use, copy, modify, and distribute this software for any
+.\" purpose with or without fee is hereby granted, provided that the above
+.\" copyright notice and this permission notice appear in all copies.
+.\"
+.\" THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+.\" WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+.\" MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+.\" ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+.\" WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+.\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+.\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+.\"
+.Dd April 13, 2004
+.Dt HOSTAPD.CONF 5
+.Os
+.Sh NAME
+.Nm hostapd.conf
+.Nd configuration file for the Host Access Point daemon
+.Sh DESCRIPTION
+.Nm
+is the configuration file for the
+.Xr hostapd 8
+daemon.
+.Sh SECTIONS
+The
+.Nm
+file is divided into two main sections.
+.Bl -tag -width xxxx
+.It Sy Macros
+User-defined variables may be defined and used later, simplifying the
+configuration file.
+.It Sy Global Configuration
+Global settings for
+.Xr hostapd 8 .
+.El
+.Pp
+Comments can be put anywhere in the file using a hash mark
+.Pq Sq # ,
+and extends to the end of the current line.
+.Sh MACROS
+Much like
+.Xr cpp 1
+or
+.Xr m4 1 ,
+macros can be defined that will later be expanded in context.
+Macro names must start with a letter, and may contain letters, digits
+and underscores.
+Macro names may not be reserved words (for example,
+.Ic set ,
+.Ic interface ,
+or
+.Ic hostap ) .
+Macros are not expanded inside quotes.
+.Pp
+For example,
+.Bd -literal -offset indent
+wlan="ath0"
+set iapp interface $wlan
+.Ed
+.Sh GLOBAL CONFIGURATION
+The following configuration settings are understood:
+.Bl -tag -width Ds
+.It Ic set hostap interface Ar interface
+Specify the wireless interface running in
+.Pa Host AP
+mode.
+This this option could be omitted to use the
+.Xr hostapd 8
+to log received
+.Pa IAPP
+messages.
+.It Ic set iapp interface Ar interface
+Specify the mandatory Inter-Access-Point (IAPP) interface.
+.It Ic set iapp mode Ar mode
+Specify the IAPP mode, if not using the default one.
+IAPP could be used in multicast (default) or in broadcast mode.
+The used multicast group is 224.0.1.178.
+.Pp
+Possible options:
+.Bd -literal -offset indent
+set iapp mode multicast
+set iapp mode broadcast
+.Ed
+.El
+.Sh SEE ALSO
+.Xr hostapd 8
+.Sh AUTHORS
+The
+.Xr hostapd 8
+program was written by
+.An Reyk Floeter Aq reyk@openbsd.org .
diff --git a/usr.sbin/hostapd/hostapd.h b/usr.sbin/hostapd/hostapd.h
new file mode 100644
index 00000000000..ec8040824c4
--- /dev/null
+++ b/usr.sbin/hostapd/hostapd.h
@@ -0,0 +1,191 @@
+/* $OpenBSD: hostapd.h,v 1.1 2005/04/13 18:12:23 reyk Exp $ */
+
+/*
+ * Copyright (c) 2004, 2005 Reyk Floeter <reyk@vantronix.net>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef _HOSTAPD_H
+#define _HOSTAPD_H
+
+#include <sys/param.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+
+#include <net/if.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+#include <errno.h>
+#include <event.h>
+#include <syslog.h>
+
+#include <net80211/ieee80211.h>
+#include <net80211/ieee80211_ioctl.h>
+
+#define IEEE80211_IAPP_VERSION 0
+
+/*
+ * hostapd (IAPP) <-> Host AP (APME)
+ */
+
+#define SIOCS80211IAPP 0
+#define SIOCG80211IAPP 1
+
+struct hostapd_node {
+ u_int8_t ni_macaddr[IEEE80211_ADDR_LEN];
+ u_int8_t ni_bssid[IEEE80211_ADDR_LEN];
+ u_int32_t ni_associd;
+ u_int16_t ni_capinfo;
+ u_int16_t ni_flags;
+ u_int16_t ni_rxseq;
+ u_int16_t ni_rssi;
+};
+
+/*
+ * IAPP <-> IAPP
+ */
+
+struct ieee80211_iapp_frame {
+ u_int8_t i_version;
+ u_int8_t i_command;
+ u_int16_t i_identifier;
+ u_int16_t i_length;
+} __packed;
+
+enum ieee80211_iapp_frame_type {
+ IEEE80211_IAPP_FRAME_ADD_NOTIFY = 0,
+ IEEE80211_IAPP_FRAME_MOVE_NOTIFY = 1,
+ IEEE80211_IAPP_FRAME_MOVE_RESPONSE = 2,
+ IEEE80211_IAPP_FRAME_SEND_SECURITY_BLOCK = 3,
+ IEEE80211_IAPP_FRAME_ACK_SECURITY_BLOCK = 4,
+ IEEE80211_IAPP_FRAME_CACHE_NOTIFY = 5,
+ IEEE80211_IAPP_FRAME_CACHE_RESPONSE = 6
+};
+
+struct ieee80211_iapp_add_notify {
+ u_int8_t a_length;
+ u_int8_t a_reserved;
+ u_int8_t a_macaddr[IEEE80211_ADDR_LEN];
+ u_int16_t a_seqnum;
+} __packed;
+
+/*
+ * IAPP -> switches (LLC)
+ */
+
+struct hostapd_llc {
+ struct ether_header x_hdr;
+ struct llc x_llc;
+} __packed;
+
+#define IAPP_LLC LLC_XID
+#define IAPP_LLC_XID 0x81
+#define IAPP_LLC_CLASS 1
+#define IAPP_LLC_WINDOW 1 << 1
+
+/*
+ * hostapd configuration
+ */
+
+struct hostapd_counter {
+ u_int64_t cn_tx_llc; /* sent LLC messages */
+ u_int64_t cn_rx_iapp; /* received IAPP messages */
+ u_int64_t cn_tx_iapp; /* sent IAPP messages */
+ u_int64_t cn_rx_apme; /* received Host AP messages */
+ u_int64_t cn_tx_apme; /* sent Host AP messages */
+};
+
+struct hostapd_config {
+ int c_apme;
+ int c_apme_raw;
+ u_int c_apme_rawlen;
+ struct event c_apme_ev;
+ char c_apme_iface[IFNAMSIZ];
+ int c_apme_n;
+ u_int8_t c_apme_bssid[IEEE80211_ADDR_LEN];
+
+ u_int16_t c_iapp;
+ int c_iapp_raw;
+ char c_iapp_iface[IFNAMSIZ];
+ int c_iapp_udp;
+ struct event c_iapp_udp_ev;
+ u_int16_t c_iapp_udp_port;
+ struct sockaddr_in c_iapp_addr;
+ struct sockaddr_in c_iapp_broadcast;
+ struct sockaddr_in c_iapp_multicast;
+
+ u_int8_t c_flags;
+
+#define HOSTAPD_CFG_F_APME 0x01
+#define HOSTAPD_CFG_F_IAPP 0x02
+#define HOSTAPD_CFG_F_RAW 0x04
+#define HOSTAPD_CFG_F_UDP 0x08
+#define HOSTAPD_CFG_F_BRDCAST 0x10
+#define HOSTAPD_CFG_F_PRIV 0x20
+
+ struct event c_priv_ev;
+
+ char c_config[MAXPATHLEN];
+
+ u_int c_verbose;
+ u_int c_debug;
+
+ struct hostapd_counter c_stats;
+};
+
+#define IAPP_PORT 3517 /* XXX this should be added to /etc/services */
+#define IAPP_MCASTADDR "224.0.1.178"
+#define IAPP_DLT DLT_IEEE802_11
+#define IAPP_MAXSIZE 512
+
+#define HOSTAPD_USER "_hostapd"
+
+#define HOSTAPD_CONFIG "/etc/hostapd.conf"
+
+#define HOSTAPD_LOG 0
+#define HOSTAPD_LOG_VERBOSE 1
+#define HOSTAPD_LOG_DEBUG 2
+
+__BEGIN_DECLS
+
+void hostapd_log(u_int, const char *, ...);
+void hostapd_fatal(const char *, ...);
+int hostapd_bpf_open(u_int);
+void hostapd_cleanup(struct hostapd_config *);
+int hostapd_check_file_secrecy(int, const char *);
+
+int hostapd_parse_file(struct hostapd_config *);
+int hostapd_parse_symset(char *);
+
+void hostapd_priv_init(struct hostapd_config *);
+int hostapd_priv_llc_xid(struct hostapd_config *, struct hostapd_node *);
+void hostapd_priv_apme_bssid(struct hostapd_config *);
+int hostapd_priv_apme_getnode(struct hostapd_config *, struct hostapd_node *);
+int hostapd_priv_apme_delnode(struct hostapd_config *, struct hostapd_node *);
+
+void hostapd_apme_init(struct hostapd_config *);
+void hostapd_apme_input(int, short, void *);
+
+void hostapd_iapp_init(struct hostapd_config *);
+void hostapd_iapp_term(struct hostapd_config *);
+int hostapd_iapp_add_notify(struct hostapd_config *, struct hostapd_node *);
+void hostapd_iapp_input(int, short, void *);
+
+void hostapd_llc_init(struct hostapd_config *);
+int hostapd_llc_send_xid(struct hostapd_config *, struct hostapd_node *);
+
+__END_DECLS
+
+#endif /* _HOSTAPD_H */
diff --git a/usr.sbin/hostapd/iapp.c b/usr.sbin/hostapd/iapp.c
new file mode 100644
index 00000000000..60c64c1b438
--- /dev/null
+++ b/usr.sbin/hostapd/iapp.c
@@ -0,0 +1,209 @@
+/* $OpenBSD: iapp.c,v 1.1 2005/04/13 18:12:23 reyk Exp $ */
+
+/*
+ * Copyright (c) 2004, 2005 Reyk Floeter <reyk@vantronix.net>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <sys/param.h>
+#include <sys/ioctl.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/time.h>
+
+#include <net/if.h>
+#include <net/if_dl.h>
+#include <net/if_media.h>
+#include <net/if_arp.h>
+#include <net/if_llc.h>
+#include <net/bpf.h>
+
+#include <netinet/in.h>
+#include <netinet/if_ether.h>
+#include <arpa/inet.h>
+
+#include <fcntl.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "hostapd.h"
+
+void
+hostapd_iapp_init(struct hostapd_config *cfg)
+{
+ if ((cfg->c_flags & HOSTAPD_CFG_F_APME) == 0)
+ return;
+
+ /* Get Host AP's BSSID */
+ hostapd_priv_apme_bssid(cfg);
+
+ hostapd_log(HOSTAPD_LOG_VERBOSE,
+ "%s/%s: attaching to Host AP with BSSID \"%s\"\n",
+ cfg->c_apme_iface, cfg->c_iapp_iface,
+ ether_ntoa((struct ether_addr *)cfg->c_apme_bssid));
+}
+
+void
+hostapd_iapp_term(struct hostapd_config *cfg)
+{
+ if ((cfg->c_flags & HOSTAPD_CFG_F_APME) == 0)
+ return;
+
+ /* XXX not yet used but inspired by the APME TERMINATE action */
+ hostapd_log(HOSTAPD_LOG_VERBOSE, "%s/%s: detaching from Host AP\n",
+ cfg->c_apme_iface, cfg->c_iapp_iface);
+}
+
+int
+hostapd_iapp_add_notify(struct hostapd_config *cfg, struct hostapd_node *node)
+{
+ struct sockaddr_in *addr;
+ struct {
+ struct ieee80211_iapp_frame hdr;
+ struct ieee80211_iapp_add_notify add;
+ } __packed frame;
+
+ /*
+ * Send an ADD.notify message to other accesspoints to notify
+ * about a new association on our Host AP.
+ */
+
+ bzero(&frame, sizeof(frame));
+
+ frame.hdr.i_version = IEEE80211_IAPP_VERSION;
+ frame.hdr.i_command = IEEE80211_IAPP_FRAME_ADD_NOTIFY;
+ frame.hdr.i_identifier = htons(cfg->c_iapp++);
+
+ frame.add.a_length = IEEE80211_ADDR_LEN;
+ frame.add.a_seqnum = htons(node->ni_rxseq);
+ bcopy(node->ni_macaddr, frame.add.a_macaddr, IEEE80211_ADDR_LEN);
+
+ if (cfg->c_flags & HOSTAPD_CFG_F_BRDCAST)
+ addr = &cfg->c_iapp_broadcast;
+ else
+ addr = &cfg->c_iapp_multicast;
+
+ if (sendto(cfg->c_iapp_udp, &frame, sizeof(frame),
+ 0, (struct sockaddr *)addr, sizeof(struct sockaddr_in)) == -1) {
+ hostapd_log(HOSTAPD_LOG,
+ "%s/%s: failed to send ADD notification: %s\n",
+ cfg->c_apme_iface, cfg->c_iapp_iface,
+ strerror(errno));
+ return (errno);
+ }
+
+ hostapd_log(HOSTAPD_LOG, "%s/%s: sent ADD notification for %s\n",
+ cfg->c_apme_iface, cfg->c_iapp_iface,
+ ether_ntoa((struct ether_addr*)frame.add.a_macaddr));
+
+ /* Send a LLC XID frame, see llc.c for details */
+ return (hostapd_priv_llc_xid(cfg, node));
+}
+
+void
+hostapd_iapp_input(int fd, short sig, void *arg)
+{
+ struct hostapd_config *cfg = (struct hostapd_config *)arg;
+ struct sockaddr_in addr;
+ socklen_t addr_len;
+ u_int8_t buf[IAPP_MAXSIZE];
+ struct hostapd_node node;
+ struct ieee80211_iapp_recv {
+ struct ieee80211_iapp_frame hdr;
+ union {
+ struct ieee80211_iapp_add_notify add;
+ } u;
+ } __packed *frame;
+ int ret = 0;
+
+ /* Ignore invalid signals */
+ if (sig != EV_READ)
+ return;
+
+ /*
+ * Listen to possible messages from other IAPP
+ */
+
+ bzero(buf, sizeof(buf));
+
+ if (recvfrom(fd, buf, sizeof(buf), 0,
+ (struct sockaddr*)&addr, &addr_len) < 1)
+ return;
+
+ if (memcmp(&cfg->c_iapp_addr.sin_addr, &addr.sin_addr,
+ sizeof(addr.sin_addr)) == 0)
+ return;
+
+ frame = (struct ieee80211_iapp_recv*)buf;
+
+ /* Validate the IAPP version */
+ if (frame->hdr.i_version != IEEE80211_IAPP_VERSION ||
+ addr_len < sizeof(struct sockaddr_in))
+ return;
+
+ cfg->c_stats.cn_rx_iapp++;
+
+ /*
+ * Process the IAPP frame
+ */
+ switch (frame->hdr.i_command) {
+ case IEEE80211_IAPP_FRAME_ADD_NOTIFY:
+ /* Don't support non-48bit MAC addresses, yet */
+ if (frame->u.add.a_length != IEEE80211_ADDR_LEN)
+ return;
+
+ node.ni_rxseq = frame->u.add.a_seqnum;
+ bcopy(frame->u.add.a_macaddr, node.ni_macaddr,
+ IEEE80211_ADDR_LEN);
+
+ /*
+ * Try to remove a node from our Host AP and to free
+ * any allocated resources. Otherwise the received
+ * ADD.notify message will be ignored.
+ */
+ if (cfg->c_flags & HOSTAPD_CFG_F_APME)
+ ret = hostapd_priv_apme_delnode(cfg, &node);
+ else
+ ret = 0;
+
+ hostapd_log(HOSTAPD_LOG, "%s/%s: %s ADD notification "
+ "for %s at %s (%s)\n",
+ ret == 0 ? "received" : "ignored",
+ cfg->c_apme_iface, cfg->c_iapp_iface,
+ ether_ntoa((struct ether_addr*)node.ni_macaddr),
+ inet_ntoa(addr.sin_addr));
+ break;
+
+ case IEEE80211_IAPP_FRAME_MOVE_NOTIFY:
+ case IEEE80211_IAPP_FRAME_MOVE_RESPONSE:
+ case IEEE80211_IAPP_FRAME_SEND_SECURITY_BLOCK:
+ case IEEE80211_IAPP_FRAME_ACK_SECURITY_BLOCK:
+ case IEEE80211_IAPP_FRAME_CACHE_NOTIFY:
+ case IEEE80211_IAPP_FRAME_CACHE_RESPONSE:
+
+ /*
+ * XXX TODO
+ */
+
+ hostapd_log(HOSTAPD_LOG_VERBOSE,
+ "%s/%s: received unsupported IAPP message %d\n",
+ cfg->c_apme_iface, cfg->c_iapp_iface,
+ frame->hdr.i_command);
+ return;
+
+ default:
+ return;
+ }
+}
diff --git a/usr.sbin/hostapd/llc.c b/usr.sbin/hostapd/llc.c
new file mode 100644
index 00000000000..e15460b4252
--- /dev/null
+++ b/usr.sbin/hostapd/llc.c
@@ -0,0 +1,102 @@
+/* $OpenBSD: llc.c,v 1.1 2005/04/13 18:12:23 reyk Exp $ */
+
+/*
+ * Copyright (c) 2004, 2005 Reyk Floeter <reyk@vantronix.net>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <sys/param.h>
+#include <sys/ioctl.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/time.h>
+
+#include <net/if.h>
+#include <net/if_dl.h>
+#include <net/if_media.h>
+#include <net/if_arp.h>
+#include <net/if_llc.h>
+#include <net/bpf.h>
+
+#include <netinet/in.h>
+#include <netinet/if_ether.h>
+#include <arpa/inet.h>
+
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "hostapd.h"
+
+void
+hostapd_llc_init(struct hostapd_config *cfg)
+{
+ struct ifreq ifr;
+ u_int i;
+
+ cfg->c_iapp_raw = hostapd_bpf_open(O_WRONLY);
+
+ cfg->c_flags |= HOSTAPD_CFG_F_RAW;
+
+ bzero(&ifr, sizeof(struct ifreq));
+ strlcpy(ifr.ifr_name, cfg->c_iapp_iface, sizeof(ifr.ifr_name));
+
+ /* Associate the wired network interface to the BPF descriptor */
+ if (ioctl(cfg->c_iapp_raw, BIOCSETIF, &ifr) == -1)
+ hostapd_fatal("failed to set BPF interface \"%s\": %s\n",
+ cfg->c_iapp_iface, strerror(errno));
+
+ i = 1;
+ if (ioctl(cfg->c_iapp_raw, BIOCSHDRCMPLT, &i) == -1)
+ hostapd_fatal("failed to set BPF header completion: %s\n",
+ strerror(errno));
+
+ /* Lock the BPF descriptor, no further configuration */
+ if (ioctl(cfg->c_iapp_raw, BIOCLOCK, NULL) == -1)
+ hostapd_fatal("failed to lock BPF interface on \"%s\": %s\n",
+ cfg->c_iapp_iface, strerror(errno));
+}
+
+int
+hostapd_llc_send_xid(struct hostapd_config *cfg, struct hostapd_node *node)
+{
+ struct hostapd_llc *llc;
+ u_int8_t buf[ETHER_HDR_LEN + (LLC_UFRAMELEN * 2)];
+
+ /*
+ * Send an IEEE 802.3 LLC XID frame which will possibly force
+ * our switch port on the wired network to learn the station's
+ * new MAC address.
+ */
+
+ bzero(&buf, sizeof(buf));
+
+ llc = (struct hostapd_llc *)&buf;
+ memset(&llc->x_hdr.ether_dhost, 0xff,
+ sizeof(llc->x_hdr.ether_dhost));
+ bcopy(&node->ni_macaddr, &llc->x_hdr.ether_shost,
+ sizeof(llc->x_hdr.ether_shost));
+ llc->x_hdr.ether_type = htons(sizeof(buf));
+ llc->x_llc.llc_control = IAPP_LLC;
+ llc->x_llc.llc_fid = IAPP_LLC_XID;
+ llc->x_llc.llc_class = IAPP_LLC_CLASS;
+ llc->x_llc.llc_window = IAPP_LLC_WINDOW;
+
+ if (write(cfg->c_iapp_raw, &buf, sizeof(buf)) == -1)
+ return (errno);
+
+ return (0);
+}
diff --git a/usr.sbin/hostapd/parse.y b/usr.sbin/hostapd/parse.y
new file mode 100644
index 00000000000..5a420f86ee1
--- /dev/null
+++ b/usr.sbin/hostapd/parse.y
@@ -0,0 +1,562 @@
+/* $OpenBSD: parse.y,v 1.1 2005/04/13 18:12:23 reyk Exp $ */
+
+/*
+ * Copyright (c) 2004, 2005 Reyk Floeter <reyk@vantronix.net>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+%{
+#include <sys/param.h>
+#include <sys/ioctl.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/time.h>
+#include <sys/queue.h>
+#include <sys/stat.h>
+
+#include <net/if.h>
+#include <net/if_dl.h>
+#include <net/if_media.h>
+#include <net/if_arp.h>
+#include <net/if_llc.h>
+#include <net/bpf.h>
+
+#include <netinet/in.h>
+#include <netinet/if_ether.h>
+#include <arpa/inet.h>
+
+#include <ctype.h>
+#include <errno.h>
+#include <event.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "hostapd.h"
+
+extern struct hostapd_config hostapd_cfg;
+
+static FILE *fin = NULL;
+static int lineno = 1;
+static int errors = 0;
+char *infile;
+
+TAILQ_HEAD(symhead, sym) symhead = TAILQ_HEAD_INITIALIZER(symhead);
+struct sym {
+ TAILQ_ENTRY(sym) entry;
+ int used;
+ int persist;
+ char *nam;
+ char *val;
+};
+
+int yyerror(const char *, ...);
+int yyparse(void);
+int kw_cmp(const void *, const void *);
+int lookup(char *);
+int lgetc(FILE *);
+int lungetc(int);
+int findeol(void);
+int yylex(void);
+int symset(const char *, const char *, int);
+char *symget(const char *);
+
+typedef struct {
+ union {
+ char *string;
+ int val;
+ } v;
+ int lineno;
+} YYSTYPE;
+
+%}
+
+%token MODE INTERFACE IAPP HOSTAP MULTICAST BROADCAST SET
+%token ERROR
+%token <v.string> STRING
+%token <v.val> VALUE
+%type <v.string> string
+
+%%
+
+/*
+ * Configuration grammar
+ */
+
+grammar : /* empty */
+ | grammar '\n'
+ | grammar option '\n'
+ | grammar varset '\n'
+ | grammar error '\n' { errors++; }
+ ;
+
+option : SET HOSTAP INTERFACE STRING
+ {
+ strlcpy(hostapd_cfg.c_apme_iface, $4,
+ sizeof(hostapd_cfg.c_apme_iface));
+
+ hostapd_cfg.c_flags |= HOSTAPD_CFG_F_APME;
+
+ hostapd_log(HOSTAPD_LOG_DEBUG,
+ "parse %s: Host AP interface %s\n",
+ hostapd_cfg.c_config, $4);
+
+ free($4);
+ }
+ | SET IAPP INTERFACE STRING
+ {
+ strlcpy(hostapd_cfg.c_iapp_iface, $4,
+ sizeof(hostapd_cfg.c_iapp_iface));
+
+ hostapd_cfg.c_flags |= HOSTAPD_CFG_F_IAPP;
+
+ hostapd_log(HOSTAPD_LOG_DEBUG, "parse %s: "
+ "IAPP interface %s\n", hostapd_cfg.c_config, $4);
+
+ free($4);
+ }
+ | SET IAPP MODE MULTICAST
+ {
+ hostapd_cfg.c_flags &= ~HOSTAPD_CFG_F_BRDCAST;
+ }
+ | SET IAPP MODE BROADCAST
+ {
+ hostapd_cfg.c_flags |= HOSTAPD_CFG_F_BRDCAST;
+ }
+ ;
+
+string : string STRING {
+ if (asprintf(&$$, "%s %s", $1, $2) == -1)
+ hostapd_fatal("string: asprintf");
+ free($1);
+ free($2);
+ }
+ | STRING
+ ;
+
+varset : STRING '=' string
+ {
+ if (symset($1, $3, 0) == -1)
+ hostapd_fatal("cannot store variable");
+ free($1);
+ free($3);
+ }
+ ;
+
+%%
+
+/*
+ * Parser and lexer
+ */
+
+struct keywords {
+ char *k_name;
+ int k_val;
+};
+
+int
+kw_cmp(const void *a, const void *b)
+{
+ return strcmp(a, ((const struct keywords *)b)->k_name);
+}
+
+int
+lookup(char *token)
+{
+ /* Keep this list sorted */
+ static const struct keywords keywords[] = {
+ { "broadcast", BROADCAST },
+ { "hostap", HOSTAP },
+ { "iapp", IAPP },
+ { "interface", INTERFACE },
+ { "mode", MODE },
+ { "multicast", MULTICAST },
+ { "set", SET },
+ };
+ const struct keywords *p;
+
+ p = bsearch(token, keywords, sizeof(keywords) / sizeof(keywords[0]),
+ sizeof(keywords[0]), kw_cmp);
+
+ return (p == NULL ? STRING : p->k_val);
+}
+
+#define MAXPUSHBACK 128
+
+char *parsebuf;
+int parseindex;
+char pushback_buffer[MAXPUSHBACK];
+int pushback_index = 0;
+
+int
+lgetc(FILE *f)
+{
+ int c, next;
+
+ if (parsebuf) {
+ /* Read character from the parsebuffer instead of input. */
+ if (parseindex >= 0) {
+ c = parsebuf[parseindex++];
+ if (c != '\0')
+ return (c);
+ parsebuf = NULL;
+ } else
+ parseindex++;
+ }
+
+ if (pushback_index)
+ return (pushback_buffer[--pushback_index]);
+
+ while ((c = getc(f)) == '\\') {
+ next = getc(f);
+ if (next != '\n') {
+ if (isspace(next))
+ yyerror("whitespace after \\");
+ ungetc(next, f);
+ break;
+ }
+ yylval.lineno = lineno;
+ lineno++;
+ }
+ if (c == '\t' || c == ' ') {
+ /* Compress blanks to a single space. */
+ do {
+ c = getc(f);
+ } while (c == '\t' || c == ' ');
+ ungetc(c, f);
+ c = ' ';
+ }
+
+ return (c);
+}
+
+int
+lungetc(int c)
+{
+ if (c == EOF)
+ return (EOF);
+ if (parsebuf) {
+ parseindex--;
+ if (parseindex >= 0)
+ return (c);
+ }
+ if (pushback_index < MAXPUSHBACK-1)
+ return (pushback_buffer[pushback_index++] = c);
+ else
+ return (EOF);
+}
+
+int
+findeol(void)
+{
+ int c;
+
+ parsebuf = NULL;
+ pushback_index = 0;
+
+ /* skip to either EOF or the first real EOL */
+ while (1) {
+ c = lgetc(fin);
+ if (c == '\n') {
+ lineno++;
+ break;
+ }
+ if (c == EOF)
+ break;
+ }
+ return (ERROR);
+}
+
+int
+yylex(void)
+{
+ char buf[8096];
+ char *p, *val;
+ int endc, c;
+ int token;
+
+top:
+ p = buf;
+ while ((c = lgetc(fin)) == ' ')
+ ; /* nothing */
+
+ yylval.lineno = lineno;
+ if (c == '#')
+ while ((c = lgetc(fin)) != '\n' && c != EOF)
+ ; /* nothing */
+ if (c == '$' && parsebuf == NULL) {
+ while (1) {
+ if ((c = lgetc(fin)) == EOF)
+ return (0);
+
+ if (p + 1 >= buf + sizeof(buf) - 1) {
+ yyerror("string too long");
+ return (findeol());
+ }
+ if (isalnum(c) || c == '_') {
+ *p++ = (char)c;
+ continue;
+ }
+ *p = '\0';
+ lungetc(c);
+ break;
+ }
+ val = symget(buf);
+ if (val == NULL) {
+ yyerror("macro \"%s\" not defined", buf);
+ return (findeol());
+ }
+ parsebuf = val;
+ parseindex = 0;
+ goto top;
+ }
+
+ switch (c) {
+ case '\'':
+ case '"':
+ endc = c;
+ while (1) {
+ if ((c = lgetc(fin)) == EOF)
+ return (0);
+ if (c == endc) {
+ *p = '\0';
+ break;
+ }
+ if (c == '\n') {
+ lineno++;
+ continue;
+ }
+ if (p + 1 >= buf + sizeof(buf) - 1) {
+ yyerror("string too long");
+ return (findeol());
+ }
+ *p++ = (char)c;
+ }
+ yylval.v.string = strdup(buf);
+ if (yylval.v.string == NULL)
+ hostapd_fatal("yylex: strdup");
+ return (STRING);
+ }
+
+#define allowed_in_string(x) \
+ (isalnum(x) || (ispunct(x) && x != '(' && x != ')' && \
+ x != '{' && x != '}' && x != '<' && x != '>' && \
+ x != '!' && x != '=' && x != '/' && x != '#' && \
+ x != ','))
+
+ if (isalnum(c) || c == ':' || c == '_' || c == '*') {
+ do {
+ *p++ = c;
+ if ((unsigned)(p-buf) >= sizeof(buf)) {
+ yyerror("string too long");
+ return (findeol());
+ }
+ } while ((c = lgetc(fin)) != EOF && (allowed_in_string(c)));
+ lungetc(c);
+ *p = '\0';
+ if ((token = lookup(buf)) == STRING)
+ if ((yylval.v.string = strdup(buf)) == NULL)
+ hostapd_fatal("yylex: strdup");
+ return (token);
+ }
+ if (c == '\n') {
+ yylval.lineno = lineno;
+ lineno++;
+ }
+ if (c == EOF)
+ return (0);
+ return (c);
+}
+
+int
+symset(const char *nam, const char *val, int persist)
+{
+ struct sym *sym;
+
+ for (sym = TAILQ_FIRST(&symhead); sym && strcmp(nam, sym->nam);
+ sym = TAILQ_NEXT(sym, entry))
+ ; /* nothing */
+
+ if (sym != NULL) {
+ if (sym->persist == 1)
+ return (0);
+ else {
+ free(sym->nam);
+ free(sym->val);
+ TAILQ_REMOVE(&symhead, sym, entry);
+ free(sym);
+ }
+ }
+ if ((sym = calloc(1, sizeof(*sym))) == NULL)
+ return (-1);
+
+ sym->nam = strdup(nam);
+ if (sym->nam == NULL) {
+ free(sym);
+ return (-1);
+ }
+ sym->val = strdup(val);
+ if (sym->val == NULL) {
+ free(sym->nam);
+ free(sym);
+ return (-1);
+ }
+ sym->used = 0;
+ sym->persist = persist;
+ TAILQ_INSERT_TAIL(&symhead, sym, entry);
+
+ hostapd_log(HOSTAPD_LOG_DEBUG, "%s = \"%s\"\n", sym->nam, sym->val);
+
+ return (0);
+}
+
+int
+hostapd_parse_symset(char *s)
+{
+ char *sym, *val;
+ int ret;
+ size_t len;
+
+ if ((val = strrchr(s, '=')) == NULL)
+ return (-1);
+
+ len = strlen(s) - strlen(val) + 1;
+ if ((sym = malloc(len)) == NULL)
+ hostapd_fatal("cmdline_symset: malloc");
+
+ strlcpy(sym, s, len);
+
+ ret = symset(sym, val + 1, 1);
+
+ free(sym);
+
+ return (ret);
+}
+
+char *
+symget(const char *nam)
+{
+ struct sym *sym;
+
+ TAILQ_FOREACH(sym, &symhead, entry)
+ if (strcmp(nam, sym->nam) == 0) {
+ sym->used = 1;
+ return (sym->val);
+ }
+ return (NULL);
+}
+
+#if 0
+int
+yylex(void)
+{
+ char *p;
+ int v;
+
+ /* Locate next token */
+ if (confptr == NULL) {
+ confptr = confbuf;
+ } else {
+ for (p = confptr; *p && p < confbuf + conflen; p++)
+ ;
+ *p++;
+ if (!*p)
+ return 0;
+ confptr = p;
+ }
+
+ /* Numerical token? */
+ if (isdigit(*confptr)) {
+ for (p = confptr; *p; p++)
+ if (*p == '.') /* IP-address, or bad input */
+ goto is_string;
+ v = (int)strtol(confptr, (char **)NULL, 10);
+ yylval.val = v;
+ return VALUE;
+ }
+
+ is_string:
+ if ((v = lookup(confptr)) == STRING) {
+ yylval.string = strdup(confptr);
+ if (yylval.string == NULL)
+ hostapd_fatal("yylex: strdup()");
+ }
+ return v;
+}
+#endif
+
+int
+hostapd_parse_file(struct hostapd_config *cfg)
+{
+ struct sym *sym, *next;
+ int ret;
+
+ if ((fin = fopen(cfg->c_config, "r")) == NULL) {
+ hostapd_log(HOSTAPD_LOG, "unable to find %s, using defaults\n",
+ cfg->c_config);
+ return (0);
+ }
+ infile = cfg->c_config;
+
+ if (hostapd_check_file_secrecy(fileno(fin), cfg->c_config)) {
+ hostapd_fatal("invalid permissions for %s\n", cfg->c_config);
+ fclose(fin);
+ return (-1);
+ }
+
+ lineno = 1;
+ errors = 0;
+
+ ret = yyparse();
+
+ fclose(fin);
+
+ /* Free macros and check which have not been used. */
+ for (sym = TAILQ_FIRST(&symhead); sym != NULL; sym = next) {
+ next = TAILQ_NEXT(sym, entry);
+ if (!sym->used)
+ hostapd_log(HOSTAPD_LOG_VERBOSE,
+ "warning: macro \"%s\" not used\n", sym->nam);
+ if (!sym->persist) {
+ free(sym->nam);
+ free(sym->val);
+ TAILQ_REMOVE(&symhead, sym, entry);
+ free(sym);
+ }
+ }
+
+ return (ret);
+}
+
+int
+yyerror(const char *fmt, ...)
+{
+ va_list ap;
+ char *nfmt;
+
+ errors = 1;
+
+ va_start(ap, fmt);
+ if (asprintf(&nfmt, "%s:%d: %s\n", infile, yylval.lineno, fmt) == -1)
+ hostapd_fatal("yyerror asprintf");
+ hostapd_log(HOSTAPD_LOG, nfmt, ap);
+ va_end(ap);
+ free(nfmt);
+
+ return (0);
+}
+
diff --git a/usr.sbin/hostapd/privsep.c b/usr.sbin/hostapd/privsep.c
new file mode 100644
index 00000000000..5a395b82991
--- /dev/null
+++ b/usr.sbin/hostapd/privsep.c
@@ -0,0 +1,484 @@
+/* $OpenBSD: privsep.c,v 1.1 2005/04/13 18:12:23 reyk Exp $ */
+
+/*
+ * Copyright (c) 2004, 2005 Reyk Floeter <reyk@vantronix.net>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <sys/param.h>
+#include <sys/ioctl.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/time.h>
+
+#include <net/if.h>
+#include <net/if_dl.h>
+#include <net/if_media.h>
+#include <net/if_arp.h>
+#include <net/if_llc.h>
+#include <net/bpf.h>
+
+#include <netinet/in.h>
+#include <netinet/if_ether.h>
+#include <arpa/inet.h>
+
+#include <dev/ic/if_wi_ieee.h>
+#include <dev/ic/if_wireg.h>
+#include <dev/ic/if_wi_hostap.h>
+
+#include <errno.h>
+#include <event.h>
+#include <fcntl.h>
+#include <netdb.h>
+#include <pwd.h>
+#include <signal.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "hostapd.h"
+
+enum hostapd_cmd_types {
+ PRIV_APME_BSSID, /* Get the Host AP's BSSID */
+ PRIV_APME_GETNODE, /* Get a node from the Host AP */
+ PRIV_APME_DELNODE, /* Delete a node from the Host AP */
+ PRIV_LLC_SEND_XID /* Send IEEE 802.3 LLC XID frame */
+};
+
+void hostapd_priv(int, short, void *);
+void hostapd_sig_relay(int);
+void hostapd_sig_chld(int);
+int hostapd_may_read(int, void *, size_t);
+void hostapd_must_read(int, void *, size_t);
+void hostapd_must_write(int, void *, size_t);
+
+static int priv_fd = -1;
+static volatile pid_t child_pid = -1;
+
+/* defined in event(3) to terminate the event loop */
+extern volatile sig_atomic_t event_gotterm;
+
+/*
+ * Main privsep functions
+ */
+
+void
+hostapd_priv_init(struct hostapd_config *cfg)
+{
+ int i, socks[2];
+ struct passwd *pw;
+ struct servent *se;
+
+ for (i = 1; i < _NSIG; i++)
+ signal(i, SIG_DFL);
+
+ if ((se = getservbyname("iapp", "udp")) == NULL) {
+ cfg->c_iapp_udp_port = IAPP_PORT;
+ } else
+ cfg->c_iapp_udp_port = se->s_port;
+
+ if ((pw = getpwnam(HOSTAPD_USER)) == NULL)
+ hostapd_fatal("failed to get user \"%s\"\n", HOSTAPD_USER);
+
+ endpwent();
+ endservent();
+
+ /* Create sockets */
+ if (socketpair(AF_LOCAL, SOCK_STREAM, PF_UNSPEC, socks) == -1)
+ hostapd_fatal("failed to get socket pair");
+
+ if ((child_pid = fork()) < 0)
+ hostapd_fatal("failed to fork child process");
+
+ /*
+ * Unprivileged child process
+ */
+ if (child_pid == 0) {
+ cfg->c_flags &= ~HOSTAPD_CFG_F_PRIV;
+
+ /*
+ * Change the child's root directory to the unprivileged
+ * user's home directory
+ */
+ if (chroot(pw->pw_dir) == -1)
+ hostapd_fatal("failed to change root directory\n");
+ if (chdir("/") == -1)
+ hostapd_fatal("failed to change directory");
+
+ /*
+ * Drop privileges and clear the group access list
+ */
+ if ((setgroups(1, &pw->pw_gid) ||
+ setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid) ||
+ setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid)) == -1)
+ hostapd_fatal("can't drop privileges");
+
+ close(socks[0]);
+ priv_fd = socks[1];
+ return;
+ }
+
+
+ /*
+ * Privileged mother process
+ */
+ cfg->c_flags |= HOSTAPD_CFG_F_PRIV;
+
+ event_init();
+
+ /* Pass ALR/TERM/HUP through to child, and accept CHLD */
+ signal(SIGALRM, hostapd_sig_relay);
+ signal(SIGTERM, hostapd_sig_relay);
+ signal(SIGINT, hostapd_sig_relay);
+ signal(SIGHUP, hostapd_sig_relay);
+ signal(SIGCHLD, hostapd_sig_chld);
+
+ close(socks[1]);
+
+ if (cfg->c_flags & HOSTAPD_CFG_F_APME) {
+ if ((cfg->c_apme = socket(AF_INET, SOCK_DGRAM, 0)) == -1) {
+ hostapd_fatal("unable to open ioctl socket\n");
+ }
+ }
+
+ setproctitle("[priv]");
+
+ /* Start a new event listener */
+ event_set(&cfg->c_priv_ev, socks[0], EV_READ, hostapd_priv, cfg);
+ event_add(&cfg->c_priv_ev, NULL);
+
+ /* Run privileged event loop */
+ event_dispatch();
+
+ /* Executed after the event loop has been terminated */
+ hostapd_cleanup(cfg);
+
+ _exit(EXIT_SUCCESS);
+}
+
+void
+hostapd_priv(int fd, short sig, void *arg)
+{
+ struct hostapd_config *cfg = (struct hostapd_config *)arg;
+ struct hostapd_node node;
+ struct ieee80211_bssid bssid;
+ struct hostap_sta sta;
+ struct ifreq ifr;
+ int ret, cmd;
+
+ /* Terminate the event if we got an invalid signal */
+ if (sig != EV_READ)
+ return;
+
+ bzero(&node, sizeof(struct hostapd_node));
+ bzero(&sta, sizeof(struct hostap_sta));
+
+ /* Get privsep command */
+ if (hostapd_may_read(fd, &cmd, sizeof(int)))
+ return;
+
+ switch (cmd) {
+ case PRIV_APME_BSSID:
+ hostapd_log(HOSTAPD_LOG_DEBUG,
+ "[priv]: msg PRIV_APME_BSSID received\n");
+
+ strlcpy(bssid.i_name, cfg->c_apme_iface, sizeof(bssid.i_name));
+
+ /* Try to get the APME's BSSID */
+ if (cfg->c_flags & HOSTAPD_CFG_F_APME) {
+ if ((ret = ioctl(cfg->c_apme,
+ SIOCG80211BSSID, &bssid)) != 0)
+ ret = errno;
+ } else
+ ret = ENXIO;
+
+ hostapd_must_write(fd, &ret, sizeof(int));
+ if (ret == 0) {
+ hostapd_must_write(fd, &bssid.i_bssid,
+ IEEE80211_ADDR_LEN);
+ }
+ break;
+
+ case PRIV_APME_GETNODE:
+ hostapd_log(HOSTAPD_LOG_DEBUG,
+ "[priv]: msg PRIV_APME_GETNODE received\n");
+
+ hostapd_must_read(fd, &node, sizeof(struct hostapd_node));
+ bcopy(node.ni_macaddr, sta.addr, IEEE80211_ADDR_LEN);
+
+ strlcpy(ifr.ifr_name, cfg->c_apme_iface, sizeof(ifr.ifr_name));
+ ifr.ifr_data = (caddr_t)&sta;
+
+ /* Try to get a station from the APME */
+ if (cfg->c_flags & HOSTAPD_CFG_F_APME) {
+ if ((ret = ioctl(cfg->c_apme,
+ SIOCHOSTAP_GET, &ifr)) != 0)
+ ret = errno;
+ } else
+ ret = ENXIO;
+
+ hostapd_must_write(fd, &ret, sizeof(int));
+ if (ret == 0) {
+ node.ni_associd = sta.asid;
+ node.ni_flags = sta.flags;
+ node.ni_rssi = sta.sig_info;
+ node.ni_capinfo = sta.capinfo;
+
+ hostapd_must_write(fd, &node, sizeof(struct hostapd_node));
+ }
+ break;
+
+ case PRIV_APME_DELNODE:
+ hostapd_log(HOSTAPD_LOG_DEBUG,
+ "[priv]: msg PRIV_APME_DELNODE received\n");
+
+ hostapd_must_read(fd, &node, sizeof(struct hostapd_node));
+ bcopy(node.ni_macaddr, sta.addr, IEEE80211_ADDR_LEN);
+
+ /* Try to delete a station from the APME */
+ if (cfg->c_flags & HOSTAPD_CFG_F_APME) {
+ if ((ret = ioctl(cfg->c_apme,
+ SIOCHOSTAP_DEL, &sta)) != 0)
+ ret = errno;
+ } else
+ ret = ENXIO;
+
+ hostapd_must_write(fd, &ret, sizeof(int));
+ break;
+
+ case PRIV_LLC_SEND_XID:
+ hostapd_log(HOSTAPD_LOG_DEBUG,
+ "[priv]: msg PRIV_LLC_SEND_XID received\n");
+
+ hostapd_must_read(fd, &node, sizeof(struct hostapd_node));
+
+ /* Send a LLC XID frame to reset possible switch ports */
+ ret = hostapd_llc_send_xid(cfg, &node);
+
+ hostapd_must_write(fd, &ret, sizeof(int));
+ break;
+
+ default:
+ hostapd_fatal("[priv]: unknown command %d\n", cmd);
+ }
+
+ event_add(&cfg->c_priv_ev, NULL);
+}
+
+/*
+ * Unprivileged callers
+ */
+
+int
+hostapd_priv_apme_getnode(struct hostapd_config *cfg, struct hostapd_node *node)
+{
+ int ret, cmd;
+
+ if (priv_fd < 0)
+ hostapd_fatal("%s: called from privileged portion\n", __func__);
+
+ if ((cfg->c_flags & HOSTAPD_CFG_F_APME) == 0)
+ hostapd_fatal("%s: Host AP is not available\n", __func__);
+
+ cmd = PRIV_APME_GETNODE;
+ hostapd_must_write(priv_fd, &cmd, sizeof(int));
+ hostapd_must_write(priv_fd, node, sizeof(struct hostapd_node));
+
+ hostapd_must_read(priv_fd, &ret, sizeof(int));
+ if (ret != 0)
+ return (ret);
+
+ hostapd_must_read(priv_fd, node, sizeof(struct hostapd_node));
+
+ cfg->c_stats.cn_tx_apme++;
+
+ return (ret);
+}
+
+int
+hostapd_priv_apme_delnode(struct hostapd_config *cfg, struct hostapd_node *node)
+{
+ int ret, cmd;
+
+ if (priv_fd < 0)
+ hostapd_fatal("%s: called from privileged portion\n", __func__);
+
+ if ((cfg->c_flags & HOSTAPD_CFG_F_APME) == 0)
+ hostapd_fatal("%s: Host AP is not available\n", __func__);
+
+ cmd = PRIV_APME_DELNODE;
+ hostapd_must_write(priv_fd, &cmd, sizeof(int));
+ hostapd_must_write(priv_fd, node, sizeof(struct hostapd_node));
+
+ hostapd_must_read(priv_fd, &ret, sizeof(int));
+
+ if (ret == 0)
+ cfg->c_stats.cn_tx_apme++;
+
+ return (ret);
+}
+
+void
+hostapd_priv_apme_bssid(struct hostapd_config *cfg)
+{
+ int ret, cmd;
+
+ if (priv_fd < 0)
+ hostapd_fatal("%s: called from privileged portion\n", __func__);
+
+ if ((cfg->c_flags & HOSTAPD_CFG_F_APME) == 0)
+ hostapd_fatal("%s: Host AP is not available\n", __func__);
+
+ cmd = PRIV_APME_BSSID;
+ hostapd_must_write(priv_fd, &cmd, sizeof(int));
+
+ hostapd_must_read(priv_fd, &ret, sizeof(int));
+ if (ret != 0)
+ hostapd_fatal("%s: failed to get Host AP's BSSID on"
+ " \"%s\": %s\n",
+ cfg->c_apme_iface, strerror(errno));
+
+ hostapd_must_read(priv_fd, &cfg->c_apme_bssid, IEEE80211_ADDR_LEN);
+
+ cfg->c_stats.cn_tx_apme++;
+}
+
+int
+hostapd_priv_llc_xid(struct hostapd_config *cfg, struct hostapd_node *node)
+{
+ int ret, cmd;
+
+ if (priv_fd < 0)
+ hostapd_fatal("%s: called from privileged portion\n", __func__);
+
+ cmd = PRIV_LLC_SEND_XID;
+ hostapd_must_write(priv_fd, &cmd, sizeof(int));
+ hostapd_must_write(priv_fd, node, sizeof(struct hostapd_node));
+ hostapd_must_read(priv_fd, &ret, sizeof(int));
+
+ if (ret == 0)
+ cfg->c_stats.cn_tx_llc++;
+
+ return (ret);
+}
+
+/*
+ * Signal handlers
+ */
+
+void
+hostapd_sig_relay(int sig)
+{
+ int oerrno = errno;
+
+ /*
+ * If priv parent gets a TERM or HUP, pass it through to child
+ * instead.
+ */
+
+ if (child_pid != -1)
+ kill(child_pid, sig);
+
+ errno = oerrno;
+}
+
+void
+hostapd_sig_chld(int sig)
+{
+ /*
+ * If parent gets a SIGCHLD, it will exit.
+ */
+
+ if (sig == SIGCHLD) {
+ /* This will terminate libevent's main loop */
+ event_gotterm = 1;
+ }
+}
+
+/*
+ * privsep I/O functions
+ */
+
+/* Read all data or return 1 for error. */
+int
+hostapd_may_read(int fd, void *buf, size_t n)
+{
+ char *s = buf;
+ ssize_t res, pos = 0;
+
+ while ((ssize_t)n > pos) {
+ res = read(fd, s + pos, n - pos);
+ switch (res) {
+ case -1:
+ if (errno == EINTR || errno == EAGAIN)
+ continue;
+ case 0:
+ return (1);
+ default:
+ pos += res;
+ }
+ }
+ return (0);
+}
+
+/*
+ * Read data with the assertion that it all must come through, or
+ * else abort the process. Based on atomicio() from openssh.
+ */
+void
+hostapd_must_read(int fd, void *buf, size_t n)
+{
+ char *s = buf;
+ ssize_t res, pos = 0;
+
+ while ((ssize_t)n > pos) {
+ res = read(fd, s + pos, n - pos);
+ switch (res) {
+ case -1:
+ if (errno == EINTR || errno == EAGAIN)
+ continue;
+ case 0:
+ _exit(0);
+ default:
+ pos += res;
+ }
+ }
+}
+
+/*
+ * Write data with the assertion that it all has to be written, or
+ * else abort the process. Based on atomicio() from openssh.
+ */
+void
+hostapd_must_write(int fd, void *buf, size_t n)
+{
+ char *s = buf;
+ ssize_t res, pos = 0;
+
+ while ((ssize_t)n > pos) {
+ res = write(fd, s + pos, n - pos);
+ switch (res) {
+ case -1:
+ if (errno == EINTR || errno == EAGAIN)
+ continue;
+ case 0:
+ _exit(0);
+ default:
+ pos += res;
+ }
+ }
+}
+