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