%{
/*
* Copyright (C) 2001-2008 by Darren Reed.
*
* See the IPFILTER.LICENCE file for details on licencing.
*
* Copyright 2008 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 <osreldate.h>
# else
# if __FreeBSD_cc_version < 430000
# include <osreldate.h>
# endif
# endif
#endif
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <fcntl.h>
#include <errno.h>
#if !defined(__SVR4) && !defined(__GNUC__)
#include <strings.h>
#endif
#include <sys/types.h>
#include <sys/param.h>
#include <sys/file.h>
#include <stdlib.h>
#include <stddef.h>
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <netinet/in.h>
#include <netinet/in_systm.h>
#include <sys/time.h>
#include <syslog.h>
#include <net/if.h>
#if __FreeBSD_version >= 300000
# include <net/if_var.h>
#endif
#include <netdb.h>
#include <arpa/nameser.h>
#include <resolv.h>
#include "ipf.h"
#include "netinet/ipl.h"
#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 {
i6addr_t a;
int v;
} ipa;
frentry_t fr;
frtuc_t *frt;
u_short port;
struct {
u_short p1;
u_short p2;
int pc;
} pc;
struct {
i6addr_t a;
i6addr_t m;
int v;
} ipp;
union i6addr ip6;
};
%token <num> YY_NUMBER YY_HEX
%token <str> 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 <ip6> 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_SEQUENTIAL
%type <port> portspec
%type <num> hexnumber compare range proto
%type <num> saddr daddr sobject dobject mapfrom rdrfrom dip
%type <ipa> hostname ipv4 ipaddr
%type <ipp> addr rhaddr
%type <pc> portstuff
%%
file: line
| assign
| file line
| file assign
;
line: xx rule { while ((nat = nattop) != NULL) {
if (nat->in_v == 0)
nat->in_v = 4;
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 eol
| mapblock eol
| redir eol
;
eol: | ';'
;
map: mapit ifnames addr IPNY_TLATE rhaddr proxy mapoptions
{ if ($3.v != 0 && $3.v != $5.v && $5.v != 0)
yyerror("1.address family mismatch");
bcopy(&$3.a, &nat->in_in[0], sizeof($3.a));
bcopy(&$3.m, &nat->in_in[1], sizeof($3.a));
bcopy(&$5.a, &nat->in_out[0], sizeof($5.a));
bcopy(&$5.m, &nat->in_out[1], sizeof($5.a));
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
{ if ($3.v != 0 && $3.v != $5.v && $5.v != 0)
yyerror("2.address family mismatch");
bcopy(&$3.a, &nat->in_in[0], sizeof($3.a));
bcopy(&$3.m, &nat->in_in[1], sizeof($3.a));
bcopy(&$5.a, &nat->in_out[0], sizeof($5.a));
bcopy(&$5.m, &nat->in_out[1], sizeof($5.a));
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_TCPUDPICMPQ) == 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
{ if ($3 != 0 && $3 != $5.v && $5.v != 0)
yyerror("3.address family mismatch");
bcopy(&$5.a, &nat->in_out[0], sizeof($5.a));
bcopy(&$5.m, &nat->in_out[1], sizeof($5.a));
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
{ if ($3 != 0 && $3 != $5.v && $5.v != 0)
yyerror("4.address family mismatch");
bcopy(&$5.a, &nat->in_out[0], sizeof($5.a));
bcopy(&$5.m, &nat->in_out[1], sizeof($5.a));
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_TCPUDPICMPQ) == 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
{ if ($3.v != 0 && $3.v != $5.v && $5.v != 0)
yyerror("5.address family mismatch");
bcopy(&$3.a, &nat->in_in[0], sizeof($3.a));
bcopy(&$3.m, &nat->in_in[1], sizeof($3.a));
bcopy(&$5.a, &nat->in_out[0], sizeof($5.a));
bcopy(&$5.m, &nat->in_out[1], sizeof($5.a));
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 setproto rdroptions
{ if ($6 != 0 && $3.v != 0 && $6 != $3.v)
yyerror("6.address family mismatch");
bcopy(&$3.a, &nat->in_out[0], sizeof($3.a));
bcopy(&$3.m, &nat->in_out[1], sizeof($3.a));
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 setproto rdroptions
{ if ($5 != 0 && $3 != 0 && $5 != $3)
yyerror("7.address family mismatch");
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 setproto rdroptions
{ if ($5 != 0 && $3.v != 0 && $5 != $3.v)
yyerror("8.address family mismatch");
bcopy(&$3.a, &nat->in_out[0], sizeof($3.a));
bcopy(&$3.m, &nat->in_out[1], sizeof($3.a));
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 portspec 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
{ int pnum;
strncpy(nat->in_plabel, $4, sizeof(nat->in_plabel));
pnum = getportproto($3, $6);
if (pnum == -1)
yyerror("invalid port number");
nat->in_dport = pnum;
setnatproto($6);
free($3);
free($4);
}
;
setproto:
| proto { if (nat->in_p != 0 ||
nat->in_flags & IPN_TCPUDP)
yyerror("protocol set twice");
setnatproto($1);
}
| IPNY_TCPUDP { if (nat->in_p != 0 ||
nat->in_flags & IPN_TCPUDP)
yyerror("protocol set twice");
nat->in_flags |= IPN_TCPUDP;
nat->in_p = 0;
}
| IPNY_TCP '/' IPNY_UDP { if (nat->in_p != 0 ||
nat->in_flags & IPN_TCPUDP)
yyerror("protocol set twice");
nat->in_flags |= IPN_TCPUDP;
nat->in_p = 0;
}
;
rhaddr: addr { $$.a = $1.a;
$$.m = $1.m;
$$.v = $1.v;
if ($$.v == 0)
$$.v = nat->in_v;
yyexpectaddr = 0; }
| IPNY_RANGE hostname '-' hostname
{ if ($2.v != 0 && $4.v != 0 && $4.v != $2.v)
yyerror("9.address family "
"mismatch");
$$.v = $2.v;
$$.a = $2.a;
$$.m = $4.a;
nat->in_flags |= IPN_IPRANGE;
yyexpectaddr = 0; }
;
dip:
hostname { bcopy(&$1.a, &nat->in_in[0],
sizeof($1.a));
if ($1.v == 0)
$1.v = nat->in_v;
if ($1.v == 4) {
nat->in_inmsk = 0xffffffff;
} else {
nat->in_in[1].i6[0] = 0xffffffff;
nat->in_in[1].i6[1] = 0xffffffff;
nat->in_in[1].i6[2] = 0xffffffff;
nat->in_in[1].i6[3] = 0xffffffff;
}
$$ = $1.v;
}
| hostname '/' YY_NUMBER { if ($1.v == 0)
$1.v = nat->in_v;
if ($1.v == 4 &&
($1.a.in4.s_addr != 0 ||
($3 != 0 && $3 != 32)))
yyerror("Invalid mask for dip");
else if ($1.v == 6 &&
($1.a.in4.s_addr != 0 ||
($3 != 0 && $3 != 128)))
yyerror("Invalid mask for dip");
else if ($1.v == 0 ) {
if ($1.a.in4.s_addr == 0 &&
($3 == 32 || $3 == 0))
$1.v = 4;
else if ($3 == 128)
$1.v = 6;
}
bcopy(&$1.a, &nat->in_in[0],
sizeof($1.a));
ntomask($1.v, $3,
(u_32_t *)&nat->in_in[1]);
nat->in_in[0].i6[0] &= nat->in_in[1].i6[0];
nat->in_in[0].i6[0] &= nat->in_in[1].i6[1];
nat->in_in[0].i6[0] &= nat->in_in[1].i6[2];
nat->in_in[0].i6[0] &= nat->in_in[1].i6[3];
nat->in_v = $1.v;
$$ = $1.v;
}
| hostname ',' { yyexpectaddr = 1; } hostname
{ if ($1.v != $4.v)
yyerror("10.address family "
"mismatch");
$$ = $1.v;
nat->in_flags |= IPN_SPLIT;
bcopy(&$1.a, &nat->in_in[0],
sizeof($1.a));
bcopy(&$4.a, &nat->in_in[1],
sizeof($4.a));
yyexpectaddr = 0; }
;
portspec:
YY_NUMBER { if ($1 > 65535) /* Unsigned */
yyerror("invalid port number");
else
$$ = $1;
}
| YY_STR { if (getport(NULL, $1, &($$)) == -1)
yyerror("invalid port number");
$$ = ntohs($$);
}
;
dport: | IPNY_PORT portspec { nat->in_pmin = htons($2);
nat->in_pmax = htons($2); }
| IPNY_PORT portspec '-' portspec { nat->in_pmin = htons($2);
nat->in_pmax = htons($4); }
| IPNY_PORT portspec ':' portspec { nat->in_pmin = htons($2);
nat->in_pmax = htons($4); }
;
nport: IPNY_PORT portspec { nat->in_pnext = htons($2); }
| IPNY_PORT '=' portspec { nat->in_pnext = htons($3);
nat->in_flags |= IPN_FIXEDDPORT;
}
;
ports: | IPNY_PORTS YY_NUMBER { 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 { if ($2 != 0 && $4 != 0 && $2 != $4)
yyerror("11.address family "
"mismatch");
$$ = $2;
}
| from sobject '!' IPNY_TO dobject
{ if ($2 != 0 && $5 != 0 && $2 != $5)
yyerror("12.address family "
"mismatch");
nat->in_flags |= IPN_NOTDST;
$$ = $2;
}
;
rdrfrom:
from sobject IPNY_TO dobject { if ($2 != 0 && $4 != 0 && $2 != $4)
yyerror("13.address family "
"mismatch");
$$ = $2;
}
| '!' from sobject IPNY_TO dobject
{ if ($3 != 0 && $5 != 0 && $3 != $5)
yyerror("14.address family "
"mismatch");
nat->in_flags |= IPN_NOTSRC;
$$ = $3;
}
;
from: IPNY_FROM { nat->in_flags |= IPN_FILTER;
yyexpectaddr = 1; }
;
ifnames:
ifname { yyexpectaddr = 1; }
| ifname ',' otherifname { yyexpectaddr = 1; }
;
ifname: YY_STR { strncpy(nat->in_ifnames[0], $1,
sizeof(nat->in_ifnames[0]));
nat->in_ifnames[0][LIFNAMSIZ - 1] = '\0';
free($1);
}
;
otherifname:
YY_STR { strncpy(nat->in_ifnames[1], $1,
sizeof(nat->in_ifnames[1]));
nat->in_ifnames[1][LIFNAMSIZ - 1] = '\0';
free($1);
}
;
mapport:
IPNY_PORTMAP tcpudp portspec ':' portspec randport
{ nat->in_pmin = htons($3);
nat->in_pmax = htons($5);
}
| IPNY_PORTMAP tcpudp IPNY_AUTO randport
{ nat->in_flags |= IPN_AUTOPORTMAP;
nat->in_pmin = htons(1024);
nat->in_pmax = htons(65535);
}
| IPNY_ICMPIDMAP YY_STR YY_NUMBER ':' YY_NUMBER
{ if (strcmp($2, "icmp") != 0) {
yyerror("icmpidmap not followed by icmp");
}
free($2);
if ($3 < 0 || $3 > 65535)
yyerror("invalid ICMP Id number");
if ($5 < 0 || $5 > 65535)
yyerror("invalid ICMP Id number");
nat->in_flags = IPN_ICMPQUERY;
nat->in_pmin = htons($3);
nat->in_pmax = htons($5);
}
;
randport:
| IPNY_SEQUENTIAL { nat->in_flags |= IPN_SEQUENTIAL; }
;
sobject:
saddr { $$ = $1; }
| saddr IPNY_PORT portstuff { nat->in_sport = $3.p1;
nat->in_stop = $3.p2;
nat->in_scmp = $3.pc;
$$ = $1;
}
;
saddr: addr { if (nat->in_redir == NAT_REDIRECT) {
bcopy(&$1.a, &nat->in_src[0],
sizeof($1.a));
bcopy(&$1.m, &nat->in_src[1],
sizeof($1.a));
} else {
bcopy(&$1.a, &nat->in_in[0],
sizeof($1.a));
bcopy(&$1.m, &nat->in_in[1],
sizeof($1.a));
}
$$ = $1.v;
}
;
dobject:
daddr { $$ = $1; }
| 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) {
bcopy(&$1.a, &nat->in_out[0],
sizeof($1.a));
bcopy(&$1.m, &nat->in_out[1],
sizeof($1.a));
} else {
bcopy(&$1.a, &nat->in_src[0],
sizeof($1.a));
bcopy(&$1.m, &nat->in_src[1],
sizeof($1.a));
}
$$ = $1.v;
}
;
addr: IPNY_ANY { yyexpectaddr = 0;
bzero(&$$.a, sizeof($$.a));
bzero(&$$.m, sizeof($$.a));
$$.v = nat->in_v;
}
| hostname { $$.a = $1.a;
$$.v = $1.v;
if ($$.v == 4) {
$$.m.in4.s_addr = 0xffffffff;
} else {
$$.m.i6[0] = 0xffffffff;
$$.m.i6[1] = 0xffffffff;
$$.m.i6[2] = 0xffffffff;
$$.m.i6[3] = 0xffffffff;
}
yyexpectaddr = 0;
}
| hostname '/' YY_NUMBER { $$.a = $1.a;
if ($1.v == 0) {
if ($1.a.in4.s_addr != 0)
yyerror("invalid addr");
if ($3 == 0 || $3 == 32)
$1.v = 4;
else if ($3 == 128)
$1.v = 6;
else
yyerror("invalid mask");
nat->in_v = $1.v;
}
ntomask($1.v, $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];
$$.v = $1.v;
yyexpectaddr = 0;
}
| hostname '/' ipaddr { if ($1.v != $3.v) {
yyerror("1.address family "
"mismatch");
}
$$.a = $1.a;
$$.m = $3.a;
$$.a.i6[0] &= $$.m.i6[0];
$$.a.i6[1] &= $$.m.i6[1];
$$.a.i6[2] &= $$.m.i6[2];
$$.a.i6[3] &= $$.m.i6[3];
$$.v = $1.v;
yyexpectaddr = 0;
}
| hostname '/' hexnumber { $$.a = $1.a;
$$.m.in4.s_addr = htonl($3);
$$.a.in4.s_addr &= $$.m.in4.s_addr;
$$.v = 4;
}
| hostname IPNY_MASK ipaddr { if ($1.v != $3.v) {
yyerror("2.address family "
"mismatch");
}
$$.a = $1.a;
$$.m = $3.a;
$$.a.i6[0] &= $$.m.i6[0];
$$.a.i6[1] &= $$.m.i6[1];
$$.a.i6[2] &= $$.m.i6[2];
$$.a.i6[3] &= $$.m.i6[3];
$$.v = $1.v;
yyexpectaddr = 0;
}
| hostname IPNY_MASK hexnumber { $$.a = $1.a;
$$.m.in4.s_addr = htonl($3);
$$.a.in4.s_addr &= $$.m.in4.s_addr;
$$.v = 4;
}
;
portstuff:
compare portspec { $$.pc = $1; $$.p1 = $2; }
| portspec range portspec { $$.pc = $2; $$.p1 = $1; $$.p2 = $3; }
;
mapoptions:
rr frag age mssclamp nattag setproto
;
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;
nat->in_dport = htons(nat->in_dport);
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;
}
}
;
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 { i6addr_t addr;
if (gethost($1, &addr, 0) == 0) {
$$.a = addr;
$$.v = 4;
} else
if (gethost($1, &addr, 1) == 0) {
$$.a = addr;
$$.v = 6;
} else {
yyerror("Unknown hostname");
}
if ($$.v != 0)
nat->in_v = $$.v;
free($1);
}
| YY_NUMBER { bzero(&$$.a, sizeof($$.a));
$$.a.in4.s_addr = htonl($1);
if ($$.a.in4.s_addr != 0)
$$.v = 4;
else
$$.v = nat->in_v;
if ($$.v != 0)
nat->in_v = $$.v;
}
| ipv4 { $$ = $1;
nat->in_v = 4;
}
| YY_IPV6 { $$.a = $1;
$$.v = 6;
nat->in_v = 6;
}
| YY_NUMBER YY_IPV6 { $$.a = $2;
$$.v = 6;
}
;
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; }
;
ipaddr: ipv4 { $$ = $1; }
| YY_IPV6 { $$.a = $1;
$$.v = 6;
}
;
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;
}
$$.a.in4.s_addr = ($1 << 24) | ($3 << 16) | ($5 << 8) | $7;
$$.a.in4.s_addr = htonl($$.a.in4.s_addr);
$$.v = 4;
}
;
%%
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 },
{ "netmask", IPNY_MASK },
{ "port", IPNY_PORT },
{ "portmap", IPNY_PORTMAP },
{ "ports", IPNY_PORTS },
{ "proxy", IPNY_PROXY },
{ "range", IPNY_RANGE },
{ "rdr", IPNY_RDR },
{ "round-robin",IPNY_ROUNDROBIN },
{ "sequential", IPNY_SEQUENTIAL },
{ "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(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;
case IPPROTO_ICMP :
nat->in_flags &= ~IPN_TCPUDP;
if (!(nat->in_flags & IPN_ICMPQUERY)) {
nat->in_dcmp = 0;
nat->in_scmp = 0;
nat->in_pmin = 0;
nat->in_pmax = 0;
nat->in_pnext = 0;
}
break;
default :
if ((nat->in_redir & NAT_MAPBLK) == 0) {
/* Only reset dcmp/scmp in case dport/sport not set */
if (0 == nat->in_tuc.ftu_dport)
nat->in_dcmp = 0;
if (0 == nat->in_tuc.ftu_sport)
nat->in_scmp = 0;
nat->in_pmin = 0;
nat->in_pmax = 0;
nat->in_pnext = 0;
nat->in_flags &= ~IPN_TCPUDP;
}
break;
}
if ((nat->in_flags & (IPN_TCPUDP|IPN_FIXEDDPORT)) == IPN_FIXEDDPORT)
nat->in_flags &= ~IPN_FIXEDDPORT;
}
void ipnat_addrule(fd, ioctlfunc, ptr)
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 = 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)");
}
}
}
}