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