xref: /openbsd-src/usr.sbin/hostapd/parse.y (revision 3a50f0a93a2072911d0ba6ababa815fb04bf9a71)
1*3a50f0a9Sjmc /*	$OpenBSD: parse.y,v 1.63 2022/12/28 21:30:16 jmc Exp $	*/
2d2b2a2e3Sreyk 
3d2b2a2e3Sreyk /*
49a571ad9Sreyk  * Copyright (c) 2004, 2005, 2006 Reyk Floeter <reyk@openbsd.org>
5a4abcc5fShenning  * Copyright (c) 2002 - 2005 Henning Brauer <henning@openbsd.org>
6a4abcc5fShenning  * Copyright (c) 2001 Markus Friedl.  All rights reserved.
7a4abcc5fShenning  * Copyright (c) 2001 Daniel Hartmeier.  All rights reserved.
8a4abcc5fShenning  * Copyright (c) 2001 Theo de Raadt.  All rights reserved.
9d2b2a2e3Sreyk  *
10d2b2a2e3Sreyk  * Permission to use, copy, modify, and distribute this software for any
11d2b2a2e3Sreyk  * purpose with or without fee is hereby granted, provided that the above
12d2b2a2e3Sreyk  * copyright notice and this permission notice appear in all copies.
13d2b2a2e3Sreyk  *
14d2b2a2e3Sreyk  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
15d2b2a2e3Sreyk  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
16d2b2a2e3Sreyk  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
17d2b2a2e3Sreyk  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
18d2b2a2e3Sreyk  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
19d2b2a2e3Sreyk  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
20d2b2a2e3Sreyk  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
21d2b2a2e3Sreyk  */
22d2b2a2e3Sreyk 
23d2b2a2e3Sreyk %{
24d2b2a2e3Sreyk #include <sys/ioctl.h>
25d2b2a2e3Sreyk #include <sys/types.h>
26d2b2a2e3Sreyk #include <sys/socket.h>
27d2b2a2e3Sreyk #include <sys/time.h>
28d2b2a2e3Sreyk #include <sys/queue.h>
29d2b2a2e3Sreyk #include <sys/stat.h>
30d2b2a2e3Sreyk 
31d2b2a2e3Sreyk #include <net/if.h>
32d2b2a2e3Sreyk #include <net/if_media.h>
33d2b2a2e3Sreyk #include <net/if_arp.h>
34d2b2a2e3Sreyk #include <net/if_llc.h>
35d2b2a2e3Sreyk #include <net/bpf.h>
36d2b2a2e3Sreyk 
37d2b2a2e3Sreyk #include <netinet/in.h>
38d2b2a2e3Sreyk #include <netinet/if_ether.h>
39d2b2a2e3Sreyk #include <arpa/inet.h>
40d2b2a2e3Sreyk 
419a571ad9Sreyk #include <net80211/ieee80211.h>
429a571ad9Sreyk #include <net80211/ieee80211_radiotap.h>
439a571ad9Sreyk 
44d2b2a2e3Sreyk #include <ctype.h>
45d2b2a2e3Sreyk #include <errno.h>
46d2b2a2e3Sreyk #include <event.h>
47d2b2a2e3Sreyk #include <fcntl.h>
48d2b2a2e3Sreyk #include <stdio.h>
49d2b2a2e3Sreyk #include <stdlib.h>
50d2b2a2e3Sreyk #include <stdarg.h>
51d2b2a2e3Sreyk #include <string.h>
52d2b2a2e3Sreyk #include <unistd.h>
53b9fc9a72Sderaadt #include <limits.h>
54ca87d875Sreyk #include <stdint.h>
5520741916Sderaadt #include <err.h>
56d2b2a2e3Sreyk 
57d2b2a2e3Sreyk #include "hostapd.h"
58d2b2a2e3Sreyk 
5920741916Sderaadt TAILQ_HEAD(files, file)		 files = TAILQ_HEAD_INITIALIZER(files);
6020741916Sderaadt static struct file {
61ab224e3bSreyk 	TAILQ_ENTRY(file)	 entry;
62ab224e3bSreyk 	FILE			*stream;
6320741916Sderaadt 	char			*name;
649a683e37Sdenis 	size_t	 		 ungetpos;
659a683e37Sdenis 	size_t			 ungetsize;
669a683e37Sdenis 	u_char			*ungetbuf;
679a683e37Sdenis 	int			 eof_reached;
68ab224e3bSreyk 	int			 lineno;
6920741916Sderaadt 	int			 errors;
70c6004ab9Smpf } *file, *topfile;
7120741916Sderaadt struct file	*pushfile(const char *, int);
7220741916Sderaadt int		 popfile(void);
7320741916Sderaadt int		 check_file_secrecy(int, const char *);
7420741916Sderaadt int		 yyparse(void);
7520741916Sderaadt int		 yylex(void);
760f79392cSdoug int		 yyerror(const char *, ...)
770f79392cSdoug     __attribute__((__format__ (printf, 1, 2)))
780f79392cSdoug     __attribute__((__nonnull__ (1)));
7920741916Sderaadt int		 kw_cmp(const void *, const void *);
8020741916Sderaadt int		 lookup(char *);
819a683e37Sdenis int		 igetc(void);
8220741916Sderaadt int		 lgetc(int);
839a683e37Sdenis void		 lungetc(int);
8420741916Sderaadt int		 findeol(void);
85d2b2a2e3Sreyk 
86d2b2a2e3Sreyk TAILQ_HEAD(symhead, sym)	 symhead = TAILQ_HEAD_INITIALIZER(symhead);
87d2b2a2e3Sreyk struct sym {
88d2b2a2e3Sreyk 	TAILQ_ENTRY(sym)	 entry;
89d2b2a2e3Sreyk 	int			 used;
90d2b2a2e3Sreyk 	int			 persist;
91d2b2a2e3Sreyk 	char			*nam;
92d2b2a2e3Sreyk 	char			*val;
93d2b2a2e3Sreyk };
94d2b2a2e3Sreyk int		 symset(const char *, const char *, int);
95d2b2a2e3Sreyk char		*symget(const char *);
9620741916Sderaadt 
9720741916Sderaadt extern struct hostapd_config hostapd_cfg;
98d2b2a2e3Sreyk 
99d2b2a2e3Sreyk typedef struct {
100d2b2a2e3Sreyk 	union {
10150ce650fSreyk 		struct {
10250ce650fSreyk 			u_int8_t		lladdr[IEEE80211_ADDR_LEN];
10350ce650fSreyk 			struct hostapd_table	*table;
10450ce650fSreyk 			u_int32_t		flags;
105553c8b9aSderaadt 		} reflladdr;
1068b938b68Sreyk 		struct {
1078b938b68Sreyk 			u_int16_t		alg;
1088b938b68Sreyk 			u_int16_t		transaction;
1098b938b68Sreyk 		} authalg;
11050ce650fSreyk 		struct in_addr		in;
111d2b2a2e3Sreyk 		char			*string;
112ca87d875Sreyk 		int64_t			number;
11350ce650fSreyk 		u_int16_t		reason;
1149a571ad9Sreyk 		enum hostapd_op		op;
11501908219Sreyk 		struct timeval		timeout;
116d2b2a2e3Sreyk 	} v;
117d2b2a2e3Sreyk 	int lineno;
118d2b2a2e3Sreyk } YYSTYPE;
119d2b2a2e3Sreyk 
12001908219Sreyk struct hostapd_apme *apme;
12150ce650fSreyk struct hostapd_table *table;
12250ce650fSreyk struct hostapd_entry *entry;
12350ce650fSreyk struct hostapd_frame frame, *frame_ptr;
12450ce650fSreyk struct hostapd_ieee80211_frame *frame_ieee80211;
12550ce650fSreyk 
12658d5d429Sreyk #define HOSTAPD_MATCH(_m, _not)	{					\
12758d5d429Sreyk 	frame.f_flags |= (_not) ?					\
12850ce650fSreyk 	    HOSTAPD_FRAME_F_##_m##_N : HOSTAPD_FRAME_F_##_m;		\
12950ce650fSreyk }
13058d5d429Sreyk #define HOSTAPD_MATCH_TABLE(_m, _not)	{				\
13158d5d429Sreyk 	frame.f_flags |= HOSTAPD_FRAME_F_##_m##_TABLE | ((_not) ?	\
13250ce650fSreyk 	    HOSTAPD_FRAME_F_##_m##_N : HOSTAPD_FRAME_F_##_m);		\
1333a61e91cSreyk }
1349a571ad9Sreyk #define HOSTAPD_MATCH_RADIOTAP(_x) {					\
1359a571ad9Sreyk 	if (hostapd_cfg.c_apme_dlt == DLT_IEEE802_11 ||			\
1369a571ad9Sreyk 	    (hostapd_cfg.c_apme_dlt == 0 &&				\
1379a571ad9Sreyk 	    HOSTAPD_DLT == DLT_IEEE802_11)) {				\
1389a571ad9Sreyk 		yyerror("option %s requires radiotap headers", #_x);	\
1399a571ad9Sreyk 		YYERROR;						\
1409a571ad9Sreyk 	}								\
1419a571ad9Sreyk 	frame.f_radiotap |= HOSTAPD_RADIOTAP_F(RSSI);			\
1429a571ad9Sreyk 	frame.f_flags |= HOSTAPD_FRAME_F_##_x;				\
1439a571ad9Sreyk }
14458d5d429Sreyk #define HOSTAPD_IAPP_FLAG(_f, _not) {					\
14558d5d429Sreyk 	if (_not)							\
1463a61e91cSreyk 		hostapd_cfg.c_iapp.i_flags &= ~(HOSTAPD_IAPP_F_##_f);	\
1473a61e91cSreyk 	else								\
1483a61e91cSreyk 		hostapd_cfg.c_iapp.i_flags |= (HOSTAPD_IAPP_F_##_f);	\
14950ce650fSreyk }
1509a571ad9Sreyk 
151d2b2a2e3Sreyk %}
152d2b2a2e3Sreyk 
15350ce650fSreyk %token	MODE INTERFACE IAPP HOSTAP MULTICAST BROADCAST SET SEC USEC
15450ce650fSreyk %token	HANDLE TYPE SUBTYPE FROM TO BSSID WITH FRAME RADIOTAP NWID PASSIVE
1558b938b68Sreyk %token	MANAGEMENT DATA PROBE BEACON ATIM ANY DS NO DIR RESEND RANDOM
1569834aad8Sreyk %token	AUTH DEAUTH ASSOC DISASSOC REASSOC REQUEST RESPONSE PCAP RATE
15750ce650fSreyk %token	ERROR CONST TABLE NODE DELETE ADD LOG VERBOSE LIMIT QUICK SKIP
15850ce650fSreyk %token	REASON UNSPECIFIED EXPIRE LEAVE ASSOC TOOMANY NOT AUTHED ASSOCED
1598b938b68Sreyk %token	RESERVED RSN REQUIRED INCONSISTENT IE INVALID MIC FAILURE OPEN
1609a571ad9Sreyk %token	ADDRESS PORT ON NOTIFY TTL INCLUDE ROUTE ROAMING RSSI TXRATE FREQ
1614a040c17Sderaadt %token	HOPPER DELAY NE LE GE ARROW
162d2b2a2e3Sreyk %token	<v.string>	STRING
163ca87d875Sreyk %token	<v.number>	NUMBER
16450ce650fSreyk %type	<v.in>		ipv4addr
1658b938b68Sreyk %type	<v.reflladdr>	refaddr, lladdr, randaddr, frmactionaddr, frmmatchaddr
166dc8f19a1Sreyk %type	<v.reason>	frmreason_l
16750ce650fSreyk %type	<v.string>	table
168d2b2a2e3Sreyk %type	<v.string>	string
1698b938b68Sreyk %type	<v.authalg>	authalg
1709a571ad9Sreyk %type	<v.op>		unaryop
171ca87d875Sreyk %type	<v.number>	percent
172ca87d875Sreyk %type	<v.number>	txrate
173ca87d875Sreyk %type	<v.number>	freq
174ca87d875Sreyk %type	<v.number>	not
17501908219Sreyk %type	<v.timeout>	timeout
176d2b2a2e3Sreyk 
177d2b2a2e3Sreyk %%
178d2b2a2e3Sreyk 
179d2b2a2e3Sreyk /*
180d2b2a2e3Sreyk  * Configuration grammar
181d2b2a2e3Sreyk  */
182d2b2a2e3Sreyk 
183d2b2a2e3Sreyk grammar		: /* empty */
184d2b2a2e3Sreyk 		| grammar '\n'
185ab224e3bSreyk 		| grammar include '\n'
18650ce650fSreyk 		| grammar tabledef '\n'
187d2b2a2e3Sreyk 		| grammar option '\n'
18850ce650fSreyk 		| grammar event '\n'
189d2b2a2e3Sreyk 		| grammar varset '\n'
19020741916Sderaadt 		| grammar error '\n'		{ file->errors++; }
191d2b2a2e3Sreyk 		;
192d2b2a2e3Sreyk 
193ab224e3bSreyk include		: INCLUDE STRING
194ab224e3bSreyk 		{
195ab224e3bSreyk 			struct file *nfile;
196ab224e3bSreyk 
1979a683e37Sdenis 			if ((nfile = pushfile($2, 1)) == NULL) {
198ab224e3bSreyk 				yyerror("failed to include file %s", $2);
199ab224e3bSreyk 				free($2);
200ab224e3bSreyk 				YYERROR;
201ab224e3bSreyk 			}
202ab224e3bSreyk 			free($2);
203ab224e3bSreyk 
204ab224e3bSreyk 			file = nfile;
205ab224e3bSreyk 			lungetc('\n');
206ab224e3bSreyk 		}
207ab224e3bSreyk 
208d01b6ac2Sreyk option		: SET HOSTAP INTERFACE hostapifaces
209d2b2a2e3Sreyk 		{
210d01b6ac2Sreyk 			if (!TAILQ_EMPTY(&hostapd_cfg.c_apmes))
211d2b2a2e3Sreyk 				hostapd_cfg.c_flags |= HOSTAPD_CFG_F_APME;
212d2b2a2e3Sreyk 		}
21301908219Sreyk 		| SET HOSTAP HOPPER INTERFACE hopperifaces
21401908219Sreyk 		| SET HOSTAP HOPPER DELAY timeout
21501908219Sreyk 		{
21601908219Sreyk 			bcopy(&$5, &hostapd_cfg.c_apme_hopdelay,
21701908219Sreyk 			    sizeof(struct timeval));
21801908219Sreyk 		}
21950ce650fSreyk 		| SET HOSTAP MODE hostapmode
22050ce650fSreyk 		| SET IAPP INTERFACE STRING passive
221d2b2a2e3Sreyk 		{
222ae7a93f5Sreyk 			if (strlcpy(hostapd_cfg.c_iapp.i_iface, $4,
223ae7a93f5Sreyk 			    sizeof(hostapd_cfg.c_iapp.i_iface)) >=
224ae7a93f5Sreyk 			    sizeof(hostapd_cfg.c_iapp.i_iface)) {
225ae7a93f5Sreyk 				yyerror("invalid interface %s", $4);
226ae7a93f5Sreyk 				free($4);
227ae7a93f5Sreyk 				YYERROR;
228ae7a93f5Sreyk 			}
229d2b2a2e3Sreyk 
230d2b2a2e3Sreyk 			hostapd_cfg.c_flags |= HOSTAPD_CFG_F_IAPP;
231d2b2a2e3Sreyk 
232d01b6ac2Sreyk 			hostapd_log(HOSTAPD_LOG_DEBUG,
233f83005a6Sreyk 			    "%s: IAPP interface added", $4);
234d2b2a2e3Sreyk 
235d2b2a2e3Sreyk 			free($4);
236d2b2a2e3Sreyk 		}
23750ce650fSreyk 		| SET IAPP MODE iappmode
23874ff1540Sreyk 		| SET IAPP ADDRESS ROAMING TABLE table
23974ff1540Sreyk 		{
24074ff1540Sreyk 			if ((hostapd_cfg.c_iapp.i_addr_tbl =
24174ff1540Sreyk 			    hostapd_table_lookup(&hostapd_cfg, $6)) == NULL) {
24274ff1540Sreyk 				yyerror("undefined table <%s>", $6);
24374ff1540Sreyk 				free($6);
24474ff1540Sreyk 				YYERROR;
24574ff1540Sreyk 			}
24674ff1540Sreyk 			free($6);
24774ff1540Sreyk 		}
24874ff1540Sreyk 		| SET IAPP ROUTE ROAMING TABLE table
24974ff1540Sreyk 		{
25074ff1540Sreyk 			if ((hostapd_cfg.c_iapp.i_route_tbl =
25174ff1540Sreyk 			    hostapd_table_lookup(&hostapd_cfg, $6)) == NULL) {
25274ff1540Sreyk 				yyerror("undefined table <%s>", $6);
25374ff1540Sreyk 				free($6);
25474ff1540Sreyk 				YYERROR;
25574ff1540Sreyk 			}
25674ff1540Sreyk 			free($6);
25774ff1540Sreyk 		}
2583a61e91cSreyk 		| SET IAPP HANDLE SUBTYPE iappsubtypes
25950ce650fSreyk 		;
26050ce650fSreyk 
26102fe3327Sreyk iappmode	: MULTICAST iappmodeaddr iappmodeport iappmodettl
262d2b2a2e3Sreyk 		{
263d2b2a2e3Sreyk 			hostapd_cfg.c_flags &= ~HOSTAPD_CFG_F_BRDCAST;
264d2b2a2e3Sreyk 		}
26576fb2f4fSreyk 		| BROADCAST iappmodeport
266d2b2a2e3Sreyk 		{
267d2b2a2e3Sreyk 			hostapd_cfg.c_flags |= HOSTAPD_CFG_F_BRDCAST;
268d2b2a2e3Sreyk 		}
269d2b2a2e3Sreyk 		;
270d2b2a2e3Sreyk 
27176fb2f4fSreyk iappmodeaddr	: /* empty */
27276fb2f4fSreyk 		| ADDRESS ipv4addr
27376fb2f4fSreyk 		{
2747c8c4753Sreyk 			bcopy(&$2, &hostapd_cfg.c_iapp.i_multicast.sin_addr,
27576fb2f4fSreyk 			    sizeof(struct in_addr));
27676fb2f4fSreyk 		}
27776fb2f4fSreyk 		;
27876fb2f4fSreyk 
27976fb2f4fSreyk iappmodeport	: /* empty */
280ca87d875Sreyk 		| PORT NUMBER
28176fb2f4fSreyk 		{
282ca87d875Sreyk 			if ($2 < 0 || $2 > UINT16_MAX) {
283ca87d875Sreyk 				yyerror("port out of range: %lld", $2);
284ca87d875Sreyk 				YYERROR;
285ca87d875Sreyk 			}
2867c8c4753Sreyk 			hostapd_cfg.c_iapp.i_addr.sin_port = htons($2);
28776fb2f4fSreyk 		}
28876fb2f4fSreyk 		;
28976fb2f4fSreyk 
29002fe3327Sreyk iappmodettl	: /* empty */
291ca87d875Sreyk 		| TTL NUMBER
29202fe3327Sreyk 		{
293ca87d875Sreyk 			if ($2 < 1 || $2 > UINT8_MAX) {
294ca87d875Sreyk 				yyerror("ttl out of range: %lld", $2);
295ca87d875Sreyk 				YYERROR;
296ca87d875Sreyk 			}
29702fe3327Sreyk 			hostapd_cfg.c_iapp.i_ttl = $2;
29802fe3327Sreyk 		}
29902fe3327Sreyk 		;
30002fe3327Sreyk 
30150ce650fSreyk hostapmode	: RADIOTAP
30250ce650fSreyk 		{
30350ce650fSreyk 			hostapd_cfg.c_apme_dlt = DLT_IEEE802_11_RADIO;
30450ce650fSreyk 		}
30550ce650fSreyk 		| PCAP
30650ce650fSreyk 		{
30750ce650fSreyk 			hostapd_cfg.c_apme_dlt = DLT_IEEE802_11;
30850ce650fSreyk 		}
30950ce650fSreyk 		;
31050ce650fSreyk 
311d01b6ac2Sreyk hostapifaces	: '{' optnl hostapifacelist optnl '}'
312d01b6ac2Sreyk 		| hostapiface
313d01b6ac2Sreyk 		;
314d01b6ac2Sreyk 
315d01b6ac2Sreyk hostapifacelist	: hostapiface
316d01b6ac2Sreyk 		| hostapifacelist comma hostapiface
317d01b6ac2Sreyk 		;
318d01b6ac2Sreyk 
319d01b6ac2Sreyk hostapiface	: STRING
320d01b6ac2Sreyk 		{
321d01b6ac2Sreyk 			if (hostapd_apme_add(&hostapd_cfg, $1) != 0) {
322d01b6ac2Sreyk 				yyerror("failed to add hostap interface");
323d01b6ac2Sreyk 				YYERROR;
324d01b6ac2Sreyk 			}
325d01b6ac2Sreyk 			free($1);
326d01b6ac2Sreyk 		}
327d01b6ac2Sreyk 		;
328d01b6ac2Sreyk 
32901908219Sreyk hopperifaces	: '{' optnl hopperifacelist optnl '}'
33001908219Sreyk 		| hopperiface
33101908219Sreyk 		;
33201908219Sreyk 
33301908219Sreyk hopperifacelist	: hopperiface
33401908219Sreyk 		| hopperifacelist comma hopperiface
33501908219Sreyk 		;
33601908219Sreyk 
33701908219Sreyk hopperiface	: STRING
33801908219Sreyk 		{
33901908219Sreyk 			if ((apme = hostapd_apme_addhopper(&hostapd_cfg,
34001908219Sreyk 			    $1)) == NULL) {
34101908219Sreyk 				yyerror("failed to add hopper %s", $1);
34201908219Sreyk 				free($1);
34301908219Sreyk 				YYERROR;
34401908219Sreyk 			}
34501908219Sreyk 			free($1);
34601908219Sreyk 		}
34701908219Sreyk 		;
34801908219Sreyk 
3495058e176Sreyk hostapmatch	: /* empty */
35058d5d429Sreyk 		| ON not STRING
3515058e176Sreyk 		{
3525058e176Sreyk 			if ((frame.f_apme =
35358d5d429Sreyk 			    hostapd_apme_lookup(&hostapd_cfg, $3)) == NULL) {
3545058e176Sreyk 				yyerror("undefined hostap interface");
35558d5d429Sreyk 				free($3);
3565058e176Sreyk 				YYERROR;
3575058e176Sreyk 			}
35858d5d429Sreyk 			free($3);
3595058e176Sreyk 
36058d5d429Sreyk 			HOSTAPD_MATCH(APME, $2);
3615058e176Sreyk 		}
3625058e176Sreyk 		;
3635058e176Sreyk 
36450ce650fSreyk event		: HOSTAP HANDLE
36550ce650fSreyk 		{
36650ce650fSreyk 			bzero(&frame, sizeof(struct hostapd_frame));
36750ce650fSreyk 			/* IEEE 802.11 frame to match */
36850ce650fSreyk 			frame_ieee80211 = &frame.f_frame;
3695058e176Sreyk 		} eventopt hostapmatch frmmatch {
37050ce650fSreyk 			/* IEEE 802.11 raw frame to send as an action */
37150ce650fSreyk 			frame_ieee80211 = &frame.f_action_data.a_frame;
3729834aad8Sreyk 		} action limit rate {
37335de856eSderaadt 			if ((frame_ptr = calloc(1, sizeof(struct hostapd_frame)))
37435de856eSderaadt 			    == NULL) {
37550ce650fSreyk 				yyerror("calloc");
37650ce650fSreyk 				YYERROR;
37750ce650fSreyk 			}
37850ce650fSreyk 
379ae7a93f5Sreyk 			if (gettimeofday(&frame.f_last, NULL) == -1)
380ae7a93f5Sreyk 				hostapd_fatal("gettimeofday");
3819834aad8Sreyk 			timeradd(&frame.f_last, &frame.f_limit, &frame.f_then);
38250ce650fSreyk 
38350ce650fSreyk 			bcopy(&frame, frame_ptr, sizeof(struct hostapd_frame));
38450ce650fSreyk 			TAILQ_INSERT_TAIL(&hostapd_cfg.c_frames,
38550ce650fSreyk 			    frame_ptr, f_entries);
38650ce650fSreyk 		}
38750ce650fSreyk 		;
38850ce650fSreyk 
3893a61e91cSreyk iappsubtypes	: '{' optnl iappsubtypelist optnl '}'
3903a61e91cSreyk 		| iappsubtype
3913a61e91cSreyk 		;
3923a61e91cSreyk 
3933a61e91cSreyk iappsubtypelist	: iappsubtype
3943a61e91cSreyk 		| iappsubtypelist comma iappsubtype
3953a61e91cSreyk 		;
3963a61e91cSreyk 
3973a61e91cSreyk iappsubtype	: not ADD NOTIFY
3983a61e91cSreyk 		{
39958d5d429Sreyk 			HOSTAPD_IAPP_FLAG(ADD_NOTIFY, $1);
4003a61e91cSreyk 		}
4013a61e91cSreyk 		| not RADIOTAP
4023a61e91cSreyk 		{
40358d5d429Sreyk 			HOSTAPD_IAPP_FLAG(RADIOTAP, $1);
4043a61e91cSreyk 		}
40574ff1540Sreyk 		| not ROUTE ROAMING
40674ff1540Sreyk 		{
40758d5d429Sreyk 			HOSTAPD_IAPP_FLAG(ROAMING_ROUTE, $1);
40874ff1540Sreyk 		}
40974ff1540Sreyk 		| not ADDRESS ROAMING
41074ff1540Sreyk 		{
41158d5d429Sreyk 			HOSTAPD_IAPP_FLAG(ROAMING_ADDRESS, $1);
41274ff1540Sreyk 		}
4133a61e91cSreyk 		;
4143a61e91cSreyk 
41550ce650fSreyk eventopt	: /* empty */
41650ce650fSreyk 		{
41750ce650fSreyk 			frame.f_flags |= HOSTAPD_FRAME_F_RET_OK;
41850ce650fSreyk 		}
41950ce650fSreyk 		| QUICK
42050ce650fSreyk 		{
42150ce650fSreyk 			frame.f_flags |= HOSTAPD_FRAME_F_RET_QUICK;
42250ce650fSreyk 		}
42350ce650fSreyk 		| SKIP
42450ce650fSreyk 		{
42550ce650fSreyk 			frame.f_flags |= HOSTAPD_FRAME_F_RET_SKIP;
42650ce650fSreyk 		}
42750ce650fSreyk 		;
42850ce650fSreyk 
42950ce650fSreyk action		: /* empty */
43050ce650fSreyk 		{
43150ce650fSreyk 			frame.f_action = HOSTAPD_ACTION_NONE;
43250ce650fSreyk 		}
43350ce650fSreyk 		| WITH LOG verbose
43450ce650fSreyk 		{
43550ce650fSreyk 			frame.f_action = HOSTAPD_ACTION_LOG;
43650ce650fSreyk 		}
43750ce650fSreyk 		| WITH FRAME frmaction
43850ce650fSreyk 		{
43950ce650fSreyk 			frame.f_action = HOSTAPD_ACTION_FRAME;
44050ce650fSreyk 		}
44150ce650fSreyk 		| WITH IAPP iapp
44250ce650fSreyk 		| WITH NODE nodeopt frmactionaddr
44350ce650fSreyk 		{
44450ce650fSreyk 			if (($4.flags & HOSTAPD_ACTION_F_REF_M) == 0) {
44550ce650fSreyk 				bcopy($4.lladdr, frame.f_action_data.a_lladdr,
44650ce650fSreyk 				    IEEE80211_ADDR_LEN);
44750ce650fSreyk 			} else
44850ce650fSreyk 				frame.f_action_data.a_flags |= $4.flags;
44950ce650fSreyk 		}
45050ce650fSreyk 		| WITH RESEND
45150ce650fSreyk 		{
45250ce650fSreyk 			frame.f_action = HOSTAPD_ACTION_RESEND;
45350ce650fSreyk 		}
45450ce650fSreyk 		;
45550ce650fSreyk 
45650ce650fSreyk verbose		: /* empty */
45750ce650fSreyk 		| VERBOSE
45850ce650fSreyk 		{
45950ce650fSreyk 			frame.f_action_flags |= HOSTAPD_ACTION_VERBOSE;
46050ce650fSreyk 		}
46150ce650fSreyk 		;
46250ce650fSreyk 
46350ce650fSreyk iapp		: TYPE RADIOTAP verbose
46450ce650fSreyk 		{
46550ce650fSreyk 			frame.f_action = HOSTAPD_ACTION_RADIOTAP;
46650ce650fSreyk 		}
46750ce650fSreyk 		;
46850ce650fSreyk 
46950ce650fSreyk nodeopt		: DELETE
47050ce650fSreyk 		{
47150ce650fSreyk 			frame.f_action = HOSTAPD_ACTION_DELNODE;
47250ce650fSreyk 		}
47350ce650fSreyk 		| ADD
47450ce650fSreyk 		{
47550ce650fSreyk 			frame.f_action = HOSTAPD_ACTION_ADDNODE;
47650ce650fSreyk 		}
47750ce650fSreyk 		;
47850ce650fSreyk 
4798b938b68Sreyk frmmatch	: ANY
4808b938b68Sreyk 		| frm frmmatchtype frmmatchdir frmmatchfrom frmmatchto
4819a571ad9Sreyk 			frmmatchbssid frmmatchrtap
4828b938b68Sreyk 		;
4838b938b68Sreyk 
4848b938b68Sreyk frm		: /* empty */
4858b938b68Sreyk 		| FRAME
48650ce650fSreyk 		;
48750ce650fSreyk 
48850ce650fSreyk frmaction	: frmactiontype frmactiondir frmactionfrom frmactionto frmactionbssid
48950ce650fSreyk 		;
49050ce650fSreyk 
49150ce650fSreyk limit		: /* empty */
492ca87d875Sreyk 		| LIMIT NUMBER SEC
49350ce650fSreyk 		{
494ca87d875Sreyk 			if ($2 < 0 || $2 > LONG_MAX) {
495ca87d875Sreyk 				yyerror("limit out of range: %lld sec", $2);
496ca87d875Sreyk 				YYERROR;
497ca87d875Sreyk 			}
49850ce650fSreyk 			frame.f_limit.tv_sec = $2;
49950ce650fSreyk 		}
500ca87d875Sreyk 		| LIMIT NUMBER USEC
50150ce650fSreyk 		{
502ca87d875Sreyk 			if ($2 < 0 || $2 > LONG_MAX) {
503ca87d875Sreyk 				yyerror("limit out of range: %lld usec", $2);
504ca87d875Sreyk 				YYERROR;
505ca87d875Sreyk 			}
506081c88afScheloha 			frame.f_limit.tv_sec = $2 / 1000000;
507081c88afScheloha 			frame.f_limit.tv_usec = $2 % 1000000;
50850ce650fSreyk 		}
50950ce650fSreyk 		;
51050ce650fSreyk 
5119834aad8Sreyk rate		: /* empty */
512ca87d875Sreyk 		| RATE NUMBER '/' NUMBER SEC
5139834aad8Sreyk 		{
514ca87d875Sreyk 			if (($2 < 1 || $2 > LONG_MAX) ||
515ca87d875Sreyk 			    ($4 < 1 || $4 > LONG_MAX)) {
516ca87d875Sreyk 				yyerror("rate out of range: %lld/%lld sec",
517ca87d875Sreyk 				    $2, $4);
518ca87d875Sreyk 				YYERROR;
519ca87d875Sreyk 			}
520ca87d875Sreyk 
5219834aad8Sreyk 			if (!($2 && $4)) {
5229834aad8Sreyk 				yyerror("invalid rate");
5239834aad8Sreyk 				YYERROR;
5249834aad8Sreyk 			}
5259834aad8Sreyk 
5269834aad8Sreyk 			frame.f_rate = $2;
5279834aad8Sreyk 			frame.f_rate_intval = $4;
5289834aad8Sreyk 		}
5299834aad8Sreyk 		;
5309834aad8Sreyk 
53150ce650fSreyk frmmatchtype	: /* any */
53250ce650fSreyk 		| TYPE ANY
53350ce650fSreyk 		| TYPE not DATA
53450ce650fSreyk 		{
53550ce650fSreyk 			frame_ieee80211->i_fc[0] |=
53650ce650fSreyk 			    IEEE80211_FC0_TYPE_DATA;
53758d5d429Sreyk 			HOSTAPD_MATCH(TYPE, $2);
53850ce650fSreyk 		}
53950ce650fSreyk 		| TYPE not MANAGEMENT frmmatchmgmt
54050ce650fSreyk 		{
54150ce650fSreyk 			frame_ieee80211->i_fc[0] |=
54250ce650fSreyk 			    IEEE80211_FC0_TYPE_MGT;
54358d5d429Sreyk 			HOSTAPD_MATCH(TYPE, $2);
54450ce650fSreyk 		}
54550ce650fSreyk 		;
54650ce650fSreyk 
54750ce650fSreyk frmmatchmgmt	: /* any */
54850ce650fSreyk 		| SUBTYPE ANY
54950ce650fSreyk 		| SUBTYPE not frmsubtype
55050ce650fSreyk 		{
55158d5d429Sreyk 			HOSTAPD_MATCH(SUBTYPE, $2);
55250ce650fSreyk 		}
55350ce650fSreyk 		;
55450ce650fSreyk 
55550ce650fSreyk frmsubtype	: PROBE REQUEST frmelems
55650ce650fSreyk 		{
55750ce650fSreyk 			frame_ieee80211->i_fc[0] |=
55850ce650fSreyk 			    IEEE80211_FC0_SUBTYPE_PROBE_REQ;
55950ce650fSreyk 		}
56050ce650fSreyk 		| PROBE RESPONSE frmelems
56150ce650fSreyk 		{
56250ce650fSreyk 			frame_ieee80211->i_fc[0] |=
56350ce650fSreyk 			    IEEE80211_FC0_SUBTYPE_PROBE_RESP;
56450ce650fSreyk 		}
56550ce650fSreyk 		| BEACON frmelems
56650ce650fSreyk 		{
56750ce650fSreyk 			frame_ieee80211->i_fc[0] |=
56850ce650fSreyk 			    IEEE80211_FC0_SUBTYPE_BEACON;
56950ce650fSreyk 		}
57050ce650fSreyk 		| ATIM
57150ce650fSreyk 		{
57250ce650fSreyk 			frame_ieee80211->i_fc[0] |=
57350ce650fSreyk 			    IEEE80211_FC0_SUBTYPE_ATIM;
57450ce650fSreyk 		}
5758b938b68Sreyk 		| AUTH frmauth
57650ce650fSreyk 		{
57750ce650fSreyk 			frame_ieee80211->i_fc[0] |=
57850ce650fSreyk 			    IEEE80211_FC0_SUBTYPE_AUTH;
57950ce650fSreyk 		}
58050ce650fSreyk 		| DEAUTH frmreason
58150ce650fSreyk 		{
58250ce650fSreyk 			frame_ieee80211->i_fc[0] |=
58350ce650fSreyk 			    IEEE80211_FC0_SUBTYPE_DEAUTH;
58450ce650fSreyk 		}
58550ce650fSreyk 		| ASSOC REQUEST
58650ce650fSreyk 		{
58750ce650fSreyk 			frame_ieee80211->i_fc[0] |=
58850ce650fSreyk 			    IEEE80211_FC0_SUBTYPE_ASSOC_REQ;
58950ce650fSreyk 		}
590dc8f19a1Sreyk 		| DISASSOC frmreason
59150ce650fSreyk 		{
59250ce650fSreyk 			frame_ieee80211->i_fc[0] |=
59350ce650fSreyk 			    IEEE80211_FC0_SUBTYPE_DISASSOC;
59450ce650fSreyk 		}
59550ce650fSreyk 		| ASSOC RESPONSE
59650ce650fSreyk 		{
59750ce650fSreyk 			frame_ieee80211->i_fc[0] |=
59850ce650fSreyk 			    IEEE80211_FC0_SUBTYPE_ASSOC_RESP;
59950ce650fSreyk 		}
60050ce650fSreyk 		| REASSOC REQUEST
60150ce650fSreyk 		{
60250ce650fSreyk 			frame_ieee80211->i_fc[0] |=
60350ce650fSreyk 			    IEEE80211_FC0_SUBTYPE_REASSOC_REQ;
60450ce650fSreyk 		}
60550ce650fSreyk 		| REASSOC RESPONSE
60650ce650fSreyk 		{
60750ce650fSreyk 			frame_ieee80211->i_fc[0] |=
60850ce650fSreyk 			    IEEE80211_FC0_SUBTYPE_REASSOC_RESP;
60950ce650fSreyk 		}
61050ce650fSreyk 		;
61150ce650fSreyk 
61250ce650fSreyk frmelems	: /* empty */
61350ce650fSreyk 		| frmelems_l
61450ce650fSreyk 		;
61550ce650fSreyk 
61650ce650fSreyk frmelems_l	: frmelems_l frmelem
61750ce650fSreyk 		| frmelem
61850ce650fSreyk 		;
61950ce650fSreyk 
62050ce650fSreyk frmelem		: NWID not STRING
62150ce650fSreyk 		;
62250ce650fSreyk 
6238b938b68Sreyk frmauth		: /* empty */
6248b938b68Sreyk 		| authalg
6258b938b68Sreyk 		{
6268b938b68Sreyk 			if ((frame_ieee80211->i_data = malloc(6)) == NULL) {
6278b938b68Sreyk 				yyerror("failed to allocate auth");
6288b938b68Sreyk 				YYERROR;
6298b938b68Sreyk 			}
6308b938b68Sreyk 			((u_int16_t *)frame_ieee80211->i_data)[0] =
6318b938b68Sreyk 				$1.alg;
6328b938b68Sreyk 			((u_int16_t *)frame_ieee80211->i_data)[1] =
6338b938b68Sreyk 				$1.transaction;
6348b938b68Sreyk 			((u_int16_t *)frame_ieee80211->i_data)[0] = 0;
6358b938b68Sreyk 			frame_ieee80211->i_data_len = 6;
6368b938b68Sreyk 		}
6378b938b68Sreyk 		;
6388b938b68Sreyk 
6398b938b68Sreyk authalg		: OPEN REQUEST
6408b938b68Sreyk 		{
6418b938b68Sreyk 			$$.alg = htole16(IEEE80211_AUTH_ALG_OPEN);
6428b938b68Sreyk 			$$.transaction = htole16(IEEE80211_AUTH_OPEN_REQUEST);
6438b938b68Sreyk 		}
6448b938b68Sreyk 		| OPEN RESPONSE
6458b938b68Sreyk 		{
6468b938b68Sreyk 			$$.alg = htole16(IEEE80211_AUTH_ALG_OPEN);
6478b938b68Sreyk 			$$.transaction = htole16(IEEE80211_AUTH_OPEN_RESPONSE);
6488b938b68Sreyk 		}
6498b938b68Sreyk 		;
650dc8f19a1Sreyk 
651dc8f19a1Sreyk frmreason	: frmreason_l
652dc8f19a1Sreyk 		{
653dc8f19a1Sreyk 			if ($1 != 0) {
65435de856eSderaadt 				if ((frame_ieee80211->i_data =
655dc8f19a1Sreyk 				    malloc(sizeof(u_int16_t))) == NULL) {
656dc8f19a1Sreyk 					yyerror("failed to allocate "
657dc8f19a1Sreyk 					    "reason code %u", $1);
658dc8f19a1Sreyk 					YYERROR;
659dc8f19a1Sreyk 				}
660dc8f19a1Sreyk 				*(u_int16_t *)frame_ieee80211->i_data =
661dc8f19a1Sreyk 				    htole16($1);
662dc8f19a1Sreyk 				frame_ieee80211->i_data_len = sizeof(u_int16_t);
663dc8f19a1Sreyk 			}
664dc8f19a1Sreyk 		}
665dc8f19a1Sreyk 		;
666dc8f19a1Sreyk 
667dc8f19a1Sreyk frmreason_l	: /* empty */
66850ce650fSreyk 		{
66950ce650fSreyk 			$$ = 0;
67050ce650fSreyk 		}
67150ce650fSreyk 		| REASON UNSPECIFIED
67250ce650fSreyk 		{
67350ce650fSreyk 			$$ = IEEE80211_REASON_UNSPECIFIED;
67450ce650fSreyk 		}
67550ce650fSreyk 		| REASON AUTH EXPIRE
67650ce650fSreyk 		{
67750ce650fSreyk 			$$ = IEEE80211_REASON_AUTH_EXPIRE;
67850ce650fSreyk 		}
67950ce650fSreyk 		| REASON AUTH LEAVE
68050ce650fSreyk 		{
68150ce650fSreyk 			$$ = IEEE80211_REASON_AUTH_LEAVE;
68250ce650fSreyk 		}
68350ce650fSreyk 		| REASON ASSOC EXPIRE
68450ce650fSreyk 		{
68550ce650fSreyk 			$$ = IEEE80211_REASON_ASSOC_EXPIRE;
68650ce650fSreyk 		}
68750ce650fSreyk 		| REASON ASSOC TOOMANY
68850ce650fSreyk 		{
68950ce650fSreyk 			$$ = IEEE80211_REASON_ASSOC_TOOMANY;
69050ce650fSreyk 		}
69150ce650fSreyk 		| REASON NOT AUTHED
69250ce650fSreyk 		{
69350ce650fSreyk 			$$ = IEEE80211_REASON_NOT_AUTHED;
69450ce650fSreyk 		}
69550ce650fSreyk 		| REASON NOT ASSOCED
69650ce650fSreyk 		{
69750ce650fSreyk 			$$ = IEEE80211_REASON_NOT_ASSOCED;
69850ce650fSreyk 		}
69950ce650fSreyk 		| REASON ASSOC LEAVE
70050ce650fSreyk 		{
70150ce650fSreyk 			$$ = IEEE80211_REASON_ASSOC_LEAVE;
70250ce650fSreyk 		}
70350ce650fSreyk 		| REASON ASSOC NOT AUTHED
70450ce650fSreyk 		{
70550ce650fSreyk 			$$ = IEEE80211_REASON_NOT_AUTHED;
70650ce650fSreyk 		}
70750ce650fSreyk 		| REASON RESERVED
70850ce650fSreyk 		{
70950ce650fSreyk 			$$ = 10;	/* XXX unknown */
71050ce650fSreyk 		}
71150ce650fSreyk 		| REASON RSN REQUIRED
71250ce650fSreyk 		{
71350ce650fSreyk 			$$ = IEEE80211_REASON_RSN_REQUIRED;
71450ce650fSreyk 		}
71550ce650fSreyk 		| REASON RSN INCONSISTENT
71650ce650fSreyk 		{
71750ce650fSreyk 			$$ = IEEE80211_REASON_RSN_INCONSISTENT;
71850ce650fSreyk 		}
71950ce650fSreyk 		| REASON IE INVALID
72050ce650fSreyk 		{
72150ce650fSreyk 			$$ = IEEE80211_REASON_IE_INVALID;
72250ce650fSreyk 		}
72350ce650fSreyk 		| REASON MIC FAILURE
72450ce650fSreyk 		{
72550ce650fSreyk 			$$ = IEEE80211_REASON_MIC_FAILURE;
72650ce650fSreyk 		}
72750ce650fSreyk 		;
72850ce650fSreyk 
72950ce650fSreyk frmmatchdir	: /* any */
73050ce650fSreyk 		| DIR ANY
73158d5d429Sreyk 		| DIR not frmdir
73250ce650fSreyk 		{
73358d5d429Sreyk 			HOSTAPD_MATCH(DIR, $2);
73450ce650fSreyk 		}
73550ce650fSreyk 		;
73650ce650fSreyk 
73750ce650fSreyk frmdir		: NO DS
73850ce650fSreyk 		{
73950ce650fSreyk 			frame_ieee80211->i_fc[1] |= IEEE80211_FC1_DIR_NODS;
74050ce650fSreyk 		}
74150ce650fSreyk 		| TO DS
74250ce650fSreyk 		{
74350ce650fSreyk 			frame_ieee80211->i_fc[1] |= IEEE80211_FC1_DIR_TODS;
74450ce650fSreyk 		}
74550ce650fSreyk 		| FROM DS
74650ce650fSreyk 		{
74750ce650fSreyk 			frame_ieee80211->i_fc[1] |= IEEE80211_FC1_DIR_FROMDS;
74850ce650fSreyk 		}
74950ce650fSreyk 		| DS TO DS
75050ce650fSreyk 		{
75150ce650fSreyk 			frame_ieee80211->i_fc[1] |= IEEE80211_FC1_DIR_DSTODS;
75250ce650fSreyk 		}
75350ce650fSreyk 		;
75450ce650fSreyk 
75550ce650fSreyk frmmatchfrom	: /* any */
75658d5d429Sreyk 		| FROM ANY
75758d5d429Sreyk 		| FROM not frmmatchaddr
75850ce650fSreyk 		{
75958d5d429Sreyk 			if (($3.flags & HOSTAPD_ACTION_F_OPT_TABLE) == 0) {
76058d5d429Sreyk 				bcopy($3.lladdr, &frame_ieee80211->i_from,
76150ce650fSreyk 				    IEEE80211_ADDR_LEN);
76258d5d429Sreyk 				HOSTAPD_MATCH(FROM, $2);
76350ce650fSreyk 			} else {
76458d5d429Sreyk 				frame.f_from = $3.table;
76558d5d429Sreyk 				HOSTAPD_MATCH_TABLE(FROM, $2);
76650ce650fSreyk 			}
76750ce650fSreyk 		}
76850ce650fSreyk 		;
76950ce650fSreyk 
77050ce650fSreyk frmmatchto	: /* any */
77158d5d429Sreyk 		| TO ANY
77258d5d429Sreyk 		| TO not frmmatchaddr
77350ce650fSreyk 		{
77458d5d429Sreyk 			if (($3.flags & HOSTAPD_ACTION_F_OPT_TABLE) == 0) {
77558d5d429Sreyk 				bcopy($3.lladdr, &frame_ieee80211->i_to,
77650ce650fSreyk 				    IEEE80211_ADDR_LEN);
77758d5d429Sreyk 				HOSTAPD_MATCH(TO, $2);
77850ce650fSreyk 			} else {
77958d5d429Sreyk 				frame.f_to = $3.table;
78058d5d429Sreyk 				HOSTAPD_MATCH_TABLE(TO, $2);
78150ce650fSreyk 			}
78250ce650fSreyk 		}
78350ce650fSreyk 		;
78450ce650fSreyk 
78550ce650fSreyk frmmatchbssid	: /* any */
78658d5d429Sreyk 		| BSSID ANY
78758d5d429Sreyk 		| BSSID not frmmatchaddr
78850ce650fSreyk 		{
78958d5d429Sreyk 			if (($3.flags & HOSTAPD_ACTION_F_OPT_TABLE) == 0) {
79058d5d429Sreyk 				bcopy($3.lladdr, &frame_ieee80211->i_bssid,
79150ce650fSreyk 				    IEEE80211_ADDR_LEN);
79258d5d429Sreyk 				HOSTAPD_MATCH(BSSID, $2);
79350ce650fSreyk 			} else {
79458d5d429Sreyk 				frame.f_bssid = $3.table;
79558d5d429Sreyk 				HOSTAPD_MATCH_TABLE(BSSID, $2);
79650ce650fSreyk 			}
79750ce650fSreyk 		}
79850ce650fSreyk 		;
79950ce650fSreyk 
8009a571ad9Sreyk frmmatchrtap	: /* empty */
8019a571ad9Sreyk 		| frmmatchrtap_l
8029a571ad9Sreyk 		;
8039a571ad9Sreyk 
8049a571ad9Sreyk frmmatchrtap_l	: frmmatchrtap_l frmmatchrtapopt
8059a571ad9Sreyk 		| frmmatchrtapopt
8069a571ad9Sreyk 		;
8079a571ad9Sreyk 
8089a571ad9Sreyk frmmatchrtapopt	: RSSI unaryop percent
8099a571ad9Sreyk 		{
8109a571ad9Sreyk 			if (($2 == HOSTAPD_OP_GT && $3 == 100) ||
8119a571ad9Sreyk 			    ($2 == HOSTAPD_OP_LE && $3 == 100) ||
8129a571ad9Sreyk 			    ($2 == HOSTAPD_OP_LT && $3 == 0) ||
8139a571ad9Sreyk 			    ($2 == HOSTAPD_OP_GE && $3 == 0)) {
8149a571ad9Sreyk 				yyerror("absurd unary comparison");
8159a571ad9Sreyk 				YYERROR;
8169a571ad9Sreyk 			}
8179a571ad9Sreyk 
8189a571ad9Sreyk 			frame.f_rssi_op = $2;
8199a571ad9Sreyk 			frame.f_rssi = $3;
8209a571ad9Sreyk 			HOSTAPD_MATCH_RADIOTAP(RSSI);
8219a571ad9Sreyk 		}
8229a571ad9Sreyk 		| TXRATE unaryop txrate
8239a571ad9Sreyk 		{
8249a571ad9Sreyk 			frame.f_txrate_op = $2;
8259a571ad9Sreyk 			frame.f_txrate = $3;
8269a571ad9Sreyk 			HOSTAPD_MATCH_RADIOTAP(RATE);
8279a571ad9Sreyk 		}
8289a571ad9Sreyk 		| FREQ unaryop freq
8299a571ad9Sreyk 		{
8309a571ad9Sreyk 			frame.f_chan_op = $2;
8319a571ad9Sreyk 			frame.f_chan = $3;
8329a571ad9Sreyk 			HOSTAPD_MATCH_RADIOTAP(CHANNEL);
8339a571ad9Sreyk 		}
8349a571ad9Sreyk 		;
8359a571ad9Sreyk 
83658d5d429Sreyk frmmatchaddr	: table
83750ce650fSreyk 		{
83850ce650fSreyk 			if (($$.table =
83958d5d429Sreyk 			    hostapd_table_lookup(&hostapd_cfg, $1)) == NULL) {
84058d5d429Sreyk 				yyerror("undefined table <%s>", $1);
84158d5d429Sreyk 				free($1);
84250ce650fSreyk 				YYERROR;
84350ce650fSreyk 			}
84450ce650fSreyk 			$$.flags = HOSTAPD_ACTION_F_OPT_TABLE;
84558d5d429Sreyk 			free($1);
84650ce650fSreyk 		}
84758d5d429Sreyk 		| lladdr
84850ce650fSreyk 		{
84958d5d429Sreyk 			bcopy($1.lladdr, $$.lladdr, IEEE80211_ADDR_LEN);
85058d5d429Sreyk 			$$.flags = HOSTAPD_ACTION_F_OPT_LLADDR;
85150ce650fSreyk 		}
85250ce650fSreyk 		;
85350ce650fSreyk 
85450ce650fSreyk frmactiontype	: TYPE DATA
85550ce650fSreyk 		{
85650ce650fSreyk 			frame_ieee80211->i_fc[0] |= IEEE80211_FC0_TYPE_DATA;
85750ce650fSreyk 		}
85850ce650fSreyk 		| TYPE MANAGEMENT frmactionmgmt
85950ce650fSreyk 		{
86050ce650fSreyk 			frame_ieee80211->i_fc[0] |= IEEE80211_FC0_TYPE_MGT;
86150ce650fSreyk 		}
86250ce650fSreyk 		;
86350ce650fSreyk 
86450ce650fSreyk frmactionmgmt	: SUBTYPE frmsubtype
86550ce650fSreyk 		;
86650ce650fSreyk 
86750ce650fSreyk frmactiondir	: /* empty */
86850ce650fSreyk 		{
86950ce650fSreyk 			frame.f_action_data.a_flags |=
87050ce650fSreyk 			    HOSTAPD_ACTION_F_OPT_DIR_AUTO;
87150ce650fSreyk 		}
87250ce650fSreyk 		| DIR frmdir
87350ce650fSreyk 		;
87450ce650fSreyk 
87550ce650fSreyk frmactionfrom	: FROM frmactionaddr
87650ce650fSreyk 		{
87750ce650fSreyk 			if (($2.flags & HOSTAPD_ACTION_F_REF_M) == 0) {
87850ce650fSreyk 				bcopy($2.lladdr, frame_ieee80211->i_from,
87950ce650fSreyk 				    IEEE80211_ADDR_LEN);
88050ce650fSreyk 			} else
88150ce650fSreyk 				frame.f_action_data.a_flags |=
88250ce650fSreyk 				    ($2.flags << HOSTAPD_ACTION_F_REF_FROM_S);
88350ce650fSreyk 		}
88450ce650fSreyk 		;
88550ce650fSreyk 
88650ce650fSreyk frmactionto	: TO frmactionaddr
88750ce650fSreyk 		{
88850ce650fSreyk 			if (($2.flags & HOSTAPD_ACTION_F_REF_M) == 0) {
88950ce650fSreyk 				bcopy($2.lladdr, frame_ieee80211->i_to,
89050ce650fSreyk 				    IEEE80211_ADDR_LEN);
89150ce650fSreyk 			} else
89250ce650fSreyk 				frame.f_action_data.a_flags |=
89350ce650fSreyk 				    ($2.flags << HOSTAPD_ACTION_F_REF_TO_S);
89450ce650fSreyk 		}
89550ce650fSreyk 		;
89650ce650fSreyk 
89750ce650fSreyk frmactionbssid	: BSSID frmactionaddr
89850ce650fSreyk 		{
89950ce650fSreyk 			if (($2.flags & HOSTAPD_ACTION_F_REF_M) == 0) {
90050ce650fSreyk 				bcopy($2.lladdr, frame_ieee80211->i_bssid,
90150ce650fSreyk 				    IEEE80211_ADDR_LEN);
90250ce650fSreyk 			} else
90350ce650fSreyk 				frame.f_action_data.a_flags |=
90450ce650fSreyk 				    ($2.flags << HOSTAPD_ACTION_F_REF_BSSID_S);
90550ce650fSreyk 		}
90650ce650fSreyk 		;
90750ce650fSreyk 
90850ce650fSreyk frmactionaddr	: lladdr
90950ce650fSreyk 		{
91050ce650fSreyk 			bcopy($1.lladdr, $$.lladdr, IEEE80211_ADDR_LEN);
9118b938b68Sreyk 			$$.flags = $1.flags;
9128b938b68Sreyk 		}
9138b938b68Sreyk 		| randaddr
9148b938b68Sreyk 		{
9158b938b68Sreyk 			$$.flags = $1.flags;
91650ce650fSreyk 		}
91750ce650fSreyk 		| refaddr
91850ce650fSreyk 		{
91950ce650fSreyk 			$$.flags = $1.flags;
92050ce650fSreyk 		}
92150ce650fSreyk 		;
92250ce650fSreyk 
92350ce650fSreyk table		: '<' STRING '>' {
92450ce650fSreyk 			if (strlen($2) >= HOSTAPD_TABLE_NAMELEN) {
92550ce650fSreyk 				yyerror("table name %s too long, max %u",
92650ce650fSreyk 				    $2, HOSTAPD_TABLE_NAMELEN - 1);
92750ce650fSreyk 				free($2);
92850ce650fSreyk 				YYERROR;
92950ce650fSreyk 			}
93050ce650fSreyk 			$$ = $2;
93150ce650fSreyk 		}
93250ce650fSreyk 		;
93350ce650fSreyk 
93450ce650fSreyk tabledef	: TABLE table {
93550ce650fSreyk 			if ((table =
93650ce650fSreyk 			    hostapd_table_add(&hostapd_cfg, $2)) == NULL) {
93750ce650fSreyk 				yyerror("failed to add table: %s", $2);
93850ce650fSreyk 				free($2);
93950ce650fSreyk 				YYERROR;
94050ce650fSreyk 			}
94150ce650fSreyk 			free($2);
94250ce650fSreyk 		} tableopts {
94350ce650fSreyk 			table = NULL;
94450ce650fSreyk 		}
94550ce650fSreyk 		;
94650ce650fSreyk 
94750ce650fSreyk tableopts	: /* empty */
94850ce650fSreyk 		| tableopts_l
94950ce650fSreyk 		;
95050ce650fSreyk 
95150ce650fSreyk tableopts_l	: tableopts_l tableopt
95250ce650fSreyk 		| tableopt
95350ce650fSreyk 		;
95450ce650fSreyk 
95550ce650fSreyk tableopt	: CONST	{
95650ce650fSreyk 			if (table->t_flags & HOSTAPD_TABLE_F_CONST) {
95750ce650fSreyk 				yyerror("option already specified");
95850ce650fSreyk 				YYERROR;
95950ce650fSreyk 			}
96050ce650fSreyk 			table->t_flags |= HOSTAPD_TABLE_F_CONST;
96150ce650fSreyk 		}
96250ce650fSreyk 		| '{' optnl '}'
96350ce650fSreyk 		| '{' optnl tableaddrlist optnl '}'
96450ce650fSreyk 		;
96550ce650fSreyk 
96650ce650fSreyk string		: string STRING
96750ce650fSreyk 		{
968d2b2a2e3Sreyk 			if (asprintf(&$$, "%s %s", $1, $2) == -1)
969d2b2a2e3Sreyk 				hostapd_fatal("string: asprintf");
970d2b2a2e3Sreyk 			free($1);
971d2b2a2e3Sreyk 			free($2);
972d2b2a2e3Sreyk 		}
973d2b2a2e3Sreyk 		| STRING
974d2b2a2e3Sreyk 		;
975d2b2a2e3Sreyk 
976d2b2a2e3Sreyk varset		: STRING '=' string
977d2b2a2e3Sreyk 		{
9780c7b4ca6Sbenno 			char *s = $1;
9790c7b4ca6Sbenno 			while (*s++) {
9800c7b4ca6Sbenno 				if (isspace((unsigned char)*s)) {
9810c7b4ca6Sbenno 					yyerror("macro name cannot contain "
9820c7b4ca6Sbenno 					    "whitespace");
98316a0a906Skrw 					free($1);
98416a0a906Skrw 					free($3);
9850c7b4ca6Sbenno 					YYERROR;
9860c7b4ca6Sbenno 				}
9870c7b4ca6Sbenno 			}
988d2b2a2e3Sreyk 			if (symset($1, $3, 0) == -1)
989d2b2a2e3Sreyk 				hostapd_fatal("cannot store variable");
990d2b2a2e3Sreyk 			free($1);
991d2b2a2e3Sreyk 			free($3);
992d2b2a2e3Sreyk 		}
993d2b2a2e3Sreyk 		;
994d2b2a2e3Sreyk 
99550ce650fSreyk refaddr		: '&' FROM
99650ce650fSreyk 		{
99750ce650fSreyk 			$$.flags |= HOSTAPD_ACTION_F_REF_FROM;
99850ce650fSreyk 		}
99950ce650fSreyk 		| '&' TO
100050ce650fSreyk 		{
100150ce650fSreyk 			$$.flags |= HOSTAPD_ACTION_F_REF_TO;
100250ce650fSreyk 		}
100350ce650fSreyk 		| '&' BSSID
100450ce650fSreyk 		{
100550ce650fSreyk 			$$.flags |= HOSTAPD_ACTION_F_REF_BSSID;
100650ce650fSreyk 		}
100750ce650fSreyk 		;
100850ce650fSreyk 
100950ce650fSreyk tableaddrlist	: tableaddrentry
101050ce650fSreyk 		| tableaddrlist comma tableaddrentry
101150ce650fSreyk 		;
101250ce650fSreyk 
101350ce650fSreyk tableaddrentry	: lladdr
101450ce650fSreyk 		{
101550ce650fSreyk 			if ((entry = hostapd_entry_add(table,
101650ce650fSreyk 			    $1.lladdr)) == NULL) {
101750ce650fSreyk 				yyerror("failed to add entry: %s",
101850ce650fSreyk 				    etheraddr_string($1.lladdr));
101950ce650fSreyk 				YYERROR;
102050ce650fSreyk 			}
102150ce650fSreyk 		} tableaddropt {
102250ce650fSreyk 			entry = NULL;
102350ce650fSreyk 		}
102450ce650fSreyk 		;
102550ce650fSreyk 
102650ce650fSreyk tableaddropt	: /* empty */
1027ca87d875Sreyk 		| assign ipv4addr ipv4netmask
102850ce650fSreyk 		{
1029d359c216Sreyk 			entry->e_flags |= HOSTAPD_ENTRY_F_INADDR;
1030d359c216Sreyk 			entry->e_inaddr.in_af = AF_INET;
1031d359c216Sreyk 			bcopy(&$2, &entry->e_inaddr.in_v4,
1032d359c216Sreyk 			    sizeof(struct in_addr));
103350ce650fSreyk 		}
103450ce650fSreyk 		| mask lladdr
103550ce650fSreyk 		{
103650ce650fSreyk 			entry->e_flags |= HOSTAPD_ENTRY_F_MASK;
103750ce650fSreyk 			bcopy($2.lladdr, entry->e_mask, IEEE80211_ADDR_LEN);
103850ce650fSreyk 
103950ce650fSreyk 			/* Update entry position in the table */
104050ce650fSreyk 			hostapd_entry_update(table, entry);
104150ce650fSreyk 		}
104250ce650fSreyk 		;
104350ce650fSreyk 
104450ce650fSreyk ipv4addr	: STRING
104550ce650fSreyk 		{
104650ce650fSreyk 			if (inet_net_pton(AF_INET, $1, &$$, sizeof($$)) == -1) {
104750ce650fSreyk 				yyerror("invalid address: %s\n", $1);
104850ce650fSreyk 				free($1);
104950ce650fSreyk 				YYERROR;
105050ce650fSreyk 			}
105150ce650fSreyk 			free($1);
105250ce650fSreyk 		}
105350ce650fSreyk 		;
105450ce650fSreyk 
1055ca87d875Sreyk ipv4netmask	: /* empty */
1056d359c216Sreyk 		{
1057d359c216Sreyk 			entry->e_inaddr.in_netmask = -1;
1058d359c216Sreyk 		}
1059ca87d875Sreyk 		| '/' NUMBER
1060d359c216Sreyk 		{
1061ca87d875Sreyk 			if ($2 < 0 || $2 > 32) {
1062ca87d875Sreyk 				yyerror("netmask out of range: %lld", $2);
1063ca87d875Sreyk 				YYERROR;
1064ca87d875Sreyk 			}
1065d359c216Sreyk 			entry->e_inaddr.in_netmask = $2;
1066d359c216Sreyk 		}
1067d359c216Sreyk 		;
1068d359c216Sreyk 
106950ce650fSreyk lladdr		: STRING
107050ce650fSreyk 		{
107150ce650fSreyk 			struct ether_addr *ea;
107250ce650fSreyk 
107350ce650fSreyk 			if ((ea = ether_aton($1)) == NULL) {
107450ce650fSreyk 				yyerror("invalid address: %s\n", $1);
107550ce650fSreyk 				free($1);
107650ce650fSreyk 				YYERROR;
107750ce650fSreyk 			}
107850ce650fSreyk 			free($1);
107950ce650fSreyk 
108050ce650fSreyk 			bcopy(ea, $$.lladdr, IEEE80211_ADDR_LEN);
10818b938b68Sreyk 			$$.flags = HOSTAPD_ACTION_F_OPT_LLADDR;
10828b938b68Sreyk 		}
10838b938b68Sreyk 		;
10848b938b68Sreyk 
10858b938b68Sreyk randaddr	: RANDOM
10868b938b68Sreyk 		{
10878b938b68Sreyk 			$$.flags |= HOSTAPD_ACTION_F_REF_RANDOM;
108850ce650fSreyk 		}
108950ce650fSreyk 		;
109050ce650fSreyk 
109150ce650fSreyk passive		: /* empty */
109250ce650fSreyk 		| PASSIVE
109350ce650fSreyk 		{
109450ce650fSreyk 			hostapd_cfg.c_flags |= HOSTAPD_CFG_F_IAPP_PASSIVE;
109550ce650fSreyk 		}
109650ce650fSreyk 		;
109750ce650fSreyk 
10984a040c17Sderaadt assign		: ARROW
109950ce650fSreyk 		;
110050ce650fSreyk 
110150ce650fSreyk mask		: '&'
110250ce650fSreyk 		;
110350ce650fSreyk 
1104*3a50f0a9Sjmc comma		: /* empty */
110550ce650fSreyk 		| ',' optnl
110650ce650fSreyk 		;
110750ce650fSreyk 
110850ce650fSreyk optnl		: /* empty */
110950ce650fSreyk 		| '\n'
111050ce650fSreyk 		;
111150ce650fSreyk 
111250ce650fSreyk not		: /* empty */
11133a61e91cSreyk 		{
111458d5d429Sreyk 			$$ = 0;
11153a61e91cSreyk 		}
111650ce650fSreyk 		| '!'
111750ce650fSreyk 		{
111858d5d429Sreyk 			$$ = 1;
111950ce650fSreyk 		}
1120cb46a11aSreyk 		| NOT
1121cb46a11aSreyk 		{
112258d5d429Sreyk 			$$ = 1;
1123cb46a11aSreyk 		}
112450ce650fSreyk 		;
112550ce650fSreyk 
11269a571ad9Sreyk unaryop		: /* any */
11279a571ad9Sreyk 		{
11289a571ad9Sreyk 			$$ = HOSTAPD_OP_EQ;
11299a571ad9Sreyk 		}
11309a571ad9Sreyk 		| '='
11319a571ad9Sreyk 		{
11329a571ad9Sreyk 			$$ = HOSTAPD_OP_EQ;
11339a571ad9Sreyk 		}
11349a571ad9Sreyk 		| '=='
11359a571ad9Sreyk 		{
11369a571ad9Sreyk 			$$ = HOSTAPD_OP_EQ;
11379a571ad9Sreyk 		}
11389a571ad9Sreyk 		| '!'
11399a571ad9Sreyk 		{
11409a571ad9Sreyk 			$$ = HOSTAPD_OP_NE;
11419a571ad9Sreyk 		}
11424a040c17Sderaadt 		| NE
11439a571ad9Sreyk 		{
11449a571ad9Sreyk 			$$ = HOSTAPD_OP_NE;
11459a571ad9Sreyk 		}
11464a040c17Sderaadt 		| LE
11479a571ad9Sreyk 		{
11489a571ad9Sreyk 			$$ = HOSTAPD_OP_LE;
11499a571ad9Sreyk 		}
11509a571ad9Sreyk 		| '<'
11519a571ad9Sreyk 		{
11529a571ad9Sreyk 			$$ = HOSTAPD_OP_LT;
11539a571ad9Sreyk 		}
11544a040c17Sderaadt 		| GE
11559a571ad9Sreyk 		{
11569a571ad9Sreyk 			$$ = HOSTAPD_OP_GE;
11579a571ad9Sreyk 		}
11589a571ad9Sreyk 		| '>'
11599a571ad9Sreyk 		{
11609a571ad9Sreyk 			$$ = HOSTAPD_OP_GT;
11619a571ad9Sreyk 		}
11629a571ad9Sreyk 		;
11639a571ad9Sreyk 
11649a571ad9Sreyk percent		: STRING
11659a571ad9Sreyk 		{
11669a571ad9Sreyk 			double val;
11679a571ad9Sreyk 			char *cp;
11689a571ad9Sreyk 
11699a571ad9Sreyk 			val = strtod($1, &cp);
11709a571ad9Sreyk 			if (cp == NULL || strcmp(cp, "%") != 0 ||
11719a571ad9Sreyk 			    val < 0 || val > 100) {
11729a571ad9Sreyk 				yyerror("invalid percentage: %s", $1);
11739a571ad9Sreyk 				free($1);
11749a571ad9Sreyk 				YYERROR;
11759a571ad9Sreyk 			}
11769a571ad9Sreyk 			free($1);
11779a571ad9Sreyk 			$$ = val;
11789a571ad9Sreyk 		}
11799a571ad9Sreyk 		;
11809a571ad9Sreyk 
11819a571ad9Sreyk txrate		: STRING
11829a571ad9Sreyk 		{
11839a571ad9Sreyk 			double val;
11849a571ad9Sreyk 			char *cp;
11859a571ad9Sreyk 
11869a571ad9Sreyk 			val = strtod($1, &cp) * 2;
11879a571ad9Sreyk 			if (cp == NULL || strcasecmp(cp, "mb") != 0 ||
11889a571ad9Sreyk 			    val != (int)val) {
11899a571ad9Sreyk 				yyerror("invalid rate: %s", $1);
11909a571ad9Sreyk 				free($1);
11919a571ad9Sreyk 				YYERROR;
11929a571ad9Sreyk 			}
11939a571ad9Sreyk 			free($1);
11949a571ad9Sreyk 			$$ = val;
11959a571ad9Sreyk 		}
11969a571ad9Sreyk 		;
11979a571ad9Sreyk 
11989a571ad9Sreyk freq		: STRING
11999a571ad9Sreyk 		{
12009a571ad9Sreyk 			double val;
12019a571ad9Sreyk 			char *cp;
12029a571ad9Sreyk 
12039a571ad9Sreyk 			val = strtod($1, &cp);
12049a571ad9Sreyk 			if (cp != NULL) {
12059a571ad9Sreyk 				if (strcasecmp(cp, "ghz") == 0) {
12069a571ad9Sreyk 					$$ = val * 1000;
12079a571ad9Sreyk 				} else if (strcasecmp(cp, "mhz") == 0) {
12089a571ad9Sreyk 					$$ = val;
12099a571ad9Sreyk 				} else
12109a571ad9Sreyk 					cp = NULL;
12119a571ad9Sreyk 			}
12129a571ad9Sreyk 			if (cp == NULL) {
12139a571ad9Sreyk 				yyerror("invalid frequency: %s", $1);
12149a571ad9Sreyk 				free($1);
12159a571ad9Sreyk 				YYERROR;
12169a571ad9Sreyk 			}
12179a571ad9Sreyk 			free($1);
12189a571ad9Sreyk 		}
12199a571ad9Sreyk 		;
122001908219Sreyk 
1221ca87d875Sreyk timeout		: NUMBER
122201908219Sreyk 		{
1223ca87d875Sreyk 			if ($1 < 1 || $1 > LONG_MAX) {
1224ca87d875Sreyk 				yyerror("timeout out of range: %lld", $1);
1225ca87d875Sreyk 				YYERROR;
1226ca87d875Sreyk 			}
122701908219Sreyk 			$$.tv_sec = $1 / 1000;
122801908219Sreyk 			$$.tv_usec = ($1 % 1000) * 1000;
122901908219Sreyk 		}
123001908219Sreyk 		;
1231d2b2a2e3Sreyk %%
1232d2b2a2e3Sreyk 
1233d2b2a2e3Sreyk /*
1234d2b2a2e3Sreyk  * Parser and lexer
1235d2b2a2e3Sreyk  */
1236d2b2a2e3Sreyk 
1237d2b2a2e3Sreyk struct keywords {
1238d2b2a2e3Sreyk 	char *k_name;
1239d2b2a2e3Sreyk 	int k_val;
1240d2b2a2e3Sreyk };
1241d2b2a2e3Sreyk 
1242d2b2a2e3Sreyk int
kw_cmp(const void * a,const void * b)1243d2b2a2e3Sreyk kw_cmp(const void *a, const void *b)
1244d2b2a2e3Sreyk {
1245d2b2a2e3Sreyk 	return strcmp(a, ((const struct keywords *)b)->k_name);
1246d2b2a2e3Sreyk }
1247d2b2a2e3Sreyk 
1248d2b2a2e3Sreyk int
lookup(char * token)1249d2b2a2e3Sreyk lookup(char *token)
1250d2b2a2e3Sreyk {
1251d2b2a2e3Sreyk 	/* Keep this list sorted */
1252d2b2a2e3Sreyk 	static const struct keywords keywords[] = {
125350ce650fSreyk 		{ "add",		ADD },
125476fb2f4fSreyk 		{ "address",		ADDRESS },
125550ce650fSreyk 		{ "any",		ANY },
125650ce650fSreyk 		{ "assoc",		ASSOC },
125750ce650fSreyk 		{ "assoced",		ASSOCED },
125850ce650fSreyk 		{ "atim",		ATIM },
125950ce650fSreyk 		{ "auth",		AUTH },
126050ce650fSreyk 		{ "authed",		AUTHED },
126150ce650fSreyk 		{ "beacon",		BEACON },
1262d2b2a2e3Sreyk 		{ "broadcast",		BROADCAST },
126350ce650fSreyk 		{ "bssid",		BSSID },
126450ce650fSreyk 		{ "const",		CONST },
126550ce650fSreyk 		{ "data",		DATA },
126650ce650fSreyk 		{ "deauth",		DEAUTH },
126701908219Sreyk 		{ "delay",		DELAY },
126850ce650fSreyk 		{ "delete",		DELETE },
126950ce650fSreyk 		{ "dir",		DIR },
127050ce650fSreyk 		{ "disassoc",		DISASSOC },
127150ce650fSreyk 		{ "ds",			DS },
127250ce650fSreyk 		{ "expire",		EXPIRE },
127350ce650fSreyk 		{ "failure",		FAILURE },
127450ce650fSreyk 		{ "frame",		FRAME },
12759a571ad9Sreyk 		{ "freq",		FREQ },
127650ce650fSreyk 		{ "from",		FROM },
127750ce650fSreyk 		{ "handle",		HANDLE },
127801908219Sreyk 		{ "hopper",		HOPPER },
1279d2b2a2e3Sreyk 		{ "hostap",		HOSTAP },
1280d2b2a2e3Sreyk 		{ "iapp",		IAPP },
128150ce650fSreyk 		{ "ie",			IE },
1282ab224e3bSreyk 		{ "include",		INCLUDE },
128350ce650fSreyk 		{ "inconsistent",	INCONSISTENT },
1284d2b2a2e3Sreyk 		{ "interface",		INTERFACE },
128550ce650fSreyk 		{ "invalid",		INVALID },
128650ce650fSreyk 		{ "leave",		LEAVE },
128750ce650fSreyk 		{ "limit",		LIMIT },
128850ce650fSreyk 		{ "log",		LOG },
128950ce650fSreyk 		{ "management",		MANAGEMENT },
129050ce650fSreyk 		{ "mic",		MIC },
1291d2b2a2e3Sreyk 		{ "mode",		MODE },
1292d2b2a2e3Sreyk 		{ "multicast",		MULTICAST },
129350ce650fSreyk 		{ "no",			NO },
129450ce650fSreyk 		{ "node",		NODE },
1295fbd03df9Sreyk 		{ "not",		NOT },
12963a61e91cSreyk 		{ "notify",		NOTIFY },
129750ce650fSreyk 		{ "nwid",		NWID },
12985058e176Sreyk 		{ "on",			ON },
12998b938b68Sreyk 		{ "open",		OPEN },
130050ce650fSreyk 		{ "passive",		PASSIVE },
130150ce650fSreyk 		{ "pcap",		PCAP },
130276fb2f4fSreyk 		{ "port",		PORT },
130350ce650fSreyk 		{ "probe",		PROBE },
130450ce650fSreyk 		{ "quick",		QUICK },
130550ce650fSreyk 		{ "radiotap",		RADIOTAP },
13068b938b68Sreyk 		{ "random",		RANDOM },
13079834aad8Sreyk 		{ "rate",		RATE },
130850ce650fSreyk 		{ "reason",		REASON },
130950ce650fSreyk 		{ "reassoc",		REASSOC },
131050ce650fSreyk 		{ "request",		REQUEST },
131150ce650fSreyk 		{ "required",		REQUIRED },
131250ce650fSreyk 		{ "resend",		RESEND },
131350ce650fSreyk 		{ "reserved",		RESERVED },
131450ce650fSreyk 		{ "response",		RESPONSE },
131574ff1540Sreyk 		{ "roaming",		ROAMING },
131674ff1540Sreyk 		{ "route",		ROUTE },
131750ce650fSreyk 		{ "rsn",		RSN },
131850ce650fSreyk 		{ "sec",		SEC },
1319d2b2a2e3Sreyk 		{ "set",		SET },
13209a571ad9Sreyk 		{ "signal",		RSSI },
132150ce650fSreyk 		{ "skip",		SKIP },
132250ce650fSreyk 		{ "subtype",		SUBTYPE },
132350ce650fSreyk 		{ "table",		TABLE },
132450ce650fSreyk 		{ "to",			TO },
132550ce650fSreyk 		{ "toomany",		TOOMANY },
132602fe3327Sreyk 		{ "ttl",		TTL },
13279a571ad9Sreyk 		{ "txrate",		TXRATE },
132850ce650fSreyk 		{ "type",		TYPE },
132950ce650fSreyk 		{ "unspecified",	UNSPECIFIED },
133050ce650fSreyk 		{ "usec",		USEC },
133150ce650fSreyk 		{ "verbose",		VERBOSE },
133250ce650fSreyk 		{ "with",		WITH }
1333d2b2a2e3Sreyk 	};
1334d2b2a2e3Sreyk 	const struct keywords *p;
1335d2b2a2e3Sreyk 
1336d2b2a2e3Sreyk 	p = bsearch(token, keywords, sizeof(keywords) / sizeof(keywords[0]),
1337d2b2a2e3Sreyk 	    sizeof(keywords[0]), kw_cmp);
1338d2b2a2e3Sreyk 
1339d2b2a2e3Sreyk 	return (p == NULL ? STRING : p->k_val);
1340d2b2a2e3Sreyk }
1341d2b2a2e3Sreyk 
13429a683e37Sdenis #define	START_EXPAND	1
13439a683e37Sdenis #define	DONE_EXPAND	2
1344d2b2a2e3Sreyk 
13459a683e37Sdenis static int	expanding;
13469a683e37Sdenis 
13479a683e37Sdenis int
igetc(void)13489a683e37Sdenis igetc(void)
13499a683e37Sdenis {
13509a683e37Sdenis 	int	c;
13519a683e37Sdenis 
13529a683e37Sdenis 	while (1) {
13539a683e37Sdenis 		if (file->ungetpos > 0)
13549a683e37Sdenis 			c = file->ungetbuf[--file->ungetpos];
13559a683e37Sdenis 		else
13569a683e37Sdenis 			c = getc(file->stream);
13579a683e37Sdenis 
13589a683e37Sdenis 		if (c == START_EXPAND)
13599a683e37Sdenis 			expanding = 1;
13609a683e37Sdenis 		else if (c == DONE_EXPAND)
13619a683e37Sdenis 			expanding = 0;
13629a683e37Sdenis 		else
13639a683e37Sdenis 			break;
13649a683e37Sdenis 	}
13659a683e37Sdenis 	return (c);
13669a683e37Sdenis }
1367d2b2a2e3Sreyk 
1368d2b2a2e3Sreyk int
lgetc(int quotec)136920741916Sderaadt lgetc(int quotec)
1370d2b2a2e3Sreyk {
1371d2b2a2e3Sreyk 	int		c, next;
1372d2b2a2e3Sreyk 
137320741916Sderaadt 	if (quotec) {
13749a683e37Sdenis 		if ((c = igetc()) == EOF) {
1375c6004ab9Smpf 			yyerror("reached end of file while parsing "
1376c6004ab9Smpf 			    "quoted string");
1377c6004ab9Smpf 			if (file == topfile || popfile() == EOF)
137820741916Sderaadt 				return (EOF);
137920741916Sderaadt 			return (quotec);
138020741916Sderaadt 		}
1381d5d66eaeSderaadt 		return (c);
1382d5d66eaeSderaadt 	}
1383d5d66eaeSderaadt 
13849a683e37Sdenis 	while ((c = igetc()) == '\\') {
13859a683e37Sdenis 		next = igetc();
1386d2b2a2e3Sreyk 		if (next != '\n') {
1387e3bfd77aSderaadt 			c = next;
1388d2b2a2e3Sreyk 			break;
1389d2b2a2e3Sreyk 		}
1390ab224e3bSreyk 		yylval.lineno = file->lineno;
1391ab224e3bSreyk 		file->lineno++;
1392d2b2a2e3Sreyk 	}
1393d2b2a2e3Sreyk 
13949a683e37Sdenis 	if (c == EOF) {
13959a683e37Sdenis 		/*
13969a683e37Sdenis 		 * Fake EOL when hit EOF for the first time. This gets line
13979a683e37Sdenis 		 * count right if last line in included file is syntactically
13989a683e37Sdenis 		 * invalid and has no newline.
13999a683e37Sdenis 		 */
14009a683e37Sdenis 		if (file->eof_reached == 0) {
14019a683e37Sdenis 			file->eof_reached = 1;
14029a683e37Sdenis 			return ('\n');
14039a683e37Sdenis 		}
140420741916Sderaadt 		while (c == EOF) {
1405c6004ab9Smpf 			if (file == topfile || popfile() == EOF)
140620741916Sderaadt 				return (EOF);
14079a683e37Sdenis 			c = igetc();
14089a683e37Sdenis 		}
1409ab224e3bSreyk 	}
1410d2b2a2e3Sreyk 	return (c);
1411d2b2a2e3Sreyk }
1412d2b2a2e3Sreyk 
14139a683e37Sdenis void
lungetc(int c)1414d2b2a2e3Sreyk lungetc(int c)
1415d2b2a2e3Sreyk {
1416d2b2a2e3Sreyk 	if (c == EOF)
14179a683e37Sdenis 		return;
14189a683e37Sdenis 
14199a683e37Sdenis 	if (file->ungetpos >= file->ungetsize) {
14209a683e37Sdenis 		void *p = reallocarray(file->ungetbuf, file->ungetsize, 2);
14219a683e37Sdenis 		if (p == NULL)
1422a062aa9dSkrw 			err(1, "%s", __func__);
14239a683e37Sdenis 		file->ungetbuf = p;
14249a683e37Sdenis 		file->ungetsize *= 2;
1425d2b2a2e3Sreyk 	}
14269a683e37Sdenis 	file->ungetbuf[file->ungetpos++] = c;
1427d2b2a2e3Sreyk }
1428d2b2a2e3Sreyk 
1429d2b2a2e3Sreyk int
findeol(void)1430d2b2a2e3Sreyk findeol(void)
1431d2b2a2e3Sreyk {
1432d2b2a2e3Sreyk 	int	c;
1433d2b2a2e3Sreyk 
1434d2b2a2e3Sreyk 	/* skip to either EOF or the first real EOL */
1435d2b2a2e3Sreyk 	while (1) {
1436d5d66eaeSderaadt 		c = lgetc(0);
1437d2b2a2e3Sreyk 		if (c == '\n') {
1438ab224e3bSreyk 			file->lineno++;
1439d2b2a2e3Sreyk 			break;
1440d2b2a2e3Sreyk 		}
1441d2b2a2e3Sreyk 		if (c == EOF)
1442d2b2a2e3Sreyk 			break;
1443d2b2a2e3Sreyk 	}
1444d2b2a2e3Sreyk 	return (ERROR);
1445d2b2a2e3Sreyk }
1446d2b2a2e3Sreyk 
1447d2b2a2e3Sreyk int
yylex(void)1448d2b2a2e3Sreyk yylex(void)
1449d2b2a2e3Sreyk {
145008f6ba19Snaddy 	char	 buf[8096];
145108f6ba19Snaddy 	char	*p, *val;
145220741916Sderaadt 	int	 quotec, next, c;
1453d2b2a2e3Sreyk 	int	 token;
1454d2b2a2e3Sreyk 
1455d2b2a2e3Sreyk top:
1456d2b2a2e3Sreyk 	p = buf;
14572053f12aSmpf 	while ((c = lgetc(0)) == ' ' || c == '\t')
1458d2b2a2e3Sreyk 		; /* nothing */
1459d2b2a2e3Sreyk 
1460ab224e3bSreyk 	yylval.lineno = file->lineno;
1461d2b2a2e3Sreyk 	if (c == '#')
1462d5d66eaeSderaadt 		while ((c = lgetc(0)) != '\n' && c != EOF)
1463d2b2a2e3Sreyk 			; /* nothing */
14649a683e37Sdenis 	if (c == '$' && !expanding) {
1465d2b2a2e3Sreyk 		while (1) {
1466d5d66eaeSderaadt 			if ((c = lgetc(0)) == EOF)
1467d2b2a2e3Sreyk 				return (0);
1468d2b2a2e3Sreyk 
1469d2b2a2e3Sreyk 			if (p + 1 >= buf + sizeof(buf) - 1) {
1470d2b2a2e3Sreyk 				yyerror("string too long");
1471d2b2a2e3Sreyk 				return (findeol());
1472d2b2a2e3Sreyk 			}
1473d2b2a2e3Sreyk 			if (isalnum(c) || c == '_') {
1474015d7b4dSbenno 				*p++ = c;
1475d2b2a2e3Sreyk 				continue;
1476d2b2a2e3Sreyk 			}
1477d2b2a2e3Sreyk 			*p = '\0';
1478d2b2a2e3Sreyk 			lungetc(c);
1479d2b2a2e3Sreyk 			break;
1480d2b2a2e3Sreyk 		}
1481d2b2a2e3Sreyk 		val = symget(buf);
1482d2b2a2e3Sreyk 		if (val == NULL) {
1483d2b2a2e3Sreyk 			yyerror("macro \"%s\" not defined", buf);
1484d2b2a2e3Sreyk 			return (findeol());
1485d2b2a2e3Sreyk 		}
14869a683e37Sdenis 		p = val + strlen(val) - 1;
14879a683e37Sdenis 		lungetc(DONE_EXPAND);
14889a683e37Sdenis 		while (p >= val) {
148908f6ba19Snaddy 			lungetc((unsigned char)*p);
14909a683e37Sdenis 			p--;
14919a683e37Sdenis 		}
14929a683e37Sdenis 		lungetc(START_EXPAND);
1493d2b2a2e3Sreyk 		goto top;
1494d2b2a2e3Sreyk 	}
1495d2b2a2e3Sreyk 
1496d2b2a2e3Sreyk 	switch (c) {
1497d2b2a2e3Sreyk 	case '\'':
1498d2b2a2e3Sreyk 	case '"':
149920741916Sderaadt 		quotec = c;
1500d2b2a2e3Sreyk 		while (1) {
150120741916Sderaadt 			if ((c = lgetc(quotec)) == EOF)
1502d2b2a2e3Sreyk 				return (0);
1503d2b2a2e3Sreyk 			if (c == '\n') {
1504ab224e3bSreyk 				file->lineno++;
1505d2b2a2e3Sreyk 				continue;
150620741916Sderaadt 			} else if (c == '\\') {
150720741916Sderaadt 				if ((next = lgetc(quotec)) == EOF)
150820741916Sderaadt 					return (0);
1509a1533359Ssashan 				if (next == quotec || next == ' ' ||
1510a1533359Ssashan 				    next == '\t')
151120741916Sderaadt 					c = next;
1512daf24110Shenning 				else if (next == '\n') {
1513daf24110Shenning 					file->lineno++;
1514ea014f46Sderaadt 					continue;
1515daf24110Shenning 				} else
151620741916Sderaadt 					lungetc(next);
151720741916Sderaadt 			} else if (c == quotec) {
151820741916Sderaadt 				*p = '\0';
151920741916Sderaadt 				break;
152041eef22fSjsg 			} else if (c == '\0') {
152141eef22fSjsg 				yyerror("syntax error");
152241eef22fSjsg 				return (findeol());
1523d2b2a2e3Sreyk 			}
1524d2b2a2e3Sreyk 			if (p + 1 >= buf + sizeof(buf) - 1) {
1525d2b2a2e3Sreyk 				yyerror("string too long");
1526d2b2a2e3Sreyk 				return (findeol());
1527d2b2a2e3Sreyk 			}
1528015d7b4dSbenno 			*p++ = c;
1529d2b2a2e3Sreyk 		}
1530d2b2a2e3Sreyk 		yylval.v.string = strdup(buf);
1531d2b2a2e3Sreyk 		if (yylval.v.string == NULL)
1532d2b2a2e3Sreyk 			hostapd_fatal("yylex: strdup");
1533d2b2a2e3Sreyk 		return (STRING);
15344a040c17Sderaadt 	case '-':
15354a040c17Sderaadt 		next = lgetc(0);
15364a040c17Sderaadt 		if (next == '>')
15374a040c17Sderaadt 			return (ARROW);
15384a040c17Sderaadt 		lungetc(next);
15394a040c17Sderaadt 		break;
15404a040c17Sderaadt 	case '!':
15414a040c17Sderaadt 		next = lgetc(0);
15424a040c17Sderaadt 		if (next == '=')
15434a040c17Sderaadt 			return (NE);
15444a040c17Sderaadt 		lungetc(next);
15454a040c17Sderaadt 		break;
15464a040c17Sderaadt 	case '<':
15474a040c17Sderaadt 		next = lgetc(0);
15484a040c17Sderaadt 		if (next == '=')
15494a040c17Sderaadt 			return (LE);
15504a040c17Sderaadt 		lungetc(next);
15514a040c17Sderaadt 		break;
15524a040c17Sderaadt 	case '>':
15534a040c17Sderaadt 		next = lgetc(0);
15544a040c17Sderaadt 		if (next == '=')
15554a040c17Sderaadt 			return (GE);
15564a040c17Sderaadt 		lungetc(next);
15574a040c17Sderaadt 		break;
1558d2b2a2e3Sreyk 	}
1559d2b2a2e3Sreyk 
1560ca87d875Sreyk #define allowed_to_end_number(x) \
15610cf2c9c3Smpf 	(isspace(x) || x == ')' || x ==',' || x == '/' || x == '}' || x == '=')
1562ca87d875Sreyk 
1563ca87d875Sreyk 	if (c == '-' || isdigit(c)) {
1564ca87d875Sreyk 		do {
1565ca87d875Sreyk 			*p++ = c;
1566915c3f33Sderaadt 			if ((size_t)(p-buf) >= sizeof(buf)) {
1567ca87d875Sreyk 				yyerror("string too long");
1568ca87d875Sreyk 				return (findeol());
1569ca87d875Sreyk 			}
1570d5d66eaeSderaadt 		} while ((c = lgetc(0)) != EOF && isdigit(c));
1571ca87d875Sreyk 		lungetc(c);
1572ca87d875Sreyk 		if (p == buf + 1 && buf[0] == '-')
1573ca87d875Sreyk 			goto nodigits;
1574ca87d875Sreyk 		if (c == EOF || allowed_to_end_number(c)) {
1575ca87d875Sreyk 			const char *errstr = NULL;
1576ca87d875Sreyk 
1577ca87d875Sreyk 			*p = '\0';
1578d5d66eaeSderaadt 			yylval.v.number = strtonum(buf, LLONG_MIN,
1579d5d66eaeSderaadt 			    LLONG_MAX, &errstr);
1580ca87d875Sreyk 			if (errstr) {
1581d5d66eaeSderaadt 				yyerror("\"%s\" invalid number: %s",
1582d5d66eaeSderaadt 				    buf, errstr);
1583ca87d875Sreyk 				return (findeol());
1584ca87d875Sreyk 			}
1585ca87d875Sreyk 			return (NUMBER);
1586ca87d875Sreyk 		} else {
1587ca87d875Sreyk nodigits:
1588ca87d875Sreyk 			while (p > buf + 1)
158908f6ba19Snaddy 				lungetc((unsigned char)*--p);
159008f6ba19Snaddy 			c = (unsigned char)*--p;
1591ca87d875Sreyk 			if (c == '-')
1592ca87d875Sreyk 				return (c);
1593ca87d875Sreyk 		}
1594ca87d875Sreyk 	}
1595ca87d875Sreyk 
1596d2b2a2e3Sreyk #define allowed_in_string(x) \
1597d2b2a2e3Sreyk 	(isalnum(x) || (ispunct(x) && x != '(' && x != ')' && \
1598d2b2a2e3Sreyk 	x != '{' && x != '}' && x != '<' && x != '>' && \
1599d2b2a2e3Sreyk 	x != '!' && x != '=' && x != '/' && x != '#' && \
1600d2b2a2e3Sreyk 	x != ','))
1601d2b2a2e3Sreyk 
1602d2b2a2e3Sreyk 	if (isalnum(c) || c == ':' || c == '_' || c == '*') {
1603d2b2a2e3Sreyk 		do {
1604d2b2a2e3Sreyk 			*p++ = c;
1605915c3f33Sderaadt 			if ((size_t)(p-buf) >= sizeof(buf)) {
1606d2b2a2e3Sreyk 				yyerror("string too long");
1607d2b2a2e3Sreyk 				return (findeol());
1608d2b2a2e3Sreyk 			}
1609d5d66eaeSderaadt 		} while ((c = lgetc(0)) != EOF && (allowed_in_string(c)));
1610d2b2a2e3Sreyk 		lungetc(c);
1611d2b2a2e3Sreyk 		*p = '\0';
1612d2b2a2e3Sreyk 		if ((token = lookup(buf)) == STRING)
1613d2b2a2e3Sreyk 			if ((yylval.v.string = strdup(buf)) == NULL)
1614d2b2a2e3Sreyk 				hostapd_fatal("yylex: strdup");
1615d2b2a2e3Sreyk 		return (token);
1616d2b2a2e3Sreyk 	}
1617d2b2a2e3Sreyk 	if (c == '\n') {
1618ab224e3bSreyk 		yylval.lineno = file->lineno;
1619ab224e3bSreyk 		file->lineno++;
1620d2b2a2e3Sreyk 	}
1621d2b2a2e3Sreyk 	if (c == EOF)
1622d2b2a2e3Sreyk 		return (0);
1623d2b2a2e3Sreyk 	return (c);
1624d2b2a2e3Sreyk }
1625d2b2a2e3Sreyk 
1626d2b2a2e3Sreyk int
symset(const char * nam,const char * val,int persist)1627d2b2a2e3Sreyk symset(const char *nam, const char *val, int persist)
1628d2b2a2e3Sreyk {
1629d2b2a2e3Sreyk 	struct sym	*sym;
1630d2b2a2e3Sreyk 
163154c95b7aSkrw 	TAILQ_FOREACH(sym, &symhead, entry) {
163254c95b7aSkrw 		if (strcmp(nam, sym->nam) == 0)
163354c95b7aSkrw 			break;
163454c95b7aSkrw 	}
1635d2b2a2e3Sreyk 
1636d2b2a2e3Sreyk 	if (sym != NULL) {
1637d2b2a2e3Sreyk 		if (sym->persist == 1)
1638d2b2a2e3Sreyk 			return (0);
1639d2b2a2e3Sreyk 		else {
1640d2b2a2e3Sreyk 			free(sym->nam);
1641d2b2a2e3Sreyk 			free(sym->val);
1642d2b2a2e3Sreyk 			TAILQ_REMOVE(&symhead, sym, entry);
1643d2b2a2e3Sreyk 			free(sym);
1644d2b2a2e3Sreyk 		}
1645d2b2a2e3Sreyk 	}
164635de856eSderaadt 	if ((sym = calloc(1, sizeof(*sym))) == NULL)
1647d2b2a2e3Sreyk 		return (-1);
1648d2b2a2e3Sreyk 
1649d2b2a2e3Sreyk 	sym->nam = strdup(nam);
1650d2b2a2e3Sreyk 	if (sym->nam == NULL) {
1651d2b2a2e3Sreyk 		free(sym);
1652d2b2a2e3Sreyk 		return (-1);
1653d2b2a2e3Sreyk 	}
1654d2b2a2e3Sreyk 	sym->val = strdup(val);
1655d2b2a2e3Sreyk 	if (sym->val == NULL) {
1656d2b2a2e3Sreyk 		free(sym->nam);
1657d2b2a2e3Sreyk 		free(sym);
1658d2b2a2e3Sreyk 		return (-1);
1659d2b2a2e3Sreyk 	}
1660d2b2a2e3Sreyk 	sym->used = 0;
1661d2b2a2e3Sreyk 	sym->persist = persist;
1662d2b2a2e3Sreyk 	TAILQ_INSERT_TAIL(&symhead, sym, entry);
1663d2b2a2e3Sreyk 
1664f83005a6Sreyk 	hostapd_log(HOSTAPD_LOG_DEBUG, "%s = \"%s\"", sym->nam, sym->val);
1665d2b2a2e3Sreyk 
1666d2b2a2e3Sreyk 	return (0);
1667d2b2a2e3Sreyk }
1668d2b2a2e3Sreyk 
1669d2b2a2e3Sreyk int
hostapd_parse_symset(char * s)1670d2b2a2e3Sreyk hostapd_parse_symset(char *s)
1671d2b2a2e3Sreyk {
1672d2b2a2e3Sreyk 	char	*sym, *val;
1673d2b2a2e3Sreyk 	int	ret;
1674d2b2a2e3Sreyk 	size_t	len;
1675d2b2a2e3Sreyk 
1676d2b2a2e3Sreyk 	if ((val = strrchr(s, '=')) == NULL)
1677d2b2a2e3Sreyk 		return (-1);
1678d2b2a2e3Sreyk 
1679d2b2a2e3Sreyk 	len = strlen(s) - strlen(val) + 1;
168035de856eSderaadt 	if ((sym = malloc(len)) == NULL)
1681d2b2a2e3Sreyk 		hostapd_fatal("cmdline_symset: malloc");
1682d2b2a2e3Sreyk 
168375c9c663Sreyk 	(void)strlcpy(sym, s, len);
1684d2b2a2e3Sreyk 
1685d2b2a2e3Sreyk 	ret = symset(sym, val + 1, 1);
1686d2b2a2e3Sreyk 
1687d2b2a2e3Sreyk 	free(sym);
1688d2b2a2e3Sreyk 
1689d2b2a2e3Sreyk 	return (ret);
1690d2b2a2e3Sreyk }
1691d2b2a2e3Sreyk 
1692d2b2a2e3Sreyk char *
symget(const char * nam)1693d2b2a2e3Sreyk symget(const char *nam)
1694d2b2a2e3Sreyk {
1695d2b2a2e3Sreyk 	struct sym	*sym;
1696d2b2a2e3Sreyk 
169754c95b7aSkrw 	TAILQ_FOREACH(sym, &symhead, entry) {
1698d2b2a2e3Sreyk 		if (strcmp(nam, sym->nam) == 0) {
1699d2b2a2e3Sreyk 			sym->used = 1;
1700d2b2a2e3Sreyk 			return (sym->val);
1701d2b2a2e3Sreyk 		}
170254c95b7aSkrw 	}
1703d2b2a2e3Sreyk 	return (NULL);
1704d2b2a2e3Sreyk }
1705d2b2a2e3Sreyk 
170620741916Sderaadt int
check_file_secrecy(int fd,const char * fname)170720741916Sderaadt check_file_secrecy(int fd, const char *fname)
1708ab224e3bSreyk {
170920741916Sderaadt 	struct stat	st;
1710ab224e3bSreyk 
171120741916Sderaadt 	if (fstat(fd, &st)) {
171220741916Sderaadt 		warn("cannot stat %s", fname);
171320741916Sderaadt 		return (-1);
171420741916Sderaadt 	}
171520741916Sderaadt 	if (st.st_uid != 0 && st.st_uid != getuid()) {
171620741916Sderaadt 		warnx("%s: owner not root or current user", fname);
171720741916Sderaadt 		return (-1);
171820741916Sderaadt 	}
17197140c133Shenning 	if (st.st_mode & (S_IWGRP | S_IXGRP | S_IRWXO)) {
17207140c133Shenning 		warnx("%s: group writable or world read/writable", fname);
172120741916Sderaadt 		return (-1);
172220741916Sderaadt 	}
172320741916Sderaadt 	return (0);
172420741916Sderaadt }
172520741916Sderaadt 
172620741916Sderaadt struct file *
pushfile(const char * name,int secret)172720741916Sderaadt pushfile(const char *name, int secret)
172820741916Sderaadt {
172920741916Sderaadt 	struct file	*nfile;
173020741916Sderaadt 
17317fc93de0Stobias 	if ((nfile = calloc(1, sizeof(struct file))) == NULL) {
17326a3d55f9Skrw 		warn("%s", __func__);
1733ab224e3bSreyk 		return (NULL);
1734bbde4b2eSpyr 	}
17357fc93de0Stobias 	if ((nfile->name = strdup(name)) == NULL) {
17366a3d55f9Skrw 		warn("%s", __func__);
17377fc93de0Stobias 		free(nfile);
17387fc93de0Stobias 		return (NULL);
17397fc93de0Stobias 	}
174020741916Sderaadt 	if ((nfile->stream = fopen(nfile->name, "r")) == NULL) {
17416a3d55f9Skrw 		warn("%s: %s", __func__, nfile->name);
1742ab224e3bSreyk 		free(nfile->name);
1743ab224e3bSreyk 		free(nfile);
1744ab224e3bSreyk 		return (NULL);
174520741916Sderaadt 	} else if (secret &&
174620741916Sderaadt 	    check_file_secrecy(fileno(nfile->stream), nfile->name)) {
174720741916Sderaadt 		fclose(nfile->stream);
174820741916Sderaadt 		free(nfile->name);
174920741916Sderaadt 		free(nfile);
175020741916Sderaadt 		return (NULL);
175120741916Sderaadt 	}
17529a683e37Sdenis 	nfile->lineno = TAILQ_EMPTY(&files) ? 1 : 0;
17539a683e37Sdenis 	nfile->ungetsize = 16;
17549a683e37Sdenis 	nfile->ungetbuf = malloc(nfile->ungetsize);
17559a683e37Sdenis 	if (nfile->ungetbuf == NULL) {
17566a3d55f9Skrw 		warn("%s", __func__);
17579a683e37Sdenis 		fclose(nfile->stream);
17589a683e37Sdenis 		free(nfile->name);
17599a683e37Sdenis 		free(nfile);
17609a683e37Sdenis 		return (NULL);
17619a683e37Sdenis 	}
176220741916Sderaadt 	TAILQ_INSERT_TAIL(&files, nfile, entry);
176320741916Sderaadt 	return (nfile);
176420741916Sderaadt }
176520741916Sderaadt 
176620741916Sderaadt int
popfile(void)176720741916Sderaadt popfile(void)
176820741916Sderaadt {
176920741916Sderaadt 	struct file	*prev;
177020741916Sderaadt 
1771c6004ab9Smpf 	if ((prev = TAILQ_PREV(file, files, entry)) != NULL)
177220741916Sderaadt 		prev->errors += file->errors;
1773c6004ab9Smpf 
177420741916Sderaadt 	TAILQ_REMOVE(&files, file, entry);
177520741916Sderaadt 	fclose(file->stream);
177620741916Sderaadt 	free(file->name);
17779a683e37Sdenis 	free(file->ungetbuf);
177820741916Sderaadt 	free(file);
177920741916Sderaadt 	file = prev;
1780c6004ab9Smpf 	return (file ? 0 : EOF);
1781ab224e3bSreyk }
1782ab224e3bSreyk 
1783d2b2a2e3Sreyk int
hostapd_parse_file(struct hostapd_config * cfg)1784d2b2a2e3Sreyk hostapd_parse_file(struct hostapd_config *cfg)
1785d2b2a2e3Sreyk {
1786d2b2a2e3Sreyk 	struct sym *sym, *next;
178720741916Sderaadt 	int errors = 0;
1788d2b2a2e3Sreyk 	int ret;
1789d2b2a2e3Sreyk 
179020741916Sderaadt 	if ((file = pushfile(cfg->c_config, 1)) == NULL)
1791ab224e3bSreyk 		hostapd_fatal("failed to open the main config file: %s\n",
1792ab224e3bSreyk 		    cfg->c_config);
1793d6fc1e53Smpf 	topfile = file;
1794d2b2a2e3Sreyk 
179550ce650fSreyk 	/* Init tables and data structures */
1796d01b6ac2Sreyk 	TAILQ_INIT(&cfg->c_apmes);
179750ce650fSreyk 	TAILQ_INIT(&cfg->c_tables);
179850ce650fSreyk 	TAILQ_INIT(&cfg->c_frames);
17997c8c4753Sreyk 	cfg->c_iapp.i_multicast.sin_addr.s_addr = INADDR_ANY;
18003a61e91cSreyk 	cfg->c_iapp.i_flags = HOSTAPD_IAPP_F_DEFAULT;
180102fe3327Sreyk 	cfg->c_iapp.i_ttl = IP_DEFAULT_MULTICAST_TTL;
180201908219Sreyk 	cfg->c_apme_hopdelay.tv_sec = HOSTAPD_HOPPER_MDELAY / 1000;
180301908219Sreyk 	cfg->c_apme_hopdelay.tv_usec = (HOSTAPD_HOPPER_MDELAY % 1000) * 1000;
180450ce650fSreyk 
1805d2b2a2e3Sreyk 	ret = yyparse();
180620741916Sderaadt 	errors = file->errors;
180720741916Sderaadt 	popfile();
1808d2b2a2e3Sreyk 
1809d2b2a2e3Sreyk 	/* Free macros and check which have not been used. */
181046bca67bSkrw 	TAILQ_FOREACH_SAFE(sym, &symhead, entry, next) {
1811d2b2a2e3Sreyk 		if (!sym->used)
1812d2b2a2e3Sreyk 			hostapd_log(HOSTAPD_LOG_VERBOSE,
1813d5d66eaeSderaadt 			    "warning: macro '%s' not used", sym->nam);
1814d2b2a2e3Sreyk 		if (!sym->persist) {
1815d2b2a2e3Sreyk 			free(sym->nam);
1816d2b2a2e3Sreyk 			free(sym->val);
1817d2b2a2e3Sreyk 			TAILQ_REMOVE(&symhead, sym, entry);
1818d2b2a2e3Sreyk 			free(sym);
1819d2b2a2e3Sreyk 		}
1820d2b2a2e3Sreyk 	}
1821d2b2a2e3Sreyk 
182250ce650fSreyk 	return (errors ? EINVAL : ret);
1823d2b2a2e3Sreyk }
1824d2b2a2e3Sreyk 
1825d2b2a2e3Sreyk int
yyerror(const char * fmt,...)1826d2b2a2e3Sreyk yyerror(const char *fmt, ...)
1827d2b2a2e3Sreyk {
1828d2b2a2e3Sreyk 	va_list		 ap;
18292f94bf7fSbluhm 	char		*msg;
1830d2b2a2e3Sreyk 
183120741916Sderaadt 	file->errors++;
1832d2b2a2e3Sreyk 
1833d2b2a2e3Sreyk 	va_start(ap, fmt);
18342f94bf7fSbluhm 	if (vasprintf(&msg, fmt, ap) == -1)
18352f94bf7fSbluhm 		hostapd_fatal("yyerror vasprintf");
1836d2b2a2e3Sreyk 	va_end(ap);
18372f94bf7fSbluhm 	fprintf(stderr, "%s:%d: %s\n", file->name, yylval.lineno, msg);
18382f94bf7fSbluhm 	fflush(stderr);
18392f94bf7fSbluhm 	free(msg);
1840d2b2a2e3Sreyk 
1841d2b2a2e3Sreyk 	return (0);
1842d2b2a2e3Sreyk }
1843