parse.y revision 1.20
1/*	$OpenBSD: parse.y,v 1.20 2001/08/19 16:16:41 dhartmei 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
87typedef struct {
88	union {
89		u_int32_t		number;
90		int			i;
91		char			*string;
92		struct {
93			char		*string;
94			int		not;
95		}			iface;
96		struct {
97			u_int8_t	b1;
98			u_int8_t	b2;
99			u_int16_t	w;
100		}			b;
101		struct {
102			int		a;
103			int		b;
104			int		t;
105		}			range;
106		struct node_proto	*proto;
107		struct node_host	*host;
108		struct node_port	*port;
109		struct peer		peer;
110		struct {
111			struct peer	src, dst;
112		}			fromto;
113	} v;
114	int lineno;
115} YYSTYPE;
116
117%}
118
119%token	PASS BLOCK SCRUB RETURN IN OUT LOG LOGALL QUICK ON FROM TO FLAGS
120%token	RETURNRST RETURNICMP PROTO ALL ANY ICMPTYPE CODE KEEP STATE PORT
121%token	RDR NAT ARROW NODF MINTTL ERROR
122%token	<v.string> STRING
123%token	<v.number> NUMBER
124%token	<v.i>	PORTUNARY PORTBINARY
125%type	<v.iface> iface natiface
126%type	<v.number> port icmptype minttl
127%type	<v.i>	direction log quick keep nodf
128%type	<v.b>	action icmpspec flag flags blockspec
129%type	<v.range>	dport rport
130%type	<v.proto>	proto proto_list proto_item
131%type	<v.fromto>	fromto
132%type	<v.peer>	ipportspec
133%type	<v.host>	ipspec host address host_list
134%type	<v.port>	portspec port_list port_item
135%%
136
137ruleset:	/* empty */
138		| ruleset '\n'
139		| ruleset pfrule '\n'
140		| ruleset natrule '\n'
141		| ruleset rdrrule '\n'
142		| ruleset error '\n'		{ errors++; }
143		;
144
145pfrule:		action direction log quick iface proto fromto flags icmpspec keep nodf minttl
146		{
147			struct pf_rule r;
148
149			if (natmode) {
150				yyerror("filter rule not permitted in nat mode");
151				YYERROR;
152			}
153			memset(&r, 0, sizeof(r));
154
155			r.action = $1.b1;
156			if ($1.b2)
157				r.rule_flag |= PFRULE_RETURNRST;
158			else
159				r.return_icmp = $1.w;
160			r.direction = $2;
161			r.log = $3;
162			r.quick = $4;
163			if ($5.string)
164				memcpy(r.ifname, $5.string, sizeof(r.ifname));
165
166			r.flags = $8.b1;
167			r.flagset = $8.b2;
168			r.type = $9.b1;
169			r.code = $9.b2;
170			r.keep_state = $10;
171
172			if ($11)
173				r.rule_flag |= PFRULE_NODF;
174			if ($12)
175				r.min_ttl = $12;
176
177			expand_rule(&r, $6, $7.src.host, $7.src.port,
178			    $7.dst.host, $7.dst.port);
179		}
180		;
181
182action:		PASS			{ $$.b1 = PF_PASS; }
183		| BLOCK blockspec	{ $$ = $2; $$.b1 = PF_DROP; }
184		| SCRUB			{ $$.b1 = PF_SCRUB; }
185		;
186
187blockspec:				{ $$.b2 = 0; $$.w = 0; }
188		| RETURNRST		{ $$.b2 = 1; $$.w = 0;}
189		| RETURNICMP		{
190			$$.b2 = 0;
191			$$.w = (ICMP_UNREACH << 8) | ICMP_UNREACH_PORT;
192		}
193		| RETURNICMP '(' STRING ')'	{
194			struct icmpcodeent *p;
195
196			if ((p = geticmpcodebyname(ICMP_UNREACH, $3)) == NULL) {
197				yyerror("unknown icmp code %s", $3);
198				YYERROR;
199			}
200			$$.w = (p->type << 8) | p->code;
201			$$.b2 = 0;
202		}
203		;
204
205direction:	IN			{ $$ = PF_IN; }
206		| OUT			{ $$ = PF_OUT; }
207		;
208
209log:					{ $$ = 0; }
210		| LOG			{ $$ = 1; }
211		| LOGALL		{ $$ = 2; }
212		;
213
214quick:					{ $$ = 0; }
215		| QUICK			{ $$ = 1; }
216		;
217
218natiface:	iface
219		| ON '!' STRING		{
220			$$.string = strdup($3); $$.not = 1;
221		}
222		;
223iface:					{ $$.string = NULL; }
224		| ON STRING		{ $$.string = strdup($2); }
225		;
226
227proto:						{ $$ = NULL; }
228		| PROTO proto_item		{ $$ = $2; }
229		| PROTO '{' proto_list '}'	{ $$ = $3; }
230		;
231
232proto_list:	proto_item			{ $$ = $1; }
233		| proto_list ',' proto_item	{ $3->next = $1; $$ = $3; }
234		;
235
236proto_item:	NUMBER			{
237			struct protoent *p;
238
239			if ((p = getprotobynumber($1)) == NULL) {
240				yyerror("unknown protocol %d", $1);
241				YYERROR;
242			}
243			$$ = malloc(sizeof(struct node_proto));
244			$$->proto = p->p_proto;
245			$$->next = NULL;
246		}
247		| STRING		{
248			struct protoent *p;
249
250			if ((p = getprotobyname($1)) == NULL) {
251				yyerror("unknown protocol %s", $1);
252				YYERROR;
253			}
254			$$ = malloc(sizeof(struct node_proto));
255			$$->proto = p->p_proto;
256			$$->next = NULL;
257		}
258		;
259
260fromto:		ALL			{
261			$$.src.host = NULL;
262			$$.src.port = NULL;
263			$$.dst.host = NULL;
264			$$.dst.port = NULL;
265		}
266		| FROM ipportspec TO ipportspec	{
267			$$.src = $2;
268			$$.dst = $4;
269		}
270		;
271
272ipportspec:	ipspec			{ $$.host = $1; $$.port = NULL; }
273		| ipspec PORT portspec	{
274			$$.host = $1;
275			$$.port = $3;
276		}
277		;
278
279ipspec:		ANY			{ $$ = NULL; }
280		| '!' host		{ $$ = $2; $$->not = 1; }
281		| host			{ $$ = $1; $$->not = 0; }
282		| '{' host_list '}'	{ $$ = $2; }
283		;
284
285host_list:	host			{ $$ = $1; }
286		| host_list ',' host	{ $3->next = $1; $$ = $3; }
287
288host:		address			{
289			$$ = $1;
290			$$->mask = 0xffffffff;
291		}
292		| address '/' NUMBER	{
293			if ($3 < 0 || $3 > 32) {
294				yyerror("illegal netmask value %d", $3);
295				YYERROR;
296			}
297			$$ = $1;
298			$$->mask = ipmask($3);
299		}
300		;
301
302address:	STRING {
303			struct hostent *hp;
304
305			if ((hp = gethostbyname($1)) == NULL) {
306				yyerror("cannot resolve %s", $1);
307				YYERROR;
308			}
309			$$ = malloc(sizeof(struct node_host));
310			memcpy(&$$->addr, hp->h_addr, sizeof(u_int32_t));
311		}
312		| NUMBER '.' NUMBER '.' NUMBER '.' NUMBER {
313			if ($1 < 0 || $3 < 0 || $5 < 0 || $7 < 0 ||
314			    $1 > 255 || $3 > 255 || $5 > 255 || $7 > 255) {
315				yyerror("illegal ip address %d.%d.%d.%d",
316				    $1, $3, $5, $7);
317				YYERROR;
318			}
319			$$ = malloc(sizeof(struct node_host));
320			$$->addr = htonl(($1 << 24) | ($3 << 16) | ($5 << 8) | $7);
321		}
322		;
323
324portspec:	port_item			{ $$ = $1; }
325		| '{' port_list '}'		{ $$ = $2; }
326		;
327
328port_list:	port_item			{ $$ = $1; }
329		| port_list ',' port_item	{ $3->next = $1; $$ = $3; }
330		;
331
332port_item:	port				{
333			$$ = malloc(sizeof(struct node_port));
334			$$->port[0] = $1;
335			$$->port[1] = $1;
336			$$->op = PF_OP_EQ;
337		}
338		| PORTUNARY port		{
339			$$ = malloc(sizeof(struct node_port));
340			$$->port[0] = $2;
341			$$->port[1] = $2;
342			$$->op = $1;
343		}
344		| port PORTBINARY port		{
345			$$ = malloc(sizeof(struct node_port));
346			$$->port[0] = $1;
347			$$->port[1] = $3;
348			$$->op = $2;
349		}
350		;
351
352port:		NUMBER				{
353			if (0 > $1 || $1 > 65535) {
354				yyerror("illegal port value %d", $1);
355				YYERROR;
356			}
357			$$ = htons($1);
358		}
359		| STRING		{
360			struct servent *s = NULL;
361
362			s = getservbyname($1, "tcp");
363			if (s == NULL)
364				s = getservbyname($1, "udp");
365			if (s == NULL) {
366				yyerror("unknown protocol %s", $1);
367				YYERROR;
368			}
369			$$ = s->s_port;
370		}
371		;
372
373flag:		STRING			{
374			int f;
375
376			if ((f = parse_flags($1)) < 0) {
377				yyerror("bad flags %s", $1);
378				YYERROR;
379			}
380			$$.b1 = f;
381		}
382		;
383
384flags:					{ $$.b1 = 0; $$.b2 = 0; }
385		| FLAGS flag		{ $$.b1 = $2.b1; $$.b2 = 63; }
386		| FLAGS flag "/" flag	{ $$.b1 = $2.b1; $$.b2 = $4.b1; }
387		| FLAGS "/" flag	{ $$.b1 = 0; $$.b2 = $3.b1; }
388		;
389
390icmpspec:				{ $$.b1 = 0; $$.b2 = 0; }
391		| ICMPTYPE icmptype	{ $$.b1 = $2; $$.b2 = 0; }
392		| ICMPTYPE icmptype CODE NUMBER	{
393			if ($4 < 0 || $4 > 255) {
394				yyerror("illegal icmp code %d", $4);
395				YYERROR;
396			}
397			$$.b1 = $2;
398			$$.b2 = $4 + 1;
399		}
400		| ICMPTYPE icmptype CODE STRING	{
401			struct icmpcodeent *p;
402
403			$$.b1 = $2;
404			if ((p = geticmpcodebyname($2, $4)) == NULL) {
405				yyerror("unknown icmp-code %s", $4);
406				YYERROR;
407			}
408			$$.b2 = p->code + 1;
409		}
410		;
411
412icmptype:	STRING			{
413			struct icmptypeent *p;
414
415			if ((p = geticmptypebyname($1)) == NULL) {
416				yyerror("unknown icmp-type %s", $1);
417				YYERROR;
418			}
419			$$ = p->type + 1;
420		}
421		| NUMBER		{
422			if ($1 < 0 || $1 > 255) {
423				yyerror("illegal icmp type %d", $1);
424				YYERROR;
425			}
426			$$ = $1 + 1;
427		}
428		;
429
430
431keep:					{ $$ = 0; }
432		| KEEP STATE		{ $$ = 1; }
433		;
434
435minttl:					{ $$ = 0; }
436		| MINTTL NUMBER		{
437			if ($2 < 0 || $2 > 255) {
438				yyerror("illegal min-ttl value %d", $2);
439				YYERROR;
440			}
441			$$ = $2;
442		}
443		;
444
445nodf:					{ $$ = 0; }
446		| NODF			{ $$ = 1; }
447		;
448
449natrule:	NAT natiface proto FROM ipspec TO ipspec ARROW address
450		{
451			struct pf_nat nat;
452
453			if (!natmode) {
454				yyerror("nat rule not permitted in filter mode");
455				YYERROR;
456			}
457
458			memset(&nat, 0, sizeof(nat));
459
460			if ($2.string) {
461				memcpy(nat.ifname, $2.string,
462				    sizeof(nat.ifname));
463				nat.ifnot = $2.not;
464			}
465			if ($3 != NULL) {
466				nat.proto = $3->proto;
467				free($3);
468			}
469			if ($5 != NULL) {
470				nat.saddr = $5->addr;
471				nat.smask = $5->mask;
472				nat.snot  = $5->not;
473				free($5);
474			}
475			if ($7 != NULL) {
476				nat.daddr = $7->addr;
477				nat.dmask = $7->mask;
478				nat.dnot  = $7->not;
479				free($7);
480			}
481
482			if ($9 == NULL) {
483				yyerror("nat rule requires redirection address");
484				YYERROR;
485			}
486			nat.raddr = $9->addr;
487			free($9);
488
489			pfctl_add_nat(pf, &nat);
490		}
491		;
492
493rdrrule:	RDR natiface proto FROM ipspec TO ipspec dport ARROW address rport
494		{
495			struct pf_rdr rdr;
496
497			if (!natmode) {
498				yyerror("rdr rule not permitted in filter mode");
499				YYERROR;
500			}
501
502			memset(&rdr, 0, sizeof(rdr));
503
504			if ($2.string) {
505				memcpy(rdr.ifname, $2.string,
506				    sizeof(rdr.ifname));
507				rdr.ifnot = $2.not;
508			}
509			if ($3 != NULL) {
510				rdr.proto = $3->proto;
511				free($3);
512			}
513			if ($5 != NULL) {
514				rdr.saddr = $5->addr;
515				rdr.smask = $5->mask;
516				rdr.snot  = $5->not;
517				free($5);
518			}
519			if ($7 != NULL) {
520				rdr.daddr = $7->addr;
521				rdr.dmask = $7->mask;
522				rdr.dnot  = $7->not;
523				free($7);
524			}
525
526			rdr.dport  = $8.a;
527			rdr.dport2 = $8.b;
528			rdr.opts  |= $8.t;
529
530			if ($10 == NULL) {
531				yyerror("rdr rule requires redirection address");
532				YYERROR;
533			}
534			rdr.raddr = $10->addr;
535			free($10);
536
537			rdr.rport  = $11.a;
538			rdr.opts  |= $11.t;
539
540			pfctl_add_rdr(pf, &rdr);
541		}
542		;
543
544dport:		PORT port			{
545			$$.a = $2;
546			$$.b = $$.t = 0;
547		}
548		| PORT port ':' port		{
549			$$.a = $2;
550			$$.b = $4;
551			$$.t = PF_DPORT_RANGE;
552		}
553		;
554
555rport:		PORT port			{
556			$$.a = $2;
557			$$.b = $$.t = 0;
558		}
559		| PORT port ':' '*'		{
560			$$.a = $2;
561			$$.b = 0;
562			$$.t = PF_RPORT_RANGE;
563		}
564		;
565
566%%
567
568int
569yyerror(char *fmt, ...)
570{
571	va_list ap;
572	extern char *infile;
573	errors = 1;
574
575	va_start(ap, fmt);
576	fprintf(stderr, "%s:%d: ", infile, yyval.lineno);
577	vfprintf(stderr, fmt, ap);
578	fprintf(stderr, "\n");
579	va_end(ap);
580	return (0);
581}
582
583int
584rule_consistent(struct pf_rule *r)
585{
586	int problems = 0;
587
588	if (r->action == PF_SCRUB) {
589		if (r->quick) {
590			yyerror("quick does not apply to scrub");
591			problems++;
592		}
593		if (r->keep_state) {
594			yyerror("keep state does not apply to scrub");
595			problems++;
596		}
597		if (r->src.port_op) {
598			yyerror("src port does not apply to scrub");
599			problems++;
600		}
601		if (r->dst.port_op) {
602			yyerror("dst port does not apply to scrub");
603			problems++;
604		}
605		if (r->type || r->code) {
606			yyerror("icmp-type/code does not apply to scrub");
607			problems++;
608		}
609	} else {
610		if (r->rule_flag & PFRULE_NODF) {
611			yyerror("nodf only applies to scrub");
612			problems++;
613		}
614		if (r->min_ttl) {
615			yyerror("min-ttl only applies to scrub");
616			problems++;
617		}
618	}
619	if (r->proto != IPPROTO_TCP && r->proto != IPPROTO_UDP &&
620	    (r->src.port_op || r->dst.port_op)) {
621		yyerror("port only applies to tcp/udp");
622		problems++;
623	}
624	if (r->proto != IPPROTO_ICMP && (r->type || r->code)) {
625		yyerror("icmp-type/code only applies to icmp");
626		problems++;
627	}
628	return (-problems);
629}
630
631#define CHECK_ROOT(T,r) \
632	do { \
633		if (r == NULL) { \
634			r = malloc(sizeof(T)); \
635			memset(r, 0, sizeof(T)); \
636		} \
637	} while (0)
638
639#define FREE_LIST(T,r) \
640	do { \
641		T *p, *n = r; \
642		while (n != NULL) { \
643			p = n; \
644			n = n->next; \
645			free(p); \
646		} \
647	} while (0)
648
649void
650expand_rule(struct pf_rule *r, struct node_proto *protos,
651    struct node_host *src_hosts, struct node_port *src_ports,
652    struct node_host *dst_hosts, struct node_port *dst_ports)
653{
654	struct node_proto *proto;
655	struct node_host *src_host, *dst_host;
656	struct node_port *src_port, *dst_port;
657
658	CHECK_ROOT(struct node_proto, protos);
659	CHECK_ROOT(struct node_host, src_hosts);
660	CHECK_ROOT(struct node_port, src_ports);
661	CHECK_ROOT(struct node_host, dst_hosts);
662	CHECK_ROOT(struct node_port, dst_ports);
663
664	proto = protos;
665	while (proto != NULL) {
666		src_host = src_hosts;
667		while (src_host != NULL) {
668			src_port = src_ports;
669			while (src_port != NULL) {
670				dst_host = dst_hosts;
671				while (dst_host != NULL) {
672					dst_port = dst_ports;
673					while (dst_port != NULL) {
674						r->proto = proto->proto;
675						r->src.addr = src_host->addr;
676						r->src.mask = src_host->mask;
677						r->src.not = src_host->not;
678						r->src.port[0] = src_port->port[0];
679						r->src.port[1] = src_port->port[1];
680						r->src.port_op = src_port->op;
681						r->dst.addr = dst_host->addr;
682						r->dst.mask = dst_host->mask;
683						r->dst.not = dst_host->not;
684						r->dst.port[0] = dst_port->port[0];
685						r->dst.port[1] = dst_port->port[1];
686						r->dst.port_op = dst_port->op;
687						if (rule_consistent(r) < 0)
688							yyerror("skipping rule "
689							    "due to errors");
690						else
691							pfctl_add_rule(pf, r);
692						dst_port = dst_port->next;
693					}
694					dst_host = dst_host->next;
695				}
696				src_port = src_port->next;
697			}
698			src_host = src_host->next;
699		}
700		proto = proto->next;
701	}
702
703	FREE_LIST(struct node_proto, protos);
704	FREE_LIST(struct node_host, src_hosts);
705	FREE_LIST(struct node_port, src_ports);
706	FREE_LIST(struct node_host, dst_hosts);
707	FREE_LIST(struct node_port, dst_ports);
708}
709
710#undef FREE_LIST
711#undef CHECK_ROOT
712
713int
714lookup(char *s)
715{
716	int i;
717	struct keywords {
718		char	*k_name;
719		int	 k_val;
720	} keywords[] = {
721		{ "all",	ALL},
722		{ "any",	ANY},
723		{ "block",	BLOCK},
724		{ "code",	CODE},
725		{ "flags",	FLAGS},
726		{ "from",	FROM},
727		{ "icmp-type",	ICMPTYPE},
728		{ "in",		IN},
729		{ "keep",	KEEP},
730		{ "log",	LOG},
731		{ "log-all",	LOGALL},
732		{ "min-ttl",	MINTTL},
733		{ "nat",	NAT},
734		{ "no-df",	NODF},
735		{ "on",		ON},
736		{ "out",	OUT},
737		{ "pass",	PASS},
738		{ "port",	PORT},
739		{ "proto",	PROTO},
740		{ "quick",	QUICK},
741		{ "rdr",	RDR},
742		{ "return",	RETURN},
743		{ "return-icmp",RETURNICMP},
744		{ "return-rst",	RETURNRST},
745		{ "scrub",	SCRUB},
746		{ "state",	STATE},
747		{ "to",		TO},
748		{ NULL,		0 },
749	};
750
751	for (i = 0; keywords[i].k_name != NULL; i++) {
752		if (strcmp(s, keywords[i].k_name) == 0) {
753			if (debug > 1)
754				fprintf(stderr, "%s: %d\n", s,
755				    keywords[i].k_val);
756			return (keywords[i].k_val);
757		}
758	}
759	if (debug > 1)
760		fprintf(stderr, "string: %s\n", s);
761	return (STRING);
762}
763
764int
765lgetc(FILE *fin)
766{
767	int c, next;
768
769restart:
770	c = getc(fin);
771	if (c == '\\') {
772		next = getc(fin);
773		if (next != '\n') {
774			ungetc(next, fin);
775			return (c);
776		}
777		yylval.lineno = lineno;
778		lineno++;
779		goto restart;
780	}
781	return (c);
782}
783
784int
785yylex(void)
786{
787	char *p, buf[8096];
788	int c, next;
789	int token;
790
791	while ((c = lgetc(fin)) == ' ' || c == '\t')
792		;
793
794	yylval.lineno = lineno;
795	if (c == '#')
796		while ((c = lgetc(fin)) != '\n' && c != EOF)
797			;
798	if (c == '-') {
799		next = lgetc(fin);
800		if (next == '>')
801			return (ARROW);
802		ungetc(next, fin);
803	}
804	switch (c) {
805	case '=':
806		yylval.v.i = PF_OP_EQ;
807		return (PORTUNARY);
808	case '!':
809		next = lgetc(fin);
810		if (next == '=') {
811			yylval.v.i = PF_OP_NE;
812			return (PORTUNARY);
813		}
814		ungetc(next, fin);
815		break;
816	case '<':
817		next = lgetc(fin);
818		if (next == '>') {
819			yylval.v.i = PF_OP_XRG;
820			return (PORTBINARY);
821		} else  if (next == '=') {
822			yylval.v.i = PF_OP_LE;
823		} else {
824			yylval.v.i = PF_OP_LT;
825			ungetc(next, fin);
826		}
827		return (PORTUNARY);
828		break;
829	case '>':
830		next = lgetc(fin);
831		if (next == '<') {
832			yylval.v.i = PF_OP_IRG;
833			return (PORTBINARY);
834		} else  if (next == '=') {
835			yylval.v.i = PF_OP_GE;
836		} else {
837			yylval.v.i = PF_OP_GT;
838			ungetc(next, fin);
839		}
840		return (PORTUNARY);
841		break;
842	}
843	if (isdigit(c)) {
844		yylval.v.number = 0;
845		do {
846			u_int64_t n = (u_int64_t)yylval.v.number * 10 + c - '0';
847			if (n > 0xffffffff) {
848				yyerror("number is too large");
849				return (ERROR);
850			}
851			yylval.v.number = (u_int32_t)n;
852		} while ((c = lgetc(fin)) != EOF && isdigit(c));
853		ungetc(c, fin);
854		if (debug > 1)
855			fprintf(stderr, "number: %d\n", yylval.v.number);
856		return (NUMBER);
857	}
858
859#define allowed_in_string(x) \
860	isalnum(x) || (ispunct(x) && x != '(' && x != ')' && x != '<' \
861	&& x != '>' && x != '!' && x != '=' && x != '/' && x != '#' && x != ',')
862
863	if (isalnum(c)) {
864		p = buf;
865		do {
866			*p++ = c;
867			if (p-buf >= sizeof buf) {
868				yyerror("string too long");
869				return (ERROR);
870			}
871		} while ((c = lgetc(fin)) != EOF && (allowed_in_string(c)));
872		ungetc(c, fin);
873		*p = '\0';
874		token = lookup(buf);
875		yylval.v.string = strdup(buf);
876		return (token);
877	}
878	if (c == '\n') {
879		yylval.lineno = lineno;
880		lineno++;
881	}
882	if (c == EOF)
883		return (0);
884	return (c);
885}
886
887int
888parse_rules(FILE *input, struct pfctl *xpf)
889{
890	natmode = 0;
891	fin = input;
892	pf = xpf;
893	errors = 0;
894	yyparse();
895	return (errors ? -1 : 0);
896}
897
898int
899parse_nat(FILE *input, struct pfctl *xpf)
900{
901	natmode = 1;
902	fin = input;
903	pf = xpf;
904	errors = 0;
905	yyparse();
906	return (errors ? -1 : 0);
907}
908
909u_int32_t
910ipmask(u_int8_t b)
911{
912	u_int32_t m = 0;
913	int i;
914
915	for (i = 31; i > 31-b; --i)
916		m |= (1 << i);
917	return (htonl(m));
918}
919
920struct pf_rule_addr *
921new_addr(void)
922{
923	struct pf_rule_addr *ra;
924
925	ra = malloc(sizeof(struct pf_rule_addr));
926	if (ra == NULL)
927		err(1, "new_addr: malloc failed");
928	memset(ra, 0, sizeof(*ra));
929	return (ra);
930}
931