/* $FreeBSD$ */ /* * Copyright (C) 2012 by Darren Reed. * * See the IPFILTER.LICENCE file for details on licencing. */ %{ #include #include #include #include #include #if !defined(__SVR4) && !defined(__GNUC__) #include #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "ipf.h" #include "netinet/ipl.h" #include "ipnat_l.h" #define YYDEBUG 1 extern void yyerror(char *); extern int yyparse(void); extern int yylex(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 int suggest_port = 0; static proxyrule_t *prules = NULL; static int parser_error = 0; static void newnatrule(void); static void setnatproto(int); static void setmapifnames(void); static void setrdrifnames(void); static void proxy_setconfig(int); static void proxy_unsetconfig(void); static namelist_t *proxy_dns_add_pass(char *, char *); static namelist_t *proxy_dns_add_block(char *, char *); static void proxy_addconfig(char *, int, char *, namelist_t *); static void proxy_loadconfig(int, ioctlfunc_t, char *, int, char *, namelist_t *); static void proxy_loadrules(int, ioctlfunc_t, proxyrule_t *); static void setmapifnames(void); static void setrdrifnames(void); static void setifname(ipnat_t **, int, char *); static int addname(ipnat_t **, char *); %} %union { char *str; u_32_t num; struct { i6addr_t a; int f; } ipa; frentry_t fr; frtuc_t *frt; u_short port; struct { int p1; int p2; int pc; } pc; struct { i6addr_t a; i6addr_t m; int t; /* Address type */ int u; int f; /* Family */ int v; /* IP version */ int s; /* 0 = number, 1 = text */ int n; /* number */ } ipp; union i6addr ip6; namelist_t *names; }; %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 IPNY_POOL IPNY_HASH IPNY_NO IPNY_REWRITE IPNY_PROTO %token IPNY_ON IPNY_SRC IPNY_DST IPNY_IN IPNY_OUT IPNY_DIVERT %token IPNY_CONFIG IPNY_ALLOW IPNY_DENY IPNY_DNS IPNY_INET IPNY_INET6 %token IPNY_SEQUENTIAL IPNY_DSTLIST IPNY_PURGE %type portspec %type hexnumber compare range proto %type saddr daddr sobject dobject mapfrom rdrfrom dip %type hostname ipv4 ipaddr %type addr rhsaddr rhdaddr erhdaddr %type portstuff portpair comaports srcports dstports %type dnslines dnsline %% file: line | assign | file line | file assign | file pconf ';' ; line: xx rule { int err; while ((nat = nattop) != NULL) { if (nat->in_v[0] == 0) nat->in_v[0] = 4; if (nat->in_v[1] == 0) nat->in_v[1] = nat->in_v[0]; nattop = nat->in_next; err = (*nataddfunc)(natfd, natioctlfunc, nat); free(nat); if (err != 0) { parser_error = err; break; } } if (parser_error == 0 && prules != NULL) { proxy_loadrules(natfd, natioctlfunc, prules); prules = NULL; } resetlexer(); } | YY_COMMENT ; assign: YY_STR assigning YY_STR ';' { set_variable($1, $3); resetlexer(); free($1); free($3); yyvarnext = 0; } ; assigning: '=' { yyvarnext = 1; } ; xx: { newnatrule(); } ; rule: map eol | mapblock eol | redir eol | rewrite ';' | divert ';' ; no: IPNY_NO { nat->in_flags |= IPN_NO; } ; eol: | ';' ; map: mapit ifnames addr tlate rhsaddr proxy mapoptions { if ($3.f != 0 && $3.f != $5.f && $5.f != 0) yyerror("3.address family mismatch"); if (nat->in_v[0] == 0 && $5.v != 0) nat->in_v[0] = $5.v; else if (nat->in_v[0] == 0 && $3.v != 0) nat->in_v[0] = $3.v; if (nat->in_v[1] == 0 && $5.v != 0) nat->in_v[1] = $5.v; else if (nat->in_v[1] == 0 && $3.v != 0) nat->in_v[1] = $3.v; nat->in_osrcatype = $3.t; bcopy(&$3.a, &nat->in_osrc.na_addr[0], sizeof($3.a)); bcopy(&$3.m, &nat->in_osrc.na_addr[1], sizeof($3.a)); nat->in_nsrcatype = $5.t; nat->in_nsrcafunc = $5.u; bcopy(&$5.a, &nat->in_nsrc.na_addr[0], sizeof($5.a)); bcopy(&$5.m, &nat->in_nsrc.na_addr[1], sizeof($5.a)); setmapifnames(); } | mapit ifnames addr tlate rhsaddr mapport mapoptions { if ($3.f != $5.f && $3.f != 0 && $5.f != 0) yyerror("4.address family mismatch"); if (nat->in_v[1] == 0 && $5.v != 0) nat->in_v[1] = $5.v; else if (nat->in_v[0] == 0 && $3.v != 0) nat->in_v[0] = $3.v; if (nat->in_v[0] == 0 && $5.v != 0) nat->in_v[0] = $5.v; else if (nat->in_v[1] == 0 && $3.v != 0) nat->in_v[1] = $3.v; nat->in_osrcatype = $3.t; bcopy(&$3.a, &nat->in_osrc.na_addr[0], sizeof($3.a)); bcopy(&$3.m, &nat->in_osrc.na_addr[1], sizeof($3.a)); nat->in_nsrcatype = $5.t; nat->in_nsrcafunc = $5.u; bcopy(&$5.a, &nat->in_nsrc.na_addr[0], sizeof($5.a)); bcopy(&$5.m, &nat->in_nsrc.na_addr[1], sizeof($5.a)); setmapifnames(); } | no mapit ifnames addr setproto ';' { if (nat->in_v[0] == 0) nat->in_v[0] = $4.v; nat->in_osrcatype = $4.t; bcopy(&$4.a, &nat->in_osrc.na_addr[0], sizeof($4.a)); bcopy(&$4.m, &nat->in_osrc.na_addr[1], sizeof($4.a)); setmapifnames(); } | mapit ifnames mapfrom tlate rhsaddr proxy mapoptions { if ($3 != 0 && $5.f != 0 && $3 != $5.f) yyerror("5.address family mismatch"); if (nat->in_v[0] == 0 && $5.v != 0) nat->in_v[0] = $5.v; else if (nat->in_v[0] == 0 && $3 != 0) nat->in_v[0] = ftov($3); if (nat->in_v[1] == 0 && $5.v != 0) nat->in_v[1] = $5.v; else if (nat->in_v[1] == 0 && $3 != 0) nat->in_v[1] = ftov($3); nat->in_nsrcatype = $5.t; nat->in_nsrcafunc = $5.u; bcopy(&$5.a, &nat->in_nsrc.na_addr[0], sizeof($5.a)); bcopy(&$5.m, &nat->in_nsrc.na_addr[1], sizeof($5.a)); setmapifnames(); } | no mapit ifnames mapfrom setproto ';' { nat->in_v[0] = ftov($4); setmapifnames(); } | mapit ifnames mapfrom tlate rhsaddr mapport mapoptions { if ($3 != 0 && $5.f != 0 && $3 != $5.f) yyerror("6.address family mismatch"); if (nat->in_v[0] == 0 && $5.v != 0) nat->in_v[0] = $5.v; else if (nat->in_v[0] == 0 && $3 != 0) nat->in_v[0] = ftov($3); if (nat->in_v[1] == 0 && $5.v != 0) nat->in_v[1] = $5.v; else if (nat->in_v[1] == 0 && $3 != 0) nat->in_v[1] = ftov($3); nat->in_nsrcatype = $5.t; nat->in_nsrcafunc = $5.u; bcopy(&$5.a, &nat->in_nsrc.na_addr[0], sizeof($5.a)); bcopy(&$5.m, &nat->in_nsrc.na_addr[1], sizeof($5.a)); setmapifnames(); } ; mapblock: mapblockit ifnames addr tlate addr ports mapoptions { if ($3.f != 0 && $5.f != 0 && $3.f != $5.f) yyerror("7.address family mismatch"); if (nat->in_v[0] == 0 && $5.v != 0) nat->in_v[0] = $5.v; else if (nat->in_v[0] == 0 && $3.v != 0) nat->in_v[0] = $3.v; if (nat->in_v[1] == 0 && $5.v != 0) nat->in_v[1] = $5.v; else if (nat->in_v[1] == 0 && $3.v != 0) nat->in_v[1] = $3.v; nat->in_osrcatype = $3.t; bcopy(&$3.a, &nat->in_osrc.na_addr[0], sizeof($3.a)); bcopy(&$3.m, &nat->in_osrc.na_addr[1], sizeof($3.a)); nat->in_nsrcatype = $5.t; nat->in_nsrcafunc = $5.u; bcopy(&$5.a, &nat->in_nsrc.na_addr[0], sizeof($5.a)); bcopy(&$5.m, &nat->in_nsrc.na_addr[1], sizeof($5.a)); setmapifnames(); } | no mapblockit ifnames { yyexpectaddr = 1; } addr setproto ';' { if (nat->in_v[0] == 0) nat->in_v[0] = $5.v; if (nat->in_v[1] == 0) nat->in_v[1] = $5.v; nat->in_osrcatype = $5.t; bcopy(&$5.a, &nat->in_osrc.na_addr[0], sizeof($5.a)); bcopy(&$5.m, &nat->in_osrc.na_addr[1], sizeof($5.a)); setmapifnames(); } ; redir: rdrit ifnames addr dport tlate dip nport setproto rdroptions { if ($6 != 0 && $3.f != 0 && $6 != $3.f) yyerror("21.address family mismatch"); if (nat->in_v[0] == 0) { if ($3.v != AF_UNSPEC) nat->in_v[0] = ftov($3.f); else nat->in_v[0] = ftov($6); } nat->in_odstatype = $3.t; bcopy(&$3.a, &nat->in_odst.na_addr[0], sizeof($3.a)); bcopy(&$3.m, &nat->in_odst.na_addr[1], sizeof($3.a)); setrdrifnames(); } | no rdrit ifnames addr dport setproto ';' { if (nat->in_v[0] == 0) nat->in_v[0] = ftov($4.f); nat->in_odstatype = $4.t; bcopy(&$4.a, &nat->in_odst.na_addr[0], sizeof($4.a)); bcopy(&$4.m, &nat->in_odst.na_addr[1], sizeof($4.a)); setrdrifnames(); } | rdrit ifnames rdrfrom tlate dip nport setproto rdroptions { if ($5 != 0 && $3 != 0 && $5 != $3) yyerror("20.address family mismatch"); if (nat->in_v[0] == 0) { if ($3 != AF_UNSPEC) nat->in_v[0] = ftov($3); else nat->in_v[0] = ftov($5); } setrdrifnames(); } | no rdrit ifnames rdrfrom setproto ';' { nat->in_v[0] = ftov($4); setrdrifnames(); } ; rewrite: IPNY_REWRITE oninout rwrproto mapfrom tlate newdst newopts { if (nat->in_v[0] == 0) nat->in_v[0] = ftov($4); if (nat->in_redir & NAT_MAP) setmapifnames(); else setrdrifnames(); nat->in_redir |= NAT_REWRITE; } ; divert: IPNY_DIVERT oninout rwrproto mapfrom tlate divdst newopts { if (nat->in_v[0] == 0) nat->in_v[0] = ftov($4); if (nat->in_redir & NAT_MAP) { setmapifnames(); nat->in_pr[0] = IPPROTO_UDP; } else { setrdrifnames(); nat->in_pr[1] = IPPROTO_UDP; } nat->in_flags &= ~IPN_TCP; } ; tlate: IPNY_TLATE { yyexpectaddr = 1; } ; pconf: IPNY_PROXY { yysetdict(proxies); } IPNY_DNS '/' proto IPNY_CONFIG YY_STR '{' { proxy_setconfig(IPNY_DNS); } dnslines ';' '}' { proxy_addconfig("dns", $5, $7, $10); proxy_unsetconfig(); } ; dnslines: dnsline { $$ = $1; } | dnslines ';' dnsline { $$ = $1; $1->na_next = $3; } ; dnsline: IPNY_ALLOW YY_STR { $$ = proxy_dns_add_pass(NULL, $2); } | IPNY_DENY YY_STR { $$ = proxy_dns_add_block(NULL, $2); } | IPNY_ALLOW '.' YY_STR { $$ = proxy_dns_add_pass(".", $3); } | IPNY_DENY '.' YY_STR { $$ = proxy_dns_add_block(".", $3); } ; oninout: inout IPNY_ON ifnames { ; } ; inout: IPNY_IN { nat->in_redir = NAT_REDIRECT; } | IPNY_OUT { nat->in_redir = NAT_MAP; } ; rwrproto: | IPNY_PROTO setproto ; newdst: src rhsaddr srcports dst erhdaddr dstports { nat->in_nsrc.na_addr[0] = $2.a; nat->in_nsrc.na_addr[1] = $2.m; nat->in_nsrc.na_atype = $2.t; if ($2.t == FRI_LOOKUP) { nat->in_nsrc.na_type = $2.u; nat->in_nsrc.na_subtype = $2.s; nat->in_nsrc.na_num = $2.n; } nat->in_nsports[0] = $3.p1; nat->in_nsports[1] = $3.p2; nat->in_ndst.na_addr[0] = $5.a; nat->in_ndst.na_addr[1] = $5.m; nat->in_ndst.na_atype = $5.t; if ($5.t == FRI_LOOKUP) { nat->in_ndst.na_type = $5.u; nat->in_ndst.na_subtype = $5.s; nat->in_ndst.na_num = $5.n; } nat->in_ndports[0] = $6.p1; nat->in_ndports[1] = $6.p2; } ; divdst: src addr ',' portspec dst addr ',' portspec IPNY_UDP { nat->in_nsrc.na_addr[0] = $2.a; if ($2.m.in4.s_addr != 0xffffffff) yyerror("divert must have /32 dest"); nat->in_nsrc.na_addr[1] = $2.m; nat->in_nsports[0] = $4; nat->in_nsports[1] = $4; nat->in_ndst.na_addr[0] = $6.a; nat->in_ndst.na_addr[1] = $6.m; if ($6.m.in4.s_addr != 0xffffffff) yyerror("divert must have /32 dest"); nat->in_ndports[0] = $8; nat->in_ndports[1] = $8; nat->in_redir |= NAT_DIVERTUDP; } ; src: IPNY_SRC { yyexpectaddr = 1; } ; dst: IPNY_DST { yyexpectaddr = 1; } ; srcports: comaports { $$.p1 = $1.p1; $$.p2 = $1.p2; } | IPNY_PORT '=' portspec { $$.p1 = $3; $$.p2 = $3; nat->in_flags |= IPN_FIXEDSPORT; } ; dstports: comaports { $$.p1 = $1.p1; $$.p2 = $1.p2; } | IPNY_PORT '=' portspec { $$.p1 = $3; $$.p2 = $3; nat->in_flags |= IPN_FIXEDDPORT; } ; comaports: { $$.p1 = 0; $$.p2 = 0; } | ',' { if (!(nat->in_flags & IPN_TCPUDP)) yyerror("must be TCP/UDP for ports"); } portpair { $$.p1 = $3.p1; $$.p2 = $3.p2; } ; proxy: | IPNY_PROXY port portspec YY_STR '/' proto { int pos; pos = addname(&nat, $4); nat->in_plabel = pos; if (nat->in_dcmp == 0) { nat->in_odport = $3; } else if ($3 != nat->in_odport) { yyerror("proxy port numbers not consistant"); } nat->in_ndport = $3; setnatproto($6); free($4); } | IPNY_PROXY port YY_STR YY_STR '/' proto { int pnum, pos; pos = addname(&nat, $4); nat->in_plabel = pos; pnum = getportproto($3, $6); if (pnum == -1) yyerror("invalid port number"); nat->in_odport = ntohs(pnum); nat->in_ndport = ntohs(pnum); setnatproto($6); free($3); free($4); } | IPNY_PROXY port portspec YY_STR '/' proto IPNY_CONFIG YY_STR { int pos; pos = addname(&nat, $4); nat->in_plabel = pos; if (nat->in_dcmp == 0) { nat->in_odport = $3; } else if ($3 != nat->in_odport) { yyerror("proxy port numbers not consistant"); } nat->in_ndport = $3; setnatproto($6); nat->in_pconfig = addname(&nat, $8); free($4); free($8); } | IPNY_PROXY port YY_STR YY_STR '/' proto IPNY_CONFIG YY_STR { int pnum, pos; pos = addname(&nat, $4); nat->in_plabel = pos; pnum = getportproto($3, $6); if (pnum == -1) yyerror("invalid port number"); nat->in_odport = ntohs(pnum); nat->in_ndport = ntohs(pnum); setnatproto($6); pos = addname(&nat, $8); nat->in_pconfig = pos; free($3); free($4); free($8); } ; setproto: | proto { if (nat->in_pr[0] != 0 || nat->in_pr[1] != 0 || nat->in_flags & IPN_TCPUDP) yyerror("protocol set twice"); setnatproto($1); } | IPNY_TCPUDP { if (nat->in_pr[0] != 0 || nat->in_pr[1] != 0 || nat->in_flags & IPN_TCPUDP) yyerror("protocol set twice"); nat->in_flags |= IPN_TCPUDP; nat->in_pr[0] = 0; nat->in_pr[1] = 0; } | IPNY_TCP '/' IPNY_UDP { if (nat->in_pr[0] != 0 || nat->in_pr[1] != 0 || nat->in_flags & IPN_TCPUDP) yyerror("protocol set twice"); nat->in_flags |= IPN_TCPUDP; nat->in_pr[0] = 0; nat->in_pr[1] = 0; } ; rhsaddr: addr { $$ = $1; yyexpectaddr = 0; } | hostname '-' { yyexpectaddr = 1; } hostname { $$.t = FRI_RANGE; if ($1.f != $4.f) yyerror("8.address family " "mismatch"); $$.f = $1.f; $$.v = ftov($1.f); $$.a = $1.a; $$.m = $4.a; nat->in_flags |= IPN_SIPRANGE; yyexpectaddr = 0; } | IPNY_RANGE hostname '-' { yyexpectaddr = 1; } hostname { $$.t = FRI_RANGE; if ($2.f != $5.f) yyerror("9.address family " "mismatch"); $$.f = $2.f; $$.v = ftov($2.f); $$.a = $2.a; $$.m = $5.a; nat->in_flags |= IPN_SIPRANGE; yyexpectaddr = 0; } ; dip: hostname ',' { yyexpectaddr = 1; } hostname { nat->in_flags |= IPN_SPLIT; if ($1.f != $4.f) yyerror("10.address family " "mismatch"); $$ = $1.f; nat->in_ndstip6 = $1.a; nat->in_ndstmsk6 = $4.a; nat->in_ndstatype = FRI_SPLIT; yyexpectaddr = 0; } | rhdaddr { int bits; nat->in_ndstip6 = $1.a; nat->in_ndstmsk6 = $1.m; nat->in_ndst.na_atype = $1.t; yyexpectaddr = 0; if ($1.f == AF_INET) bits = count4bits($1.m.in4.s_addr); else bits = count6bits($1.m.i6); if (($1.f == AF_INET) && (bits != 0) && (bits != 32)) { yyerror("dest ip bitmask not /32"); } else if (($1.f == AF_INET6) && (bits != 0) && (bits != 128)) { yyerror("dest ip bitmask not /128"); } $$ = $1.f; } ; rhdaddr: addr { $$ = $1; yyexpectaddr = 0; } | hostname '-' hostname { bzero(&$$, sizeof($$)); $$.t = FRI_RANGE; if ($1.f != 0 && $3.f != 0 && $1.f != $3.f) yyerror("11.address family " "mismatch"); $$.a = $1.a; $$.m = $3.a; nat->in_flags |= IPN_DIPRANGE; yyexpectaddr = 0; } | IPNY_RANGE hostname '-' hostname { bzero(&$$, sizeof($$)); $$.t = FRI_RANGE; if ($2.f != 0 && $4.f != 0 && $2.f != $4.f) yyerror("12.address family " "mismatch"); $$.a = $2.a; $$.m = $4.a; nat->in_flags |= IPN_DIPRANGE; yyexpectaddr = 0; } ; erhdaddr: rhdaddr { $$ = $1; } | IPNY_DSTLIST '/' YY_NUMBER { $$.t = FRI_LOOKUP; $$.u = IPLT_DSTLIST; $$.s = 0; $$.n = $3; } | IPNY_DSTLIST '/' YY_STR { $$.t = FRI_LOOKUP; $$.u = IPLT_DSTLIST; $$.s = 1; $$.n = addname(&nat, $3); } ; port: IPNY_PORT { suggest_port = 1; } ; portspec: YY_NUMBER { if ($1 > 65535) /* Unsigned */ yyerror("invalid port number"); else $$ = $1; } | YY_STR { if (getport(NULL, $1, &($$), NULL) == -1) yyerror("invalid port number"); $$ = ntohs($$); } ; portpair: portspec { $$.p1 = $1; $$.p2 = $1; } | portspec '-' portspec { $$.p1 = $1; $$.p2 = $3; } | portspec ':' portspec { $$.p1 = $1; $$.p2 = $3; } ; dport: | port portpair { nat->in_odport = $2.p1; if ($2.p2 == 0) nat->in_dtop = $2.p1; else nat->in_dtop = $2.p2; } ; nport: | port portpair { nat->in_dpmin = $2.p1; nat->in_dpnext = $2.p1; nat->in_dpmax = $2.p2; nat->in_ndport = $2.p1; if (nat->in_dtop == 0) nat->in_dtop = $2.p2; } | port '=' portspec { nat->in_dpmin = $3; nat->in_dpnext = $3; nat->in_ndport = $3; if (nat->in_dtop == 0) nat->in_dtop = nat->in_odport; nat->in_flags |= IPN_FIXEDDPORT; } ; ports: | IPNY_PORTS YY_NUMBER { nat->in_spmin = $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 to dobject { if ($2 != 0 && $4 != 0 && $2 != $4) yyerror("13.address family " "mismatch"); $$ = $2; } | from sobject '!' to dobject { if ($2 != 0 && $5 != 0 && $2 != $5) yyerror("14.address family " "mismatch"); nat->in_flags |= IPN_NOTDST; $$ = $2; } | from sobject to '!' dobject { if ($2 != 0 && $5 != 0 && $2 != $5) yyerror("15.address family " "mismatch"); nat->in_flags |= IPN_NOTDST; $$ = $2; } ; rdrfrom: from sobject to dobject { if ($2 != 0 && $4 != 0 && $2 != $4) yyerror("16.address family " "mismatch"); $$ = $2; } | '!' from sobject to dobject { if ($3 != 0 && $5 != 0 && $3 != $5) yyerror("17.address family " "mismatch"); nat->in_flags |= IPN_NOTSRC; $$ = $3; } | from '!' sobject to dobject { if ($3 != 0 && $5 != 0 && $3 != $5) yyerror("18.address family " "mismatch"); nat->in_flags |= IPN_NOTSRC; $$ = $3; } ; from: IPNY_FROM { nat->in_flags |= IPN_FILTER; yyexpectaddr = 1; } ; to: IPNY_TO { yyexpectaddr = 1; } ; ifnames: ifname family { yyexpectaddr = 1; } | ifname ',' otherifname family { yyexpectaddr = 1; } ; ifname: YY_STR { setifname(&nat, 0, $1); free($1); } ; family: | IPNY_INET { nat->in_v[0] = 4; nat->in_v[1] = 4; } | IPNY_INET6 { nat->in_v[0] = 6; nat->in_v[1] = 6; } ; otherifname: YY_STR { setifname(&nat, 1, $1); free($1); } ; mapport: IPNY_PORTMAP tcpudp portpair sequential { nat->in_spmin = $3.p1; nat->in_spmax = $3.p2; } | IPNY_PORTMAP portpair tcpudp sequential { nat->in_spmin = $2.p1; nat->in_spmax = $2.p2; } | IPNY_PORTMAP tcpudp IPNY_AUTO sequential { nat->in_flags |= IPN_AUTOPORTMAP; nat->in_spmin = 1024; nat->in_spmax = 65535; } | IPNY_ICMPIDMAP YY_STR portpair sequential { if (strcmp($2, "icmp") != 0 && strcmp($2, "ipv6-icmp") != 0) { yyerror("icmpidmap not followed by icmp"); } free($2); if ($3.p1 < 0 || $3.p1 > 65535) yyerror("invalid 1st ICMP Id number"); if ($3.p2 < 0 || $3.p2 > 65535) yyerror("invalid 2nd ICMP Id number"); if (strcmp($2, "ipv6-icmp") == 0) { nat->in_pr[0] = IPPROTO_ICMPV6; nat->in_pr[1] = IPPROTO_ICMPV6; } else { nat->in_pr[0] = IPPROTO_ICMP; nat->in_pr[1] = IPPROTO_ICMP; } nat->in_flags = IPN_ICMPQUERY; nat->in_spmin = $3.p1; nat->in_spmax = $3.p2; } ; sobject: saddr { $$ = $1; } | saddr port portstuff { nat->in_osport = $3.p1; nat->in_stop = $3.p2; nat->in_scmp = $3.pc; $$ = $1; } ; saddr: addr { nat->in_osrcatype = $1.t; bcopy(&$1.a, &nat->in_osrc.na_addr[0], sizeof($1.a)); bcopy(&$1.m, &nat->in_osrc.na_addr[1], sizeof($1.m)); $$ = $1.f; } ; dobject: daddr { $$ = $1; } | daddr port portstuff { nat->in_odport = $3.p1; nat->in_dtop = $3.p2; nat->in_dcmp = $3.pc; $$ = $1; } ; daddr: addr { nat->in_odstatype = $1.t; bcopy(&$1.a, &nat->in_odst.na_addr[0], sizeof($1.a)); bcopy(&$1.m, &nat->in_odst.na_addr[1], sizeof($1.m)); $$ = $1.f; } ; addr: IPNY_ANY { yyexpectaddr = 0; bzero(&$$, sizeof($$)); $$.t = FRI_NORMAL; } | hostname { bzero(&$$, sizeof($$)); $$.a = $1.a; $$.t = FRI_NORMAL; $$.v = ftov($1.f); $$.f = $1.f; if ($$.f == AF_INET) { $$.m.in4.s_addr = 0xffffffff; } else if ($$.f == AF_INET6) { $$.m.i6[0] = 0xffffffff; $$.m.i6[1] = 0xffffffff; $$.m.i6[2] = 0xffffffff; $$.m.i6[3] = 0xffffffff; } yyexpectaddr = 0; } | hostname slash YY_NUMBER { bzero(&$$, sizeof($$)); $$.a = $1.a; $$.f = $1.f; $$.v = ftov($1.f); $$.t = FRI_NORMAL; ntomask($$.f, $3, (u_32_t *)&$$.m); $$.a.i6[0] &= $$.m.i6[0]; $$.a.i6[1] &= $$.m.i6[1]; $$.a.i6[2] &= $$.m.i6[2]; $$.a.i6[3] &= $$.m.i6[3]; yyexpectaddr = 0; } | hostname slash ipaddr { bzero(&$$, sizeof($$)); if ($1.f != $3.f) { yyerror("1.address family " "mismatch"); } $$.a = $1.a; $$.m = $3.a; $$.t = FRI_NORMAL; $$.a.i6[0] &= $$.m.i6[0]; $$.a.i6[1] &= $$.m.i6[1]; $$.a.i6[2] &= $$.m.i6[2]; $$.a.i6[3] &= $$.m.i6[3]; $$.f = $1.f; $$.v = ftov($1.f); yyexpectaddr = 0; } | hostname slash hexnumber { bzero(&$$, sizeof($$)); $$.a = $1.a; $$.m.in4.s_addr = htonl($3); $$.t = FRI_NORMAL; $$.a.in4.s_addr &= $$.m.in4.s_addr; $$.f = $1.f; $$.v = ftov($1.f); if ($$.f == AF_INET6) yyerror("incorrect inet6 mask"); } | hostname mask ipaddr { bzero(&$$, sizeof($$)); if ($1.f != $3.f) { yyerror("2.address family " "mismatch"); } $$.a = $1.a; $$.m = $3.a; $$.t = FRI_NORMAL; $$.a.i6[0] &= $$.m.i6[0]; $$.a.i6[1] &= $$.m.i6[1]; $$.a.i6[2] &= $$.m.i6[2]; $$.a.i6[3] &= $$.m.i6[3]; $$.f = $1.f; $$.v = ftov($1.f); yyexpectaddr = 0; } | hostname mask hexnumber { bzero(&$$, sizeof($$)); $$.a = $1.a; $$.m.in4.s_addr = htonl($3); $$.t = FRI_NORMAL; $$.a.in4.s_addr &= $$.m.in4.s_addr; $$.f = AF_INET; $$.v = 4; } | pool slash YY_NUMBER { bzero(&$$, sizeof($$)); $$.a.iplookupnum = $3; $$.a.iplookuptype = IPLT_POOL; $$.a.iplookupsubtype = 0; $$.t = FRI_LOOKUP; } | pool slash YY_STR { bzero(&$$, sizeof($$)); $$.a.iplookupname = addname(&nat,$3); $$.a.iplookuptype = IPLT_POOL; $$.a.iplookupsubtype = 1; $$.t = FRI_LOOKUP; } | hash slash YY_NUMBER { bzero(&$$, sizeof($$)); $$.a.iplookupnum = $3; $$.a.iplookuptype = IPLT_HASH; $$.a.iplookupsubtype = 0; $$.t = FRI_LOOKUP; } | hash slash YY_STR { bzero(&$$, sizeof($$)); $$.a.iplookupname = addname(&nat,$3); $$.a.iplookuptype = IPLT_HASH; $$.a.iplookupsubtype = 1; $$.t = FRI_LOOKUP; } ; slash: '/' { yyexpectaddr = 0; } ; mask: IPNY_MASK { yyexpectaddr = 0; } ; pool: IPNY_POOL { if (!(nat->in_flags & IPN_FILTER)) { yyerror("Can only use pool with from/to rules\n"); } yyexpectaddr = 0; yyresetdict(); } ; hash: IPNY_HASH { if (!(nat->in_flags & IPN_FILTER)) { yyerror("Can only use hash with from/to rules\n"); } yyexpectaddr = 0; yyresetdict(); } ; portstuff: compare portspec { $$.pc = $1; $$.p1 = $2; $$.p2 = 0; } | portspec range portspec { $$.pc = $2; $$.p1 = $1; $$.p2 = $3; } ; mapoptions: rr frag age mssclamp nattag setproto purge ; rdroptions: rr frag age sticky mssclamp rdrproxy nattag purge ; 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_pr[0] = 0; nat->in_pr[1] = 0; } | IPNY_TCP '/' IPNY_UDP { nat->in_flags |= IPN_TCPUDP; nat->in_pr[0] = 0; nat->in_pr[1] = 0; } ; sequential: | IPNY_SEQUENTIAL { nat->in_flags |= IPN_SEQUENTIAL; } ; purge: | IPNY_PURGE { nat->in_flags |= IPN_PURGE; } ; rdrproxy: IPNY_PROXY YY_STR { int pos; pos = addname(&nat, $2); nat->in_plabel = pos; nat->in_odport = nat->in_dpnext; nat->in_dtop = nat->in_odport; free($2); } | proxy { if (nat->in_plabel != -1) { nat->in_ndport = nat->in_odport; nat->in_dpmin = nat->in_odport; nat->in_dpmax = nat->in_dpmin; nat->in_dtop = nat->in_dpmin; nat->in_dpnext = nat->in_dpmin; } } ; newopts: | IPNY_PURGE { nat->in_flags |= IPN_PURGE; } ; proto: YY_NUMBER { $$ = $1; if ($$ != IPPROTO_TCP && $$ != IPPROTO_UDP) suggest_port = 0; } | IPNY_TCP { $$ = IPPROTO_TCP; } | IPNY_UDP { $$ = IPPROTO_UDP; } | YY_STR { $$ = getproto($1); free($1); if ($$ == -1) yyerror("unknown protocol"); if ($$ != IPPROTO_TCP && $$ != IPPROTO_UDP) suggest_port = 0; } ; hexnumber: YY_HEX { $$ = $1; } ; hostname: YY_STR { i6addr_t addr; int family; #ifdef USE_INET6 if (nat->in_v[0] == 6) family = AF_INET6; else #endif family = AF_INET; memset(&($$), 0, sizeof($$)); memset(&addr, 0, sizeof(addr)); $$.f = family; if (gethost(family, $1, &addr) == 0) { $$.a = addr; } else { FPRINTF(stderr, "Unknown host '%s'\n", $1); } free($1); } | YY_NUMBER { memset(&($$), 0, sizeof($$)); $$.a.in4.s_addr = htonl($1); if ($$.a.in4.s_addr != 0) $$.f = AF_INET; } | ipv4 { $$ = $1; } | YY_IPV6 { memset(&($$), 0, sizeof($$)); $$.a = $1; $$.f = AF_INET6; } | YY_NUMBER YY_IPV6 { memset(&($$), 0, sizeof($$)); $$.a = $2; $$.f = AF_INET6; } ; 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; } | ':' { $$ = FR_INCRANGE; } ; ipaddr: ipv4 { $$ = $1; } | YY_IPV6 { $$.a = $1; $$.f = AF_INET6; } ; 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); } bzero((char *)&$$, sizeof($$)); $$.a.in4.s_addr = ($1 << 24) | ($3 << 16) | ($5 << 8) | $7; $$.a.in4.s_addr = htonl($$.a.in4.s_addr); $$.f = AF_INET; } ; %% static wordtab_t proxies[] = { { "dns", IPNY_DNS } }; static wordtab_t dnswords[] = { { "allow", IPNY_ALLOW }, { "block", IPNY_DENY }, { "deny", IPNY_DENY }, { "drop", IPNY_DENY }, { "pass", IPNY_ALLOW }, }; static wordtab_t yywords[] = { { "age", IPNY_AGE }, { "any", IPNY_ANY }, { "auto", IPNY_AUTO }, { "bimap", IPNY_BIMAP }, { "config", IPNY_CONFIG }, { "divert", IPNY_DIVERT }, { "dst", IPNY_DST }, { "dstlist", IPNY_DSTLIST }, { "frag", IPNY_FRAG }, { "from", IPNY_FROM }, { "hash", IPNY_HASH }, { "icmpidmap", IPNY_ICMPIDMAP }, { "in", IPNY_IN }, { "inet", IPNY_INET }, { "inet6", IPNY_INET6 }, { "mask", IPNY_MASK }, { "map", IPNY_MAP }, { "map-block", IPNY_MAPBLOCK }, { "mssclamp", IPNY_MSSCLAMP }, { "netmask", IPNY_MASK }, { "no", IPNY_NO }, { "on", IPNY_ON }, { "out", IPNY_OUT }, { "pool", IPNY_POOL }, { "port", IPNY_PORT }, { "portmap", IPNY_PORTMAP }, { "ports", IPNY_PORTS }, { "proto", IPNY_PROTO }, { "proxy", IPNY_PROXY }, { "purge", IPNY_PURGE }, { "range", IPNY_RANGE }, { "rewrite", IPNY_REWRITE }, { "rdr", IPNY_RDR }, { "round-robin",IPNY_ROUNDROBIN }, { "sequential", IPNY_SEQUENTIAL }, { "src", IPNY_SRC }, { "sticky", IPNY_STICKY }, { "tag", IPNY_TAG }, { "tcp", IPNY_TCP }, { "tcpudp", IPNY_TCPUDP }, { "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(int fd, addfunc_t addfunc, ioctlfunc_t ioctlfunc, char *filename) { FILE *fp = NULL; int rval; char *s; yylineNum = 1; (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 ((rval = ipnat_parsesome(fd, addfunc, ioctlfunc, fp)) == 0) ; if (fp != NULL) fclose(fp); if (rval == -1) rval = 0; else if (rval != 0) rval = 1; return(rval); } int ipnat_parsesome(int fd, addfunc_t addfunc, ioctlfunc_t ioctlfunc, FILE *fp) { char *s; int i; natfd = fd; parser_error = 0; nataddfunc = addfunc; natioctlfunc = ioctlfunc; if (feof(fp)) return(-1); i = fgetc(fp); if (i == EOF) return(-1); if (ungetc(i, fp) == EOF) return(-1); if (feof(fp)) return(-1); s = getenv("YYDEBUG"); if (s) yydebug = atoi(s); else yydebug = 0; yyin = fp; yyparse(); return(parser_error); } static void newnatrule(void) { ipnat_t *n; n = calloc(1, sizeof(*n)); if (n == NULL) return; if (nat == NULL) { nattop = nat = n; n->in_pnext = &nattop; } else { nat->in_next = n; n->in_pnext = &nat->in_next; nat = n; } n->in_flineno = yylineNum; n->in_ifnames[0] = -1; n->in_ifnames[1] = -1; n->in_plabel = -1; n->in_pconfig = -1; n->in_size = sizeof(*n); suggest_port = 0; } static void setnatproto(int p) { nat->in_pr[0] = p; nat->in_pr[1] = 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; #ifdef USE_INET6 case IPPROTO_ICMPV6 : #endif case IPPROTO_ICMP : nat->in_flags &= ~IPN_TCPUDP; if (!(nat->in_flags & IPN_ICMPQUERY) && !(nat->in_redir & NAT_DIVERTUDP)) { nat->in_dcmp = 0; nat->in_scmp = 0; nat->in_dpmin = 0; nat->in_dpmax = 0; nat->in_dpnext = 0; nat->in_spmin = 0; nat->in_spmax = 0; nat->in_spnext = 0; } break; default : if ((nat->in_redir & NAT_MAPBLK) == 0) { nat->in_flags &= ~IPN_TCPUDP; nat->in_dcmp = 0; nat->in_scmp = 0; nat->in_dpmin = 0; nat->in_dpmax = 0; nat->in_dpnext = 0; nat->in_spmin = 0; nat->in_spmax = 0; nat->in_spnext = 0; } break; } if ((nat->in_flags & (IPN_TCP|IPN_UDP)) == 0) { nat->in_stop = 0; nat->in_dtop = 0; nat->in_osport = 0; nat->in_odport = 0; nat->in_stop = 0; nat->in_osport = 0; nat->in_dtop = 0; nat->in_odport = 0; } if ((nat->in_flags & (IPN_TCPUDP|IPN_FIXEDDPORT)) == IPN_FIXEDDPORT) nat->in_flags &= ~IPN_FIXEDDPORT; } int ipnat_addrule(int fd, ioctlfunc_t ioctlfunc, void *ptr) { ioctlcmd_t add, del; ipfobj_t obj; ipnat_t *ipn; ipn = ptr; bzero((char *)&obj, sizeof(obj)); obj.ipfo_rev = IPFILTER_VERSION; obj.ipfo_size = ipn->in_size; obj.ipfo_type = IPFOBJ_IPNAT; obj.ipfo_ptr = ptr; if ((opts & OPT_DONOTHING) != 0) fd = -1; if (opts & OPT_ZERORULEST) { add = SIOCZRLST; del = 0; } else if (opts & OPT_PURGE) { add = 0; del = SIOCPURGENAT; } else { add = SIOCADNAT; del = SIOCRMNAT; } if ((opts & OPT_VERBOSE) != 0) printnat(ipn, opts); if (opts & OPT_DEBUG) binprint(ipn, ipn->in_size); if ((opts & OPT_ZERORULEST) != 0) { if ((*ioctlfunc)(fd, add, (void *)&obj) == -1) { if ((opts & OPT_DONOTHING) == 0) { char msg[80]; snprintf(msg, sizeof(msg), "%d:ioctl(zero nat rule)", ipn->in_flineno); return(ipf_perror_fd(fd, ioctlfunc, msg)); } } else { PRINTF("hits %lu ", ipn->in_hits); #ifdef USE_QUAD_T PRINTF("bytes %"PRIu64" ", ipn->in_bytes[0] + ipn->in_bytes[1]); #else PRINTF("bytes %lu ", ipn->in_bytes[0] + ipn->in_bytes[1]); #endif printnat(ipn, opts); } } else if ((opts & OPT_REMOVE) != 0) { if ((*ioctlfunc)(fd, del, (void *)&obj) == -1) { if ((opts & OPT_DONOTHING) == 0) { char msg[80]; snprintf(msg, sizeof(msg), "%d:ioctl(delete nat rule)", ipn->in_flineno); return(ipf_perror_fd(fd, ioctlfunc, msg)); } } } else { if ((*ioctlfunc)(fd, add, (void *)&obj) == -1) { if ((opts & OPT_DONOTHING) == 0) { char msg[80]; snprintf(msg, sizeof(msg), "%d:ioctl(add/insert nat rule)", ipn->in_flineno); if (errno == EEXIST) { int strlen_msg = strlen(msg); snprintf(msg + strlen_msg, sizeof(msg) -strlen_msg, "(line %d)", ipn->in_flineno); } return(ipf_perror_fd(fd, ioctlfunc, msg)); } } } return(0); } static void setmapifnames() { if (nat->in_ifnames[1] == -1) nat->in_ifnames[1] = nat->in_ifnames[0]; if ((suggest_port == 1) && (nat->in_flags & IPN_TCPUDP) == 0) nat->in_flags |= IPN_TCPUDP; if ((nat->in_flags & IPN_TCPUDP) == 0) setnatproto(nat->in_pr[1]); if (((nat->in_redir & NAT_MAPBLK) != 0) || ((nat->in_flags & IPN_AUTOPORTMAP) != 0)) nat_setgroupmap(nat); } static void setrdrifnames(void) { if ((suggest_port == 1) && (nat->in_flags & IPN_TCPUDP) == 0) nat->in_flags |= IPN_TCPUDP; if ((nat->in_pr[0] == 0) && ((nat->in_flags & IPN_TCPUDP) == 0) && (nat->in_dpmin != 0 || nat->in_dpmax != 0 || nat->in_dpnext != 0)) setnatproto(IPPROTO_TCP); if (nat->in_ifnames[1] == -1) nat->in_ifnames[1] = nat->in_ifnames[0]; } static void proxy_setconfig(int proxy) { if (proxy == IPNY_DNS) { yysetfixeddict(dnswords); } } static void proxy_unsetconfig(void) { yyresetdict(); } static namelist_t * proxy_dns_add_pass(char *prefix, char *name) { namelist_t *n; n = calloc(1, sizeof(*n)); if (n != NULL) { if (prefix == NULL || *prefix == '\0') { n->na_name = strdup(name); } else { n->na_name = malloc(strlen(name) + strlen(prefix) + 1); strcpy(n->na_name, prefix); strcat(n->na_name, name); } } return(n); } static namelist_t * proxy_dns_add_block(char *prefix, char *name) { namelist_t *n; n = calloc(1, sizeof(*n)); if (n != NULL) { if (prefix == NULL || *prefix == '\0') { n->na_name = strdup(name); } else { n->na_name = malloc(strlen(name) + strlen(prefix) + 1); strcpy(n->na_name, prefix); strcat(n->na_name, name); } n->na_value = 1; } return(n); } static void proxy_addconfig(char *proxy, int proto, char *conf, namelist_t *list) { proxyrule_t *pr; pr = calloc(1, sizeof(*pr)); if (pr != NULL) { pr->pr_proto = proto; pr->pr_proxy = proxy; pr->pr_conf = conf; pr->pr_names = list; pr->pr_next = prules; prules = pr; } } static void proxy_loadrules(int fd, ioctlfunc_t ioctlfunc, proxyrule_t *rules) { proxyrule_t *pr; while ((pr = rules) != NULL) { proxy_loadconfig(fd, ioctlfunc, pr->pr_proxy, pr->pr_proto, pr->pr_conf, pr->pr_names); rules = pr->pr_next; free(pr->pr_conf); free(pr); } } static void proxy_loadconfig(int fd, ioctlfunc_t ioctlfunc, char *proxy, int proto, char *conf, namelist_t *list) { namelist_t *na; ipfobj_t obj; ap_ctl_t pcmd; obj.ipfo_rev = IPFILTER_VERSION; obj.ipfo_type = IPFOBJ_PROXYCTL; obj.ipfo_size = sizeof(pcmd); obj.ipfo_ptr = &pcmd; while ((na = list) != NULL) { if ((opts & OPT_REMOVE) != 0) pcmd.apc_cmd = APC_CMD_DEL; else pcmd.apc_cmd = APC_CMD_ADD; pcmd.apc_dsize = strlen(na->na_name) + 1; pcmd.apc_data = na->na_name; pcmd.apc_arg = na->na_value; pcmd.apc_p = proto; strncpy(pcmd.apc_label, proxy, APR_LABELLEN); pcmd.apc_label[APR_LABELLEN - 1] = '\0'; strncpy(pcmd.apc_config, conf, APR_LABELLEN); pcmd.apc_config[APR_LABELLEN - 1] = '\0'; if ((*ioctlfunc)(fd, SIOCPROXY, (void *)&obj) == -1) { if ((opts & OPT_DONOTHING) == 0) { char msg[80]; snprintf(msg, sizeof(msg), "%d:ioctl(add/remove proxy rule)", yylineNum); ipf_perror_fd(fd, ioctlfunc, msg); return; } } list = na->na_next; free(na->na_name); free(na); } } static void setifname(ipnat_t **np, int idx, char *name) { int pos; pos = addname(np, name); if (pos == -1) return; (*np)->in_ifnames[idx] = pos; } static int addname(ipnat_t **np, char *name) { ipnat_t *n; int nlen; int pos; nlen = strlen(name) + 1; n = realloc(*np, (*np)->in_size + nlen); if (*np == nattop) nattop = n; *np = n; if (n == NULL) return(-1); if (n->in_pnext != NULL) *n->in_pnext = n; n->in_size += nlen; pos = n->in_namelen; n->in_namelen += nlen; strcpy(n->in_names + pos, name); n->in_names[n->in_namelen] = '\0'; return(pos); }