2 * Copyright (C) 2001-2006 by Darren Reed.
4 * See the IPFILTER.LICENCE file for details on licencing.
8 # ifndef __FreeBSD_cc_version
9 # include <osreldate.h>
11 # if __FreeBSD_cc_version < 430000
12 # include <osreldate.h>
21 #if !defined(__SVR4) && !defined(__GNUC__)
24 #include <sys/types.h>
25 #include <sys/param.h>
29 #include <sys/socket.h>
30 #include <sys/ioctl.h>
31 #include <netinet/in.h>
32 #include <netinet/in_systm.h>
36 #if __FreeBSD_version >= 300000
37 # include <net/if_var.h>
40 #include <arpa/nameser.h>
43 #include "netinet/ipl.h"
48 extern void yyerror __P((char *));
49 extern int yyparse __P((void));
50 extern int yylex __P((void));
55 static ipnat_t *nattop = NULL;
56 static ipnat_t *nat = NULL;
57 static int natfd = -1;
58 static ioctlfunc_t natioctlfunc = NULL;
59 static addfunc_t nataddfunc = NULL;
60 static int suggest_port = 0;
62 static void newnatrule __P((void));
63 static void setnatproto __P((int));
85 %token <num> YY_NUMBER YY_HEX
88 %token YY_CMP_EQ YY_CMP_NE YY_CMP_LE YY_CMP_GE YY_CMP_LT YY_CMP_GT
89 %token YY_RANGE_OUT YY_RANGE_IN
92 %token IPNY_MAPBLOCK IPNY_RDR IPNY_PORT IPNY_PORTS IPNY_AUTO IPNY_RANGE
93 %token IPNY_MAP IPNY_BIMAP IPNY_FROM IPNY_TO IPNY_MASK IPNY_PORTMAP IPNY_ANY
94 %token IPNY_ROUNDROBIN IPNY_FRAG IPNY_AGE IPNY_ICMPIDMAP IPNY_PROXY
95 %token IPNY_TCP IPNY_UDP IPNY_TCPUDP IPNY_STICKY IPNY_MSSCLAMP IPNY_TAG
98 %type <num> hexnumber compare range proto
99 %type <ipa> hostname ipv4
100 %type <ipp> addr nummask rhaddr
109 line: xx rule { while ((nat = nattop) != NULL) {
110 nattop = nat->in_next;
111 (*nataddfunc)(natfd, natioctlfunc, nat);
119 assign: YY_STR assigning YY_STR ';' { set_variable($1, $3);
128 '=' { yyvarnext = 1; }
131 xx: { newnatrule(); }
142 map: mapit ifnames addr IPNY_TLATE rhaddr proxy mapoptions
144 nat->in_inip = $3.a.s_addr;
145 nat->in_inmsk = $3.m.s_addr;
146 nat->in_outip = $5.a.s_addr;
147 nat->in_outmsk = $5.m.s_addr;
148 if (nat->in_ifnames[1][0] == '\0')
149 strncpy(nat->in_ifnames[1],
151 sizeof(nat->in_ifnames[0]));
152 if ((nat->in_flags & IPN_TCPUDP) == 0)
153 setnatproto(nat->in_p);
154 if (((nat->in_redir & NAT_MAPBLK) != 0) ||
155 ((nat->in_flags & IPN_AUTOPORTMAP) != 0))
156 nat_setgroupmap(nat);
158 | mapit ifnames addr IPNY_TLATE rhaddr mapport mapoptions
160 nat->in_inip = $3.a.s_addr;
161 nat->in_inmsk = $3.m.s_addr;
162 nat->in_outip = $5.a.s_addr;
163 nat->in_outmsk = $5.m.s_addr;
164 if (nat->in_ifnames[1][0] == '\0')
165 strncpy(nat->in_ifnames[1],
167 sizeof(nat->in_ifnames[0]));
168 if (((nat->in_redir & NAT_MAPBLK) != 0) ||
169 ((nat->in_flags & IPN_AUTOPORTMAP) != 0))
170 nat_setgroupmap(nat);
172 | mapit ifnames mapfrom IPNY_TLATE rhaddr proxy mapoptions
174 nat->in_outip = $5.a.s_addr;
175 nat->in_outmsk = $5.m.s_addr;
176 if (nat->in_ifnames[1][0] == '\0')
177 strncpy(nat->in_ifnames[1],
179 sizeof(nat->in_ifnames[0]));
180 if ((suggest_port == 1) &&
181 (nat->in_flags & IPN_TCPUDP) == 0)
182 nat->in_flags |= IPN_TCPUDP;
183 if ((nat->in_flags & IPN_TCPUDP) == 0)
184 setnatproto(nat->in_p);
185 if (((nat->in_redir & NAT_MAPBLK) != 0) ||
186 ((nat->in_flags & IPN_AUTOPORTMAP) != 0))
187 nat_setgroupmap(nat);
189 | mapit ifnames mapfrom IPNY_TLATE rhaddr mapport mapoptions
191 nat->in_outip = $5.a.s_addr;
192 nat->in_outmsk = $5.m.s_addr;
193 if (nat->in_ifnames[1][0] == '\0')
194 strncpy(nat->in_ifnames[1],
196 sizeof(nat->in_ifnames[0]));
197 if ((suggest_port == 1) &&
198 (nat->in_flags & IPN_TCPUDP) == 0)
199 nat->in_flags |= IPN_TCPUDP;
200 if (((nat->in_redir & NAT_MAPBLK) != 0) ||
201 ((nat->in_flags & IPN_AUTOPORTMAP) != 0))
202 nat_setgroupmap(nat);
207 mapblockit ifnames addr IPNY_TLATE addr ports mapoptions
209 nat->in_inip = $3.a.s_addr;
210 nat->in_inmsk = $3.m.s_addr;
211 nat->in_outip = $5.a.s_addr;
212 nat->in_outmsk = $5.m.s_addr;
213 if (nat->in_ifnames[1][0] == '\0')
214 strncpy(nat->in_ifnames[1],
216 sizeof(nat->in_ifnames[0]));
217 if ((nat->in_flags & IPN_TCPUDP) == 0)
218 setnatproto(nat->in_p);
219 if (((nat->in_redir & NAT_MAPBLK) != 0) ||
220 ((nat->in_flags & IPN_AUTOPORTMAP) != 0))
221 nat_setgroupmap(nat);
225 redir: rdrit ifnames addr dport IPNY_TLATE dip nport setproto rdroptions
227 nat->in_outip = $3.a.s_addr;
228 nat->in_outmsk = $3.m.s_addr;
229 if (nat->in_ifnames[1][0] == '\0')
230 strncpy(nat->in_ifnames[1],
232 sizeof(nat->in_ifnames[0]));
233 if ((nat->in_p == 0) &&
234 ((nat->in_flags & IPN_TCPUDP) == 0) &&
235 (nat->in_pmin != 0 ||
238 setnatproto(IPPROTO_TCP);
240 | rdrit ifnames rdrfrom IPNY_TLATE dip nport setproto rdroptions
242 if ((nat->in_p == 0) &&
243 ((nat->in_flags & IPN_TCPUDP) == 0) &&
244 (nat->in_pmin != 0 ||
247 setnatproto(IPPROTO_TCP);
248 if ((suggest_port == 1) &&
249 (nat->in_flags & IPN_TCPUDP) == 0)
250 nat->in_flags |= IPN_TCPUDP;
251 if (nat->in_ifnames[1][0] == '\0')
252 strncpy(nat->in_ifnames[1],
254 sizeof(nat->in_ifnames[0]));
256 | rdrit ifnames addr IPNY_TLATE dip setproto rdroptions
258 nat->in_outip = $3.a.s_addr;
259 nat->in_outmsk = $3.m.s_addr;
260 if (nat->in_ifnames[1][0] == '\0')
261 strncpy(nat->in_ifnames[1],
263 sizeof(nat->in_ifnames[0]));
265 | rdrit ifnames rdrfrom IPNY_TLATE dip setproto rdroptions
267 if ((suggest_port == 1) &&
268 (nat->in_flags & IPN_TCPUDP) == 0)
269 nat->in_flags |= IPN_TCPUDP;
270 if (nat->in_ifnames[1][0] == '\0')
271 strncpy(nat->in_ifnames[1],
273 sizeof(nat->in_ifnames[0]));
277 proxy: | IPNY_PROXY port portspec YY_STR '/' proto
278 { strncpy(nat->in_plabel, $4, sizeof(nat->in_plabel));
279 if (nat->in_dcmp == 0) {
280 nat->in_dport = htons($3);
281 } else if ($3 != nat->in_dport) {
282 yyerror("proxy port numbers not consistant");
287 | IPNY_PROXY port YY_STR YY_STR '/' proto
289 strncpy(nat->in_plabel, $4, sizeof(nat->in_plabel));
290 pnum = getportproto($3, $6);
292 yyerror("invalid port number");
293 nat->in_dport = pnum;
301 | proto { if (nat->in_p != 0 ||
302 nat->in_flags & IPN_TCPUDP)
303 yyerror("protocol set twice");
306 | IPNY_TCPUDP { if (nat->in_p != 0 ||
307 nat->in_flags & IPN_TCPUDP)
308 yyerror("protocol set twice");
309 nat->in_flags |= IPN_TCPUDP;
312 | IPNY_TCP '/' IPNY_UDP { if (nat->in_p != 0 ||
313 nat->in_flags & IPN_TCPUDP)
314 yyerror("protocol set twice");
315 nat->in_flags |= IPN_TCPUDP;
320 rhaddr: addr { $$.a = $1.a; $$.m = $1.m; }
321 | IPNY_RANGE ipv4 '-' ipv4
322 { $$.a = $2; $$.m = $4;
323 nat->in_flags |= IPN_IPRANGE; }
327 hostname { nat->in_inip = $1.s_addr;
328 nat->in_inmsk = 0xffffffff; }
329 | hostname '/' YY_NUMBER { if ($3 != 0 || $1.s_addr != 0)
330 yyerror("Only 0/0 supported");
334 | hostname ',' hostname { nat->in_flags |= IPN_SPLIT;
335 nat->in_inip = $1.s_addr;
336 nat->in_inmsk = $3.s_addr; }
339 port: IPNY_PORT { suggest_port = 1; }
343 YY_NUMBER { if ($1 > 65535) /* Unsigned */
344 yyerror("invalid port number");
348 | YY_STR { if (getport(NULL, $1, &($$)) == -1)
349 yyerror("invalid port number");
354 dport: | port portspec { nat->in_pmin = htons($2);
355 nat->in_pmax = htons($2); }
356 | port portspec '-' portspec { nat->in_pmin = htons($2);
357 nat->in_pmax = htons($4); }
358 | port portspec ':' portspec { nat->in_pmin = htons($2);
359 nat->in_pmax = htons($4); }
362 nport: port portspec { nat->in_pnext = htons($2); }
363 | port '=' portspec { nat->in_pnext = htons($3);
364 nat->in_flags |= IPN_FIXEDDPORT;
368 ports: | IPNY_PORTS YY_NUMBER { nat->in_pmin = $2; }
369 | IPNY_PORTS IPNY_AUTO { nat->in_flags |= IPN_AUTOPORTMAP; }
372 mapit: IPNY_MAP { nat->in_redir = NAT_MAP; }
373 | IPNY_BIMAP { nat->in_redir = NAT_BIMAP; }
376 rdrit: IPNY_RDR { nat->in_redir = NAT_REDIRECT; }
380 IPNY_MAPBLOCK { nat->in_redir = NAT_MAPBLK; }
384 from sobject IPNY_TO dobject
385 | from sobject '!' IPNY_TO dobject
386 { nat->in_flags |= IPN_NOTDST; }
387 | from sobject IPNY_TO '!' dobject
388 { nat->in_flags |= IPN_NOTDST; }
392 from sobject IPNY_TO dobject
393 | '!' from sobject IPNY_TO dobject
394 { nat->in_flags |= IPN_NOTSRC; }
395 | from '!' sobject IPNY_TO dobject
396 { nat->in_flags |= IPN_NOTSRC; }
399 from: IPNY_FROM { nat->in_flags |= IPN_FILTER; }
404 | ifname ',' otherifname
407 ifname: YY_STR { strncpy(nat->in_ifnames[0], $1,
408 sizeof(nat->in_ifnames[0]));
409 nat->in_ifnames[0][LIFNAMSIZ - 1] = '\0';
415 YY_STR { strncpy(nat->in_ifnames[1], $1,
416 sizeof(nat->in_ifnames[1]));
417 nat->in_ifnames[1][LIFNAMSIZ - 1] = '\0';
423 IPNY_PORTMAP tcpudp portspec ':' portspec
424 { nat->in_pmin = htons($3);
425 nat->in_pmax = htons($5);
427 | IPNY_PORTMAP tcpudp IPNY_AUTO
428 { nat->in_flags |= IPN_AUTOPORTMAP;
429 nat->in_pmin = htons(1024);
430 nat->in_pmax = htons(65535);
432 | IPNY_ICMPIDMAP YY_STR YY_NUMBER ':' YY_NUMBER
433 { if (strcmp($2, "icmp") != 0) {
434 yyerror("icmpidmap not followed by icmp");
437 if ($3 < 0 || $3 > 65535)
438 yyerror("invalid ICMP Id number");
439 if ($5 < 0 || $5 > 65535)
440 yyerror("invalid ICMP Id number");
441 nat->in_flags = IPN_ICMPQUERY;
442 nat->in_pmin = htons($3);
443 nat->in_pmax = htons($5);
449 | saddr port portstuff { nat->in_sport = $3.p1;
450 nat->in_stop = $3.p2;
451 nat->in_scmp = $3.pc; }
454 saddr: addr { if (nat->in_redir == NAT_REDIRECT) {
455 nat->in_srcip = $1.a.s_addr;
456 nat->in_srcmsk = $1.m.s_addr;
458 nat->in_inip = $1.a.s_addr;
459 nat->in_inmsk = $1.m.s_addr;
466 | daddr port portstuff { nat->in_dport = $3.p1;
467 nat->in_dtop = $3.p2;
468 nat->in_dcmp = $3.pc;
469 if (nat->in_redir == NAT_REDIRECT)
470 nat->in_pmin = htons($3.p1);
474 daddr: addr { if (nat->in_redir == NAT_REDIRECT) {
475 nat->in_outip = $1.a.s_addr;
476 nat->in_outmsk = $1.m.s_addr;
478 nat->in_srcip = $1.a.s_addr;
479 nat->in_srcmsk = $1.m.s_addr;
484 addr: IPNY_ANY { $$.a.s_addr = 0; $$.m.s_addr = 0; }
485 | nummask { $$.a = $1.a; $$.m = $1.m;
486 $$.a.s_addr &= $$.m.s_addr; }
487 | hostname '/' ipv4 { $$.a = $1; $$.m = $3;
488 $$.a.s_addr &= $$.m.s_addr; }
489 | hostname '/' hexnumber { $$.a = $1; $$.m.s_addr = htonl($3);
490 $$.a.s_addr &= $$.m.s_addr; }
491 | hostname IPNY_MASK ipv4 { $$.a = $1; $$.m = $3;
492 $$.a.s_addr &= $$.m.s_addr; }
493 | hostname IPNY_MASK hexnumber { $$.a = $1; $$.m.s_addr = htonl($3);
494 $$.a.s_addr &= $$.m.s_addr; }
498 hostname { $$.a = $1;
499 $$.m.s_addr = 0xffffffff; }
500 | hostname '/' YY_NUMBER { $$.a = $1;
501 ntomask(4, $3, &$$.m.s_addr); }
505 compare portspec { $$.pc = $1; $$.p1 = $2; }
506 | portspec range portspec { $$.pc = $2; $$.p1 = $1; $$.p2 = $3; }
510 rr frag age mssclamp nattag setproto
514 rr frag age sticky mssclamp rdrproxy nattag
517 nattag: | IPNY_TAG YY_STR { strncpy(nat->in_tag.ipt_tag, $2,
518 sizeof(nat->in_tag.ipt_tag));
520 rr: | IPNY_ROUNDROBIN { nat->in_flags |= IPN_ROUNDR; }
523 frag: | IPNY_FRAG { nat->in_flags |= IPN_FRAG; }
526 age: | IPNY_AGE YY_NUMBER { nat->in_age[0] = $2;
527 nat->in_age[1] = $2; }
528 | IPNY_AGE YY_NUMBER '/' YY_NUMBER { nat->in_age[0] = $2;
529 nat->in_age[1] = $4; }
532 sticky: | IPNY_STICKY { if (!(nat->in_flags & IPN_ROUNDR) &&
533 !(nat->in_flags & IPN_SPLIT)) {
535 "'sticky' for use with round-robin/IP splitting only\n");
537 nat->in_flags |= IPN_STICKY;
542 | IPNY_MSSCLAMP YY_NUMBER { nat->in_mssclamp = $2; }
545 tcpudp: | IPNY_TCP { setnatproto(IPPROTO_TCP); }
546 | IPNY_UDP { setnatproto(IPPROTO_UDP); }
547 | IPNY_TCPUDP { nat->in_flags |= IPN_TCPUDP;
550 | IPNY_TCP '/' IPNY_UDP { nat->in_flags |= IPN_TCPUDP;
557 { strncpy(nat->in_plabel, $2,
558 sizeof(nat->in_plabel));
559 nat->in_dport = nat->in_pnext;
560 nat->in_dport = htons(nat->in_dport);
563 | proxy { if (nat->in_plabel[0] != '\0') {
564 nat->in_pmin = nat->in_dport;
565 nat->in_pmax = nat->in_pmin;
566 nat->in_pnext = nat->in_pmin;
571 proto: YY_NUMBER { $$ = $1;
572 if ($$ != IPPROTO_TCP &&
576 | IPNY_TCP { $$ = IPPROTO_TCP; }
577 | IPNY_UDP { $$ = IPPROTO_UDP; }
578 | YY_STR { $$ = getproto($1); free($1);
579 if ($$ != IPPROTO_TCP &&
590 YY_STR { if (gethost($1, &$$.s_addr) == -1)
592 "Unknown host '%s'\n",
596 | YY_NUMBER { $$.s_addr = htonl($1); }
597 | ipv4 { $$.s_addr = $1.s_addr; }
601 '=' { $$ = FR_EQUAL; }
602 | YY_CMP_EQ { $$ = FR_EQUAL; }
603 | YY_CMP_NE { $$ = FR_NEQUAL; }
604 | YY_CMP_LT { $$ = FR_LESST; }
605 | YY_CMP_LE { $$ = FR_LESSTE; }
606 | YY_CMP_GT { $$ = FR_GREATERT; }
607 | YY_CMP_GE { $$ = FR_GREATERTE; }
610 YY_RANGE_OUT { $$ = FR_OUTRANGE; }
611 | YY_RANGE_IN { $$ = FR_INRANGE; }
612 | ':' { $$ = FR_INCRANGE; }
615 ipv4: YY_NUMBER '.' YY_NUMBER '.' YY_NUMBER '.' YY_NUMBER
616 { if ($1 > 255 || $3 > 255 || $5 > 255 || $7 > 255) {
617 yyerror("Invalid octet string for IP address");
620 $$.s_addr = ($1 << 24) | ($3 << 16) | ($5 << 8) | $7;
621 $$.s_addr = htonl($$.s_addr);
628 static wordtab_t yywords[] = {
631 { "auto", IPNY_AUTO },
632 { "bimap", IPNY_BIMAP },
633 { "frag", IPNY_FRAG },
634 { "from", IPNY_FROM },
635 { "icmpidmap", IPNY_ICMPIDMAP },
636 { "mask", IPNY_MASK },
638 { "map-block", IPNY_MAPBLOCK },
639 { "mssclamp", IPNY_MSSCLAMP },
640 { "netmask", IPNY_MASK },
641 { "port", IPNY_PORT },
642 { "portmap", IPNY_PORTMAP },
643 { "ports", IPNY_PORTS },
644 { "proxy", IPNY_PROXY },
645 { "range", IPNY_RANGE },
647 { "round-robin",IPNY_ROUNDROBIN },
648 { "sticky", IPNY_STICKY },
651 { "tcpudp", IPNY_TCPUDP },
655 { "->", IPNY_TLATE },
666 int ipnat_parsefile(fd, addfunc, ioctlfunc, filename)
669 ioctlfunc_t ioctlfunc;
675 (void) yysettab(yywords);
677 s = getenv("YYDEBUG");
683 if (strcmp(filename, "-")) {
684 fp = fopen(filename, "r");
686 fprintf(stderr, "fopen(%s) failed: %s\n", filename,
693 while (ipnat_parsesome(fd, addfunc, ioctlfunc, fp) == 1)
701 int ipnat_parsesome(fd, addfunc, ioctlfunc, fp)
704 ioctlfunc_t ioctlfunc;
713 nataddfunc = addfunc;
714 natioctlfunc = ioctlfunc;
721 if (ungetc(i, fp) == EOF)
725 s = getenv("YYDEBUG");
737 static void newnatrule()
741 n = calloc(1, sizeof(*n));
756 static void setnatproto(p)
764 nat->in_flags |= IPN_TCP;
765 nat->in_flags &= ~IPN_UDP;
768 nat->in_flags |= IPN_UDP;
769 nat->in_flags &= ~IPN_TCP;
772 nat->in_flags &= ~IPN_TCPUDP;
773 if (!(nat->in_flags & IPN_ICMPQUERY)) {
782 if ((nat->in_redir & NAT_MAPBLK) == 0) {
783 nat->in_flags &= ~IPN_TCPUDP;
793 if ((nat->in_flags & (IPN_TCPUDP|IPN_FIXEDDPORT)) == IPN_FIXEDDPORT)
794 nat->in_flags &= ~IPN_FIXEDDPORT;
798 void ipnat_addrule(fd, ioctlfunc, ptr)
800 ioctlfunc_t ioctlfunc;
808 bzero((char *)&obj, sizeof(obj));
809 obj.ipfo_rev = IPFILTER_VERSION;
810 obj.ipfo_size = sizeof(ipnat_t);
811 obj.ipfo_type = IPFOBJ_IPNAT;
816 if ((opts & OPT_DONOTHING) != 0)
819 if (opts & OPT_ZERORULEST) {
821 } else if (opts & OPT_INACTIVE) {
829 if ((opts & OPT_VERBOSE) != 0)
832 if (opts & OPT_DEBUG)
833 binprint(ipn, sizeof(*ipn));
835 if ((opts & OPT_ZERORULEST) != 0) {
836 if ((*ioctlfunc)(fd, add, (void *)&obj) == -1) {
837 if ((opts & OPT_DONOTHING) == 0) {
838 fprintf(stderr, "%d:", yylineNum);
839 perror("ioctl(SIOCZRLST)");
844 printf("hits %qd bytes %qd ",
845 (long long)fr->fr_hits,
846 (long long)fr->fr_bytes);
850 printf("hits %ld bytes %ld ",
851 fr->fr_hits, fr->fr_bytes);
856 } else if ((opts & OPT_REMOVE) != 0) {
857 if ((*ioctlfunc)(fd, del, (void *)&obj) == -1) {
858 if ((opts & OPT_DONOTHING) == 0) {
859 fprintf(stderr, "%d:", yylineNum);
860 perror("ioctl(delete nat rule)");
864 if ((*ioctlfunc)(fd, add, (void *)&obj) == -1) {
865 if ((opts & OPT_DONOTHING) == 0) {
866 fprintf(stderr, "%d:", yylineNum);
867 perror("ioctl(add/insert nat rule)");