xref: /illumos-gate/usr/src/cmd/ipf/tools/ipnat_y.y (revision 6bc074b1c1f7d3014541f4c3e3152dcf2b19eed6)
1 %{
2 /*
3  * Copyright (C) 2001-2008 by Darren Reed.
4  *
5  * See the IPFILTER.LICENCE file for details on licencing.
6  *
7  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
8  * Use is subject to license terms.
9  */
10 
11 #ifdef  __FreeBSD__
12 # ifndef __FreeBSD_cc_version
13 #  include <osreldate.h>
14 # else
15 #  if __FreeBSD_cc_version < 430000
16 #   include <osreldate.h>
17 #  endif
18 # endif
19 #endif
20 #include <stdio.h>
21 #include <unistd.h>
22 #include <string.h>
23 #include <fcntl.h>
24 #include <errno.h>
25 #if !defined(__SVR4) && !defined(__GNUC__)
26 #include <strings.h>
27 #endif
28 #include <sys/types.h>
29 #include <sys/param.h>
30 #include <sys/file.h>
31 #include <stdlib.h>
32 #include <stddef.h>
33 #include <sys/socket.h>
34 #include <sys/ioctl.h>
35 #include <netinet/in.h>
36 #include <netinet/in_systm.h>
37 #include <sys/time.h>
38 #include <syslog.h>
39 #include <net/if.h>
40 #if __FreeBSD_version >= 300000
41 # include <net/if_var.h>
42 #endif
43 #include <netdb.h>
44 #include <arpa/nameser.h>
45 #include <resolv.h>
46 #include "ipf.h"
47 #include "netinet/ipl.h"
48 #include "ipnat_l.h"
49 
50 #define	YYDEBUG	1
51 
52 extern	void	yyerror __P((char *));
53 extern	int	yyparse __P((void));
54 extern	int	yylex __P((void));
55 extern	int	yydebug;
56 extern	FILE	*yyin;
57 extern	int	yylineNum;
58 
59 static	ipnat_t		*nattop = NULL;
60 static	ipnat_t		*nat = NULL;
61 static	int		natfd = -1;
62 static	ioctlfunc_t	natioctlfunc = NULL;
63 static	addfunc_t	nataddfunc = NULL;
64 
65 static	void	newnatrule __P((void));
66 static	void	setnatproto __P((int));
67 
68 %}
69 %union	{
70 	char	*str;
71 	u_32_t	num;
72 	struct	{
73 		i6addr_t	a;
74 		int		v;
75 	} ipa;
76 	frentry_t	fr;
77 	frtuc_t	*frt;
78 	u_short	port;
79 	struct	{
80 		u_short	p1;
81 		u_short	p2;
82 		int	pc;
83 	} pc;
84 	struct	{
85 		i6addr_t	a;
86 		i6addr_t	m;
87 		int	v;
88 	} ipp;
89 	union	i6addr	ip6;
90 };
91 
92 %token  <num>   YY_NUMBER YY_HEX
93 %token  <str>   YY_STR
94 %token	  YY_COMMENT
95 %token	  YY_CMP_EQ YY_CMP_NE YY_CMP_LE YY_CMP_GE YY_CMP_LT YY_CMP_GT
96 %token	  YY_RANGE_OUT YY_RANGE_IN
97 %token  <ip6>   YY_IPV6
98 
99 %token	IPNY_MAPBLOCK IPNY_RDR IPNY_PORT IPNY_PORTS IPNY_AUTO IPNY_RANGE
100 %token	IPNY_MAP IPNY_BIMAP IPNY_FROM IPNY_TO IPNY_MASK IPNY_PORTMAP IPNY_ANY
101 %token	IPNY_ROUNDROBIN IPNY_FRAG IPNY_AGE IPNY_ICMPIDMAP IPNY_PROXY
102 %token	IPNY_TCP IPNY_UDP IPNY_TCPUDP IPNY_STICKY IPNY_MSSCLAMP IPNY_TAG
103 %token	IPNY_TLATE IPNY_SEQUENTIAL
104 %type	<port> portspec
105 %type	<num> hexnumber compare range proto
106 %type	<num> saddr daddr sobject dobject mapfrom rdrfrom dip
107 %type	<ipa> hostname ipv4 ipaddr
108 %type	<ipp> addr rhaddr
109 %type	<pc> portstuff
110 %%
111 file:	line
112 	| assign
113 	| file line
114 	| file assign
115 	;
116 
117 line:	xx rule		{ while ((nat = nattop) != NULL) {
118 				if (nat->in_v == 0)
119 					nat->in_v = 4;
120 				nattop = nat->in_next;
121 				(*nataddfunc)(natfd, natioctlfunc, nat);
122 				free(nat);
123 			  }
124 			  resetlexer();
125 			}
126 	| YY_COMMENT
127 	;
128 
129 assign:	YY_STR assigning YY_STR ';'	{ set_variable($1, $3);
130 					  resetlexer();
131 					  free($1);
132 					  free($3);
133 					}
134 	;
135 
136 assigning:
137 	'='				{ yyvarnext = 1; }
138 	;
139 
140 xx:					{ newnatrule(); }
141 	;
142 
143 rule:	map eol
144 	| mapblock eol
145 	| redir eol
146 	;
147 
148 eol:	| ';'
149 	;
150 
151 map:	mapit ifnames addr IPNY_TLATE rhaddr proxy mapoptions
152 				{ if ($3.v != 0 && $3.v != $5.v && $5.v != 0)
153 					yyerror("1.address family mismatch");
154 				  bcopy(&$3.a, &nat->in_in[0], sizeof($3.a));
155 				  bcopy(&$3.m, &nat->in_in[1], sizeof($3.a));
156 				  bcopy(&$5.a, &nat->in_out[0], sizeof($5.a));
157 				  bcopy(&$5.m, &nat->in_out[1], sizeof($5.a));
158 				  if (nat->in_ifnames[1][0] == '\0')
159 					strncpy(nat->in_ifnames[1],
160 						nat->in_ifnames[0],
161 						sizeof(nat->in_ifnames[0]));
162 				  if ((nat->in_flags & IPN_TCPUDP) == 0)
163 					setnatproto(nat->in_p);
164 				  if (((nat->in_redir & NAT_MAPBLK) != 0) ||
165 				      ((nat->in_flags & IPN_AUTOPORTMAP) != 0))
166 					nat_setgroupmap(nat);
167 				}
168 	| mapit ifnames addr IPNY_TLATE rhaddr mapport mapoptions
169 				{ if ($3.v != 0 && $3.v != $5.v && $5.v != 0)
170 					yyerror("2.address family mismatch");
171 				  bcopy(&$3.a, &nat->in_in[0], sizeof($3.a));
172 				  bcopy(&$3.m, &nat->in_in[1], sizeof($3.a));
173 				  bcopy(&$5.a, &nat->in_out[0], sizeof($5.a));
174 				  bcopy(&$5.m, &nat->in_out[1], sizeof($5.a));
175 				  if (nat->in_ifnames[1][0] == '\0')
176 					strncpy(nat->in_ifnames[1],
177 						nat->in_ifnames[0],
178 						sizeof(nat->in_ifnames[0]));
179 				  if ((nat->in_flags & IPN_TCPUDPICMPQ) == 0)
180 					setnatproto(nat->in_p);
181 				  if (((nat->in_redir & NAT_MAPBLK) != 0) ||
182 				      ((nat->in_flags & IPN_AUTOPORTMAP) != 0))
183 					nat_setgroupmap(nat);
184 				}
185 	| mapit ifnames mapfrom IPNY_TLATE rhaddr proxy mapoptions
186 				{ if ($3 != 0 && $3 != $5.v && $5.v != 0)
187 					yyerror("3.address family mismatch");
188 				  bcopy(&$5.a, &nat->in_out[0], sizeof($5.a));
189 				  bcopy(&$5.m, &nat->in_out[1], sizeof($5.a));
190 				  if (nat->in_ifnames[1][0] == '\0')
191 					strncpy(nat->in_ifnames[1],
192 						nat->in_ifnames[0],
193 						sizeof(nat->in_ifnames[0]));
194 				  if ((nat->in_flags & IPN_TCPUDP) == 0)
195 					setnatproto(nat->in_p);
196 				  if (((nat->in_redir & NAT_MAPBLK) != 0) ||
197 				      ((nat->in_flags & IPN_AUTOPORTMAP) != 0))
198 					nat_setgroupmap(nat);
199 				}
200 	| mapit ifnames mapfrom IPNY_TLATE rhaddr mapport mapoptions
201 				{ if ($3 != 0 && $3 != $5.v && $5.v != 0)
202 					yyerror("4.address family mismatch");
203 				  bcopy(&$5.a, &nat->in_out[0], sizeof($5.a));
204 				  bcopy(&$5.m, &nat->in_out[1], sizeof($5.a));
205 				  if (nat->in_ifnames[1][0] == '\0')
206 					strncpy(nat->in_ifnames[1],
207 						nat->in_ifnames[0],
208 						sizeof(nat->in_ifnames[0]));
209 				  if ((nat->in_flags & IPN_TCPUDPICMPQ) == 0)
210 					setnatproto(nat->in_p);
211 				  if (((nat->in_redir & NAT_MAPBLK) != 0) ||
212 				      ((nat->in_flags & IPN_AUTOPORTMAP) != 0))
213 					nat_setgroupmap(nat);
214 				}
215 	;
216 
217 mapblock:
218 	mapblockit ifnames addr IPNY_TLATE addr ports mapoptions
219 				{ if ($3.v != 0 && $3.v != $5.v && $5.v != 0)
220 					yyerror("5.address family mismatch");
221 				  bcopy(&$3.a, &nat->in_in[0], sizeof($3.a));
222 				  bcopy(&$3.m, &nat->in_in[1], sizeof($3.a));
223 				  bcopy(&$5.a, &nat->in_out[0], sizeof($5.a));
224 				  bcopy(&$5.m, &nat->in_out[1], sizeof($5.a));
225 				  if (nat->in_ifnames[1][0] == '\0')
226 					strncpy(nat->in_ifnames[1],
227 						nat->in_ifnames[0],
228 						sizeof(nat->in_ifnames[0]));
229 				  if ((nat->in_flags & IPN_TCPUDP) == 0)
230 					setnatproto(nat->in_p);
231 				  if (((nat->in_redir & NAT_MAPBLK) != 0) ||
232 				      ((nat->in_flags & IPN_AUTOPORTMAP) != 0))
233 					nat_setgroupmap(nat);
234 				}
235 	;
236 
237 redir:	rdrit ifnames addr dport IPNY_TLATE dip nport setproto rdroptions
238 				{ if ($6 != 0 && $3.v != 0 && $6 != $3.v)
239 					yyerror("6.address family mismatch");
240 				  bcopy(&$3.a, &nat->in_out[0], sizeof($3.a));
241 				  bcopy(&$3.m, &nat->in_out[1], sizeof($3.a));
242 				  if (nat->in_ifnames[1][0] == '\0')
243 					strncpy(nat->in_ifnames[1],
244 						nat->in_ifnames[0],
245 						sizeof(nat->in_ifnames[0]));
246 				  if ((nat->in_p == 0) &&
247 				      ((nat->in_flags & IPN_TCPUDP) == 0) &&
248 				      (nat->in_pmin != 0 ||
249 				       nat->in_pmax != 0 ||
250 				       nat->in_pnext != 0))
251 						setnatproto(IPPROTO_TCP);
252 				}
253 	| rdrit ifnames rdrfrom IPNY_TLATE dip nport setproto rdroptions
254 				{ if ($5 != 0 && $3 != 0 && $5 != $3)
255 					yyerror("7.address family mismatch");
256 				  if ((nat->in_p == 0) &&
257 				      ((nat->in_flags & IPN_TCPUDP) == 0) &&
258 				      (nat->in_pmin != 0 ||
259 				       nat->in_pmax != 0 ||
260 				       nat->in_pnext != 0))
261 					setnatproto(IPPROTO_TCP);
262 				  if (nat->in_ifnames[1][0] == '\0')
263 					strncpy(nat->in_ifnames[1],
264 						nat->in_ifnames[0],
265 						sizeof(nat->in_ifnames[0]));
266 				}
267 	| rdrit ifnames addr IPNY_TLATE dip setproto rdroptions
268 				{ if ($5 != 0 && $3.v != 0 && $5 != $3.v)
269 					yyerror("8.address family mismatch");
270 				  bcopy(&$3.a, &nat->in_out[0], sizeof($3.a));
271 				  bcopy(&$3.m, &nat->in_out[1], sizeof($3.a));
272 				  if (nat->in_ifnames[1][0] == '\0')
273 					strncpy(nat->in_ifnames[1],
274 						nat->in_ifnames[0],
275 						sizeof(nat->in_ifnames[0]));
276 				}
277 	;
278 
279 proxy:	| IPNY_PROXY IPNY_PORT portspec YY_STR '/' proto
280 			{ strncpy(nat->in_plabel, $4, sizeof(nat->in_plabel));
281 			  if (nat->in_dcmp == 0) {
282 				nat->in_dport = htons($3);
283 			  } else if ($3 != nat->in_dport) {
284 				yyerror("proxy port numbers not consistant");
285 			  }
286 			  setnatproto($6);
287 			  free($4);
288 			}
289 	| IPNY_PROXY IPNY_PORT YY_STR YY_STR '/' proto
290 			{ int pnum;
291 			  strncpy(nat->in_plabel, $4, sizeof(nat->in_plabel));
292 			  pnum = getportproto($3, $6);
293 			  if (pnum == -1)
294 				yyerror("invalid port number");
295 			  nat->in_dport = pnum;
296 			  setnatproto($6);
297 			  free($3);
298 			  free($4);
299 			}
300 	;
301 
302 setproto:
303 	| proto				{ if (nat->in_p != 0 ||
304 					      nat->in_flags & IPN_TCPUDP)
305 						yyerror("protocol set twice");
306 					  setnatproto($1);
307 					}
308 	| IPNY_TCPUDP			{ if (nat->in_p != 0 ||
309 					      nat->in_flags & IPN_TCPUDP)
310 						yyerror("protocol set twice");
311 					  nat->in_flags |= IPN_TCPUDP;
312 					  nat->in_p = 0;
313 					}
314 	| IPNY_TCP '/' IPNY_UDP		{ if (nat->in_p != 0 ||
315 					      nat->in_flags & IPN_TCPUDP)
316 						yyerror("protocol set twice");
317 					  nat->in_flags |= IPN_TCPUDP;
318 					  nat->in_p = 0;
319 					}
320 	;
321 
322 rhaddr:	addr				{ $$.a = $1.a;
323 					  $$.m = $1.m;
324 					  $$.v = $1.v;
325 					  if ($$.v == 0)
326 						$$.v = nat->in_v;
327 					  yyexpectaddr = 0; }
328 	| IPNY_RANGE hostname '-' hostname
329 					{ if ($2.v != 0 && $4.v != 0 && $4.v != $2.v)
330 						yyerror("9.address family "
331 							"mismatch");
332 					  $$.v = $2.v;
333 					  $$.a = $2.a;
334 					  $$.m = $4.a;
335 					  nat->in_flags |= IPN_IPRANGE;
336 					  yyexpectaddr = 0; }
337 	;
338 
339 dip:
340 	hostname			{ bcopy(&$1.a, &nat->in_in[0],
341 						sizeof($1.a));
342 					  if ($1.v == 0)
343 						$1.v = nat->in_v;
344 					  if ($1.v == 4) {
345 						nat->in_inmsk = 0xffffffff;
346 					  } else {
347 						nat->in_in[1].i6[0] = 0xffffffff;
348 						nat->in_in[1].i6[1] = 0xffffffff;
349 						nat->in_in[1].i6[2] = 0xffffffff;
350 						nat->in_in[1].i6[3] = 0xffffffff;
351 					  }
352 					  $$ = $1.v;
353 					}
354 	| hostname '/' YY_NUMBER        { if ($1.v == 0)
355 						$1.v = nat->in_v;
356 					  if ($1.v == 4 &&
357 					      ($1.a.in4.s_addr != 0 ||
358 					      ($3 != 0 && $3 != 32)))
359 						yyerror("Invalid mask for dip");
360 					  else if ($1.v == 6 &&
361 					      ($1.a.in4.s_addr != 0 ||
362 					      ($3 != 0 && $3 != 128)))
363 						yyerror("Invalid mask for dip");
364 					  else if ($1.v == 0 ) {
365 						if ($1.a.in4.s_addr == 0 &&
366 						    ($3 == 32 || $3 == 0))
367 							$1.v = 4;
368 						else if ($3 == 128)
369 							$1.v = 6;
370 					  }
371 					  bcopy(&$1.a, &nat->in_in[0],
372 						sizeof($1.a));
373 					  ntomask($1.v, $3,
374 						(u_32_t *)&nat->in_in[1]);
375 					  nat->in_in[0].i6[0] &= nat->in_in[1].i6[0];
376 					  nat->in_in[0].i6[0] &= nat->in_in[1].i6[1];
377 					  nat->in_in[0].i6[0] &= nat->in_in[1].i6[2];
378 					  nat->in_in[0].i6[0] &= nat->in_in[1].i6[3];
379 					  nat->in_v = $1.v;
380 					  $$ = $1.v;
381 					}
382 	| hostname ',' { yyexpectaddr = 1; } hostname
383 					{ if ($1.v != $4.v)
384 						yyerror("10.address family "
385 							"mismatch");
386 					  $$ = $1.v;
387 					  nat->in_flags |= IPN_SPLIT;
388 					  bcopy(&$1.a, &nat->in_in[0],
389 						sizeof($1.a));
390 					  bcopy(&$4.a, &nat->in_in[1],
391 						sizeof($4.a));
392 					  yyexpectaddr = 0; }
393 	;
394 
395 portspec:
396 	YY_NUMBER			{ if ($1 > 65535)	/* Unsigned */
397 						yyerror("invalid port number");
398 					  else
399 						$$ = $1;
400 					}
401 	| YY_STR			{ if (getport(NULL, $1, &($$)) == -1)
402 						yyerror("invalid port number");
403 					  $$ = ntohs($$);
404 					}
405 	;
406 
407 dport:	| IPNY_PORT portspec			{ nat->in_pmin = htons($2);
408 						  nat->in_pmax = htons($2); }
409 	| IPNY_PORT portspec '-' portspec	{ nat->in_pmin = htons($2);
410 						  nat->in_pmax = htons($4); }
411 	| IPNY_PORT portspec ':' portspec	{ nat->in_pmin = htons($2);
412 						  nat->in_pmax = htons($4); }
413 	;
414 
415 nport:	IPNY_PORT portspec		{ nat->in_pnext = htons($2); }
416 	| IPNY_PORT '=' portspec	{ nat->in_pnext = htons($3);
417 					  nat->in_flags |= IPN_FIXEDDPORT;
418 					}
419 	;
420 
421 ports:	| IPNY_PORTS YY_NUMBER		{ nat->in_pmin = $2; }
422 	| IPNY_PORTS IPNY_AUTO		{ nat->in_flags |= IPN_AUTOPORTMAP; }
423 	;
424 
425 mapit:	IPNY_MAP			{ nat->in_redir = NAT_MAP; }
426 	| IPNY_BIMAP			{ nat->in_redir = NAT_BIMAP; }
427 	;
428 
429 rdrit:	IPNY_RDR			{ nat->in_redir = NAT_REDIRECT; }
430 	;
431 
432 mapblockit:
433 	IPNY_MAPBLOCK			{ nat->in_redir = NAT_MAPBLK; }
434 	;
435 
436 mapfrom:
437 	from sobject IPNY_TO dobject	{ if ($2 != 0 && $4 != 0 && $2 != $4)
438 						yyerror("11.address family "
439 							"mismatch");
440 					  $$ = $2;
441 					}
442 	| from sobject '!' IPNY_TO dobject
443 					{ if ($2 != 0 && $5 != 0 && $2 != $5)
444 						yyerror("12.address family "
445 							"mismatch");
446 					  nat->in_flags |= IPN_NOTDST;
447 					  $$ = $2;
448 					}
449 	;
450 
451 rdrfrom:
452 	from sobject IPNY_TO dobject	{ if ($2 != 0 && $4 != 0 && $2 != $4)
453 						yyerror("13.address family "
454 							"mismatch");
455 					  $$ = $2;
456 					}
457 	| '!' from sobject IPNY_TO dobject
458 					{ if ($3 != 0 && $5 != 0 && $3 != $5)
459 						yyerror("14.address family "
460 							"mismatch");
461 					  nat->in_flags |= IPN_NOTSRC;
462 					  $$ = $3;
463 					}
464 	;
465 
466 from:	IPNY_FROM			{ nat->in_flags |= IPN_FILTER;
467 					  yyexpectaddr = 1; }
468 	;
469 
470 ifnames:
471 	ifname				{ yyexpectaddr = 1; }
472 	| ifname ',' otherifname	{ yyexpectaddr = 1; }
473 	;
474 
475 ifname:	YY_STR			{ strncpy(nat->in_ifnames[0], $1,
476 					  sizeof(nat->in_ifnames[0]));
477 				  nat->in_ifnames[0][LIFNAMSIZ - 1] = '\0';
478 				  free($1);
479 				}
480 	;
481 
482 otherifname:
483 	YY_STR			{ strncpy(nat->in_ifnames[1], $1,
484 					  sizeof(nat->in_ifnames[1]));
485 				  nat->in_ifnames[1][LIFNAMSIZ - 1] = '\0';
486 				  free($1);
487 				}
488 	;
489 
490 mapport:
491 	IPNY_PORTMAP tcpudp portspec ':' portspec randport
492 			{ nat->in_pmin = htons($3);
493 			  nat->in_pmax = htons($5);
494 			}
495 	| IPNY_PORTMAP tcpudp IPNY_AUTO randport
496 			{ nat->in_flags |= IPN_AUTOPORTMAP;
497 			  nat->in_pmin = htons(1024);
498 			  nat->in_pmax = htons(65535);
499 			}
500 	| IPNY_ICMPIDMAP YY_STR YY_NUMBER ':' YY_NUMBER
501 			{ if (strcmp($2, "icmp") != 0) {
502 				yyerror("icmpidmap not followed by icmp");
503 			  }
504 			  free($2);
505 			  if ($3 < 0 || $3 > 65535)
506 				yyerror("invalid ICMP Id number");
507 			  if ($5 < 0 || $5 > 65535)
508 				yyerror("invalid ICMP Id number");
509 			  nat->in_flags = IPN_ICMPQUERY;
510 			  nat->in_pmin = htons($3);
511 			  nat->in_pmax = htons($5);
512 			}
513 	;
514 
515 randport:
516 	| IPNY_SEQUENTIAL	{ nat->in_flags |= IPN_SEQUENTIAL; }
517 	;
518 
519 sobject:
520 	saddr				{ $$ = $1; }
521 	| saddr IPNY_PORT portstuff	{ nat->in_sport = $3.p1;
522 					  nat->in_stop = $3.p2;
523 					  nat->in_scmp = $3.pc;
524 					  $$ = $1;
525 					}
526 	;
527 
528 saddr:	addr				{ if (nat->in_redir == NAT_REDIRECT) {
529 						bcopy(&$1.a, &nat->in_src[0],
530 							sizeof($1.a));
531 						bcopy(&$1.m, &nat->in_src[1],
532 							sizeof($1.a));
533 					  } else {
534 						bcopy(&$1.a, &nat->in_in[0],
535 							sizeof($1.a));
536 						bcopy(&$1.m, &nat->in_in[1],
537 							sizeof($1.a));
538 					  }
539 					  $$ = $1.v;
540 					}
541 	;
542 
543 dobject:
544 	daddr				{ $$ = $1; }
545 	| daddr IPNY_PORT portstuff	{ nat->in_dport = $3.p1;
546 					  nat->in_dtop = $3.p2;
547 					  nat->in_dcmp = $3.pc;
548 					  if (nat->in_redir == NAT_REDIRECT)
549 						nat->in_pmin = htons($3.p1);
550 					}
551 	;
552 
553 daddr:	addr				{ if (nat->in_redir == NAT_REDIRECT) {
554 						bcopy(&$1.a, &nat->in_out[0],
555 							sizeof($1.a));
556 						bcopy(&$1.m, &nat->in_out[1],
557 							sizeof($1.a));
558 					  } else {
559 						bcopy(&$1.a, &nat->in_src[0],
560 							sizeof($1.a));
561 						bcopy(&$1.m, &nat->in_src[1],
562 							sizeof($1.a));
563 					  }
564 					  $$ = $1.v;
565 					}
566 	;
567 
568 addr:	IPNY_ANY			{ yyexpectaddr = 0;
569 					  bzero(&$$.a, sizeof($$.a));
570 					  bzero(&$$.m, sizeof($$.a));
571 					  $$.v = nat->in_v;
572 					}
573 	| hostname			{ $$.a = $1.a;
574 					  $$.v = $1.v;
575 					  if ($$.v == 4) {
576 						$$.m.in4.s_addr = 0xffffffff;
577 					  } else {
578 						$$.m.i6[0] = 0xffffffff;
579 						$$.m.i6[1] = 0xffffffff;
580 						$$.m.i6[2] = 0xffffffff;
581 						$$.m.i6[3] = 0xffffffff;
582 					  }
583 					  yyexpectaddr = 0;
584 					}
585 	| hostname '/' YY_NUMBER	{ $$.a = $1.a;
586 					  if ($1.v == 0) {
587 						if ($1.a.in4.s_addr != 0)
588 							yyerror("invalid addr");
589 						if ($3 == 0 || $3 == 32)
590 							$1.v = 4;
591 						else if ($3 == 128)
592 							$1.v = 6;
593 						else
594 							yyerror("invalid mask");
595 						nat->in_v = $1.v;
596 					  }
597 					  ntomask($1.v, $3, (u_32_t *)&$$.m);
598 					  $$.a.i6[0] &= $$.m.i6[0];
599 					  $$.a.i6[1] &= $$.m.i6[1];
600 					  $$.a.i6[2] &= $$.m.i6[2];
601 					  $$.a.i6[3] &= $$.m.i6[3];
602 					  $$.v = $1.v;
603 					  yyexpectaddr = 0;
604 					}
605 	| hostname '/' ipaddr		{ if ($1.v != $3.v) {
606 						yyerror("1.address family "
607 							"mismatch");
608 					  }
609 					  $$.a = $1.a;
610 					  $$.m = $3.a;
611 					  $$.a.i6[0] &= $$.m.i6[0];
612 					  $$.a.i6[1] &= $$.m.i6[1];
613 					  $$.a.i6[2] &= $$.m.i6[2];
614 					  $$.a.i6[3] &= $$.m.i6[3];
615 					  $$.v = $1.v;
616 					  yyexpectaddr = 0;
617 					}
618 	| hostname '/' hexnumber	{ $$.a = $1.a;
619 					  $$.m.in4.s_addr = htonl($3);
620 					  $$.a.in4.s_addr &= $$.m.in4.s_addr;
621 					  $$.v = 4;
622 					}
623 	| hostname IPNY_MASK ipaddr	{ if ($1.v != $3.v) {
624 						yyerror("2.address family "
625 							"mismatch");
626 					  }
627 					  $$.a = $1.a;
628 					  $$.m = $3.a;
629 					  $$.a.i6[0] &= $$.m.i6[0];
630 					  $$.a.i6[1] &= $$.m.i6[1];
631 					  $$.a.i6[2] &= $$.m.i6[2];
632 					  $$.a.i6[3] &= $$.m.i6[3];
633 					  $$.v = $1.v;
634 					  yyexpectaddr = 0;
635 					}
636 	| hostname IPNY_MASK hexnumber	{ $$.a = $1.a;
637 					  $$.m.in4.s_addr = htonl($3);
638 					  $$.a.in4.s_addr &= $$.m.in4.s_addr;
639 					  $$.v = 4;
640 					}
641 	;
642 
643 portstuff:
644 	compare portspec		{ $$.pc = $1; $$.p1 = $2; }
645 	| portspec range portspec	{ $$.pc = $2; $$.p1 = $1; $$.p2 = $3; }
646 	;
647 
648 mapoptions:
649 	rr frag age mssclamp nattag setproto
650 	;
651 
652 rdroptions:
653 	rr frag age sticky mssclamp rdrproxy nattag
654 	;
655 
656 nattag:	| IPNY_TAG YY_STR		{ strncpy(nat->in_tag.ipt_tag, $2,
657 						  sizeof(nat->in_tag.ipt_tag));
658 					}
659 
660 rr:	| IPNY_ROUNDROBIN		{ nat->in_flags |= IPN_ROUNDR; }
661 	;
662 
663 frag:	| IPNY_FRAG			{ nat->in_flags |= IPN_FRAG; }
664 	;
665 
666 age:	| IPNY_AGE YY_NUMBER			{ nat->in_age[0] = $2;
667 						  nat->in_age[1] = $2; }
668 	| IPNY_AGE YY_NUMBER '/' YY_NUMBER	{ nat->in_age[0] = $2;
669 						  nat->in_age[1] = $4; }
670 	;
671 
672 sticky:	| IPNY_STICKY			{ if (!(nat->in_flags & IPN_ROUNDR) &&
673 					      !(nat->in_flags & IPN_SPLIT)) {
674 						fprintf(stderr,
675 		"'sticky' for use with round-robin/IP splitting only\n");
676 					  } else
677 						nat->in_flags |= IPN_STICKY;
678 					}
679 	;
680 
681 mssclamp:
682 	| IPNY_MSSCLAMP YY_NUMBER		{ nat->in_mssclamp = $2; }
683 	;
684 
685 tcpudp:	| IPNY_TCP			{ setnatproto(IPPROTO_TCP); }
686 	| IPNY_UDP			{ setnatproto(IPPROTO_UDP); }
687 	| IPNY_TCPUDP			{ nat->in_flags |= IPN_TCPUDP;
688 					  nat->in_p = 0;
689 					}
690 	| IPNY_TCP '/' IPNY_UDP		{ nat->in_flags |= IPN_TCPUDP;
691 					  nat->in_p = 0;
692 					}
693 	;
694 
695 rdrproxy:
696 	IPNY_PROXY YY_STR
697 					{ strncpy(nat->in_plabel, $2,
698 						  sizeof(nat->in_plabel));
699 					  nat->in_dport = nat->in_pnext;
700 					  nat->in_dport = htons(nat->in_dport);
701 					  free($2);
702 					}
703 	| proxy				{ if (nat->in_plabel[0] != '\0') {
704 						  nat->in_pmin = nat->in_dport;
705 						  nat->in_pmax = nat->in_pmin;
706 						  nat->in_pnext = nat->in_pmin;
707 					  }
708 					}
709 	;
710 
711 proto:	YY_NUMBER			{ $$ = $1; }
712 	| IPNY_TCP			{ $$ = IPPROTO_TCP; }
713 	| IPNY_UDP			{ $$ = IPPROTO_UDP; }
714 	| YY_STR			{ $$ = getproto($1); free($1); }
715 	;
716 
717 hexnumber:
718 	YY_HEX				{ $$ = $1; }
719 	;
720 
721 hostname:
722 	YY_STR				{ i6addr_t addr;
723 					  if (gethost($1, &addr, 0) == 0) {
724 						$$.a = addr;
725 						$$.v = 4;
726 					  } else
727 					  if (gethost($1, &addr, 1) == 0) {
728 						$$.a = addr;
729 						$$.v = 6;
730 					  } else {
731 						yyerror("Unknown hostname");
732 					  }
733 					  if ($$.v != 0)
734 						nat->in_v = $$.v;
735 					  free($1);
736 					}
737 	| YY_NUMBER			{ bzero(&$$.a, sizeof($$.a));
738 					  $$.a.in4.s_addr = htonl($1);
739 					  if ($$.a.in4.s_addr != 0)
740 						$$.v = 4;
741 					  else
742 						$$.v = nat->in_v;
743 					  if ($$.v != 0)
744 						nat->in_v = $$.v;
745 					}
746 	| ipv4				{ $$ = $1;
747 					  nat->in_v = 4;
748 					}
749 	| YY_IPV6			{ $$.a = $1;
750 					  $$.v = 6;
751 					  nat->in_v = 6;
752 					}
753 	| YY_NUMBER YY_IPV6		{ $$.a = $2;
754 					  $$.v = 6;
755 					}
756 	;
757 
758 compare:
759 	'='				{ $$ = FR_EQUAL; }
760 	| YY_CMP_EQ			{ $$ = FR_EQUAL; }
761 	| YY_CMP_NE			{ $$ = FR_NEQUAL; }
762 	| YY_CMP_LT			{ $$ = FR_LESST; }
763 	| YY_CMP_LE			{ $$ = FR_LESSTE; }
764 	| YY_CMP_GT			{ $$ = FR_GREATERT; }
765 	| YY_CMP_GE			{ $$ = FR_GREATERTE; }
766 
767 range:
768 	YY_RANGE_OUT			{ $$ = FR_OUTRANGE; }
769 	| YY_RANGE_IN			{ $$ = FR_INRANGE; }
770 	;
771 
772 ipaddr:	ipv4				{ $$ = $1; }
773 	| YY_IPV6			{ $$.a = $1;
774 					  $$.v = 6;
775 					}
776 	;
777 
778 ipv4:	YY_NUMBER '.' YY_NUMBER '.' YY_NUMBER '.' YY_NUMBER
779 		{ if ($1 > 255 || $3 > 255 || $5 > 255 || $7 > 255) {
780 			yyerror("Invalid octet string for IP address");
781 			return 0;
782 		  }
783 		  $$.a.in4.s_addr = ($1 << 24) | ($3 << 16) | ($5 << 8) | $7;
784 		  $$.a.in4.s_addr = htonl($$.a.in4.s_addr);
785 		  $$.v = 4;
786 		}
787 	;
788 
789 %%
790 
791 
792 static	wordtab_t	yywords[] = {
793 	{ "age",	IPNY_AGE },
794 	{ "any",	IPNY_ANY },
795 	{ "auto",	IPNY_AUTO },
796 	{ "bimap",	IPNY_BIMAP },
797 	{ "frag",	IPNY_FRAG },
798 	{ "from",	IPNY_FROM },
799 	{ "icmpidmap",	IPNY_ICMPIDMAP },
800 	{ "mask",	IPNY_MASK },
801 	{ "map",	IPNY_MAP },
802 	{ "map-block",	IPNY_MAPBLOCK },
803 	{ "mssclamp",	IPNY_MSSCLAMP },
804 	{ "netmask",	IPNY_MASK },
805 	{ "port",	IPNY_PORT },
806 	{ "portmap",	IPNY_PORTMAP },
807 	{ "ports",	IPNY_PORTS },
808 	{ "proxy",	IPNY_PROXY },
809 	{ "range",	IPNY_RANGE },
810 	{ "rdr",	IPNY_RDR },
811 	{ "round-robin",IPNY_ROUNDROBIN },
812 	{ "sequential",	IPNY_SEQUENTIAL },
813 	{ "sticky",	IPNY_STICKY },
814 	{ "tag",	IPNY_TAG },
815 	{ "tcp",	IPNY_TCP },
816 	{ "tcpudp",	IPNY_TCPUDP },
817 	{ "to",		IPNY_TO },
818 	{ "udp",	IPNY_UDP },
819 	{ "-",		'-' },
820 	{ "->",		IPNY_TLATE },
821 	{ "eq",		YY_CMP_EQ },
822 	{ "ne",		YY_CMP_NE },
823 	{ "lt",		YY_CMP_LT },
824 	{ "gt",		YY_CMP_GT },
825 	{ "le",		YY_CMP_LE },
826 	{ "ge",		YY_CMP_GE },
827 	{ NULL,		0 }
828 };
829 
830 
831 int ipnat_parsefile(fd, addfunc, ioctlfunc, filename)
832 int fd;
833 addfunc_t addfunc;
834 ioctlfunc_t ioctlfunc;
835 char *filename;
836 {
837 	FILE *fp = NULL;
838 	char *s;
839 
840 	(void) yysettab(yywords);
841 
842 	s = getenv("YYDEBUG");
843 	if (s)
844 		yydebug = atoi(s);
845 	else
846 		yydebug = 0;
847 
848 	if (strcmp(filename, "-")) {
849 		fp = fopen(filename, "r");
850 		if (!fp) {
851 			fprintf(stderr, "fopen(%s) failed: %s\n", filename,
852 				STRERROR(errno));
853 			return -1;
854 		}
855 	} else
856 		fp = stdin;
857 
858 	while (ipnat_parsesome(fd, addfunc, ioctlfunc, fp) == 1)
859 		;
860 	if (fp != NULL)
861 		fclose(fp);
862 	return 0;
863 }
864 
865 
866 int ipnat_parsesome(fd, addfunc, ioctlfunc, fp)
867 int fd;
868 addfunc_t addfunc;
869 ioctlfunc_t ioctlfunc;
870 FILE *fp;
871 {
872 	char *s;
873 	int i;
874 
875 	yylineNum = 1;
876 
877 	natfd = fd;
878 	nataddfunc = addfunc;
879 	natioctlfunc = ioctlfunc;
880 
881 	if (feof(fp))
882 		return 0;
883 	i = fgetc(fp);
884 	if (i == EOF)
885 		return 0;
886 	if (ungetc(i, fp) == EOF)
887 		return 0;
888 	if (feof(fp))
889 		return 0;
890 	s = getenv("YYDEBUG");
891 	if (s)
892 		yydebug = atoi(s);
893 	else
894 		yydebug = 0;
895 
896 	yyin = fp;
897 	yyparse();
898 	return 1;
899 }
900 
901 
902 static void newnatrule()
903 {
904 	ipnat_t *n;
905 
906 	n = calloc(1, sizeof(*n));
907 	if (n == NULL)
908 		return;
909 
910 	if (nat == NULL)
911 		nattop = nat = n;
912 	else {
913 		nat->in_next = n;
914 		nat = n;
915 	}
916 }
917 
918 
919 static void setnatproto(p)
920 int p;
921 {
922 	nat->in_p = p;
923 
924 	switch (p)
925 	{
926 	case IPPROTO_TCP :
927 		nat->in_flags |= IPN_TCP;
928 		nat->in_flags &= ~IPN_UDP;
929 		break;
930 	case IPPROTO_UDP :
931 		nat->in_flags |= IPN_UDP;
932 		nat->in_flags &= ~IPN_TCP;
933 		break;
934 	case IPPROTO_ICMP :
935 		nat->in_flags &= ~IPN_TCPUDP;
936 		if (!(nat->in_flags & IPN_ICMPQUERY)) {
937 			nat->in_dcmp = 0;
938 			nat->in_scmp = 0;
939 			nat->in_pmin = 0;
940 			nat->in_pmax = 0;
941 			nat->in_pnext = 0;
942 		}
943 		break;
944 	default :
945 		if ((nat->in_redir & NAT_MAPBLK) == 0) {
946 			/* Only reset dcmp/scmp in case dport/sport not set */
947 			if (0 == nat->in_tuc.ftu_dport)
948 				nat->in_dcmp = 0;
949 			if (0 == nat->in_tuc.ftu_sport)
950 				nat->in_scmp = 0;
951 			nat->in_pmin = 0;
952 			nat->in_pmax = 0;
953 			nat->in_pnext = 0;
954 			nat->in_flags &= ~IPN_TCPUDP;
955 		}
956 		break;
957 	}
958 
959 	if ((nat->in_flags & (IPN_TCPUDP|IPN_FIXEDDPORT)) == IPN_FIXEDDPORT)
960 		nat->in_flags &= ~IPN_FIXEDDPORT;
961 }
962 
963 
964 void ipnat_addrule(fd, ioctlfunc, ptr)
965 int fd;
966 ioctlfunc_t ioctlfunc;
967 void *ptr;
968 {
969 	ioctlcmd_t add, del;
970 	ipfobj_t obj;
971 	ipnat_t *ipn;
972 
973 	ipn = ptr;
974 	bzero((char *)&obj, sizeof(obj));
975 	obj.ipfo_rev = IPFILTER_VERSION;
976 	obj.ipfo_size = sizeof(ipnat_t);
977 	obj.ipfo_type = IPFOBJ_IPNAT;
978 	obj.ipfo_ptr = ptr;
979 	add = 0;
980 	del = 0;
981 
982 	if ((opts & OPT_DONOTHING) != 0)
983 		fd = -1;
984 
985 	if (opts & OPT_ZERORULEST) {
986 		add = SIOCZRLST;
987 	} else if (opts & OPT_INACTIVE) {
988 		add = SIOCADNAT;
989 		del = SIOCRMNAT;
990 	} else {
991 		add = SIOCADNAT;
992 		del = SIOCRMNAT;
993 	}
994 
995 	if (ipn && (opts & OPT_VERBOSE))
996 		printnat(ipn, opts);
997 
998 	if (opts & OPT_DEBUG)
999 		binprint(ipn, sizeof(*ipn));
1000 
1001 	if ((opts & OPT_ZERORULEST) != 0) {
1002 		if ((*ioctlfunc)(fd, add, (void *)&obj) == -1) {
1003 			if ((opts & OPT_DONOTHING) == 0) {
1004 				fprintf(stderr, "%d:", yylineNum);
1005 				perror("ioctl(SIOCZRLST)");
1006 			}
1007 		} else {
1008 #ifdef	USE_QUAD_T
1009 /*
1010 			printf("hits %qd bytes %qd ",
1011 				(long long)fr->fr_hits,
1012 				(long long)fr->fr_bytes);
1013 */
1014 #else
1015 /*
1016 			printf("hits %ld bytes %ld ",
1017 				fr->fr_hits, fr->fr_bytes);
1018 */
1019 #endif
1020 			printnat(ipn, opts);
1021 		}
1022 	} else if ((opts & OPT_REMOVE) != 0) {
1023 		if ((*ioctlfunc)(fd, del, (void *)&obj) == -1) {
1024 			if ((opts & OPT_DONOTHING) == 0) {
1025 				fprintf(stderr, "%d:", yylineNum);
1026 				perror("ioctl(delete nat rule)");
1027 			}
1028 		}
1029 	} else {
1030 		if ((*ioctlfunc)(fd, add, (void *)&obj) == -1) {
1031 			if ((opts & OPT_DONOTHING) == 0) {
1032 				fprintf(stderr, "%d:", yylineNum);
1033 				perror("ioctl(add/insert nat rule)");
1034 			}
1035 		}
1036 	}
1037 }
1038 
1039