%{ /* * Copyright (C) 2003 by Darren Reed. * * See the IPFILTER.LICENCE file for details on licencing. * * Copyright 2005 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #pragma ident "%Z%%M% %I% %E% SMI" #ifdef __FreeBSD__ # ifndef __FreeBSD_cc_version # include # else # if __FreeBSD_cc_version < 430000 # include # endif # endif #endif #include #include #include #include #include #if !defined(__SVR4) && !defined(__GNUC__) #include #endif #include #include #include #include #include #include #include #ifdef IPFILTER_BPF # include # include # include #endif #include #include #include #include #include #if __FreeBSD_version >= 300000 # include #endif #include #include #include #include #include #include "ipf.h" #if SOLARIS2 >= 10 #include "ipl.h" #else #include "netinet/ipl.h" #endif #include "ipnat_l.h" #define YYDEBUG 1 extern void yyerror __P((char *)); extern int yyparse __P((void)); extern int yylex __P((void)); extern int yydebug; extern FILE *yyin; extern int yylineNum; static ipnat_t *nattop = NULL; static ipnat_t *nat = NULL; static int natfd = -1; static ioctlfunc_t natioctlfunc = NULL; static addfunc_t nataddfunc = NULL; static void newnatrule __P((void)); static void setnatproto __P((int)); %} %union { char *str; u_32_t num; struct in_addr ipa; frentry_t fr; frtuc_t *frt; struct { u_short p1; u_short p2; int pc; } pc; struct { struct in_addr a; struct in_addr m; } ipp; union i6addr ip6; }; %token YY_NUMBER YY_HEX %token YY_STR %token YY_COMMENT %token YY_CMP_EQ YY_CMP_NE YY_CMP_LE YY_CMP_GE YY_CMP_LT YY_CMP_GT %token YY_RANGE_OUT YY_RANGE_IN %token YY_IPV6 %token IPNY_MAPBLOCK IPNY_RDR IPNY_PORT IPNY_PORTS IPNY_AUTO IPNY_RANGE %token IPNY_MAP IPNY_BIMAP IPNY_FROM IPNY_TO IPNY_MASK IPNY_PORTMAP IPNY_ANY %token IPNY_ROUNDROBIN IPNY_FRAG IPNY_AGE IPNY_ICMPIDMAP IPNY_PROXY %token IPNY_TCP IPNY_UDP IPNY_TCPUDP IPNY_STICKY IPNY_MSSCLAMP IPNY_TAG %token IPNY_TLATE %type hexnumber numports compare range proto %type hostname ipv4 %type addr nummask rhaddr %type portstuff %% file: line | assign | file line | file assign ; line: xx rule { while ((nat = nattop) != NULL) { nattop = nat->in_next; (*nataddfunc)(natfd, natioctlfunc, nat); free(nat); } resetlexer(); } | YY_COMMENT ; assign: YY_STR assigning YY_STR ';' { set_variable($1, $3); resetlexer(); free($1); free($3); } ; assigning: '=' { yyvarnext = 1; } ; xx: { newnatrule(); } ; rule: map | mapblock | redir ; map: mapit ifnames addr IPNY_TLATE rhaddr proxy mapoptions { nat->in_inip = $3.a.s_addr; nat->in_inmsk = $3.m.s_addr; nat->in_outip = $5.a.s_addr; nat->in_outmsk = $5.m.s_addr; if (nat->in_ifnames[1][0] == '\0') strncpy(nat->in_ifnames[1], nat->in_ifnames[0], sizeof(nat->in_ifnames[0])); if ((nat->in_flags & IPN_TCPUDP) == 0) setnatproto(nat->in_p); if (((nat->in_redir & NAT_MAPBLK) != 0) || ((nat->in_flags & IPN_AUTOPORTMAP) != 0)) nat_setgroupmap(nat); } | mapit ifnames addr IPNY_TLATE rhaddr mapport mapoptions { nat->in_inip = $3.a.s_addr; nat->in_inmsk = $3.m.s_addr; nat->in_outip = $5.a.s_addr; nat->in_outmsk = $5.m.s_addr; if (nat->in_ifnames[1][0] == '\0') strncpy(nat->in_ifnames[1], nat->in_ifnames[0], sizeof(nat->in_ifnames[0])); if ((nat->in_flags & IPN_TCPUDP) == 0) setnatproto(nat->in_p); if (((nat->in_redir & NAT_MAPBLK) != 0) || ((nat->in_flags & IPN_AUTOPORTMAP) != 0)) nat_setgroupmap(nat); } | mapit ifnames mapfrom IPNY_TLATE rhaddr proxy mapoptions { nat->in_outip = $5.a.s_addr; nat->in_outmsk = $5.m.s_addr; if (nat->in_ifnames[1][0] == '\0') strncpy(nat->in_ifnames[1], nat->in_ifnames[0], sizeof(nat->in_ifnames[0])); if ((nat->in_flags & IPN_TCPUDP) == 0) setnatproto(nat->in_p); if (((nat->in_redir & NAT_MAPBLK) != 0) || ((nat->in_flags & IPN_AUTOPORTMAP) != 0)) nat_setgroupmap(nat); } | mapit ifnames mapfrom IPNY_TLATE rhaddr mapport mapoptions { nat->in_outip = $5.a.s_addr; nat->in_outmsk = $5.m.s_addr; if (nat->in_ifnames[1][0] == '\0') strncpy(nat->in_ifnames[1], nat->in_ifnames[0], sizeof(nat->in_ifnames[0])); if ((nat->in_flags & IPN_TCPUDP) == 0) setnatproto(nat->in_p); if (((nat->in_redir & NAT_MAPBLK) != 0) || ((nat->in_flags & IPN_AUTOPORTMAP) != 0)) nat_setgroupmap(nat); } ; mapblock: mapblockit ifnames addr IPNY_TLATE addr ports mapoptions { nat->in_inip = $3.a.s_addr; nat->in_inmsk = $3.m.s_addr; nat->in_outip = $5.a.s_addr; nat->in_outmsk = $5.m.s_addr; if (nat->in_ifnames[1][0] == '\0') strncpy(nat->in_ifnames[1], nat->in_ifnames[0], sizeof(nat->in_ifnames[0])); if ((nat->in_flags & IPN_TCPUDP) == 0) setnatproto(nat->in_p); if (((nat->in_redir & NAT_MAPBLK) != 0) || ((nat->in_flags & IPN_AUTOPORTMAP) != 0)) nat_setgroupmap(nat); } ; redir: rdrit ifnames addr dport IPNY_TLATE dip nport rdrproto rdroptions { nat->in_outip = $3.a.s_addr; nat->in_outmsk = $3.m.s_addr; if (nat->in_ifnames[1][0] == '\0') strncpy(nat->in_ifnames[1], nat->in_ifnames[0], sizeof(nat->in_ifnames[0])); if ((nat->in_p == 0) && ((nat->in_flags & IPN_TCPUDP) == 0) && (nat->in_pmin != 0 || nat->in_pmax != 0 || nat->in_pnext != 0)) setnatproto(IPPROTO_TCP); } | rdrit ifnames rdrfrom IPNY_TLATE dip nport rdrproto rdroptions { if ((nat->in_p == 0) && ((nat->in_flags & IPN_TCPUDP) == 0) && (nat->in_pmin != 0 || nat->in_pmax != 0 || nat->in_pnext != 0)) setnatproto(IPPROTO_TCP); if (nat->in_ifnames[1][0] == '\0') strncpy(nat->in_ifnames[1], nat->in_ifnames[0], sizeof(nat->in_ifnames[0])); } | rdrit ifnames addr IPNY_TLATE dip rdrproto rdroptions { nat->in_outip = $3.a.s_addr; nat->in_outmsk = $3.m.s_addr; if (nat->in_ifnames[1][0] == '\0') strncpy(nat->in_ifnames[1], nat->in_ifnames[0], sizeof(nat->in_ifnames[0])); } ; proxy: | IPNY_PROXY IPNY_PORT YY_NUMBER YY_STR '/' proto { strncpy(nat->in_plabel, $4, sizeof(nat->in_plabel)); if (nat->in_dcmp == 0) { nat->in_dport = htons($3); } else if ($3 != nat->in_dport) { yyerror("proxy port numbers not consistant"); } setnatproto($6); free($4); } | IPNY_PROXY IPNY_PORT YY_STR YY_STR '/' proto { strncpy(nat->in_plabel, $4, sizeof(nat->in_plabel)); nat->in_dport = getportproto($3, $6); setnatproto($6); free($3); free($4); } ; rdrproto: | IPNY_TCP { setnatproto(IPPROTO_TCP); } | IPNY_UDP { setnatproto(IPPROTO_UDP); } | IPNY_TCPUDP { nat->in_flags |= IPN_TCPUDP; nat->in_p = 0; } | IPNY_TCP '/' IPNY_UDP { nat->in_flags |= IPN_TCPUDP; nat->in_p = 0; } | YY_NUMBER { setnatproto($1); } | YY_STR { setnatproto(getproto($1)); free($1); } ; rhaddr: addr { $$.a = $1.a; $$.m = $1.m; } | IPNY_RANGE ipv4 '-' ipv4 { $$.a = $2; $$.m = $4; nat->in_flags |= IPN_IPRANGE; } dip: ipv4 { nat->in_inip = $1.s_addr; nat->in_inmsk = 0xffffffff; } | ipv4 '/' YY_NUMBER { nat->in_inip = $1.s_addr; if (nat->in_inip != 0 || ($3 != 0 && $3 != 32)) yyerror("Invalid mask for dip"); ntomask(4, $3, &nat->in_inmsk); } | ipv4 ',' ipv4 { nat->in_flags |= IPN_SPLIT; nat->in_inip = $1.s_addr; nat->in_inmsk = $3.s_addr; } ; dport: | IPNY_PORT YY_NUMBER { nat->in_pmin = htons($2); nat->in_pmax = htons($2); } | IPNY_PORT YY_NUMBER '-' YY_NUMBER { nat->in_pmin = htons($2); nat->in_pmax = htons($4); } ; nport: IPNY_PORT YY_NUMBER { nat->in_pnext = htons($2); } ; ports: | IPNY_PORTS numports { nat->in_pmin = $2; } | IPNY_PORTS IPNY_AUTO { nat->in_flags |= IPN_AUTOPORTMAP; } ; mapit: IPNY_MAP { nat->in_redir = NAT_MAP; } | IPNY_BIMAP { nat->in_redir = NAT_BIMAP; } ; rdrit: IPNY_RDR { nat->in_redir = NAT_REDIRECT; } ; mapblockit: IPNY_MAPBLOCK { nat->in_redir = NAT_MAPBLK; } ; mapfrom: from sobject IPNY_TO dobject | from sobject '!' IPNY_TO dobject { nat->in_flags |= IPN_NOTDST; } ; rdrfrom: from sobject IPNY_TO dobject | '!' from sobject IPNY_TO dobject { nat->in_flags |= IPN_NOTSRC; } ; from: IPNY_FROM { nat->in_flags |= IPN_FILTER; } ; ifnames: ifname | ifname ',' otherifname ; ifname: YY_STR { strncpy(nat->in_ifnames[0], $1, sizeof(nat->in_ifnames[0])); free($1); } ; otherifname: YY_STR { strncpy(nat->in_ifnames[1], $1, sizeof(nat->in_ifnames[1])); free($1); } ; mapport: IPNY_PORTMAP tcpudp YY_NUMBER ':' YY_NUMBER { nat->in_pmin = htons($3); nat->in_pmax = htons($5); } | IPNY_PORTMAP tcpudp IPNY_AUTO { nat->in_flags |= IPN_AUTOPORTMAP; nat->in_pmin = htons(1024); nat->in_pmax = htons(65535); } ; sobject: saddr | saddr IPNY_PORT portstuff { nat->in_sport = $3.p1; nat->in_stop = $3.p2; nat->in_scmp = $3.pc; } ; saddr: addr { if (nat->in_redir == NAT_REDIRECT) { nat->in_srcip = $1.a.s_addr; nat->in_srcmsk = $1.m.s_addr; } else { nat->in_inip = $1.a.s_addr; nat->in_inmsk = $1.m.s_addr; } } ; dobject: daddr | daddr IPNY_PORT portstuff { nat->in_dport = $3.p1; nat->in_dtop = $3.p2; nat->in_dcmp = $3.pc; if (nat->in_redir == NAT_REDIRECT) nat->in_pmin = htons($3.p1); } ; daddr: addr { if (nat->in_redir == NAT_REDIRECT) { nat->in_outip = $1.a.s_addr; nat->in_outmsk = $1.m.s_addr; } else { nat->in_srcip = $1.a.s_addr; nat->in_srcmsk = $1.m.s_addr; } } ; addr: IPNY_ANY { $$.a.s_addr = 0; $$.m.s_addr = 0; } | nummask { $$.a = $1.a; $$.m = $1.m; $$.a.s_addr &= $$.m.s_addr; } | hostname '/' ipv4 { $$.a = $1; $$.m = $3; $$.a.s_addr &= $$.m.s_addr; } | hostname '/' hexnumber { $$.a = $1; $$.m.s_addr = $3; $$.a.s_addr &= $$.m.s_addr; } | hostname IPNY_MASK ipv4 { $$.a = $1; $$.m = $3; $$.a.s_addr &= $$.m.s_addr; } | hostname IPNY_MASK hexnumber { $$.a = $1; $$.m.s_addr = $3; $$.a.s_addr &= $$.m.s_addr; } ; nummask: hostname { $$.a = $1; $$.m.s_addr = 0xffffffff; } | hostname '/' YY_NUMBER { $$.a = $1; ntomask(4, $3, &$$.m.s_addr); } ; portstuff: compare YY_NUMBER { $$.pc = $1; $$.p1 = $2; } | YY_NUMBER range YY_NUMBER { $$.pc = $2; $$.p1 = $1; $$.p2 = $3; } ; mapoptions: rr frag age mssclamp nattag ; rdroptions: rr frag age sticky mssclamp rdrproxy nattag ; nattag: | IPNY_TAG YY_STR { strncpy(nat->in_tag.ipt_tag, $2, sizeof(nat->in_tag.ipt_tag)); } rr: | IPNY_ROUNDROBIN { nat->in_flags |= IPN_ROUNDR; } ; frag: | IPNY_FRAG { nat->in_flags |= IPN_FRAG; } ; age: | IPNY_AGE YY_NUMBER { nat->in_age[0] = $2; nat->in_age[1] = $2; } | IPNY_AGE YY_NUMBER '/' YY_NUMBER { nat->in_age[0] = $2; nat->in_age[1] = $4; } ; sticky: | IPNY_STICKY { if (!(nat->in_flags & IPN_ROUNDR) && !(nat->in_flags & IPN_SPLIT)) { fprintf(stderr, "'sticky' for use with round-robin/IP splitting only\n"); } else nat->in_flags |= IPN_STICKY; } ; mssclamp: | IPNY_MSSCLAMP YY_NUMBER { nat->in_mssclamp = $2; } ; tcpudp: | IPNY_TCP { setnatproto(IPPROTO_TCP); } | IPNY_UDP { setnatproto(IPPROTO_UDP); } | IPNY_TCPUDP { nat->in_flags |= IPN_TCPUDP; nat->in_p = 0; } | IPNY_TCP '/' IPNY_UDP { nat->in_flags |= IPN_TCPUDP; nat->in_p = 0; } ; rdrproxy: | IPNY_PROXY YY_STR { strncpy(nat->in_plabel, $2, sizeof(nat->in_plabel)); nat->in_dport = nat->in_pnext; free($2); } | proxy { if (nat->in_plabel[0] != '\0') { nat->in_pmin = nat->in_dport; nat->in_pmax = nat->in_pmin; nat->in_pnext = nat->in_pmin; } } ; numports: YY_NUMBER { $$ = $1; } ; proto: YY_NUMBER { $$ = $1; } | IPNY_TCP { $$ = IPPROTO_TCP; } | IPNY_UDP { $$ = IPPROTO_UDP; } | YY_STR { $$ = getproto($1); free($1); } ; hexnumber: YY_HEX { $$ = $1; } ; hostname: YY_STR { if (gethost($1, &$$.s_addr) == -1) fprintf(stderr, "Unknown host '%s'\n", $1); free($1); } | YY_NUMBER { $$.s_addr = htonl($1); } | ipv4 { $$.s_addr = $1.s_addr; } ; compare: '=' { $$ = FR_EQUAL; } | YY_CMP_EQ { $$ = FR_EQUAL; } | YY_CMP_NE { $$ = FR_NEQUAL; } | YY_CMP_LT { $$ = FR_LESST; } | YY_CMP_LE { $$ = FR_LESSTE; } | YY_CMP_GT { $$ = FR_GREATERT; } | YY_CMP_GE { $$ = FR_GREATERTE; } range: YY_RANGE_OUT { $$ = FR_OUTRANGE; } | YY_RANGE_IN { $$ = FR_INRANGE; } ; ipv4: YY_NUMBER '.' YY_NUMBER '.' YY_NUMBER '.' YY_NUMBER { if ($1 > 255 || $3 > 255 || $5 > 255 || $7 > 255) { yyerror("Invalid octet string for IP address"); return 0; } $$.s_addr = ($1 << 24) | ($3 << 16) | ($5 << 8) | $7; $$.s_addr = htonl($$.s_addr); } ; %% static wordtab_t yywords[] = { { "age", IPNY_AGE }, { "any", IPNY_ANY }, { "auto", IPNY_AUTO }, { "bimap", IPNY_BIMAP }, { "frag", IPNY_FRAG }, { "from", IPNY_FROM }, { "icmpidmap", IPNY_ICMPIDMAP }, { "mask", IPNY_MASK }, { "map", IPNY_MAP }, { "map-block", IPNY_MAPBLOCK }, { "mssclamp", IPNY_MSSCLAMP }, { "port", IPNY_PORT }, { "portmap", IPNY_PORTMAP }, { "ports", IPNY_PORTS }, { "proxy", IPNY_PROXY }, { "range", IPNY_RANGE }, { "rdr", IPNY_RDR }, { "round-robin",IPNY_ROUNDROBIN }, { "sticky", IPNY_STICKY }, { "tag", IPNY_TAG }, { "tcp", IPNY_TCP }, { "to", IPNY_TO }, { "udp", IPNY_UDP }, { "-", '-' }, { "->", IPNY_TLATE }, { "eq", YY_CMP_EQ }, { "ne", YY_CMP_NE }, { "lt", YY_CMP_LT }, { "gt", YY_CMP_GT }, { "le", YY_CMP_LE }, { "ge", YY_CMP_GE }, { NULL, 0 } }; int ipnat_parsefile(fd, addfunc, ioctlfunc, filename) int fd; addfunc_t addfunc; ioctlfunc_t ioctlfunc; char *filename; { FILE *fp = NULL; char *s; (void) yysettab(yywords); s = getenv("YYDEBUG"); if (s) yydebug = atoi(s); else yydebug = 0; if (strcmp(filename, "-")) { fp = fopen(filename, "r"); if (!fp) { fprintf(stderr, "fopen(%s) failed: %s\n", filename, STRERROR(errno)); return -1; } } else fp = stdin; while (ipnat_parsesome(fd, addfunc, ioctlfunc, fp) == 1) ; if (fp != NULL) fclose(fp); return 0; } int ipnat_parsesome(fd, addfunc, ioctlfunc, fp) int fd; addfunc_t addfunc; ioctlfunc_t ioctlfunc; FILE *fp; { char *s; int i; yylineNum = 1; natfd = fd; nataddfunc = addfunc; natioctlfunc = ioctlfunc; if (feof(fp)) return 0; i = fgetc(fp); if (i == EOF) return 0; if (ungetc(i, fp) == EOF) return 0; if (feof(fp)) return 0; s = getenv("YYDEBUG"); if (s) yydebug = atoi(s); else yydebug = 0; yyin = fp; yyparse(); return 1; } static void newnatrule() { ipnat_t *n; n = calloc(1, sizeof(*n)); if (n == NULL) return; if (nat == NULL) nattop = nat = n; else { nat->in_next = n; nat = n; } } static void setnatproto(p) int p; { nat->in_p = p; switch (p) { case IPPROTO_TCP : nat->in_flags |= IPN_TCP; nat->in_flags &= ~IPN_UDP; break; case IPPROTO_UDP : nat->in_flags |= IPN_UDP; nat->in_flags &= ~IPN_TCP; break; default : if ((nat->in_redir & NAT_MAPBLK) == 0) { nat->in_pmin = 0; nat->in_pmax = 0; nat->in_pnext = 0; nat->in_flags &= ~IPN_TCPUDP; } break; } } void ipnat_addrule(fd, ioctlfunc, ptr) int fd; ioctlfunc_t ioctlfunc; void *ptr; { ipfobj_t obj; int add, del; ipnat_t *ipn; ipn = ptr; bzero((char *)&obj, sizeof(obj)); obj.ipfo_rev = IPFILTER_VERSION; obj.ipfo_size = sizeof(ipnat_t); obj.ipfo_type = IPFOBJ_IPNAT; obj.ipfo_ptr = ptr; add = 0; del = 0; if ((opts & OPT_DONOTHING) != 0) fd = -1; if (opts & OPT_ZERORULEST) { add = SIOCZRLST; } else if (opts & OPT_INACTIVE) { add = SIOCADNAT; del = SIOCRMNAT; } else { add = SIOCADNAT; del = SIOCRMNAT; } if (ipn && (opts & OPT_VERBOSE)) printnat(ipn, opts); if (opts & OPT_DEBUG) binprint(ipn, sizeof(*ipn)); if ((opts & OPT_ZERORULEST) != 0) { if ((*ioctlfunc)(fd, add, (void *)&obj) == -1) { if ((opts & OPT_DONOTHING) == 0) { fprintf(stderr, "%d:", yylineNum); perror("ioctl(SIOCZRLST)"); } } else { #ifdef USE_QUAD_T /* printf("hits %qd bytes %qd ", (long long)fr->fr_hits, (long long)fr->fr_bytes); */ #else /* printf("hits %ld bytes %ld ", fr->fr_hits, fr->fr_bytes); */ #endif printnat(ipn, opts); } } else if ((opts & OPT_REMOVE) != 0) { if ((*ioctlfunc)(fd, del, (void *)&obj) == -1) { if ((opts & OPT_DONOTHING) == 0) { fprintf(stderr, "%d:", yylineNum); perror("ioctl(delete nat rule)"); } } } else { if ((*ioctlfunc)(fd, add, (void *)&obj) == -1) { if ((opts & OPT_DONOTHING) == 0) { fprintf(stderr, "%d:", yylineNum); perror("ioctl(add/insert nat rule)"); } } } }