diff options
author | luigi <luigi@FreeBSD.org> | 2009-01-27 12:01:30 +0000 |
---|---|---|
committer | luigi <luigi@FreeBSD.org> | 2009-01-27 12:01:30 +0000 |
commit | 8a3b5c8587eefdf155a589610647bbc582948ce7 (patch) | |
tree | f06b5de4d78a1614f8a0927f17e29b5ec6459ad1 /sbin/ipfw | |
parent | 5153c1f1c42195d9130f83a12b751e798054a287 (diff) | |
download | FreeBSD-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
Diffstat (limited to 'sbin/ipfw')
-rw-r--r-- | sbin/ipfw/Makefile | 2 | ||||
-rw-r--r-- | sbin/ipfw/ipfw2.c | 1319 | ||||
-rw-r--r-- | sbin/ipfw/ipfw2.h | 29 | ||||
-rw-r--r-- | sbin/ipfw/ipv6.c | 501 | ||||
-rw-r--r-- | sbin/ipfw/nat.c | 905 |
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)); + } + } +} |