xref: /openbsd-src/usr.sbin/hostapd/hostapd.c (revision 5b133f3f277e80f096764111e64f3a1284acb179)
1*5b133f3fSguenther /*	$OpenBSD: hostapd.c,v 1.42 2023/03/08 04:43:13 guenther Exp $	*/
2d2b2a2e3Sreyk 
3d2b2a2e3Sreyk /*
42c56d0d6Sreyk  * Copyright (c) 2004, 2005 Reyk Floeter <reyk@openbsd.org>
593bf35b3Shenning  * Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org>
6d2b2a2e3Sreyk  *
7d2b2a2e3Sreyk  * Permission to use, copy, modify, and distribute this software for any
8d2b2a2e3Sreyk  * purpose with or without fee is hereby granted, provided that the above
9d2b2a2e3Sreyk  * copyright notice and this permission notice appear in all copies.
10d2b2a2e3Sreyk  *
11d2b2a2e3Sreyk  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
12d2b2a2e3Sreyk  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13d2b2a2e3Sreyk  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
14d2b2a2e3Sreyk  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15d2b2a2e3Sreyk  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
16d2b2a2e3Sreyk  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
17d2b2a2e3Sreyk  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18d2b2a2e3Sreyk  */
19d2b2a2e3Sreyk 
20d2b2a2e3Sreyk #include <sys/ioctl.h>
21d2b2a2e3Sreyk #include <sys/types.h>
22b9fc9a72Sderaadt #include <sys/signal.h>
23d2b2a2e3Sreyk #include <sys/socket.h>
24d2b2a2e3Sreyk #include <sys/time.h>
25d2b2a2e3Sreyk #include <sys/queue.h>
26d2b2a2e3Sreyk #include <sys/stat.h>
27d2b2a2e3Sreyk 
28d2b2a2e3Sreyk #include <net/if.h>
29d2b2a2e3Sreyk #include <net/if_media.h>
30d2b2a2e3Sreyk #include <net/if_arp.h>
31d2b2a2e3Sreyk #include <net/if_llc.h>
32d2b2a2e3Sreyk #include <net/bpf.h>
33d2b2a2e3Sreyk 
34d2b2a2e3Sreyk #include <netinet/in.h>
35d2b2a2e3Sreyk #include <netinet/if_ether.h>
36d2b2a2e3Sreyk #include <arpa/inet.h>
37d2b2a2e3Sreyk 
38d2b2a2e3Sreyk #include <errno.h>
39d2b2a2e3Sreyk #include <event.h>
40d2b2a2e3Sreyk #include <fcntl.h>
41d2b2a2e3Sreyk #include <stdio.h>
42d2b2a2e3Sreyk #include <stdlib.h>
43d2b2a2e3Sreyk #include <stdarg.h>
44d2b2a2e3Sreyk #include <string.h>
45d2b2a2e3Sreyk #include <unistd.h>
46b9fc9a72Sderaadt #include <limits.h>
473dfd0658Sreyk #include <err.h>
48d2b2a2e3Sreyk 
49d2b2a2e3Sreyk #include "hostapd.h"
509b9e1fddSreyk #include "iapp.h"
51d2b2a2e3Sreyk 
52d2b2a2e3Sreyk void	 hostapd_usage(void);
53d2b2a2e3Sreyk void	 hostapd_udp_init(struct hostapd_config *);
547b37258aSclaudio void	 hostapd_sig_handler(int, short, void *);
552a7c9000Sreyk static __inline int
562a7c9000Sreyk 	 hostapd_entry_cmp(struct hostapd_entry *, struct hostapd_entry *);
57d2b2a2e3Sreyk 
58d2b2a2e3Sreyk struct hostapd_config hostapd_cfg;
59d2b2a2e3Sreyk 
60d2b2a2e3Sreyk extern char *__progname;
6150ce650fSreyk char printbuf[BUFSIZ];
62d2b2a2e3Sreyk 
63d2b2a2e3Sreyk void
hostapd_usage(void)64d2b2a2e3Sreyk hostapd_usage(void)
65d2b2a2e3Sreyk {
66cb46a11aSreyk 	fprintf(stderr, "usage: %s [-dv] [-D macro=value] [-f file]\n",
67cb46a11aSreyk 	    __progname);
68d2b2a2e3Sreyk 	exit(EXIT_FAILURE);
69d2b2a2e3Sreyk }
70d2b2a2e3Sreyk 
71d2b2a2e3Sreyk void
hostapd_log(u_int level,const char * fmt,...)72d2b2a2e3Sreyk hostapd_log(u_int level, const char *fmt, ...)
73d2b2a2e3Sreyk {
74f83005a6Sreyk 	char *nfmt = NULL;
75d2b2a2e3Sreyk 	va_list ap;
76d2b2a2e3Sreyk 
77d2b2a2e3Sreyk 	if (level > hostapd_cfg.c_verbose)
78d2b2a2e3Sreyk 		return;
79d2b2a2e3Sreyk 
80d2b2a2e3Sreyk 	va_start(ap, fmt);
81a79ed0b3Shenning 	if (hostapd_cfg.c_debug) {
82f83005a6Sreyk 		if (asprintf(&nfmt, "%s\n", fmt) != -1)
83f83005a6Sreyk 			vfprintf(stderr, nfmt, ap);
84f83005a6Sreyk 		else {
85d2b2a2e3Sreyk 			vfprintf(stderr, fmt, ap);
86f83005a6Sreyk 			fprintf(stderr, "\n");
87f83005a6Sreyk 		}
88d2b2a2e3Sreyk 		fflush(stderr);
89a79ed0b3Shenning 	} else
90a79ed0b3Shenning 		vsyslog(LOG_INFO, fmt, ap);
91d2b2a2e3Sreyk 	va_end(ap);
92f83005a6Sreyk 
93f83005a6Sreyk 	free(nfmt);
94d2b2a2e3Sreyk }
95d2b2a2e3Sreyk 
96d2b2a2e3Sreyk void
hostapd_printf(const char * fmt,...)9750ce650fSreyk hostapd_printf(const char *fmt, ...)
9850ce650fSreyk {
9950ce650fSreyk 	char newfmt[BUFSIZ];
10050ce650fSreyk 	va_list ap;
10150ce650fSreyk 	size_t n;
10250ce650fSreyk 
1034d33977eSreyk 	if (fmt == NULL)
1044d33977eSreyk 		goto flush;
10550ce650fSreyk 
10650ce650fSreyk 	va_start(ap, fmt);
10750ce650fSreyk 	bzero(newfmt, sizeof(newfmt));
10850ce650fSreyk 	if ((n = strlcpy(newfmt, printbuf, sizeof(newfmt))) >= sizeof(newfmt))
1094d33977eSreyk 		goto va_flush;
11050ce650fSreyk 	if (strlcpy(newfmt + n, fmt, sizeof(newfmt) - n) >= sizeof(newfmt) - n)
1114d33977eSreyk 		goto va_flush;
112515e489cSderaadt 	if (vsnprintf(printbuf, sizeof(printbuf), newfmt, ap) < 0)
1134d33977eSreyk 		goto va_flush;
11450ce650fSreyk 	va_end(ap);
1150ceae6b8Sreyk 
1164d33977eSreyk 	return;
1174d33977eSreyk 
1184d33977eSreyk  va_flush:
1194d33977eSreyk 	va_end(ap);
1204d33977eSreyk  flush:
121f83005a6Sreyk 	if (strlen(printbuf))
1224d33977eSreyk 		hostapd_log(HOSTAPD_LOG, "%s", printbuf);
1234d33977eSreyk 	bzero(printbuf, sizeof(printbuf));
12450ce650fSreyk }
12550ce650fSreyk 
12650ce650fSreyk void
hostapd_fatal(const char * fmt,...)127d2b2a2e3Sreyk hostapd_fatal(const char *fmt, ...)
128d2b2a2e3Sreyk {
129d2b2a2e3Sreyk 	va_list ap;
130d2b2a2e3Sreyk 
131d2b2a2e3Sreyk 	va_start(ap, fmt);
132a79ed0b3Shenning 	if (hostapd_cfg.c_debug) {
133d2b2a2e3Sreyk 		vfprintf(stderr, fmt, ap);
134d2b2a2e3Sreyk 		fflush(stderr);
135a79ed0b3Shenning 	} else
136a79ed0b3Shenning 		vsyslog(LOG_ERR, fmt, ap);
137d2b2a2e3Sreyk 	va_end(ap);
138d2b2a2e3Sreyk 
139d2b2a2e3Sreyk 	hostapd_cleanup(&hostapd_cfg);
140d2b2a2e3Sreyk 	exit(EXIT_FAILURE);
141d2b2a2e3Sreyk }
142d2b2a2e3Sreyk 
143d2b2a2e3Sreyk int
hostapd_check_file_secrecy(int fd,const char * fname)144d2b2a2e3Sreyk hostapd_check_file_secrecy(int fd, const char *fname)
145d2b2a2e3Sreyk {
146d2b2a2e3Sreyk 	struct stat st;
147d2b2a2e3Sreyk 
148d2b2a2e3Sreyk 	if (fstat(fd, &st)) {
149d2b2a2e3Sreyk 		hostapd_log(HOSTAPD_LOG,
150f83005a6Sreyk 		    "cannot stat %s", fname);
151d2b2a2e3Sreyk 		return (-1);
152d2b2a2e3Sreyk 	}
153d2b2a2e3Sreyk 
154d2b2a2e3Sreyk 	if (st.st_uid != 0 && st.st_uid != getuid()) {
155d2b2a2e3Sreyk 		hostapd_log(HOSTAPD_LOG,
156f83005a6Sreyk 		    "%s: owner not root or current user", fname);
157d2b2a2e3Sreyk 		return (-1);
158d2b2a2e3Sreyk 	}
159d2b2a2e3Sreyk 
160d2b2a2e3Sreyk 	if (st.st_mode & (S_IRWXG | S_IRWXO)) {
161d2b2a2e3Sreyk 		hostapd_log(HOSTAPD_LOG,
162f83005a6Sreyk 		    "%s: group/world readable/writeable", fname);
163d2b2a2e3Sreyk 		return (-1);
164d2b2a2e3Sreyk 	}
165d2b2a2e3Sreyk 
166d2b2a2e3Sreyk 	return (0);
167d2b2a2e3Sreyk }
168d2b2a2e3Sreyk 
169d2b2a2e3Sreyk int
hostapd_bpf_open(u_int flags)170d2b2a2e3Sreyk hostapd_bpf_open(u_int flags)
171d2b2a2e3Sreyk {
172d2b2a2e3Sreyk 	int fd = -1;
173d2b2a2e3Sreyk 	struct bpf_version bpv;
174d2b2a2e3Sreyk 
1757b08a90aSnatano 	if ((fd = open("/dev/bpf", flags)) == -1) {
1762abf9a0dSnatano 		hostapd_fatal("unable to open BPF device: %s\n",
1772abf9a0dSnatano 		    strerror(errno));
178fe058dd8Sreyk 	}
179d2b2a2e3Sreyk 
180d2b2a2e3Sreyk 	/*
181d2b2a2e3Sreyk 	 * Get and validate the BPF version
182d2b2a2e3Sreyk 	 */
183d2b2a2e3Sreyk 
184d2b2a2e3Sreyk 	if (ioctl(fd, BIOCVERSION, &bpv) == -1)
185d2b2a2e3Sreyk 		hostapd_fatal("failed to get BPF version: %s\n",
186d2b2a2e3Sreyk 		    strerror(errno));
187d2b2a2e3Sreyk 
188d2b2a2e3Sreyk 	if (bpv.bv_major != BPF_MAJOR_VERSION ||
189d2b2a2e3Sreyk 	    bpv.bv_minor < BPF_MINOR_VERSION)
190d2b2a2e3Sreyk 		hostapd_fatal("invalid BPF version\n");
191d2b2a2e3Sreyk 
192d2b2a2e3Sreyk 	return (fd);
193d2b2a2e3Sreyk }
194d2b2a2e3Sreyk 
195d2b2a2e3Sreyk void
hostapd_udp_init(struct hostapd_config * cfg)196d2b2a2e3Sreyk hostapd_udp_init(struct hostapd_config *cfg)
197d2b2a2e3Sreyk {
1987c8c4753Sreyk 	struct hostapd_iapp *iapp = &cfg->c_iapp;
199d2b2a2e3Sreyk 	struct ifreq ifr;
200d2b2a2e3Sreyk 	struct sockaddr_in *addr, baddr;
201d2b2a2e3Sreyk 	struct ip_mreq mreq;
202d2b2a2e3Sreyk 	int brd = 1;
203d2b2a2e3Sreyk 
204d2b2a2e3Sreyk 	bzero(&ifr, sizeof(ifr));
205d2b2a2e3Sreyk 
206d2b2a2e3Sreyk 	/*
207d2b2a2e3Sreyk 	 * Open a listening UDP socket
208d2b2a2e3Sreyk 	 */
209d2b2a2e3Sreyk 
2107c8c4753Sreyk 	if ((iapp->i_udp = socket(AF_INET, SOCK_DGRAM, 0)) == -1)
211d2b2a2e3Sreyk 		hostapd_fatal("unable to open udp socket\n");
212d2b2a2e3Sreyk 
213d2b2a2e3Sreyk 	cfg->c_flags |= HOSTAPD_CFG_F_UDP;
214d2b2a2e3Sreyk 
215ae7a93f5Sreyk 	(void)strlcpy(ifr.ifr_name, iapp->i_iface, sizeof(ifr.ifr_name));
216d2b2a2e3Sreyk 
2177c8c4753Sreyk 	if (ioctl(iapp->i_udp, SIOCGIFADDR, &ifr) == -1)
218d2b2a2e3Sreyk 		hostapd_fatal("UDP ioctl %s on \"%s\" failed: %s\n",
219d2b2a2e3Sreyk 		    "SIOCGIFADDR", ifr.ifr_name, strerror(errno));
220d2b2a2e3Sreyk 
221d2b2a2e3Sreyk 	addr = (struct sockaddr_in *)&ifr.ifr_addr;
2227c8c4753Sreyk 	iapp->i_addr.sin_family = AF_INET;
2237c8c4753Sreyk 	iapp->i_addr.sin_addr.s_addr = addr->sin_addr.s_addr;
2247c8c4753Sreyk 	if (iapp->i_addr.sin_port == 0)
2257c8c4753Sreyk 		iapp->i_addr.sin_port = htons(IAPP_PORT);
226d2b2a2e3Sreyk 
2277c8c4753Sreyk 	if (ioctl(iapp->i_udp, SIOCGIFBRDADDR, &ifr) == -1)
228d2b2a2e3Sreyk 		hostapd_fatal("UDP ioctl %s on \"%s\" failed: %s\n",
229d2b2a2e3Sreyk 		    "SIOCGIFBRDADDR", ifr.ifr_name, strerror(errno));
230d2b2a2e3Sreyk 
231d2b2a2e3Sreyk 	addr = (struct sockaddr_in *)&ifr.ifr_addr;
2327c8c4753Sreyk 	iapp->i_broadcast.sin_family = AF_INET;
2337c8c4753Sreyk 	iapp->i_broadcast.sin_addr.s_addr = addr->sin_addr.s_addr;
2347c8c4753Sreyk 	iapp->i_broadcast.sin_port = iapp->i_addr.sin_port;
235d2b2a2e3Sreyk 
236d2b2a2e3Sreyk 	baddr.sin_family = AF_INET;
237d2b2a2e3Sreyk 	baddr.sin_addr.s_addr = htonl(INADDR_ANY);
2387c8c4753Sreyk 	baddr.sin_port = iapp->i_addr.sin_port;
239d2b2a2e3Sreyk 
2407c8c4753Sreyk 	if (bind(iapp->i_udp, (struct sockaddr *)&baddr,
241d2b2a2e3Sreyk 	    sizeof(baddr)) == -1)
242d2b2a2e3Sreyk 		hostapd_fatal("failed to bind UDP socket: %s\n",
243d2b2a2e3Sreyk 		    strerror(errno));
244d2b2a2e3Sreyk 
245d2b2a2e3Sreyk 	/*
246d2b2a2e3Sreyk 	 * The revised 802.11F standard requires IAPP messages to be
24776fb2f4fSreyk 	 * sent via multicast to the default group 224.0.1.178.
24876fb2f4fSreyk 	 * Nevertheless, some implementations still use broadcasts
24976fb2f4fSreyk 	 * for IAPP messages.
250d2b2a2e3Sreyk 	 */
251d2b2a2e3Sreyk 	if (cfg->c_flags & HOSTAPD_CFG_F_BRDCAST) {
252d2b2a2e3Sreyk 		/*
253d2b2a2e3Sreyk 		 * Enable broadcast
254d2b2a2e3Sreyk 		 */
2557c8c4753Sreyk 		if (setsockopt(iapp->i_udp, SOL_SOCKET, SO_BROADCAST,
256d2b2a2e3Sreyk 		    &brd, sizeof(brd)) == -1)
257d2b2a2e3Sreyk 			hostapd_fatal("failed to enable broadcast on socket\n");
25802fe3327Sreyk 
25902fe3327Sreyk 		hostapd_log(HOSTAPD_LOG_DEBUG, "%s: using broadcast mode "
260f83005a6Sreyk 		    "(address %s)", iapp->i_iface, inet_ntoa(addr->sin_addr));
261d2b2a2e3Sreyk 	} else {
262d2b2a2e3Sreyk 		/*
263d2b2a2e3Sreyk 		 * Enable multicast
264d2b2a2e3Sreyk 		 */
265d2b2a2e3Sreyk 		bzero(&mreq, sizeof(mreq));
266d2b2a2e3Sreyk 
2677c8c4753Sreyk 		iapp->i_multicast.sin_family = AF_INET;
2687c8c4753Sreyk 		if (iapp->i_multicast.sin_addr.s_addr == INADDR_ANY)
2697c8c4753Sreyk 			iapp->i_multicast.sin_addr.s_addr =
270d2b2a2e3Sreyk 			    inet_addr(IAPP_MCASTADDR);
2717c8c4753Sreyk 		iapp->i_multicast.sin_port = iapp->i_addr.sin_port;
272d2b2a2e3Sreyk 
273d2b2a2e3Sreyk 		mreq.imr_multiaddr.s_addr =
2747c8c4753Sreyk 		    iapp->i_multicast.sin_addr.s_addr;
275d2b2a2e3Sreyk 		mreq.imr_interface.s_addr =
2767c8c4753Sreyk 		    iapp->i_addr.sin_addr.s_addr;
277d2b2a2e3Sreyk 
2787c8c4753Sreyk 		if (setsockopt(iapp->i_udp, IPPROTO_IP,
279d2b2a2e3Sreyk 		    IP_ADD_MEMBERSHIP, &mreq, sizeof(mreq)) == -1)
280804d065bShenning 			hostapd_fatal("failed to add multicast membership to "
281804d065bShenning 			    "%s: %s\n", IAPP_MCASTADDR, strerror(errno));
28202fe3327Sreyk 
28302fe3327Sreyk 		if (setsockopt(iapp->i_udp, IPPROTO_IP, IP_MULTICAST_TTL,
284df69c215Sderaadt 		    &iapp->i_ttl, sizeof(iapp->i_ttl)) == -1)
28502fe3327Sreyk 			hostapd_fatal("failed to set multicast ttl to "
28602fe3327Sreyk 			    "%u: %s\n", iapp->i_ttl, strerror(errno));
28702fe3327Sreyk 
28802fe3327Sreyk 		hostapd_log(HOSTAPD_LOG_DEBUG, "%s: using multicast mode "
289f83005a6Sreyk 		    "(ttl %u, group %s)", iapp->i_iface, iapp->i_ttl,
29002fe3327Sreyk 		    inet_ntoa(iapp->i_multicast.sin_addr));
291d2b2a2e3Sreyk 	}
292d2b2a2e3Sreyk }
293d2b2a2e3Sreyk 
294d2b2a2e3Sreyk void
hostapd_sig_handler(int sig,short event,void * arg)2957b37258aSclaudio hostapd_sig_handler(int sig, short event, void *arg)
296d2b2a2e3Sreyk {
297d2b2a2e3Sreyk 	switch (sig) {
298d2b2a2e3Sreyk 	case SIGALRM:
299d2b2a2e3Sreyk 	case SIGTERM:
300d2b2a2e3Sreyk 	case SIGQUIT:
301d2b2a2e3Sreyk 	case SIGINT:
3027b37258aSclaudio 		(void)event_loopexit(NULL);
303d2b2a2e3Sreyk 	}
304d2b2a2e3Sreyk }
305d2b2a2e3Sreyk 
306d2b2a2e3Sreyk void
hostapd_cleanup(struct hostapd_config * cfg)307d2b2a2e3Sreyk hostapd_cleanup(struct hostapd_config *cfg)
308d2b2a2e3Sreyk {
3097c8c4753Sreyk 	struct hostapd_iapp *iapp = &cfg->c_iapp;
310d2b2a2e3Sreyk 	struct ip_mreq mreq;
311d01b6ac2Sreyk 	struct hostapd_apme *apme;
31250ce650fSreyk 	struct hostapd_table *table;
31350ce650fSreyk 	struct hostapd_entry *entry;
314d2b2a2e3Sreyk 
315d01b6ac2Sreyk 	/* Release all Host APs */
316d01b6ac2Sreyk 	if (cfg->c_flags & HOSTAPD_CFG_F_APME) {
317d01b6ac2Sreyk 		while ((apme = TAILQ_FIRST(&cfg->c_apmes)) != NULL)
318d01b6ac2Sreyk 			hostapd_apme_term(apme);
319d01b6ac2Sreyk 	}
320d01b6ac2Sreyk 
321d2b2a2e3Sreyk 	if (cfg->c_flags & HOSTAPD_CFG_F_PRIV &&
322d01b6ac2Sreyk 	    (cfg->c_flags & HOSTAPD_CFG_F_BRDCAST) == 0) {
323d2b2a2e3Sreyk 		/*
324d2b2a2e3Sreyk 		 * Disable multicast and let the kernel unsubscribe
325d2b2a2e3Sreyk 		 * from the multicast group.
326d2b2a2e3Sreyk 		 */
327d2b2a2e3Sreyk 
328d2b2a2e3Sreyk 		bzero(&mreq, sizeof(mreq));
329d2b2a2e3Sreyk 
330d2b2a2e3Sreyk 		mreq.imr_multiaddr.s_addr =
331d2b2a2e3Sreyk 		    inet_addr(IAPP_MCASTADDR);
332d2b2a2e3Sreyk 		mreq.imr_interface.s_addr =
3337c8c4753Sreyk 		    iapp->i_addr.sin_addr.s_addr;
334d2b2a2e3Sreyk 
3357c8c4753Sreyk 		if (setsockopt(iapp->i_udp, IPPROTO_IP,
336d2b2a2e3Sreyk 		    IP_DROP_MEMBERSHIP, &mreq, sizeof(mreq)) == -1)
337d2b2a2e3Sreyk 			hostapd_log(HOSTAPD_LOG, "failed to remove multicast"
338f83005a6Sreyk 			    " membership to %s: %s",
339d2b2a2e3Sreyk 			    IAPP_MCASTADDR, strerror(errno));
340d2b2a2e3Sreyk 	}
341d2b2a2e3Sreyk 
342d2b2a2e3Sreyk 	if ((cfg->c_flags & HOSTAPD_CFG_F_PRIV) == 0 &&
343d2b2a2e3Sreyk 	    cfg->c_flags & HOSTAPD_CFG_F_APME) {
344d2b2a2e3Sreyk 		/* Shutdown the Host AP protocol handler */
345d2b2a2e3Sreyk 		hostapd_iapp_term(&hostapd_cfg);
346d2b2a2e3Sreyk 	}
347d2b2a2e3Sreyk 
34850ce650fSreyk 	/* Cleanup tables */
34950ce650fSreyk 	while ((table = TAILQ_FIRST(&cfg->c_tables)) != NULL) {
3509f63336bSreyk 		while ((entry = RB_MIN(hostapd_tree, &table->t_tree)) != NULL) {
3519f63336bSreyk 			RB_REMOVE(hostapd_tree, &table->t_tree, entry);
35250ce650fSreyk 			free(entry);
35350ce650fSreyk 		}
35450ce650fSreyk 		while ((entry = TAILQ_FIRST(&table->t_mask_head)) != NULL) {
35550ce650fSreyk 			TAILQ_REMOVE(&table->t_mask_head, entry, e_entries);
35650ce650fSreyk 			free(entry);
35750ce650fSreyk 		}
35850ce650fSreyk 		TAILQ_REMOVE(&cfg->c_tables, table, t_entries);
35950ce650fSreyk 		free(table);
36050ce650fSreyk 	}
36150ce650fSreyk 
362f83005a6Sreyk 	hostapd_log(HOSTAPD_LOG_VERBOSE, "bye!");
363d2b2a2e3Sreyk }
364d2b2a2e3Sreyk 
365d2b2a2e3Sreyk int
main(int argc,char * argv[])366d2b2a2e3Sreyk main(int argc, char *argv[])
367d2b2a2e3Sreyk {
3687b37258aSclaudio 	struct event ev_sigalrm;
3697b37258aSclaudio 	struct event ev_sigterm;
3707b37258aSclaudio 	struct event ev_sigquit;
3717b37258aSclaudio 	struct event ev_sigint;
372d2b2a2e3Sreyk 	struct hostapd_config *cfg = &hostapd_cfg;
3737c8c4753Sreyk 	struct hostapd_iapp *iapp;
374d01b6ac2Sreyk 	struct hostapd_apme *apme;
375d01b6ac2Sreyk 	char *config = NULL;
376ae7a93f5Sreyk 	u_int debug = 0, ret;
377d2b2a2e3Sreyk 	int ch;
378d2b2a2e3Sreyk 
3793dfd0658Sreyk 	/* Set startup logging */
3803dfd0658Sreyk 	cfg->c_debug = 1;
3813dfd0658Sreyk 
382d2b2a2e3Sreyk 	/*
383d2b2a2e3Sreyk 	 * Get and parse command line options
384d2b2a2e3Sreyk 	 */
385cb46a11aSreyk 	while ((ch = getopt(argc, argv, "f:D:dv")) != -1) {
386d2b2a2e3Sreyk 		switch (ch) {
387d2b2a2e3Sreyk 		case 'f':
388d2b2a2e3Sreyk 			config = optarg;
389d2b2a2e3Sreyk 			break;
390d2b2a2e3Sreyk 		case 'D':
391d2b2a2e3Sreyk 			if (hostapd_parse_symset(optarg) < 0)
392804d065bShenning 				hostapd_fatal("could not parse macro "
3932dd2df06Sreyk 				    "definition %s\n", optarg);
394d2b2a2e3Sreyk 			break;
395d2b2a2e3Sreyk 		case 'd':
3963dfd0658Sreyk 			debug++;
397d2b2a2e3Sreyk 			break;
398d2b2a2e3Sreyk 		case 'v':
399d2b2a2e3Sreyk 			cfg->c_verbose++;
400d2b2a2e3Sreyk 			break;
401d2b2a2e3Sreyk 		default:
402d2b2a2e3Sreyk 			hostapd_usage();
403d2b2a2e3Sreyk 		}
404d2b2a2e3Sreyk 	}
405d2b2a2e3Sreyk 
406fde55bc4Spyr 	argc -= optind;
407fde55bc4Spyr 	argv += optind;
408fde55bc4Spyr 	if (argc > 0)
409e6a5de6eSmillert 		hostapd_usage();
410fde55bc4Spyr 
411d2b2a2e3Sreyk 	if (config == NULL)
412ae7a93f5Sreyk 		ret = strlcpy(cfg->c_config, HOSTAPD_CONFIG, sizeof(cfg->c_config));
413d2b2a2e3Sreyk 	else
414ae7a93f5Sreyk 		ret = strlcpy(cfg->c_config, config, sizeof(cfg->c_config));
415ae7a93f5Sreyk 	if (ret >= sizeof(cfg->c_config))
416ae7a93f5Sreyk 		hostapd_fatal("invalid configuration file\n");
417d2b2a2e3Sreyk 
41809c3f497Smsf 	if (geteuid())
41909c3f497Smsf 		hostapd_fatal("need root privileges\n");
42009c3f497Smsf 
421d2b2a2e3Sreyk 	/* Parse the configuration file */
42250ce650fSreyk 	if (hostapd_parse_file(cfg) != 0)
423cb46a11aSreyk 		hostapd_fatal("invalid configuration in %s\n", cfg->c_config);
424d2b2a2e3Sreyk 
4257c8c4753Sreyk 	iapp = &cfg->c_iapp;
4267c8c4753Sreyk 
427d2b2a2e3Sreyk 	if ((cfg->c_flags & HOSTAPD_CFG_F_IAPP) == 0)
428f57a7c3aSderaadt 		hostapd_fatal("IAPP interface not specified\n");
429d2b2a2e3Sreyk 
43050ce650fSreyk 	if (cfg->c_apme_dlt == 0)
43150ce650fSreyk 		cfg->c_apme_dlt = HOSTAPD_DLT;
43250ce650fSreyk 
433d2b2a2e3Sreyk 	/*
434d2b2a2e3Sreyk 	 * Setup the hostapd handlers
435d2b2a2e3Sreyk 	 */
436d2b2a2e3Sreyk 	hostapd_udp_init(cfg);
437d2b2a2e3Sreyk 	hostapd_llc_init(cfg);
438d2b2a2e3Sreyk 
4393dfd0658Sreyk 	/*
4403dfd0658Sreyk 	 * Set runtime logging and detach as daemon
4413dfd0658Sreyk 	 */
4423dfd0658Sreyk 	if ((cfg->c_debug = debug) == 0) {
4433dfd0658Sreyk 		openlog(__progname, LOG_PID | LOG_NDELAY, LOG_DAEMON);
444c07f4ffdSreyk 		tzset();
445ae7a93f5Sreyk 		if (daemon(0, 0) == -1)
446ae7a93f5Sreyk 			hostapd_fatal("failed to daemonize\n");
4473dfd0658Sreyk 	}
4483dfd0658Sreyk 
449d01b6ac2Sreyk 	if (cfg->c_flags & HOSTAPD_CFG_F_APME) {
450d01b6ac2Sreyk 		TAILQ_FOREACH(apme, &cfg->c_apmes, a_entries)
451d01b6ac2Sreyk 			hostapd_apme_init(apme);
452d01b6ac2Sreyk 	} else
453f83005a6Sreyk 		hostapd_log(HOSTAPD_LOG, "%s: running without a Host AP",
4547c8c4753Sreyk 		    iapp->i_iface);
455d2b2a2e3Sreyk 
456d2b2a2e3Sreyk 	/* Drop all privileges in an unprivileged child process */
457d2b2a2e3Sreyk 	hostapd_priv_init(cfg);
458d2b2a2e3Sreyk 
459d01b6ac2Sreyk 	if (cfg->c_flags & HOSTAPD_CFG_F_APME)
4607c8c4753Sreyk 		setproctitle("IAPP: %s, Host AP", iapp->i_iface);
461d01b6ac2Sreyk 	else
4627c8c4753Sreyk 		setproctitle("IAPP: %s", iapp->i_iface);
463d2b2a2e3Sreyk 
464d2b2a2e3Sreyk 	/*
465d2b2a2e3Sreyk 	 * Unprivileged child process
466d2b2a2e3Sreyk 	 */
467d2b2a2e3Sreyk 
468ae7a93f5Sreyk 	(void)event_init();
469d2b2a2e3Sreyk 
470d2b2a2e3Sreyk 	/*
471d2b2a2e3Sreyk 	 * Set signal handlers
472d2b2a2e3Sreyk 	 */
4737b37258aSclaudio 	signal_set(&ev_sigalrm, SIGALRM, hostapd_sig_handler, NULL);
4747b37258aSclaudio 	signal_set(&ev_sigterm, SIGTERM, hostapd_sig_handler, NULL);
4757b37258aSclaudio 	signal_set(&ev_sigquit, SIGQUIT, hostapd_sig_handler, NULL);
4767b37258aSclaudio 	signal_set(&ev_sigint, SIGINT, hostapd_sig_handler, NULL);
4777b37258aSclaudio 	signal_add(&ev_sigalrm, NULL);
4787b37258aSclaudio 	signal_add(&ev_sigterm, NULL);
4797b37258aSclaudio 	signal_add(&ev_sigquit, NULL);
4807b37258aSclaudio 	signal_add(&ev_sigint, NULL);
481d2b2a2e3Sreyk 	signal(SIGHUP, SIG_IGN);
482d2b2a2e3Sreyk 	signal(SIGCHLD, SIG_IGN);
483d2b2a2e3Sreyk 
4842dd2df06Sreyk 	/* Initialize the IAPP protocol handler */
485d2b2a2e3Sreyk 	hostapd_iapp_init(cfg);
486d2b2a2e3Sreyk 
487d2b2a2e3Sreyk 	/*
488d2b2a2e3Sreyk 	 * Schedule the Host AP listener
489d2b2a2e3Sreyk 	 */
490d2b2a2e3Sreyk 	if (cfg->c_flags & HOSTAPD_CFG_F_APME) {
491d01b6ac2Sreyk 		TAILQ_FOREACH(apme, &cfg->c_apmes, a_entries) {
492d01b6ac2Sreyk 			event_set(&apme->a_ev, apme->a_raw,
493d01b6ac2Sreyk 			    EV_READ | EV_PERSIST, hostapd_apme_input, apme);
494ae7a93f5Sreyk 			if (event_add(&apme->a_ev, NULL) == -1)
495ae7a93f5Sreyk 				hostapd_fatal("failed to add APME event");
496d01b6ac2Sreyk 		}
497d2b2a2e3Sreyk 	}
498d2b2a2e3Sreyk 
499d2b2a2e3Sreyk 	/*
500d2b2a2e3Sreyk 	 * Schedule the IAPP listener
501d2b2a2e3Sreyk 	 */
5027c8c4753Sreyk 	event_set(&iapp->i_udp_ev, iapp->i_udp, EV_READ | EV_PERSIST,
503d2b2a2e3Sreyk 	    hostapd_iapp_input, cfg);
504ae7a93f5Sreyk 	if (event_add(&iapp->i_udp_ev, NULL) == -1)
505ae7a93f5Sreyk 		hostapd_fatal("failed to add IAPP event");
506d2b2a2e3Sreyk 
507f83005a6Sreyk 	hostapd_log(HOSTAPD_LOG, "starting hostapd with pid %u",
508d01b6ac2Sreyk 	    getpid());
509d2b2a2e3Sreyk 
510d2b2a2e3Sreyk 	/* Run event loop */
511ae7a93f5Sreyk 	if (event_dispatch() == -1)
512ae7a93f5Sreyk 		hostapd_fatal("failed to dispatch hostapd");
513d2b2a2e3Sreyk 
514d2b2a2e3Sreyk 	/* Executed after the event loop has been terminated */
515d2b2a2e3Sreyk 	hostapd_cleanup(cfg);
516d2b2a2e3Sreyk 	return (EXIT_SUCCESS);
517d2b2a2e3Sreyk }
51850ce650fSreyk 
5198b938b68Sreyk void
hostapd_randval(u_int8_t * buf,const u_int len)5208b938b68Sreyk hostapd_randval(u_int8_t *buf, const u_int len)
5218b938b68Sreyk {
5228b938b68Sreyk 	u_int32_t data = 0;
5238b938b68Sreyk 	u_int i;
5248b938b68Sreyk 
5258b938b68Sreyk 	for (i = 0; i < len; i++) {
5268b938b68Sreyk 		if ((i % sizeof(data)) == 0)
5278b938b68Sreyk 			data = arc4random();
5288b938b68Sreyk 		buf[i] = data & 0xff;
5298b938b68Sreyk 		data >>= 8;
5308b938b68Sreyk 	}
5318b938b68Sreyk }
5328b938b68Sreyk 
53350ce650fSreyk struct hostapd_table *
hostapd_table_add(struct hostapd_config * cfg,const char * name)53450ce650fSreyk hostapd_table_add(struct hostapd_config *cfg, const char *name)
53550ce650fSreyk {
53650ce650fSreyk 	struct hostapd_table *table;
53750ce650fSreyk 
53850ce650fSreyk 	if (hostapd_table_lookup(cfg, name) != NULL)
53950ce650fSreyk 		return (NULL);
54050ce650fSreyk 	if ((table = (struct hostapd_table *)
54150ce650fSreyk 	    calloc(1, sizeof(struct hostapd_table))) == NULL)
54250ce650fSreyk 		return (NULL);
543ae7a93f5Sreyk 	if (strlcpy(table->t_name, name, sizeof(table->t_name)) >=
544ae7a93f5Sreyk 	    sizeof(table->t_name)) {
545ae7a93f5Sreyk 		free(table);
546ae7a93f5Sreyk 		return (NULL);
547ae7a93f5Sreyk 	}
5489f63336bSreyk 	RB_INIT(&table->t_tree);
54950ce650fSreyk 	TAILQ_INIT(&table->t_mask_head);
55050ce650fSreyk 	TAILQ_INSERT_TAIL(&cfg->c_tables, table, t_entries);
55150ce650fSreyk 
55250ce650fSreyk 	return (table);
55350ce650fSreyk }
55450ce650fSreyk 
55550ce650fSreyk struct hostapd_table *
hostapd_table_lookup(struct hostapd_config * cfg,const char * name)55650ce650fSreyk hostapd_table_lookup(struct hostapd_config *cfg, const char *name)
55750ce650fSreyk {
55850ce650fSreyk 	struct hostapd_table *table;
55950ce650fSreyk 
56050ce650fSreyk 	TAILQ_FOREACH(table, &cfg->c_tables, t_entries) {
56150ce650fSreyk 		if (strcmp(name, table->t_name) == 0)
56250ce650fSreyk 			return (table);
56350ce650fSreyk 	}
56450ce650fSreyk 
56550ce650fSreyk 	return (NULL);
56650ce650fSreyk }
56750ce650fSreyk 
56850ce650fSreyk struct hostapd_entry *
hostapd_entry_add(struct hostapd_table * table,u_int8_t * lladdr)56950ce650fSreyk hostapd_entry_add(struct hostapd_table *table, u_int8_t *lladdr)
57050ce650fSreyk {
57150ce650fSreyk 	struct hostapd_entry *entry;
57250ce650fSreyk 
57350ce650fSreyk 	if (hostapd_entry_lookup(table, lladdr) != NULL)
57450ce650fSreyk 		return (NULL);
57550ce650fSreyk 
57650ce650fSreyk 	if ((entry = (struct hostapd_entry *)
57750ce650fSreyk 	    calloc(1, sizeof(struct hostapd_entry))) == NULL)
57850ce650fSreyk 		return (NULL);
57950ce650fSreyk 
58050ce650fSreyk 	bcopy(lladdr, entry->e_lladdr, IEEE80211_ADDR_LEN);
5819f63336bSreyk 	RB_INSERT(hostapd_tree, &table->t_tree, entry);
58250ce650fSreyk 
58350ce650fSreyk 	return (entry);
58450ce650fSreyk }
58550ce650fSreyk 
58650ce650fSreyk struct hostapd_entry *
hostapd_entry_lookup(struct hostapd_table * table,u_int8_t * lladdr)58750ce650fSreyk hostapd_entry_lookup(struct hostapd_table *table, u_int8_t *lladdr)
58850ce650fSreyk {
5899f63336bSreyk 	struct hostapd_entry *entry, key;
59050ce650fSreyk 
5919f63336bSreyk 	bcopy(lladdr, key.e_lladdr, IEEE80211_ADDR_LEN);
5929f63336bSreyk 	if ((entry = RB_FIND(hostapd_tree, &table->t_tree, &key)) != NULL)
59350ce650fSreyk 		return (entry);
59450ce650fSreyk 
5959f63336bSreyk 	/* Masked entries can't be handled by the red-black tree */
59650ce650fSreyk 	TAILQ_FOREACH(entry, &table->t_mask_head, e_entries) {
59750ce650fSreyk 		if (HOSTAPD_ENTRY_MASK_MATCH(entry, lladdr))
59850ce650fSreyk 			return (entry);
59950ce650fSreyk 	}
60050ce650fSreyk 
60150ce650fSreyk 	return (NULL);
60250ce650fSreyk }
60350ce650fSreyk 
60450ce650fSreyk void
hostapd_entry_update(struct hostapd_table * table,struct hostapd_entry * entry)60550ce650fSreyk hostapd_entry_update(struct hostapd_table *table, struct hostapd_entry *entry)
60650ce650fSreyk {
6079f63336bSreyk 	RB_REMOVE(hostapd_tree, &table->t_tree, entry);
60850ce650fSreyk 
60950ce650fSreyk 	/* Apply mask to entry */
61050ce650fSreyk 	if (entry->e_flags & HOSTAPD_ENTRY_F_MASK) {
61150ce650fSreyk 		HOSTAPD_ENTRY_MASK_ADD(entry->e_lladdr, entry->e_mask);
61250ce650fSreyk 		TAILQ_INSERT_TAIL(&table->t_mask_head, entry, e_entries);
61350ce650fSreyk 	} else {
6149f63336bSreyk 		RB_INSERT(hostapd_tree, &table->t_tree, entry);
61550ce650fSreyk 	}
61650ce650fSreyk }
6179f63336bSreyk 
6182a7c9000Sreyk static __inline int
hostapd_entry_cmp(struct hostapd_entry * a,struct hostapd_entry * b)6199f63336bSreyk hostapd_entry_cmp(struct hostapd_entry *a, struct hostapd_entry *b)
6209f63336bSreyk {
62184b9a8f2Sreyk 	return (memcmp(a->e_lladdr, b->e_lladdr, IEEE80211_ADDR_LEN));
6229f63336bSreyk }
6239f63336bSreyk 
6249f63336bSreyk RB_GENERATE(hostapd_tree, hostapd_entry, e_nodes, hostapd_entry_cmp);
625