parse.y revision 1.22
1/*	$OpenBSD: parse.y,v 1.22 2001/08/23 05:36:53 millert Exp $	*/
2
3/*
4 * Copyright (c) 2001 Markus Friedl.  All rights reserved.
5 * Copyright (c) 2001 Daniel Hartmeier.  All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 *    notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 *    notice, this list of conditions and the following disclaimer in the
14 *    documentation and/or other materials provided with the distribution.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
17 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
18 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
19 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
20 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
21 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
22 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
23 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
25 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
27%{
28#include <sys/types.h>
29#include <sys/socket.h>
30#include <sys/ioctl.h>
31#include <net/if.h>
32#include <netinet/in.h>
33#include <netinet/in_systm.h>
34#include <netinet/ip.h>
35#include <netinet/ip_icmp.h>
36#include <net/pfvar.h>
37#include <arpa/inet.h>
38
39#include <stdio.h>
40#include <netdb.h>
41#include <stdarg.h>
42#include <errno.h>
43#include <string.h>
44#include <ctype.h>
45#include <err.h>
46
47#include "pfctl_parser.h"
48
49static struct pfctl *pf = NULL;
50static FILE *fin = NULL;
51static int debug = 0;
52static int lineno = 1;
53static int errors = 0;
54static int natmode = 0;
55
56struct node_proto {
57	u_int8_t		 proto;
58	struct node_proto	*next;
59};
60
61struct node_host {
62	u_int32_t		 addr;
63	u_int32_t		 mask;
64	u_int8_t		 not;
65	struct node_host	*next;
66};
67
68struct node_port {
69	u_int16_t		 port[2];
70	u_int8_t		 op;
71	struct node_port	*next;
72};
73
74struct peer {
75	struct node_host	*host;
76	struct node_port	*port;
77};
78
79int			 rule_consistent(struct pf_rule *);
80int			 yyparse(void);
81struct pf_rule_addr	*new_addr(void);
82u_int32_t		 ipmask(u_int8_t);
83void			 expand_rule(struct pf_rule *, struct node_proto *,
84			    struct node_host *, struct node_port *,
85			    struct node_host *, struct node_port *);
86
87struct sym {
88	struct sym *next;
89	char *nam;
90	char *val;
91};
92struct sym *symhead = NULL;
93
94int	symset(char *name, char *val);
95char *	symget(char *name);
96
97typedef struct {
98	union {
99		u_int32_t		number;
100		int			i;
101		char			*string;
102		struct {
103			char		*string;
104			int		not;
105		}			iface;
106		struct {
107			u_int8_t	b1;
108			u_int8_t	b2;
109			u_int16_t	w;
110		}			b;
111		struct {
112			int		a;
113			int		b;
114			int		t;
115		}			range;
116		struct node_proto	*proto;
117		struct node_host	*host;
118		struct node_port	*port;
119		struct peer		peer;
120		struct {
121			struct peer	src, dst;
122		}			fromto;
123	} v;
124	int lineno;
125} YYSTYPE;
126
127%}
128
129%token	PASS BLOCK SCRUB RETURN IN OUT LOG LOGALL QUICK ON FROM TO FLAGS
130%token	RETURNRST RETURNICMP PROTO ALL ANY ICMPTYPE CODE KEEP STATE PORT
131%token	RDR NAT ARROW NODF MINTTL ERROR
132%token	<v.string> STRING
133%token	<v.number> NUMBER
134%token	<v.i>	PORTUNARY PORTBINARY
135%type	<v.iface> iface natiface
136%type	<v.number> port icmptype minttl
137%type	<v.i>	dir log quick keep nodf
138%type	<v.b>	action icmpspec flag flags blockspec
139%type	<v.range>	dport rport
140%type	<v.proto>	proto proto_list proto_item
141%type	<v.fromto>	fromto
142%type	<v.peer>	ipportspec
143%type	<v.host>	ipspec host address host_list
144%type	<v.port>	portspec port_list port_item
145%%
146
147ruleset		: /* empty */
148		| ruleset '\n'
149		| ruleset pfrule '\n'
150		| ruleset natrule '\n'
151		| ruleset rdrrule '\n'
152		| ruleset varset '\n'
153		| ruleset error '\n'		{ errors++; }
154		;
155
156varset		: STRING PORTUNARY STRING
157		{
158			printf("%s = %s\n", $1, $3);
159			if (symset($1, $3) == -1) {
160				yyerror("cannot store variable %s", $1);
161				YYERROR;
162			}
163		}
164		;
165
166pfrule		: action dir log quick iface proto fromto flags icmpspec keep nodf minttl
167		{
168			struct pf_rule r;
169
170			if (natmode) {
171				yyerror("filter rule not permitted in nat mode");
172				YYERROR;
173			}
174			memset(&r, 0, sizeof(r));
175
176			r.action = $1.b1;
177			if ($1.b2)
178				r.rule_flag |= PFRULE_RETURNRST;
179			else
180				r.return_icmp = $1.w;
181			r.direction = $2;
182			r.log = $3;
183			r.quick = $4;
184			if ($5.string)
185				memcpy(r.ifname, $5.string, sizeof(r.ifname));
186
187			r.flags = $8.b1;
188			r.flagset = $8.b2;
189			r.type = $9.b1;
190			r.code = $9.b2;
191			r.keep_state = $10;
192
193			if ($11)
194				r.rule_flag |= PFRULE_NODF;
195			if ($12)
196				r.min_ttl = $12;
197
198			expand_rule(&r, $6, $7.src.host, $7.src.port,
199			    $7.dst.host, $7.dst.port);
200		}
201		;
202
203action		: PASS			{ $$.b1 = PF_PASS; }
204		| BLOCK blockspec	{ $$ = $2; $$.b1 = PF_DROP; }
205		| SCRUB			{ $$.b1 = PF_SCRUB; }
206		;
207
208blockspec	: /* empty */		{ $$.b2 = 0; $$.w = 0; }
209		| RETURNRST		{ $$.b2 = 1; $$.w = 0;}
210		| RETURNICMP		{
211			$$.b2 = 0;
212			$$.w = (ICMP_UNREACH << 8) | ICMP_UNREACH_PORT;
213		}
214		| RETURNICMP '(' STRING ')'	{
215			struct icmpcodeent *p;
216
217			if ((p = geticmpcodebyname(ICMP_UNREACH, $3)) == NULL) {
218				yyerror("unknown icmp code %s", $3);
219				YYERROR;
220			}
221			$$.w = (p->type << 8) | p->code;
222			$$.b2 = 0;
223		}
224		;
225
226dir		: IN			{ $$ = PF_IN; }
227		| OUT				{ $$ = PF_OUT; }
228		;
229
230log		: /* empty */			{ $$ = 0; }
231		| LOG				{ $$ = 1; }
232		| LOGALL			{ $$ = 2; }
233		;
234
235quick		: /* empty */			{ $$ = 0; }
236		| QUICK				{ $$ = 1; }
237		;
238
239natiface	: iface
240		| ON '!' STRING			{
241			$$.string = strdup($3); $$.not = 1;
242		}
243		;
244iface		: /* empty */			{ $$.string = NULL; }
245		| ON STRING			{ $$.string = strdup($2); }
246		;
247
248proto		: /* empty */			{ $$ = NULL; }
249		| PROTO proto_item		{ $$ = $2; }
250		| PROTO '{' proto_list '}'	{ $$ = $3; }
251		;
252
253proto_list	: proto_item			{ $$ = $1; }
254		| proto_list ',' proto_item	{ $3->next = $1; $$ = $3; }
255		;
256
257proto_item	: NUMBER			{
258			struct protoent *p;
259
260			if ((p = getprotobynumber($1)) == NULL) {
261				yyerror("unknown protocol %d", $1);
262				YYERROR;
263			}
264			$$ = malloc(sizeof(struct node_proto));
265			$$->proto = p->p_proto;
266			$$->next = NULL;
267		}
268		| STRING			{
269			struct protoent *p;
270
271			if ((p = getprotobyname($1)) == NULL) {
272				yyerror("unknown protocol %s", $1);
273				YYERROR;
274			}
275			$$ = malloc(sizeof(struct node_proto));
276			$$->proto = p->p_proto;
277			$$->next = NULL;
278		}
279		;
280
281fromto		: ALL				{
282			$$.src.host = NULL;
283			$$.src.port = NULL;
284			$$.dst.host = NULL;
285			$$.dst.port = NULL;
286		}
287		| FROM ipportspec TO ipportspec	{
288			$$.src = $2;
289			$$.dst = $4;
290		}
291		;
292
293ipportspec	: ipspec			{ $$.host = $1; $$.port = NULL; }
294		| ipspec PORT portspec		{
295			$$.host = $1;
296			$$.port = $3;
297		}
298		;
299
300ipspec		: ANY				{ $$ = NULL; }
301		| '!' host			{ $$ = $2; $$->not = 1; }
302		| host				{ $$ = $1; $$->not = 0; }
303		| '{' host_list '}'		{ $$ = $2; }
304		;
305
306host_list	: host				{ $$ = $1; }
307		| host_list ',' host		{ $3->next = $1; $$ = $3; }
308
309host		: address			{
310			$$ = $1;
311			$$->mask = 0xffffffff;
312		}
313		| address '/' NUMBER		{
314			if ($3 < 0 || $3 > 32) {
315				yyerror("illegal netmask value %d", $3);
316				YYERROR;
317			}
318			$$ = $1;
319			$$->mask = ipmask($3);
320		}
321		;
322
323address		: STRING			{
324			struct hostent *hp;
325
326			if ((hp = gethostbyname($1)) == NULL) {
327				yyerror("cannot resolve %s", $1);
328				YYERROR;
329			}
330			$$ = calloc(1, sizeof(struct node_host));
331			memcpy(&$$->addr, hp->h_addr, sizeof(u_int32_t));
332		}
333		| NUMBER '.' NUMBER '.' NUMBER '.' NUMBER {
334			if ($1 < 0 || $3 < 0 || $5 < 0 || $7 < 0 ||
335			    $1 > 255 || $3 > 255 || $5 > 255 || $7 > 255) {
336				yyerror("illegal ip address %d.%d.%d.%d",
337				    $1, $3, $5, $7);
338				YYERROR;
339			}
340			$$ = calloc(1, sizeof(struct node_host));
341			$$->addr = htonl(($1 << 24) | ($3 << 16) | ($5 << 8) | $7);
342		}
343		;
344
345portspec	: port_item			{ $$ = $1; }
346		| '{' port_list '}'		{ $$ = $2; }
347		;
348
349port_list	: port_item			{ $$ = $1; }
350		| port_list ',' port_item	{ $3->next = $1; $$ = $3; }
351		;
352
353port_item	: port				{
354			$$ = malloc(sizeof(struct node_port));
355			$$->port[0] = $1;
356			$$->port[1] = $1;
357			$$->op = PF_OP_EQ;
358			$$->next = NULL;
359		}
360		| PORTUNARY port		{
361			$$ = malloc(sizeof(struct node_port));
362			$$->port[0] = $2;
363			$$->port[1] = $2;
364			$$->op = $1;
365			$$->next = NULL;
366		}
367		| port PORTBINARY port		{
368			$$ = malloc(sizeof(struct node_port));
369			$$->port[0] = $1;
370			$$->port[1] = $3;
371			$$->op = $2;
372			$$->next = NULL;
373		}
374		;
375
376port		: NUMBER			{
377			if (0 > $1 || $1 > 65535) {
378				yyerror("illegal port value %d", $1);
379				YYERROR;
380			}
381			$$ = htons($1);
382		}
383		| STRING			{
384			struct servent *s = NULL;
385
386			s = getservbyname($1, "tcp");
387			if (s == NULL)
388				s = getservbyname($1, "udp");
389			if (s == NULL) {
390				yyerror("unknown protocol %s", $1);
391				YYERROR;
392			}
393			$$ = s->s_port;
394		}
395		;
396
397flag		: STRING			{
398			int f;
399
400			if ((f = parse_flags($1)) < 0) {
401				yyerror("bad flags %s", $1);
402				YYERROR;
403			}
404			$$.b1 = f;
405		}
406		;
407
408flags		: /* empty */			{ $$.b1 = 0; $$.b2 = 0; }
409		| FLAGS flag			{ $$.b1 = $2.b1; $$.b2 = 63; }
410		| FLAGS flag "/" flag		{ $$.b1 = $2.b1; $$.b2 = $4.b1; }
411		| FLAGS "/" flag		{ $$.b1 = 0; $$.b2 = $3.b1; }
412		;
413
414icmpspec	: /* empty */			{ $$.b1 = 0; $$.b2 = 0; }
415		| ICMPTYPE icmptype		{ $$.b1 = $2; $$.b2 = 0; }
416		| ICMPTYPE icmptype CODE NUMBER	{
417			if ($4 < 0 || $4 > 255) {
418				yyerror("illegal icmp code %d", $4);
419				YYERROR;
420			}
421			$$.b1 = $2;
422			$$.b2 = $4 + 1;
423		}
424		| ICMPTYPE icmptype CODE STRING	{
425			struct icmpcodeent *p;
426
427			$$.b1 = $2;
428			if ((p = geticmpcodebyname($2, $4)) == NULL) {
429				yyerror("unknown icmp-code %s", $4);
430				YYERROR;
431			}
432			$$.b2 = p->code + 1;
433		}
434		;
435
436icmptype	: STRING			{
437			struct icmptypeent *p;
438
439			if ((p = geticmptypebyname($1)) == NULL) {
440				yyerror("unknown icmp-type %s", $1);
441				YYERROR;
442			}
443			$$ = p->type + 1;
444		}
445		| NUMBER			{
446			if ($1 < 0 || $1 > 255) {
447				yyerror("illegal icmp type %d", $1);
448				YYERROR;
449			}
450			$$ = $1 + 1;
451		}
452		;
453
454
455keep		: /* empty */			{ $$ = 0; }
456		| KEEP STATE			{ $$ = 1; }
457		;
458
459minttl		: /* empty */			{ $$ = 0; }
460		| MINTTL NUMBER			{
461			if ($2 < 0 || $2 > 255) {
462				yyerror("illegal min-ttl value %d", $2);
463				YYERROR;
464			}
465			$$ = $2;
466		}
467		;
468
469nodf		: /* empty */			{ $$ = 0; }
470		| NODF				{ $$ = 1; }
471		;
472
473natrule		: NAT natiface proto FROM ipspec TO ipspec ARROW address
474		{
475			struct pf_nat nat;
476
477			if (!natmode) {
478				yyerror("nat rule not permitted in filter mode");
479				YYERROR;
480			}
481			memset(&nat, 0, sizeof(nat));
482
483			if ($2.string) {
484				memcpy(nat.ifname, $2.string,
485				    sizeof(nat.ifname));
486				nat.ifnot = $2.not;
487			}
488			if ($3 != NULL) {
489				nat.proto = $3->proto;
490				free($3);
491			}
492			if ($5 != NULL) {
493				nat.saddr = $5->addr;
494				nat.smask = $5->mask;
495				nat.snot  = $5->not;
496				free($5);
497			}
498			if ($7 != NULL) {
499				nat.daddr = $7->addr;
500				nat.dmask = $7->mask;
501				nat.dnot  = $7->not;
502				free($7);
503			}
504
505			if ($9 == NULL) {
506				yyerror("nat rule requires redirection address");
507				YYERROR;
508			}
509			nat.raddr = $9->addr;
510			free($9);
511			pfctl_add_nat(pf, &nat);
512		}
513		;
514
515rdrrule		: RDR natiface proto FROM ipspec TO ipspec dport ARROW address rport
516		{
517			struct pf_rdr rdr;
518
519			if (!natmode) {
520				yyerror("rdr rule not permitted in filter mode");
521				YYERROR;
522			}
523			memset(&rdr, 0, sizeof(rdr));
524
525			if ($2.string) {
526				memcpy(rdr.ifname, $2.string,
527				    sizeof(rdr.ifname));
528				rdr.ifnot = $2.not;
529			}
530			if ($3 != NULL) {
531				rdr.proto = $3->proto;
532				free($3);
533			}
534			if ($5 != NULL) {
535				rdr.saddr = $5->addr;
536				rdr.smask = $5->mask;
537				rdr.snot  = $5->not;
538				free($5);
539			}
540			if ($7 != NULL) {
541				rdr.daddr = $7->addr;
542				rdr.dmask = $7->mask;
543				rdr.dnot  = $7->not;
544				free($7);
545			}
546
547			rdr.dport  = $8.a;
548			rdr.dport2 = $8.b;
549			rdr.opts  |= $8.t;
550
551			if ($10 == NULL) {
552				yyerror("rdr rule requires redirection address");
553				YYERROR;
554			}
555			rdr.raddr = $10->addr;
556			free($10);
557
558			rdr.rport  = $11.a;
559			rdr.opts  |= $11.t;
560
561			pfctl_add_rdr(pf, &rdr);
562		}
563		;
564
565dport		: PORT port			{
566			$$.a = $2;
567			$$.b = $$.t = 0;
568		}
569		| PORT port ':' port		{
570			$$.a = $2;
571			$$.b = $4;
572			$$.t = PF_DPORT_RANGE;
573		}
574		;
575
576rport		: PORT port			{
577			$$.a = $2;
578			$$.b = $$.t = 0;
579		}
580		| PORT port ':' '*'		{
581			$$.a = $2;
582			$$.b = 0;
583			$$.t = PF_RPORT_RANGE;
584		}
585		;
586%%
587
588int
589yyerror(char *fmt, ...)
590{
591	va_list ap;
592	extern char *infile;
593	errors = 1;
594
595	va_start(ap, fmt);
596	fprintf(stderr, "%s:%d: ", infile, yyval.lineno);
597	vfprintf(stderr, fmt, ap);
598	fprintf(stderr, "\n");
599	va_end(ap);
600	return (0);
601}
602
603int
604rule_consistent(struct pf_rule *r)
605{
606	int problems = 0;
607
608	if (r->action == PF_SCRUB) {
609		if (r->quick) {
610			yyerror("quick does not apply to scrub");
611			problems++;
612		}
613		if (r->keep_state) {
614			yyerror("keep state does not apply to scrub");
615			problems++;
616		}
617		if (r->src.port_op) {
618			yyerror("src port does not apply to scrub");
619			problems++;
620		}
621		if (r->dst.port_op) {
622			yyerror("dst port does not apply to scrub");
623			problems++;
624		}
625		if (r->type || r->code) {
626			yyerror("icmp-type/code does not apply to scrub");
627			problems++;
628		}
629	} else {
630		if (r->rule_flag & PFRULE_NODF) {
631			yyerror("nodf only applies to scrub");
632			problems++;
633		}
634		if (r->min_ttl) {
635			yyerror("min-ttl only applies to scrub");
636			problems++;
637		}
638	}
639	if (r->proto != IPPROTO_TCP && r->proto != IPPROTO_UDP &&
640	    (r->src.port_op || r->dst.port_op)) {
641		yyerror("port only applies to tcp/udp");
642		problems++;
643	}
644	if (r->proto != IPPROTO_ICMP && (r->type || r->code)) {
645		yyerror("icmp-type/code only applies to icmp");
646		problems++;
647	}
648	return (-problems);
649}
650
651#define CHECK_ROOT(T,r) \
652	do { \
653		if (r == NULL) { \
654			r = malloc(sizeof(T)); \
655			memset(r, 0, sizeof(T)); \
656		} \
657	} while (0)
658
659#define FREE_LIST(T,r) \
660	do { \
661		T *p, *n = r; \
662		while (n != NULL) { \
663			p = n; \
664			n = n->next; \
665			free(p); \
666		} \
667	} while (0)
668
669void
670expand_rule(struct pf_rule *r, struct node_proto *protos,
671    struct node_host *src_hosts, struct node_port *src_ports,
672    struct node_host *dst_hosts, struct node_port *dst_ports)
673{
674	struct node_proto *proto;
675	struct node_host *src_host, *dst_host;
676	struct node_port *src_port, *dst_port;
677
678	CHECK_ROOT(struct node_proto, protos);
679	CHECK_ROOT(struct node_host, src_hosts);
680	CHECK_ROOT(struct node_port, src_ports);
681	CHECK_ROOT(struct node_host, dst_hosts);
682	CHECK_ROOT(struct node_port, dst_ports);
683
684	proto = protos;
685	while (proto != NULL) {
686		src_host = src_hosts;
687		while (src_host != NULL) {
688			src_port = src_ports;
689			while (src_port != NULL) {
690				dst_host = dst_hosts;
691				while (dst_host != NULL) {
692					dst_port = dst_ports;
693					while (dst_port != NULL) {
694						r->proto = proto->proto;
695						r->src.addr = src_host->addr;
696						r->src.mask = src_host->mask;
697						r->src.not = src_host->not;
698						r->src.port[0] = src_port->port[0];
699						r->src.port[1] = src_port->port[1];
700						r->src.port_op = src_port->op;
701						r->dst.addr = dst_host->addr;
702						r->dst.mask = dst_host->mask;
703						r->dst.not = dst_host->not;
704						r->dst.port[0] = dst_port->port[0];
705						r->dst.port[1] = dst_port->port[1];
706						r->dst.port_op = dst_port->op;
707						if (rule_consistent(r) < 0)
708							yyerror("skipping rule "
709							    "due to errors");
710						else
711							pfctl_add_rule(pf, r);
712						dst_port = dst_port->next;
713					}
714					dst_host = dst_host->next;
715				}
716				src_port = src_port->next;
717			}
718			src_host = src_host->next;
719		}
720		proto = proto->next;
721	}
722
723	FREE_LIST(struct node_proto, protos);
724	FREE_LIST(struct node_host, src_hosts);
725	FREE_LIST(struct node_port, src_ports);
726	FREE_LIST(struct node_host, dst_hosts);
727	FREE_LIST(struct node_port, dst_ports);
728}
729
730#undef FREE_LIST
731#undef CHECK_ROOT
732
733int
734lookup(char *s)
735{
736	int i;
737	struct keywords {
738		char	*k_name;
739		int	 k_val;
740	} keywords[] = {
741		{ "all",	ALL},
742		{ "any",	ANY},
743		{ "block",	BLOCK},
744		{ "code",	CODE},
745		{ "flags",	FLAGS},
746		{ "from",	FROM},
747		{ "icmp-type",	ICMPTYPE},
748		{ "in",		IN},
749		{ "keep",	KEEP},
750		{ "log",	LOG},
751		{ "log-all",	LOGALL},
752		{ "min-ttl",	MINTTL},
753		{ "nat",	NAT},
754		{ "no-df",	NODF},
755		{ "on",		ON},
756		{ "out",	OUT},
757		{ "pass",	PASS},
758		{ "port",	PORT},
759		{ "proto",	PROTO},
760		{ "quick",	QUICK},
761		{ "rdr",	RDR},
762		{ "return",	RETURN},
763		{ "return-icmp",RETURNICMP},
764		{ "return-rst",	RETURNRST},
765		{ "scrub",	SCRUB},
766		{ "state",	STATE},
767		{ "to",		TO},
768		{ NULL,		0 },
769	};
770
771	for (i = 0; keywords[i].k_name != NULL; i++) {
772		if (strcmp(s, keywords[i].k_name) == 0) {
773			if (debug > 1)
774				fprintf(stderr, "%s: %d\n", s,
775				    keywords[i].k_val);
776			return (keywords[i].k_val);
777		}
778	}
779	if (debug > 1)
780		fprintf(stderr, "string: %s\n", s);
781	return (STRING);
782}
783
784char	*parsebuf;
785int	parseindex;
786
787int
788lgetc(FILE *fin)
789{
790	int c, next;
791
792restart:
793	if (parsebuf) {
794		/* Reading characters from the parse buffer, instead of input */
795		c = parsebuf[parseindex++];
796		if (c != '\0')
797			return (c);
798		free(parsebuf);
799		parsebuf = NULL;
800		parseindex = 0;
801		goto restart;
802	}
803
804	c = getc(fin);
805	if (c == '\\') {
806		next = getc(fin);
807		if (next != '\n') {
808			ungetc(next, fin);
809			return (c);
810		}
811		yylval.lineno = lineno;
812		lineno++;
813		goto restart;
814	}
815	return (c);
816}
817
818int
819lungetc(int c, FILE *fin)
820{
821	if (parsebuf && parseindex) {
822		/* XXX breaks on index 0 */
823		parseindex--;
824		return (c);
825	}
826	return ungetc(c, fin);
827}
828
829int
830findeol()
831{
832	int c;
833
834	if (parsebuf) {
835		free(parsebuf);
836		parsebuf = NULL;
837		parseindex = 0;
838	}
839
840	/* skip to either EOF or the first real EOL */
841	while (1) {
842		c = lgetc(fin);
843		if (c == '\\') {
844			c = lgetc(fin);
845			if (c == '\n')
846				continue;
847		}
848		if (c == EOF || c == '\n')
849			break;
850	}
851	return (ERROR);
852}
853
854int
855yylex(void)
856{
857	char buf[8096], *p, *val;
858	int endc, c, next;
859	int token;
860
861top:
862	p = buf;
863	while ((c = lgetc(fin)) == ' ' || c == '\t')
864		;
865
866	yylval.lineno = lineno;
867	if (c == '#')
868		while ((c = lgetc(fin)) != '\n' && c != EOF)
869			;
870	if (c == '$' && parsebuf == NULL) {
871		while (1) {
872			if ((c = lgetc(fin)) == EOF)
873				return (0);
874			if (p + 1 >= buf + sizeof(buf) - 1) {
875				yyerror("string too long");
876				return (findeol());
877			}
878			if (isalnum(c)) {
879				*p++ = (char)c;
880				continue;
881			}
882			*p = '\0';
883			lungetc(c, fin);
884			break;
885		}
886		val = symget(buf);
887		if (val == NULL)
888			return (ERROR);
889		parsebuf = strdup(val);
890		parseindex = 0;
891		goto top;
892	}
893
894	switch (c) {
895	case '\'':
896	case '"':
897		endc = c;
898		while (1) {
899			if ((c = lgetc(fin)) == EOF)
900				return (0);
901			if (c == endc) {
902				*p = '\0';
903				break;
904			}
905			if (c == '\n')
906				continue;
907			if (p + 1 >= buf + sizeof(buf) - 1) {
908				yyerror("string too long");
909				return (findeol());
910			}
911			*p++ = (char)c;
912		}
913		yylval.v.string = strdup(buf);
914		return (STRING);
915	case '=':
916		yylval.v.i = PF_OP_EQ;
917		return (PORTUNARY);
918	case '!':
919		next = lgetc(fin);
920		if (next == '=') {
921			yylval.v.i = PF_OP_NE;
922			return (PORTUNARY);
923		}
924		lungetc(next, fin);
925		break;
926	case '<':
927		next = lgetc(fin);
928		if (next == '>') {
929			yylval.v.i = PF_OP_XRG;
930			return (PORTBINARY);
931		} else  if (next == '=') {
932			yylval.v.i = PF_OP_LE;
933		} else {
934			yylval.v.i = PF_OP_LT;
935			lungetc(next, fin);
936		}
937		return (PORTUNARY);
938		break;
939	case '>':
940		next = lgetc(fin);
941		if (next == '<') {
942			yylval.v.i = PF_OP_IRG;
943			return (PORTBINARY);
944		} else  if (next == '=') {
945			yylval.v.i = PF_OP_GE;
946		} else {
947			yylval.v.i = PF_OP_GT;
948			lungetc(next, fin);
949		}
950		return (PORTUNARY);
951		break;
952	case '-':
953		next = lgetc(fin);
954		if (next == '>')
955			return (ARROW);
956		lungetc(next, fin);
957		break;
958	}
959
960	if (isdigit(c)) {
961		int index = 0, base = 10;
962		u_int64_t n = 0;
963
964		yylval.v.number = 0;
965		while (1) {
966			if (base == 10) {
967				if (!isdigit(c))
968					break;
969				c -= '0';
970			} else if (base == 16) {
971				if (isdigit(c))
972					c -= '0';
973				else if (c >= 'a' && c <= 'f')
974					c -= 'a';
975				else if (c >= 'A' && c <= 'F')
976					c -= 'A';
977				else
978					break;
979			}
980			n = n * base + c;
981
982			if (n > UINT_MAX) {
983				yyerror("number is too large");
984				return (ERROR);
985			}
986			c = lgetc(fin);
987			if (c == EOF)
988				break;
989			if (index++ == 0 && n == 0 && c == 'x') {
990				base = 16;
991				c = lgetc(fin);
992				if (c == EOF)
993					break;
994			}
995		}
996		yylval.v.number = (u_int32_t)n;
997
998		if (c != EOF)
999			lungetc(c, fin);
1000		if (debug > 1)
1001			fprintf(stderr, "number: %d\n", yylval.v.number);
1002		return (NUMBER);
1003	}
1004
1005#define allowed_in_string(x) \
1006	isalnum(x) || (ispunct(x) && x != '(' && x != ')' && x != '<' && \
1007	x != '>' && x != '!' && x != '=' && x != '/' && x != '#' && x != ',')
1008
1009	if (isalnum(c)) {
1010		do {
1011			*p++ = c;
1012			if (p-buf >= sizeof buf) {
1013				yyerror("string too long");
1014				return (ERROR);
1015			}
1016		} while ((c = lgetc(fin)) != EOF && (allowed_in_string(c)));
1017		lungetc(c, fin);
1018		*p = '\0';
1019		token = lookup(buf);
1020		yylval.v.string = strdup(buf);
1021		return (token);
1022	}
1023	if (c == '\n') {
1024		yylval.lineno = lineno;
1025		lineno++;
1026	}
1027	if (c == EOF)
1028		return (0);
1029	return (c);
1030}
1031
1032int
1033parse_rules(FILE *input, struct pfctl *xpf)
1034{
1035	natmode = 0;
1036	fin = input;
1037	pf = xpf;
1038	errors = 0;
1039	yyparse();
1040	return (errors ? -1 : 0);
1041}
1042
1043int
1044parse_nat(FILE *input, struct pfctl *xpf)
1045{
1046	natmode = 1;
1047	fin = input;
1048	pf = xpf;
1049	errors = 0;
1050	yyparse();
1051	return (errors ? -1 : 0);
1052}
1053
1054u_int32_t
1055ipmask(u_int8_t b)
1056{
1057	u_int32_t m = 0;
1058	int i;
1059
1060	for (i = 31; i > 31-b; --i)
1061		m |= (1 << i);
1062	return (htonl(m));
1063}
1064
1065struct pf_rule_addr *
1066new_addr(void)
1067{
1068	struct pf_rule_addr *ra;
1069
1070	ra = malloc(sizeof(struct pf_rule_addr));
1071	if (ra == NULL)
1072		err(1, "new_addr: malloc failed");
1073	memset(ra, 0, sizeof(*ra));
1074	return (ra);
1075}
1076
1077/*
1078 * Over-designed efficiency is a French and German concept, so how about
1079 * we wait until they discover this ugliness and make it all fancy.
1080 */
1081int
1082symset(char *nam, char *val)
1083{
1084	struct sym *sym;
1085
1086	sym = calloc(1, sizeof(*sym));
1087	if (sym == NULL)
1088		return (-1);
1089	sym->nam = strdup(nam);
1090	if (sym->nam == NULL) {
1091		free(sym);
1092		return (-1);
1093	}
1094	sym->val = strdup(val);
1095	if (sym->val == NULL) {
1096		free(sym->nam);
1097		free(sym);
1098		return (-1);
1099	}
1100	sym->next = symhead;
1101	symhead = sym;
1102	return (0);
1103}
1104
1105char *
1106symget(char *nam)
1107{
1108	struct sym *sym;
1109
1110	for (sym = symhead; sym; sym = sym->next)
1111		if (strcmp(nam, sym->nam) == 0)
1112			return (sym->val);
1113	return (NULL);
1114}
1115