xref: /openbsd-src/usr.sbin/hostapd/parse.y (revision f2da64fbbbf1b03f09f390ab01267c93dfd77c4c)
1 /*	$OpenBSD: parse.y,v 1.51 2016/06/21 21:35:24 benno Exp $	*/
2 
3 /*
4  * Copyright (c) 2004, 2005, 2006 Reyk Floeter <reyk@openbsd.org>
5  * Copyright (c) 2002 - 2005 Henning Brauer <henning@openbsd.org>
6  * Copyright (c) 2001 Markus Friedl.  All rights reserved.
7  * Copyright (c) 2001 Daniel Hartmeier.  All rights reserved.
8  * Copyright (c) 2001 Theo de Raadt.  All rights reserved.
9  *
10  * Permission to use, copy, modify, and distribute this software for any
11  * purpose with or without fee is hereby granted, provided that the above
12  * copyright notice and this permission notice appear in all copies.
13  *
14  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
15  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
16  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
17  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
18  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
19  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
20  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
21  */
22 
23 %{
24 #include <sys/ioctl.h>
25 #include <sys/types.h>
26 #include <sys/socket.h>
27 #include <sys/time.h>
28 #include <sys/queue.h>
29 #include <sys/stat.h>
30 
31 #include <net/if.h>
32 #include <net/if_dl.h>
33 #include <net/if_media.h>
34 #include <net/if_arp.h>
35 #include <net/if_llc.h>
36 #include <net/bpf.h>
37 
38 #include <netinet/in.h>
39 #include <netinet/if_ether.h>
40 #include <arpa/inet.h>
41 
42 #include <net80211/ieee80211.h>
43 #include <net80211/ieee80211_radiotap.h>
44 
45 #include <ctype.h>
46 #include <errno.h>
47 #include <event.h>
48 #include <fcntl.h>
49 #include <stdio.h>
50 #include <stdlib.h>
51 #include <stdarg.h>
52 #include <string.h>
53 #include <unistd.h>
54 #include <limits.h>
55 #include <stdint.h>
56 #include <err.h>
57 
58 #include "hostapd.h"
59 
60 TAILQ_HEAD(files, file)		 files = TAILQ_HEAD_INITIALIZER(files);
61 static struct file {
62 	TAILQ_ENTRY(file)	 entry;
63 	FILE			*stream;
64 	char			*name;
65 	int			 lineno;
66 	int			 errors;
67 } *file, *topfile;
68 struct file	*pushfile(const char *, int);
69 int		 popfile(void);
70 int		 check_file_secrecy(int, const char *);
71 int		 yyparse(void);
72 int		 yylex(void);
73 int		 yyerror(const char *, ...)
74     __attribute__((__format__ (printf, 1, 2)))
75     __attribute__((__nonnull__ (1)));
76 int		 kw_cmp(const void *, const void *);
77 int		 lookup(char *);
78 int		 lgetc(int);
79 int		 lungetc(int);
80 int		 findeol(void);
81 
82 TAILQ_HEAD(symhead, sym)	 symhead = TAILQ_HEAD_INITIALIZER(symhead);
83 struct sym {
84 	TAILQ_ENTRY(sym)	 entry;
85 	int			 used;
86 	int			 persist;
87 	char			*nam;
88 	char			*val;
89 };
90 int		 symset(const char *, const char *, int);
91 char		*symget(const char *);
92 
93 extern struct hostapd_config hostapd_cfg;
94 
95 typedef struct {
96 	union {
97 		struct {
98 			u_int8_t		lladdr[IEEE80211_ADDR_LEN];
99 			struct hostapd_table	*table;
100 			u_int32_t		flags;
101 		} reflladdr;
102 		struct {
103 			u_int16_t		alg;
104 			u_int16_t		transaction;
105 		} authalg;
106 		struct in_addr		in;
107 		char			*string;
108 		int64_t			number;
109 		u_int16_t		reason;
110 		enum hostapd_op		op;
111 		struct timeval		timeout;
112 	} v;
113 	int lineno;
114 } YYSTYPE;
115 
116 struct hostapd_apme *apme;
117 struct hostapd_table *table;
118 struct hostapd_entry *entry;
119 struct hostapd_frame frame, *frame_ptr;
120 struct hostapd_ieee80211_frame *frame_ieee80211;
121 
122 #define HOSTAPD_MATCH(_m, _not)	{					\
123 	frame.f_flags |= (_not) ?					\
124 	    HOSTAPD_FRAME_F_##_m##_N : HOSTAPD_FRAME_F_##_m;		\
125 }
126 #define HOSTAPD_MATCH_TABLE(_m, _not)	{				\
127 	frame.f_flags |= HOSTAPD_FRAME_F_##_m##_TABLE | ((_not) ?	\
128 	    HOSTAPD_FRAME_F_##_m##_N : HOSTAPD_FRAME_F_##_m);		\
129 }
130 #define HOSTAPD_MATCH_RADIOTAP(_x) {					\
131 	if (hostapd_cfg.c_apme_dlt == DLT_IEEE802_11 ||			\
132 	    (hostapd_cfg.c_apme_dlt == 0 &&				\
133 	    HOSTAPD_DLT == DLT_IEEE802_11)) {				\
134 		yyerror("option %s requires radiotap headers", #_x);	\
135 		YYERROR;						\
136 	}								\
137 	frame.f_radiotap |= HOSTAPD_RADIOTAP_F(RSSI);			\
138 	frame.f_flags |= HOSTAPD_FRAME_F_##_x;				\
139 }
140 #define HOSTAPD_IAPP_FLAG(_f, _not) {					\
141 	if (_not)							\
142 		hostapd_cfg.c_iapp.i_flags &= ~(HOSTAPD_IAPP_F_##_f);	\
143 	else								\
144 		hostapd_cfg.c_iapp.i_flags |= (HOSTAPD_IAPP_F_##_f);	\
145 }
146 
147 %}
148 
149 %token	MODE INTERFACE IAPP HOSTAP MULTICAST BROADCAST SET SEC USEC
150 %token	HANDLE TYPE SUBTYPE FROM TO BSSID WITH FRAME RADIOTAP NWID PASSIVE
151 %token	MANAGEMENT DATA PROBE BEACON ATIM ANY DS NO DIR RESEND RANDOM
152 %token	AUTH DEAUTH ASSOC DISASSOC REASSOC REQUEST RESPONSE PCAP RATE
153 %token	ERROR CONST TABLE NODE DELETE ADD LOG VERBOSE LIMIT QUICK SKIP
154 %token	REASON UNSPECIFIED EXPIRE LEAVE ASSOC TOOMANY NOT AUTHED ASSOCED
155 %token	RESERVED RSN REQUIRED INCONSISTENT IE INVALID MIC FAILURE OPEN
156 %token	ADDRESS PORT ON NOTIFY TTL INCLUDE ROUTE ROAMING RSSI TXRATE FREQ
157 %token	HOPPER DELAY NE LE GE ARROW
158 %token	<v.string>	STRING
159 %token	<v.number>	NUMBER
160 %type	<v.in>		ipv4addr
161 %type	<v.reflladdr>	refaddr, lladdr, randaddr, frmactionaddr, frmmatchaddr
162 %type	<v.reason>	frmreason_l
163 %type	<v.string>	table
164 %type	<v.string>	string
165 %type	<v.authalg>	authalg
166 %type	<v.op>		unaryop
167 %type	<v.number>	percent
168 %type	<v.number>	txrate
169 %type	<v.number>	freq
170 %type	<v.number>	not
171 %type	<v.timeout>	timeout
172 
173 %%
174 
175 /*
176  * Configuration grammar
177  */
178 
179 grammar		: /* empty */
180 		| grammar '\n'
181 		| grammar include '\n'
182 		| grammar tabledef '\n'
183 		| grammar option '\n'
184 		| grammar event '\n'
185 		| grammar varset '\n'
186 		| grammar error '\n'		{ file->errors++; }
187 		;
188 
189 include		: INCLUDE STRING
190 		{
191 			struct file *nfile;
192 
193 			if ((nfile =
194 			    pushfile($2, 1)) == NULL) {
195 				yyerror("failed to include file %s", $2);
196 				free($2);
197 				YYERROR;
198 			}
199 			free($2);
200 
201 			file = nfile;
202 			lungetc('\n');
203 		}
204 
205 option		: SET HOSTAP INTERFACE hostapifaces
206 		{
207 			if (!TAILQ_EMPTY(&hostapd_cfg.c_apmes))
208 				hostapd_cfg.c_flags |= HOSTAPD_CFG_F_APME;
209 		}
210 		| SET HOSTAP HOPPER INTERFACE hopperifaces
211 		| SET HOSTAP HOPPER DELAY timeout
212 		{
213 			bcopy(&$5, &hostapd_cfg.c_apme_hopdelay,
214 			    sizeof(struct timeval));
215 		}
216 		| SET HOSTAP MODE hostapmode
217 		| SET IAPP INTERFACE STRING passive
218 		{
219 			if (strlcpy(hostapd_cfg.c_iapp.i_iface, $4,
220 			    sizeof(hostapd_cfg.c_iapp.i_iface)) >=
221 			    sizeof(hostapd_cfg.c_iapp.i_iface)) {
222 				yyerror("invalid interface %s", $4);
223 				free($4);
224 				YYERROR;
225 			}
226 
227 			hostapd_cfg.c_flags |= HOSTAPD_CFG_F_IAPP;
228 
229 			hostapd_log(HOSTAPD_LOG_DEBUG,
230 			    "%s: IAPP interface added", $4);
231 
232 			free($4);
233 		}
234 		| SET IAPP MODE iappmode
235 		| SET IAPP ADDRESS ROAMING TABLE table
236 		{
237 			if ((hostapd_cfg.c_iapp.i_addr_tbl =
238 			    hostapd_table_lookup(&hostapd_cfg, $6)) == NULL) {
239 				yyerror("undefined table <%s>", $6);
240 				free($6);
241 				YYERROR;
242 			}
243 			free($6);
244 		}
245 		| SET IAPP ROUTE ROAMING TABLE table
246 		{
247 			if ((hostapd_cfg.c_iapp.i_route_tbl =
248 			    hostapd_table_lookup(&hostapd_cfg, $6)) == NULL) {
249 				yyerror("undefined table <%s>", $6);
250 				free($6);
251 				YYERROR;
252 			}
253 			free($6);
254 		}
255 		| SET IAPP HANDLE SUBTYPE iappsubtypes
256 		;
257 
258 iappmode	: MULTICAST iappmodeaddr iappmodeport iappmodettl
259 		{
260 			hostapd_cfg.c_flags &= ~HOSTAPD_CFG_F_BRDCAST;
261 		}
262 		| BROADCAST iappmodeport
263 		{
264 			hostapd_cfg.c_flags |= HOSTAPD_CFG_F_BRDCAST;
265 		}
266 		;
267 
268 iappmodeaddr	: /* empty */
269 		| ADDRESS ipv4addr
270 		{
271 			bcopy(&$2, &hostapd_cfg.c_iapp.i_multicast.sin_addr,
272 			    sizeof(struct in_addr));
273 		}
274 		;
275 
276 iappmodeport	: /* empty */
277 		| PORT NUMBER
278 		{
279 			if ($2 < 0 || $2 > UINT16_MAX) {
280 				yyerror("port out of range: %lld", $2);
281 				YYERROR;
282 			}
283 			hostapd_cfg.c_iapp.i_addr.sin_port = htons($2);
284 		}
285 		;
286 
287 iappmodettl	: /* empty */
288 		| TTL NUMBER
289 		{
290 			if ($2 < 1 || $2 > UINT8_MAX) {
291 				yyerror("ttl out of range: %lld", $2);
292 				YYERROR;
293 			}
294 			hostapd_cfg.c_iapp.i_ttl = $2;
295 		}
296 		;
297 
298 hostapmode	: RADIOTAP
299 		{
300 			hostapd_cfg.c_apme_dlt = DLT_IEEE802_11_RADIO;
301 		}
302 		| PCAP
303 		{
304 			hostapd_cfg.c_apme_dlt = DLT_IEEE802_11;
305 		}
306 		;
307 
308 hostapifaces	: '{' optnl hostapifacelist optnl '}'
309 		| hostapiface
310 		;
311 
312 hostapifacelist	: hostapiface
313 		| hostapifacelist comma hostapiface
314 		;
315 
316 hostapiface	: STRING
317 		{
318 			if (hostapd_apme_add(&hostapd_cfg, $1) != 0) {
319 				yyerror("failed to add hostap interface");
320 				YYERROR;
321 			}
322 			free($1);
323 		}
324 		;
325 
326 hopperifaces	: '{' optnl hopperifacelist optnl '}'
327 		| hopperiface
328 		;
329 
330 hopperifacelist	: hopperiface
331 		| hopperifacelist comma hopperiface
332 		;
333 
334 hopperiface	: STRING
335 		{
336 			if ((apme = hostapd_apme_addhopper(&hostapd_cfg,
337 			    $1)) == NULL) {
338 				yyerror("failed to add hopper %s", $1);
339 				free($1);
340 				YYERROR;
341 			}
342 			free($1);
343 		}
344 		;
345 
346 hostapmatch	: /* empty */
347 		| ON not STRING
348 		{
349 			if ((frame.f_apme =
350 			    hostapd_apme_lookup(&hostapd_cfg, $3)) == NULL) {
351 				yyerror("undefined hostap interface");
352 				free($3);
353 				YYERROR;
354 			}
355 			free($3);
356 
357 			HOSTAPD_MATCH(APME, $2);
358 		}
359 		;
360 
361 event		: HOSTAP HANDLE
362 		{
363 			bzero(&frame, sizeof(struct hostapd_frame));
364 			/* IEEE 802.11 frame to match */
365 			frame_ieee80211 = &frame.f_frame;
366 		} eventopt hostapmatch frmmatch {
367 			/* IEEE 802.11 raw frame to send as an action */
368 			frame_ieee80211 = &frame.f_action_data.a_frame;
369 		} action limit rate {
370 			if ((frame_ptr = calloc(1, sizeof(struct hostapd_frame)))
371 			    == NULL) {
372 				yyerror("calloc");
373 				YYERROR;
374 			}
375 
376 			if (gettimeofday(&frame.f_last, NULL) == -1)
377 				hostapd_fatal("gettimeofday");
378 			timeradd(&frame.f_last, &frame.f_limit, &frame.f_then);
379 
380 			bcopy(&frame, frame_ptr, sizeof(struct hostapd_frame));
381 			TAILQ_INSERT_TAIL(&hostapd_cfg.c_frames,
382 			    frame_ptr, f_entries);
383 		}
384 		;
385 
386 iappsubtypes	: '{' optnl iappsubtypelist optnl '}'
387 		| iappsubtype
388 		;
389 
390 iappsubtypelist	: iappsubtype
391 		| iappsubtypelist comma iappsubtype
392 		;
393 
394 iappsubtype	: not ADD NOTIFY
395 		{
396 			HOSTAPD_IAPP_FLAG(ADD_NOTIFY, $1);
397 		}
398 		| not RADIOTAP
399 		{
400 			HOSTAPD_IAPP_FLAG(RADIOTAP, $1);
401 		}
402 		| not ROUTE ROAMING
403 		{
404 			HOSTAPD_IAPP_FLAG(ROAMING_ROUTE, $1);
405 		}
406 		| not ADDRESS ROAMING
407 		{
408 			HOSTAPD_IAPP_FLAG(ROAMING_ADDRESS, $1);
409 		}
410 		;
411 
412 eventopt	: /* empty */
413 		{
414 			frame.f_flags |= HOSTAPD_FRAME_F_RET_OK;
415 		}
416 		| QUICK
417 		{
418 			frame.f_flags |= HOSTAPD_FRAME_F_RET_QUICK;
419 		}
420 		| SKIP
421 		{
422 			frame.f_flags |= HOSTAPD_FRAME_F_RET_SKIP;
423 		}
424 		;
425 
426 action		: /* empty */
427 		{
428 			frame.f_action = HOSTAPD_ACTION_NONE;
429 		}
430 		| WITH LOG verbose
431 		{
432 			frame.f_action = HOSTAPD_ACTION_LOG;
433 		}
434 		| WITH FRAME frmaction
435 		{
436 			frame.f_action = HOSTAPD_ACTION_FRAME;
437 		}
438 		| WITH IAPP iapp
439 		| WITH NODE nodeopt frmactionaddr
440 		{
441 			if (($4.flags & HOSTAPD_ACTION_F_REF_M) == 0) {
442 				bcopy($4.lladdr, frame.f_action_data.a_lladdr,
443 				    IEEE80211_ADDR_LEN);
444 			} else
445 				frame.f_action_data.a_flags |= $4.flags;
446 		}
447 		| WITH RESEND
448 		{
449 			frame.f_action = HOSTAPD_ACTION_RESEND;
450 		}
451 		;
452 
453 verbose		: /* empty */
454 		| VERBOSE
455 		{
456 			frame.f_action_flags |= HOSTAPD_ACTION_VERBOSE;
457 		}
458 		;
459 
460 iapp		: TYPE RADIOTAP verbose
461 		{
462 			frame.f_action = HOSTAPD_ACTION_RADIOTAP;
463 		}
464 		;
465 
466 nodeopt		: DELETE
467 		{
468 			frame.f_action = HOSTAPD_ACTION_DELNODE;
469 		}
470 		| ADD
471 		{
472 			frame.f_action = HOSTAPD_ACTION_ADDNODE;
473 		}
474 		;
475 
476 frmmatch	: ANY
477 		| frm frmmatchtype frmmatchdir frmmatchfrom frmmatchto
478 			frmmatchbssid frmmatchrtap
479 		;
480 
481 frm		: /* empty */
482 		| FRAME
483 		;
484 
485 frmaction	: frmactiontype frmactiondir frmactionfrom frmactionto frmactionbssid
486 		;
487 
488 limit		: /* empty */
489 		| LIMIT NUMBER SEC
490 		{
491 			if ($2 < 0 || $2 > LONG_MAX) {
492 				yyerror("limit out of range: %lld sec", $2);
493 				YYERROR;
494 			}
495 			frame.f_limit.tv_sec = $2;
496 		}
497 		| LIMIT NUMBER USEC
498 		{
499 			if ($2 < 0 || $2 > LONG_MAX) {
500 				yyerror("limit out of range: %lld usec", $2);
501 				YYERROR;
502 			}
503 			frame.f_limit.tv_usec = $2;
504 		}
505 		;
506 
507 rate		: /* empty */
508 		| RATE NUMBER '/' NUMBER SEC
509 		{
510 			if (($2 < 1 || $2 > LONG_MAX) ||
511 			    ($4 < 1 || $4 > LONG_MAX)) {
512 				yyerror("rate out of range: %lld/%lld sec",
513 				    $2, $4);
514 				YYERROR;
515 			}
516 
517 			if (!($2 && $4)) {
518 				yyerror("invalid rate");
519 				YYERROR;
520 			}
521 
522 			frame.f_rate = $2;
523 			frame.f_rate_intval = $4;
524 		}
525 		;
526 
527 frmmatchtype	: /* any */
528 		| TYPE ANY
529 		| TYPE not DATA
530 		{
531 			frame_ieee80211->i_fc[0] |=
532 			    IEEE80211_FC0_TYPE_DATA;
533 			HOSTAPD_MATCH(TYPE, $2);
534 		}
535 		| TYPE not MANAGEMENT frmmatchmgmt
536 		{
537 			frame_ieee80211->i_fc[0] |=
538 			    IEEE80211_FC0_TYPE_MGT;
539 			HOSTAPD_MATCH(TYPE, $2);
540 		}
541 		;
542 
543 frmmatchmgmt	: /* any */
544 		| SUBTYPE ANY
545 		| SUBTYPE not frmsubtype
546 		{
547 			HOSTAPD_MATCH(SUBTYPE, $2);
548 		}
549 		;
550 
551 frmsubtype	: PROBE REQUEST frmelems
552 		{
553 			frame_ieee80211->i_fc[0] |=
554 			    IEEE80211_FC0_SUBTYPE_PROBE_REQ;
555 		}
556 		| PROBE RESPONSE frmelems
557 		{
558 			frame_ieee80211->i_fc[0] |=
559 			    IEEE80211_FC0_SUBTYPE_PROBE_RESP;
560 		}
561 		| BEACON frmelems
562 		{
563 			frame_ieee80211->i_fc[0] |=
564 			    IEEE80211_FC0_SUBTYPE_BEACON;
565 		}
566 		| ATIM
567 		{
568 			frame_ieee80211->i_fc[0] |=
569 			    IEEE80211_FC0_SUBTYPE_ATIM;
570 		}
571 		| AUTH frmauth
572 		{
573 			frame_ieee80211->i_fc[0] |=
574 			    IEEE80211_FC0_SUBTYPE_AUTH;
575 		}
576 		| DEAUTH frmreason
577 		{
578 			frame_ieee80211->i_fc[0] |=
579 			    IEEE80211_FC0_SUBTYPE_DEAUTH;
580 		}
581 		| ASSOC REQUEST
582 		{
583 			frame_ieee80211->i_fc[0] |=
584 			    IEEE80211_FC0_SUBTYPE_ASSOC_REQ;
585 		}
586 		| DISASSOC frmreason
587 		{
588 			frame_ieee80211->i_fc[0] |=
589 			    IEEE80211_FC0_SUBTYPE_DISASSOC;
590 		}
591 		| ASSOC RESPONSE
592 		{
593 			frame_ieee80211->i_fc[0] |=
594 			    IEEE80211_FC0_SUBTYPE_ASSOC_RESP;
595 		}
596 		| REASSOC REQUEST
597 		{
598 			frame_ieee80211->i_fc[0] |=
599 			    IEEE80211_FC0_SUBTYPE_REASSOC_REQ;
600 		}
601 		| REASSOC RESPONSE
602 		{
603 			frame_ieee80211->i_fc[0] |=
604 			    IEEE80211_FC0_SUBTYPE_REASSOC_RESP;
605 		}
606 		;
607 
608 frmelems	: /* empty */
609 		| frmelems_l
610 		;
611 
612 frmelems_l	: frmelems_l frmelem
613 		| frmelem
614 		;
615 
616 frmelem		: NWID not STRING
617 		;
618 
619 frmauth		: /* empty */
620 		| authalg
621 		{
622 			if ((frame_ieee80211->i_data = malloc(6)) == NULL) {
623 				yyerror("failed to allocate auth");
624 				YYERROR;
625 			}
626 			((u_int16_t *)frame_ieee80211->i_data)[0] =
627 				$1.alg;
628 			((u_int16_t *)frame_ieee80211->i_data)[1] =
629 				$1.transaction;
630 			((u_int16_t *)frame_ieee80211->i_data)[0] = 0;
631 			frame_ieee80211->i_data_len = 6;
632 		}
633 		;
634 
635 authalg		: OPEN REQUEST
636 		{
637 			$$.alg = htole16(IEEE80211_AUTH_ALG_OPEN);
638 			$$.transaction = htole16(IEEE80211_AUTH_OPEN_REQUEST);
639 		}
640 		| OPEN RESPONSE
641 		{
642 			$$.alg = htole16(IEEE80211_AUTH_ALG_OPEN);
643 			$$.transaction = htole16(IEEE80211_AUTH_OPEN_RESPONSE);
644 		}
645 		;
646 
647 frmreason	: frmreason_l
648 		{
649 			if ($1 != 0) {
650 				if ((frame_ieee80211->i_data =
651 				    malloc(sizeof(u_int16_t))) == NULL) {
652 					yyerror("failed to allocate "
653 					    "reason code %u", $1);
654 					YYERROR;
655 				}
656 				*(u_int16_t *)frame_ieee80211->i_data =
657 				    htole16($1);
658 				frame_ieee80211->i_data_len = sizeof(u_int16_t);
659 			}
660 		}
661 		;
662 
663 frmreason_l	: /* empty */
664 		{
665 			$$ = 0;
666 		}
667 		| REASON UNSPECIFIED
668 		{
669 			$$ = IEEE80211_REASON_UNSPECIFIED;
670 		}
671 		| REASON AUTH EXPIRE
672 		{
673 			$$ = IEEE80211_REASON_AUTH_EXPIRE;
674 		}
675 		| REASON AUTH LEAVE
676 		{
677 			$$ = IEEE80211_REASON_AUTH_LEAVE;
678 		}
679 		| REASON ASSOC EXPIRE
680 		{
681 			$$ = IEEE80211_REASON_ASSOC_EXPIRE;
682 		}
683 		| REASON ASSOC TOOMANY
684 		{
685 			$$ = IEEE80211_REASON_ASSOC_TOOMANY;
686 		}
687 		| REASON NOT AUTHED
688 		{
689 			$$ = IEEE80211_REASON_NOT_AUTHED;
690 		}
691 		| REASON NOT ASSOCED
692 		{
693 			$$ = IEEE80211_REASON_NOT_ASSOCED;
694 		}
695 		| REASON ASSOC LEAVE
696 		{
697 			$$ = IEEE80211_REASON_ASSOC_LEAVE;
698 		}
699 		| REASON ASSOC NOT AUTHED
700 		{
701 			$$ = IEEE80211_REASON_NOT_AUTHED;
702 		}
703 		| REASON RESERVED
704 		{
705 			$$ = 10;	/* XXX unknown */
706 		}
707 		| REASON RSN REQUIRED
708 		{
709 			$$ = IEEE80211_REASON_RSN_REQUIRED;
710 		}
711 		| REASON RSN INCONSISTENT
712 		{
713 			$$ = IEEE80211_REASON_RSN_INCONSISTENT;
714 		}
715 		| REASON IE INVALID
716 		{
717 			$$ = IEEE80211_REASON_IE_INVALID;
718 		}
719 		| REASON MIC FAILURE
720 		{
721 			$$ = IEEE80211_REASON_MIC_FAILURE;
722 		}
723 		;
724 
725 frmmatchdir	: /* any */
726 		| DIR ANY
727 		| DIR not frmdir
728 		{
729 			HOSTAPD_MATCH(DIR, $2);
730 		}
731 		;
732 
733 frmdir		: NO DS
734 		{
735 			frame_ieee80211->i_fc[1] |= IEEE80211_FC1_DIR_NODS;
736 		}
737 		| TO DS
738 		{
739 			frame_ieee80211->i_fc[1] |= IEEE80211_FC1_DIR_TODS;
740 		}
741 		| FROM DS
742 		{
743 			frame_ieee80211->i_fc[1] |= IEEE80211_FC1_DIR_FROMDS;
744 		}
745 		| DS TO DS
746 		{
747 			frame_ieee80211->i_fc[1] |= IEEE80211_FC1_DIR_DSTODS;
748 		}
749 		;
750 
751 frmmatchfrom	: /* any */
752 		| FROM ANY
753 		| FROM not frmmatchaddr
754 		{
755 			if (($3.flags & HOSTAPD_ACTION_F_OPT_TABLE) == 0) {
756 				bcopy($3.lladdr, &frame_ieee80211->i_from,
757 				    IEEE80211_ADDR_LEN);
758 				HOSTAPD_MATCH(FROM, $2);
759 			} else {
760 				frame.f_from = $3.table;
761 				HOSTAPD_MATCH_TABLE(FROM, $2);
762 			}
763 		}
764 		;
765 
766 frmmatchto	: /* any */
767 		| TO ANY
768 		| TO not frmmatchaddr
769 		{
770 			if (($3.flags & HOSTAPD_ACTION_F_OPT_TABLE) == 0) {
771 				bcopy($3.lladdr, &frame_ieee80211->i_to,
772 				    IEEE80211_ADDR_LEN);
773 				HOSTAPD_MATCH(TO, $2);
774 			} else {
775 				frame.f_to = $3.table;
776 				HOSTAPD_MATCH_TABLE(TO, $2);
777 			}
778 		}
779 		;
780 
781 frmmatchbssid	: /* any */
782 		| BSSID ANY
783 		| BSSID not frmmatchaddr
784 		{
785 			if (($3.flags & HOSTAPD_ACTION_F_OPT_TABLE) == 0) {
786 				bcopy($3.lladdr, &frame_ieee80211->i_bssid,
787 				    IEEE80211_ADDR_LEN);
788 				HOSTAPD_MATCH(BSSID, $2);
789 			} else {
790 				frame.f_bssid = $3.table;
791 				HOSTAPD_MATCH_TABLE(BSSID, $2);
792 			}
793 		}
794 		;
795 
796 frmmatchrtap	: /* empty */
797 		| frmmatchrtap_l
798 		;
799 
800 frmmatchrtap_l	: frmmatchrtap_l frmmatchrtapopt
801 		| frmmatchrtapopt
802 		;
803 
804 frmmatchrtapopt	: RSSI unaryop percent
805 		{
806 			if (($2 == HOSTAPD_OP_GT && $3 == 100) ||
807 			    ($2 == HOSTAPD_OP_LE && $3 == 100) ||
808 			    ($2 == HOSTAPD_OP_LT && $3 == 0) ||
809 			    ($2 == HOSTAPD_OP_GE && $3 == 0)) {
810 				yyerror("absurd unary comparison");
811 				YYERROR;
812 			}
813 
814 			frame.f_rssi_op = $2;
815 			frame.f_rssi = $3;
816 			HOSTAPD_MATCH_RADIOTAP(RSSI);
817 		}
818 		| TXRATE unaryop txrate
819 		{
820 			frame.f_txrate_op = $2;
821 			frame.f_txrate = $3;
822 			HOSTAPD_MATCH_RADIOTAP(RATE);
823 		}
824 		| FREQ unaryop freq
825 		{
826 			frame.f_chan_op = $2;
827 			frame.f_chan = $3;
828 			HOSTAPD_MATCH_RADIOTAP(CHANNEL);
829 		}
830 		;
831 
832 frmmatchaddr	: table
833 		{
834 			if (($$.table =
835 			    hostapd_table_lookup(&hostapd_cfg, $1)) == NULL) {
836 				yyerror("undefined table <%s>", $1);
837 				free($1);
838 				YYERROR;
839 			}
840 			$$.flags = HOSTAPD_ACTION_F_OPT_TABLE;
841 			free($1);
842 		}
843 		| lladdr
844 		{
845 			bcopy($1.lladdr, $$.lladdr, IEEE80211_ADDR_LEN);
846 			$$.flags = HOSTAPD_ACTION_F_OPT_LLADDR;
847 		}
848 		;
849 
850 frmactiontype	: TYPE DATA
851 		{
852 			frame_ieee80211->i_fc[0] |= IEEE80211_FC0_TYPE_DATA;
853 		}
854 		| TYPE MANAGEMENT frmactionmgmt
855 		{
856 			frame_ieee80211->i_fc[0] |= IEEE80211_FC0_TYPE_MGT;
857 		}
858 		;
859 
860 frmactionmgmt	: SUBTYPE frmsubtype
861 		;
862 
863 frmactiondir	: /* empty */
864 		{
865 			frame.f_action_data.a_flags |=
866 			    HOSTAPD_ACTION_F_OPT_DIR_AUTO;
867 		}
868 		| DIR frmdir
869 		;
870 
871 frmactionfrom	: FROM frmactionaddr
872 		{
873 			if (($2.flags & HOSTAPD_ACTION_F_REF_M) == 0) {
874 				bcopy($2.lladdr, frame_ieee80211->i_from,
875 				    IEEE80211_ADDR_LEN);
876 			} else
877 				frame.f_action_data.a_flags |=
878 				    ($2.flags << HOSTAPD_ACTION_F_REF_FROM_S);
879 		}
880 		;
881 
882 frmactionto	: TO frmactionaddr
883 		{
884 			if (($2.flags & HOSTAPD_ACTION_F_REF_M) == 0) {
885 				bcopy($2.lladdr, frame_ieee80211->i_to,
886 				    IEEE80211_ADDR_LEN);
887 			} else
888 				frame.f_action_data.a_flags |=
889 				    ($2.flags << HOSTAPD_ACTION_F_REF_TO_S);
890 		}
891 		;
892 
893 frmactionbssid	: BSSID frmactionaddr
894 		{
895 			if (($2.flags & HOSTAPD_ACTION_F_REF_M) == 0) {
896 				bcopy($2.lladdr, frame_ieee80211->i_bssid,
897 				    IEEE80211_ADDR_LEN);
898 			} else
899 				frame.f_action_data.a_flags |=
900 				    ($2.flags << HOSTAPD_ACTION_F_REF_BSSID_S);
901 		}
902 		;
903 
904 frmactionaddr	: lladdr
905 		{
906 			bcopy($1.lladdr, $$.lladdr, IEEE80211_ADDR_LEN);
907 			$$.flags = $1.flags;
908 		}
909 		| randaddr
910 		{
911 			$$.flags = $1.flags;
912 		}
913 		| refaddr
914 		{
915 			$$.flags = $1.flags;
916 		}
917 		;
918 
919 table		: '<' STRING '>' {
920 			if (strlen($2) >= HOSTAPD_TABLE_NAMELEN) {
921 				yyerror("table name %s too long, max %u",
922 				    $2, HOSTAPD_TABLE_NAMELEN - 1);
923 				free($2);
924 				YYERROR;
925 			}
926 			$$ = $2;
927 		}
928 		;
929 
930 tabledef	: TABLE table {
931 			if ((table =
932 			    hostapd_table_add(&hostapd_cfg, $2)) == NULL) {
933 				yyerror("failed to add table: %s", $2);
934 				free($2);
935 				YYERROR;
936 			}
937 			free($2);
938 		} tableopts {
939 			table = NULL;
940 		}
941 		;
942 
943 tableopts	: /* empty */
944 		| tableopts_l
945 		;
946 
947 tableopts_l	: tableopts_l tableopt
948 		| tableopt
949 		;
950 
951 tableopt	: CONST	{
952 			if (table->t_flags & HOSTAPD_TABLE_F_CONST) {
953 				yyerror("option already specified");
954 				YYERROR;
955 			}
956 			table->t_flags |= HOSTAPD_TABLE_F_CONST;
957 		}
958 		| '{' optnl '}'
959 		| '{' optnl tableaddrlist optnl '}'
960 		;
961 
962 string		: string STRING
963 		{
964 			if (asprintf(&$$, "%s %s", $1, $2) == -1)
965 				hostapd_fatal("string: asprintf");
966 			free($1);
967 			free($2);
968 		}
969 		| STRING
970 		;
971 
972 varset		: STRING '=' string
973 		{
974 			char *s = $1;
975 			while (*s++) {
976 				if (isspace((unsigned char)*s)) {
977 					yyerror("macro name cannot contain "
978 					    "whitespace");
979 					YYERROR;
980 				}
981 			}
982 			if (symset($1, $3, 0) == -1)
983 				hostapd_fatal("cannot store variable");
984 			free($1);
985 			free($3);
986 		}
987 		;
988 
989 refaddr		: '&' FROM
990 		{
991 			$$.flags |= HOSTAPD_ACTION_F_REF_FROM;
992 		}
993 		| '&' TO
994 		{
995 			$$.flags |= HOSTAPD_ACTION_F_REF_TO;
996 		}
997 		| '&' BSSID
998 		{
999 			$$.flags |= HOSTAPD_ACTION_F_REF_BSSID;
1000 		}
1001 		;
1002 
1003 tableaddrlist	: tableaddrentry
1004 		| tableaddrlist comma tableaddrentry
1005 		;
1006 
1007 tableaddrentry	: lladdr
1008 		{
1009 			if ((entry = hostapd_entry_add(table,
1010 			    $1.lladdr)) == NULL) {
1011 				yyerror("failed to add entry: %s",
1012 				    etheraddr_string($1.lladdr));
1013 				YYERROR;
1014 			}
1015 		} tableaddropt {
1016 			entry = NULL;
1017 		}
1018 		;
1019 
1020 tableaddropt	: /* empty */
1021 		| assign ipv4addr ipv4netmask
1022 		{
1023 			entry->e_flags |= HOSTAPD_ENTRY_F_INADDR;
1024 			entry->e_inaddr.in_af = AF_INET;
1025 			bcopy(&$2, &entry->e_inaddr.in_v4,
1026 			    sizeof(struct in_addr));
1027 		}
1028 		| mask lladdr
1029 		{
1030 			entry->e_flags |= HOSTAPD_ENTRY_F_MASK;
1031 			bcopy($2.lladdr, entry->e_mask, IEEE80211_ADDR_LEN);
1032 
1033 			/* Update entry position in the table */
1034 			hostapd_entry_update(table, entry);
1035 		}
1036 		;
1037 
1038 ipv4addr	: STRING
1039 		{
1040 			if (inet_net_pton(AF_INET, $1, &$$, sizeof($$)) == -1) {
1041 				yyerror("invalid address: %s\n", $1);
1042 				free($1);
1043 				YYERROR;
1044 			}
1045 			free($1);
1046 		}
1047 		;
1048 
1049 ipv4netmask	: /* empty */
1050 		{
1051 			entry->e_inaddr.in_netmask = -1;
1052 		}
1053 		| '/' NUMBER
1054 		{
1055 			if ($2 < 0 || $2 > 32) {
1056 				yyerror("netmask out of range: %lld", $2);
1057 				YYERROR;
1058 			}
1059 			entry->e_inaddr.in_netmask = $2;
1060 		}
1061 		;
1062 
1063 lladdr		: STRING
1064 		{
1065 			struct ether_addr *ea;
1066 
1067 			if ((ea = ether_aton($1)) == NULL) {
1068 				yyerror("invalid address: %s\n", $1);
1069 				free($1);
1070 				YYERROR;
1071 			}
1072 			free($1);
1073 
1074 			bcopy(ea, $$.lladdr, IEEE80211_ADDR_LEN);
1075 			$$.flags = HOSTAPD_ACTION_F_OPT_LLADDR;
1076 		}
1077 		;
1078 
1079 randaddr	: RANDOM
1080 		{
1081 			$$.flags |= HOSTAPD_ACTION_F_REF_RANDOM;
1082 		}
1083 		;
1084 
1085 passive		: /* empty */
1086 		| PASSIVE
1087 		{
1088 			hostapd_cfg.c_flags |= HOSTAPD_CFG_F_IAPP_PASSIVE;
1089 		}
1090 		;
1091 
1092 assign		: ARROW
1093 		;
1094 
1095 mask		: '&'
1096 		;
1097 
1098 comma		: /* emtpy */
1099 		| ',' optnl
1100 		;
1101 
1102 optnl		: /* empty */
1103 		| '\n'
1104 		;
1105 
1106 not		: /* empty */
1107 		{
1108 			$$ = 0;
1109 		}
1110 		| '!'
1111 		{
1112 			$$ = 1;
1113 		}
1114 		| NOT
1115 		{
1116 			$$ = 1;
1117 		}
1118 		;
1119 
1120 unaryop		: /* any */
1121 		{
1122 			$$ = HOSTAPD_OP_EQ;
1123 		}
1124 		| '='
1125 		{
1126 			$$ = HOSTAPD_OP_EQ;
1127 		}
1128 		| '=='
1129 		{
1130 			$$ = HOSTAPD_OP_EQ;
1131 		}
1132 		| '!'
1133 		{
1134 			$$ = HOSTAPD_OP_NE;
1135 		}
1136 		| NE
1137 		{
1138 			$$ = HOSTAPD_OP_NE;
1139 		}
1140 		| LE
1141 		{
1142 			$$ = HOSTAPD_OP_LE;
1143 		}
1144 		| '<'
1145 		{
1146 			$$ = HOSTAPD_OP_LT;
1147 		}
1148 		| GE
1149 		{
1150 			$$ = HOSTAPD_OP_GE;
1151 		}
1152 		| '>'
1153 		{
1154 			$$ = HOSTAPD_OP_GT;
1155 		}
1156 		;
1157 
1158 percent		: STRING
1159 		{
1160 			double val;
1161 			char *cp;
1162 
1163 			val = strtod($1, &cp);
1164 			if (cp == NULL || strcmp(cp, "%") != 0 ||
1165 			    val < 0 || val > 100) {
1166 				yyerror("invalid percentage: %s", $1);
1167 				free($1);
1168 				YYERROR;
1169 			}
1170 			free($1);
1171 			$$ = val;
1172 		}
1173 		;
1174 
1175 txrate		: STRING
1176 		{
1177 			double val;
1178 			char *cp;
1179 
1180 			val = strtod($1, &cp) * 2;
1181 			if (cp == NULL || strcasecmp(cp, "mb") != 0 ||
1182 			    val != (int)val) {
1183 				yyerror("invalid rate: %s", $1);
1184 				free($1);
1185 				YYERROR;
1186 			}
1187 			free($1);
1188 			$$ = val;
1189 		}
1190 		;
1191 
1192 freq		: STRING
1193 		{
1194 			double val;
1195 			char *cp;
1196 
1197 			val = strtod($1, &cp);
1198 			if (cp != NULL) {
1199 				if (strcasecmp(cp, "ghz") == 0) {
1200 					$$ = val * 1000;
1201 				} else if (strcasecmp(cp, "mhz") == 0) {
1202 					$$ = val;
1203 				} else
1204 					cp = NULL;
1205 			}
1206 			if (cp == NULL) {
1207 				yyerror("invalid frequency: %s", $1);
1208 				free($1);
1209 				YYERROR;
1210 			}
1211 			free($1);
1212 		}
1213 		;
1214 
1215 timeout		: NUMBER
1216 		{
1217 			if ($1 < 1 || $1 > LONG_MAX) {
1218 				yyerror("timeout out of range: %lld", $1);
1219 				YYERROR;
1220 			}
1221 			$$.tv_sec = $1 / 1000;
1222 			$$.tv_usec = ($1 % 1000) * 1000;
1223 		}
1224 		;
1225 %%
1226 
1227 /*
1228  * Parser and lexer
1229  */
1230 
1231 struct keywords {
1232 	char *k_name;
1233 	int k_val;
1234 };
1235 
1236 int
1237 kw_cmp(const void *a, const void *b)
1238 {
1239 	return strcmp(a, ((const struct keywords *)b)->k_name);
1240 }
1241 
1242 int
1243 lookup(char *token)
1244 {
1245 	/* Keep this list sorted */
1246 	static const struct keywords keywords[] = {
1247 		{ "add",		ADD },
1248 		{ "address",		ADDRESS },
1249 		{ "any",		ANY },
1250 		{ "assoc",		ASSOC },
1251 		{ "assoced",		ASSOCED },
1252 		{ "atim",		ATIM },
1253 		{ "auth",		AUTH },
1254 		{ "authed",		AUTHED },
1255 		{ "beacon",		BEACON },
1256 		{ "broadcast",		BROADCAST },
1257 		{ "bssid",		BSSID },
1258 		{ "const",		CONST },
1259 		{ "data",		DATA },
1260 		{ "deauth",		DEAUTH },
1261 		{ "delay",		DELAY },
1262 		{ "delete",		DELETE },
1263 		{ "dir",		DIR },
1264 		{ "disassoc",		DISASSOC },
1265 		{ "ds",			DS },
1266 		{ "expire",		EXPIRE },
1267 		{ "failure",		FAILURE },
1268 		{ "frame",		FRAME },
1269 		{ "freq",		FREQ },
1270 		{ "from",		FROM },
1271 		{ "handle",		HANDLE },
1272 		{ "hopper",		HOPPER },
1273 		{ "hostap",		HOSTAP },
1274 		{ "iapp",		IAPP },
1275 		{ "ie",			IE },
1276 		{ "include",		INCLUDE },
1277 		{ "inconsistent",	INCONSISTENT },
1278 		{ "interface",		INTERFACE },
1279 		{ "invalid",		INVALID },
1280 		{ "leave",		LEAVE },
1281 		{ "limit",		LIMIT },
1282 		{ "log",		LOG },
1283 		{ "management",		MANAGEMENT },
1284 		{ "mic",		MIC },
1285 		{ "mode",		MODE },
1286 		{ "multicast",		MULTICAST },
1287 		{ "no",			NO },
1288 		{ "node",		NODE },
1289 		{ "not",		NOT },
1290 		{ "notify",		NOTIFY },
1291 		{ "nwid",		NWID },
1292 		{ "on",			ON },
1293 		{ "open",		OPEN },
1294 		{ "passive",		PASSIVE },
1295 		{ "pcap",		PCAP },
1296 		{ "port",		PORT },
1297 		{ "probe",		PROBE },
1298 		{ "quick",		QUICK },
1299 		{ "radiotap",		RADIOTAP },
1300 		{ "random",		RANDOM },
1301 		{ "rate",		RATE },
1302 		{ "reason",		REASON },
1303 		{ "reassoc",		REASSOC },
1304 		{ "request",		REQUEST },
1305 		{ "required",		REQUIRED },
1306 		{ "resend",		RESEND },
1307 		{ "reserved",		RESERVED },
1308 		{ "response",		RESPONSE },
1309 		{ "roaming",		ROAMING },
1310 		{ "route",		ROUTE },
1311 		{ "rsn",		RSN },
1312 		{ "sec",		SEC },
1313 		{ "set",		SET },
1314 		{ "signal",		RSSI },
1315 		{ "skip",		SKIP },
1316 		{ "subtype",		SUBTYPE },
1317 		{ "table",		TABLE },
1318 		{ "to",			TO },
1319 		{ "toomany",		TOOMANY },
1320 		{ "ttl",		TTL },
1321 		{ "txrate",		TXRATE },
1322 		{ "type",		TYPE },
1323 		{ "unspecified",	UNSPECIFIED },
1324 		{ "usec",		USEC },
1325 		{ "verbose",		VERBOSE },
1326 		{ "with",		WITH }
1327 	};
1328 	const struct keywords *p;
1329 
1330 	p = bsearch(token, keywords, sizeof(keywords) / sizeof(keywords[0]),
1331 	    sizeof(keywords[0]), kw_cmp);
1332 
1333 	return (p == NULL ? STRING : p->k_val);
1334 }
1335 
1336 #define MAXPUSHBACK	128
1337 
1338 u_char	*parsebuf;
1339 int	 parseindex;
1340 u_char	 pushback_buffer[MAXPUSHBACK];
1341 int	 pushback_index = 0;
1342 
1343 int
1344 lgetc(int quotec)
1345 {
1346 	int		c, next;
1347 
1348 	if (parsebuf) {
1349 		/* Read character from the parsebuffer instead of input. */
1350 		if (parseindex >= 0) {
1351 			c = parsebuf[parseindex++];
1352 			if (c != '\0')
1353 				return (c);
1354 			parsebuf = NULL;
1355 		} else
1356 			parseindex++;
1357 	}
1358 
1359 	if (pushback_index)
1360 		return (pushback_buffer[--pushback_index]);
1361 
1362 	if (quotec) {
1363 		if ((c = getc(file->stream)) == EOF) {
1364 			yyerror("reached end of file while parsing "
1365 			    "quoted string");
1366 			if (file == topfile || popfile() == EOF)
1367 				return (EOF);
1368 			return (quotec);
1369 		}
1370 		return (c);
1371 	}
1372 
1373 	while ((c = getc(file->stream)) == '\\') {
1374 		next = getc(file->stream);
1375 		if (next != '\n') {
1376 			c = next;
1377 			break;
1378 		}
1379 		yylval.lineno = file->lineno;
1380 		file->lineno++;
1381 	}
1382 
1383 	while (c == EOF) {
1384 		if (file == topfile || popfile() == EOF)
1385 			return (EOF);
1386 		c = getc(file->stream);
1387 	}
1388 	return (c);
1389 }
1390 
1391 int
1392 lungetc(int c)
1393 {
1394 	if (c == EOF)
1395 		return (EOF);
1396 	if (parsebuf) {
1397 		parseindex--;
1398 		if (parseindex >= 0)
1399 			return (c);
1400 	}
1401 	if (pushback_index < MAXPUSHBACK-1)
1402 		return (pushback_buffer[pushback_index++] = c);
1403 	else
1404 		return (EOF);
1405 }
1406 
1407 int
1408 findeol(void)
1409 {
1410 	int	c;
1411 
1412 	parsebuf = NULL;
1413 
1414 	/* skip to either EOF or the first real EOL */
1415 	while (1) {
1416 		if (pushback_index)
1417 			c = pushback_buffer[--pushback_index];
1418 		else
1419 			c = lgetc(0);
1420 		if (c == '\n') {
1421 			file->lineno++;
1422 			break;
1423 		}
1424 		if (c == EOF)
1425 			break;
1426 	}
1427 	return (ERROR);
1428 }
1429 
1430 int
1431 yylex(void)
1432 {
1433 	u_char	 buf[8096];
1434 	u_char	*p, *val;
1435 	int	 quotec, next, c;
1436 	int	 token;
1437 
1438 top:
1439 	p = buf;
1440 	while ((c = lgetc(0)) == ' ' || c == '\t')
1441 		; /* nothing */
1442 
1443 	yylval.lineno = file->lineno;
1444 	if (c == '#')
1445 		while ((c = lgetc(0)) != '\n' && c != EOF)
1446 			; /* nothing */
1447 	if (c == '$' && parsebuf == NULL) {
1448 		while (1) {
1449 			if ((c = lgetc(0)) == EOF)
1450 				return (0);
1451 
1452 			if (p + 1 >= buf + sizeof(buf) - 1) {
1453 				yyerror("string too long");
1454 				return (findeol());
1455 			}
1456 			if (isalnum(c) || c == '_') {
1457 				*p++ = c;
1458 				continue;
1459 			}
1460 			*p = '\0';
1461 			lungetc(c);
1462 			break;
1463 		}
1464 		val = symget(buf);
1465 		if (val == NULL) {
1466 			yyerror("macro \"%s\" not defined", buf);
1467 			return (findeol());
1468 		}
1469 		parsebuf = val;
1470 		parseindex = 0;
1471 		goto top;
1472 	}
1473 
1474 	switch (c) {
1475 	case '\'':
1476 	case '"':
1477 		quotec = c;
1478 		while (1) {
1479 			if ((c = lgetc(quotec)) == EOF)
1480 				return (0);
1481 			if (c == '\n') {
1482 				file->lineno++;
1483 				continue;
1484 			} else if (c == '\\') {
1485 				if ((next = lgetc(quotec)) == EOF)
1486 					return (0);
1487 				if (next == quotec || c == ' ' || c == '\t')
1488 					c = next;
1489 				else if (next == '\n') {
1490 					file->lineno++;
1491 					continue;
1492 				} else
1493 					lungetc(next);
1494 			} else if (c == quotec) {
1495 				*p = '\0';
1496 				break;
1497 			} else if (c == '\0') {
1498 				yyerror("syntax error");
1499 				return (findeol());
1500 			}
1501 			if (p + 1 >= buf + sizeof(buf) - 1) {
1502 				yyerror("string too long");
1503 				return (findeol());
1504 			}
1505 			*p++ = c;
1506 		}
1507 		yylval.v.string = strdup(buf);
1508 		if (yylval.v.string == NULL)
1509 			hostapd_fatal("yylex: strdup");
1510 		return (STRING);
1511 	case '-':
1512 		next = lgetc(0);
1513 		if (next == '>')
1514 			return (ARROW);
1515 		lungetc(next);
1516 		break;
1517 	case '!':
1518 		next = lgetc(0);
1519 		if (next == '=')
1520 			return (NE);
1521 		lungetc(next);
1522 		break;
1523 	case '<':
1524 		next = lgetc(0);
1525 		if (next == '=')
1526 			return (LE);
1527 		lungetc(next);
1528 		break;
1529 	case '>':
1530 		next = lgetc(0);
1531 		if (next == '=')
1532 			return (GE);
1533 		lungetc(next);
1534 		break;
1535 	}
1536 
1537 #define allowed_to_end_number(x) \
1538 	(isspace(x) || x == ')' || x ==',' || x == '/' || x == '}' || x == '=')
1539 
1540 	if (c == '-' || isdigit(c)) {
1541 		do {
1542 			*p++ = c;
1543 			if ((unsigned)(p-buf) >= sizeof(buf)) {
1544 				yyerror("string too long");
1545 				return (findeol());
1546 			}
1547 		} while ((c = lgetc(0)) != EOF && isdigit(c));
1548 		lungetc(c);
1549 		if (p == buf + 1 && buf[0] == '-')
1550 			goto nodigits;
1551 		if (c == EOF || allowed_to_end_number(c)) {
1552 			const char *errstr = NULL;
1553 
1554 			*p = '\0';
1555 			yylval.v.number = strtonum(buf, LLONG_MIN,
1556 			    LLONG_MAX, &errstr);
1557 			if (errstr) {
1558 				yyerror("\"%s\" invalid number: %s",
1559 				    buf, errstr);
1560 				return (findeol());
1561 			}
1562 			return (NUMBER);
1563 		} else {
1564 nodigits:
1565 			while (p > buf + 1)
1566 				lungetc(*--p);
1567 			c = *--p;
1568 			if (c == '-')
1569 				return (c);
1570 		}
1571 	}
1572 
1573 #define allowed_in_string(x) \
1574 	(isalnum(x) || (ispunct(x) && x != '(' && x != ')' && \
1575 	x != '{' && x != '}' && x != '<' && x != '>' && \
1576 	x != '!' && x != '=' && x != '/' && x != '#' && \
1577 	x != ','))
1578 
1579 	if (isalnum(c) || c == ':' || c == '_' || c == '*') {
1580 		do {
1581 			*p++ = c;
1582 			if ((unsigned)(p-buf) >= sizeof(buf)) {
1583 				yyerror("string too long");
1584 				return (findeol());
1585 			}
1586 		} while ((c = lgetc(0)) != EOF && (allowed_in_string(c)));
1587 		lungetc(c);
1588 		*p = '\0';
1589 		if ((token = lookup(buf)) == STRING)
1590 			if ((yylval.v.string = strdup(buf)) == NULL)
1591 				hostapd_fatal("yylex: strdup");
1592 		return (token);
1593 	}
1594 	if (c == '\n') {
1595 		yylval.lineno = file->lineno;
1596 		file->lineno++;
1597 	}
1598 	if (c == EOF)
1599 		return (0);
1600 	return (c);
1601 }
1602 
1603 int
1604 symset(const char *nam, const char *val, int persist)
1605 {
1606 	struct sym	*sym;
1607 
1608 	for (sym = TAILQ_FIRST(&symhead); sym && strcmp(nam, sym->nam);
1609 	    sym = TAILQ_NEXT(sym, entry))
1610 		;	/* nothing */
1611 
1612 	if (sym != NULL) {
1613 		if (sym->persist == 1)
1614 			return (0);
1615 		else {
1616 			free(sym->nam);
1617 			free(sym->val);
1618 			TAILQ_REMOVE(&symhead, sym, entry);
1619 			free(sym);
1620 		}
1621 	}
1622 	if ((sym = calloc(1, sizeof(*sym))) == NULL)
1623 		return (-1);
1624 
1625 	sym->nam = strdup(nam);
1626 	if (sym->nam == NULL) {
1627 		free(sym);
1628 		return (-1);
1629 	}
1630 	sym->val = strdup(val);
1631 	if (sym->val == NULL) {
1632 		free(sym->nam);
1633 		free(sym);
1634 		return (-1);
1635 	}
1636 	sym->used = 0;
1637 	sym->persist = persist;
1638 	TAILQ_INSERT_TAIL(&symhead, sym, entry);
1639 
1640 	hostapd_log(HOSTAPD_LOG_DEBUG, "%s = \"%s\"", sym->nam, sym->val);
1641 
1642 	return (0);
1643 }
1644 
1645 int
1646 hostapd_parse_symset(char *s)
1647 {
1648 	char	*sym, *val;
1649 	int	ret;
1650 	size_t	len;
1651 
1652 	if ((val = strrchr(s, '=')) == NULL)
1653 		return (-1);
1654 
1655 	len = strlen(s) - strlen(val) + 1;
1656 	if ((sym = malloc(len)) == NULL)
1657 		hostapd_fatal("cmdline_symset: malloc");
1658 
1659 	(void)strlcpy(sym, s, len);
1660 
1661 	ret = symset(sym, val + 1, 1);
1662 
1663 	free(sym);
1664 
1665 	return (ret);
1666 }
1667 
1668 char *
1669 symget(const char *nam)
1670 {
1671 	struct sym	*sym;
1672 
1673 	TAILQ_FOREACH(sym, &symhead, entry)
1674 		if (strcmp(nam, sym->nam) == 0) {
1675 			sym->used = 1;
1676 			return (sym->val);
1677 		}
1678 	return (NULL);
1679 }
1680 
1681 int
1682 check_file_secrecy(int fd, const char *fname)
1683 {
1684 	struct stat	st;
1685 
1686 	if (fstat(fd, &st)) {
1687 		warn("cannot stat %s", fname);
1688 		return (-1);
1689 	}
1690 	if (st.st_uid != 0 && st.st_uid != getuid()) {
1691 		warnx("%s: owner not root or current user", fname);
1692 		return (-1);
1693 	}
1694 	if (st.st_mode & (S_IWGRP | S_IXGRP | S_IRWXO)) {
1695 		warnx("%s: group writable or world read/writable", fname);
1696 		return (-1);
1697 	}
1698 	return (0);
1699 }
1700 
1701 struct file *
1702 pushfile(const char *name, int secret)
1703 {
1704 	struct file	*nfile;
1705 
1706 	if ((nfile = calloc(1, sizeof(struct file))) == NULL) {
1707 		warn("out of memory");
1708 		return (NULL);
1709 	}
1710 	if ((nfile->name = strdup(name)) == NULL) {
1711 		warn("out of memory");
1712 		free(nfile);
1713 		return (NULL);
1714 	}
1715 	if ((nfile->stream = fopen(nfile->name, "r")) == NULL) {
1716 		warn("%s", nfile->name);
1717 		free(nfile->name);
1718 		free(nfile);
1719 		return (NULL);
1720 	} else if (secret &&
1721 	    check_file_secrecy(fileno(nfile->stream), nfile->name)) {
1722 		fclose(nfile->stream);
1723 		free(nfile->name);
1724 		free(nfile);
1725 		return (NULL);
1726 	}
1727 	nfile->lineno = 1;
1728 	TAILQ_INSERT_TAIL(&files, nfile, entry);
1729 	return (nfile);
1730 }
1731 
1732 int
1733 popfile(void)
1734 {
1735 	struct file	*prev;
1736 
1737 	if ((prev = TAILQ_PREV(file, files, entry)) != NULL)
1738 		prev->errors += file->errors;
1739 
1740 	TAILQ_REMOVE(&files, file, entry);
1741 	fclose(file->stream);
1742 	free(file->name);
1743 	free(file);
1744 	file = prev;
1745 	return (file ? 0 : EOF);
1746 }
1747 
1748 int
1749 hostapd_parse_file(struct hostapd_config *cfg)
1750 {
1751 	struct sym *sym, *next;
1752 	int errors = 0;
1753 	int ret;
1754 
1755 	if ((file = pushfile(cfg->c_config, 1)) == NULL)
1756 		hostapd_fatal("failed to open the main config file: %s\n",
1757 		    cfg->c_config);
1758 	topfile = file;
1759 
1760 	/* Init tables and data structures */
1761 	TAILQ_INIT(&cfg->c_apmes);
1762 	TAILQ_INIT(&cfg->c_tables);
1763 	TAILQ_INIT(&cfg->c_frames);
1764 	cfg->c_iapp.i_multicast.sin_addr.s_addr = INADDR_ANY;
1765 	cfg->c_iapp.i_flags = HOSTAPD_IAPP_F_DEFAULT;
1766 	cfg->c_iapp.i_ttl = IP_DEFAULT_MULTICAST_TTL;
1767 	cfg->c_apme_hopdelay.tv_sec = HOSTAPD_HOPPER_MDELAY / 1000;
1768 	cfg->c_apme_hopdelay.tv_usec = (HOSTAPD_HOPPER_MDELAY % 1000) * 1000;
1769 
1770 	ret = yyparse();
1771 	errors = file->errors;
1772 	popfile();
1773 
1774 	/* Free macros and check which have not been used. */
1775 	for (sym = TAILQ_FIRST(&symhead); sym != NULL; sym = next) {
1776 		next = TAILQ_NEXT(sym, entry);
1777 		if (!sym->used)
1778 			hostapd_log(HOSTAPD_LOG_VERBOSE,
1779 			    "warning: macro '%s' not used", sym->nam);
1780 		if (!sym->persist) {
1781 			free(sym->nam);
1782 			free(sym->val);
1783 			TAILQ_REMOVE(&symhead, sym, entry);
1784 			free(sym);
1785 		}
1786 	}
1787 
1788 	return (errors ? EINVAL : ret);
1789 }
1790 
1791 int
1792 yyerror(const char *fmt, ...)
1793 {
1794 	va_list		 ap;
1795 	char		*msg;
1796 
1797 	file->errors++;
1798 
1799 	va_start(ap, fmt);
1800 	if (vasprintf(&msg, fmt, ap) == -1)
1801 		hostapd_fatal("yyerror vasprintf");
1802 	va_end(ap);
1803 	fprintf(stderr, "%s:%d: %s\n", file->name, yylval.lineno, msg);
1804 	fflush(stderr);
1805 	free(msg);
1806 
1807 	return (0);
1808 }
1809