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