xref: /illumos-gate/usr/src/cmd/ipf/tools/ipnat_y.y (revision d6c23f6f)
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
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
494 			{ nat->in_pmin = htons($3);
495 			  nat->in_pmax = htons($5);
496 			}
497 	| IPNY_PORTMAP tcpudp IPNY_AUTO
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 sobject:
518 	saddr				{ $$ = $1; }
519 	| saddr IPNY_PORT portstuff	{ nat->in_sport = $3.p1;
520 					  nat->in_stop = $3.p2;
521 					  nat->in_scmp = $3.pc;
522 					  $$ = $1;
523 					}
524 	;
525 
526 saddr:	addr				{ if (nat->in_redir == NAT_REDIRECT) {
527 						bcopy(&$1.a, &nat->in_src[0],
528 							sizeof($1.a));
529 						bcopy(&$1.m, &nat->in_src[1],
530 							sizeof($1.a));
531 					  } else {
532 						bcopy(&$1.a, &nat->in_in[0],
533 							sizeof($1.a));
534 						bcopy(&$1.m, &nat->in_in[1],
535 							sizeof($1.a));
536 					  }
537 					  $$ = $1.v;
538 					}
539 	;
540 
541 dobject:
542 	daddr				{ $$ = $1; }
543 	| daddr IPNY_PORT portstuff	{ nat->in_dport = $3.p1;
544 					  nat->in_dtop = $3.p2;
545 					  nat->in_dcmp = $3.pc;
546 					  if (nat->in_redir == NAT_REDIRECT)
547 						nat->in_pmin = htons($3.p1);
548 					}
549 	;
550 
551 daddr:	addr				{ if (nat->in_redir == NAT_REDIRECT) {
552 						bcopy(&$1.a, &nat->in_out[0],
553 							sizeof($1.a));
554 						bcopy(&$1.m, &nat->in_out[1],
555 							sizeof($1.a));
556 					  } else {
557 						bcopy(&$1.a, &nat->in_src[0],
558 							sizeof($1.a));
559 						bcopy(&$1.m, &nat->in_src[1],
560 							sizeof($1.a));
561 					  }
562 					  $$ = $1.v;
563 					}
564 	;
565 
566 addr:	IPNY_ANY			{ yyexpectaddr = 0;
567 					  bzero(&$$.a, sizeof($$.a));
568 					  bzero(&$$.m, sizeof($$.a));
569 					  $$.v = nat->in_v;
570 					}
571 	| hostname			{ $$.a = $1.a;
572 					  $$.v = $1.v;
573 					  if ($$.v == 4) {
574 						$$.m.in4.s_addr = 0xffffffff;
575 					  } else {
576 						$$.m.i6[0] = 0xffffffff;
577 						$$.m.i6[1] = 0xffffffff;
578 						$$.m.i6[2] = 0xffffffff;
579 						$$.m.i6[3] = 0xffffffff;
580 					  }
581 					  yyexpectaddr = 0;
582 					}
583 	| hostname '/' YY_NUMBER	{ $$.a = $1.a;
584 					  if ($1.v == 0) {
585 						if ($1.a.in4.s_addr != 0)
586 							yyerror("invalid addr");
587 						if ($3 == 0 || $3 == 32)
588 							$1.v = 4;
589 						else if ($3 == 128)
590 							$1.v = 6;
591 						else
592 							yyerror("invalid mask");
593 						nat->in_v = $1.v;
594 					  }
595 					  ntomask($1.v, $3, (u_32_t *)&$$.m);
596 					  $$.a.i6[0] &= $$.m.i6[0];
597 					  $$.a.i6[1] &= $$.m.i6[1];
598 					  $$.a.i6[2] &= $$.m.i6[2];
599 					  $$.a.i6[3] &= $$.m.i6[3];
600 					  $$.v = $1.v;
601 					  yyexpectaddr = 0;
602 					}
603 	| hostname '/' ipaddr		{ if ($1.v != $3.v) {
604 						yyerror("1.address family "
605 							"mismatch");
606 					  }
607 					  $$.a = $1.a;
608 					  $$.m = $3.a;
609 					  $$.a.i6[0] &= $$.m.i6[0];
610 					  $$.a.i6[1] &= $$.m.i6[1];
611 					  $$.a.i6[2] &= $$.m.i6[2];
612 					  $$.a.i6[3] &= $$.m.i6[3];
613 					  $$.v = $1.v;
614 					  yyexpectaddr = 0;
615 					}
616 	| hostname '/' hexnumber	{ $$.a = $1.a;
617 					  $$.m.in4.s_addr = htonl($3);
618 					  $$.a.in4.s_addr &= $$.m.in4.s_addr;
619 					  $$.v = 4;
620 					}
621 	| hostname IPNY_MASK ipaddr	{ if ($1.v != $3.v) {
622 						yyerror("2.address family "
623 							"mismatch");
624 					  }
625 					  $$.a = $1.a;
626 					  $$.m = $3.a;
627 					  $$.a.i6[0] &= $$.m.i6[0];
628 					  $$.a.i6[1] &= $$.m.i6[1];
629 					  $$.a.i6[2] &= $$.m.i6[2];
630 					  $$.a.i6[3] &= $$.m.i6[3];
631 					  $$.v = $1.v;
632 					  yyexpectaddr = 0;
633 					}
634 	| hostname IPNY_MASK hexnumber	{ $$.a = $1.a;
635 					  $$.m.in4.s_addr = htonl($3);
636 					  $$.a.in4.s_addr &= $$.m.in4.s_addr;
637 					  $$.v = 4;
638 					}
639 	;
640 
641 portstuff:
642 	compare portspec		{ $$.pc = $1; $$.p1 = $2; }
643 	| portspec range portspec	{ $$.pc = $2; $$.p1 = $1; $$.p2 = $3; }
644 	;
645 
646 mapoptions:
647 	rr frag age mssclamp nattag setproto
648 	;
649 
650 rdroptions:
651 	rr frag age sticky mssclamp rdrproxy nattag
652 	;
653 
654 nattag:	| IPNY_TAG YY_STR		{ strncpy(nat->in_tag.ipt_tag, $2,
655 						  sizeof(nat->in_tag.ipt_tag));
656 					}
657 rr:	| IPNY_ROUNDROBIN		{ nat->in_flags |= IPN_ROUNDR; }
658 	;
659 
660 frag:	| IPNY_FRAG			{ nat->in_flags |= IPN_FRAG; }
661 	;
662 
663 age:	| IPNY_AGE YY_NUMBER			{ nat->in_age[0] = $2;
664 						  nat->in_age[1] = $2; }
665 	| IPNY_AGE YY_NUMBER '/' YY_NUMBER	{ nat->in_age[0] = $2;
666 						  nat->in_age[1] = $4; }
667 	;
668 
669 sticky:	| IPNY_STICKY			{ if (!(nat->in_flags & IPN_ROUNDR) &&
670 					      !(nat->in_flags & IPN_SPLIT)) {
671 						fprintf(stderr,
672 		"'sticky' for use with round-robin/IP splitting only\n");
673 					  } else
674 						nat->in_flags |= IPN_STICKY;
675 					}
676 	;
677 
678 mssclamp:
679 	| IPNY_MSSCLAMP YY_NUMBER		{ nat->in_mssclamp = $2; }
680 	;
681 
682 tcpudp:	| IPNY_TCP			{ setnatproto(IPPROTO_TCP); }
683 	| IPNY_UDP			{ setnatproto(IPPROTO_UDP); }
684 	| IPNY_TCPUDP			{ nat->in_flags |= IPN_TCPUDP;
685 					  nat->in_p = 0;
686 					}
687 	| IPNY_TCP '/' IPNY_UDP		{ nat->in_flags |= IPN_TCPUDP;
688 					  nat->in_p = 0;
689 					}
690 	;
691 
692 rdrproxy:
693 	IPNY_PROXY YY_STR
694 					{ strncpy(nat->in_plabel, $2,
695 						  sizeof(nat->in_plabel));
696 					  nat->in_dport = nat->in_pnext;
697 					  nat->in_dport = htons(nat->in_dport);
698 					  free($2);
699 					}
700 	| proxy				{ if (nat->in_plabel[0] != '\0') {
701 						  nat->in_pmin = nat->in_dport;
702 						  nat->in_pmax = nat->in_pmin;
703 						  nat->in_pnext = nat->in_pmin;
704 					  }
705 					}
706 	;
707 
708 proto:	YY_NUMBER			{ $$ = $1; }
709 	| IPNY_TCP			{ $$ = IPPROTO_TCP; }
710 	| IPNY_UDP			{ $$ = IPPROTO_UDP; }
711 	| YY_STR			{ $$ = getproto($1); free($1); }
712 	;
713 
714 hexnumber:
715 	YY_HEX				{ $$ = $1; }
716 	;
717 
718 hostname:
719 	YY_STR				{ i6addr_t addr;
720 					  if (gethost($1, &addr, 0) == 0) {
721 						$$.a = addr;
722 						$$.v = 4;
723 					  } else
724 					  if (gethost($1, &addr, 1) == 0) {
725 						$$.a = addr;
726 						$$.v = 6;
727 					  } else {
728 						yyerror("Unknown hostname");
729 					  }
730 					  if ($$.v != 0)
731 						nat->in_v = $$.v;
732 					  free($1);
733 					}
734 	| YY_NUMBER			{ bzero(&$$.a, sizeof($$.a));
735 					  $$.a.in4.s_addr = htonl($1);
736 					  if ($$.a.in4.s_addr != 0)
737 						$$.v = 4;
738 					  else
739 						$$.v = nat->in_v;
740 					  if ($$.v != 0)
741 						nat->in_v = $$.v;
742 					}
743 	| ipv4				{ $$ = $1;
744 					  nat->in_v = 4;
745 					}
746 	| YY_IPV6			{ $$.a = $1;
747 					  $$.v = 6;
748 					  nat->in_v = 6;
749 					}
750 	| YY_NUMBER YY_IPV6		{ $$.a = $2;
751 					  $$.v = 6;
752 					}
753 	;
754 
755 compare:
756 	'='				{ $$ = FR_EQUAL; }
757 	| YY_CMP_EQ			{ $$ = FR_EQUAL; }
758 	| YY_CMP_NE			{ $$ = FR_NEQUAL; }
759 	| YY_CMP_LT			{ $$ = FR_LESST; }
760 	| YY_CMP_LE			{ $$ = FR_LESSTE; }
761 	| YY_CMP_GT			{ $$ = FR_GREATERT; }
762 	| YY_CMP_GE			{ $$ = FR_GREATERTE; }
763 
764 range:
765 	YY_RANGE_OUT			{ $$ = FR_OUTRANGE; }
766 	| YY_RANGE_IN			{ $$ = FR_INRANGE; }
767 	;
768 
769 ipaddr:	ipv4				{ $$ = $1; }
770 	| YY_IPV6			{ $$.a = $1;
771 					  $$.v = 6;
772 					}
773 	;
774 
775 ipv4:	YY_NUMBER '.' YY_NUMBER '.' YY_NUMBER '.' YY_NUMBER
776 		{ if ($1 > 255 || $3 > 255 || $5 > 255 || $7 > 255) {
777 			yyerror("Invalid octet string for IP address");
778 			return 0;
779 		  }
780 		  $$.a.in4.s_addr = ($1 << 24) | ($3 << 16) | ($5 << 8) | $7;
781 		  $$.a.in4.s_addr = htonl($$.a.in4.s_addr);
782 		  $$.v = 4;
783 		}
784 	;
785 
786 %%
787 
788 
789 static	wordtab_t	yywords[] = {
790 	{ "age",	IPNY_AGE },
791 	{ "any",	IPNY_ANY },
792 	{ "auto",	IPNY_AUTO },
793 	{ "bimap",	IPNY_BIMAP },
794 	{ "frag",	IPNY_FRAG },
795 	{ "from",	IPNY_FROM },
796 	{ "icmpidmap",	IPNY_ICMPIDMAP },
797 	{ "mask",	IPNY_MASK },
798 	{ "map",	IPNY_MAP },
799 	{ "map-block",	IPNY_MAPBLOCK },
800 	{ "mssclamp",	IPNY_MSSCLAMP },
801 	{ "netmask",	IPNY_MASK },
802 	{ "port",	IPNY_PORT },
803 	{ "portmap",	IPNY_PORTMAP },
804 	{ "ports",	IPNY_PORTS },
805 	{ "proxy",	IPNY_PROXY },
806 	{ "range",	IPNY_RANGE },
807 	{ "rdr",	IPNY_RDR },
808 	{ "round-robin",IPNY_ROUNDROBIN },
809 	{ "sticky",	IPNY_STICKY },
810 	{ "tag",	IPNY_TAG },
811 	{ "tcp",	IPNY_TCP },
812 	{ "tcpudp",	IPNY_TCPUDP },
813 	{ "to",		IPNY_TO },
814 	{ "udp",	IPNY_UDP },
815 	{ "-",		'-' },
816 	{ "->",		IPNY_TLATE },
817 	{ "eq",		YY_CMP_EQ },
818 	{ "ne",		YY_CMP_NE },
819 	{ "lt",		YY_CMP_LT },
820 	{ "gt",		YY_CMP_GT },
821 	{ "le",		YY_CMP_LE },
822 	{ "ge",		YY_CMP_GE },
823 	{ NULL,		0 }
824 };
825 
826 
827 int ipnat_parsefile(fd, addfunc, ioctlfunc, filename)
828 int fd;
829 addfunc_t addfunc;
830 ioctlfunc_t ioctlfunc;
831 char *filename;
832 {
833 	FILE *fp = NULL;
834 	char *s;
835 
836 	(void) yysettab(yywords);
837 
838 	s = getenv("YYDEBUG");
839 	if (s)
840 		yydebug = atoi(s);
841 	else
842 		yydebug = 0;
843 
844 	if (strcmp(filename, "-")) {
845 		fp = fopen(filename, "r");
846 		if (!fp) {
847 			fprintf(stderr, "fopen(%s) failed: %s\n", filename,
848 				STRERROR(errno));
849 			return -1;
850 		}
851 	} else
852 		fp = stdin;
853 
854 	while (ipnat_parsesome(fd, addfunc, ioctlfunc, fp) == 1)
855 		;
856 	if (fp != NULL)
857 		fclose(fp);
858 	return 0;
859 }
860 
861 
862 int ipnat_parsesome(fd, addfunc, ioctlfunc, fp)
863 int fd;
864 addfunc_t addfunc;
865 ioctlfunc_t ioctlfunc;
866 FILE *fp;
867 {
868 	char *s;
869 	int i;
870 
871 	yylineNum = 1;
872 
873 	natfd = fd;
874 	nataddfunc = addfunc;
875 	natioctlfunc = ioctlfunc;
876 
877 	if (feof(fp))
878 		return 0;
879 	i = fgetc(fp);
880 	if (i == EOF)
881 		return 0;
882 	if (ungetc(i, fp) == EOF)
883 		return 0;
884 	if (feof(fp))
885 		return 0;
886 	s = getenv("YYDEBUG");
887 	if (s)
888 		yydebug = atoi(s);
889 	else
890 		yydebug = 0;
891 
892 	yyin = fp;
893 	yyparse();
894 	return 1;
895 }
896 
897 
898 static void newnatrule()
899 {
900 	ipnat_t *n;
901 
902 	n = calloc(1, sizeof(*n));
903 	if (n == NULL)
904 		return;
905 
906 	if (nat == NULL)
907 		nattop = nat = n;
908 	else {
909 		nat->in_next = n;
910 		nat = n;
911 	}
912 }
913 
914 
915 static void setnatproto(p)
916 int p;
917 {
918 	nat->in_p = p;
919 
920 	switch (p)
921 	{
922 	case IPPROTO_TCP :
923 		nat->in_flags |= IPN_TCP;
924 		nat->in_flags &= ~IPN_UDP;
925 		break;
926 	case IPPROTO_UDP :
927 		nat->in_flags |= IPN_UDP;
928 		nat->in_flags &= ~IPN_TCP;
929 		break;
930 	case IPPROTO_ICMP :
931 		nat->in_flags &= ~IPN_TCPUDP;
932 		if (!(nat->in_flags & IPN_ICMPQUERY)) {
933 			nat->in_dcmp = 0;
934 			nat->in_scmp = 0;
935 			nat->in_pmin = 0;
936 			nat->in_pmax = 0;
937 			nat->in_pnext = 0;
938 		}
939 		break;
940 	default :
941 		if ((nat->in_redir & NAT_MAPBLK) == 0) {
942 			/* Only reset dcmp/scmp in case dport/sport not set */
943 			if (0 == nat->in_tuc.ftu_dport)
944 				nat->in_dcmp = 0;
945 			if (0 == nat->in_tuc.ftu_sport)
946 				nat->in_scmp = 0;
947 			nat->in_pmin = 0;
948 			nat->in_pmax = 0;
949 			nat->in_pnext = 0;
950 			nat->in_flags &= ~IPN_TCPUDP;
951 		}
952 		break;
953 	}
954 
955 	if ((nat->in_flags & (IPN_TCPUDP|IPN_FIXEDDPORT)) == IPN_FIXEDDPORT)
956 		nat->in_flags &= ~IPN_FIXEDDPORT;
957 }
958 
959 
960 void ipnat_addrule(fd, ioctlfunc, ptr)
961 int fd;
962 ioctlfunc_t ioctlfunc;
963 void *ptr;
964 {
965 	ioctlcmd_t add, del;
966 	ipfobj_t obj;
967 	ipnat_t *ipn;
968 
969 	ipn = ptr;
970 	bzero((char *)&obj, sizeof(obj));
971 	obj.ipfo_rev = IPFILTER_VERSION;
972 	obj.ipfo_size = sizeof(ipnat_t);
973 	obj.ipfo_type = IPFOBJ_IPNAT;
974 	obj.ipfo_ptr = ptr;
975 	add = 0;
976 	del = 0;
977 
978 	if ((opts & OPT_DONOTHING) != 0)
979 		fd = -1;
980 
981 	if (opts & OPT_ZERORULEST) {
982 		add = SIOCZRLST;
983 	} else if (opts & OPT_INACTIVE) {
984 		add = SIOCADNAT;
985 		del = SIOCRMNAT;
986 	} else {
987 		add = SIOCADNAT;
988 		del = SIOCRMNAT;
989 	}
990 
991 	if (ipn && (opts & OPT_VERBOSE))
992 		printnat(ipn, opts);
993 
994 	if (opts & OPT_DEBUG)
995 		binprint(ipn, sizeof(*ipn));
996 
997 	if ((opts & OPT_ZERORULEST) != 0) {
998 		if ((*ioctlfunc)(fd, add, (void *)&obj) == -1) {
999 			if ((opts & OPT_DONOTHING) == 0) {
1000 				fprintf(stderr, "%d:", yylineNum);
1001 				perror("ioctl(SIOCZRLST)");
1002 			}
1003 		} else {
1004 #ifdef	USE_QUAD_T
1005 /*
1006 			printf("hits %qd bytes %qd ",
1007 				(long long)fr->fr_hits,
1008 				(long long)fr->fr_bytes);
1009 */
1010 #else
1011 /*
1012 			printf("hits %ld bytes %ld ",
1013 				fr->fr_hits, fr->fr_bytes);
1014 */
1015 #endif
1016 			printnat(ipn, opts);
1017 		}
1018 	} else if ((opts & OPT_REMOVE) != 0) {
1019 		if ((*ioctlfunc)(fd, del, (void *)&obj) == -1) {
1020 			if ((opts & OPT_DONOTHING) == 0) {
1021 				fprintf(stderr, "%d:", yylineNum);
1022 				perror("ioctl(delete nat rule)");
1023 			}
1024 		}
1025 	} else {
1026 		if ((*ioctlfunc)(fd, add, (void *)&obj) == -1) {
1027 			if ((opts & OPT_DONOTHING) == 0) {
1028 				fprintf(stderr, "%d:", yylineNum);
1029 				perror("ioctl(add/insert nat rule)");
1030 			}
1031 		}
1032 	}
1033 }
1034 
1035