4 * Copyright (C) 2001-2006 by Darren Reed.
6 * See the IPFILTER.LICENCE file for details on licencing.
10 # ifndef __FreeBSD_cc_version
11 # include <osreldate.h>
13 # if __FreeBSD_cc_version < 430000
14 # include <osreldate.h>
23 #if !defined(__SVR4) && !defined(__GNUC__)
26 #include <sys/types.h>
27 #include <sys/param.h>
31 #include <sys/socket.h>
32 #include <sys/ioctl.h>
33 #include <netinet/in.h>
34 #include <netinet/in_systm.h>
38 #if __FreeBSD_version >= 300000
39 # include <net/if_var.h>
42 #include <arpa/nameser.h>
45 #include "netinet/ipl.h"
50 extern void yyerror __P((char *));
51 extern int yyparse __P((void));
52 extern int yylex __P((void));
57 static ipnat_t *nattop = NULL;
58 static ipnat_t *nat = NULL;
59 static int natfd = -1;
60 static ioctlfunc_t natioctlfunc = NULL;
61 static addfunc_t nataddfunc = NULL;
62 static int suggest_port = 0;
64 static void newnatrule __P((void));
65 static void setnatproto __P((int));
87 %token <num> YY_NUMBER YY_HEX
90 %token YY_CMP_EQ YY_CMP_NE YY_CMP_LE YY_CMP_GE YY_CMP_LT YY_CMP_GT
91 %token YY_RANGE_OUT YY_RANGE_IN
94 %token IPNY_MAPBLOCK IPNY_RDR IPNY_PORT IPNY_PORTS IPNY_AUTO IPNY_RANGE
95 %token IPNY_MAP IPNY_BIMAP IPNY_FROM IPNY_TO IPNY_MASK IPNY_PORTMAP IPNY_ANY
96 %token IPNY_ROUNDROBIN IPNY_FRAG IPNY_AGE IPNY_ICMPIDMAP IPNY_PROXY
97 %token IPNY_TCP IPNY_UDP IPNY_TCPUDP IPNY_STICKY IPNY_MSSCLAMP IPNY_TAG
98 %token IPNY_TLATE IPNY_SEQUENTIAL
100 %type <num> hexnumber compare range proto
101 %type <ipa> hostname ipv4
102 %type <ipp> addr nummask rhaddr
111 line: xx rule { while ((nat = nattop) != NULL) {
112 nattop = nat->in_next;
113 (*nataddfunc)(natfd, natioctlfunc, nat);
121 assign: YY_STR assigning YY_STR ';' { set_variable($1, $3);
130 '=' { yyvarnext = 1; }
133 xx: { newnatrule(); }
144 map: mapit ifnames addr IPNY_TLATE rhaddr proxy mapoptions
146 nat->in_inip = $3.a.s_addr;
147 nat->in_inmsk = $3.m.s_addr;
148 nat->in_outip = $5.a.s_addr;
149 nat->in_outmsk = $5.m.s_addr;
150 if (nat->in_ifnames[1][0] == '\0')
151 strncpy(nat->in_ifnames[1],
153 sizeof(nat->in_ifnames[0]));
154 if ((nat->in_flags & IPN_TCPUDP) == 0)
155 setnatproto(nat->in_p);
156 if (((nat->in_redir & NAT_MAPBLK) != 0) ||
157 ((nat->in_flags & IPN_AUTOPORTMAP) != 0))
158 nat_setgroupmap(nat);
160 | mapit ifnames addr IPNY_TLATE rhaddr mapport mapoptions
162 nat->in_inip = $3.a.s_addr;
163 nat->in_inmsk = $3.m.s_addr;
164 nat->in_outip = $5.a.s_addr;
165 nat->in_outmsk = $5.m.s_addr;
166 if (nat->in_ifnames[1][0] == '\0')
167 strncpy(nat->in_ifnames[1],
169 sizeof(nat->in_ifnames[0]));
170 if (((nat->in_redir & NAT_MAPBLK) != 0) ||
171 ((nat->in_flags & IPN_AUTOPORTMAP) != 0))
172 nat_setgroupmap(nat);
174 | mapit ifnames mapfrom IPNY_TLATE rhaddr proxy mapoptions
176 nat->in_outip = $5.a.s_addr;
177 nat->in_outmsk = $5.m.s_addr;
178 if (nat->in_ifnames[1][0] == '\0')
179 strncpy(nat->in_ifnames[1],
181 sizeof(nat->in_ifnames[0]));
182 if ((suggest_port == 1) &&
183 (nat->in_flags & IPN_TCPUDP) == 0)
184 nat->in_flags |= IPN_TCPUDP;
185 if ((nat->in_flags & IPN_TCPUDP) == 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);
191 | mapit ifnames mapfrom IPNY_TLATE rhaddr mapport mapoptions
193 nat->in_outip = $5.a.s_addr;
194 nat->in_outmsk = $5.m.s_addr;
195 if (nat->in_ifnames[1][0] == '\0')
196 strncpy(nat->in_ifnames[1],
198 sizeof(nat->in_ifnames[0]));
199 if ((suggest_port == 1) &&
200 (nat->in_flags & IPN_TCPUDP) == 0)
201 nat->in_flags |= IPN_TCPUDP;
202 if (((nat->in_redir & NAT_MAPBLK) != 0) ||
203 ((nat->in_flags & IPN_AUTOPORTMAP) != 0))
204 nat_setgroupmap(nat);
209 mapblockit ifnames addr IPNY_TLATE addr ports mapoptions
211 nat->in_inip = $3.a.s_addr;
212 nat->in_inmsk = $3.m.s_addr;
213 nat->in_outip = $5.a.s_addr;
214 nat->in_outmsk = $5.m.s_addr;
215 if (nat->in_ifnames[1][0] == '\0')
216 strncpy(nat->in_ifnames[1],
218 sizeof(nat->in_ifnames[0]));
219 if ((nat->in_flags & IPN_TCPUDP) == 0)
220 setnatproto(nat->in_p);
221 if (((nat->in_redir & NAT_MAPBLK) != 0) ||
222 ((nat->in_flags & IPN_AUTOPORTMAP) != 0))
223 nat_setgroupmap(nat);
227 redir: rdrit ifnames addr dport IPNY_TLATE dip nport setproto rdroptions
229 nat->in_outip = $3.a.s_addr;
230 nat->in_outmsk = $3.m.s_addr;
231 if (nat->in_ifnames[1][0] == '\0')
232 strncpy(nat->in_ifnames[1],
234 sizeof(nat->in_ifnames[0]));
235 if ((nat->in_p == 0) &&
236 ((nat->in_flags & IPN_TCPUDP) == 0) &&
237 (nat->in_pmin != 0 ||
240 setnatproto(IPPROTO_TCP);
242 | rdrit ifnames rdrfrom IPNY_TLATE dip nport setproto rdroptions
244 if ((nat->in_p == 0) &&
245 ((nat->in_flags & IPN_TCPUDP) == 0) &&
246 (nat->in_pmin != 0 ||
249 setnatproto(IPPROTO_TCP);
250 if ((suggest_port == 1) &&
251 (nat->in_flags & IPN_TCPUDP) == 0)
252 nat->in_flags |= IPN_TCPUDP;
253 if (nat->in_ifnames[1][0] == '\0')
254 strncpy(nat->in_ifnames[1],
256 sizeof(nat->in_ifnames[0]));
258 | rdrit ifnames addr IPNY_TLATE dip setproto rdroptions
260 nat->in_outip = $3.a.s_addr;
261 nat->in_outmsk = $3.m.s_addr;
262 if (nat->in_ifnames[1][0] == '\0')
263 strncpy(nat->in_ifnames[1],
265 sizeof(nat->in_ifnames[0]));
267 | rdrit ifnames rdrfrom IPNY_TLATE dip setproto rdroptions
269 if ((suggest_port == 1) &&
270 (nat->in_flags & IPN_TCPUDP) == 0)
271 nat->in_flags |= IPN_TCPUDP;
272 if (nat->in_ifnames[1][0] == '\0')
273 strncpy(nat->in_ifnames[1],
275 sizeof(nat->in_ifnames[0]));
279 proxy: | IPNY_PROXY 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");
289 | IPNY_PROXY port YY_STR YY_STR '/' proto
291 strncpy(nat->in_plabel, $4, sizeof(nat->in_plabel));
292 pnum = getportproto($3, $6);
294 yyerror("invalid port number");
295 nat->in_dport = pnum;
303 | proto { if (nat->in_p != 0 ||
304 nat->in_flags & IPN_TCPUDP)
305 yyerror("protocol set twice");
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;
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;
322 rhaddr: addr { $$.a = $1.a; $$.m = $1.m; }
323 | IPNY_RANGE ipv4 '-' ipv4
324 { $$.a = $2; $$.m = $4;
325 nat->in_flags |= IPN_IPRANGE; }
329 hostname { nat->in_inip = $1.s_addr;
330 nat->in_inmsk = 0xffffffff; }
331 | hostname '/' YY_NUMBER { if ($3 != 0 || $1.s_addr != 0)
332 yyerror("Only 0/0 supported");
336 | hostname ',' hostname { nat->in_flags |= IPN_SPLIT;
337 nat->in_inip = $1.s_addr;
338 nat->in_inmsk = $3.s_addr; }
341 port: IPNY_PORT { suggest_port = 1; }
345 YY_NUMBER { if ($1 > 65535) /* Unsigned */
346 yyerror("invalid port number");
350 | YY_STR { if (getport(NULL, $1, &($$)) == -1)
351 yyerror("invalid port number");
356 dport: | port portspec { nat->in_pmin = htons($2);
357 nat->in_pmax = htons($2); }
358 | port portspec '-' portspec { nat->in_pmin = htons($2);
359 nat->in_pmax = htons($4); }
360 | port portspec ':' portspec { nat->in_pmin = htons($2);
361 nat->in_pmax = htons($4); }
364 nport: port portspec { nat->in_pnext = htons($2); }
365 | port '=' portspec { nat->in_pnext = htons($3);
366 nat->in_flags |= IPN_FIXEDDPORT;
370 ports: | IPNY_PORTS YY_NUMBER { nat->in_pmin = $2; }
371 | IPNY_PORTS IPNY_AUTO { nat->in_flags |= IPN_AUTOPORTMAP; }
374 mapit: IPNY_MAP { nat->in_redir = NAT_MAP; }
375 | IPNY_BIMAP { nat->in_redir = NAT_BIMAP; }
378 rdrit: IPNY_RDR { nat->in_redir = NAT_REDIRECT; }
382 IPNY_MAPBLOCK { nat->in_redir = NAT_MAPBLK; }
386 from sobject IPNY_TO dobject
387 | from sobject '!' IPNY_TO dobject
388 { nat->in_flags |= IPN_NOTDST; }
389 | from sobject IPNY_TO '!' dobject
390 { nat->in_flags |= IPN_NOTDST; }
394 from sobject IPNY_TO dobject
395 | '!' from sobject IPNY_TO dobject
396 { nat->in_flags |= IPN_NOTSRC; }
397 | from '!' sobject IPNY_TO dobject
398 { nat->in_flags |= IPN_NOTSRC; }
401 from: IPNY_FROM { nat->in_flags |= IPN_FILTER; }
406 | ifname ',' otherifname
409 ifname: YY_STR { strncpy(nat->in_ifnames[0], $1,
410 sizeof(nat->in_ifnames[0]));
411 nat->in_ifnames[0][LIFNAMSIZ - 1] = '\0';
417 YY_STR { strncpy(nat->in_ifnames[1], $1,
418 sizeof(nat->in_ifnames[1]));
419 nat->in_ifnames[1][LIFNAMSIZ - 1] = '\0';
425 IPNY_PORTMAP tcpudp portspec ':' portspec randport
426 { nat->in_pmin = htons($3);
427 nat->in_pmax = htons($5);
429 | IPNY_PORTMAP tcpudp IPNY_AUTO randport
430 { nat->in_flags |= IPN_AUTOPORTMAP;
431 nat->in_pmin = htons(1024);
432 nat->in_pmax = htons(65535);
434 | IPNY_ICMPIDMAP YY_STR YY_NUMBER ':' YY_NUMBER
435 { if (strcmp($2, "icmp") != 0) {
436 yyerror("icmpidmap not followed by icmp");
439 if ($3 < 0 || $3 > 65535)
440 yyerror("invalid ICMP Id number");
441 if ($5 < 0 || $5 > 65535)
442 yyerror("invalid ICMP Id number");
443 nat->in_flags = IPN_ICMPQUERY;
444 nat->in_pmin = htons($3);
445 nat->in_pmax = htons($5);
450 | IPNY_SEQUENTIAL { nat->in_flags |= IPN_SEQUENTIAL; }
455 | saddr port portstuff { nat->in_sport = $3.p1;
456 nat->in_stop = $3.p2;
457 nat->in_scmp = $3.pc; }
460 saddr: addr { if (nat->in_redir == NAT_REDIRECT) {
461 nat->in_srcip = $1.a.s_addr;
462 nat->in_srcmsk = $1.m.s_addr;
464 nat->in_inip = $1.a.s_addr;
465 nat->in_inmsk = $1.m.s_addr;
472 | daddr port portstuff { nat->in_dport = $3.p1;
473 nat->in_dtop = $3.p2;
474 nat->in_dcmp = $3.pc;
475 if (nat->in_redir == NAT_REDIRECT)
476 nat->in_pmin = htons($3.p1);
480 daddr: addr { if (nat->in_redir == NAT_REDIRECT) {
481 nat->in_outip = $1.a.s_addr;
482 nat->in_outmsk = $1.m.s_addr;
484 nat->in_srcip = $1.a.s_addr;
485 nat->in_srcmsk = $1.m.s_addr;
490 addr: IPNY_ANY { $$.a.s_addr = 0; $$.m.s_addr = 0; }
491 | nummask { $$.a = $1.a; $$.m = $1.m;
492 $$.a.s_addr &= $$.m.s_addr; }
493 | hostname '/' ipv4 { $$.a = $1; $$.m = $3;
494 $$.a.s_addr &= $$.m.s_addr; }
495 | hostname '/' hexnumber { $$.a = $1; $$.m.s_addr = htonl($3);
496 $$.a.s_addr &= $$.m.s_addr; }
497 | hostname IPNY_MASK ipv4 { $$.a = $1; $$.m = $3;
498 $$.a.s_addr &= $$.m.s_addr; }
499 | hostname IPNY_MASK hexnumber { $$.a = $1; $$.m.s_addr = htonl($3);
500 $$.a.s_addr &= $$.m.s_addr; }
504 hostname { $$.a = $1;
505 $$.m.s_addr = 0xffffffff; }
506 | hostname '/' YY_NUMBER { $$.a = $1;
507 ntomask(4, $3, &$$.m.s_addr); }
511 compare portspec { $$.pc = $1; $$.p1 = $2; }
512 | portspec range portspec { $$.pc = $2; $$.p1 = $1; $$.p2 = $3; }
516 rr frag age mssclamp nattag setproto
520 rr frag age sticky mssclamp rdrproxy nattag
523 nattag: | IPNY_TAG YY_STR { strncpy(nat->in_tag.ipt_tag, $2,
524 sizeof(nat->in_tag.ipt_tag));
527 rr: | IPNY_ROUNDROBIN { nat->in_flags |= IPN_ROUNDR; }
530 frag: | IPNY_FRAG { nat->in_flags |= IPN_FRAG; }
533 age: | IPNY_AGE YY_NUMBER { nat->in_age[0] = $2;
534 nat->in_age[1] = $2; }
535 | IPNY_AGE YY_NUMBER '/' YY_NUMBER { nat->in_age[0] = $2;
536 nat->in_age[1] = $4; }
539 sticky: | IPNY_STICKY { if (!(nat->in_flags & IPN_ROUNDR) &&
540 !(nat->in_flags & IPN_SPLIT)) {
542 "'sticky' for use with round-robin/IP splitting only\n");
544 nat->in_flags |= IPN_STICKY;
549 | IPNY_MSSCLAMP YY_NUMBER { nat->in_mssclamp = $2; }
552 tcpudp: | IPNY_TCP { setnatproto(IPPROTO_TCP); }
553 | IPNY_UDP { setnatproto(IPPROTO_UDP); }
554 | IPNY_TCPUDP { nat->in_flags |= IPN_TCPUDP;
557 | IPNY_TCP '/' IPNY_UDP { nat->in_flags |= IPN_TCPUDP;
564 { strncpy(nat->in_plabel, $2,
565 sizeof(nat->in_plabel));
566 nat->in_dport = nat->in_pnext;
567 nat->in_dport = htons(nat->in_dport);
570 | proxy { if (nat->in_plabel[0] != '\0') {
571 nat->in_pmin = nat->in_dport;
572 nat->in_pmax = nat->in_pmin;
573 nat->in_pnext = nat->in_pmin;
578 proto: YY_NUMBER { $$ = $1;
579 if ($$ != IPPROTO_TCP &&
583 | IPNY_TCP { $$ = IPPROTO_TCP; }
584 | IPNY_UDP { $$ = IPPROTO_UDP; }
585 | YY_STR { $$ = getproto($1); free($1);
586 if ($$ != IPPROTO_TCP &&
597 YY_STR { if (gethost($1, &$$.s_addr) == -1)
599 "Unknown host '%s'\n",
603 | YY_NUMBER { $$.s_addr = htonl($1); }
604 | ipv4 { $$.s_addr = $1.s_addr; }
608 '=' { $$ = FR_EQUAL; }
609 | YY_CMP_EQ { $$ = FR_EQUAL; }
610 | YY_CMP_NE { $$ = FR_NEQUAL; }
611 | YY_CMP_LT { $$ = FR_LESST; }
612 | YY_CMP_LE { $$ = FR_LESSTE; }
613 | YY_CMP_GT { $$ = FR_GREATERT; }
614 | YY_CMP_GE { $$ = FR_GREATERTE; }
617 YY_RANGE_OUT { $$ = FR_OUTRANGE; }
618 | YY_RANGE_IN { $$ = FR_INRANGE; }
619 | ':' { $$ = FR_INCRANGE; }
622 ipv4: YY_NUMBER '.' YY_NUMBER '.' YY_NUMBER '.' YY_NUMBER
623 { if ($1 > 255 || $3 > 255 || $5 > 255 || $7 > 255) {
624 yyerror("Invalid octet string for IP address");
627 $$.s_addr = ($1 << 24) | ($3 << 16) | ($5 << 8) | $7;
628 $$.s_addr = htonl($$.s_addr);
635 static wordtab_t yywords[] = {
638 { "auto", IPNY_AUTO },
639 { "bimap", IPNY_BIMAP },
640 { "frag", IPNY_FRAG },
641 { "from", IPNY_FROM },
642 { "icmpidmap", IPNY_ICMPIDMAP },
643 { "mask", IPNY_MASK },
645 { "map-block", IPNY_MAPBLOCK },
646 { "mssclamp", IPNY_MSSCLAMP },
647 { "netmask", IPNY_MASK },
648 { "port", IPNY_PORT },
649 { "portmap", IPNY_PORTMAP },
650 { "ports", IPNY_PORTS },
651 { "proxy", IPNY_PROXY },
652 { "range", IPNY_RANGE },
654 { "round-robin",IPNY_ROUNDROBIN },
655 { "sequential", IPNY_SEQUENTIAL },
656 { "sticky", IPNY_STICKY },
659 { "tcpudp", IPNY_TCPUDP },
663 { "->", IPNY_TLATE },
674 int ipnat_parsefile(fd, addfunc, ioctlfunc, filename)
677 ioctlfunc_t ioctlfunc;
683 (void) yysettab(yywords);
685 s = getenv("YYDEBUG");
691 if (strcmp(filename, "-")) {
692 fp = fopen(filename, "r");
694 fprintf(stderr, "fopen(%s) failed: %s\n", filename,
701 while (ipnat_parsesome(fd, addfunc, ioctlfunc, fp) == 1)
709 int ipnat_parsesome(fd, addfunc, ioctlfunc, fp)
712 ioctlfunc_t ioctlfunc;
721 nataddfunc = addfunc;
722 natioctlfunc = ioctlfunc;
729 if (ungetc(i, fp) == EOF)
733 s = getenv("YYDEBUG");
745 static void newnatrule()
749 n = calloc(1, sizeof(*n));
764 static void setnatproto(p)
772 nat->in_flags |= IPN_TCP;
773 nat->in_flags &= ~IPN_UDP;
776 nat->in_flags |= IPN_UDP;
777 nat->in_flags &= ~IPN_TCP;
780 nat->in_flags &= ~IPN_TCPUDP;
781 if (!(nat->in_flags & IPN_ICMPQUERY)) {
790 if ((nat->in_redir & NAT_MAPBLK) == 0) {
791 nat->in_flags &= ~IPN_TCPUDP;
801 if ((nat->in_flags & (IPN_TCPUDP|IPN_FIXEDDPORT)) == IPN_FIXEDDPORT)
802 nat->in_flags &= ~IPN_FIXEDDPORT;
806 void ipnat_addrule(fd, ioctlfunc, ptr)
808 ioctlfunc_t ioctlfunc;
816 bzero((char *)&obj, sizeof(obj));
817 obj.ipfo_rev = IPFILTER_VERSION;
818 obj.ipfo_size = sizeof(ipnat_t);
819 obj.ipfo_type = IPFOBJ_IPNAT;
824 if ((opts & OPT_DONOTHING) != 0)
827 if (opts & OPT_ZERORULEST) {
829 } else if (opts & OPT_INACTIVE) {
837 if ((opts & OPT_VERBOSE) != 0)
840 if (opts & OPT_DEBUG)
841 binprint(ipn, sizeof(*ipn));
843 if ((opts & OPT_ZERORULEST) != 0) {
844 if ((*ioctlfunc)(fd, add, (void *)&obj) == -1) {
845 if ((opts & OPT_DONOTHING) == 0) {
846 fprintf(stderr, "%d:", yylineNum);
847 perror("ioctl(SIOCZRLST)");
852 printf("hits %qd bytes %qd ",
853 (long long)fr->fr_hits,
854 (long long)fr->fr_bytes);
858 printf("hits %ld bytes %ld ",
859 fr->fr_hits, fr->fr_bytes);
864 } else if ((opts & OPT_REMOVE) != 0) {
865 if ((*ioctlfunc)(fd, del, (void *)&obj) == -1) {
866 if ((opts & OPT_DONOTHING) == 0) {
867 fprintf(stderr, "%d:", yylineNum);
868 perror("ioctl(delete nat rule)");
872 if ((*ioctlfunc)(fd, add, (void *)&obj) == -1) {
873 if ((opts & OPT_DONOTHING) == 0) {
874 fprintf(stderr, "%d:", yylineNum);
875 perror("ioctl(add/insert nat rule)");