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