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