xref: /freebsd/sbin/ipf/ipnat/ipnat_y.y (revision d93b4d32034df7cd70e80b496e8fe8c1bc57c629)
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 
38 extern	void	yyerror(char *);
39 extern	int	yyparse(void);
40 extern	int	yylex(void);
41 extern	int	yydebug;
42 extern	FILE	*yyin;
43 extern	int	yylineNum;
44 
45 static	ipnat_t		*nattop = NULL;
46 static	ipnat_t		*nat = NULL;
47 static	int		natfd = -1;
48 static	ioctlfunc_t	natioctlfunc = NULL;
49 static	addfunc_t	nataddfunc = NULL;
50 static	int		suggest_port = 0;
51 static	proxyrule_t	*prules = NULL;
52 static	int		parser_error = 0;
53 
54 static	void	newnatrule(void);
55 static	void	setnatproto(int);
56 static	void	setmapifnames(void);
57 static	void	setrdrifnames(void);
58 static	void	proxy_setconfig(int);
59 static	void	proxy_unsetconfig(void);
60 static	namelist_t *proxy_dns_add_pass(char *, char *);
61 static	namelist_t *proxy_dns_add_block(char *, char *);
62 static	void	proxy_addconfig(char *, int, char *, namelist_t *);
63 static	void	proxy_loadconfig(int, ioctlfunc_t, char *, int,
64 				      char *, namelist_t *);
65 static	void	proxy_loadrules(int, ioctlfunc_t, proxyrule_t *);
66 static	void	setmapifnames(void);
67 static	void	setrdrifnames(void);
68 static	void	setifname(ipnat_t **, int, char *);
69 static	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 %%
123 file:	line
124 	| assign
125 	| file line
126 	| file assign
127 	| file pconf ';'
128 	;
129 
130 line:	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 
153 assign:	YY_STR assigning YY_STR ';'	{ set_variable($1, $3);
154 					  resetlexer();
155 					  free($1);
156 					  free($3);
157 					  yyvarnext = 0;
158 					}
159 	;
160 
161 assigning:
162 	'='				{ yyvarnext = 1; }
163 	;
164 
165 xx:					{ newnatrule(); }
166 	;
167 
168 rule:	map eol
169 	| mapblock eol
170 	| redir eol
171 	| rewrite ';'
172 	| divert ';'
173 	;
174 
175 no:	IPNY_NO				{ nat->in_flags |= IPN_NO; }
176 	;
177 
178 eol:	| ';'
179 	;
180 
181 map:	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 
288 mapblock:
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 
329 redir:	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 
375 rewrite:
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 
387 divert:	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 
401 tlate:	IPNY_TLATE		{ yyexpectaddr = 1; }
402 	;
403 
404 pconf:	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 
413 dnslines:
414 	dnsline 		{ $$ = $1; }
415 	| dnslines ';' dnsline	{ $$ = $1; $1->na_next = $3; }
416 	;
417 
418 dnsline:
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 
425 oninout:
426 	inout IPNY_ON ifnames	{ ; }
427 	;
428 
429 inout:	IPNY_IN			{ nat->in_redir = NAT_REDIRECT; }
430 	| IPNY_OUT		{ nat->in_redir = NAT_MAP; }
431 	;
432 
433 rwrproto:
434 	| IPNY_PROTO setproto
435 	;
436 
437 newdst:	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 
461 divdst:	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 
480 src:	IPNY_SRC		{ yyexpectaddr = 1; }
481 	;
482 
483 dst:	IPNY_DST		{ yyexpectaddr = 1; }
484 	;
485 
486 srcports:
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 
497 dstports:
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 
508 comaports:
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 
520 proxy:	| 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 	;
578 setproto:
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 
603 rhsaddr:
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 
633 dip:
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 
665 rhdaddr:
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 
694 erhdaddr:
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 
708 port:	IPNY_PORT			{ suggest_port = 1; }
709 	;
710 
711 portspec:
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 
724 portpair:
725 	portspec			{ $$.p1 = $1; $$.p2 = $1; }
726 	| portspec '-' portspec		{ $$.p1 = $1; $$.p2 = $3; }
727 	| portspec ':' portspec		{ $$.p1 = $1; $$.p2 = $3; }
728 	;
729 
730 dport:	| 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 
738 nport:	| 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 
754 ports:	| IPNY_PORTS YY_NUMBER		{ nat->in_spmin = $2; }
755 	| IPNY_PORTS IPNY_AUTO		{ nat->in_flags |= IPN_AUTOPORTMAP; }
756 	;
757 
758 mapit:	IPNY_MAP			{ nat->in_redir = NAT_MAP; }
759 	| IPNY_BIMAP			{ nat->in_redir = NAT_BIMAP; }
760 	;
761 
762 rdrit:	IPNY_RDR			{ nat->in_redir = NAT_REDIRECT; }
763 	;
764 
765 mapblockit:
766 	IPNY_MAPBLOCK			{ nat->in_redir = NAT_MAPBLK; }
767 	;
768 
769 mapfrom:
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 
791 rdrfrom:
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 
813 from:	IPNY_FROM			{ nat->in_flags |= IPN_FILTER;
814 					  yyexpectaddr = 1;
815 					}
816 	;
817 
818 to:	IPNY_TO				{ yyexpectaddr = 1; }
819 	;
820 
821 ifnames:
822 	ifname family			{ yyexpectaddr = 1; }
823 	| ifname ',' otherifname family	{ yyexpectaddr = 1; }
824 	;
825 
826 ifname:	YY_STR				{ setifname(&nat, 0, $1);
827 					  free($1);
828 					}
829 	;
830 
831 family:	| 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 
835 otherifname:
836 	YY_STR				{ setifname(&nat, 1, $1);
837 					  free($1);
838 					}
839 	;
840 
841 mapport:
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 
878 sobject:
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 
887 saddr:	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 
898 dobject:
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 
907 daddr:	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 
918 addr:	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 
1026 slash:	'/'				{ yyexpectaddr = 0; }
1027 	;
1028 
1029 mask:	IPNY_MASK			{ yyexpectaddr = 0; }
1030 	;
1031 
1032 pool:	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 
1040 hash:	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 
1048 portstuff:
1049 	compare portspec		{ $$.pc = $1; $$.p1 = $2; $$.p2 = 0; }
1050 	| portspec range portspec	{ $$.pc = $2; $$.p1 = $1; $$.p2 = $3; }
1051 	;
1052 
1053 mapoptions:
1054 	rr frag age mssclamp nattag setproto purge
1055 	;
1056 
1057 rdroptions:
1058 	rr frag age sticky mssclamp rdrproxy nattag purge
1059 	;
1060 
1061 nattag:	| IPNY_TAG YY_STR		{ strncpy(nat->in_tag.ipt_tag, $2,
1062 						  sizeof(nat->in_tag.ipt_tag));
1063 					}
1064 rr:	| IPNY_ROUNDROBIN		{ nat->in_flags |= IPN_ROUNDR; }
1065 	;
1066 
1067 frag:	| IPNY_FRAG			{ nat->in_flags |= IPN_FRAG; }
1068 	;
1069 
1070 age:	| 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 
1076 sticky: | 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 
1085 mssclamp:
1086 	| IPNY_MSSCLAMP YY_NUMBER		{ nat->in_mssclamp = $2; }
1087 	;
1088 
1089 tcpudp:	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 
1101 sequential:
1102 	| IPNY_SEQUENTIAL		{ nat->in_flags |= IPN_SEQUENTIAL; }
1103 	;
1104 
1105 purge:
1106 	| IPNY_PURGE			{ nat->in_flags |= IPN_PURGE; }
1107 	;
1108 
1109 rdrproxy:
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 
1128 newopts:
1129 	| IPNY_PURGE			{ nat->in_flags |= IPN_PURGE; }
1130 	;
1131 
1132 proto:	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 
1149 hexnumber:
1150 	YY_HEX				{ $$ = $1; }
1151 	;
1152 
1153 hostname:
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 
1192 compare:
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 
1201 range:
1202 	YY_RANGE_OUT			{ $$ = FR_OUTRANGE; }
1203 	| YY_RANGE_IN			{ $$ = FR_INRANGE; }
1204 	| ':'				{ $$ = FR_INCRANGE; }
1205 	;
1206 
1207 ipaddr:	ipv4				{ $$ = $1; }
1208 	| YY_IPV6			{ $$.a = $1;
1209 					  $$.f = AF_INET6;
1210 					}
1211 	;
1212 
1213 ipv4:	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 
1228 static	wordtab_t	proxies[] = {
1229 	{ "dns",	IPNY_DNS }
1230 };
1231 
1232 static	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 
1241 static	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 
1296 int
1297 ipnat_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 
1339 int
1340 ipnat_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 
1375 static void
1376 newnatrule()
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 
1404 static void
1405 setnatproto(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 
1468 int
1469 ipnat_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 				snprintf(msg, sizeof(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 				snprintf(msg, sizeof(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 				snprintf(msg, sizeof(msg), "%d:ioctl(add/insert nat rule)",
1541 					ipn->in_flineno);
1542 				if (errno == EEXIST) {
1543 					int strlen_msg = strlen(msg);
1544 					snprintf(msg + strlen_msg, sizeof(msg) -strlen_msg, "(line %d)",
1545 						ipn->in_flineno);
1546 				}
1547 				return ipf_perror_fd(fd, ioctlfunc, msg);
1548 			}
1549 		}
1550 	}
1551 	return 0;
1552 }
1553 
1554 
1555 static void
1556 setmapifnames()
1557 {
1558 	if (nat->in_ifnames[1] == -1)
1559 		nat->in_ifnames[1] = nat->in_ifnames[0];
1560 
1561 	if ((suggest_port == 1) && (nat->in_flags & IPN_TCPUDP) == 0)
1562 		nat->in_flags |= IPN_TCPUDP;
1563 
1564 	if ((nat->in_flags & IPN_TCPUDP) == 0)
1565 		setnatproto(nat->in_pr[1]);
1566 
1567 	if (((nat->in_redir & NAT_MAPBLK) != 0) ||
1568 	      ((nat->in_flags & IPN_AUTOPORTMAP) != 0))
1569 		nat_setgroupmap(nat);
1570 }
1571 
1572 
1573 static void
1574 setrdrifnames()
1575 {
1576 	if ((suggest_port == 1) && (nat->in_flags & IPN_TCPUDP) == 0)
1577 		nat->in_flags |= IPN_TCPUDP;
1578 
1579 	if ((nat->in_pr[0] == 0) && ((nat->in_flags & IPN_TCPUDP) == 0) &&
1580 	    (nat->in_dpmin != 0 || nat->in_dpmax != 0 || nat->in_dpnext != 0))
1581 		setnatproto(IPPROTO_TCP);
1582 
1583 	if (nat->in_ifnames[1] == -1)
1584 		nat->in_ifnames[1] = nat->in_ifnames[0];
1585 }
1586 
1587 
1588 static void
1589 proxy_setconfig(proxy)
1590 	int proxy;
1591 {
1592 	if (proxy == IPNY_DNS) {
1593 		yysetfixeddict(dnswords);
1594 	}
1595 }
1596 
1597 
1598 static void
1599 proxy_unsetconfig()
1600 {
1601 	yyresetdict();
1602 }
1603 
1604 
1605 static namelist_t *
1606 proxy_dns_add_pass(prefix, name)
1607 	char *prefix, *name;
1608 {
1609 	namelist_t *n;
1610 
1611 	n = calloc(1, sizeof(*n));
1612 	if (n != NULL) {
1613 		if (prefix == NULL || *prefix == '\0') {
1614 			n->na_name = strdup(name);
1615 		} else {
1616 			n->na_name = malloc(strlen(name) + strlen(prefix) + 1);
1617 			strcpy(n->na_name, prefix);
1618 			strcat(n->na_name, name);
1619 		}
1620 	}
1621 	return n;
1622 }
1623 
1624 
1625 static namelist_t *
1626 proxy_dns_add_block(prefix, name)
1627 	char *prefix, *name;
1628 {
1629 	namelist_t *n;
1630 
1631 	n = calloc(1, sizeof(*n));
1632 	if (n != NULL) {
1633 		if (prefix == NULL || *prefix == '\0') {
1634 			n->na_name = strdup(name);
1635 		} else {
1636 			n->na_name = malloc(strlen(name) + strlen(prefix) + 1);
1637 			strcpy(n->na_name, prefix);
1638 			strcat(n->na_name, name);
1639 		}
1640 		n->na_value = 1;
1641 	}
1642 	return n;
1643 }
1644 
1645 
1646 static void
1647 proxy_addconfig(proxy, proto, conf, list)
1648 	char *proxy, *conf;
1649 	int proto;
1650 	namelist_t *list;
1651 {
1652 	proxyrule_t *pr;
1653 
1654 	pr = calloc(1, sizeof(*pr));
1655 	if (pr != NULL) {
1656 		pr->pr_proto = proto;
1657 		pr->pr_proxy = proxy;
1658 		pr->pr_conf = conf;
1659 		pr->pr_names = list;
1660 		pr->pr_next = prules;
1661 		prules = pr;
1662 	}
1663 }
1664 
1665 
1666 static void
1667 proxy_loadrules(fd, ioctlfunc, rules)
1668 	int fd;
1669 	ioctlfunc_t ioctlfunc;
1670 	proxyrule_t *rules;
1671 {
1672 	proxyrule_t *pr;
1673 
1674 	while ((pr = rules) != NULL) {
1675 		proxy_loadconfig(fd, ioctlfunc, pr->pr_proxy, pr->pr_proto,
1676 				 pr->pr_conf, pr->pr_names);
1677 		rules = pr->pr_next;
1678 		free(pr->pr_conf);
1679 		free(pr);
1680 	}
1681 }
1682 
1683 
1684 static void
1685 proxy_loadconfig(fd, ioctlfunc, proxy, proto, conf, list)
1686 	int fd;
1687 	ioctlfunc_t ioctlfunc;
1688 	char *proxy, *conf;
1689 	int proto;
1690 	namelist_t *list;
1691 {
1692 	namelist_t *na;
1693 	ipfobj_t obj;
1694 	ap_ctl_t pcmd;
1695 
1696 	obj.ipfo_rev = IPFILTER_VERSION;
1697 	obj.ipfo_type = IPFOBJ_PROXYCTL;
1698 	obj.ipfo_size = sizeof(pcmd);
1699 	obj.ipfo_ptr = &pcmd;
1700 
1701 	while ((na = list) != NULL) {
1702 		if ((opts & OPT_REMOVE) != 0)
1703 			pcmd.apc_cmd = APC_CMD_DEL;
1704 		else
1705 			pcmd.apc_cmd = APC_CMD_ADD;
1706 		pcmd.apc_dsize = strlen(na->na_name) + 1;
1707 		pcmd.apc_data = na->na_name;
1708 		pcmd.apc_arg = na->na_value;
1709 		pcmd.apc_p = proto;
1710 
1711 		strncpy(pcmd.apc_label, proxy, APR_LABELLEN);
1712 		pcmd.apc_label[APR_LABELLEN - 1] = '\0';
1713 
1714 		strncpy(pcmd.apc_config, conf, APR_LABELLEN);
1715 		pcmd.apc_config[APR_LABELLEN - 1] = '\0';
1716 
1717 		if ((*ioctlfunc)(fd, SIOCPROXY, (void *)&obj) == -1) {
1718                         if ((opts & OPT_DONOTHING) == 0) {
1719                                 char msg[80];
1720 
1721                                 snprintf(msg, sizeof(msg), "%d:ioctl(add/remove proxy rule)",
1722 					yylineNum);
1723                                 ipf_perror_fd(fd, ioctlfunc, msg);
1724 				return;
1725                         }
1726 		}
1727 
1728 		list = na->na_next;
1729 		free(na->na_name);
1730 		free(na);
1731 	}
1732 }
1733 
1734 
1735 static void
1736 setifname(np, idx, name)
1737 	ipnat_t **np;
1738 	int idx;
1739 	char *name;
1740 {
1741 	int pos;
1742 
1743 	pos = addname(np, name);
1744 	if (pos == -1)
1745 		return;
1746 	(*np)->in_ifnames[idx] = pos;
1747 }
1748 
1749 
1750 static int
1751 addname(np, name)
1752 	ipnat_t **np;
1753 	char *name;
1754 {
1755 	ipnat_t *n;
1756 	int nlen;
1757 	int pos;
1758 
1759 	nlen = strlen(name) + 1;
1760 	n = realloc(*np, (*np)->in_size + nlen);
1761 	if (*np == nattop)
1762 		nattop = n;
1763 	*np = n;
1764 	if (n == NULL)
1765 		return -1;
1766 	if (n->in_pnext != NULL)
1767 		*n->in_pnext = n;
1768 	n->in_size += nlen;
1769 	pos = n->in_namelen;
1770 	n->in_namelen += nlen;
1771 	strcpy(n->in_names + pos, name);
1772 	n->in_names[n->in_namelen] = '\0';
1773 	return pos;
1774 }
1775