summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorluigi <luigi@FreeBSD.org>2009-01-27 12:01:30 +0000
committerluigi <luigi@FreeBSD.org>2009-01-27 12:01:30 +0000
commit8a3b5c8587eefdf155a589610647bbc582948ce7 (patch)
treef06b5de4d78a1614f8a0927f17e29b5ec6459ad1
parent5153c1f1c42195d9130f83a12b751e798054a287 (diff)
downloadFreeBSD-src-8a3b5c8587eefdf155a589610647bbc582948ce7.zip
FreeBSD-src-8a3b5c8587eefdf155a589610647bbc582948ce7.tar.gz
Put nat and ipv6 support in their own files.
Usual moving of code with no changes from ipfw2.c to the newly created files, and addition of prototypes to ipfw2.h I have added forward declarations for ipfw_insn_* in ipfw2.h to avoid a global dependency on ip_fw.h
-rw-r--r--sbin/ipfw/Makefile2
-rw-r--r--sbin/ipfw/ipfw2.c1319
-rw-r--r--sbin/ipfw/ipfw2.h29
-rw-r--r--sbin/ipfw/ipv6.c501
-rw-r--r--sbin/ipfw/nat.c905
5 files changed, 1436 insertions, 1320 deletions
diff --git a/sbin/ipfw/Makefile b/sbin/ipfw/Makefile
index 190bea5..15e4568 100644
--- a/sbin/ipfw/Makefile
+++ b/sbin/ipfw/Makefile
@@ -1,7 +1,7 @@
# $FreeBSD$
PROG= ipfw
-SRCS= ipfw2.c dummynet.c main.c
+SRCS= ipfw2.c dummynet.c ipv6.c main.c nat.c
WARNS?= 0
MAN= ipfw.8
diff --git a/sbin/ipfw/ipfw2.c b/sbin/ipfw/ipfw2.c
index 5d73567..ffd74e8 100644
--- a/sbin/ipfw/ipfw2.c
+++ b/sbin/ipfw/ipfw2.c
@@ -52,11 +52,9 @@
#include <netinet/in_systm.h>
#include <netinet/ip.h>
#include <netinet/ip_icmp.h>
-#include <netinet/icmp6.h>
#include <netinet/ip_fw.h>
#include <netinet/tcp.h>
#include <arpa/inet.h>
-#include <alias.h>
struct cmdline_opts co; /* global options */
@@ -192,22 +190,6 @@ static struct _s_x ether_types[] = {
};
-static struct _s_x nat_params[] = {
- { "ip", TOK_IP },
- { "if", TOK_IF },
- { "log", TOK_ALOG },
- { "deny_in", TOK_DENY_INC },
- { "same_ports", TOK_SAME_PORTS },
- { "unreg_only", TOK_UNREG_ONLY },
- { "reset", TOK_RESET_ADDR },
- { "reverse", TOK_ALIAS_REV },
- { "proxy_only", TOK_PROXY_ONLY },
- { "redirect_addr", TOK_REDIR_ADDR },
- { "redirect_port", TOK_REDIR_PORT },
- { "redirect_proto", TOK_REDIR_PROTO },
- { NULL, 0 } /* terminator */
-};
-
static struct _s_x rule_actions[] = {
{ "accept", TOK_ACCEPT },
{ "pass", TOK_ACCEPT },
@@ -401,7 +383,7 @@ match_token(struct _s_x *table, char *string)
* match_value takes a table and a value, returns the string associated
* with the value (NULL in case of failure).
*/
-static char const *
+char const *
match_value(struct _s_x *p, int value)
{
for (; p->s != NULL; p++)
@@ -786,40 +768,6 @@ print_reject_code(uint16_t code)
printf("unreach %u", code);
}
-static struct _s_x icmp6codes[] = {
- { "no-route", ICMP6_DST_UNREACH_NOROUTE },
- { "admin-prohib", ICMP6_DST_UNREACH_ADMIN },
- { "address", ICMP6_DST_UNREACH_ADDR },
- { "port", ICMP6_DST_UNREACH_NOPORT },
- { NULL, 0 }
-};
-
-static void
-fill_unreach6_code(u_short *codep, char *str)
-{
- int val;
- char *s;
-
- val = strtoul(str, &s, 0);
- if (s == str || *s != '\0' || val >= 0x100)
- val = match_token(icmp6codes, str);
- if (val < 0)
- errx(EX_DATAERR, "unknown ICMPv6 unreachable code ``%s''", str);
- *codep = val;
- return;
-}
-
-static void
-print_unreach6_code(uint16_t code)
-{
- char const *s = match_value(icmp6codes, code);
-
- if (s != NULL)
- printf("unreach6 %s", s);
- else
- printf("unreach6 %u", code);
-}
-
/*
* Returns the number of bits set (from left) in a contiguous bitmask,
* or -1 if the mask is not contiguous.
@@ -831,7 +779,7 @@ print_unreach6_code(uint16_t code)
* the first bit on the wire is bit 0 of the first byte.
* len is the max length in bits.
*/
-static int
+int
contigmask(uint8_t *p, int len)
{
int i, n;
@@ -1022,226 +970,6 @@ print_icmptypes(ipfw_insn_u32 *cmd)
}
}
-/*
- * Print the ip address contained in a command.
- */
-static void
-print_ip6(ipfw_insn_ip6 *cmd, char const *s)
-{
- struct hostent *he = NULL;
- int len = F_LEN((ipfw_insn *) cmd) - 1;
- struct in6_addr *a = &(cmd->addr6);
- char trad[255];
-
- printf("%s%s ", cmd->o.len & F_NOT ? " not": "", s);
-
- if (cmd->o.opcode == O_IP6_SRC_ME || cmd->o.opcode == O_IP6_DST_ME) {
- printf("me6");
- return;
- }
- if (cmd->o.opcode == O_IP6) {
- printf(" ip6");
- return;
- }
-
- /*
- * len == 4 indicates a single IP, whereas lists of 1 or more
- * addr/mask pairs have len = (2n+1). We convert len to n so we
- * use that to count the number of entries.
- */
-
- for (len = len / 4; len > 0; len -= 2, a += 2) {
- int mb = /* mask length */
- (cmd->o.opcode == O_IP6_SRC || cmd->o.opcode == O_IP6_DST) ?
- 128 : contigmask((uint8_t *)&(a[1]), 128);
-
- if (mb == 128 && co.do_resolv)
- he = gethostbyaddr((char *)a, sizeof(*a), AF_INET6);
- if (he != NULL) /* resolved to name */
- printf("%s", he->h_name);
- else if (mb == 0) /* any */
- printf("any");
- else { /* numeric IP followed by some kind of mask */
- if (inet_ntop(AF_INET6, a, trad, sizeof( trad ) ) == NULL)
- printf("Error ntop in print_ip6\n");
- printf("%s", trad );
- if (mb < 0) /* XXX not really legal... */
- printf(":%s",
- inet_ntop(AF_INET6, &a[1], trad, sizeof(trad)));
- else if (mb < 128)
- printf("/%d", mb);
- }
- if (len > 2)
- printf(",");
- }
-}
-
-static void
-fill_icmp6types(ipfw_insn_icmp6 *cmd, char *av)
-{
- uint8_t type;
-
- bzero(cmd, sizeof(*cmd));
- while (*av) {
- if (*av == ',')
- av++;
- type = strtoul(av, &av, 0);
- if (*av != ',' && *av != '\0')
- errx(EX_DATAERR, "invalid ICMP6 type");
- /*
- * XXX: shouldn't this be 0xFF? I can't see any reason why
- * we shouldn't be able to filter all possiable values
- * regardless of the ability of the rest of the kernel to do
- * anything useful with them.
- */
- if (type > ICMP6_MAXTYPE)
- errx(EX_DATAERR, "ICMP6 type out of range");
- cmd->d[type / 32] |= ( 1 << (type % 32));
- }
- cmd->o.opcode = O_ICMP6TYPE;
- cmd->o.len |= F_INSN_SIZE(ipfw_insn_icmp6);
-}
-
-
-static void
-print_icmp6types(ipfw_insn_u32 *cmd)
-{
- int i, j;
- char sep= ' ';
-
- printf(" ip6 icmp6types");
- for (i = 0; i < 7; i++)
- for (j=0; j < 32; ++j) {
- if ( (cmd->d[i] & (1 << (j))) == 0)
- continue;
- printf("%c%d", sep, (i*32 + j));
- sep = ',';
- }
-}
-
-static void
-print_flow6id( ipfw_insn_u32 *cmd)
-{
- uint16_t i, limit = cmd->o.arg1;
- char sep = ',';
-
- printf(" flow-id ");
- for( i=0; i < limit; ++i) {
- if (i == limit - 1)
- sep = ' ';
- printf("%d%c", cmd->d[i], sep);
- }
-}
-
-/* structure and define for the extension header in ipv6 */
-static struct _s_x ext6hdrcodes[] = {
- { "frag", EXT_FRAGMENT },
- { "hopopt", EXT_HOPOPTS },
- { "route", EXT_ROUTING },
- { "dstopt", EXT_DSTOPTS },
- { "ah", EXT_AH },
- { "esp", EXT_ESP },
- { "rthdr0", EXT_RTHDR0 },
- { "rthdr2", EXT_RTHDR2 },
- { NULL, 0 }
-};
-
-/* fills command for the extension header filtering */
-static int
-fill_ext6hdr( ipfw_insn *cmd, char *av)
-{
- int tok;
- char *s = av;
-
- cmd->arg1 = 0;
-
- while(s) {
- av = strsep( &s, ",") ;
- tok = match_token(ext6hdrcodes, av);
- switch (tok) {
- case EXT_FRAGMENT:
- cmd->arg1 |= EXT_FRAGMENT;
- break;
-
- case EXT_HOPOPTS:
- cmd->arg1 |= EXT_HOPOPTS;
- break;
-
- case EXT_ROUTING:
- cmd->arg1 |= EXT_ROUTING;
- break;
-
- case EXT_DSTOPTS:
- cmd->arg1 |= EXT_DSTOPTS;
- break;
-
- case EXT_AH:
- cmd->arg1 |= EXT_AH;
- break;
-
- case EXT_ESP:
- cmd->arg1 |= EXT_ESP;
- break;
-
- case EXT_RTHDR0:
- cmd->arg1 |= EXT_RTHDR0;
- break;
-
- case EXT_RTHDR2:
- cmd->arg1 |= EXT_RTHDR2;
- break;
-
- default:
- errx( EX_DATAERR, "invalid option for ipv6 exten header" );
- break;
- }
- }
- if (cmd->arg1 == 0 )
- return 0;
- cmd->opcode = O_EXT_HDR;
- cmd->len |= F_INSN_SIZE( ipfw_insn );
- return 1;
-}
-
-static void
-print_ext6hdr( ipfw_insn *cmd )
-{
- char sep = ' ';
-
- printf(" extension header:");
- if (cmd->arg1 & EXT_FRAGMENT ) {
- printf("%cfragmentation", sep);
- sep = ',';
- }
- if (cmd->arg1 & EXT_HOPOPTS ) {
- printf("%chop options", sep);
- sep = ',';
- }
- if (cmd->arg1 & EXT_ROUTING ) {
- printf("%crouting options", sep);
- sep = ',';
- }
- if (cmd->arg1 & EXT_RTHDR0 ) {
- printf("%crthdr0", sep);
- sep = ',';
- }
- if (cmd->arg1 & EXT_RTHDR2 ) {
- printf("%crthdr2", sep);
- sep = ',';
- }
- if (cmd->arg1 & EXT_DSTOPTS ) {
- printf("%cdestination options", sep);
- sep = ',';
- }
- if (cmd->arg1 & EXT_AH ) {
- printf("%cauthentication header", sep);
- sep = ',';
- }
- if (cmd->arg1 & EXT_ESP ) {
- printf("%cencapsulated security payload", sep);
- }
-}
-
/*
* show_ipfw() prints the body of an ipfw rule.
* Because the standard rule has at least proto src_ip dst_ip, we use
@@ -2490,21 +2218,6 @@ fill_ip(ipfw_insn_ip *cmd, char *av)
}
-/* Try to find ipv6 address by hostname */
-static int
-lookup_host6 (char *host, struct in6_addr *ip6addr)
-{
- struct hostent *he;
-
- if (!inet_pton(AF_INET6, host, ip6addr)) {
- if ((he = gethostbyname2(host, AF_INET6)) == NULL)
- return(-1);
- memcpy(ip6addr, he->h_addr_list[0], sizeof( struct in6_addr));
- }
- return(0);
-}
-
-
/* n2mask sets n bits of the mask */
void
n2mask(struct in6_addr *mask, int n)
@@ -2526,196 +2239,6 @@ n2mask(struct in6_addr *mask, int n)
/*
- * fill the addr and mask fields in the instruction as appropriate from av.
- * Update length as appropriate.
- * The following formats are allowed:
- * any matches any IP6. Actually returns an empty instruction.
- * me returns O_IP6_*_ME
- *
- * 03f1::234:123:0342 single IP6 addres
- * 03f1::234:123:0342/24 address/mask
- * 03f1::234:123:0342/24,03f1::234:123:0343/ List of address
- *
- * Set of address (as in ipv6) not supported because ipv6 address
- * are typically random past the initial prefix.
- * Return 1 on success, 0 on failure.
- */
-static int
-fill_ip6(ipfw_insn_ip6 *cmd, char *av)
-{
- int len = 0;
- struct in6_addr *d = &(cmd->addr6);
- /*
- * Needed for multiple address.
- * Note d[1] points to struct in6_add r mask6 of cmd
- */
-
- cmd->o.len &= ~F_LEN_MASK; /* zero len */
-
- if (strcmp(av, "any") == 0)
- return (1);
-
-
- if (strcmp(av, "me") == 0) { /* Set the data for "me" opt*/
- cmd->o.len |= F_INSN_SIZE(ipfw_insn);
- return (1);
- }
-
- if (strcmp(av, "me6") == 0) { /* Set the data for "me" opt*/
- cmd->o.len |= F_INSN_SIZE(ipfw_insn);
- return (1);
- }
-
- av = strdup(av);
- while (av) {
- /*
- * After the address we can have '/' indicating a mask,
- * or ',' indicating another address follows.
- */
-
- char *p;
- int masklen;
- char md = '\0';
-
- if ((p = strpbrk(av, "/,")) ) {
- md = *p; /* save the separator */
- *p = '\0'; /* terminate address string */
- p++; /* and skip past it */
- }
- /* now p points to NULL, mask or next entry */
-
- /* lookup stores address in *d as a side effect */
- if (lookup_host6(av, d) != 0) {
- /* XXX: failed. Free memory and go */
- errx(EX_DATAERR, "bad address \"%s\"", av);
- }
- /* next, look at the mask, if any */
- masklen = (md == '/') ? atoi(p) : 128;
- if (masklen > 128 || masklen < 0)
- errx(EX_DATAERR, "bad width \"%s\''", p);
- else
- n2mask(&d[1], masklen);
-
- APPLY_MASK(d, &d[1]) /* mask base address with mask */
-
- /* find next separator */
-
- if (md == '/') { /* find separator past the mask */
- p = strpbrk(p, ",");
- if (p != NULL)
- p++;
- }
- av = p;
-
- /* Check this entry */
- if (masklen == 0) {
- /*
- * 'any' turns the entire list into a NOP.
- * 'not any' never matches, so it is removed from the
- * list unless it is the only item, in which case we
- * report an error.
- */
- if (cmd->o.len & F_NOT && av == NULL && len == 0)
- errx(EX_DATAERR, "not any never matches");
- continue;
- }
-
- /*
- * A single IP can be stored alone
- */
- if (masklen == 128 && av == NULL && len == 0) {
- len = F_INSN_SIZE(struct in6_addr);
- break;
- }
-
- /* Update length and pointer to arguments */
- len += F_INSN_SIZE(struct in6_addr)*2;
- d += 2;
- } /* end while */
-
- /*
- * Total length of the command, remember that 1 is the size of
- * the base command.
- */
- if (len + 1 > F_LEN_MASK)
- errx(EX_DATAERR, "address list too long");
- cmd->o.len |= len+1;
- free(av);
- return (1);
-}
-
-/*
- * fills command for ipv6 flow-id filtering
- * note that the 20 bit flow number is stored in a array of u_int32_t
- * it's supported lists of flow-id, so in the o.arg1 we store how many
- * additional flow-id we want to filter, the basic is 1
- */
-static void
-fill_flow6( ipfw_insn_u32 *cmd, char *av )
-{
- u_int32_t type; /* Current flow number */
- u_int16_t nflow = 0; /* Current flow index */
- char *s = av;
- cmd->d[0] = 0; /* Initializing the base number*/
-
- while (s) {
- av = strsep( &s, ",") ;
- type = strtoul(av, &av, 0);
- if (*av != ',' && *av != '\0')
- errx(EX_DATAERR, "invalid ipv6 flow number %s", av);
- if (type > 0xfffff)
- errx(EX_DATAERR, "flow number out of range %s", av);
- cmd->d[nflow] |= type;
- nflow++;
- }
- if( nflow > 0 ) {
- cmd->o.opcode = O_FLOW6ID;
- cmd->o.len |= F_INSN_SIZE(ipfw_insn_u32) + nflow;
- cmd->o.arg1 = nflow;
- }
- else {
- errx(EX_DATAERR, "invalid ipv6 flow number %s", av);
- }
-}
-
-static ipfw_insn *
-add_srcip6(ipfw_insn *cmd, char *av)
-{
-
- fill_ip6((ipfw_insn_ip6 *)cmd, av);
- if (F_LEN(cmd) == 0) { /* any */
- } else if (F_LEN(cmd) == F_INSN_SIZE(ipfw_insn)) { /* "me" */
- cmd->opcode = O_IP6_SRC_ME;
- } else if (F_LEN(cmd) ==
- (F_INSN_SIZE(struct in6_addr) + F_INSN_SIZE(ipfw_insn))) {
- /* single IP, no mask*/
- cmd->opcode = O_IP6_SRC;
- } else { /* addr/mask opt */
- cmd->opcode = O_IP6_SRC_MASK;
- }
- return cmd;
-}
-
-static ipfw_insn *
-add_dstip6(ipfw_insn *cmd, char *av)
-{
-
- fill_ip6((ipfw_insn_ip6 *)cmd, av);
- if (F_LEN(cmd) == 0) { /* any */
- } else if (F_LEN(cmd) == F_INSN_SIZE(ipfw_insn)) { /* "me" */
- cmd->opcode = O_IP6_DST_ME;
- } else if (F_LEN(cmd) ==
- (F_INSN_SIZE(struct in6_addr) + F_INSN_SIZE(ipfw_insn))) {
- /* single IP, no mask*/
- cmd->opcode = O_IP6_DST;
- } else { /* addr/mask opt */
- cmd->opcode = O_IP6_DST_MASK;
- }
- return cmd;
-}
-
-
-/*
* helper function to process a set of flags and set bits in the
* appropriate masks.
*/
@@ -2824,769 +2347,6 @@ fill_iface(ipfw_insn_if *cmd, char *arg)
errx(EX_DATAERR, "bad ip address ``%s''", arg);
}
-/*
- * Search for interface with name "ifn", and fill n accordingly:
- *
- * n->ip ip address of interface "ifn"
- * n->if_name copy of interface name "ifn"
- */
-static void
-set_addr_dynamic(const char *ifn, struct cfg_nat *n)
-{
- size_t needed;
- int mib[6];
- char *buf, *lim, *next;
- struct if_msghdr *ifm;
- struct ifa_msghdr *ifam;
- struct sockaddr_dl *sdl;
- struct sockaddr_in *sin;
- int ifIndex, ifMTU;
-
- mib[0] = CTL_NET;
- mib[1] = PF_ROUTE;
- mib[2] = 0;
- mib[3] = AF_INET;
- mib[4] = NET_RT_IFLIST;
- mib[5] = 0;
-/*
- * Get interface data.
- */
- if (sysctl(mib, 6, NULL, &needed, NULL, 0) == -1)
- err(1, "iflist-sysctl-estimate");
- buf = safe_calloc(1, needed);
- if (sysctl(mib, 6, buf, &needed, NULL, 0) == -1)
- err(1, "iflist-sysctl-get");
- lim = buf + needed;
-/*
- * Loop through interfaces until one with
- * given name is found. This is done to
- * find correct interface index for routing
- * message processing.
- */
- ifIndex = 0;
- next = buf;
- while (next < lim) {
- ifm = (struct if_msghdr *)next;
- next += ifm->ifm_msglen;
- if (ifm->ifm_version != RTM_VERSION) {
- if (co.verbose)
- warnx("routing message version %d "
- "not understood", ifm->ifm_version);
- continue;
- }
- if (ifm->ifm_type == RTM_IFINFO) {
- sdl = (struct sockaddr_dl *)(ifm + 1);
- if (strlen(ifn) == sdl->sdl_nlen &&
- strncmp(ifn, sdl->sdl_data, sdl->sdl_nlen) == 0) {
- ifIndex = ifm->ifm_index;
- ifMTU = ifm->ifm_data.ifi_mtu;
- break;
- }
- }
- }
- if (!ifIndex)
- errx(1, "unknown interface name %s", ifn);
-/*
- * Get interface address.
- */
- sin = NULL;
- while (next < lim) {
- ifam = (struct ifa_msghdr *)next;
- next += ifam->ifam_msglen;
- if (ifam->ifam_version != RTM_VERSION) {
- if (co.verbose)
- warnx("routing message version %d "
- "not understood", ifam->ifam_version);
- continue;
- }
- if (ifam->ifam_type != RTM_NEWADDR)
- break;
- if (ifam->ifam_addrs & RTA_IFA) {
- int i;
- char *cp = (char *)(ifam + 1);
-
- for (i = 1; i < RTA_IFA; i <<= 1) {
- if (ifam->ifam_addrs & i)
- cp += SA_SIZE((struct sockaddr *)cp);
- }
- if (((struct sockaddr *)cp)->sa_family == AF_INET) {
- sin = (struct sockaddr_in *)cp;
- break;
- }
- }
- }
- if (sin == NULL)
- errx(1, "%s: cannot get interface address", ifn);
-
- n->ip = sin->sin_addr;
- strncpy(n->if_name, ifn, IF_NAMESIZE);
-
- free(buf);
-}
-
-/*
- * XXX - The following functions, macros and definitions come from natd.c:
- * it would be better to move them outside natd.c, in a file
- * (redirect_support.[ch]?) shared by ipfw and natd, but for now i can live
- * with it.
- */
-
-/*
- * Definition of a port range, and macros to deal with values.
- * FORMAT: HI 16-bits == first port in range, 0 == all ports.
- * LO 16-bits == number of ports in range
- * NOTES: - Port values are not stored in network byte order.
- */
-
-#define port_range u_long
-
-#define GETLOPORT(x) ((x) >> 0x10)
-#define GETNUMPORTS(x) ((x) & 0x0000ffff)
-#define GETHIPORT(x) (GETLOPORT((x)) + GETNUMPORTS((x)))
-
-/* Set y to be the low-port value in port_range variable x. */
-#define SETLOPORT(x,y) ((x) = ((x) & 0x0000ffff) | ((y) << 0x10))
-
-/* Set y to be the number of ports in port_range variable x. */
-#define SETNUMPORTS(x,y) ((x) = ((x) & 0xffff0000) | (y))
-
-static void
-StrToAddr (const char* str, struct in_addr* addr)
-{
- struct hostent* hp;
-
- if (inet_aton (str, addr))
- return;
-
- hp = gethostbyname (str);
- if (!hp)
- errx (1, "unknown host %s", str);
-
- memcpy (addr, hp->h_addr, sizeof (struct in_addr));
-}
-
-static int
-StrToPortRange (const char* str, const char* proto, port_range *portRange)
-{
- char* sep;
- struct servent* sp;
- char* end;
- u_short loPort;
- u_short hiPort;
-
- /* First see if this is a service, return corresponding port if so. */
- sp = getservbyname (str,proto);
- if (sp) {
- SETLOPORT(*portRange, ntohs(sp->s_port));
- SETNUMPORTS(*portRange, 1);
- return 0;
- }
-
- /* Not a service, see if it's a single port or port range. */
- sep = strchr (str, '-');
- if (sep == NULL) {
- SETLOPORT(*portRange, strtol(str, &end, 10));
- if (end != str) {
- /* Single port. */
- SETNUMPORTS(*portRange, 1);
- return 0;
- }
-
- /* Error in port range field. */
- errx (EX_DATAERR, "%s/%s: unknown service", str, proto);
- }
-
- /* Port range, get the values and sanity check. */
- sscanf (str, "%hu-%hu", &loPort, &hiPort);
- SETLOPORT(*portRange, loPort);
- SETNUMPORTS(*portRange, 0); /* Error by default */
- if (loPort <= hiPort)
- SETNUMPORTS(*portRange, hiPort - loPort + 1);
-
- if (GETNUMPORTS(*portRange) == 0)
- errx (EX_DATAERR, "invalid port range %s", str);
-
- return 0;
-}
-
-static int
-StrToProto (const char* str)
-{
- if (!strcmp (str, "tcp"))
- return IPPROTO_TCP;
-
- if (!strcmp (str, "udp"))
- return IPPROTO_UDP;
-
- errx (EX_DATAERR, "unknown protocol %s. Expected tcp or udp", str);
-}
-
-static int
-StrToAddrAndPortRange (const char* str, struct in_addr* addr, char* proto,
- port_range *portRange)
-{
- char* ptr;
-
- ptr = strchr (str, ':');
- if (!ptr)
- errx (EX_DATAERR, "%s is missing port number", str);
-
- *ptr = '\0';
- ++ptr;
-
- StrToAddr (str, addr);
- return StrToPortRange (ptr, proto, portRange);
-}
-
-/* End of stuff taken from natd.c. */
-
-#define INC_ARGCV() do { \
- (*_av)++; \
- (*_ac)--; \
- av = *_av; \
- ac = *_ac; \
-} while(0)
-
-/*
- * The next 3 functions add support for the addr, port and proto redirect and
- * their logic is loosely based on SetupAddressRedirect(), SetupPortRedirect()
- * and SetupProtoRedirect() from natd.c.
- *
- * Every setup_* function fills at least one redirect entry
- * (struct cfg_redir) and zero or more server pool entry (struct cfg_spool)
- * in buf.
- *
- * The format of data in buf is:
- *
- *
- * cfg_nat cfg_redir cfg_spool ...... cfg_spool
- *
- * ------------------------------------- ------------
- * | | .....X ... | | | | .....
- * ------------------------------------- ...... ------------
- * ^
- * spool_cnt n=0 ...... n=(X-1)
- *
- * len points to the amount of available space in buf
- * space counts the memory consumed by every function
- *
- * XXX - Every function get all the argv params so it
- * has to check, in optional parameters, that the next
- * args is a valid option for the redir entry and not
- * another token. Only redir_port and redir_proto are
- * affected by this.
- */
-
-static int
-setup_redir_addr(char *spool_buf, int len,
- int *_ac, char ***_av)
-{
- char **av, *sep; /* Token separator. */
- /* Temporary buffer used to hold server pool ip's. */
- char tmp_spool_buf[NAT_BUF_LEN];
- int ac, space, lsnat;
- struct cfg_redir *r;
- struct cfg_spool *tmp;
-
- av = *_av;
- ac = *_ac;
- space = 0;
- lsnat = 0;
- if (len >= SOF_REDIR) {
- r = (struct cfg_redir *)spool_buf;
- /* Skip cfg_redir at beginning of buf. */
- spool_buf = &spool_buf[SOF_REDIR];
- space = SOF_REDIR;
- len -= SOF_REDIR;
- } else
- goto nospace;
- r->mode = REDIR_ADDR;
- /* Extract local address. */
- if (ac == 0)
- errx(EX_DATAERR, "redirect_addr: missing local address");
- sep = strchr(*av, ',');
- if (sep) { /* LSNAT redirection syntax. */
- r->laddr.s_addr = INADDR_NONE;
- /* Preserve av, copy spool servers to tmp_spool_buf. */
- strncpy(tmp_spool_buf, *av, strlen(*av)+1);
- lsnat = 1;
- } else
- StrToAddr(*av, &r->laddr);
- INC_ARGCV();
-
- /* Extract public address. */
- if (ac == 0)
- errx(EX_DATAERR, "redirect_addr: missing public address");
- StrToAddr(*av, &r->paddr);
- INC_ARGCV();
-
- /* Setup LSNAT server pool. */
- if (sep) {
- sep = strtok(tmp_spool_buf, ",");
- while (sep != NULL) {
- tmp = (struct cfg_spool *)spool_buf;
- if (len < SOF_SPOOL)
- goto nospace;
- len -= SOF_SPOOL;
- space += SOF_SPOOL;
- StrToAddr(sep, &tmp->addr);
- tmp->port = ~0;
- r->spool_cnt++;
- /* Point to the next possible cfg_spool. */
- spool_buf = &spool_buf[SOF_SPOOL];
- sep = strtok(NULL, ",");
- }
- }
- return(space);
-nospace:
- errx(EX_DATAERR, "redirect_addr: buf is too small\n");
-}
-
-static int
-setup_redir_port(char *spool_buf, int len,
- int *_ac, char ***_av)
-{
- char **av, *sep, *protoName;
- char tmp_spool_buf[NAT_BUF_LEN];
- int ac, space, lsnat;
- struct cfg_redir *r;
- struct cfg_spool *tmp;
- u_short numLocalPorts;
- port_range portRange;
-
- av = *_av;
- ac = *_ac;
- space = 0;
- lsnat = 0;
- numLocalPorts = 0;
-
- if (len >= SOF_REDIR) {
- r = (struct cfg_redir *)spool_buf;
- /* Skip cfg_redir at beginning of buf. */
- spool_buf = &spool_buf[SOF_REDIR];
- space = SOF_REDIR;
- len -= SOF_REDIR;
- } else
- goto nospace;
- r->mode = REDIR_PORT;
- /*
- * Extract protocol.
- */
- if (ac == 0)
- errx (EX_DATAERR, "redirect_port: missing protocol");
- r->proto = StrToProto(*av);
- protoName = *av;
- INC_ARGCV();
-
- /*
- * Extract local address.
- */
- if (ac == 0)
- errx (EX_DATAERR, "redirect_port: missing local address");
-
- sep = strchr(*av, ',');
- /* LSNAT redirection syntax. */
- if (sep) {
- r->laddr.s_addr = INADDR_NONE;
- r->lport = ~0;
- numLocalPorts = 1;
- /* Preserve av, copy spool servers to tmp_spool_buf. */
- strncpy(tmp_spool_buf, *av, strlen(*av)+1);
- lsnat = 1;
- } else {
- if (StrToAddrAndPortRange (*av, &r->laddr, protoName,
- &portRange) != 0)
- errx(EX_DATAERR, "redirect_port:"
- "invalid local port range");
-
- r->lport = GETLOPORT(portRange);
- numLocalPorts = GETNUMPORTS(portRange);
- }
- INC_ARGCV();
-
- /*
- * Extract public port and optionally address.
- */
- if (ac == 0)
- errx (EX_DATAERR, "redirect_port: missing public port");
-
- sep = strchr (*av, ':');
- if (sep) {
- if (StrToAddrAndPortRange (*av, &r->paddr, protoName,
- &portRange) != 0)
- errx(EX_DATAERR, "redirect_port:"
- "invalid public port range");
- } else {
- r->paddr.s_addr = INADDR_ANY;
- if (StrToPortRange (*av, protoName, &portRange) != 0)
- errx(EX_DATAERR, "redirect_port:"
- "invalid public port range");
- }
-
- r->pport = GETLOPORT(portRange);
- r->pport_cnt = GETNUMPORTS(portRange);
- INC_ARGCV();
-
- /*
- * Extract remote address and optionally port.
- */
- /*
- * NB: isalpha(**av) => we've to check that next parameter is really an
- * option for this redirect entry, else stop here processing arg[cv].
- */
- if (ac != 0 && !isalpha(**av)) {
- sep = strchr (*av, ':');
- if (sep) {
- if (StrToAddrAndPortRange (*av, &r->raddr, protoName,
- &portRange) != 0)
- errx(EX_DATAERR, "redirect_port:"
- "invalid remote port range");
- } else {
- SETLOPORT(portRange, 0);
- SETNUMPORTS(portRange, 1);
- StrToAddr (*av, &r->raddr);
- }
- INC_ARGCV();
- } else {
- SETLOPORT(portRange, 0);
- SETNUMPORTS(portRange, 1);
- r->raddr.s_addr = INADDR_ANY;
- }
- r->rport = GETLOPORT(portRange);
- r->rport_cnt = GETNUMPORTS(portRange);
-
- /*
- * Make sure port ranges match up, then add the redirect ports.
- */
- if (numLocalPorts != r->pport_cnt)
- errx(EX_DATAERR, "redirect_port:"
- "port ranges must be equal in size");
-
- /* Remote port range is allowed to be '0' which means all ports. */
- if (r->rport_cnt != numLocalPorts &&
- (r->rport_cnt != 1 || r->rport != 0))
- errx(EX_DATAERR, "redirect_port: remote port must"
- "be 0 or equal to local port range in size");
-
- /*
- * Setup LSNAT server pool.
- */
- if (lsnat) {
- sep = strtok(tmp_spool_buf, ",");
- while (sep != NULL) {
- tmp = (struct cfg_spool *)spool_buf;
- if (len < SOF_SPOOL)
- goto nospace;
- len -= SOF_SPOOL;
- space += SOF_SPOOL;
- if (StrToAddrAndPortRange(sep, &tmp->addr, protoName,
- &portRange) != 0)
- errx(EX_DATAERR, "redirect_port:"
- "invalid local port range");
- if (GETNUMPORTS(portRange) != 1)
- errx(EX_DATAERR, "redirect_port: local port"
- "must be single in this context");
- tmp->port = GETLOPORT(portRange);
- r->spool_cnt++;
- /* Point to the next possible cfg_spool. */
- spool_buf = &spool_buf[SOF_SPOOL];
- sep = strtok(NULL, ",");
- }
- }
- return (space);
-nospace:
- errx(EX_DATAERR, "redirect_port: buf is too small\n");
-}
-
-static int
-setup_redir_proto(char *spool_buf, int len,
- int *_ac, char ***_av)
-{
- char **av;
- int ac, space;
- struct protoent *protoent;
- struct cfg_redir *r;
-
- av = *_av;
- ac = *_ac;
- if (len >= SOF_REDIR) {
- r = (struct cfg_redir *)spool_buf;
- /* Skip cfg_redir at beginning of buf. */
- spool_buf = &spool_buf[SOF_REDIR];
- space = SOF_REDIR;
- len -= SOF_REDIR;
- } else
- goto nospace;
- r->mode = REDIR_PROTO;
- /*
- * Extract protocol.
- */
- if (ac == 0)
- errx(EX_DATAERR, "redirect_proto: missing protocol");
-
- protoent = getprotobyname(*av);
- if (protoent == NULL)
- errx(EX_DATAERR, "redirect_proto: unknown protocol %s", *av);
- else
- r->proto = protoent->p_proto;
-
- INC_ARGCV();
-
- /*
- * Extract local address.
- */
- if (ac == 0)
- errx(EX_DATAERR, "redirect_proto: missing local address");
- else
- StrToAddr(*av, &r->laddr);
-
- INC_ARGCV();
-
- /*
- * Extract optional public address.
- */
- if (ac == 0) {
- r->paddr.s_addr = INADDR_ANY;
- r->raddr.s_addr = INADDR_ANY;
- } else {
- /* see above in setup_redir_port() */
- if (!isalpha(**av)) {
- StrToAddr(*av, &r->paddr);
- INC_ARGCV();
-
- /*
- * Extract optional remote address.
- */
- /* see above in setup_redir_port() */
- if (ac!=0 && !isalpha(**av)) {
- StrToAddr(*av, &r->raddr);
- INC_ARGCV();
- }
- }
- }
- return (space);
-nospace:
- errx(EX_DATAERR, "redirect_proto: buf is too small\n");
-}
-
-static void
-print_nat_config(unsigned char *buf)
-{
- struct cfg_nat *n;
- int i, cnt, flag, off;
- struct cfg_redir *t;
- struct cfg_spool *s;
- struct protoent *p;
-
- n = (struct cfg_nat *)buf;
- flag = 1;
- off = sizeof(*n);
- printf("ipfw nat %u config", n->id);
- if (strlen(n->if_name) != 0)
- printf(" if %s", n->if_name);
- else if (n->ip.s_addr != 0)
- printf(" ip %s", inet_ntoa(n->ip));
- while (n->mode != 0) {
- if (n->mode & PKT_ALIAS_LOG) {
- printf(" log");
- n->mode &= ~PKT_ALIAS_LOG;
- } else if (n->mode & PKT_ALIAS_DENY_INCOMING) {
- printf(" deny_in");
- n->mode &= ~PKT_ALIAS_DENY_INCOMING;
- } else if (n->mode & PKT_ALIAS_SAME_PORTS) {
- printf(" same_ports");
- n->mode &= ~PKT_ALIAS_SAME_PORTS;
- } else if (n->mode & PKT_ALIAS_UNREGISTERED_ONLY) {
- printf(" unreg_only");
- n->mode &= ~PKT_ALIAS_UNREGISTERED_ONLY;
- } else if (n->mode & PKT_ALIAS_RESET_ON_ADDR_CHANGE) {
- printf(" reset");
- n->mode &= ~PKT_ALIAS_RESET_ON_ADDR_CHANGE;
- } else if (n->mode & PKT_ALIAS_REVERSE) {
- printf(" reverse");
- n->mode &= ~PKT_ALIAS_REVERSE;
- } else if (n->mode & PKT_ALIAS_PROXY_ONLY) {
- printf(" proxy_only");
- n->mode &= ~PKT_ALIAS_PROXY_ONLY;
- }
- }
- /* Print all the redirect's data configuration. */
- for (cnt = 0; cnt < n->redir_cnt; cnt++) {
- t = (struct cfg_redir *)&buf[off];
- off += SOF_REDIR;
- switch (t->mode) {
- case REDIR_ADDR:
- printf(" redirect_addr");
- if (t->spool_cnt == 0)
- printf(" %s", inet_ntoa(t->laddr));
- else
- for (i = 0; i < t->spool_cnt; i++) {
- s = (struct cfg_spool *)&buf[off];
- if (i)
- printf(",");
- else
- printf(" ");
- printf("%s", inet_ntoa(s->addr));
- off += SOF_SPOOL;
- }
- printf(" %s", inet_ntoa(t->paddr));
- break;
- case REDIR_PORT:
- p = getprotobynumber(t->proto);
- printf(" redirect_port %s ", p->p_name);
- if (!t->spool_cnt) {
- printf("%s:%u", inet_ntoa(t->laddr), t->lport);
- if (t->pport_cnt > 1)
- printf("-%u", t->lport +
- t->pport_cnt - 1);
- } else
- for (i=0; i < t->spool_cnt; i++) {
- s = (struct cfg_spool *)&buf[off];
- if (i)
- printf(",");
- printf("%s:%u", inet_ntoa(s->addr),
- s->port);
- off += SOF_SPOOL;
- }
-
- printf(" ");
- if (t->paddr.s_addr)
- printf("%s:", inet_ntoa(t->paddr));
- printf("%u", t->pport);
- if (!t->spool_cnt && t->pport_cnt > 1)
- printf("-%u", t->pport + t->pport_cnt - 1);
-
- if (t->raddr.s_addr) {
- printf(" %s", inet_ntoa(t->raddr));
- if (t->rport) {
- printf(":%u", t->rport);
- if (!t->spool_cnt && t->rport_cnt > 1)
- printf("-%u", t->rport +
- t->rport_cnt - 1);
- }
- }
- break;
- case REDIR_PROTO:
- p = getprotobynumber(t->proto);
- printf(" redirect_proto %s %s", p->p_name,
- inet_ntoa(t->laddr));
- if (t->paddr.s_addr != 0) {
- printf(" %s", inet_ntoa(t->paddr));
- if (t->raddr.s_addr)
- printf(" %s", inet_ntoa(t->raddr));
- }
- break;
- default:
- errx(EX_DATAERR, "unknown redir mode");
- break;
- }
- }
- printf("\n");
-}
-
-void
-ipfw_config_nat(int ac, char **av)
-{
- struct cfg_nat *n; /* Nat instance configuration. */
- int i, len, off, tok;
- char *id, buf[NAT_BUF_LEN]; /* Buffer for serialized data. */
-
- len = NAT_BUF_LEN;
- /* Offset in buf: save space for n at the beginning. */
- off = sizeof(*n);
- memset(buf, 0, sizeof(buf));
- n = (struct cfg_nat *)buf;
-
- av++; ac--;
- /* Nat id. */
- if (ac && isdigit(**av)) {
- id = *av;
- i = atoi(*av);
- ac--; av++;
- n->id = i;
- } else
- errx(EX_DATAERR, "missing nat id");
- if (ac == 0)
- errx(EX_DATAERR, "missing option");
-
- while (ac > 0) {
- tok = match_token(nat_params, *av);
- ac--; av++;
- switch (tok) {
- case TOK_IP:
- if (ac == 0)
- errx(EX_DATAERR, "missing option");
- if (!inet_aton(av[0], &(n->ip)))
- errx(EX_DATAERR, "bad ip address ``%s''",
- av[0]);
- ac--; av++;
- break;
- case TOK_IF:
- if (ac == 0)
- errx(EX_DATAERR, "missing option");
- set_addr_dynamic(av[0], n);
- ac--; av++;
- break;
- case TOK_ALOG:
- n->mode |= PKT_ALIAS_LOG;
- break;
- case TOK_DENY_INC:
- n->mode |= PKT_ALIAS_DENY_INCOMING;
- break;
- case TOK_SAME_PORTS:
- n->mode |= PKT_ALIAS_SAME_PORTS;
- break;
- case TOK_UNREG_ONLY:
- n->mode |= PKT_ALIAS_UNREGISTERED_ONLY;
- break;
- case TOK_RESET_ADDR:
- n->mode |= PKT_ALIAS_RESET_ON_ADDR_CHANGE;
- break;
- case TOK_ALIAS_REV:
- n->mode |= PKT_ALIAS_REVERSE;
- break;
- case TOK_PROXY_ONLY:
- n->mode |= PKT_ALIAS_PROXY_ONLY;
- break;
- /*
- * All the setup_redir_* functions work directly in the final
- * buffer, see above for details.
- */
- case TOK_REDIR_ADDR:
- case TOK_REDIR_PORT:
- case TOK_REDIR_PROTO:
- switch (tok) {
- case TOK_REDIR_ADDR:
- i = setup_redir_addr(&buf[off], len, &ac, &av);
- break;
- case TOK_REDIR_PORT:
- i = setup_redir_port(&buf[off], len, &ac, &av);
- break;
- case TOK_REDIR_PROTO:
- i = setup_redir_proto(&buf[off], len, &ac, &av);
- break;
- }
- n->redir_cnt++;
- off += i;
- len -= i;
- break;
- default:
- errx(EX_DATAERR, "unrecognised option ``%s''", av[-1]);
- }
- }
-
- i = do_cmd(IP_FW_NAT_CFG, buf, off);
- if (i)
- err(1, "setsockopt(%s)", "IP_FW_NAT_CFG");
-
- if (!co.do_quiet) {
- /* After every modification, we show the resultant rule. */
- int _ac = 3;
- char *_av[] = {"show", "config", id};
- ipfw_show_nat(_ac, _av);
- }
-}
-
static void
get_mac_addr_mask(const char *p, uint8_t *addr, uint8_t *mask)
{
@@ -5149,78 +3909,3 @@ table_list(ipfw_table_entry ent, int need_header)
}
free(tbl);
}
-
-void
-ipfw_show_nat(int ac, char **av)
-{
- struct cfg_nat *n;
- struct cfg_redir *e;
- int cmd, i, nbytes, do_cfg, do_rule, frule, lrule, nalloc, size;
- int nat_cnt, redir_cnt, r;
- uint8_t *data, *p;
- char *endptr;
-
- do_rule = 0;
- nalloc = 1024;
- size = 0;
- data = NULL;
- frule = 0;
- lrule = IPFW_DEFAULT_RULE; /* max ipfw rule number */
- ac--; av++;
-
- if (co.test_only)
- return;
-
- /* Parse parameters. */
- for (cmd = IP_FW_NAT_GET_LOG, do_cfg = 0; ac != 0; ac--, av++) {
- if (!strncmp(av[0], "config", strlen(av[0]))) {
- cmd = IP_FW_NAT_GET_CONFIG, do_cfg = 1;
- continue;
- }
- /* Convert command line rule #. */
- frule = lrule = strtoul(av[0], &endptr, 10);
- if (*endptr == '-')
- lrule = strtoul(endptr+1, &endptr, 10);
- if (lrule == 0)
- err(EX_USAGE, "invalid rule number: %s", av[0]);
- do_rule = 1;
- }
-
- nbytes = nalloc;
- while (nbytes >= nalloc) {
- nalloc = nalloc * 2;
- nbytes = nalloc;
- data = safe_realloc(data, nbytes);
- if (do_cmd(cmd, data, (uintptr_t)&nbytes) < 0)
- err(EX_OSERR, "getsockopt(IP_FW_GET_%s)",
- (cmd == IP_FW_NAT_GET_LOG) ? "LOG" : "CONFIG");
- }
- if (nbytes == 0)
- exit(0);
- if (do_cfg) {
- nat_cnt = *((int *)data);
- for (i = sizeof(nat_cnt); nat_cnt; nat_cnt--) {
- n = (struct cfg_nat *)&data[i];
- if (frule <= n->id && lrule >= n->id)
- print_nat_config(&data[i]);
- i += sizeof(struct cfg_nat);
- for (redir_cnt = 0; redir_cnt < n->redir_cnt; redir_cnt++) {
- e = (struct cfg_redir *)&data[i];
- i += sizeof(struct cfg_redir) + e->spool_cnt *
- sizeof(struct cfg_spool);
- }
- }
- } else {
- for (i = 0; 1; i += LIBALIAS_BUF_SIZE + sizeof(int)) {
- p = &data[i];
- if (p == data + nbytes)
- break;
- bcopy(p, &r, sizeof(int));
- if (do_rule) {
- if (!(frule <= r && lrule >= r))
- continue;
- }
- printf("nat %u: %s\n", r, p+sizeof(int));
- }
- }
-}
diff --git a/sbin/ipfw/ipfw2.h b/sbin/ipfw/ipfw2.h
index 8e4e00d..14c3837 100644
--- a/sbin/ipfw/ipfw2.h
+++ b/sbin/ipfw/ipfw2.h
@@ -194,15 +194,25 @@ enum tokens {
void *safe_calloc(size_t number, size_t size);
void *safe_realloc(void *ptr, size_t size);
-/* a string comparison function used for historical compatibility */
+/* string comparison functions used for historical compatibility */
int _substrcmp(const char *str1, const char* str2);
int _substrcmp2(const char *str1, const char* str2, const char* str3);
+/* utility functions */
int match_token(struct _s_x *table, char *string);
+char const *match_value(struct _s_x *p, int value);
+
int do_cmd(int optname, void *optval, uintptr_t optlen);
struct in6_addr;
void n2mask(struct in6_addr *mask, int n);
+int contigmask(uint8_t *p, int len);
+
+/* forward declarations to avoid header dependency */
+typedef struct _ipfw_insn ipfw_insn;
+typedef struct _ipfw_insn_u32 ipfw_insn_u32;
+typedef struct _ipfw_insn_ip6 ipfw_insn_ip6;
+typedef struct _ipfw_insn_icmp6 ipfw_insn_icmp6;
/*
@@ -212,6 +222,7 @@ void n2mask(struct in6_addr *mask, int n);
*/
extern int resvd_set_number;
+/* first-level command handlers */
void ipfw_add(int ac, char *av[]);
void ipfw_show_nat(int ac, char **av);
void ipfw_config_pipe(int ac, char **av);
@@ -224,7 +235,21 @@ void ipfw_flush(int force);
void ipfw_zero(int ac, char *av[], int optname);
void ipfw_list(int ac, char *av[], int show_counters);
-/* in dummynet.c */
+/* dummynet.c */
void ipfw_list_pipes(void *data, uint nbytes, int ac, char *av[]);
int ipfw_delete_pipe(int pipe_or_queue, int n);
+/* ipv6.c */
+void print_unreach6_code(uint16_t code);
+void print_ip6(ipfw_insn_ip6 *cmd, char const *s);
+void print_flow6id( ipfw_insn_u32 *cmd);
+void print_icmp6types(ipfw_insn_u32 *cmd);
+void print_ext6hdr( ipfw_insn *cmd );
+
+ipfw_insn *add_srcip6(ipfw_insn *cmd, char *av);
+ipfw_insn *add_dstip6(ipfw_insn *cmd, char *av);
+
+void fill_flow6( ipfw_insn_u32 *cmd, char *av );
+void fill_unreach6_code(u_short *codep, char *str);
+void fill_icmp6types(ipfw_insn_icmp6 *cmd, char *av);
+int fill_ext6hdr( ipfw_insn *cmd, char *av);
diff --git a/sbin/ipfw/ipv6.c b/sbin/ipfw/ipv6.c
new file mode 100644
index 0000000..40f078b
--- /dev/null
+++ b/sbin/ipfw/ipv6.c
@@ -0,0 +1,501 @@
+/*
+ * Copyright (c) 2002-2003 Luigi Rizzo
+ * Copyright (c) 1996 Alex Nash, Paul Traina, Poul-Henning Kamp
+ * Copyright (c) 1994 Ugen J.S.Antsilevich
+ *
+ * Idea and grammar partially left from:
+ * Copyright (c) 1993 Daniel Boulet
+ *
+ * Redistribution and use in source forms, with and without modification,
+ * are permitted provided that this entire comment appears intact.
+ *
+ * Redistribution in binary form may occur without any restrictions.
+ * Obviously, it would be nice if you gave credit where credit is due
+ * but requiring it would be too onerous.
+ *
+ * This software is provided ``AS IS'' without any warranties of any kind.
+ *
+ * NEW command line interface for IP firewall facility
+ *
+ * $FreeBSD$
+ *
+ * ipv6 support
+ */
+
+#include <sys/types.h>
+#include <sys/socket.h>
+
+#include "ipfw2.h"
+
+#include <err.h>
+#include <netdb.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sysexits.h>
+
+#include <net/if.h>
+#include <netinet/in.h>
+#include <netinet/in_systm.h>
+#include <netinet/ip.h>
+#include <netinet/icmp6.h>
+#include <netinet/ip_fw.h>
+#include <arpa/inet.h>
+
+static struct _s_x icmp6codes[] = {
+ { "no-route", ICMP6_DST_UNREACH_NOROUTE },
+ { "admin-prohib", ICMP6_DST_UNREACH_ADMIN },
+ { "address", ICMP6_DST_UNREACH_ADDR },
+ { "port", ICMP6_DST_UNREACH_NOPORT },
+ { NULL, 0 }
+};
+
+void
+fill_unreach6_code(u_short *codep, char *str)
+{
+ int val;
+ char *s;
+
+ val = strtoul(str, &s, 0);
+ if (s == str || *s != '\0' || val >= 0x100)
+ val = match_token(icmp6codes, str);
+ if (val < 0)
+ errx(EX_DATAERR, "unknown ICMPv6 unreachable code ``%s''", str);
+ *codep = val;
+ return;
+}
+
+void
+print_unreach6_code(uint16_t code)
+{
+ char const *s = match_value(icmp6codes, code);
+
+ if (s != NULL)
+ printf("unreach6 %s", s);
+ else
+ printf("unreach6 %u", code);
+}
+
+/*
+ * Print the ip address contained in a command.
+ */
+void
+print_ip6(ipfw_insn_ip6 *cmd, char const *s)
+{
+ struct hostent *he = NULL;
+ int len = F_LEN((ipfw_insn *) cmd) - 1;
+ struct in6_addr *a = &(cmd->addr6);
+ char trad[255];
+
+ printf("%s%s ", cmd->o.len & F_NOT ? " not": "", s);
+
+ if (cmd->o.opcode == O_IP6_SRC_ME || cmd->o.opcode == O_IP6_DST_ME) {
+ printf("me6");
+ return;
+ }
+ if (cmd->o.opcode == O_IP6) {
+ printf(" ip6");
+ return;
+ }
+
+ /*
+ * len == 4 indicates a single IP, whereas lists of 1 or more
+ * addr/mask pairs have len = (2n+1). We convert len to n so we
+ * use that to count the number of entries.
+ */
+
+ for (len = len / 4; len > 0; len -= 2, a += 2) {
+ int mb = /* mask length */
+ (cmd->o.opcode == O_IP6_SRC || cmd->o.opcode == O_IP6_DST) ?
+ 128 : contigmask((uint8_t *)&(a[1]), 128);
+
+ if (mb == 128 && co.do_resolv)
+ he = gethostbyaddr((char *)a, sizeof(*a), AF_INET6);
+ if (he != NULL) /* resolved to name */
+ printf("%s", he->h_name);
+ else if (mb == 0) /* any */
+ printf("any");
+ else { /* numeric IP followed by some kind of mask */
+ if (inet_ntop(AF_INET6, a, trad, sizeof( trad ) ) == NULL)
+ printf("Error ntop in print_ip6\n");
+ printf("%s", trad );
+ if (mb < 0) /* XXX not really legal... */
+ printf(":%s",
+ inet_ntop(AF_INET6, &a[1], trad, sizeof(trad)));
+ else if (mb < 128)
+ printf("/%d", mb);
+ }
+ if (len > 2)
+ printf(",");
+ }
+}
+
+void
+fill_icmp6types(ipfw_insn_icmp6 *cmd, char *av)
+{
+ uint8_t type;
+
+ bzero(cmd, sizeof(*cmd));
+ while (*av) {
+ if (*av == ',')
+ av++;
+ type = strtoul(av, &av, 0);
+ if (*av != ',' && *av != '\0')
+ errx(EX_DATAERR, "invalid ICMP6 type");
+ /*
+ * XXX: shouldn't this be 0xFF? I can't see any reason why
+ * we shouldn't be able to filter all possiable values
+ * regardless of the ability of the rest of the kernel to do
+ * anything useful with them.
+ */
+ if (type > ICMP6_MAXTYPE)
+ errx(EX_DATAERR, "ICMP6 type out of range");
+ cmd->d[type / 32] |= ( 1 << (type % 32));
+ }
+ cmd->o.opcode = O_ICMP6TYPE;
+ cmd->o.len |= F_INSN_SIZE(ipfw_insn_icmp6);
+}
+
+
+void
+print_icmp6types(ipfw_insn_u32 *cmd)
+{
+ int i, j;
+ char sep= ' ';
+
+ printf(" ip6 icmp6types");
+ for (i = 0; i < 7; i++)
+ for (j=0; j < 32; ++j) {
+ if ( (cmd->d[i] & (1 << (j))) == 0)
+ continue;
+ printf("%c%d", sep, (i*32 + j));
+ sep = ',';
+ }
+}
+
+void
+print_flow6id( ipfw_insn_u32 *cmd)
+{
+ uint16_t i, limit = cmd->o.arg1;
+ char sep = ',';
+
+ printf(" flow-id ");
+ for( i=0; i < limit; ++i) {
+ if (i == limit - 1)
+ sep = ' ';
+ printf("%d%c", cmd->d[i], sep);
+ }
+}
+
+/* structure and define for the extension header in ipv6 */
+static struct _s_x ext6hdrcodes[] = {
+ { "frag", EXT_FRAGMENT },
+ { "hopopt", EXT_HOPOPTS },
+ { "route", EXT_ROUTING },
+ { "dstopt", EXT_DSTOPTS },
+ { "ah", EXT_AH },
+ { "esp", EXT_ESP },
+ { "rthdr0", EXT_RTHDR0 },
+ { "rthdr2", EXT_RTHDR2 },
+ { NULL, 0 }
+};
+
+/* fills command for the extension header filtering */
+int
+fill_ext6hdr( ipfw_insn *cmd, char *av)
+{
+ int tok;
+ char *s = av;
+
+ cmd->arg1 = 0;
+
+ while(s) {
+ av = strsep( &s, ",") ;
+ tok = match_token(ext6hdrcodes, av);
+ switch (tok) {
+ case EXT_FRAGMENT:
+ cmd->arg1 |= EXT_FRAGMENT;
+ break;
+
+ case EXT_HOPOPTS:
+ cmd->arg1 |= EXT_HOPOPTS;
+ break;
+
+ case EXT_ROUTING:
+ cmd->arg1 |= EXT_ROUTING;
+ break;
+
+ case EXT_DSTOPTS:
+ cmd->arg1 |= EXT_DSTOPTS;
+ break;
+
+ case EXT_AH:
+ cmd->arg1 |= EXT_AH;
+ break;
+
+ case EXT_ESP:
+ cmd->arg1 |= EXT_ESP;
+ break;
+
+ case EXT_RTHDR0:
+ cmd->arg1 |= EXT_RTHDR0;
+ break;
+
+ case EXT_RTHDR2:
+ cmd->arg1 |= EXT_RTHDR2;
+ break;
+
+ default:
+ errx( EX_DATAERR, "invalid option for ipv6 exten header" );
+ break;
+ }
+ }
+ if (cmd->arg1 == 0 )
+ return 0;
+ cmd->opcode = O_EXT_HDR;
+ cmd->len |= F_INSN_SIZE( ipfw_insn );
+ return 1;
+}
+
+void
+print_ext6hdr( ipfw_insn *cmd )
+{
+ char sep = ' ';
+
+ printf(" extension header:");
+ if (cmd->arg1 & EXT_FRAGMENT ) {
+ printf("%cfragmentation", sep);
+ sep = ',';
+ }
+ if (cmd->arg1 & EXT_HOPOPTS ) {
+ printf("%chop options", sep);
+ sep = ',';
+ }
+ if (cmd->arg1 & EXT_ROUTING ) {
+ printf("%crouting options", sep);
+ sep = ',';
+ }
+ if (cmd->arg1 & EXT_RTHDR0 ) {
+ printf("%crthdr0", sep);
+ sep = ',';
+ }
+ if (cmd->arg1 & EXT_RTHDR2 ) {
+ printf("%crthdr2", sep);
+ sep = ',';
+ }
+ if (cmd->arg1 & EXT_DSTOPTS ) {
+ printf("%cdestination options", sep);
+ sep = ',';
+ }
+ if (cmd->arg1 & EXT_AH ) {
+ printf("%cauthentication header", sep);
+ sep = ',';
+ }
+ if (cmd->arg1 & EXT_ESP ) {
+ printf("%cencapsulated security payload", sep);
+ }
+}
+
+/* Try to find ipv6 address by hostname */
+static int
+lookup_host6 (char *host, struct in6_addr *ip6addr)
+{
+ struct hostent *he;
+
+ if (!inet_pton(AF_INET6, host, ip6addr)) {
+ if ((he = gethostbyname2(host, AF_INET6)) == NULL)
+ return(-1);
+ memcpy(ip6addr, he->h_addr_list[0], sizeof( struct in6_addr));
+ }
+ return(0);
+}
+
+
+/*
+ * fill the addr and mask fields in the instruction as appropriate from av.
+ * Update length as appropriate.
+ * The following formats are allowed:
+ * any matches any IP6. Actually returns an empty instruction.
+ * me returns O_IP6_*_ME
+ *
+ * 03f1::234:123:0342 single IP6 addres
+ * 03f1::234:123:0342/24 address/mask
+ * 03f1::234:123:0342/24,03f1::234:123:0343/ List of address
+ *
+ * Set of address (as in ipv6) not supported because ipv6 address
+ * are typically random past the initial prefix.
+ * Return 1 on success, 0 on failure.
+ */
+static int
+fill_ip6(ipfw_insn_ip6 *cmd, char *av)
+{
+ int len = 0;
+ struct in6_addr *d = &(cmd->addr6);
+ /*
+ * Needed for multiple address.
+ * Note d[1] points to struct in6_add r mask6 of cmd
+ */
+
+ cmd->o.len &= ~F_LEN_MASK; /* zero len */
+
+ if (strcmp(av, "any") == 0)
+ return (1);
+
+
+ if (strcmp(av, "me") == 0) { /* Set the data for "me" opt*/
+ cmd->o.len |= F_INSN_SIZE(ipfw_insn);
+ return (1);
+ }
+
+ if (strcmp(av, "me6") == 0) { /* Set the data for "me" opt*/
+ cmd->o.len |= F_INSN_SIZE(ipfw_insn);
+ return (1);
+ }
+
+ av = strdup(av);
+ while (av) {
+ /*
+ * After the address we can have '/' indicating a mask,
+ * or ',' indicating another address follows.
+ */
+
+ char *p;
+ int masklen;
+ char md = '\0';
+
+ if ((p = strpbrk(av, "/,")) ) {
+ md = *p; /* save the separator */
+ *p = '\0'; /* terminate address string */
+ p++; /* and skip past it */
+ }
+ /* now p points to NULL, mask or next entry */
+
+ /* lookup stores address in *d as a side effect */
+ if (lookup_host6(av, d) != 0) {
+ /* XXX: failed. Free memory and go */
+ errx(EX_DATAERR, "bad address \"%s\"", av);
+ }
+ /* next, look at the mask, if any */
+ masklen = (md == '/') ? atoi(p) : 128;
+ if (masklen > 128 || masklen < 0)
+ errx(EX_DATAERR, "bad width \"%s\''", p);
+ else
+ n2mask(&d[1], masklen);
+
+ APPLY_MASK(d, &d[1]) /* mask base address with mask */
+
+ /* find next separator */
+
+ if (md == '/') { /* find separator past the mask */
+ p = strpbrk(p, ",");
+ if (p != NULL)
+ p++;
+ }
+ av = p;
+
+ /* Check this entry */
+ if (masklen == 0) {
+ /*
+ * 'any' turns the entire list into a NOP.
+ * 'not any' never matches, so it is removed from the
+ * list unless it is the only item, in which case we
+ * report an error.
+ */
+ if (cmd->o.len & F_NOT && av == NULL && len == 0)
+ errx(EX_DATAERR, "not any never matches");
+ continue;
+ }
+
+ /*
+ * A single IP can be stored alone
+ */
+ if (masklen == 128 && av == NULL && len == 0) {
+ len = F_INSN_SIZE(struct in6_addr);
+ break;
+ }
+
+ /* Update length and pointer to arguments */
+ len += F_INSN_SIZE(struct in6_addr)*2;
+ d += 2;
+ } /* end while */
+
+ /*
+ * Total length of the command, remember that 1 is the size of
+ * the base command.
+ */
+ if (len + 1 > F_LEN_MASK)
+ errx(EX_DATAERR, "address list too long");
+ cmd->o.len |= len+1;
+ free(av);
+ return (1);
+}
+
+/*
+ * fills command for ipv6 flow-id filtering
+ * note that the 20 bit flow number is stored in a array of u_int32_t
+ * it's supported lists of flow-id, so in the o.arg1 we store how many
+ * additional flow-id we want to filter, the basic is 1
+ */
+void
+fill_flow6( ipfw_insn_u32 *cmd, char *av )
+{
+ u_int32_t type; /* Current flow number */
+ u_int16_t nflow = 0; /* Current flow index */
+ char *s = av;
+ cmd->d[0] = 0; /* Initializing the base number*/
+
+ while (s) {
+ av = strsep( &s, ",") ;
+ type = strtoul(av, &av, 0);
+ if (*av != ',' && *av != '\0')
+ errx(EX_DATAERR, "invalid ipv6 flow number %s", av);
+ if (type > 0xfffff)
+ errx(EX_DATAERR, "flow number out of range %s", av);
+ cmd->d[nflow] |= type;
+ nflow++;
+ }
+ if( nflow > 0 ) {
+ cmd->o.opcode = O_FLOW6ID;
+ cmd->o.len |= F_INSN_SIZE(ipfw_insn_u32) + nflow;
+ cmd->o.arg1 = nflow;
+ }
+ else {
+ errx(EX_DATAERR, "invalid ipv6 flow number %s", av);
+ }
+}
+
+ipfw_insn *
+add_srcip6(ipfw_insn *cmd, char *av)
+{
+
+ fill_ip6((ipfw_insn_ip6 *)cmd, av);
+ if (F_LEN(cmd) == 0) { /* any */
+ } else if (F_LEN(cmd) == F_INSN_SIZE(ipfw_insn)) { /* "me" */
+ cmd->opcode = O_IP6_SRC_ME;
+ } else if (F_LEN(cmd) ==
+ (F_INSN_SIZE(struct in6_addr) + F_INSN_SIZE(ipfw_insn))) {
+ /* single IP, no mask*/
+ cmd->opcode = O_IP6_SRC;
+ } else { /* addr/mask opt */
+ cmd->opcode = O_IP6_SRC_MASK;
+ }
+ return cmd;
+}
+
+ipfw_insn *
+add_dstip6(ipfw_insn *cmd, char *av)
+{
+
+ fill_ip6((ipfw_insn_ip6 *)cmd, av);
+ if (F_LEN(cmd) == 0) { /* any */
+ } else if (F_LEN(cmd) == F_INSN_SIZE(ipfw_insn)) { /* "me" */
+ cmd->opcode = O_IP6_DST_ME;
+ } else if (F_LEN(cmd) ==
+ (F_INSN_SIZE(struct in6_addr) + F_INSN_SIZE(ipfw_insn))) {
+ /* single IP, no mask*/
+ cmd->opcode = O_IP6_DST;
+ } else { /* addr/mask opt */
+ cmd->opcode = O_IP6_DST_MASK;
+ }
+ return cmd;
+}
diff --git a/sbin/ipfw/nat.c b/sbin/ipfw/nat.c
new file mode 100644
index 0000000..7c2453b
--- /dev/null
+++ b/sbin/ipfw/nat.c
@@ -0,0 +1,905 @@
+/*
+ * Copyright (c) 2002-2003 Luigi Rizzo
+ * Copyright (c) 1996 Alex Nash, Paul Traina, Poul-Henning Kamp
+ * Copyright (c) 1994 Ugen J.S.Antsilevich
+ *
+ * Idea and grammar partially left from:
+ * Copyright (c) 1993 Daniel Boulet
+ *
+ * Redistribution and use in source forms, with and without modification,
+ * are permitted provided that this entire comment appears intact.
+ *
+ * Redistribution in binary form may occur without any restrictions.
+ * Obviously, it would be nice if you gave credit where credit is due
+ * but requiring it would be too onerous.
+ *
+ * This software is provided ``AS IS'' without any warranties of any kind.
+ *
+ * NEW command line interface for IP firewall facility
+ *
+ * $FreeBSD$
+ *
+ * In-kernel nat support
+ */
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/sockio.h>
+#include <sys/sysctl.h>
+
+#include "ipfw2.h"
+
+#include <ctype.h>
+#include <err.h>
+#include <netdb.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sysexits.h>
+
+#define IPFW_INTERNAL /* Access to protected structures in ip_fw.h. */
+
+#include <net/if.h>
+#include <net/if_dl.h>
+#include <net/route.h> /* def. of struct route */
+#include <netinet/in.h>
+#include <netinet/in_systm.h>
+#include <netinet/ip_fw.h>
+#include <arpa/inet.h>
+#include <alias.h>
+
+static struct _s_x nat_params[] = {
+ { "ip", TOK_IP },
+ { "if", TOK_IF },
+ { "log", TOK_ALOG },
+ { "deny_in", TOK_DENY_INC },
+ { "same_ports", TOK_SAME_PORTS },
+ { "unreg_only", TOK_UNREG_ONLY },
+ { "reset", TOK_RESET_ADDR },
+ { "reverse", TOK_ALIAS_REV },
+ { "proxy_only", TOK_PROXY_ONLY },
+ { "redirect_addr", TOK_REDIR_ADDR },
+ { "redirect_port", TOK_REDIR_PORT },
+ { "redirect_proto", TOK_REDIR_PROTO },
+ { NULL, 0 } /* terminator */
+};
+
+
+/*
+ * Search for interface with name "ifn", and fill n accordingly:
+ *
+ * n->ip ip address of interface "ifn"
+ * n->if_name copy of interface name "ifn"
+ */
+static void
+set_addr_dynamic(const char *ifn, struct cfg_nat *n)
+{
+ size_t needed;
+ int mib[6];
+ char *buf, *lim, *next;
+ struct if_msghdr *ifm;
+ struct ifa_msghdr *ifam;
+ struct sockaddr_dl *sdl;
+ struct sockaddr_in *sin;
+ int ifIndex, ifMTU;
+
+ mib[0] = CTL_NET;
+ mib[1] = PF_ROUTE;
+ mib[2] = 0;
+ mib[3] = AF_INET;
+ mib[4] = NET_RT_IFLIST;
+ mib[5] = 0;
+/*
+ * Get interface data.
+ */
+ if (sysctl(mib, 6, NULL, &needed, NULL, 0) == -1)
+ err(1, "iflist-sysctl-estimate");
+ buf = safe_calloc(1, needed);
+ if (sysctl(mib, 6, buf, &needed, NULL, 0) == -1)
+ err(1, "iflist-sysctl-get");
+ lim = buf + needed;
+/*
+ * Loop through interfaces until one with
+ * given name is found. This is done to
+ * find correct interface index for routing
+ * message processing.
+ */
+ ifIndex = 0;
+ next = buf;
+ while (next < lim) {
+ ifm = (struct if_msghdr *)next;
+ next += ifm->ifm_msglen;
+ if (ifm->ifm_version != RTM_VERSION) {
+ if (co.verbose)
+ warnx("routing message version %d "
+ "not understood", ifm->ifm_version);
+ continue;
+ }
+ if (ifm->ifm_type == RTM_IFINFO) {
+ sdl = (struct sockaddr_dl *)(ifm + 1);
+ if (strlen(ifn) == sdl->sdl_nlen &&
+ strncmp(ifn, sdl->sdl_data, sdl->sdl_nlen) == 0) {
+ ifIndex = ifm->ifm_index;
+ ifMTU = ifm->ifm_data.ifi_mtu;
+ break;
+ }
+ }
+ }
+ if (!ifIndex)
+ errx(1, "unknown interface name %s", ifn);
+/*
+ * Get interface address.
+ */
+ sin = NULL;
+ while (next < lim) {
+ ifam = (struct ifa_msghdr *)next;
+ next += ifam->ifam_msglen;
+ if (ifam->ifam_version != RTM_VERSION) {
+ if (co.verbose)
+ warnx("routing message version %d "
+ "not understood", ifam->ifam_version);
+ continue;
+ }
+ if (ifam->ifam_type != RTM_NEWADDR)
+ break;
+ if (ifam->ifam_addrs & RTA_IFA) {
+ int i;
+ char *cp = (char *)(ifam + 1);
+
+ for (i = 1; i < RTA_IFA; i <<= 1) {
+ if (ifam->ifam_addrs & i)
+ cp += SA_SIZE((struct sockaddr *)cp);
+ }
+ if (((struct sockaddr *)cp)->sa_family == AF_INET) {
+ sin = (struct sockaddr_in *)cp;
+ break;
+ }
+ }
+ }
+ if (sin == NULL)
+ errx(1, "%s: cannot get interface address", ifn);
+
+ n->ip = sin->sin_addr;
+ strncpy(n->if_name, ifn, IF_NAMESIZE);
+
+ free(buf);
+}
+
+/*
+ * XXX - The following functions, macros and definitions come from natd.c:
+ * it would be better to move them outside natd.c, in a file
+ * (redirect_support.[ch]?) shared by ipfw and natd, but for now i can live
+ * with it.
+ */
+
+/*
+ * Definition of a port range, and macros to deal with values.
+ * FORMAT: HI 16-bits == first port in range, 0 == all ports.
+ * LO 16-bits == number of ports in range
+ * NOTES: - Port values are not stored in network byte order.
+ */
+
+#define port_range u_long
+
+#define GETLOPORT(x) ((x) >> 0x10)
+#define GETNUMPORTS(x) ((x) & 0x0000ffff)
+#define GETHIPORT(x) (GETLOPORT((x)) + GETNUMPORTS((x)))
+
+/* Set y to be the low-port value in port_range variable x. */
+#define SETLOPORT(x,y) ((x) = ((x) & 0x0000ffff) | ((y) << 0x10))
+
+/* Set y to be the number of ports in port_range variable x. */
+#define SETNUMPORTS(x,y) ((x) = ((x) & 0xffff0000) | (y))
+
+static void
+StrToAddr (const char* str, struct in_addr* addr)
+{
+ struct hostent* hp;
+
+ if (inet_aton (str, addr))
+ return;
+
+ hp = gethostbyname (str);
+ if (!hp)
+ errx (1, "unknown host %s", str);
+
+ memcpy (addr, hp->h_addr, sizeof (struct in_addr));
+}
+
+static int
+StrToPortRange (const char* str, const char* proto, port_range *portRange)
+{
+ char* sep;
+ struct servent* sp;
+ char* end;
+ u_short loPort;
+ u_short hiPort;
+
+ /* First see if this is a service, return corresponding port if so. */
+ sp = getservbyname (str,proto);
+ if (sp) {
+ SETLOPORT(*portRange, ntohs(sp->s_port));
+ SETNUMPORTS(*portRange, 1);
+ return 0;
+ }
+
+ /* Not a service, see if it's a single port or port range. */
+ sep = strchr (str, '-');
+ if (sep == NULL) {
+ SETLOPORT(*portRange, strtol(str, &end, 10));
+ if (end != str) {
+ /* Single port. */
+ SETNUMPORTS(*portRange, 1);
+ return 0;
+ }
+
+ /* Error in port range field. */
+ errx (EX_DATAERR, "%s/%s: unknown service", str, proto);
+ }
+
+ /* Port range, get the values and sanity check. */
+ sscanf (str, "%hu-%hu", &loPort, &hiPort);
+ SETLOPORT(*portRange, loPort);
+ SETNUMPORTS(*portRange, 0); /* Error by default */
+ if (loPort <= hiPort)
+ SETNUMPORTS(*portRange, hiPort - loPort + 1);
+
+ if (GETNUMPORTS(*portRange) == 0)
+ errx (EX_DATAERR, "invalid port range %s", str);
+
+ return 0;
+}
+
+static int
+StrToProto (const char* str)
+{
+ if (!strcmp (str, "tcp"))
+ return IPPROTO_TCP;
+
+ if (!strcmp (str, "udp"))
+ return IPPROTO_UDP;
+
+ errx (EX_DATAERR, "unknown protocol %s. Expected tcp or udp", str);
+}
+
+static int
+StrToAddrAndPortRange (const char* str, struct in_addr* addr, char* proto,
+ port_range *portRange)
+{
+ char* ptr;
+
+ ptr = strchr (str, ':');
+ if (!ptr)
+ errx (EX_DATAERR, "%s is missing port number", str);
+
+ *ptr = '\0';
+ ++ptr;
+
+ StrToAddr (str, addr);
+ return StrToPortRange (ptr, proto, portRange);
+}
+
+/* End of stuff taken from natd.c. */
+
+#define INC_ARGCV() do { \
+ (*_av)++; \
+ (*_ac)--; \
+ av = *_av; \
+ ac = *_ac; \
+} while(0)
+
+/*
+ * The next 3 functions add support for the addr, port and proto redirect and
+ * their logic is loosely based on SetupAddressRedirect(), SetupPortRedirect()
+ * and SetupProtoRedirect() from natd.c.
+ *
+ * Every setup_* function fills at least one redirect entry
+ * (struct cfg_redir) and zero or more server pool entry (struct cfg_spool)
+ * in buf.
+ *
+ * The format of data in buf is:
+ *
+ *
+ * cfg_nat cfg_redir cfg_spool ...... cfg_spool
+ *
+ * ------------------------------------- ------------
+ * | | .....X ... | | | | .....
+ * ------------------------------------- ...... ------------
+ * ^
+ * spool_cnt n=0 ...... n=(X-1)
+ *
+ * len points to the amount of available space in buf
+ * space counts the memory consumed by every function
+ *
+ * XXX - Every function get all the argv params so it
+ * has to check, in optional parameters, that the next
+ * args is a valid option for the redir entry and not
+ * another token. Only redir_port and redir_proto are
+ * affected by this.
+ */
+
+static int
+setup_redir_addr(char *spool_buf, int len,
+ int *_ac, char ***_av)
+{
+ char **av, *sep; /* Token separator. */
+ /* Temporary buffer used to hold server pool ip's. */
+ char tmp_spool_buf[NAT_BUF_LEN];
+ int ac, space, lsnat;
+ struct cfg_redir *r;
+ struct cfg_spool *tmp;
+
+ av = *_av;
+ ac = *_ac;
+ space = 0;
+ lsnat = 0;
+ if (len >= SOF_REDIR) {
+ r = (struct cfg_redir *)spool_buf;
+ /* Skip cfg_redir at beginning of buf. */
+ spool_buf = &spool_buf[SOF_REDIR];
+ space = SOF_REDIR;
+ len -= SOF_REDIR;
+ } else
+ goto nospace;
+ r->mode = REDIR_ADDR;
+ /* Extract local address. */
+ if (ac == 0)
+ errx(EX_DATAERR, "redirect_addr: missing local address");
+ sep = strchr(*av, ',');
+ if (sep) { /* LSNAT redirection syntax. */
+ r->laddr.s_addr = INADDR_NONE;
+ /* Preserve av, copy spool servers to tmp_spool_buf. */
+ strncpy(tmp_spool_buf, *av, strlen(*av)+1);
+ lsnat = 1;
+ } else
+ StrToAddr(*av, &r->laddr);
+ INC_ARGCV();
+
+ /* Extract public address. */
+ if (ac == 0)
+ errx(EX_DATAERR, "redirect_addr: missing public address");
+ StrToAddr(*av, &r->paddr);
+ INC_ARGCV();
+
+ /* Setup LSNAT server pool. */
+ if (sep) {
+ sep = strtok(tmp_spool_buf, ",");
+ while (sep != NULL) {
+ tmp = (struct cfg_spool *)spool_buf;
+ if (len < SOF_SPOOL)
+ goto nospace;
+ len -= SOF_SPOOL;
+ space += SOF_SPOOL;
+ StrToAddr(sep, &tmp->addr);
+ tmp->port = ~0;
+ r->spool_cnt++;
+ /* Point to the next possible cfg_spool. */
+ spool_buf = &spool_buf[SOF_SPOOL];
+ sep = strtok(NULL, ",");
+ }
+ }
+ return(space);
+nospace:
+ errx(EX_DATAERR, "redirect_addr: buf is too small\n");
+}
+
+static int
+setup_redir_port(char *spool_buf, int len,
+ int *_ac, char ***_av)
+{
+ char **av, *sep, *protoName;
+ char tmp_spool_buf[NAT_BUF_LEN];
+ int ac, space, lsnat;
+ struct cfg_redir *r;
+ struct cfg_spool *tmp;
+ u_short numLocalPorts;
+ port_range portRange;
+
+ av = *_av;
+ ac = *_ac;
+ space = 0;
+ lsnat = 0;
+ numLocalPorts = 0;
+
+ if (len >= SOF_REDIR) {
+ r = (struct cfg_redir *)spool_buf;
+ /* Skip cfg_redir at beginning of buf. */
+ spool_buf = &spool_buf[SOF_REDIR];
+ space = SOF_REDIR;
+ len -= SOF_REDIR;
+ } else
+ goto nospace;
+ r->mode = REDIR_PORT;
+ /*
+ * Extract protocol.
+ */
+ if (ac == 0)
+ errx (EX_DATAERR, "redirect_port: missing protocol");
+ r->proto = StrToProto(*av);
+ protoName = *av;
+ INC_ARGCV();
+
+ /*
+ * Extract local address.
+ */
+ if (ac == 0)
+ errx (EX_DATAERR, "redirect_port: missing local address");
+
+ sep = strchr(*av, ',');
+ /* LSNAT redirection syntax. */
+ if (sep) {
+ r->laddr.s_addr = INADDR_NONE;
+ r->lport = ~0;
+ numLocalPorts = 1;
+ /* Preserve av, copy spool servers to tmp_spool_buf. */
+ strncpy(tmp_spool_buf, *av, strlen(*av)+1);
+ lsnat = 1;
+ } else {
+ if (StrToAddrAndPortRange (*av, &r->laddr, protoName,
+ &portRange) != 0)
+ errx(EX_DATAERR, "redirect_port:"
+ "invalid local port range");
+
+ r->lport = GETLOPORT(portRange);
+ numLocalPorts = GETNUMPORTS(portRange);
+ }
+ INC_ARGCV();
+
+ /*
+ * Extract public port and optionally address.
+ */
+ if (ac == 0)
+ errx (EX_DATAERR, "redirect_port: missing public port");
+
+ sep = strchr (*av, ':');
+ if (sep) {
+ if (StrToAddrAndPortRange (*av, &r->paddr, protoName,
+ &portRange) != 0)
+ errx(EX_DATAERR, "redirect_port:"
+ "invalid public port range");
+ } else {
+ r->paddr.s_addr = INADDR_ANY;
+ if (StrToPortRange (*av, protoName, &portRange) != 0)
+ errx(EX_DATAERR, "redirect_port:"
+ "invalid public port range");
+ }
+
+ r->pport = GETLOPORT(portRange);
+ r->pport_cnt = GETNUMPORTS(portRange);
+ INC_ARGCV();
+
+ /*
+ * Extract remote address and optionally port.
+ */
+ /*
+ * NB: isalpha(**av) => we've to check that next parameter is really an
+ * option for this redirect entry, else stop here processing arg[cv].
+ */
+ if (ac != 0 && !isalpha(**av)) {
+ sep = strchr (*av, ':');
+ if (sep) {
+ if (StrToAddrAndPortRange (*av, &r->raddr, protoName,
+ &portRange) != 0)
+ errx(EX_DATAERR, "redirect_port:"
+ "invalid remote port range");
+ } else {
+ SETLOPORT(portRange, 0);
+ SETNUMPORTS(portRange, 1);
+ StrToAddr (*av, &r->raddr);
+ }
+ INC_ARGCV();
+ } else {
+ SETLOPORT(portRange, 0);
+ SETNUMPORTS(portRange, 1);
+ r->raddr.s_addr = INADDR_ANY;
+ }
+ r->rport = GETLOPORT(portRange);
+ r->rport_cnt = GETNUMPORTS(portRange);
+
+ /*
+ * Make sure port ranges match up, then add the redirect ports.
+ */
+ if (numLocalPorts != r->pport_cnt)
+ errx(EX_DATAERR, "redirect_port:"
+ "port ranges must be equal in size");
+
+ /* Remote port range is allowed to be '0' which means all ports. */
+ if (r->rport_cnt != numLocalPorts &&
+ (r->rport_cnt != 1 || r->rport != 0))
+ errx(EX_DATAERR, "redirect_port: remote port must"
+ "be 0 or equal to local port range in size");
+
+ /*
+ * Setup LSNAT server pool.
+ */
+ if (lsnat) {
+ sep = strtok(tmp_spool_buf, ",");
+ while (sep != NULL) {
+ tmp = (struct cfg_spool *)spool_buf;
+ if (len < SOF_SPOOL)
+ goto nospace;
+ len -= SOF_SPOOL;
+ space += SOF_SPOOL;
+ if (StrToAddrAndPortRange(sep, &tmp->addr, protoName,
+ &portRange) != 0)
+ errx(EX_DATAERR, "redirect_port:"
+ "invalid local port range");
+ if (GETNUMPORTS(portRange) != 1)
+ errx(EX_DATAERR, "redirect_port: local port"
+ "must be single in this context");
+ tmp->port = GETLOPORT(portRange);
+ r->spool_cnt++;
+ /* Point to the next possible cfg_spool. */
+ spool_buf = &spool_buf[SOF_SPOOL];
+ sep = strtok(NULL, ",");
+ }
+ }
+ return (space);
+nospace:
+ errx(EX_DATAERR, "redirect_port: buf is too small\n");
+}
+
+static int
+setup_redir_proto(char *spool_buf, int len,
+ int *_ac, char ***_av)
+{
+ char **av;
+ int ac, space;
+ struct protoent *protoent;
+ struct cfg_redir *r;
+
+ av = *_av;
+ ac = *_ac;
+ if (len >= SOF_REDIR) {
+ r = (struct cfg_redir *)spool_buf;
+ /* Skip cfg_redir at beginning of buf. */
+ spool_buf = &spool_buf[SOF_REDIR];
+ space = SOF_REDIR;
+ len -= SOF_REDIR;
+ } else
+ goto nospace;
+ r->mode = REDIR_PROTO;
+ /*
+ * Extract protocol.
+ */
+ if (ac == 0)
+ errx(EX_DATAERR, "redirect_proto: missing protocol");
+
+ protoent = getprotobyname(*av);
+ if (protoent == NULL)
+ errx(EX_DATAERR, "redirect_proto: unknown protocol %s", *av);
+ else
+ r->proto = protoent->p_proto;
+
+ INC_ARGCV();
+
+ /*
+ * Extract local address.
+ */
+ if (ac == 0)
+ errx(EX_DATAERR, "redirect_proto: missing local address");
+ else
+ StrToAddr(*av, &r->laddr);
+
+ INC_ARGCV();
+
+ /*
+ * Extract optional public address.
+ */
+ if (ac == 0) {
+ r->paddr.s_addr = INADDR_ANY;
+ r->raddr.s_addr = INADDR_ANY;
+ } else {
+ /* see above in setup_redir_port() */
+ if (!isalpha(**av)) {
+ StrToAddr(*av, &r->paddr);
+ INC_ARGCV();
+
+ /*
+ * Extract optional remote address.
+ */
+ /* see above in setup_redir_port() */
+ if (ac!=0 && !isalpha(**av)) {
+ StrToAddr(*av, &r->raddr);
+ INC_ARGCV();
+ }
+ }
+ }
+ return (space);
+nospace:
+ errx(EX_DATAERR, "redirect_proto: buf is too small\n");
+}
+
+static void
+print_nat_config(unsigned char *buf)
+{
+ struct cfg_nat *n;
+ int i, cnt, flag, off;
+ struct cfg_redir *t;
+ struct cfg_spool *s;
+ struct protoent *p;
+
+ n = (struct cfg_nat *)buf;
+ flag = 1;
+ off = sizeof(*n);
+ printf("ipfw nat %u config", n->id);
+ if (strlen(n->if_name) != 0)
+ printf(" if %s", n->if_name);
+ else if (n->ip.s_addr != 0)
+ printf(" ip %s", inet_ntoa(n->ip));
+ while (n->mode != 0) {
+ if (n->mode & PKT_ALIAS_LOG) {
+ printf(" log");
+ n->mode &= ~PKT_ALIAS_LOG;
+ } else if (n->mode & PKT_ALIAS_DENY_INCOMING) {
+ printf(" deny_in");
+ n->mode &= ~PKT_ALIAS_DENY_INCOMING;
+ } else if (n->mode & PKT_ALIAS_SAME_PORTS) {
+ printf(" same_ports");
+ n->mode &= ~PKT_ALIAS_SAME_PORTS;
+ } else if (n->mode & PKT_ALIAS_UNREGISTERED_ONLY) {
+ printf(" unreg_only");
+ n->mode &= ~PKT_ALIAS_UNREGISTERED_ONLY;
+ } else if (n->mode & PKT_ALIAS_RESET_ON_ADDR_CHANGE) {
+ printf(" reset");
+ n->mode &= ~PKT_ALIAS_RESET_ON_ADDR_CHANGE;
+ } else if (n->mode & PKT_ALIAS_REVERSE) {
+ printf(" reverse");
+ n->mode &= ~PKT_ALIAS_REVERSE;
+ } else if (n->mode & PKT_ALIAS_PROXY_ONLY) {
+ printf(" proxy_only");
+ n->mode &= ~PKT_ALIAS_PROXY_ONLY;
+ }
+ }
+ /* Print all the redirect's data configuration. */
+ for (cnt = 0; cnt < n->redir_cnt; cnt++) {
+ t = (struct cfg_redir *)&buf[off];
+ off += SOF_REDIR;
+ switch (t->mode) {
+ case REDIR_ADDR:
+ printf(" redirect_addr");
+ if (t->spool_cnt == 0)
+ printf(" %s", inet_ntoa(t->laddr));
+ else
+ for (i = 0; i < t->spool_cnt; i++) {
+ s = (struct cfg_spool *)&buf[off];
+ if (i)
+ printf(",");
+ else
+ printf(" ");
+ printf("%s", inet_ntoa(s->addr));
+ off += SOF_SPOOL;
+ }
+ printf(" %s", inet_ntoa(t->paddr));
+ break;
+ case REDIR_PORT:
+ p = getprotobynumber(t->proto);
+ printf(" redirect_port %s ", p->p_name);
+ if (!t->spool_cnt) {
+ printf("%s:%u", inet_ntoa(t->laddr), t->lport);
+ if (t->pport_cnt > 1)
+ printf("-%u", t->lport +
+ t->pport_cnt - 1);
+ } else
+ for (i=0; i < t->spool_cnt; i++) {
+ s = (struct cfg_spool *)&buf[off];
+ if (i)
+ printf(",");
+ printf("%s:%u", inet_ntoa(s->addr),
+ s->port);
+ off += SOF_SPOOL;
+ }
+
+ printf(" ");
+ if (t->paddr.s_addr)
+ printf("%s:", inet_ntoa(t->paddr));
+ printf("%u", t->pport);
+ if (!t->spool_cnt && t->pport_cnt > 1)
+ printf("-%u", t->pport + t->pport_cnt - 1);
+
+ if (t->raddr.s_addr) {
+ printf(" %s", inet_ntoa(t->raddr));
+ if (t->rport) {
+ printf(":%u", t->rport);
+ if (!t->spool_cnt && t->rport_cnt > 1)
+ printf("-%u", t->rport +
+ t->rport_cnt - 1);
+ }
+ }
+ break;
+ case REDIR_PROTO:
+ p = getprotobynumber(t->proto);
+ printf(" redirect_proto %s %s", p->p_name,
+ inet_ntoa(t->laddr));
+ if (t->paddr.s_addr != 0) {
+ printf(" %s", inet_ntoa(t->paddr));
+ if (t->raddr.s_addr)
+ printf(" %s", inet_ntoa(t->raddr));
+ }
+ break;
+ default:
+ errx(EX_DATAERR, "unknown redir mode");
+ break;
+ }
+ }
+ printf("\n");
+}
+
+void
+ipfw_config_nat(int ac, char **av)
+{
+ struct cfg_nat *n; /* Nat instance configuration. */
+ int i, len, off, tok;
+ char *id, buf[NAT_BUF_LEN]; /* Buffer for serialized data. */
+
+ len = NAT_BUF_LEN;
+ /* Offset in buf: save space for n at the beginning. */
+ off = sizeof(*n);
+ memset(buf, 0, sizeof(buf));
+ n = (struct cfg_nat *)buf;
+
+ av++; ac--;
+ /* Nat id. */
+ if (ac && isdigit(**av)) {
+ id = *av;
+ i = atoi(*av);
+ ac--; av++;
+ n->id = i;
+ } else
+ errx(EX_DATAERR, "missing nat id");
+ if (ac == 0)
+ errx(EX_DATAERR, "missing option");
+
+ while (ac > 0) {
+ tok = match_token(nat_params, *av);
+ ac--; av++;
+ switch (tok) {
+ case TOK_IP:
+ if (ac == 0)
+ errx(EX_DATAERR, "missing option");
+ if (!inet_aton(av[0], &(n->ip)))
+ errx(EX_DATAERR, "bad ip address ``%s''",
+ av[0]);
+ ac--; av++;
+ break;
+ case TOK_IF:
+ if (ac == 0)
+ errx(EX_DATAERR, "missing option");
+ set_addr_dynamic(av[0], n);
+ ac--; av++;
+ break;
+ case TOK_ALOG:
+ n->mode |= PKT_ALIAS_LOG;
+ break;
+ case TOK_DENY_INC:
+ n->mode |= PKT_ALIAS_DENY_INCOMING;
+ break;
+ case TOK_SAME_PORTS:
+ n->mode |= PKT_ALIAS_SAME_PORTS;
+ break;
+ case TOK_UNREG_ONLY:
+ n->mode |= PKT_ALIAS_UNREGISTERED_ONLY;
+ break;
+ case TOK_RESET_ADDR:
+ n->mode |= PKT_ALIAS_RESET_ON_ADDR_CHANGE;
+ break;
+ case TOK_ALIAS_REV:
+ n->mode |= PKT_ALIAS_REVERSE;
+ break;
+ case TOK_PROXY_ONLY:
+ n->mode |= PKT_ALIAS_PROXY_ONLY;
+ break;
+ /*
+ * All the setup_redir_* functions work directly in the final
+ * buffer, see above for details.
+ */
+ case TOK_REDIR_ADDR:
+ case TOK_REDIR_PORT:
+ case TOK_REDIR_PROTO:
+ switch (tok) {
+ case TOK_REDIR_ADDR:
+ i = setup_redir_addr(&buf[off], len, &ac, &av);
+ break;
+ case TOK_REDIR_PORT:
+ i = setup_redir_port(&buf[off], len, &ac, &av);
+ break;
+ case TOK_REDIR_PROTO:
+ i = setup_redir_proto(&buf[off], len, &ac, &av);
+ break;
+ }
+ n->redir_cnt++;
+ off += i;
+ len -= i;
+ break;
+ default:
+ errx(EX_DATAERR, "unrecognised option ``%s''", av[-1]);
+ }
+ }
+
+ i = do_cmd(IP_FW_NAT_CFG, buf, off);
+ if (i)
+ err(1, "setsockopt(%s)", "IP_FW_NAT_CFG");
+
+ if (!co.do_quiet) {
+ /* After every modification, we show the resultant rule. */
+ int _ac = 3;
+ char *_av[] = {"show", "config", id};
+ ipfw_show_nat(_ac, _av);
+ }
+}
+
+
+void
+ipfw_show_nat(int ac, char **av)
+{
+ struct cfg_nat *n;
+ struct cfg_redir *e;
+ int cmd, i, nbytes, do_cfg, do_rule, frule, lrule, nalloc, size;
+ int nat_cnt, redir_cnt, r;
+ uint8_t *data, *p;
+ char *endptr;
+
+ do_rule = 0;
+ nalloc = 1024;
+ size = 0;
+ data = NULL;
+ frule = 0;
+ lrule = IPFW_DEFAULT_RULE; /* max ipfw rule number */
+ ac--; av++;
+
+ if (co.test_only)
+ return;
+
+ /* Parse parameters. */
+ for (cmd = IP_FW_NAT_GET_LOG, do_cfg = 0; ac != 0; ac--, av++) {
+ if (!strncmp(av[0], "config", strlen(av[0]))) {
+ cmd = IP_FW_NAT_GET_CONFIG, do_cfg = 1;
+ continue;
+ }
+ /* Convert command line rule #. */
+ frule = lrule = strtoul(av[0], &endptr, 10);
+ if (*endptr == '-')
+ lrule = strtoul(endptr+1, &endptr, 10);
+ if (lrule == 0)
+ err(EX_USAGE, "invalid rule number: %s", av[0]);
+ do_rule = 1;
+ }
+
+ nbytes = nalloc;
+ while (nbytes >= nalloc) {
+ nalloc = nalloc * 2;
+ nbytes = nalloc;
+ data = safe_realloc(data, nbytes);
+ if (do_cmd(cmd, data, (uintptr_t)&nbytes) < 0)
+ err(EX_OSERR, "getsockopt(IP_FW_GET_%s)",
+ (cmd == IP_FW_NAT_GET_LOG) ? "LOG" : "CONFIG");
+ }
+ if (nbytes == 0)
+ exit(0);
+ if (do_cfg) {
+ nat_cnt = *((int *)data);
+ for (i = sizeof(nat_cnt); nat_cnt; nat_cnt--) {
+ n = (struct cfg_nat *)&data[i];
+ if (frule <= n->id && lrule >= n->id)
+ print_nat_config(&data[i]);
+ i += sizeof(struct cfg_nat);
+ for (redir_cnt = 0; redir_cnt < n->redir_cnt; redir_cnt++) {
+ e = (struct cfg_redir *)&data[i];
+ i += sizeof(struct cfg_redir) + e->spool_cnt *
+ sizeof(struct cfg_spool);
+ }
+ }
+ } else {
+ for (i = 0; 1; i += LIBALIAS_BUF_SIZE + sizeof(int)) {
+ p = &data[i];
+ if (p == data + nbytes)
+ break;
+ bcopy(p, &r, sizeof(int));
+ if (do_rule) {
+ if (!(frule <= r && lrule >= r))
+ continue;
+ }
+ printf("nat %u: %s\n", r, p+sizeof(int));
+ }
+ }
+}
OpenPOWER on IntegriCloud