1 /*-
2  * Copyright (c) 2011-2020 The NetBSD Foundation, Inc.
3  * All rights reserved.
4  *
5  * This code is derived from software contributed to The NetBSD Foundation
6  * by Martin Husemann, Christos Zoulas and Mindaugas Rasiukevicius.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
18  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
19  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
20  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
21  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
22  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
23  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
24  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
25  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
26  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27  * POSSIBILITY OF SUCH DAMAGE.
28  */
29 
30 %{
31 
32 #include <err.h>
33 #include <netdb.h>
34 #include <stdio.h>
35 #include <stdlib.h>
36 #include <string.h>
37 #ifdef __NetBSD__
38 #include <vis.h>
39 #endif
40 
41 #include "npfctl.h"
42 
43 #define   YYSTACKSIZE         4096
44 
45 int                           yystarttoken;
46 const char *                  yyfilename;
47 
48 extern int                    yylineno, yycolumn;
49 extern int                    yylex(int);
50 
51 void
yyerror(const char * fmt,...)52 yyerror(const char *fmt, ...)
53 {
54           extern int yyleng;
55           extern char *yytext;
56 
57           char *msg, *context = estrndup(yytext, yyleng);
58           bool eol = (*context == '\n');
59           va_list ap;
60 
61           va_start(ap, fmt);
62           vasprintf(&msg, fmt, ap);
63           va_end(ap);
64 
65           fprintf(stderr, "%s:%d:%d: %s", yyfilename,
66               yylineno - (int)eol, yycolumn, msg);
67           if (!eol) {
68 #ifdef __NetBSD__
69                     size_t len = strlen(context);
70                     char *dst = ecalloc(1, len * 4 + 1);
71 
72                     strvisx(dst, context, len, VIS_WHITE|VIS_CSTYLE);
73                     context = dst;
74 #endif
75                     fprintf(stderr, " near '%s'", context);
76           }
77           fprintf(stderr, "\n");
78           exit(EXIT_FAILURE);
79 }
80 
81 %}
82 
83 /*
84  * No conflicts allowed.  Keep it this way.
85  */
86 %expect 0
87 %expect-rr 0
88 
89 /*
90  * Depending on the mode of operation, set a different start symbol.
91  * Workaround yacc limitation by passing the start token.
92  */
93 %start input
94 %token RULE_ENTRY_TOKEN MAP_ENTRY_TOKEN
95 %lex-param { int yystarttoken }
96 
97 /*
98  * General tokens.
99  */
100 %token                        ALG
101 %token                        ALGO
102 %token                        ALL
103 %token                        ANY
104 %token                        APPLY
105 %token                        ARROWBOTH
106 %token                        ARROWLEFT
107 %token                        ARROWRIGHT
108 %token                        BLOCK
109 %token                        CDB
110 %token                        CONST
111 %token                        CURLY_CLOSE
112 %token                        CURLY_OPEN
113 %token                        CODE
114 %token                        COLON
115 %token                        COMMA
116 %token                        DEFAULT
117 %token                        TDYNAMIC
118 %token                        TSTATIC
119 %token                        EQ
120 %token                        EXCL_MARK
121 %token                        TFILE
122 %token                        FLAGS
123 %token                        FROM
124 %token                        GROUP
125 %token                        HASH
126 %token                        ICMPTYPE
127 %token                        ID
128 %token                        IFADDRS
129 %token                        IN
130 %token                        INET4
131 %token                        INET6
132 %token                        INTERFACE
133 %token                        INVALID
134 %token                        IPHASH
135 %token                        IPSET
136 %token                        LPM
137 %token                        MAP
138 %token                        NEWLINE
139 %token                        NO_PORTS
140 %token                        MINUS
141 %token                        NAME
142 %token                        NETMAP
143 %token                        NPT66
144 %token                        ON
145 %token                        OFF
146 %token                        OUT
147 %token                        PAR_CLOSE
148 %token                        PAR_OPEN
149 %token                        PASS
150 %token                        PCAP_FILTER
151 %token                        PORT
152 %token                        PROCEDURE
153 %token                        PROTO
154 %token                        FAMILY
155 %token                        FINAL
156 %token                        FORW
157 %token                        RETURN
158 %token                        RETURNICMP
159 %token                        RETURNRST
160 %token                        ROUNDROBIN
161 %token                        RULESET
162 %token                        SEMICOLON
163 %token                        SET
164 %token                        SLASH
165 %token                        STATEFUL
166 %token                        STATEFUL_ALL
167 %token                        TABLE
168 %token                        TCP
169 %token                        TO
170 %token                        TREE
171 %token                        TYPE
172 %token    <num>               ICMP
173 %token    <num>               ICMP6
174 
175 %token    <num>               HEX
176 %token    <str>               IDENTIFIER
177 %token    <str>               IPV4ADDR
178 %token    <str>               IPV6ADDR
179 %token    <num>               NUM
180 %token    <fpnum>             FPNUM
181 %token    <str>               STRING
182 %token    <str>               PARAM
183 %token    <str>               TABLE_ID
184 %token    <str>               VAR_ID
185 
186 %type     <str>               addr some_name table_store dynamic_ifaddrs
187 %type     <str>               proc_param_val opt_apply ifname on_ifname ifref
188 %type     <num>               port opt_final number afamily opt_family
189 %type     <num>               block_or_pass rule_dir group_dir block_opts
190 %type     <num>               maybe_not opt_stateful icmp_type table_type
191 %type     <num>               map_sd map_algo map_flags map_type
192 %type     <num>               param_val
193 %type     <var>               static_ifaddrs filt_addr_element
194 %type     <var>               filt_port filt_port_list port_range icmp_type_and_code
195 %type     <var>               filt_addr addr_and_mask tcp_flags tcp_flags_and_mask
196 %type     <var>               procs proc_call proc_param_list proc_param
197 %type     <var>               element list_elems list_trail list value filt_addr_list
198 %type     <var>               opt_proto proto proto_elems
199 %type     <addrport>          mapseg
200 %type     <filtopts>          filt_opts all_or_filt_opts
201 %type     <optproto>          rawproto
202 %type     <rulegroup>         group_opts
203 
204 %union {
205           char *              str;
206           unsigned long       num;
207           double              fpnum;
208           npfvar_t *          var;
209           addr_port_t         addrport;
210           filt_opts_t         filtopts;
211           opt_proto_t         optproto;
212           rule_group_t        rulegroup;
213 }
214 
215 %%
216 
217 input
218           : lines
219           | RULE_ENTRY_TOKEN  rule
220           | MAP_ENTRY_TOKEN   map
221           ;
222 
223 lines
224           : lines sepline line
225           | line
226           ;
227 
228 line
229           : vardef
230           | table
231           | map
232           | group
233           | rproc
234           | alg
235           | set
236           |
237           ;
238 
239 alg
240           : ALG STRING
241           {
242                     npfctl_build_alg($2);
243           }
244           ;
245 
246 sepline
247           : NEWLINE
248           | SEMICOLON
249           ;
250 
251 param_val
252           : number  { $$ = $1; }
253           | ON                { $$ = true; }
254           | OFF               { $$ = false; }
255           ;
256 
257 set
258           : SET PARAM param_val {
259                     npfctl_setparam($2, $3);
260           }
261           ;
262 
263 /*
264  * A value - an element or a list of elements.
265  * Can be assigned to a variable or used inline.
266  */
267 
268 vardef
269           : VAR_ID EQ value
270           {
271                     npfvar_add($3, $1);
272           }
273           ;
274 
275 value
276           : element
277           | list
278           ;
279 
280 list
281           : CURLY_OPEN opt_nl list_elems CURLY_CLOSE
282           {
283                     $$ = $3;
284           }
285           ;
286 
287 list_elems
288           : element list_trail
289           {
290                     $$ = npfvar_add_elements($1, $2);
291           }
292           ;
293 
294 element
295           : IDENTIFIER
296           {
297                     $$ = npfvar_create_from_string(NPFVAR_IDENTIFIER, $1);
298           }
299           | STRING
300           {
301                     $$ = npfvar_create_from_string(NPFVAR_STRING, $1);
302           }
303           | number MINUS number
304           {
305                     $$ = npfctl_parse_port_range($1, $3);
306           }
307           | number
308           {
309                     $$ = npfvar_create_element(NPFVAR_NUM, &$1, sizeof($1));
310           }
311           | VAR_ID
312           {
313                     $$ = npfvar_create_from_string(NPFVAR_VAR_ID, $1);
314           }
315           | TABLE_ID                    { $$ = npfctl_parse_table_id($1); }
316           | dynamic_ifaddrs   { $$ = npfctl_ifnet_table($1); }
317           | static_ifaddrs    { $$ = $1; }
318           | addr_and_mask               { $$ = $1; }
319           ;
320 
321 list_trail
322           : element_sep element list_trail
323           {
324                     $$ = npfvar_add_elements($2, $3);
325           }
326           | opt_nl            { $$ = NULL; }
327           | element_sep                 { $$ = NULL; }
328           ;
329 
330 element_sep
331           : opt_nl COMMA opt_nl
332           ;
333 
334 opt_nl
335           : opt_nl NEWLINE
336           |
337           ;
338 
339 /*
340  * Table definition.
341  */
342 
343 table
344           : TABLE TABLE_ID TYPE table_type table_store
345           {
346                     npfctl_build_table($2, $4, $5);
347           }
348           ;
349 
350 table_type
351           : IPSET             { $$ = NPF_TABLE_IPSET; }
352           | HASH
353           {
354                     warnx("warning - table type \"hash\" is deprecated and may be "
355                         "deleted in\nthe future; please use the \"ipset\" type "
356                         "instead.");
357                     $$ = NPF_TABLE_IPSET;
358           }
359           | LPM               { $$ = NPF_TABLE_LPM; }
360           | TREE
361           {
362                     warnx("warning - table type \"tree\" is deprecated and may be "
363                         "deleted in\nthe future; please use the \"lpm\" type "
364                         "instead.");
365                     $$ = NPF_TABLE_LPM;
366           }
367           | CONST             { $$ = NPF_TABLE_CONST; }
368           | CDB
369           {
370                     warnx("warning -- table type \"cdb\" is deprecated and may be "
371                         "deleted in\nthe future; please use the \"const\" type "
372                         "instead.");
373                     $$ = NPF_TABLE_CONST;
374           }
375           ;
376 
377 table_store
378           : TFILE STRING      { $$ = $2; }
379           | TDYNAMIC
380           {
381                     warnx("warning - the \"dynamic\" keyword for tables is obsolete");
382                     $$ = NULL;
383           }
384           |                   { $$ = NULL; }
385           ;
386 
387 /*
388  * Map definition.
389  */
390 
391 map_sd
392           : TSTATIC { $$ = NPFCTL_NAT_STATIC; }
393           | TDYNAMIC          { $$ = NPFCTL_NAT_DYNAMIC; }
394           |                   { $$ = NPFCTL_NAT_DYNAMIC; }
395           ;
396 
397 map_algo
398           : ALGO NETMAP                 { $$ = NPF_ALGO_NETMAP; }
399           | ALGO IPHASH                 { $$ = NPF_ALGO_IPHASH; }
400           | ALGO ROUNDROBIN   { $$ = NPF_ALGO_RR; }
401           | ALGO NPT66                  { $$ = NPF_ALGO_NPT66; }
402           |                             { $$ = 0; }
403           ;
404 
405 map_flags
406           : NO_PORTS          { $$ = NPF_NAT_PORTS; }
407           |                   { $$ = 0; }
408           ;
409 
410 map_type
411           : ARROWBOTH         { $$ = NPF_NATIN | NPF_NATOUT; }
412           | ARROWLEFT         { $$ = NPF_NATIN; }
413           | ARROWRIGHT        { $$ = NPF_NATOUT; }
414           ;
415 
416 mapseg
417           : filt_addr filt_port
418           {
419                     $$.ap_netaddr = $1;
420                     $$.ap_portrange = $2;
421           }
422           ;
423 
424 map
425           : MAP ifref map_sd map_algo map_flags mapseg map_type mapseg
426             PASS opt_family opt_proto all_or_filt_opts
427           {
428                     npfctl_build_natseg($3, $7, $5, $2, &$6, &$8, $11, &$12, $4);
429           }
430           | MAP ifref map_sd map_algo map_flags mapseg map_type mapseg
431           {
432                     npfctl_build_natseg($3, $7, $5, $2, &$6, &$8, NULL, NULL, $4);
433           }
434           | MAP ifref map_sd map_algo map_flags proto mapseg map_type mapseg
435           {
436                     npfctl_build_natseg($3, $8, $5, $2, &$7, &$9, $6, NULL, $4);
437           }
438           | MAP RULESET group_opts
439           {
440                     npfctl_build_maprset($3.rg_name, $3.rg_attr, $3.rg_ifname);
441           }
442           ;
443 
444 /*
445  * Rule procedure definition and its parameters.
446  */
447 
448 rproc
449           : PROCEDURE STRING CURLY_OPEN procs CURLY_CLOSE
450           {
451                     npfctl_build_rproc($2, $4);
452           }
453           ;
454 
455 procs
456           : procs sepline proc_call
457           {
458                     $$ = npfvar_add_elements($1, $3);
459           }
460           | proc_call         { $$ = $1; }
461           ;
462 
463 proc_call
464           : IDENTIFIER COLON proc_param_list
465           {
466                     proc_call_t pc;
467 
468                     pc.pc_name = estrdup($1);
469                     pc.pc_opts = $3;
470 
471                     $$ = npfvar_create_element(NPFVAR_PROC, &pc, sizeof(pc));
472           }
473           |                   { $$ = NULL; }
474           ;
475 
476 proc_param_list
477           : proc_param_list COMMA proc_param
478           {
479                     $$ = npfvar_add_elements($1, $3);
480           }
481           | proc_param        { $$ = $1; }
482           |                   { $$ = NULL; }
483           ;
484 
485 proc_param
486           : some_name proc_param_val
487           {
488                     proc_param_t pp;
489 
490                     pp.pp_param = estrdup($1);
491                     pp.pp_value = $2 ? estrdup($2) : NULL;
492 
493                     $$ = npfvar_create_element(NPFVAR_PROC_PARAM, &pp, sizeof(pp));
494           }
495           ;
496 
497 proc_param_val
498           : some_name         { $$ = $1; }
499           | number  { (void)asprintf(&$$, "%ld", $1); }
500           | FPNUM             { (void)asprintf(&$$, "%lf", $1); }
501           |                   { $$ = NULL; }
502           ;
503 
504 /*
505  * Group and dynamic ruleset definition.
506  */
507 
508 group
509           : GROUP group_opts
510           {
511                     /* Build a group.  Increase the nesting level. */
512                     npfctl_build_group($2.rg_name, $2.rg_attr,
513                         $2.rg_ifname, $2.rg_default);
514           }
515             ruleset_block
516           {
517                     /* Decrease the nesting level. */
518                     npfctl_build_group_end();
519           }
520           ;
521 
522 ruleset
523           : RULESET group_opts
524           {
525                     /* Ruleset is a dynamic group. */
526                     npfctl_build_group($2.rg_name, $2.rg_attr | NPF_RULE_DYNAMIC,
527                         $2.rg_ifname, $2.rg_default);
528                     npfctl_build_group_end();
529           }
530           ;
531 
532 group_dir
533           : FORW              { $$ = NPF_RULE_FORW; }
534           | rule_dir
535           ;
536 
537 group_opts
538           : DEFAULT
539           {
540                     memset(&$$, 0, sizeof(rule_group_t));
541                     $$.rg_default = true;
542           }
543           | STRING group_dir on_ifname
544           {
545                     memset(&$$, 0, sizeof(rule_group_t));
546                     $$.rg_name = $1;
547                     $$.rg_attr = $2;
548                     $$.rg_ifname = $3;
549           }
550           ;
551 
552 ruleset_block
553           : CURLY_OPEN ruleset_def CURLY_CLOSE
554           ;
555 
556 ruleset_def
557           : ruleset_def sepline rule_group
558           | rule_group
559           ;
560 
561 rule_group
562           : rule
563           | group
564           | ruleset
565           |
566           ;
567 
568 /*
569  * Rule and misc.
570  */
571 
572 rule
573           : block_or_pass opt_stateful rule_dir opt_final on_ifname
574             opt_family opt_proto all_or_filt_opts opt_apply
575           {
576                     npfctl_build_rule($1 | $2 | $3 | $4, $5,
577                         $6, $7, &$8, NULL, $9);
578           }
579           | block_or_pass opt_stateful rule_dir opt_final on_ifname
580             PCAP_FILTER STRING opt_apply
581           {
582                     npfctl_build_rule($1 | $2 | $3 | $4, $5,
583                         AF_UNSPEC, NULL, NULL, $7, $8);
584           }
585           ;
586 
587 block_or_pass
588           : BLOCK block_opts  { $$ = $2; }
589           | PASS                        { $$ = NPF_RULE_PASS; }
590           ;
591 
592 rule_dir
593           : IN                          { $$ = NPF_RULE_IN; }
594           | OUT                         { $$ = NPF_RULE_OUT; }
595           |                             { $$ = NPF_RULE_IN | NPF_RULE_OUT; }
596           ;
597 
598 opt_final
599           : FINAL                       { $$ = NPF_RULE_FINAL; }
600           |                             { $$ = 0; }
601           ;
602 
603 on_ifname
604           : ON ifref                    { $$ = $2; }
605           |                             { $$ = NULL; }
606           ;
607 
608 afamily
609           : INET4                       { $$ = AF_INET; }
610           | INET6                       { $$ = AF_INET6; }
611           ;
612 
613 maybe_not
614           : EXCL_MARK                   { $$ = true; }
615           |                             { $$ = false; }
616           ;
617 
618 opt_family
619           : FAMILY afamily    { $$ = $2; }
620           |                             { $$ = AF_UNSPEC; }
621           ;
622 
623 rawproto
624           : TCP tcp_flags_and_mask
625           {
626                     $$.op_proto = IPPROTO_TCP;
627                     $$.op_opts = $2;
628           }
629           | ICMP icmp_type_and_code
630           {
631                     $$.op_proto = IPPROTO_ICMP;
632                     $$.op_opts = $2;
633           }
634           | ICMP6 icmp_type_and_code
635           {
636                     $$.op_proto = IPPROTO_ICMPV6;
637                     $$.op_opts = $2;
638           }
639           | some_name
640           {
641                     $$.op_proto = npfctl_protono($1);
642                     $$.op_opts = NULL;
643           }
644           | number
645           {
646                     $$.op_proto = $1;
647                     $$.op_opts = NULL;
648           }
649           ;
650 
651 proto_elems
652           : proto_elems COMMA rawproto
653           {
654                     npfvar_t *pvar = npfvar_create_element(
655                         NPFVAR_PROTO, &$3, sizeof($3));
656                     $$ = npfvar_add_elements($1, pvar);
657           }
658           | rawproto
659           {
660                     $$ = npfvar_create_element(NPFVAR_PROTO, &$1, sizeof($1));
661           }
662           ;
663 
664 proto
665           : PROTO rawproto
666           {
667                     $$ = npfvar_create_element(NPFVAR_PROTO, &$2, sizeof($2));
668           }
669           | PROTO CURLY_OPEN proto_elems CURLY_CLOSE
670           {
671                     $$ = $3;
672           }
673           ;
674 
675 opt_proto
676           : proto                       { $$ = $1; }
677           |                             { $$ = NULL; }
678           ;
679 
680 all_or_filt_opts
681           : ALL
682           {
683                     $$.fo_finvert = false;
684                     $$.fo_from.ap_netaddr = NULL;
685                     $$.fo_from.ap_portrange = NULL;
686                     $$.fo_tinvert = false;
687                     $$.fo_to.ap_netaddr = NULL;
688                     $$.fo_to.ap_portrange = NULL;
689           }
690           | filt_opts         { $$ = $1; }
691           ;
692 
693 opt_stateful
694           : STATEFUL          { $$ = NPF_RULE_STATEFUL; }
695           | STATEFUL_ALL      { $$ = NPF_RULE_STATEFUL | NPF_RULE_GSTATEFUL; }
696           |                   { $$ = 0; }
697           ;
698 
699 opt_apply
700           : APPLY STRING      { $$ = $2; }
701           |                   { $$ = NULL; }
702           ;
703 
704 block_opts
705           : RETURNRST         { $$ = NPF_RULE_RETRST; }
706           | RETURNICMP        { $$ = NPF_RULE_RETICMP; }
707           | RETURN  { $$ = NPF_RULE_RETRST | NPF_RULE_RETICMP; }
708           |                   { $$ = 0; }
709           ;
710 
711 filt_opts
712           : FROM maybe_not filt_addr filt_port TO maybe_not filt_addr filt_port
713           {
714                     $$.fo_finvert = $2;
715                     $$.fo_from.ap_netaddr = $3;
716                     $$.fo_from.ap_portrange = $4;
717                     $$.fo_tinvert = $6;
718                     $$.fo_to.ap_netaddr = $7;
719                     $$.fo_to.ap_portrange = $8;
720           }
721           | FROM maybe_not filt_addr filt_port
722           {
723                     $$.fo_finvert = $2;
724                     $$.fo_from.ap_netaddr = $3;
725                     $$.fo_from.ap_portrange = $4;
726                     $$.fo_tinvert = false;
727                     $$.fo_to.ap_netaddr = NULL;
728                     $$.fo_to.ap_portrange = NULL;
729           }
730           | TO maybe_not filt_addr filt_port
731           {
732                     $$.fo_finvert = false;
733                     $$.fo_from.ap_netaddr = NULL;
734                     $$.fo_from.ap_portrange = NULL;
735                     $$.fo_tinvert = $2;
736                     $$.fo_to.ap_netaddr = $3;
737                     $$.fo_to.ap_portrange = $4;
738           }
739           ;
740 
741 filt_addr_list
742           : filt_addr_list COMMA filt_addr_element
743           {
744                     npfvar_add_elements($1, $3);
745           }
746           | filt_addr_element
747           ;
748 
749 filt_addr
750           : CURLY_OPEN filt_addr_list CURLY_CLOSE
751           {
752                     $$ = $2;
753           }
754           | filt_addr_element { $$ = $1; }
755           | ANY                         { $$ = NULL; }
756           ;
757 
758 addr_and_mask
759           : addr SLASH number
760           {
761                     $$ = npfctl_parse_fam_addr_mask($1, NULL, &$3);
762           }
763           | addr SLASH addr
764           {
765                     $$ = npfctl_parse_fam_addr_mask($1, $3, NULL);
766           }
767           | addr
768           {
769                     $$ = npfctl_parse_fam_addr_mask($1, NULL, NULL);
770           }
771           ;
772 
773 filt_addr_element
774           : addr_and_mask               { assert($1 != NULL); $$ = $1; }
775           | static_ifaddrs
776           {
777                     if (npfvar_get_count($1) != 1)
778                               yyerror("multiple interfaces are not supported");
779                     ifnet_addr_t *ifna = npfvar_get_data($1, NPFVAR_INTERFACE, 0);
780                     $$ = ifna->ifna_addrs;
781           }
782           | dynamic_ifaddrs   { $$ = npfctl_ifnet_table($1); }
783           | TABLE_ID                    { $$ = npfctl_parse_table_id($1); }
784           | VAR_ID
785           {
786                     npfvar_t *vp = npfvar_lookup($1);
787                     int type = npfvar_get_type(vp, 0);
788                     ifnet_addr_t *ifna;
789 again:
790                     switch (type) {
791                     case NPFVAR_IDENTIFIER:
792                     case NPFVAR_STRING:
793                               vp = npfctl_parse_ifnet(npfvar_expand_string(vp),
794                                   AF_UNSPEC);
795                               type = npfvar_get_type(vp, 0);
796                               goto again;
797                     case NPFVAR_FAM:
798                     case NPFVAR_TABLE:
799                               $$ = vp;
800                               break;
801                     case NPFVAR_INTERFACE:
802                               $$ = NULL;
803                               for (u_int i = 0; i < npfvar_get_count(vp); i++) {
804                                         ifna = npfvar_get_data(vp, type, i);
805                                         $$ = npfvar_add_elements($$, ifna->ifna_addrs);
806                               }
807                               break;
808                     case -1:
809                               yyerror("undefined variable '%s'", $1);
810                               break;
811                     default:
812                               yyerror("wrong variable '%s' type '%s' for address "
813                                   "or interface", $1, npfvar_type(type));
814                               break;
815                     }
816           }
817           ;
818 
819 addr
820           : IPV4ADDR          { $$ = $1; }
821           | IPV6ADDR          { $$ = $1; }
822           ;
823 
824 filt_port
825           : PORT CURLY_OPEN filt_port_list CURLY_CLOSE
826           {
827                     $$ = npfctl_parse_port_range_variable(NULL, $3);
828           }
829           | PORT port_range   { $$ = $2; }
830           |                             { $$ = NULL; }
831           ;
832 
833 filt_port_list
834           : filt_port_list COMMA port_range
835           {
836                     npfvar_add_elements($1, $3);
837           }
838           | port_range
839           ;
840 
841 port_range
842           : port              /* just port */
843           {
844                     $$ = npfctl_parse_port_range($1, $1);
845           }
846           | port MINUS port   /* port from-to */
847           {
848                     $$ = npfctl_parse_port_range($1, $3);
849           }
850           | VAR_ID
851           {
852                     npfvar_t *vp;
853                     if ((vp = npfvar_lookup($1)) == NULL)
854                               yyerror("undefined port variable %s", $1);
855                     $$ = npfctl_parse_port_range_variable($1, vp);
856           }
857           ;
858 
859 port
860           : number  { $$ = $1; }
861           | IDENTIFIER        { $$ = npfctl_portno($1); }
862           | STRING  { $$ = npfctl_portno($1); }
863           ;
864 
865 icmp_type_and_code
866           : ICMPTYPE icmp_type
867           {
868                     $$ = npfctl_parse_icmp($<num>0, $2, -1);
869           }
870           | ICMPTYPE icmp_type CODE number
871           {
872                     $$ = npfctl_parse_icmp($<num>0, $2, $4);
873           }
874           | ICMPTYPE icmp_type CODE IDENTIFIER
875           {
876                     $$ = npfctl_parse_icmp($<num>0, $2,
877                         npfctl_icmpcode($<num>0, $2, $4));
878           }
879           | ICMPTYPE icmp_type CODE VAR_ID
880           {
881                     char *s = npfvar_expand_string(npfvar_lookup($4));
882                     $$ = npfctl_parse_icmp($<num>0, $2,
883                         npfctl_icmpcode($<num>0, $2, s));
884           }
885           |                   { $$ = NULL; }
886           ;
887 
888 tcp_flags_and_mask
889           : FLAGS tcp_flags SLASH tcp_flags
890           {
891                     npfvar_add_elements($2, $4);
892                     $$ = $2;
893           }
894           | FLAGS tcp_flags
895           {
896                     if (npfvar_get_count($2) != 1)
897                               yyerror("multiple tcpflags are not supported");
898                     char *s = npfvar_get_data($2, NPFVAR_TCPFLAG, 0);
899                     npfvar_add_elements($2, npfctl_parse_tcpflag(s));
900                     $$ = $2;
901           }
902           |                   { $$ = NULL; }
903           ;
904 
905 tcp_flags
906           : IDENTIFIER        { $$ = npfctl_parse_tcpflag($1); }
907           ;
908 
909 icmp_type
910           : number  { $$ = $1; }
911           | IDENTIFIER        { $$ = npfctl_icmptype($<num>-1, $1); }
912           | VAR_ID
913           {
914                     char *s = npfvar_expand_string(npfvar_lookup($1));
915                     $$ = npfctl_icmptype($<num>-1, s);
916           }
917           ;
918 
919 ifname
920           : some_name
921           {
922                     npfctl_note_interface($1);
923                     $$ = $1;
924           }
925           | VAR_ID
926           {
927                     npfvar_t *vp = npfvar_lookup($1);
928                     const int type = npfvar_get_type(vp, 0);
929                     ifnet_addr_t *ifna;
930                     const char *name;
931                     unsigned *tid;
932                     bool ifaddr;
933 
934                     switch (type) {
935                     case NPFVAR_STRING:
936                     case NPFVAR_IDENTIFIER:
937                               $$ = npfvar_expand_string(vp);
938                               break;
939                     case NPFVAR_INTERFACE:
940                               if (npfvar_get_count(vp) != 1)
941                                         yyerror(
942                                             "multiple interfaces are not supported");
943                               ifna = npfvar_get_data(vp, type, 0);
944                               $$ = ifna->ifna_name;
945                               break;
946                     case NPFVAR_TABLE:
947                               tid = npfvar_get_data(vp, type, 0);
948                               name = npfctl_table_getname(npfctl_config_ref(),
949                                   *tid, &ifaddr);
950                               if (!ifaddr) {
951                                         yyerror("variable '%s' references a table "
952                                             "%s instead of an interface", $1, name);
953                               }
954                               $$ = estrdup(name);
955                               break;
956                     case -1:
957                               yyerror("undefined variable '%s' for interface", $1);
958                               break;
959                     default:
960                               yyerror("wrong variable '%s' type '%s' for interface",
961                                   $1, npfvar_type(type));
962                               break;
963                     }
964                     npfctl_note_interface($$);
965           }
966           ;
967 
968 static_ifaddrs
969           : afamily PAR_OPEN ifname PAR_CLOSE
970           {
971                     $$ = npfctl_parse_ifnet($3, $1);
972           }
973           ;
974 
975 dynamic_ifaddrs
976           : IFADDRS PAR_OPEN ifname PAR_CLOSE
977           {
978                     $$ = $3;
979           }
980           ;
981 
982 ifref
983           : ifname
984           | dynamic_ifaddrs
985           | static_ifaddrs
986           {
987                     ifnet_addr_t *ifna;
988 
989                     if (npfvar_get_count($1) != 1) {
990                               yyerror("multiple interfaces are not supported");
991                     }
992                     ifna = npfvar_get_data($1, NPFVAR_INTERFACE, 0);
993                     npfctl_note_interface(ifna->ifna_name);
994                     $$ = ifna->ifna_name;
995           }
996           ;
997 
998 number
999           : HEX               { $$ = $1; }
1000           | NUM               { $$ = $1; }
1001           ;
1002 
1003 some_name
1004           : IDENTIFIER        { $$ = $1; }
1005           | STRING  { $$ = $1; }
1006           ;
1007 
1008 %%
1009