xref: /freebsd/sbin/ipf/ipnat/ipnat_y.y (revision 18054d0220cfc8df9c9568c437bd6fbb59d53c3c)
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(int fd, addfunc_t addfunc, ioctlfunc_t ioctlfunc,
1298 	char *filename)
1299 {
1300 	FILE *fp = NULL;
1301 	int rval;
1302 	char *s;
1303 
1304 	yylineNum = 1;
1305 
1306 	(void) yysettab(yywords);
1307 
1308 	s = getenv("YYDEBUG");
1309 	if (s)
1310 		yydebug = atoi(s);
1311 	else
1312 		yydebug = 0;
1313 
1314 	if (strcmp(filename, "-")) {
1315 		fp = fopen(filename, "r");
1316 		if (!fp) {
1317 			FPRINTF(stderr, "fopen(%s) failed: %s\n", filename,
1318 				STRERROR(errno));
1319 			return(-1);
1320 		}
1321 	} else
1322 		fp = stdin;
1323 
1324 	while ((rval = ipnat_parsesome(fd, addfunc, ioctlfunc, fp)) == 0)
1325 		;
1326 	if (fp != NULL)
1327 		fclose(fp);
1328 	if (rval == -1)
1329 		rval = 0;
1330 	else if (rval != 0)
1331 		rval = 1;
1332 	return(rval);
1333 }
1334 
1335 
1336 int
1337 ipnat_parsesome(int fd, addfunc_t addfunc, ioctlfunc_t ioctlfunc,
1338 	FILE *fp)
1339 {
1340 	char *s;
1341 	int i;
1342 
1343 	natfd = fd;
1344 	parser_error = 0;
1345 	nataddfunc = addfunc;
1346 	natioctlfunc = ioctlfunc;
1347 
1348 	if (feof(fp))
1349 		return(-1);
1350 	i = fgetc(fp);
1351 	if (i == EOF)
1352 		return(-1);
1353 	if (ungetc(i, fp) == EOF)
1354 		return(-1);
1355 	if (feof(fp))
1356 		return(-1);
1357 	s = getenv("YYDEBUG");
1358 	if (s)
1359 		yydebug = atoi(s);
1360 	else
1361 		yydebug = 0;
1362 
1363 	yyin = fp;
1364 	yyparse();
1365 	return(parser_error);
1366 }
1367 
1368 
1369 static void
1370 newnatrule(void)
1371 {
1372 	ipnat_t *n;
1373 
1374 	n = calloc(1, sizeof(*n));
1375 	if (n == NULL)
1376 		return;
1377 
1378 	if (nat == NULL) {
1379 		nattop = nat = n;
1380 		n->in_pnext = &nattop;
1381 	} else {
1382 		nat->in_next = n;
1383 		n->in_pnext = &nat->in_next;
1384 		nat = n;
1385 	}
1386 
1387 	n->in_flineno = yylineNum;
1388 	n->in_ifnames[0] = -1;
1389 	n->in_ifnames[1] = -1;
1390 	n->in_plabel = -1;
1391 	n->in_pconfig = -1;
1392 	n->in_size = sizeof(*n);
1393 
1394 	suggest_port = 0;
1395 }
1396 
1397 
1398 static void
1399 setnatproto(int p)
1400 {
1401 	nat->in_pr[0] = p;
1402 	nat->in_pr[1] = p;
1403 
1404 	switch (p)
1405 	{
1406 	case IPPROTO_TCP :
1407 		nat->in_flags |= IPN_TCP;
1408 		nat->in_flags &= ~IPN_UDP;
1409 		break;
1410 	case IPPROTO_UDP :
1411 		nat->in_flags |= IPN_UDP;
1412 		nat->in_flags &= ~IPN_TCP;
1413 		break;
1414 #ifdef USE_INET6
1415 	case IPPROTO_ICMPV6 :
1416 #endif
1417 	case IPPROTO_ICMP :
1418 		nat->in_flags &= ~IPN_TCPUDP;
1419 		if (!(nat->in_flags & IPN_ICMPQUERY) &&
1420 		    !(nat->in_redir & NAT_DIVERTUDP)) {
1421 			nat->in_dcmp = 0;
1422 			nat->in_scmp = 0;
1423 			nat->in_dpmin = 0;
1424 			nat->in_dpmax = 0;
1425 			nat->in_dpnext = 0;
1426 			nat->in_spmin = 0;
1427 			nat->in_spmax = 0;
1428 			nat->in_spnext = 0;
1429 		}
1430 		break;
1431 	default :
1432 		if ((nat->in_redir & NAT_MAPBLK) == 0) {
1433 			nat->in_flags &= ~IPN_TCPUDP;
1434 			nat->in_dcmp = 0;
1435 			nat->in_scmp = 0;
1436 			nat->in_dpmin = 0;
1437 			nat->in_dpmax = 0;
1438 			nat->in_dpnext = 0;
1439 			nat->in_spmin = 0;
1440 			nat->in_spmax = 0;
1441 			nat->in_spnext = 0;
1442 		}
1443 		break;
1444 	}
1445 
1446 	if ((nat->in_flags & (IPN_TCP|IPN_UDP)) == 0) {
1447 		nat->in_stop = 0;
1448 		nat->in_dtop = 0;
1449 		nat->in_osport = 0;
1450 		nat->in_odport = 0;
1451 		nat->in_stop = 0;
1452 		nat->in_osport = 0;
1453 		nat->in_dtop = 0;
1454 		nat->in_odport = 0;
1455 	}
1456 	if ((nat->in_flags & (IPN_TCPUDP|IPN_FIXEDDPORT)) == IPN_FIXEDDPORT)
1457 		nat->in_flags &= ~IPN_FIXEDDPORT;
1458 }
1459 
1460 
1461 int
1462 ipnat_addrule(int fd, ioctlfunc_t ioctlfunc, void *ptr)
1463 {
1464 	ioctlcmd_t add, del;
1465 	ipfobj_t obj;
1466 	ipnat_t *ipn;
1467 
1468 	ipn = ptr;
1469 	bzero((char *)&obj, sizeof(obj));
1470 	obj.ipfo_rev = IPFILTER_VERSION;
1471 	obj.ipfo_size = ipn->in_size;
1472 	obj.ipfo_type = IPFOBJ_IPNAT;
1473 	obj.ipfo_ptr = ptr;
1474 
1475 	if ((opts & OPT_DONOTHING) != 0)
1476 		fd = -1;
1477 
1478 	if (opts & OPT_ZERORULEST) {
1479 		add = SIOCZRLST;
1480 		del = 0;
1481 	} else if (opts & OPT_PURGE) {
1482 		add = 0;
1483 		del = SIOCPURGENAT;
1484 	} else {
1485 		add = SIOCADNAT;
1486 		del = SIOCRMNAT;
1487 	}
1488 
1489 	if ((opts & OPT_VERBOSE) != 0)
1490 		printnat(ipn, opts);
1491 
1492 	if (opts & OPT_DEBUG)
1493 		binprint(ipn, ipn->in_size);
1494 
1495 	if ((opts & OPT_ZERORULEST) != 0) {
1496 		if ((*ioctlfunc)(fd, add, (void *)&obj) == -1) {
1497 			if ((opts & OPT_DONOTHING) == 0) {
1498 				char msg[80];
1499 
1500 				snprintf(msg, sizeof(msg), "%d:ioctl(zero nat rule)",
1501 					ipn->in_flineno);
1502 				return(ipf_perror_fd(fd, ioctlfunc, msg));
1503 			}
1504 		} else {
1505 			PRINTF("hits %lu ", ipn->in_hits);
1506 #ifdef USE_QUAD_T
1507 			PRINTF("bytes %"PRIu64" ",
1508 			       ipn->in_bytes[0] + ipn->in_bytes[1]);
1509 #else
1510 			PRINTF("bytes %lu ",
1511 			       ipn->in_bytes[0] + ipn->in_bytes[1]);
1512 #endif
1513 			printnat(ipn, opts);
1514 		}
1515 	} else if ((opts & OPT_REMOVE) != 0) {
1516 		if ((*ioctlfunc)(fd, del, (void *)&obj) == -1) {
1517 			if ((opts & OPT_DONOTHING) == 0) {
1518 				char msg[80];
1519 
1520 				snprintf(msg, sizeof(msg), "%d:ioctl(delete nat rule)",
1521 					ipn->in_flineno);
1522 				return(ipf_perror_fd(fd, ioctlfunc, msg));
1523 			}
1524 		}
1525 	} else {
1526 		if ((*ioctlfunc)(fd, add, (void *)&obj) == -1) {
1527 			if ((opts & OPT_DONOTHING) == 0) {
1528 				char msg[80];
1529 
1530 				snprintf(msg, sizeof(msg), "%d:ioctl(add/insert nat rule)",
1531 					ipn->in_flineno);
1532 				if (errno == EEXIST) {
1533 					int strlen_msg = strlen(msg);
1534 					snprintf(msg + strlen_msg, sizeof(msg) -strlen_msg, "(line %d)",
1535 						ipn->in_flineno);
1536 				}
1537 				return(ipf_perror_fd(fd, ioctlfunc, msg));
1538 			}
1539 		}
1540 	}
1541 	return(0);
1542 }
1543 
1544 
1545 static void
1546 setmapifnames()
1547 {
1548 	if (nat->in_ifnames[1] == -1)
1549 		nat->in_ifnames[1] = nat->in_ifnames[0];
1550 
1551 	if ((suggest_port == 1) && (nat->in_flags & IPN_TCPUDP) == 0)
1552 		nat->in_flags |= IPN_TCPUDP;
1553 
1554 	if ((nat->in_flags & IPN_TCPUDP) == 0)
1555 		setnatproto(nat->in_pr[1]);
1556 
1557 	if (((nat->in_redir & NAT_MAPBLK) != 0) ||
1558 	      ((nat->in_flags & IPN_AUTOPORTMAP) != 0))
1559 		nat_setgroupmap(nat);
1560 }
1561 
1562 
1563 static void
1564 setrdrifnames(void)
1565 {
1566 	if ((suggest_port == 1) && (nat->in_flags & IPN_TCPUDP) == 0)
1567 		nat->in_flags |= IPN_TCPUDP;
1568 
1569 	if ((nat->in_pr[0] == 0) && ((nat->in_flags & IPN_TCPUDP) == 0) &&
1570 	    (nat->in_dpmin != 0 || nat->in_dpmax != 0 || nat->in_dpnext != 0))
1571 		setnatproto(IPPROTO_TCP);
1572 
1573 	if (nat->in_ifnames[1] == -1)
1574 		nat->in_ifnames[1] = nat->in_ifnames[0];
1575 }
1576 
1577 
1578 static void
1579 proxy_setconfig(int proxy)
1580 {
1581 	if (proxy == IPNY_DNS) {
1582 		yysetfixeddict(dnswords);
1583 	}
1584 }
1585 
1586 
1587 static void
1588 proxy_unsetconfig(void)
1589 {
1590 	yyresetdict();
1591 }
1592 
1593 
1594 static namelist_t *
1595 proxy_dns_add_pass(char *prefix, char *name)
1596 {
1597 	namelist_t *n;
1598 
1599 	n = calloc(1, sizeof(*n));
1600 	if (n != NULL) {
1601 		if (prefix == NULL || *prefix == '\0') {
1602 			n->na_name = strdup(name);
1603 		} else {
1604 			n->na_name = malloc(strlen(name) + strlen(prefix) + 1);
1605 			strcpy(n->na_name, prefix);
1606 			strcat(n->na_name, name);
1607 		}
1608 	}
1609 	return(n);
1610 }
1611 
1612 
1613 static namelist_t *
1614 proxy_dns_add_block(char *prefix, char *name)
1615 {
1616 	namelist_t *n;
1617 
1618 	n = calloc(1, sizeof(*n));
1619 	if (n != NULL) {
1620 		if (prefix == NULL || *prefix == '\0') {
1621 			n->na_name = strdup(name);
1622 		} else {
1623 			n->na_name = malloc(strlen(name) + strlen(prefix) + 1);
1624 			strcpy(n->na_name, prefix);
1625 			strcat(n->na_name, name);
1626 		}
1627 		n->na_value = 1;
1628 	}
1629 	return(n);
1630 }
1631 
1632 
1633 static void
1634 proxy_addconfig(char *proxy, int proto, char *conf, namelist_t *list)
1635 {
1636 	proxyrule_t *pr;
1637 
1638 	pr = calloc(1, sizeof(*pr));
1639 	if (pr != NULL) {
1640 		pr->pr_proto = proto;
1641 		pr->pr_proxy = proxy;
1642 		pr->pr_conf = conf;
1643 		pr->pr_names = list;
1644 		pr->pr_next = prules;
1645 		prules = pr;
1646 	}
1647 }
1648 
1649 
1650 static void
1651 proxy_loadrules(int fd, ioctlfunc_t ioctlfunc, proxyrule_t *rules)
1652 {
1653 	proxyrule_t *pr;
1654 
1655 	while ((pr = rules) != NULL) {
1656 		proxy_loadconfig(fd, ioctlfunc, pr->pr_proxy, pr->pr_proto,
1657 				 pr->pr_conf, pr->pr_names);
1658 		rules = pr->pr_next;
1659 		free(pr->pr_conf);
1660 		free(pr);
1661 	}
1662 }
1663 
1664 
1665 static void
1666 proxy_loadconfig(int fd, ioctlfunc_t ioctlfunc, char *proxy, int proto,
1667 	char *conf, namelist_t *list)
1668 {
1669 	namelist_t *na;
1670 	ipfobj_t obj;
1671 	ap_ctl_t pcmd;
1672 
1673 	obj.ipfo_rev = IPFILTER_VERSION;
1674 	obj.ipfo_type = IPFOBJ_PROXYCTL;
1675 	obj.ipfo_size = sizeof(pcmd);
1676 	obj.ipfo_ptr = &pcmd;
1677 
1678 	while ((na = list) != NULL) {
1679 		if ((opts & OPT_REMOVE) != 0)
1680 			pcmd.apc_cmd = APC_CMD_DEL;
1681 		else
1682 			pcmd.apc_cmd = APC_CMD_ADD;
1683 		pcmd.apc_dsize = strlen(na->na_name) + 1;
1684 		pcmd.apc_data = na->na_name;
1685 		pcmd.apc_arg = na->na_value;
1686 		pcmd.apc_p = proto;
1687 
1688 		strncpy(pcmd.apc_label, proxy, APR_LABELLEN);
1689 		pcmd.apc_label[APR_LABELLEN - 1] = '\0';
1690 
1691 		strncpy(pcmd.apc_config, conf, APR_LABELLEN);
1692 		pcmd.apc_config[APR_LABELLEN - 1] = '\0';
1693 
1694 		if ((*ioctlfunc)(fd, SIOCPROXY, (void *)&obj) == -1) {
1695                         if ((opts & OPT_DONOTHING) == 0) {
1696                                 char msg[80];
1697 
1698                                 snprintf(msg, sizeof(msg), "%d:ioctl(add/remove proxy rule)",
1699 					yylineNum);
1700                                 ipf_perror_fd(fd, ioctlfunc, msg);
1701 				return;
1702                         }
1703 		}
1704 
1705 		list = na->na_next;
1706 		free(na->na_name);
1707 		free(na);
1708 	}
1709 }
1710 
1711 
1712 static void
1713 setifname(ipnat_t **np, int idx, char *name)
1714 {
1715 	int pos;
1716 
1717 	pos = addname(np, name);
1718 	if (pos == -1)
1719 		return;
1720 	(*np)->in_ifnames[idx] = pos;
1721 }
1722 
1723 
1724 static int
1725 addname(ipnat_t **np, char *name)
1726 {
1727 	ipnat_t *n;
1728 	int nlen;
1729 	int pos;
1730 
1731 	nlen = strlen(name) + 1;
1732 	n = realloc(*np, (*np)->in_size + nlen);
1733 	if (*np == nattop)
1734 		nattop = n;
1735 	*np = n;
1736 	if (n == NULL)
1737 		return(-1);
1738 	if (n->in_pnext != NULL)
1739 		*n->in_pnext = n;
1740 	n->in_size += nlen;
1741 	pos = n->in_namelen;
1742 	n->in_namelen += nlen;
1743 	strcpy(n->in_names + pos, name);
1744 	n->in_names[n->in_namelen] = '\0';
1745 	return(pos);
1746 }
1747