1/*	$FreeBSD$	*/
2
3/*
4 * Copyright (C) 2012 by Darren Reed.
5 *
6 * See the IPFILTER.LICENCE file for details on licencing.
7 */
8%{
9#include <stdio.h>
10#include <unistd.h>
11#include <string.h>
12#include <fcntl.h>
13#include <errno.h>
14#if !defined(__SVR4) && !defined(__GNUC__)
15#include <strings.h>
16#endif
17#include <sys/types.h>
18#include <sys/param.h>
19#include <sys/file.h>
20#include <stdlib.h>
21#include <stddef.h>
22#include <sys/socket.h>
23#include <sys/ioctl.h>
24#include <netinet/in.h>
25#include <netinet/in_systm.h>
26#include <sys/time.h>
27#include <syslog.h>
28#include <net/if.h>
29#include <netdb.h>
30#include <arpa/nameser.h>
31#include <resolv.h>
32#include "ipf.h"
33#include "netinet/ipl.h"
34#include "ipnat_l.h"
35
36#define	YYDEBUG	1
37
38extern	void	yyerror(char *);
39extern	int	yyparse(void);
40extern	int	yylex(void);
41extern	int	yydebug;
42extern	FILE	*yyin;
43extern	int	yylineNum;
44
45static	ipnat_t		*nattop = NULL;
46static	ipnat_t		*nat = NULL;
47static	int		natfd = -1;
48static	ioctlfunc_t	natioctlfunc = NULL;
49static	addfunc_t	nataddfunc = NULL;
50static	int		suggest_port = 0;
51static	proxyrule_t	*prules = NULL;
52static	int		parser_error = 0;
53
54static	void	newnatrule(void);
55static	void	setnatproto(int);
56static	void	setmapifnames(void);
57static	void	setrdrifnames(void);
58static	void	proxy_setconfig(int);
59static	void	proxy_unsetconfig(void);
60static	namelist_t *proxy_dns_add_pass(char *, char *);
61static	namelist_t *proxy_dns_add_block(char *, char *);
62static	void	proxy_addconfig(char *, int, char *, namelist_t *);
63static	void	proxy_loadconfig(int, ioctlfunc_t, char *, int,
64				      char *, namelist_t *);
65static	void	proxy_loadrules(int, ioctlfunc_t, proxyrule_t *);
66static	void	setmapifnames(void);
67static	void	setrdrifnames(void);
68static	void	setifname(ipnat_t **, int, char *);
69static	int	addname(ipnat_t **, char *);
70%}
71%union	{
72	char	*str;
73	u_32_t	num;
74	struct {
75		i6addr_t	a;
76		int		f;
77	} ipa;
78	frentry_t	fr;
79	frtuc_t	*frt;
80	u_short	port;
81	struct	{
82		int	p1;
83		int	p2;
84		int	pc;
85	} pc;
86	struct	{
87		i6addr_t	a;
88		i6addr_t	m;
89		int	t;		/* Address type */
90		int	u;
91		int	f;		/* Family */
92		int	v;		/* IP version */
93		int	s;		/* 0 = number, 1 = text */
94		int	n;		/* number */
95	} ipp;
96	union	i6addr	ip6;
97	namelist_t	*names;
98};
99
100%token  <num>   YY_NUMBER YY_HEX
101%token  <str>   YY_STR
102%token	  YY_COMMENT
103%token	  YY_CMP_EQ YY_CMP_NE YY_CMP_LE YY_CMP_GE YY_CMP_LT YY_CMP_GT
104%token	  YY_RANGE_OUT YY_RANGE_IN
105%token  <ip6>   YY_IPV6
106
107%token	IPNY_MAPBLOCK IPNY_RDR IPNY_PORT IPNY_PORTS IPNY_AUTO IPNY_RANGE
108%token	IPNY_MAP IPNY_BIMAP IPNY_FROM IPNY_TO IPNY_MASK IPNY_PORTMAP IPNY_ANY
109%token	IPNY_ROUNDROBIN IPNY_FRAG IPNY_AGE IPNY_ICMPIDMAP IPNY_PROXY
110%token	IPNY_TCP IPNY_UDP IPNY_TCPUDP IPNY_STICKY IPNY_MSSCLAMP IPNY_TAG
111%token	IPNY_TLATE IPNY_POOL IPNY_HASH IPNY_NO IPNY_REWRITE IPNY_PROTO
112%token	IPNY_ON IPNY_SRC IPNY_DST IPNY_IN IPNY_OUT IPNY_DIVERT
113%token	IPNY_CONFIG IPNY_ALLOW IPNY_DENY IPNY_DNS IPNY_INET IPNY_INET6
114%token	IPNY_SEQUENTIAL IPNY_DSTLIST IPNY_PURGE
115%type	<port> portspec
116%type	<num> hexnumber compare range proto
117%type	<num> saddr daddr sobject dobject mapfrom rdrfrom dip
118%type	<ipa> hostname ipv4 ipaddr
119%type	<ipp> addr rhsaddr rhdaddr erhdaddr
120%type	<pc> portstuff portpair comaports srcports dstports
121%type	<names> dnslines dnsline
122%%
123file:	line
124	| assign
125	| file line
126	| file assign
127	| file pconf ';'
128	;
129
130line:	xx rule		{ int err;
131			  while ((nat = nattop) != NULL) {
132				if (nat->in_v[0] == 0)
133					nat->in_v[0] = 4;
134				if (nat->in_v[1] == 0)
135					nat->in_v[1] = nat->in_v[0];
136				nattop = nat->in_next;
137				err = (*nataddfunc)(natfd, natioctlfunc, nat);
138				free(nat);
139				if (err != 0) {
140					parser_error = err;
141					break;
142				}
143			  }
144			  if (parser_error == 0 && prules != NULL) {
145				proxy_loadrules(natfd, natioctlfunc, prules);
146				prules = NULL;
147			  }
148			  resetlexer();
149			}
150	| YY_COMMENT
151	;
152
153assign:	YY_STR assigning YY_STR ';'	{ set_variable($1, $3);
154					  resetlexer();
155					  free($1);
156					  free($3);
157					  yyvarnext = 0;
158					}
159	;
160
161assigning:
162	'='				{ yyvarnext = 1; }
163	;
164
165xx:					{ newnatrule(); }
166	;
167
168rule:	map eol
169	| mapblock eol
170	| redir eol
171	| rewrite ';'
172	| divert ';'
173	;
174
175no:	IPNY_NO				{ nat->in_flags |= IPN_NO; }
176	;
177
178eol:	| ';'
179	;
180
181map:	mapit ifnames addr tlate rhsaddr proxy mapoptions
182				{ if ($3.f != 0 && $3.f != $5.f && $5.f != 0)
183					yyerror("3.address family mismatch");
184				  if (nat->in_v[0] == 0 && $5.v != 0)
185					nat->in_v[0] = $5.v;
186				  else if (nat->in_v[0] == 0 && $3.v != 0)
187					nat->in_v[0] = $3.v;
188				  if (nat->in_v[1] == 0 && $5.v != 0)
189					nat->in_v[1] = $5.v;
190				  else if (nat->in_v[1] == 0 && $3.v != 0)
191					nat->in_v[1] = $3.v;
192				  nat->in_osrcatype = $3.t;
193				  bcopy(&$3.a, &nat->in_osrc.na_addr[0],
194					sizeof($3.a));
195				  bcopy(&$3.m, &nat->in_osrc.na_addr[1],
196					sizeof($3.a));
197				  nat->in_nsrcatype = $5.t;
198				  nat->in_nsrcafunc = $5.u;
199				  bcopy(&$5.a, &nat->in_nsrc.na_addr[0],
200					sizeof($5.a));
201				  bcopy(&$5.m, &nat->in_nsrc.na_addr[1],
202					sizeof($5.a));
203
204				  setmapifnames();
205				}
206	| mapit ifnames addr tlate rhsaddr mapport mapoptions
207				{ if ($3.f != $5.f && $3.f != 0 && $5.f != 0)
208					yyerror("4.address family mismatch");
209				  if (nat->in_v[1] == 0 && $5.v != 0)
210					nat->in_v[1] = $5.v;
211				  else if (nat->in_v[0] == 0 && $3.v != 0)
212					nat->in_v[0] = $3.v;
213				  if (nat->in_v[0] == 0 && $5.v != 0)
214					nat->in_v[0] = $5.v;
215				  else if (nat->in_v[1] == 0 && $3.v != 0)
216					nat->in_v[1] = $3.v;
217				  nat->in_osrcatype = $3.t;
218				  bcopy(&$3.a, &nat->in_osrc.na_addr[0],
219					sizeof($3.a));
220				  bcopy(&$3.m, &nat->in_osrc.na_addr[1],
221					sizeof($3.a));
222				  nat->in_nsrcatype = $5.t;
223				  nat->in_nsrcafunc = $5.u;
224				  bcopy(&$5.a, &nat->in_nsrc.na_addr[0],
225					sizeof($5.a));
226				  bcopy(&$5.m, &nat->in_nsrc.na_addr[1],
227					sizeof($5.a));
228
229				  setmapifnames();
230				}
231	| no mapit ifnames addr setproto ';'
232				{ if (nat->in_v[0] == 0)
233					nat->in_v[0] = $4.v;
234				  nat->in_osrcatype = $4.t;
235				  bcopy(&$4.a, &nat->in_osrc.na_addr[0],
236					sizeof($4.a));
237				  bcopy(&$4.m, &nat->in_osrc.na_addr[1],
238					sizeof($4.a));
239
240				  setmapifnames();
241				}
242	| mapit ifnames mapfrom tlate rhsaddr proxy mapoptions
243				{ if ($3 != 0 && $5.f != 0 && $3 != $5.f)
244					yyerror("5.address family mismatch");
245				  if (nat->in_v[0] == 0 && $5.v != 0)
246					nat->in_v[0] = $5.v;
247				  else if (nat->in_v[0] == 0 && $3 != 0)
248					nat->in_v[0] = ftov($3);
249				  if (nat->in_v[1] == 0 && $5.v != 0)
250					nat->in_v[1] = $5.v;
251				  else if (nat->in_v[1] == 0 && $3 != 0)
252					nat->in_v[1] = ftov($3);
253				  nat->in_nsrcatype = $5.t;
254				  nat->in_nsrcafunc = $5.u;
255				  bcopy(&$5.a, &nat->in_nsrc.na_addr[0],
256					sizeof($5.a));
257				  bcopy(&$5.m, &nat->in_nsrc.na_addr[1],
258					sizeof($5.a));
259
260				  setmapifnames();
261				}
262	| no mapit ifnames mapfrom setproto ';'
263				{ nat->in_v[0] = ftov($4);
264				  setmapifnames();
265				}
266	| mapit ifnames mapfrom tlate rhsaddr mapport mapoptions
267				{ if ($3 != 0 && $5.f != 0 && $3 != $5.f)
268					yyerror("6.address family mismatch");
269				  if (nat->in_v[0] == 0 && $5.v != 0)
270					nat->in_v[0] = $5.v;
271				  else if (nat->in_v[0] == 0 && $3 != 0)
272					nat->in_v[0] = ftov($3);
273				  if (nat->in_v[1] == 0 && $5.v != 0)
274					nat->in_v[1] = $5.v;
275				  else if (nat->in_v[1] == 0 && $3 != 0)
276					nat->in_v[1] = ftov($3);
277				  nat->in_nsrcatype = $5.t;
278				  nat->in_nsrcafunc = $5.u;
279				  bcopy(&$5.a, &nat->in_nsrc.na_addr[0],
280					sizeof($5.a));
281				  bcopy(&$5.m, &nat->in_nsrc.na_addr[1],
282					sizeof($5.a));
283
284				  setmapifnames();
285				}
286	;
287
288mapblock:
289	mapblockit ifnames addr tlate addr ports mapoptions
290				{ if ($3.f != 0 && $5.f != 0 && $3.f != $5.f)
291					yyerror("7.address family mismatch");
292				  if (nat->in_v[0] == 0 && $5.v != 0)
293					nat->in_v[0] = $5.v;
294				  else if (nat->in_v[0] == 0 && $3.v != 0)
295					nat->in_v[0] = $3.v;
296				  if (nat->in_v[1] == 0 && $5.v != 0)
297					nat->in_v[1] = $5.v;
298				  else if (nat->in_v[1] == 0 && $3.v != 0)
299					nat->in_v[1] = $3.v;
300				  nat->in_osrcatype = $3.t;
301				  bcopy(&$3.a, &nat->in_osrc.na_addr[0],
302					sizeof($3.a));
303				  bcopy(&$3.m, &nat->in_osrc.na_addr[1],
304					sizeof($3.a));
305				  nat->in_nsrcatype = $5.t;
306				  nat->in_nsrcafunc = $5.u;
307				  bcopy(&$5.a, &nat->in_nsrc.na_addr[0],
308					sizeof($5.a));
309				  bcopy(&$5.m, &nat->in_nsrc.na_addr[1],
310					sizeof($5.a));
311
312				  setmapifnames();
313				}
314	| no mapblockit ifnames { yyexpectaddr = 1; } addr setproto ';'
315				{ if (nat->in_v[0] == 0)
316					nat->in_v[0] = $5.v;
317				  if (nat->in_v[1] == 0)
318					nat->in_v[1] = $5.v;
319				  nat->in_osrcatype = $5.t;
320				  bcopy(&$5.a, &nat->in_osrc.na_addr[0],
321					sizeof($5.a));
322				  bcopy(&$5.m, &nat->in_osrc.na_addr[1],
323					sizeof($5.a));
324
325				  setmapifnames();
326				}
327	;
328
329redir:	rdrit ifnames addr dport tlate dip nport setproto rdroptions
330				{ if ($6 != 0 && $3.f != 0 && $6 != $3.f)
331					yyerror("21.address family mismatch");
332				  if (nat->in_v[0] == 0) {
333					if ($3.v != AF_UNSPEC)
334						nat->in_v[0] = ftov($3.f);
335					  else
336						nat->in_v[0] = ftov($6);
337				  }
338				  nat->in_odstatype = $3.t;
339				  bcopy(&$3.a, &nat->in_odst.na_addr[0],
340					sizeof($3.a));
341				  bcopy(&$3.m, &nat->in_odst.na_addr[1],
342					sizeof($3.a));
343
344				  setrdrifnames();
345				}
346	| no rdrit ifnames addr dport setproto ';'
347				{ if (nat->in_v[0] == 0)
348					nat->in_v[0] = ftov($4.f);
349				  nat->in_odstatype = $4.t;
350				  bcopy(&$4.a, &nat->in_odst.na_addr[0],
351					sizeof($4.a));
352				  bcopy(&$4.m, &nat->in_odst.na_addr[1],
353					sizeof($4.a));
354
355				  setrdrifnames();
356				}
357	| rdrit ifnames rdrfrom tlate dip nport setproto rdroptions
358				{ if ($5 != 0 && $3 != 0 && $5 != $3)
359					yyerror("20.address family mismatch");
360				  if (nat->in_v[0] == 0) {
361					  if ($3 != AF_UNSPEC)
362						nat->in_v[0] = ftov($3);
363					  else
364						nat->in_v[0] = ftov($5);
365				  }
366				  setrdrifnames();
367				}
368	| no rdrit ifnames rdrfrom setproto ';'
369				{ nat->in_v[0] = ftov($4);
370
371				  setrdrifnames();
372				}
373	;
374
375rewrite:
376	IPNY_REWRITE oninout rwrproto mapfrom tlate newdst newopts
377				{ if (nat->in_v[0] == 0)
378					nat->in_v[0] = ftov($4);
379				  if (nat->in_redir & NAT_MAP)
380					setmapifnames();
381				  else
382					setrdrifnames();
383				  nat->in_redir |= NAT_REWRITE;
384				}
385	;
386
387divert:	IPNY_DIVERT oninout rwrproto mapfrom tlate divdst newopts
388				{ if (nat->in_v[0] == 0)
389					nat->in_v[0] = ftov($4);
390				  if (nat->in_redir & NAT_MAP) {
391					setmapifnames();
392					nat->in_pr[0] = IPPROTO_UDP;
393				  } else {
394					setrdrifnames();
395					nat->in_pr[1] = IPPROTO_UDP;
396				  }
397				  nat->in_flags &= ~IPN_TCP;
398				}
399	;
400
401tlate:	IPNY_TLATE		{ yyexpectaddr = 1; }
402	;
403
404pconf:	IPNY_PROXY		{ yysetdict(proxies); }
405	IPNY_DNS '/' proto IPNY_CONFIG YY_STR '{'
406				{ proxy_setconfig(IPNY_DNS); }
407	dnslines ';' '}'
408				{ proxy_addconfig("dns", $5, $7, $10);
409				  proxy_unsetconfig();
410				}
411	;
412
413dnslines:
414	dnsline 		{ $$ = $1; }
415	| dnslines ';' dnsline	{ $$ = $1; $1->na_next = $3; }
416	;
417
418dnsline:
419	IPNY_ALLOW YY_STR	{ $$ = proxy_dns_add_pass(NULL, $2); }
420	| IPNY_DENY YY_STR	{ $$ = proxy_dns_add_block(NULL, $2); }
421	| IPNY_ALLOW '.' YY_STR	{ $$ = proxy_dns_add_pass(".", $3); }
422	| IPNY_DENY '.' YY_STR	{ $$ = proxy_dns_add_block(".", $3); }
423	;
424
425oninout:
426	inout IPNY_ON ifnames	{ ; }
427	;
428
429inout:	IPNY_IN			{ nat->in_redir = NAT_REDIRECT; }
430	| IPNY_OUT		{ nat->in_redir = NAT_MAP; }
431	;
432
433rwrproto:
434	| IPNY_PROTO setproto
435	;
436
437newdst:	src rhsaddr srcports dst erhdaddr dstports
438				{ nat->in_nsrc.na_addr[0] = $2.a;
439				  nat->in_nsrc.na_addr[1] = $2.m;
440				  nat->in_nsrc.na_atype = $2.t;
441				  if ($2.t == FRI_LOOKUP) {
442					nat->in_nsrc.na_type = $2.u;
443					nat->in_nsrc.na_subtype = $2.s;
444					nat->in_nsrc.na_num = $2.n;
445				  }
446				  nat->in_nsports[0] = $3.p1;
447				  nat->in_nsports[1] = $3.p2;
448				  nat->in_ndst.na_addr[0] = $5.a;
449				  nat->in_ndst.na_addr[1] = $5.m;
450				  nat->in_ndst.na_atype = $5.t;
451				  if ($5.t == FRI_LOOKUP) {
452					nat->in_ndst.na_type = $5.u;
453					nat->in_ndst.na_subtype = $5.s;
454					nat->in_ndst.na_num = $5.n;
455				  }
456				  nat->in_ndports[0] = $6.p1;
457				  nat->in_ndports[1] = $6.p2;
458				}
459	;
460
461divdst:	src addr ',' portspec dst addr ',' portspec IPNY_UDP
462				{ nat->in_nsrc.na_addr[0] = $2.a;
463				  if ($2.m.in4.s_addr != 0xffffffff)
464					yyerror("divert must have /32 dest");
465				  nat->in_nsrc.na_addr[1] = $2.m;
466				  nat->in_nsports[0] = $4;
467				  nat->in_nsports[1] = $4;
468
469				  nat->in_ndst.na_addr[0] = $6.a;
470				  nat->in_ndst.na_addr[1] = $6.m;
471				  if ($6.m.in4.s_addr != 0xffffffff)
472					yyerror("divert must have /32 dest");
473				  nat->in_ndports[0] = $8;
474				  nat->in_ndports[1] = $8;
475
476				  nat->in_redir |= NAT_DIVERTUDP;
477				}
478	;
479
480src:	IPNY_SRC		{ yyexpectaddr = 1; }
481	;
482
483dst:	IPNY_DST		{ yyexpectaddr = 1; }
484	;
485
486srcports:
487	comaports		{ $$.p1 = $1.p1;
488				  $$.p2 = $1.p2;
489				}
490	| IPNY_PORT '=' portspec
491				{ $$.p1 = $3;
492				  $$.p2 = $3;
493				  nat->in_flags |= IPN_FIXEDSPORT;
494				}
495	;
496
497dstports:
498	comaports		{ $$.p1 = $1.p1;
499				  $$.p2 = $1.p2;
500				}
501	| IPNY_PORT '=' portspec
502				{ $$.p1 = $3;
503				  $$.p2 = $3;
504				  nat->in_flags |= IPN_FIXEDDPORT;
505				}
506	;
507
508comaports:
509				{ $$.p1 = 0;
510				  $$.p2 = 0;
511				}
512	| ','			{ if (!(nat->in_flags & IPN_TCPUDP))
513					yyerror("must be TCP/UDP for ports");
514				}
515	portpair		{ $$.p1 = $3.p1;
516				  $$.p2 = $3.p2;
517				}
518	;
519
520proxy:	| IPNY_PROXY port portspec YY_STR '/' proto
521			{ int pos;
522			  pos = addname(&nat, $4);
523			  nat->in_plabel = pos;
524			  if (nat->in_dcmp == 0) {
525				nat->in_odport = $3;
526			  } else if ($3 != nat->in_odport) {
527				yyerror("proxy port numbers not consistant");
528			  }
529			  nat->in_ndport = $3;
530			  setnatproto($6);
531			  free($4);
532			}
533	| IPNY_PROXY port YY_STR YY_STR '/' proto
534			{ int pnum, pos;
535			  pos = addname(&nat, $4);
536			  nat->in_plabel = pos;
537			  pnum = getportproto($3, $6);
538			  if (pnum == -1)
539				yyerror("invalid port number");
540			  nat->in_odport = ntohs(pnum);
541			  nat->in_ndport = ntohs(pnum);
542			  setnatproto($6);
543			  free($3);
544			  free($4);
545			}
546	| IPNY_PROXY port portspec YY_STR '/' proto IPNY_CONFIG YY_STR
547			{ int pos;
548			  pos = addname(&nat, $4);
549			  nat->in_plabel = pos;
550			  if (nat->in_dcmp == 0) {
551				nat->in_odport = $3;
552			  } else if ($3 != nat->in_odport) {
553				yyerror("proxy port numbers not consistant");
554			  }
555			  nat->in_ndport = $3;
556			  setnatproto($6);
557			  nat->in_pconfig = addname(&nat, $8);
558			  free($4);
559			  free($8);
560			}
561	| IPNY_PROXY port YY_STR YY_STR '/' proto IPNY_CONFIG YY_STR
562			{ int pnum, pos;
563			  pos = addname(&nat, $4);
564			  nat->in_plabel = pos;
565			  pnum = getportproto($3, $6);
566			  if (pnum == -1)
567				yyerror("invalid port number");
568			  nat->in_odport = ntohs(pnum);
569			  nat->in_ndport = ntohs(pnum);
570			  setnatproto($6);
571			  pos = addname(&nat, $8);
572			  nat->in_pconfig = pos;
573			  free($3);
574			  free($4);
575			  free($8);
576			}
577	;
578setproto:
579	| proto				{ if (nat->in_pr[0] != 0 ||
580					      nat->in_pr[1] != 0 ||
581					      nat->in_flags & IPN_TCPUDP)
582						yyerror("protocol set twice");
583					  setnatproto($1);
584					}
585	| IPNY_TCPUDP			{ if (nat->in_pr[0] != 0 ||
586					      nat->in_pr[1] != 0 ||
587					      nat->in_flags & IPN_TCPUDP)
588						yyerror("protocol set twice");
589					  nat->in_flags |= IPN_TCPUDP;
590					  nat->in_pr[0] = 0;
591					  nat->in_pr[1] = 0;
592					}
593	| IPNY_TCP '/' IPNY_UDP		{ if (nat->in_pr[0] != 0 ||
594					      nat->in_pr[1] != 0 ||
595					      nat->in_flags & IPN_TCPUDP)
596						yyerror("protocol set twice");
597					  nat->in_flags |= IPN_TCPUDP;
598					  nat->in_pr[0] = 0;
599					  nat->in_pr[1] = 0;
600					}
601	;
602
603rhsaddr:
604	addr				{ $$ = $1;
605					  yyexpectaddr = 0;
606					}
607	| hostname '-' { yyexpectaddr = 1; } hostname
608					{ $$.t = FRI_RANGE;
609					  if ($1.f != $4.f)
610						yyerror("8.address family "
611							"mismatch");
612					  $$.f = $1.f;
613					  $$.v = ftov($1.f);
614					  $$.a = $1.a;
615					  $$.m = $4.a;
616					  nat->in_flags |= IPN_SIPRANGE;
617					  yyexpectaddr = 0;
618					}
619	| IPNY_RANGE hostname '-' { yyexpectaddr = 1; } hostname
620					{ $$.t = FRI_RANGE;
621					  if ($2.f != $5.f)
622						yyerror("9.address family "
623							"mismatch");
624					  $$.f = $2.f;
625					  $$.v = ftov($2.f);
626					  $$.a = $2.a;
627					  $$.m = $5.a;
628					  nat->in_flags |= IPN_SIPRANGE;
629					  yyexpectaddr = 0;
630					}
631	;
632
633dip:
634	hostname ',' { yyexpectaddr = 1; } hostname
635				{ nat->in_flags |= IPN_SPLIT;
636				  if ($1.f != $4.f)
637					yyerror("10.address family "
638						"mismatch");
639				  $$ = $1.f;
640				  nat->in_ndstip6 = $1.a;
641				  nat->in_ndstmsk6 = $4.a;
642				  nat->in_ndstatype = FRI_SPLIT;
643				  yyexpectaddr = 0;
644				}
645	| rhdaddr		{ int bits;
646				  nat->in_ndstip6 = $1.a;
647				  nat->in_ndstmsk6 = $1.m;
648				  nat->in_ndst.na_atype = $1.t;
649				  yyexpectaddr = 0;
650				  if ($1.f == AF_INET)
651					bits = count4bits($1.m.in4.s_addr);
652				  else
653					bits = count6bits($1.m.i6);
654				  if (($1.f == AF_INET) && (bits != 0) &&
655				      (bits != 32)) {
656					yyerror("dest ip bitmask not /32");
657				  } else if (($1.f == AF_INET6) &&
658					     (bits != 0) && (bits != 128)) {
659					yyerror("dest ip bitmask not /128");
660				  }
661				  $$ = $1.f;
662				}
663	;
664
665rhdaddr:
666	addr				{ $$ = $1;
667					  yyexpectaddr = 0;
668					}
669	| hostname '-' hostname		{ bzero(&$$, sizeof($$));
670					  $$.t = FRI_RANGE;
671					  if ($1.f != 0 && $3.f != 0 &&
672					      $1.f != $3.f)
673						yyerror("11.address family "
674							"mismatch");
675					  $$.a = $1.a;
676					  $$.m = $3.a;
677					  nat->in_flags |= IPN_DIPRANGE;
678					  yyexpectaddr = 0;
679					}
680	| IPNY_RANGE hostname '-' hostname
681					{ bzero(&$$, sizeof($$));
682					  $$.t = FRI_RANGE;
683					  if ($2.f != 0 && $4.f != 0 &&
684					      $2.f != $4.f)
685						yyerror("12.address family "
686							"mismatch");
687					  $$.a = $2.a;
688					  $$.m = $4.a;
689					  nat->in_flags |= IPN_DIPRANGE;
690					  yyexpectaddr = 0;
691					}
692	;
693
694erhdaddr:
695	rhdaddr				{ $$ = $1; }
696	| IPNY_DSTLIST '/' YY_NUMBER	{ $$.t = FRI_LOOKUP;
697					  $$.u = IPLT_DSTLIST;
698					  $$.s = 0;
699					  $$.n = $3;
700					}
701	| IPNY_DSTLIST '/' YY_STR	{ $$.t = FRI_LOOKUP;
702					  $$.u = IPLT_DSTLIST;
703					  $$.s = 1;
704					  $$.n = addname(&nat, $3);
705					}
706	;
707
708port:	IPNY_PORT			{ suggest_port = 1; }
709	;
710
711portspec:
712	YY_NUMBER			{ if ($1 > 65535)	/* Unsigned */
713						yyerror("invalid port number");
714					  else
715						$$ = $1;
716					}
717	| YY_STR			{ if (getport(NULL, $1,
718						      &($$), NULL) == -1)
719						yyerror("invalid port number");
720					  $$ = ntohs($$);
721					}
722	;
723
724portpair:
725	portspec			{ $$.p1 = $1; $$.p2 = $1; }
726	| portspec '-' portspec		{ $$.p1 = $1; $$.p2 = $3; }
727	| portspec ':' portspec		{ $$.p1 = $1; $$.p2 = $3; }
728	;
729
730dport:	| port portpair			{ nat->in_odport = $2.p1;
731					  if ($2.p2 == 0)
732						nat->in_dtop = $2.p1;
733					  else
734						nat->in_dtop = $2.p2;
735					}
736	;
737
738nport:	| port portpair			{ nat->in_dpmin = $2.p1;
739					  nat->in_dpnext = $2.p1;
740					  nat->in_dpmax = $2.p2;
741					  nat->in_ndport = $2.p1;
742					  if (nat->in_dtop == 0)
743						nat->in_dtop = $2.p2;
744					}
745	| port '=' portspec		{ nat->in_dpmin = $3;
746					  nat->in_dpnext = $3;
747					  nat->in_ndport = $3;
748					  if (nat->in_dtop == 0)
749						nat->in_dtop = nat->in_odport;
750					  nat->in_flags |= IPN_FIXEDDPORT;
751					}
752	;
753
754ports:	| IPNY_PORTS YY_NUMBER		{ nat->in_spmin = $2; }
755	| IPNY_PORTS IPNY_AUTO		{ nat->in_flags |= IPN_AUTOPORTMAP; }
756	;
757
758mapit:	IPNY_MAP			{ nat->in_redir = NAT_MAP; }
759	| IPNY_BIMAP			{ nat->in_redir = NAT_BIMAP; }
760	;
761
762rdrit:	IPNY_RDR			{ nat->in_redir = NAT_REDIRECT; }
763	;
764
765mapblockit:
766	IPNY_MAPBLOCK			{ nat->in_redir = NAT_MAPBLK; }
767	;
768
769mapfrom:
770	from sobject to dobject		{ if ($2 != 0 && $4 != 0 && $2 != $4)
771						yyerror("13.address family "
772							"mismatch");
773					  $$ = $2;
774					}
775	| from sobject '!' to dobject
776					{ if ($2 != 0 && $5 != 0 && $2 != $5)
777						yyerror("14.address family "
778							"mismatch");
779					  nat->in_flags |= IPN_NOTDST;
780					  $$ = $2;
781					}
782	| from sobject to '!' dobject
783					{ if ($2 != 0 && $5 != 0 && $2 != $5)
784						yyerror("15.address family "
785							"mismatch");
786					  nat->in_flags |= IPN_NOTDST;
787					  $$ = $2;
788					}
789	;
790
791rdrfrom:
792	from sobject to dobject		{ if ($2 != 0 && $4 != 0 && $2 != $4)
793						yyerror("16.address family "
794							"mismatch");
795					  $$ = $2;
796					}
797	| '!' from sobject to dobject
798					{ if ($3 != 0 && $5 != 0 && $3 != $5)
799						yyerror("17.address family "
800							"mismatch");
801					  nat->in_flags |= IPN_NOTSRC;
802					  $$ = $3;
803					}
804	| from '!' sobject to dobject
805					{ if ($3 != 0 && $5 != 0 && $3 != $5)
806						yyerror("18.address family "
807							"mismatch");
808					  nat->in_flags |= IPN_NOTSRC;
809					  $$ = $3;
810					}
811	;
812
813from:	IPNY_FROM			{ nat->in_flags |= IPN_FILTER;
814					  yyexpectaddr = 1;
815					}
816	;
817
818to:	IPNY_TO				{ yyexpectaddr = 1; }
819	;
820
821ifnames:
822	ifname family			{ yyexpectaddr = 1; }
823	| ifname ',' otherifname family	{ yyexpectaddr = 1; }
824	;
825
826ifname:	YY_STR				{ setifname(&nat, 0, $1);
827					  free($1);
828					}
829	;
830
831family:	| IPNY_INET			{ nat->in_v[0] = 4; nat->in_v[1] = 4; }
832	| IPNY_INET6			{ nat->in_v[0] = 6; nat->in_v[1] = 6; }
833	;
834
835otherifname:
836	YY_STR				{ setifname(&nat, 1, $1);
837					  free($1);
838					}
839	;
840
841mapport:
842	IPNY_PORTMAP tcpudp portpair sequential
843					{ nat->in_spmin = $3.p1;
844					  nat->in_spmax = $3.p2;
845					}
846	| IPNY_PORTMAP portpair tcpudp sequential
847					{ nat->in_spmin = $2.p1;
848					  nat->in_spmax = $2.p2;
849					}
850	| IPNY_PORTMAP tcpudp IPNY_AUTO sequential
851					{ nat->in_flags |= IPN_AUTOPORTMAP;
852					  nat->in_spmin = 1024;
853					  nat->in_spmax = 65535;
854					}
855	| IPNY_ICMPIDMAP YY_STR portpair sequential
856			{ if (strcmp($2, "icmp") != 0 &&
857			      strcmp($2, "ipv6-icmp") != 0) {
858				yyerror("icmpidmap not followed by icmp");
859			  }
860			  free($2);
861			  if ($3.p1 < 0 || $3.p1 > 65535)
862				yyerror("invalid 1st ICMP Id number");
863			  if ($3.p2 < 0 || $3.p2 > 65535)
864				yyerror("invalid 2nd ICMP Id number");
865			  if (strcmp($2, "ipv6-icmp") == 0) {
866				nat->in_pr[0] = IPPROTO_ICMPV6;
867				nat->in_pr[1] = IPPROTO_ICMPV6;
868			  } else {
869				nat->in_pr[0] = IPPROTO_ICMP;
870				nat->in_pr[1] = IPPROTO_ICMP;
871			  }
872			  nat->in_flags = IPN_ICMPQUERY;
873			  nat->in_spmin = $3.p1;
874			  nat->in_spmax = $3.p2;
875			}
876	;
877
878sobject:
879	saddr				{ $$ = $1; }
880	| saddr port portstuff		{ nat->in_osport = $3.p1;
881					  nat->in_stop = $3.p2;
882					  nat->in_scmp = $3.pc;
883					  $$ = $1;
884					}
885	;
886
887saddr:	addr				{ nat->in_osrcatype = $1.t;
888					  bcopy(&$1.a,
889						&nat->in_osrc.na_addr[0],
890						sizeof($1.a));
891					  bcopy(&$1.m,
892						&nat->in_osrc.na_addr[1],
893						sizeof($1.m));
894					  $$ = $1.f;
895					}
896	;
897
898dobject:
899	daddr				{ $$ = $1; }
900	| daddr port portstuff		{ nat->in_odport = $3.p1;
901					  nat->in_dtop = $3.p2;
902					  nat->in_dcmp = $3.pc;
903					  $$ = $1;
904					}
905	;
906
907daddr:	addr				{ nat->in_odstatype = $1.t;
908					  bcopy(&$1.a,
909						&nat->in_odst.na_addr[0],
910						sizeof($1.a));
911					  bcopy(&$1.m,
912						&nat->in_odst.na_addr[1],
913						sizeof($1.m));
914					  $$ = $1.f;
915					}
916	;
917
918addr:	IPNY_ANY			{ yyexpectaddr = 0;
919					  bzero(&$$, sizeof($$));
920					  $$.t = FRI_NORMAL;
921					}
922	| hostname			{ bzero(&$$, sizeof($$));
923					  $$.a = $1.a;
924					  $$.t = FRI_NORMAL;
925					  $$.v = ftov($1.f);
926					  $$.f = $1.f;
927					  if ($$.f == AF_INET) {
928						  $$.m.in4.s_addr = 0xffffffff;
929					  } else if ($$.f == AF_INET6) {
930						  $$.m.i6[0] = 0xffffffff;
931						  $$.m.i6[1] = 0xffffffff;
932						  $$.m.i6[2] = 0xffffffff;
933						  $$.m.i6[3] = 0xffffffff;
934					  }
935					  yyexpectaddr = 0;
936					}
937	| hostname slash YY_NUMBER
938					{ bzero(&$$, sizeof($$));
939					  $$.a = $1.a;
940					  $$.f = $1.f;
941					  $$.v = ftov($1.f);
942					  $$.t = FRI_NORMAL;
943					  ntomask($$.f, $3, (u_32_t *)&$$.m);
944					  $$.a.i6[0] &= $$.m.i6[0];
945					  $$.a.i6[1] &= $$.m.i6[1];
946					  $$.a.i6[2] &= $$.m.i6[2];
947					  $$.a.i6[3] &= $$.m.i6[3];
948					  yyexpectaddr = 0;
949					}
950	| hostname slash ipaddr		{ bzero(&$$, sizeof($$));
951					  if ($1.f != $3.f) {
952						yyerror("1.address family "
953							"mismatch");
954					  }
955					  $$.a = $1.a;
956					  $$.m = $3.a;
957					  $$.t = FRI_NORMAL;
958					  $$.a.i6[0] &= $$.m.i6[0];
959					  $$.a.i6[1] &= $$.m.i6[1];
960					  $$.a.i6[2] &= $$.m.i6[2];
961					  $$.a.i6[3] &= $$.m.i6[3];
962					  $$.f = $1.f;
963					  $$.v = ftov($1.f);
964					  yyexpectaddr = 0;
965					}
966	| hostname slash hexnumber	{ bzero(&$$, sizeof($$));
967					  $$.a = $1.a;
968					  $$.m.in4.s_addr = htonl($3);
969					  $$.t = FRI_NORMAL;
970					  $$.a.in4.s_addr &= $$.m.in4.s_addr;
971					  $$.f = $1.f;
972					  $$.v = ftov($1.f);
973					  if ($$.f == AF_INET6)
974						yyerror("incorrect inet6 mask");
975					}
976	| hostname mask ipaddr		{ bzero(&$$, sizeof($$));
977					  if ($1.f != $3.f) {
978						yyerror("2.address family "
979							"mismatch");
980					  }
981					  $$.a = $1.a;
982					  $$.m = $3.a;
983					  $$.t = FRI_NORMAL;
984					  $$.a.i6[0] &= $$.m.i6[0];
985					  $$.a.i6[1] &= $$.m.i6[1];
986					  $$.a.i6[2] &= $$.m.i6[2];
987					  $$.a.i6[3] &= $$.m.i6[3];
988					  $$.f = $1.f;
989					  $$.v = ftov($1.f);
990					  yyexpectaddr = 0;
991					}
992	| hostname mask hexnumber	{ bzero(&$$, sizeof($$));
993					  $$.a = $1.a;
994					  $$.m.in4.s_addr = htonl($3);
995					  $$.t = FRI_NORMAL;
996					  $$.a.in4.s_addr &= $$.m.in4.s_addr;
997					  $$.f = AF_INET;
998					  $$.v = 4;
999					}
1000	| pool slash YY_NUMBER		{ bzero(&$$, sizeof($$));
1001					  $$.a.iplookupnum = $3;
1002					  $$.a.iplookuptype = IPLT_POOL;
1003					  $$.a.iplookupsubtype = 0;
1004					  $$.t = FRI_LOOKUP;
1005					}
1006	| pool slash YY_STR		{ bzero(&$$, sizeof($$));
1007					  $$.a.iplookupname = addname(&nat,$3);
1008					  $$.a.iplookuptype = IPLT_POOL;
1009					  $$.a.iplookupsubtype = 1;
1010					  $$.t = FRI_LOOKUP;
1011					}
1012	| hash slash YY_NUMBER		{ bzero(&$$, sizeof($$));
1013					  $$.a.iplookupnum = $3;
1014					  $$.a.iplookuptype = IPLT_HASH;
1015					  $$.a.iplookupsubtype = 0;
1016					  $$.t = FRI_LOOKUP;
1017					}
1018	| hash slash YY_STR		{ bzero(&$$, sizeof($$));
1019					  $$.a.iplookupname = addname(&nat,$3);
1020					  $$.a.iplookuptype = IPLT_HASH;
1021					  $$.a.iplookupsubtype = 1;
1022					  $$.t = FRI_LOOKUP;
1023					}
1024	;
1025
1026slash:	'/'				{ yyexpectaddr = 0; }
1027	;
1028
1029mask:	IPNY_MASK			{ yyexpectaddr = 0; }
1030	;
1031
1032pool:	IPNY_POOL			{ if (!(nat->in_flags & IPN_FILTER)) {
1033						yyerror("Can only use pool with from/to rules\n");
1034					  }
1035					  yyexpectaddr = 0;
1036					  yyresetdict();
1037					}
1038	;
1039
1040hash:	IPNY_HASH			{ if (!(nat->in_flags & IPN_FILTER)) {
1041						yyerror("Can only use hash with from/to rules\n");
1042					  }
1043					  yyexpectaddr = 0;
1044					  yyresetdict();
1045					}
1046	;
1047
1048portstuff:
1049	compare portspec		{ $$.pc = $1; $$.p1 = $2; $$.p2 = 0; }
1050	| portspec range portspec	{ $$.pc = $2; $$.p1 = $1; $$.p2 = $3; }
1051	;
1052
1053mapoptions:
1054	rr frag age mssclamp nattag setproto purge
1055	;
1056
1057rdroptions:
1058	rr frag age sticky mssclamp rdrproxy nattag purge
1059	;
1060
1061nattag:	| IPNY_TAG YY_STR		{ strncpy(nat->in_tag.ipt_tag, $2,
1062						  sizeof(nat->in_tag.ipt_tag));
1063					}
1064rr:	| IPNY_ROUNDROBIN		{ nat->in_flags |= IPN_ROUNDR; }
1065	;
1066
1067frag:	| IPNY_FRAG			{ nat->in_flags |= IPN_FRAG; }
1068	;
1069
1070age:	| IPNY_AGE YY_NUMBER			{ nat->in_age[0] = $2;
1071						  nat->in_age[1] = $2; }
1072	| IPNY_AGE YY_NUMBER '/' YY_NUMBER	{ nat->in_age[0] = $2;
1073						  nat->in_age[1] = $4; }
1074	;
1075
1076sticky: | IPNY_STICKY			{ if (!(nat->in_flags & IPN_ROUNDR) &&
1077					      !(nat->in_flags & IPN_SPLIT)) {
1078						FPRINTF(stderr,
1079		"'sticky' for use with round-robin/IP splitting only\n");
1080					  } else
1081						nat->in_flags |= IPN_STICKY;
1082					}
1083	;
1084
1085mssclamp:
1086	| IPNY_MSSCLAMP YY_NUMBER		{ nat->in_mssclamp = $2; }
1087	;
1088
1089tcpudp:	IPNY_TCP			{ setnatproto(IPPROTO_TCP); }
1090	| IPNY_UDP			{ setnatproto(IPPROTO_UDP); }
1091	| IPNY_TCPUDP			{ nat->in_flags |= IPN_TCPUDP;
1092					  nat->in_pr[0] = 0;
1093					  nat->in_pr[1] = 0;
1094					}
1095	| IPNY_TCP '/' IPNY_UDP		{ nat->in_flags |= IPN_TCPUDP;
1096					  nat->in_pr[0] = 0;
1097					  nat->in_pr[1] = 0;
1098					}
1099	;
1100
1101sequential:
1102	| IPNY_SEQUENTIAL		{ nat->in_flags |= IPN_SEQUENTIAL; }
1103	;
1104
1105purge:
1106	| IPNY_PURGE			{ nat->in_flags |= IPN_PURGE; }
1107	;
1108
1109rdrproxy:
1110	IPNY_PROXY YY_STR
1111					{ int pos;
1112					  pos = addname(&nat, $2);
1113					  nat->in_plabel = pos;
1114					  nat->in_odport = nat->in_dpnext;
1115					  nat->in_dtop = nat->in_odport;
1116					  free($2);
1117					}
1118	| proxy			{ if (nat->in_plabel != -1) {
1119					nat->in_ndport = nat->in_odport;
1120					nat->in_dpmin = nat->in_odport;
1121					nat->in_dpmax = nat->in_dpmin;
1122					nat->in_dtop = nat->in_dpmin;
1123					nat->in_dpnext = nat->in_dpmin;
1124				  }
1125				}
1126	;
1127
1128newopts:
1129	| IPNY_PURGE			{ nat->in_flags |= IPN_PURGE; }
1130	;
1131
1132proto:	YY_NUMBER			{ $$ = $1;
1133					  if ($$ != IPPROTO_TCP &&
1134					      $$ != IPPROTO_UDP)
1135						suggest_port = 0;
1136					}
1137	| IPNY_TCP			{ $$ = IPPROTO_TCP; }
1138	| IPNY_UDP			{ $$ = IPPROTO_UDP; }
1139	| YY_STR			{ $$ = getproto($1);
1140					  free($1);
1141					  if ($$ == -1)
1142						yyerror("unknown protocol");
1143					  if ($$ != IPPROTO_TCP &&
1144					      $$ != IPPROTO_UDP)
1145						suggest_port = 0;
1146					}
1147	;
1148
1149hexnumber:
1150	YY_HEX				{ $$ = $1; }
1151	;
1152
1153hostname:
1154	YY_STR				{ i6addr_t addr;
1155					  int family;
1156
1157#ifdef USE_INET6
1158					  if (nat->in_v[0] == 6)
1159						family = AF_INET6;
1160					  else
1161#endif
1162						family = AF_INET;
1163					  memset(&($$), 0, sizeof($$));
1164					  memset(&addr, 0, sizeof(addr));
1165					  $$.f = family;
1166					  if (gethost(family, $1,
1167						      &addr) == 0) {
1168						$$.a = addr;
1169					  } else {
1170						FPRINTF(stderr,
1171							"Unknown host '%s'\n",
1172							$1);
1173					  }
1174					  free($1);
1175					}
1176	| YY_NUMBER			{ memset(&($$), 0, sizeof($$));
1177					  $$.a.in4.s_addr = htonl($1);
1178					  if ($$.a.in4.s_addr != 0)
1179						$$.f = AF_INET;
1180					}
1181	| ipv4				{ $$ = $1; }
1182	| YY_IPV6			{ memset(&($$), 0, sizeof($$));
1183					  $$.a = $1;
1184					  $$.f = AF_INET6;
1185					}
1186	| YY_NUMBER YY_IPV6		{ memset(&($$), 0, sizeof($$));
1187					  $$.a = $2;
1188					  $$.f = AF_INET6;
1189					}
1190	;
1191
1192compare:
1193	'='				{ $$ = FR_EQUAL; }
1194	| YY_CMP_EQ			{ $$ = FR_EQUAL; }
1195	| YY_CMP_NE			{ $$ = FR_NEQUAL; }
1196	| YY_CMP_LT			{ $$ = FR_LESST; }
1197	| YY_CMP_LE			{ $$ = FR_LESSTE; }
1198	| YY_CMP_GT			{ $$ = FR_GREATERT; }
1199	| YY_CMP_GE			{ $$ = FR_GREATERTE; }
1200
1201range:
1202	YY_RANGE_OUT			{ $$ = FR_OUTRANGE; }
1203	| YY_RANGE_IN			{ $$ = FR_INRANGE; }
1204	| ':'				{ $$ = FR_INCRANGE; }
1205	;
1206
1207ipaddr:	ipv4				{ $$ = $1; }
1208	| YY_IPV6			{ $$.a = $1;
1209					  $$.f = AF_INET6;
1210					}
1211	;
1212
1213ipv4:	YY_NUMBER '.' YY_NUMBER '.' YY_NUMBER '.' YY_NUMBER
1214		{ if ($1 > 255 || $3 > 255 || $5 > 255 || $7 > 255) {
1215			yyerror("Invalid octet string for IP address");
1216			return 0;
1217		  }
1218		  bzero((char *)&$$, sizeof($$));
1219		  $$.a.in4.s_addr = ($1 << 24) | ($3 << 16) | ($5 << 8) | $7;
1220		  $$.a.in4.s_addr = htonl($$.a.in4.s_addr);
1221		  $$.f = AF_INET;
1222		}
1223	;
1224
1225%%
1226
1227
1228static	wordtab_t	proxies[] = {
1229	{ "dns",	IPNY_DNS }
1230};
1231
1232static	wordtab_t	dnswords[] = {
1233	{ "allow",	IPNY_ALLOW },
1234	{ "block",	IPNY_DENY },
1235	{ "deny",	IPNY_DENY },
1236	{ "drop",	IPNY_DENY },
1237	{ "pass",	IPNY_ALLOW },
1238
1239};
1240
1241static	wordtab_t	yywords[] = {
1242	{ "age",	IPNY_AGE },
1243	{ "any",	IPNY_ANY },
1244	{ "auto",	IPNY_AUTO },
1245	{ "bimap",	IPNY_BIMAP },
1246	{ "config",	IPNY_CONFIG },
1247	{ "divert",	IPNY_DIVERT },
1248	{ "dst",	IPNY_DST },
1249	{ "dstlist",	IPNY_DSTLIST },
1250	{ "frag",	IPNY_FRAG },
1251	{ "from",	IPNY_FROM },
1252	{ "hash",	IPNY_HASH },
1253	{ "icmpidmap",	IPNY_ICMPIDMAP },
1254	{ "in",		IPNY_IN },
1255	{ "inet",	IPNY_INET },
1256	{ "inet6",	IPNY_INET6 },
1257	{ "mask",	IPNY_MASK },
1258	{ "map",	IPNY_MAP },
1259	{ "map-block",	IPNY_MAPBLOCK },
1260	{ "mssclamp",	IPNY_MSSCLAMP },
1261	{ "netmask",	IPNY_MASK },
1262	{ "no",		IPNY_NO },
1263	{ "on",		IPNY_ON },
1264	{ "out",	IPNY_OUT },
1265	{ "pool",	IPNY_POOL },
1266	{ "port",	IPNY_PORT },
1267	{ "portmap",	IPNY_PORTMAP },
1268	{ "ports",	IPNY_PORTS },
1269	{ "proto",	IPNY_PROTO },
1270	{ "proxy",	IPNY_PROXY },
1271	{ "purge",	IPNY_PURGE },
1272	{ "range",	IPNY_RANGE },
1273	{ "rewrite",	IPNY_REWRITE },
1274	{ "rdr",	IPNY_RDR },
1275	{ "round-robin",IPNY_ROUNDROBIN },
1276	{ "sequential",	IPNY_SEQUENTIAL },
1277	{ "src",	IPNY_SRC },
1278	{ "sticky",	IPNY_STICKY },
1279	{ "tag",	IPNY_TAG },
1280	{ "tcp",	IPNY_TCP },
1281	{ "tcpudp",	IPNY_TCPUDP },
1282	{ "to",		IPNY_TO },
1283	{ "udp",	IPNY_UDP },
1284	{ "-",		'-' },
1285	{ "->",		IPNY_TLATE },
1286	{ "eq",		YY_CMP_EQ },
1287	{ "ne",		YY_CMP_NE },
1288	{ "lt",		YY_CMP_LT },
1289	{ "gt",		YY_CMP_GT },
1290	{ "le",		YY_CMP_LE },
1291	{ "ge",		YY_CMP_GE },
1292	{ NULL,		0 }
1293};
1294
1295
1296int
1297ipnat_parsefile(fd, addfunc, ioctlfunc, filename)
1298	int fd;
1299	addfunc_t addfunc;
1300	ioctlfunc_t ioctlfunc;
1301	char *filename;
1302{
1303	FILE *fp = NULL;
1304	int rval;
1305	char *s;
1306
1307	yylineNum = 1;
1308
1309	(void) yysettab(yywords);
1310
1311	s = getenv("YYDEBUG");
1312	if (s)
1313		yydebug = atoi(s);
1314	else
1315		yydebug = 0;
1316
1317	if (strcmp(filename, "-")) {
1318		fp = fopen(filename, "r");
1319		if (!fp) {
1320			FPRINTF(stderr, "fopen(%s) failed: %s\n", filename,
1321				STRERROR(errno));
1322			return -1;
1323		}
1324	} else
1325		fp = stdin;
1326
1327	while ((rval = ipnat_parsesome(fd, addfunc, ioctlfunc, fp)) == 0)
1328		;
1329	if (fp != NULL)
1330		fclose(fp);
1331	if (rval == -1)
1332		rval = 0;
1333	else if (rval != 0)
1334		rval = 1;
1335	return rval;
1336}
1337
1338
1339int
1340ipnat_parsesome(fd, addfunc, ioctlfunc, fp)
1341	int fd;
1342	addfunc_t addfunc;
1343	ioctlfunc_t ioctlfunc;
1344	FILE *fp;
1345{
1346	char *s;
1347	int i;
1348
1349	natfd = fd;
1350	parser_error = 0;
1351	nataddfunc = addfunc;
1352	natioctlfunc = ioctlfunc;
1353
1354	if (feof(fp))
1355		return -1;
1356	i = fgetc(fp);
1357	if (i == EOF)
1358		return -1;
1359	if (ungetc(i, fp) == EOF)
1360		return -1;
1361	if (feof(fp))
1362		return -1;
1363	s = getenv("YYDEBUG");
1364	if (s)
1365		yydebug = atoi(s);
1366	else
1367		yydebug = 0;
1368
1369	yyin = fp;
1370	yyparse();
1371	return parser_error;
1372}
1373
1374
1375static void
1376newnatrule()
1377{
1378	ipnat_t *n;
1379
1380	n = calloc(1, sizeof(*n));
1381	if (n == NULL)
1382		return;
1383
1384	if (nat == NULL) {
1385		nattop = nat = n;
1386		n->in_pnext = &nattop;
1387	} else {
1388		nat->in_next = n;
1389		n->in_pnext = &nat->in_next;
1390		nat = n;
1391	}
1392
1393	n->in_flineno = yylineNum;
1394	n->in_ifnames[0] = -1;
1395	n->in_ifnames[1] = -1;
1396	n->in_plabel = -1;
1397	n->in_pconfig = -1;
1398	n->in_size = sizeof(*n);
1399
1400	suggest_port = 0;
1401}
1402
1403
1404static void
1405setnatproto(p)
1406	int p;
1407{
1408	nat->in_pr[0] = p;
1409	nat->in_pr[1] = p;
1410
1411	switch (p)
1412	{
1413	case IPPROTO_TCP :
1414		nat->in_flags |= IPN_TCP;
1415		nat->in_flags &= ~IPN_UDP;
1416		break;
1417	case IPPROTO_UDP :
1418		nat->in_flags |= IPN_UDP;
1419		nat->in_flags &= ~IPN_TCP;
1420		break;
1421#ifdef USE_INET6
1422	case IPPROTO_ICMPV6 :
1423#endif
1424	case IPPROTO_ICMP :
1425		nat->in_flags &= ~IPN_TCPUDP;
1426		if (!(nat->in_flags & IPN_ICMPQUERY) &&
1427		    !(nat->in_redir & NAT_DIVERTUDP)) {
1428			nat->in_dcmp = 0;
1429			nat->in_scmp = 0;
1430			nat->in_dpmin = 0;
1431			nat->in_dpmax = 0;
1432			nat->in_dpnext = 0;
1433			nat->in_spmin = 0;
1434			nat->in_spmax = 0;
1435			nat->in_spnext = 0;
1436		}
1437		break;
1438	default :
1439		if ((nat->in_redir & NAT_MAPBLK) == 0) {
1440			nat->in_flags &= ~IPN_TCPUDP;
1441			nat->in_dcmp = 0;
1442			nat->in_scmp = 0;
1443			nat->in_dpmin = 0;
1444			nat->in_dpmax = 0;
1445			nat->in_dpnext = 0;
1446			nat->in_spmin = 0;
1447			nat->in_spmax = 0;
1448			nat->in_spnext = 0;
1449		}
1450		break;
1451	}
1452
1453	if ((nat->in_flags & (IPN_TCP|IPN_UDP)) == 0) {
1454		nat->in_stop = 0;
1455		nat->in_dtop = 0;
1456		nat->in_osport = 0;
1457		nat->in_odport = 0;
1458		nat->in_stop = 0;
1459		nat->in_osport = 0;
1460		nat->in_dtop = 0;
1461		nat->in_odport = 0;
1462	}
1463	if ((nat->in_flags & (IPN_TCPUDP|IPN_FIXEDDPORT)) == IPN_FIXEDDPORT)
1464		nat->in_flags &= ~IPN_FIXEDDPORT;
1465}
1466
1467
1468int
1469ipnat_addrule(fd, ioctlfunc, ptr)
1470	int fd;
1471	ioctlfunc_t ioctlfunc;
1472	void *ptr;
1473{
1474	ioctlcmd_t add, del;
1475	ipfobj_t obj;
1476	ipnat_t *ipn;
1477
1478	ipn = ptr;
1479	bzero((char *)&obj, sizeof(obj));
1480	obj.ipfo_rev = IPFILTER_VERSION;
1481	obj.ipfo_size = ipn->in_size;
1482	obj.ipfo_type = IPFOBJ_IPNAT;
1483	obj.ipfo_ptr = ptr;
1484
1485	if ((opts & OPT_DONOTHING) != 0)
1486		fd = -1;
1487
1488	if (opts & OPT_ZERORULEST) {
1489		add = SIOCZRLST;
1490		del = 0;
1491	} else if (opts & OPT_PURGE) {
1492		add = 0;
1493		del = SIOCPURGENAT;
1494	} else {
1495		add = SIOCADNAT;
1496		del = SIOCRMNAT;
1497	}
1498
1499	if ((opts & OPT_VERBOSE) != 0)
1500		printnat(ipn, opts);
1501
1502	if (opts & OPT_DEBUG)
1503		binprint(ipn, ipn->in_size);
1504
1505	if ((opts & OPT_ZERORULEST) != 0) {
1506		if ((*ioctlfunc)(fd, add, (void *)&obj) == -1) {
1507			if ((opts & OPT_DONOTHING) == 0) {
1508				char msg[80];
1509
1510				sprintf(msg, "%d:ioctl(zero nat rule)",
1511					ipn->in_flineno);
1512				return ipf_perror_fd(fd, ioctlfunc, msg);
1513			}
1514		} else {
1515			PRINTF("hits %lu ", ipn->in_hits);
1516#ifdef USE_QUAD_T
1517			PRINTF("bytes %"PRIu64" ",
1518			       ipn->in_bytes[0] + ipn->in_bytes[1]);
1519#else
1520			PRINTF("bytes %lu ",
1521			       ipn->in_bytes[0] + ipn->in_bytes[1]);
1522#endif
1523			printnat(ipn, opts);
1524		}
1525	} else if ((opts & OPT_REMOVE) != 0) {
1526		if ((*ioctlfunc)(fd, del, (void *)&obj) == -1) {
1527			if ((opts & OPT_DONOTHING) == 0) {
1528				char msg[80];
1529
1530				sprintf(msg, "%d:ioctl(delete nat rule)",
1531					ipn->in_flineno);
1532				return ipf_perror_fd(fd, ioctlfunc, msg);
1533			}
1534		}
1535	} else {
1536		if ((*ioctlfunc)(fd, add, (void *)&obj) == -1) {
1537			if ((opts & OPT_DONOTHING) == 0) {
1538				char msg[80];
1539
1540				sprintf(msg, "%d:ioctl(add/insert nat rule)",
1541					ipn->in_flineno);
1542				if (errno == EEXIST) {
1543					sprintf(msg + strlen(msg), "(line %d)",
1544						ipn->in_flineno);
1545				}
1546				return ipf_perror_fd(fd, ioctlfunc, msg);
1547			}
1548		}
1549	}
1550	return 0;
1551}
1552
1553
1554static void
1555setmapifnames()
1556{
1557	if (nat->in_ifnames[1] == -1)
1558		nat->in_ifnames[1] = nat->in_ifnames[0];
1559
1560	if ((suggest_port == 1) && (nat->in_flags & IPN_TCPUDP) == 0)
1561		nat->in_flags |= IPN_TCPUDP;
1562
1563	if ((nat->in_flags & IPN_TCPUDP) == 0)
1564		setnatproto(nat->in_pr[1]);
1565
1566	if (((nat->in_redir & NAT_MAPBLK) != 0) ||
1567	      ((nat->in_flags & IPN_AUTOPORTMAP) != 0))
1568		nat_setgroupmap(nat);
1569}
1570
1571
1572static void
1573setrdrifnames()
1574{
1575	if ((suggest_port == 1) && (nat->in_flags & IPN_TCPUDP) == 0)
1576		nat->in_flags |= IPN_TCPUDP;
1577
1578	if ((nat->in_pr[0] == 0) && ((nat->in_flags & IPN_TCPUDP) == 0) &&
1579	    (nat->in_dpmin != 0 || nat->in_dpmax != 0 || nat->in_dpnext != 0))
1580		setnatproto(IPPROTO_TCP);
1581
1582	if (nat->in_ifnames[1] == -1)
1583		nat->in_ifnames[1] = nat->in_ifnames[0];
1584}
1585
1586
1587static void
1588proxy_setconfig(proxy)
1589	int proxy;
1590{
1591	if (proxy == IPNY_DNS) {
1592		yysetfixeddict(dnswords);
1593	}
1594}
1595
1596
1597static void
1598proxy_unsetconfig()
1599{
1600	yyresetdict();
1601}
1602
1603
1604static namelist_t *
1605proxy_dns_add_pass(prefix, name)
1606	char *prefix, *name;
1607{
1608	namelist_t *n;
1609
1610	n = calloc(1, sizeof(*n));
1611	if (n != NULL) {
1612		if (prefix == NULL || *prefix == '\0') {
1613			n->na_name = strdup(name);
1614		} else {
1615			n->na_name = malloc(strlen(name) + strlen(prefix) + 1);
1616			strcpy(n->na_name, prefix);
1617			strcat(n->na_name, name);
1618		}
1619	}
1620	return n;
1621}
1622
1623
1624static namelist_t *
1625proxy_dns_add_block(prefix, name)
1626	char *prefix, *name;
1627{
1628	namelist_t *n;
1629
1630	n = calloc(1, sizeof(*n));
1631	if (n != NULL) {
1632		if (prefix == NULL || *prefix == '\0') {
1633			n->na_name = strdup(name);
1634		} else {
1635			n->na_name = malloc(strlen(name) + strlen(prefix) + 1);
1636			strcpy(n->na_name, prefix);
1637			strcat(n->na_name, name);
1638		}
1639		n->na_value = 1;
1640	}
1641	return n;
1642}
1643
1644
1645static void
1646proxy_addconfig(proxy, proto, conf, list)
1647	char *proxy, *conf;
1648	int proto;
1649	namelist_t *list;
1650{
1651	proxyrule_t *pr;
1652
1653	pr = calloc(1, sizeof(*pr));
1654	if (pr != NULL) {
1655		pr->pr_proto = proto;
1656		pr->pr_proxy = proxy;
1657		pr->pr_conf = conf;
1658		pr->pr_names = list;
1659		pr->pr_next = prules;
1660		prules = pr;
1661	}
1662}
1663
1664
1665static void
1666proxy_loadrules(fd, ioctlfunc, rules)
1667	int fd;
1668	ioctlfunc_t ioctlfunc;
1669	proxyrule_t *rules;
1670{
1671	proxyrule_t *pr;
1672
1673	while ((pr = rules) != NULL) {
1674		proxy_loadconfig(fd, ioctlfunc, pr->pr_proxy, pr->pr_proto,
1675				 pr->pr_conf, pr->pr_names);
1676		rules = pr->pr_next;
1677		free(pr->pr_conf);
1678		free(pr);
1679	}
1680}
1681
1682
1683static void
1684proxy_loadconfig(fd, ioctlfunc, proxy, proto, conf, list)
1685	int fd;
1686	ioctlfunc_t ioctlfunc;
1687	char *proxy, *conf;
1688	int proto;
1689	namelist_t *list;
1690{
1691	namelist_t *na;
1692	ipfobj_t obj;
1693	ap_ctl_t pcmd;
1694
1695	obj.ipfo_rev = IPFILTER_VERSION;
1696	obj.ipfo_type = IPFOBJ_PROXYCTL;
1697	obj.ipfo_size = sizeof(pcmd);
1698	obj.ipfo_ptr = &pcmd;
1699
1700	while ((na = list) != NULL) {
1701		if ((opts & OPT_REMOVE) != 0)
1702			pcmd.apc_cmd = APC_CMD_DEL;
1703		else
1704			pcmd.apc_cmd = APC_CMD_ADD;
1705		pcmd.apc_dsize = strlen(na->na_name) + 1;
1706		pcmd.apc_data = na->na_name;
1707		pcmd.apc_arg = na->na_value;
1708		pcmd.apc_p = proto;
1709
1710		strncpy(pcmd.apc_label, proxy, APR_LABELLEN);
1711		pcmd.apc_label[APR_LABELLEN - 1] = '\0';
1712
1713		strncpy(pcmd.apc_config, conf, APR_LABELLEN);
1714		pcmd.apc_config[APR_LABELLEN - 1] = '\0';
1715
1716		if ((*ioctlfunc)(fd, SIOCPROXY, (void *)&obj) == -1) {
1717                        if ((opts & OPT_DONOTHING) == 0) {
1718                                char msg[80];
1719
1720                                sprintf(msg, "%d:ioctl(add/remove proxy rule)",
1721					yylineNum);
1722                                ipf_perror_fd(fd, ioctlfunc, msg);
1723				return;
1724                        }
1725		}
1726
1727		list = na->na_next;
1728		free(na->na_name);
1729		free(na);
1730	}
1731}
1732
1733
1734static void
1735setifname(np, idx, name)
1736	ipnat_t **np;
1737	int idx;
1738	char *name;
1739{
1740	int pos;
1741
1742	pos = addname(np, name);
1743	if (pos == -1)
1744		return;
1745	(*np)->in_ifnames[idx] = pos;
1746}
1747
1748
1749static int
1750addname(np, name)
1751	ipnat_t **np;
1752	char *name;
1753{
1754	ipnat_t *n;
1755	int nlen;
1756	int pos;
1757
1758	nlen = strlen(name) + 1;
1759	n = realloc(*np, (*np)->in_size + nlen);
1760	if (*np == nattop)
1761		nattop = n;
1762	*np = n;
1763	if (n == NULL)
1764		return -1;
1765	if (n->in_pnext != NULL)
1766		*n->in_pnext = n;
1767	n->in_size += nlen;
1768	pos = n->in_namelen;
1769	n->in_namelen += nlen;
1770	strcpy(n->in_names + pos, name);
1771	n->in_names[n->in_namelen] = '\0';
1772	return pos;
1773}
1774