Date: Tue, 21 Dec 2004 11:36:50 +0100 From: Vladimir Kotal <vlada@devnull.cz> To: freebsd-ipfw@freebsd.org Subject: Re: ipfw2 for IPV6 Message-ID: <20041221103650.GC25908@otaku.xtrmntr.org>
next in thread | raw e-mail | index | archive | help
--7ZAtKRhVyVSsbBD2 Content-Type: text/plain; charset=us-ascii Content-Disposition: inline On Fri, Sep 03, 2004 at 02:51:37PM -0700, Brooks Davis wrote: > > I've included a patch against current below. Be aware that you must > run with debug.mpsafenet=0 if you want to try IPv6 output rules. The > current code doesn't handle the case where the firewall changes the > destination, but modulo bugs, we are probably at feature parity with > ip6fw. Hello, I'm attaching another variation of Luigi's/Mariano's patch. This is patch against 4.10-RELEASE and has following features/problems: - packet matching works in both directions in the right constellation - I've added logging code from ip6_fw.c - sbin/ipfw2.c parsing code can cause incorrect functionality, even segfault in some cases (see included regress script) v. --7ZAtKRhVyVSsbBD2 Content-Type: text/plain; charset=us-ascii Content-Disposition: attachment; filename="ipfw2-ipv6-dummynet.patch" diff -uNr --exclude=compile src.orig/sbin/ipfw/ipfw2.c src/sbin/ipfw/ipfw2.c --- src.orig/sbin/ipfw/ipfw2.c Sat Jan 31 20:16:46 2004 +++ src/sbin/ipfw/ipfw2.c Tue Dec 21 11:18:27 2004 @@ -53,6 +53,7 @@ #include <netinet/ip_dummynet.h> #include <netinet/tcp.h> #include <arpa/inet.h> +#include <netinet/icmp6.h> int do_resolv, /* Would try to resolve all */ @@ -244,6 +245,13 @@ TOK_DROPTAIL, TOK_PROTO, TOK_WEIGHT, + + TOK_IPV6, + TOK_FLOWID, + TOK_ICMP6TYPES, + TOK_EXT6HDR, + TOK_DSTIP6, + TOK_SRCIP6, }; struct _s_x dummynet_params[] = { @@ -266,6 +274,13 @@ { "delay", TOK_DELAY }, { "pipe", TOK_PIPE }, { "queue", TOK_QUEUE }, + + { "flow-id", TOK_FLOWID}, + { "dst-ipv6", TOK_DSTIP6}, + { "dst-ip6", TOK_DSTIP6}, + { "src-ipv6", TOK_SRCIP6}, + { "src-ip6", TOK_SRCIP6}, + { "dummynet-params", TOK_NULL }, { NULL, 0 } /* terminator */ }; @@ -340,6 +355,16 @@ { "ipsec", TOK_IPSEC }, { "//", TOK_COMMENT }, + { "icmp6type", TOK_ICMP6TYPES }, + { "icmp6types", TOK_ICMP6TYPES }, + { "ext6hdr", TOK_EXT6HDR}, + { "flow-id", TOK_FLOWID}, + { "ipv6", TOK_IPV6}, + { "dst-ipv6", TOK_DSTIP6}, + { "dst-ip6", TOK_DSTIP6}, + { "src-ipv6", TOK_SRCIP6}, + { "src-ip6", TOK_SRCIP6}, + { "not", TOK_NOT }, /* pseudo option */ { "!", /* escape ? */ TOK_NOT }, /* pseudo option */ { "or", TOK_OR }, /* pseudo option */ @@ -827,6 +852,197 @@ } } +/* XXX ipv6 stuff */ +/* + * 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(" ipv6"); + 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 && 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; + + cmd->d[0] = 0; + while (*av) { + if (*av == ',') + av++; + type = strtoul(av, &av, 0); + if (*av != ',' && *av != '\0') + errx(EX_DATAERR, "invalid ICMP6 type"); + 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(" ipv6 icmp6types"); + for (i = 0; i < IPFW2_ICMP6_MAXV; 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 }, + { "ah", EXT_AH }, + { "esp", EXT_ESP }, + { 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_AH: + cmd->arg1 |= EXT_AH; + break; + + case EXT_ESP: + cmd->arg1 |= EXT_ESP; + break; + + default: + errx( EX_DATAERR, "invalid option for ipv6 exten + headear" ); + 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_AH ) { + printf("%cauthentication header", sep); + sep = ','; + } + if (cmd->arg1 & EXT_ESP ) { + printf("%cencapsulated security payload", sep); + } +} + +/* XXX end of ipv6 stuff */ + /* * show_ipfw() prints the body of an ipfw rule. * Because the standard rule has at least proto src_ip dst_ip, we use @@ -845,6 +1061,7 @@ #define HAVE_DSTIP 0x0004 #define HAVE_MAC 0x0008 #define HAVE_MACTYPE 0x0010 +#define HAVE_PROTO6 0x0080 #define HAVE_OPTIONS 0x8000 #define HAVE_IP (HAVE_PROTO | HAVE_SRCIP | HAVE_DSTIP) @@ -865,6 +1082,8 @@ return; } if ( !(*flags & HAVE_OPTIONS)) { + if ( !(*flags & HAVE_PROTO) && (want & HAVE_PROTO6)) + printf(" ipv6"); if ( !(*flags & HAVE_PROTO) && (want & HAVE_PROTO)) printf(" ip"); if ( !(*flags & HAVE_SRCIP) && (want & HAVE_SRCIP)) @@ -1095,6 +1314,40 @@ flags |= HAVE_DSTIP; break; + case O_IP6_SRC: + case O_IP6_SRC_MASK: + case O_IP6_SRC_ME: + show_prerequisites(&flags, HAVE_PROTO6, 0); + if (!(flags & HAVE_SRCIP)) + printf(" from"); + if ((cmd->len & F_OR) && !or_block) + printf(" {"); + print_ip6((ipfw_insn_ip6 *)cmd, + (flags & HAVE_OPTIONS) ? " src-ip6" : ""); + flags |= HAVE_SRCIP | HAVE_PROTO; + break; + + case O_IP6_DST: + case O_IP6_DST_MASK: + case O_IP6_DST_ME: +#if 0 + show_prerequisites(&flags, HAVE_PROTO|HAVE_SRCIP, 0); +#endif + show_prerequisites(&flags, HAVE_PROTO6|HAVE_SRCIP, 0); + if (!(flags & HAVE_DSTIP)) + printf(" to"); + if ((cmd->len & F_OR) && !or_block) + printf(" {"); + print_ip6((ipfw_insn_ip6 *)cmd, + (flags & HAVE_OPTIONS) ? " dst-ip6" : ""); + flags |= HAVE_DSTIP | HAVE_PROTO; + break; + + case O_FLOW6ID: + print_flow6id( (ipfw_insn_u32 *) cmd ); + flags |= HAVE_OPTIONS; + break; + case O_IP_DSTPORT: show_prerequisites(&flags, HAVE_IP, 0); case O_IP_SRCPORT: @@ -1106,13 +1359,14 @@ break; case O_PROTO: { - struct protoent *pe; + struct protoent *pe = NULL; if ((cmd->len & F_OR) && !or_block) printf(" {"); if (cmd->len & F_NOT) printf(" not"); proto = cmd->arg1; + if (proto != 41) /* XXX ipv6 is special */ pe = getprotobynumber(cmd->arg1); if (flags & HAVE_OPTIONS) printf(" proto"); @@ -1289,6 +1543,18 @@ } break; + case O_IP6: + printf(" ipv6"); + break; + + case O_ICMP6TYPE: + print_icmp6types((ipfw_insn_u32 *)cmd); + break; + + case O_EXT_HDR: + print_ext6hdr( (ipfw_insn *) cmd ); + break; + default: printf(" [opcode %d len %d]", cmd->opcode, cmd->len); @@ -1342,11 +1608,20 @@ else printf(" proto %u", d->id.proto); + if (!IS_IP6_FLOW_ID(&(d->id))) { a.s_addr = htonl(d->id.src_ip); printf(" %s %d", inet_ntoa(a), d->id.src_port); - a.s_addr = htonl(d->id.dst_ip); printf(" <-> %s %d", inet_ntoa(a), d->id.dst_port); + } else { + char buff[255]; + inet_ntop(AF_INET6, &(d->id.src_ip6), + buff, sizeof(buff) ); + printf(" %s %d", buff, d->id.src_port); + inet_ntop(AF_INET6, &(d->id.dst_ip6), + buff, sizeof(buff) ); + printf(" <-> %s %d", buff, d->id.dst_port); + } printf("\n"); } @@ -1385,30 +1660,41 @@ static void list_queues(struct dn_flow_set *fs, struct dn_flow_queue *q) { - int l; + int l, index_print = 0; + char buff[255]; - printf(" mask: 0x%02x 0x%08x/0x%04x -> 0x%08x/0x%04x\n", - fs->flow_mask.proto, - fs->flow_mask.src_ip, fs->flow_mask.src_port, - fs->flow_mask.dst_ip, fs->flow_mask.dst_port); if (fs->rq_elements == 0) return; - printf("BKT Prot ___Source IP/port____ " - "____Dest. IP/port____ Tot_pkt/bytes Pkt/Byte Drp\n"); if (do_sort != 0) heapsort(q, fs->rq_elements, sizeof *q, sort_q); - for (l = 0; l < fs->rq_elements; l++) { + + /* + * Do IPv4 stuff + */ + + for (l = 0; l < fs->rq_elements; l++) + if (!IS_IP6_FLOW_ID(&(q[l].id))) { struct in_addr ina; struct protoent *pe; - ina.s_addr = htonl(q[l].id.src_ip); - printf("%3d ", q[l].hash_slot); + if (!index_print) { + index_print = 1; + printf("\n mask: 0x%02x 0x%08x/0x%04x -> 0x%08x/0x%04x\n", + fs->flow_mask.proto, + fs->flow_mask.src_ip, fs->flow_mask.src_port, + fs->flow_mask.dst_ip, fs->flow_mask.dst_port); + + printf(" BKT Prot ___Source IP/port____ " + "____Dest. IP/port____ Tot_pkt/bytes Pkt/Byte Drp\n"); + } + printf(" %3d ", q[l].hash_slot); pe = getprotobynumber(q[l].id.proto); if (pe) printf("%-4s ", pe->p_name); else printf("%4u ", q[l].id.proto); + ina.s_addr = htonl(q[l].id.src_ip); printf("%15s/%-5d ", inet_ntoa(ina), q[l].id.src_port); ina.s_addr = htonl(q[l].id.dst_ip); @@ -1421,6 +1707,54 @@ printf(" S %20qd F %20qd\n", q[l].S, q[l].F); } + + /* + * Do IPv6 stuff + */ + + index_print = 0; + for (l = 0; l < fs->rq_elements; l++) + if (IS_IP6_FLOW_ID(&(q[l].id))) { + struct protoent *pe; + + if (!index_print) { + index_print = 1; + printf("\n mask: proto: 0x%02x, flow_id: 0x%08x, ", + fs->flow_mask.proto, fs->flow_mask.flow_id6 ); + inet_ntop(AF_INET6, &(fs->flow_mask.src_ip6), + buff, sizeof(buff) ); + printf("%s/0x%04x -> ", buff, fs->flow_mask.src_port); + inet_ntop( AF_INET6, &(fs->flow_mask.dst_ip6), + buff, sizeof(buff) ); + printf("%s/0x%04x\n", buff, fs->flow_mask.dst_port); + + printf(" BKT ___Prot___ _flow-id_ " + "______________Source IPv6/port_______________ " + "_______________Dest. IPv6/port_______________ " + "Tot_pkt/bytes Pkt/Byte Drp\n"); + } + printf(" %3d ", q[l].hash_slot); + pe = getprotobynumber(q[l].id.proto); + if (pe) + printf("%9s ", pe->p_name); + else + printf("%9u ", q[l].id.proto); + printf("%7d %39s/%-5d ", q[l].id.flow_id6, + inet_ntop(AF_INET6, &(q[l].id.src_ip6), + buff, sizeof(buff)), + q[l].id.src_port); + printf(" %39s/%-5d ", + inet_ntop(AF_INET6, &(q[l].id.dst_ip6), + buff, sizeof(buff)), + q[l].id.dst_port); + printf(" %4qu %8qu %2u %4u %3u\n", + q[l].tot_pkts, q[l].tot_bytes, + q[l].len, q[l].len_bytes, q[l].drops); + if (verbose) + printf(" S %20qd F %20qd\n", + q[l].S, q[l].F); + } + printf("\n"); } static void @@ -1803,7 +2137,7 @@ if (do_dynamic && ndyn) { printf("## Dynamic rules:\n"); for (lac = ac, lav = av; lac != 0; lac--) { - rnum = strtoul(*lav++, &endptr, 10); + last = rnum = strtoul(*lav++, &endptr, 10); if (*endptr == '-') last = strtoul(endptr+1, &endptr, 10); if (*endptr) @@ -1855,17 +2189,22 @@ "ACTION: check-state | allow | count | deny | reject | skipto N |\n" " {divert|tee} PORT | forward ADDR | pipe N | queue N\n" "ADDR: [ MAC dst src ether_type ] \n" -" [ from IPADDR [ PORT ] to IPADDR [ PORTLIST ] ]\n" +" [ ip from IPADDR [ PORT ] to IPADDR [ PORTLIST ] ]\n" +" [ ipv6 from IP6ADDR [ PORT ] to IP6ADDR [ PORTLIST ] ]\n" "IPADDR: [not] { any | me | ip/bits{x,y,z} | IPLIST }\n" "IPLIST: { ip | ip/bits | ip:mask }[,IPLIST]\n" +"IP6ADDR: [not] { any | me | me6 | ip6/bits | IP6LIST }\n" +"IP6LIST: { ip6 | ip6/bits }[,IP6LIST]\n" "OPTION_LIST: OPTION [OPTION_LIST]\n" -"OPTION: bridged | {dst-ip|src-ip} ADDR | {dst-port|src-port} LIST |\n" +"OPTION: bridged | {dst-ip|src-ip} IPADDR | {dst-port|src-port} LIST |\n" " estab | frag | {gid|uid} N | icmptypes LIST | in | out | ipid LIST |\n" " iplen LIST | ipoptions SPEC | ipprecedence | ipsec | iptos SPEC |\n" " ipttl LIST | ipversion VER | keep-state | layer2 | limit ... |\n" " mac ... | mac-type LIST | proto LIST | {recv|xmit|via} {IF|IPADDR} |\n" " setup | {tcpack|tcpseq|tcpwin} NN | tcpflags SPEC | tcpoptions SPEC |\n" -" verrevpath\n" +" verrevpath | icmp6types LIST | ext6hdr LIST |\n" +" {dst-ip6|src-ip6|dst-ipv6|src-ipv6} IP6ADDR |\n" +" flow-id N[,N]\n" ); exit(0); } @@ -2059,6 +2398,226 @@ cmd->o.len |= len+1; } +/* XXX more ipv6 stuff */ +/* 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 */ + +static void +n2mask(struct in6_addr *mask, int n) +{ + static int minimask[9] = { + 0x00, 0x80, 0xc0, 0xe0, 0xf0, 0xf8, 0xfc, 0xfe, 0xff + }; + u_char *p; + int i; + + memset(mask, 0, sizeof(struct in6_addr)); + p = (u_char *) mask; + for (i = 0; i < 16; i++, p++, n -= 8) { + if (n >= 8) { + *p = 0xff; + continue; + } + *p = minimask[n]; + break; + } + return; +} + +/* + * fills 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 (!strncmp(av, "any", strlen(av))) + return 1; + + if (!strncmp(av, "me", strlen(av))) { /* Set the data for "me" opt*/ + cmd->o.len |= F_INSN_SIZE(ipfw_insn); + return 1; + } + if (!strncmp(av, "me6", strlen(av))) { /* 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) { + /* 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) + 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) { /* "not any" never matches */ + if (av == NULL && len == 0) /* only this entry */ + errx(EX_DATAERR, "not any never matches"); + } + /* else do nothing and skip this entry */ + /*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 lenght of the command, remember that 1 is the size of the base command */ + 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); + } +} + +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; +} +/* end ipv6 stuff */ /* * helper function to process a set of flags and set bits in the @@ -2182,7 +2741,6 @@ struct dn_pipe p; int i; char *end; - uint32_t a; void *par = NULL; memset(&p, 0, sizeof p); @@ -2244,16 +2802,15 @@ */ par = NULL; - p.fs.flow_mask.dst_ip = 0; - p.fs.flow_mask.src_ip = 0; - p.fs.flow_mask.dst_port = 0; - p.fs.flow_mask.src_port = 0; - p.fs.flow_mask.proto = 0; + bzero(&p.fs.flow_mask, sizeof(p.fs.flow_mask)); end = NULL; while (ac >= 1) { uint32_t *p32 = NULL; uint16_t *p16 = NULL; + uint32_t *p20 = NULL; + struct in6_addr *pa6 = NULL; + uint32_t a; /* the mask */ tok = match_token(dummynet_params, *av); ac--; av++; @@ -2267,6 +2824,9 @@ p.fs.flow_mask.dst_port = ~0; p.fs.flow_mask.src_port = ~0; p.fs.flow_mask.proto = ~0; + n2mask( &(p.fs.flow_mask.dst_ip6), 128); + n2mask( &(p.fs.flow_mask.src_ip6), 128); + p.fs.flow_mask.flow_id6 = ~0; p.fs.flags_fs |= DN_HAVE_FLOW_MASK; goto end_mask; @@ -2278,6 +2838,18 @@ p32 = &p.fs.flow_mask.src_ip; break; + case TOK_DSTIP6: + pa6 = &(p.fs.flow_mask.dst_ip6); + break; + + case TOK_SRCIP6: + pa6 = &(p.fs.flow_mask.src_ip6); + break; + + case TOK_FLOWID: + p20 = &p.fs.flow_mask.flow_id6; + break; + case TOK_DSTPORT: p16 = &p.fs.flow_mask.dst_port; break; @@ -2295,22 +2867,35 @@ } if (ac < 1) errx(EX_USAGE, "mask: value missing"); - if (*av[0] == '/') { + if (*av[0] == '/') { /* mask len */ a = strtoul(av[0]+1, &end, 0); + /* convert to a mask for non IPv6 */ + if (pa6 == NULL) a = (a == 32) ? ~0 : (1 << a) - 1; - } else + } else /* explicit mask (non IPv6) */ a = strtoul(av[0], &end, 0); if (p32 != NULL) *p32 = a; else if (p16 != NULL) { - if (a > 65535) + if (a > 0xffff) errx(EX_DATAERR, - "mask: must be 16 bit"); + "port mask must be 16 bit"); *p16 = (uint16_t)a; + } else if (p20 != NULL) { + if (a > 0xfffff) + errx(EX_DATAERR, + "flow_id mask must be 20 bit"); + *p20 = (uint32_t)a; + } else if (pa6 != NULL) { + if (a < 0 || a > 128) + errx(EX_DATAERR, + "in6addr invalid mask len" ); + else + n2mask(pa6, a); } else { - if (a > 255) + if (a > 0xff) errx(EX_DATAERR, - "mask: must be 8 bit"); + "proto mask must be 8 bit"); p.fs.flow_mask.proto = (uint8_t)a; } if (a != 0) @@ -2630,21 +3215,27 @@ } static ipfw_insn * -add_proto(ipfw_insn *cmd, char *av) +add_proto(ipfw_insn *cmd, char *av, u_char *proto) { struct protoent *pe; - u_char proto = 0; + *proto = IPPROTO_IP; if (!strncmp(av, "all", strlen(av))) ; /* same as "ip" */ - else if ((proto = atoi(av)) > 0) + else if ((*proto = atoi(av)) > 0) ; /* all done! */ else if ((pe = getprotobyname(av)) != NULL) - proto = pe->p_proto; + *proto = pe->p_proto; + else if(!strncmp(av, "ipv6", strlen(av)) || + !strncmp(av, "ip6", strlen(av)) ) + { + *proto = IPPROTO_IPV6; + return cmd; /* special case for ipv6 */ + } else return NULL; - if (proto != IPPROTO_IP) - fill_cmd(cmd, O_PROTO, 0, proto); + if (*proto != IPPROTO_IP && *proto != IPPROTO_IPV6) + fill_cmd(cmd, O_PROTO, 0, *proto); return cmd; } @@ -2691,6 +3282,36 @@ return NULL; } +static ipfw_insn * +add_src(ipfw_insn *cmd, char *av, u_char proto) +{ + if( proto == IPPROTO_IPV6 || strcmp( av, "me6") == 0 || (inet_addr( av ) == INADDR_NONE)) + return add_srcip6(cmd, av); + + if (proto == IPPROTO_IP || strcmp( av, "me") == 0 || (inet_addr(av) != INADDR_NONE) ) + return add_srcip(cmd, av); + + if( !strcmp( av, "any") ) + return cmd; + + return NULL; /* bad address */ +} + +static ipfw_insn * +add_dst(ipfw_insn *cmd, char *av, u_char proto) +{ + if( proto == IPPROTO_IPV6 || strcmp( av, "me6") == 0 || (inet_addr(av) == INADDR_NONE) ) + return add_dstip6(cmd, av); + + if (proto == IPPROTO_IP || strcmp( av, "me") == 0 || (inet_addr(av) != INADDR_NONE) ) + return add_dstip(cmd, av); + + if( !strcmp( av, "any") ) + return cmd; + + return NULL; /* bad address */ +} + /* * Parse arguments and assemble the microinstructions which make up a rule. * Rules are added into the 'rulebuf' and then copied in the correct order @@ -2714,7 +3335,7 @@ */ static uint32_t rulebuf[255], actbuf[255], cmdbuf[255]; - ipfw_insn *src, *dst, *cmd, *action, *prev=NULL; + ipfw_insn *src, *dst, *cmd, *action, *prev=NULL, *retval=NULL; ipfw_insn *first_cmd; /* first match pattern */ struct ip_fw *rule; @@ -2986,11 +3607,14 @@ OR_START(get_proto); NOT_BLOCK; NEED1("missing protocol"); - if (add_proto(cmd, *av)) { + if ( add_proto(cmd, *av, &proto) ) { +#if 0 + if ( proto == IPPROTO_IPV6 ) + fill_cmd(cmd, O_IP6, 0, 0); +#endif av++; ac--; - if (F_LEN(cmd) == 0) /* plain IP */ - proto = 0; - else { + if (F_LEN(cmd) != 0) /* plain IP */ + { proto = cmd->arg1; prev = cmd; cmd = next_cmd(cmd); @@ -3014,13 +3638,17 @@ OR_START(source_ip); NOT_BLOCK; /* optional "not" */ NEED1("missing source address"); - if (add_srcip(cmd, *av)) { + retval = add_src( cmd, *av, proto ); + + if( retval ){ ac--; av++; if (F_LEN(cmd) != 0) { /* ! any */ prev = cmd; cmd = next_cmd(cmd); } - } + } else + errx(EX_USAGE, "bad source address %s", *av); + OR_BLOCK(source_ip); /* @@ -3049,13 +3677,17 @@ OR_START(dest_ip); NOT_BLOCK; /* optional "not" */ NEED1("missing dst address"); - if (add_dstip(cmd, *av)) { + retval = NULL; + retval = add_dst(cmd, *av, proto); + + if( retval ){ ac--; av++; if (F_LEN(cmd) != 0) { /* ! any */ prev = cmd; cmd = next_cmd(cmd); } - } + } else + errx( EX_USAGE, "bad destination address %s", *av); OR_BLOCK(dest_ip); /* @@ -3161,6 +3793,12 @@ av++; ac--; break; + case TOK_ICMP6TYPES: + NEED1("icmptypes requires list of types"); + fill_icmp6types((ipfw_insn_icmp6 *)cmd, *av); + av++; ac--; + break; + case TOK_IPTTL: NEED1("ipttl requires TTL"); if (strpbrk(*av, "-,")) { @@ -3337,8 +3975,9 @@ case TOK_PROTO: NEED1("missing protocol"); - if (add_proto(cmd, *av)) { - proto = cmd->arg1; + if ( add_proto(cmd, *av, &proto)) { + if ( proto == IPPROTO_IPV6 ) + fill_cmd(cmd, O_IP6, 0, 0); ac--; av++; } else errx(EX_DATAERR, "invalid protocol ``%s''", @@ -3359,6 +3998,20 @@ } break; + case TOK_SRCIP6: + NEED1("missing source IP6"); + if (add_srcip6(cmd, *av)) { + ac--; av++; + } + break; + + case TOK_DSTIP6: + NEED1("missing destination IP6"); + if (add_dstip6(cmd, *av)) { + ac--; av++; + } + break; + case TOK_SRCPORT: NEED1("missing source port"); if (!strncmp(*av, "any", strlen(*av)) || @@ -3405,6 +4058,24 @@ fill_comment(cmd, ac, av); av += ac; ac = 0; + break; + + case TOK_IPV6: + fill_cmd(cmd, O_IP6, 0, 0); + /* XXX causes segfault */ + ac--; av++; + break; + + case TOK_EXT6HDR: + fill_ext6hdr( cmd, *av ); + ac--; av++; + break; + + case TOK_FLOWID: + if (proto != IPPROTO_IPV6 ) + errx( EX_USAGE, "flow-id filter is active only for ipv6 protocol\n"); + fill_flow6( (ipfw_insn_u32 *) cmd, *av ); + ac--;av++; break; default: diff -uNr --exclude=compile src.orig/sys/net/if_ethersubr.c src/sys/net/if_ethersubr.c --- src.orig/sys/net/if_ethersubr.c Wed Mar 3 13:35:16 2004 +++ src/sys/net/if_ethersubr.c Wed Dec 15 10:28:33 2004 @@ -40,6 +40,7 @@ #include "opt_ipx.h" #include "opt_bdg.h" #include "opt_netgraph.h" +#include "opt_ipfw.h" #include <sys/param.h> #include <sys/systm.h> diff -uNr --exclude=compile src.orig/sys/netinet/ip_dummynet.c src/sys/netinet/ip_dummynet.c --- src.orig/sys/netinet/ip_dummynet.c Tue Dec 30 13:28:09 2003 +++ src/sys/netinet/ip_dummynet.c Wed Dec 15 10:04:37 2004 @@ -85,6 +85,9 @@ #include <netinet/if_ether.h> /* for struct arpcom */ #include <net/bridge.h> +#include <netinet/ip6.h> /* for ip6_input, ip6_output prototypes */ +#include <netinet6/ip6_var.h> + /* * We keep a private variable for the simulation time, but we could * probably use an existing one ("softticks" in sys/kern/kern_timeout.c) @@ -435,6 +438,16 @@ ip_input((struct mbuf *)pkt) ; break ; + case DN_TO_IP6_IN: + ip6_input((struct mbuf *)pkt) ; + break ; + + case DN_TO_IP6_OUT: + (void)ip6_output((struct mbuf *)pkt, NULL, NULL, 0, + NULL, NULL, NULL); + rt_unref (pkt->ip6opt.ro_or.ro_rt) ; + break ; + case DN_TO_BDG_FWD : if (!BDG_LOADED) { /* somebody unloaded the bridge module. Drop pkt */ @@ -863,37 +876,80 @@ { int i = 0 ; /* we need i and q for new allocations */ struct dn_flow_queue *q, *prev; + int is_v6 = IS_IP6_FLOW_ID(id); if ( !(fs->flags_fs & DN_HAVE_FLOW_MASK) ) q = fs->rq[0] ; else { - /* first, do the masking */ - id->dst_ip &= fs->flow_mask.dst_ip ; - id->src_ip &= fs->flow_mask.src_ip ; + /* first, do the masking, then hash */ id->dst_port &= fs->flow_mask.dst_port ; id->src_port &= fs->flow_mask.src_port ; id->proto &= fs->flow_mask.proto ; id->flags = 0 ; /* we don't care about this one */ - /* then, hash function */ + if (is_v6) { + APPLY_MASK(&id->dst_ip6, &fs->flow_mask.dst_ip6); + APPLY_MASK(&id->src_ip6, &fs->flow_mask.src_ip6); + id->flow_id6 &= fs->flow_mask.flow_id6; + + i = ((id->dst_ip6.__u6_addr.__u6_addr32[0]) & 0xffff)^ + ((id->dst_ip6.__u6_addr.__u6_addr32[1]) & 0xffff)^ + ((id->dst_ip6.__u6_addr.__u6_addr32[2]) & 0xffff)^ + ((id->dst_ip6.__u6_addr.__u6_addr32[3]) & 0xffff)^ + + ((id->dst_ip6.__u6_addr.__u6_addr32[0] >> 15) & 0xffff)^ + ((id->dst_ip6.__u6_addr.__u6_addr32[1] >> 15) & 0xffff)^ + ((id->dst_ip6.__u6_addr.__u6_addr32[2] >> 15) & 0xffff)^ + ((id->dst_ip6.__u6_addr.__u6_addr32[3] >> 15) & 0xffff)^ + + ((id->src_ip6.__u6_addr.__u6_addr32[0] << 1) & 0xfffff)^ + ((id->src_ip6.__u6_addr.__u6_addr32[1] << 1) & 0xfffff)^ + ((id->src_ip6.__u6_addr.__u6_addr32[2] << 1) & 0xfffff)^ + ((id->src_ip6.__u6_addr.__u6_addr32[3] << 1) & 0xfffff)^ + + ((id->src_ip6.__u6_addr.__u6_addr32[0] << 16) & 0xffff)^ + ((id->src_ip6.__u6_addr.__u6_addr32[1] << 16) & 0xffff)^ + ((id->src_ip6.__u6_addr.__u6_addr32[2] << 16) & 0xffff)^ + ((id->src_ip6.__u6_addr.__u6_addr32[3] << 16) & 0xffff)^ + + (id->dst_port << 1) ^ (id->src_port) ^ + (id->proto ) ^ + (id->flow_id6); + } else { + id->dst_ip &= fs->flow_mask.dst_ip ; + id->src_ip &= fs->flow_mask.src_ip ; + i = ( (id->dst_ip) & 0xffff ) ^ ( (id->dst_ip >> 15) & 0xffff ) ^ ( (id->src_ip << 1) & 0xffff ) ^ ( (id->src_ip >> 16 ) & 0xffff ) ^ (id->dst_port << 1) ^ (id->src_port) ^ (id->proto ); + } i = i % fs->rq_size ; /* finally, scan the current list for a match */ searches++ ; for (prev=NULL, q = fs->rq[i] ; q ; ) { search_steps++; - if (id->dst_ip == q->id.dst_ip && + if (is_v6 && + IN6_ARE_ADDR_EQUAL(&id->dst_ip6,&q->id.dst_ip6) && + IN6_ARE_ADDR_EQUAL(&id->src_ip6,&q->id.src_ip6) && + id->dst_port == q->id.dst_port && + id->src_port == q->id.src_port && + id->proto == q->id.proto && + id->flags == q->id.flags && + id->flow_id6 == q->id.flow_id6) + break ; /* found */ + + if (!is_v6 && id->dst_ip == q->id.dst_ip && id->src_ip == q->id.src_ip && id->dst_port == q->id.dst_port && id->src_port == q->id.src_port && id->proto == q->id.proto && id->flags == q->id.flags) break ; /* found */ - else if (pipe_expire && q->head == NULL && q->S == q->F+1 ) { + + /* No match. Check if we can expire the entry */ + if (pipe_expire && q->head == NULL && q->S == q->F+1 ) { /* entry is idle and not in any heap, expire it */ struct dn_flow_queue *old_q = q ; @@ -1030,7 +1086,7 @@ { #if IPFW2 struct dn_flow_set *fs; - ipfw_insn *cmd = rule->cmd + rule->act_ofs; + ipfw_insn *cmd = ACTION_PTR(rule); if (cmd->opcode == O_LOG) cmd += F_LEN(cmd); @@ -1099,7 +1155,7 @@ int s = splimp(); int is_pipe; #if IPFW2 - ipfw_insn *cmd = fwa->rule->cmd + fwa->rule->act_ofs; + ipfw_insn *cmd = ACTION_PTR(fwa->rule); if (cmd->opcode == O_LOG) cmd += F_LEN(cmd); @@ -1177,6 +1233,15 @@ pkt->dn_dst = fwa->dst; pkt->flags = fwa->flags; + } else if (dir == DN_TO_IP6_OUT) { + pkt->ip6opt.ro_or = fwa->dummypar.ro_or; + pkt->ip6opt.flags_or = fwa->dummypar.flags_or; + pkt->ip6opt.origifp_or = fwa->dummypar.origifp_or; + pkt->ip6opt.ifp_or = fwa->dummypar.ifp_or; + pkt->ip6opt.dst_or = fwa->dummypar.dst_or; + if (fwa->dummypar.ro_or.ro_rt) + fwa->dummypar.ro_or.ro_rt->rt_refcnt++; + pkt->flags = fwa->flags; } if (q->head == NULL) q->head = pkt; @@ -1275,6 +1340,7 @@ */ #define DN_FREE_PKT(pkt) { \ struct dn_pkt *n = pkt ; \ + rt_unref ( n->ip6opt.ro_or.ro_rt ); /* XXX */ \ rt_unref ( n->ro.ro_rt ) ; \ m_freem(n->dn_m); \ pkt = DN_NEXT(n) ; \ @@ -1937,7 +2003,7 @@ static void ip_dn_init(void) { - printf("DUMMYNET initialized (011031)\n"); + printf("DUMMYNET with IPv6 initialized (040114)\n"); all_pipes = NULL ; all_flow_sets = NULL ; ready_heap.size = ready_heap.elements = 0 ; diff -uNr --exclude=compile src.orig/sys/netinet/ip_dummynet.h src/sys/netinet/ip_dummynet.h --- src.orig/sys/netinet/ip_dummynet.h Tue May 13 11:31:06 2003 +++ src/sys/netinet/ip_dummynet.h Wed Dec 15 10:04:37 2004 @@ -109,6 +109,7 @@ struct dn_heap_entry *p ; /* really an array of "size" entries */ } ; +#ifdef _KERNEL /* * struct dn_pkt identifies a packet in the dummynet queue, but * is also used to tag packets passed back to the various destinations @@ -135,13 +136,17 @@ #define DN_TO_BDG_FWD 3 #define DN_TO_ETH_DEMUX 4 #define DN_TO_ETH_OUT 5 +#define DN_TO_IP6_IN 6 +#define DN_TO_IP6_OUT 7 dn_key output_time; /* when the pkt is due for delivery */ struct ifnet *ifp; /* interface, for ip_output */ struct sockaddr_in *dn_dst ; struct route ro; /* route, for ip_output. MUST COPY */ int flags ; /* flags, for ip_output (IPv6 ?) */ + struct _ip6dn_args ip6opt; /* XXX ipv6 options */ }; +#endif /* _KERNEL */ /* * Overall structure of dummynet (with WF2Q+): diff -uNr --exclude=compile src.orig/sys/netinet/ip_fw2.c src/sys/netinet/ip_fw2.c --- src.orig/sys/netinet/ip_fw2.c Fri Apr 2 19:15:44 2004 +++ src/sys/netinet/ip_fw2.c Mon Dec 20 15:53:16 2004 @@ -77,6 +77,9 @@ #include <netinet6/ipsec.h> #endif +#include <netinet/ip6.h> +#include <netinet/icmp6.h> + #include <netinet/if_ether.h> /* XXX for ETHERTYPE_IP */ #include <machine/in_cksum.h> /* XXX for in_cksum */ @@ -235,14 +238,19 @@ ip_dn_ruledel_t *ip_dn_ruledel_ptr = NULL; /* hook into dummynet */ /* - * This macro maps an ip pointer into a layer3 header pointer of type T + * L3HDR maps an ipv4 pointer into a layer3 header pointer of type T + * Other macros just cast void * into the appropriate type */ #define L3HDR(T, ip) ((T *)((u_int32_t *)(ip) + (ip)->ip_hl)) +#define TCP(p) ((struct tcphdr *)(p)) +#define UDP(p) ((struct udphdr *)(p)) +#define ICMP(p) ((struct icmp *)(p)) +#define ICMP6(p) ((struct icmp6_hdr *)(p)) static __inline int -icmptype_match(struct ip *ip, ipfw_insn_u32 *cmd) +icmptype_match(struct icmp *icmp, ipfw_insn_u32 *cmd) { - int type = L3HDR(struct icmp,ip)->icmp_type; + int type = icmp->icmp_type; return (type <= ICMP_MAXTYPE && (cmd->d[0] & (1<<type)) ); } @@ -251,9 +259,10 @@ (1 << ICMP_TSTAMP) | (1 << ICMP_IREQ) | (1 << ICMP_MASKREQ) ) static int -is_icmp_query(struct ip *ip) +is_icmp_query(struct icmp *icmp) { - int type = L3HDR(struct icmp, ip)->icmp_type; + int type = icmp->icmp_type; + return (type <= ICMP_MAXTYPE && (TT & (1<<type)) ); } #undef TT @@ -329,10 +338,9 @@ } static int -tcpopts_match(struct ip *ip, ipfw_insn *cmd) +tcpopts_match(struct tcphdr *tcp, ipfw_insn *cmd) { int optlen, bits = 0; - struct tcphdr *tcp = L3HDR(struct tcphdr,ip); u_char *cp = (u_char *)(tcp + 1); int x = (tcp->th_off << 2) - sizeof(struct tcphdr); @@ -447,6 +455,83 @@ return 1; } +/* + * ipv6 specific rules here... + */ +static __inline int +icmp6type_match (int type, ipfw_insn_u32 *cmd) +{ + return (type <= ICMP6_MAXTYPE && (cmd->d[type/32] & (1<<(type%32)) ) ); +} + +static int +flow6id_match( int curr_flow, ipfw_insn_u32 *cmd ) +{ + int i; + for (i=0; i <= cmd->o.arg1; ++i ) + if (curr_flow == cmd->d[i] ) + return 1; + return 0; +} + +/* support for IP6_*_ME opcodes */ +static int +search_ip6_addr_net (struct in6_addr * ip6_addr) +{ + struct ifnet *mdc; + struct ifaddr *mdc2; + struct in6_ifaddr *fdm; + struct in6_addr copia; + + TAILQ_FOREACH(mdc, &ifnet, if_link) + for (mdc2 = mdc->if_addrlist.tqh_first; mdc2; + mdc2 = mdc2->ifa_list.tqe_next) { + if (!mdc2->ifa_addr) + continue; + if (mdc2->ifa_addr->sa_family == AF_INET6) { + fdm = (struct in6_ifaddr *)mdc2; + copia = fdm->ia_addr.sin6_addr; + /* need for leaving scope_id in the sock_addr */ + in6_clearscope(&copia); + if (IN6_ARE_ADDR_EQUAL(ip6_addr, &copia)) + return 1; + } + } + return 0; +} + +static int +verify_rev_path6(struct in6_addr *src, struct ifnet *ifp) +{ + static struct route_in6 ro; + struct sockaddr_in6 *dst; + + dst = (struct sockaddr_in6 * )&(ro.ro_dst); + + if ( !(IN6_ARE_ADDR_EQUAL (src, &dst->sin6_addr) )) { + bzero(dst, sizeof(*dst)); + dst->sin6_family = AF_INET6; + dst->sin6_len = sizeof(*dst); + dst->sin6_addr = *src; + rtalloc_ign((struct route *)&ro, RTF_CLONING | RTF_PRCLONING); + } + if ((ro.ro_rt == NULL) || (ifp == NULL) || + (ro.ro_rt->rt_ifp->if_index != ifp->if_index)) + return 0; + return 1; +} +static __inline int +hash_packet6(struct ipfw_flow_id *id) +{ + u_int32_t i; + i= (id->dst_ip6.__u6_addr.__u6_addr32[0]) ^ + (id->dst_ip6.__u6_addr.__u6_addr32[1]) ^ + (id->dst_ip6.__u6_addr.__u6_addr32[2]) ^ + (id->dst_ip6.__u6_addr.__u6_addr32[3]) ^ + (id->dst_port) ^ (id->src_port) ^ (id->flow_id6); + return i; +} +/* end of ipv6 opcodes */ static u_int64_t norule_counter; /* counter for ipfw_log(NULL...) */ @@ -463,7 +548,7 @@ { char *action; int limit_reached = 0; - char action2[40], proto[48], fragment[28]; + char action2[40], proto[102], fragment[28]; fragment[0] = '\0'; proto[0] = '\0'; @@ -547,9 +632,70 @@ } } - if (hlen == 0) { /* non-ip */ - snprintf(SNPARGS(proto, 0), "MAC"); + /* preserve hlen == 0 semantics */ + if ((hlen == 0) && + mtod(m, struct ip *)->ip_v == 6) { /* IPv6 packet */ + int len; + struct ip6_hdr *ip6 = mtod(m, struct ip6_hdr *); + u_int8_t protonum = mtod(m, struct ip6_hdr *)->ip6_nxt; + int off = sizeof(struct ip6_hdr); + struct tcphdr *const tcp6 = (struct tcphdr *) ((caddr_t) ip6+ off); + struct udphdr *const udp = (struct udphdr *) ((caddr_t) ip6+ off); + struct icmp6_hdr *const icmp6 = (struct icmp6_hdr *) ((caddr_t) ip6+ off); + +#define SNPARGS(buf, len) buf + len, sizeof(buf) > len ? sizeof(buf) - len : 0 + + switch (protonum) { + case IPPROTO_TCP: + len = snprintf(SNPARGS(proto, 0), "TCP [%s]", + ip6_sprintf(&ip6->ip6_src)); + if (off > 0) + len += snprintf(SNPARGS(proto, len), ":%d ", + ntohs(tcp6->th_sport)); + else + len += snprintf(SNPARGS(proto, len), " "); + len += snprintf(SNPARGS(proto, len), "[%s]", + ip6_sprintf(&ip6->ip6_dst)); + if (off > 0) + snprintf(SNPARGS(proto, len), ":%d", + ntohs(tcp6->th_dport)); + break; + case IPPROTO_UDP: + len = snprintf(SNPARGS(proto, 0), "UDP [%s]", + ip6_sprintf(&ip6->ip6_src)); + if (off > 0) + len += snprintf(SNPARGS(proto, len), ":%d ", + ntohs(udp->uh_sport)); + else + len += snprintf(SNPARGS(proto, len), " "); + len += snprintf(SNPARGS(proto, len), "[%s]", + ip6_sprintf(&ip6->ip6_dst)); + if (off > 0) + snprintf(SNPARGS(proto, len), ":%d", + ntohs(udp->uh_dport)); + break; + case IPPROTO_ICMPV6: + if (off > 0) + len = snprintf(SNPARGS(proto, 0), "IPV6-ICMP:%u.%u ", + icmp6->icmp6_type, icmp6->icmp6_code); + else + len = snprintf(SNPARGS(proto, 0), "IPV6-ICMP "); + len += snprintf(SNPARGS(proto, len), "[%s]", + ip6_sprintf(&ip6->ip6_src)); + snprintf(SNPARGS(proto, len), " [%s]", + ip6_sprintf(&ip6->ip6_dst)); + break; + default: + len = snprintf(SNPARGS(proto, 0), "P:%d [%s]", protonum, + ip6_sprintf(&ip6->ip6_src)); + snprintf(SNPARGS(proto, len), " [%s]", + ip6_sprintf(&ip6->ip6_dst)); + break; + } } else { + if (hlen == 0) + snprintf(SNPARGS(proto, 0), "MAC"); + else { struct ip *ip = mtod(m, struct ip *); /* these three are all aliases to the same thing */ struct icmp *const icmp = L3HDR(struct icmp, ip); @@ -621,6 +767,7 @@ ntohs(ip->ip_id), ip_len - (ip->ip_hl << 2), offset << 3, (ip_off & IP_MF) ? "+" : ""); + } } if (oif || m->m_pkthdr.rcvif) log(LOG_SECURITY | LOG_INFO, @@ -651,7 +798,9 @@ { u_int32_t i; - i = (id->dst_ip) ^ (id->src_ip) ^ (id->dst_port) ^ (id->src_port); + i = IS_IP6_FLOW_ID(id) ? hash_packet6(id): + (id->dst_ip) ^ (id->src_ip) ^ (id->dst_port) ^ (id->src_port); + i &= (curr_dyn_buckets - 1); return i; } @@ -776,7 +925,7 @@ if (ipfw_dyn_v == NULL) goto done; /* not found */ - i = hash_packet( pkt ); + i = hash_packet(pkt); for (prev=NULL, q = ipfw_dyn_v[i] ; q != NULL ; ) { if (q->dyn_type == O_LIMIT_PARENT && q->count) goto next; @@ -786,6 +935,27 @@ } if (pkt->proto == q->id.proto && q->dyn_type != O_LIMIT_PARENT) { + if (IS_IP6_FLOW_ID(pkt)) { + if (IN6_ARE_ADDR_EQUAL(&(pkt->src_ip6), + &(q->id.src_ip6)) && + IN6_ARE_ADDR_EQUAL(&(pkt->dst_ip6), + &(q->id.dst_ip6)) && + pkt->src_port == q->id.src_port && + pkt->dst_port == q->id.dst_port ) { + dir = MATCH_FORWARD; + break; + } + if (IN6_ARE_ADDR_EQUAL(&(pkt->src_ip6), + &(q->id.dst_ip6)) && + IN6_ARE_ADDR_EQUAL(&(pkt->dst_ip6), + &(q->id.src_ip6)) && + pkt->src_port == q->id.dst_port && + pkt->dst_port == q->id.src_port ) { + dir = MATCH_REVERSE; + break; + } + + } else { if (pkt->src_ip == q->id.src_ip && pkt->dst_ip == q->id.dst_ip && pkt->src_port == q->id.src_port && @@ -801,6 +971,7 @@ break; } } + } next: prev = q; q = q->next; @@ -976,15 +1147,25 @@ int i; if (ipfw_dyn_v) { - i = hash_packet( pkt ); + int is_v6 = IS_IP6_FLOW_ID(pkt); + i = hash_packet(pkt); for (q = ipfw_dyn_v[i] ; q != NULL ; q=q->next) if (q->dyn_type == O_LIMIT_PARENT && rule== q->rule && pkt->proto == q->id.proto && - pkt->src_ip == q->id.src_ip && - pkt->dst_ip == q->id.dst_ip && pkt->src_port == q->id.src_port && - pkt->dst_port == q->id.dst_port) { + pkt->dst_port == q->id.dst_port && + ( + (is_v6 && + IN6_ARE_ADDR_EQUAL(&(pkt->src_ip6), + &(q->id.src_ip6)) && + IN6_ARE_ADDR_EQUAL(&(pkt->dst_ip6), + &(q->id.dst_ip6))) || + (!is_v6 && + pkt->src_ip == q->id.src_ip && + pkt->dst_ip == q->id.dst_ip) + ) + ) { q->expire = time_second + dyn_short_lifetime; DEB(printf("ipfw: lookup_dyn_parent found 0x%p\n",q);) return q; @@ -1050,14 +1231,21 @@ DEB(printf("ipfw: installing dyn-limit rule %d\n", cmd->conn_limit);) - id.dst_ip = id.src_ip = 0; - id.dst_port = id.src_port = 0; + bzero (&id, sizeof(id)); + id.proto = args->f_id.proto; + if (IS_IP6_FLOW_ID (&(args->f_id))) { + if (limit_mask & DYN_SRC_ADDR) + id.src_ip6 = args->f_id.src_ip6; + if (limit_mask & DYN_DST_ADDR) + id.dst_ip6 = args->f_id.dst_ip6; + } else { if (limit_mask & DYN_SRC_ADDR) id.src_ip = args->f_id.src_ip; if (limit_mask & DYN_DST_ADDR) id.dst_ip = args->f_id.dst_ip; + } if (limit_mask & DYN_SRC_PORT) id.src_port = args->f_id.src_port; if (limit_mask & DYN_DST_PORT) @@ -1297,12 +1485,8 @@ * consumes the packet because it calls send_reject(). * XXX This has to change, so that ipfw_chk() never modifies * or consumes the buffer. - * ip is simply an alias of the value of m, and it is kept - * in sync with it (the packet is supposed to start with - * the ip header). */ struct mbuf *m = args->m; - struct ip *ip = mtod(m, struct ip *); /* * oif | args->oif If NULL, ipfw_chk has been called on the @@ -1319,12 +1503,12 @@ * hlen The length of the IPv4 header. * hlen >0 means we have an IPv4 packet. */ - u_int hlen = 0; /* hlen >0 means we have an IP pkt */ + u_int hlen = 0; /* * offset The offset of a fragment. offset != 0 means that - * we have a fragment at this offset of an IPv4 packet. - * offset == 0 means that (if this is an IPv4 packet) + * we have a fragmented ip packet. + * offset == 0 means that (if this is an IP packet) * this is the first or only fragment. */ u_short offset = 0; @@ -1348,32 +1532,151 @@ struct in_addr src_ip, dst_ip; /* NOTE: network format */ u_int16_t ip_len=0; int pktlen; - int dyn_dir = MATCH_UNKNOWN; - ipfw_dyn_rule *q = NULL; - if (m->m_flags & M_SKIP_FIREWALL) - return 0; /* accept */ /* * dyn_dir = MATCH_UNKNOWN when rules unchecked, * MATCH_NONE when checked and not matched (q = NULL), * MATCH_FORWARD or MATCH_REVERSE otherwise (q != NULL) */ + int dyn_dir = MATCH_UNKNOWN; + ipfw_dyn_rule *q = NULL; + + /* + * We store in ulp a pointer to the upper layer protocol header. + * In the ipv4 case this is easy to determine from the header, + * but for ipv6 we might have some additional headers in the middle. + * ulp is NULL if not found. + */ + void *ulp = NULL; /* upper layer protocol pointer. */ + /* XXX ipv6 variables */ + int is_ipv6 = 0; + u_int16_t ext_hd = 0; /* bits vector for extension header filtering */ + /* end of ipv6 variables */ + + if (m->m_flags & M_SKIP_FIREWALL) + return 0; /* accept */ pktlen = m->m_pkthdr.len; - if (args->eh == NULL || /* layer 3 packet */ - ( m->m_pkthdr.len >= sizeof(struct ip) && - ntohs(args->eh->ether_type) == ETHERTYPE_IP)) - hlen = ip->ip_hl << 2; + proto = args->f_id.proto = 0; /* mark f_id invalid */ + + /* Identify ipv6 packets and fill up variables. */ + if (pktlen >= sizeof(struct ip6_hdr) && + (!args->eh || ntohs(args->eh->ether_type)==ETHERTYPE_IPV6) && + mtod(m, struct ip *)->ip_v == 6) { + + is_ipv6 = 1; + args->f_id.addr_type = 6; + hlen = sizeof(struct ip6_hdr); + proto = mtod(m, struct ip6_hdr *)->ip6_nxt; + args->f_id.src_ip6 = (mtod(m, struct ip6_hdr *))->ip6_src; + args->f_id.dst_ip6 = (mtod(m, struct ip6_hdr *))->ip6_dst; + args->f_id.src_ip = 0; + args->f_id.dst_ip = 0; + args->f_id.flow_id6 = ntohs(mtod(m, struct ip6_hdr *)->ip6_flow); + + /* XXX where do we find ip_len ??? how do we set pktlen ? */ + + /* + * PULLUP6(len, p, T) makes sure that len + sizeof(T) is + * contiguous, then it sets p to point at the offset "len" in + * the mbuf. WARNING: the pointer might become stale after + * other pullups (but we never use it this way). + */ +#define PULLUP6(len, p, T) \ + do { \ + int x = (len) + sizeof(T); \ + if ((m)->m_len < x) { \ + args->m = m = m_pullup(m, x); \ + if (m == 0) \ + goto pullup_failed; \ + } \ + p = (mtod(m, char *) + (len)); \ + } while (0) + + /* Search extension headers to find upper layer protocols */ + while (ulp == NULL) { + switch (proto) { + case IPPROTO_ICMPV6: + PULLUP6(hlen, ulp, struct icmp6_hdr); + args->f_id.flags = ICMP6(ulp)->icmp6_type; + break; + + case IPPROTO_TCP: + PULLUP6(hlen, ulp, struct tcphdr); + dst_port = TCP(ulp)->th_dport; + src_port = TCP(ulp)->th_sport; + args->f_id.flags = TCP(ulp)->th_flags; + break; + + case IPPROTO_UDP: + PULLUP6(hlen, ulp, struct udphdr); + dst_port = UDP(ulp)->uh_dport; + src_port = UDP(ulp)->uh_sport; + break; + + case IPPROTO_HOPOPTS: + PULLUP6(hlen, ulp, struct ip6_hbh); + ext_hd |= EXT_HOPOPTS; + hlen += sizeof(struct ip6_hbh); + proto = ((struct ip6_hbh *)ulp)->ip6h_nxt; + ulp = NULL; + break; + + case IPPROTO_ROUTING: + PULLUP6(hlen, ulp, struct ip6_rthdr); + ext_hd |= EXT_ROUTING; + hlen += sizeof(struct ip6_rthdr); + proto = ((struct ip6_rthdr *)ulp)->ip6r_nxt; + ulp = NULL; + break; + + case IPPROTO_FRAGMENT: + PULLUP6(hlen, ulp, struct ip6_frag); + ext_hd |= EXT_FRAGMENT; + hlen += sizeof (struct ip6_frag); + proto = ((struct ip6_frag *)ulp)->ip6f_nxt; + offset = 1; + ulp = NULL; /* XXX is it correct ? */ + break; + + case IPPROTO_AH: + case IPPROTO_NONE: + case IPPROTO_ESP: + PULLUP6(hlen, ulp, struct ip6_ext); + if (proto == IPPROTO_AH) + ext_hd |= EXT_AH; + else if (proto == IPPROTO_ESP) + ext_hd |= EXT_ESP; + hlen += ((struct ip6_ext *)ulp)->ip6e_len + + sizeof (struct ip6_ext); + proto = ((struct ip6_ext *)ulp)->ip6e_nxt; + ulp = NULL; + break; + + default: + printf("IPFW2: IPV6 - Unknown Extension Header (%d)\n", + proto); + return 0; /* deny */ + break; + } /*switch */ + } + + /* hlen != 0 is used to detect ipv4 packets, so clear it now */ + hlen = 0; /* XXX why? we have args->f_id.addr_type ... */ + + } else if (pktlen >= sizeof(struct ip) && + (!args->eh || ntohs(args->eh->ether_type) == ETHERTYPE_IP) && + mtod(m, struct ip *)->ip_v == 4) { + struct ip *ip = mtod(m, struct ip *); + + hlen = ip->ip_hl << 2; + args->f_id.addr_type = 4; /* * Collect parameters into local variables for faster matching. */ - if (hlen == 0) { /* do not grab addresses for non-ip pkts */ - proto = args->f_id.proto = 0; /* mark f_id invalid */ - goto after_ip_checks; - } - proto = args->f_id.proto = ip->ip_p; + proto = ip->ip_p; src_ip = ip->ip_src; dst_ip = ip->ip_dst; if (args->eh != NULL) { /* layer 2 packets are as on the wire */ @@ -1385,58 +1688,41 @@ } pktlen = ip_len < pktlen ? ip_len : pktlen; -#define PULLUP_TO(len) \ - do { \ - if ((m)->m_len < (len)) { \ - args->m = m = m_pullup(m, (len)); \ - if (m == 0) \ - goto pullup_failed; \ - ip = mtod(m, struct ip *); \ - } \ - } while (0) - if (offset == 0) { switch (proto) { case IPPROTO_TCP: - { - struct tcphdr *tcp; - - PULLUP_TO(hlen + sizeof(struct tcphdr)); - tcp = L3HDR(struct tcphdr, ip); - dst_port = tcp->th_dport; - src_port = tcp->th_sport; - args->f_id.flags = tcp->th_flags; - } + PULLUP6(hlen, ulp, struct tcphdr); + dst_port = TCP(ulp)->th_dport; + src_port = TCP(ulp)->th_sport; + args->f_id.flags = TCP(ulp)->th_flags; break; case IPPROTO_UDP: - { - struct udphdr *udp; - - PULLUP_TO(hlen + sizeof(struct udphdr)); - udp = L3HDR(struct udphdr, ip); - dst_port = udp->uh_dport; - src_port = udp->uh_sport; - } + PULLUP6(hlen, ulp, struct udphdr); + dst_port = UDP(ulp)->uh_dport; + src_port = UDP(ulp)->uh_sport; break; case IPPROTO_ICMP: - PULLUP_TO(hlen + 4); /* type, code and checksum. */ - args->f_id.flags = L3HDR(struct icmp, ip)->icmp_type; + /* we only care for 4 bytes: type, code, checksum */ + PULLUP6(hlen, ulp, struct icmp); + args->f_id.flags = ICMP(ulp)->icmp_type; break; default: break; } -#undef PULLUP_TO } args->f_id.src_ip = ntohl(src_ip.s_addr); args->f_id.dst_ip = ntohl(dst_ip.s_addr); + } + if (proto) { /* we may have port numbers, store them */ + args->f_id.proto = proto; args->f_id.src_port = src_port = ntohs(src_port); args->f_id.dst_port = dst_port = ntohs(dst_port); + } -after_ip_checks: if (args->rule) { /* * Packet has already been tagged. Look for the next rule @@ -1529,13 +1815,11 @@ case O_GID: case O_UID: - /* - * We only check offset == 0 && proto != 0, - * as this ensures that we have an IPv4 - * packet with the ports info. - */ - if (offset!=0) + if (offset != 0) /* no port info available */ break; + if (is_ipv6) /* XXX to be fixed later */ + break; + /* the check for proto is below */ { struct inpcbinfo *pi; int wildcard; @@ -1621,7 +1905,7 @@ break; case O_FRAG: - match = (hlen > 0 && offset != 0); + match = offset != 0; break; case O_IN: /* "out" is "not in" */ @@ -1706,7 +1990,7 @@ case O_IP_DSTPORT: /* * offset == 0 && proto != 0 is enough - * to guarantee that we have an IPv4 + * to guarantee that we have a * packet with port info. */ if ((proto==IPPROTO_UDP || proto==IPPROTO_TCP) @@ -1726,15 +2010,25 @@ case O_ICMPTYPE: match = (offset == 0 && proto==IPPROTO_ICMP && - icmptype_match(ip, (ipfw_insn_u32 *)cmd) ); + icmptype_match(ICMP(ulp), (ipfw_insn_u32 *)cmd) ); + break; + + case O_ICMP6TYPE: + match = is_ipv6 && offset == 0 && + proto==IPPROTO_ICMPV6 && + icmp6type_match( + ((struct icmp6_hdr *)ulp)->icmp6_type, + (ipfw_insn_u32 *)cmd); break; case O_IPOPT: - match = (hlen > 0 && ipopts_match(ip, cmd) ); + match = (hlen > 0 && + ipopts_match(mtod(m, struct ip *), cmd) ); break; case O_IPVER: - match = (hlen > 0 && cmd->arg1 == ip->ip_v); + match = (hlen > 0 && + cmd->arg1 == mtod(m, struct ip *)->ip_v); break; case O_IPID: @@ -1748,9 +2042,9 @@ if (cmd->opcode == O_IPLEN) x = ip_len; else if (cmd->opcode == O_IPTTL) - x = ip->ip_ttl; + x = mtod(m, struct ip *)->ip_ttl; else /* must be IPID */ - x = ntohs(ip->ip_id); + x = ntohs(mtod(m, struct ip *)->ip_id); if (cmdlen == 1) { match = (cmd->arg1 == x); break; @@ -1765,49 +2059,47 @@ case O_IPPRECEDENCE: match = (hlen > 0 && - (cmd->arg1 == (ip->ip_tos & 0xe0)) ); + (cmd->arg1 == (mtod(m, struct ip *)->ip_tos & 0xe0)) ); break; case O_IPTOS: match = (hlen > 0 && - flags_match(cmd, ip->ip_tos)); + flags_match(cmd, mtod(m, struct ip *)->ip_tos)); break; case O_TCPFLAGS: - match = (proto == IPPROTO_TCP && offset == 0 && - flags_match(cmd, - L3HDR(struct tcphdr,ip)->th_flags)); + match = proto == IPPROTO_TCP && offset == 0 && + flags_match(cmd, TCP(ulp)->th_flags); break; case O_TCPOPTS: - match = (proto == IPPROTO_TCP && offset == 0 && - tcpopts_match(ip, cmd)); + match = proto == IPPROTO_TCP && offset == 0 && + tcpopts_match(TCP(ulp), cmd); break; case O_TCPSEQ: - match = (proto == IPPROTO_TCP && offset == 0 && + match = proto == IPPROTO_TCP && offset == 0 && ((ipfw_insn_u32 *)cmd)->d[0] == - L3HDR(struct tcphdr,ip)->th_seq); + TCP(ulp)->th_seq; break; case O_TCPACK: - match = (proto == IPPROTO_TCP && offset == 0 && + match = proto == IPPROTO_TCP && offset == 0 && ((ipfw_insn_u32 *)cmd)->d[0] == - L3HDR(struct tcphdr,ip)->th_ack); + TCP(ulp)->th_ack; break; case O_TCPWIN: - match = (proto == IPPROTO_TCP && offset == 0 && - cmd->arg1 == - L3HDR(struct tcphdr,ip)->th_win); + match = proto == IPPROTO_TCP && offset == 0 && + cmd->arg1 == TCP(ulp)->th_win; break; case O_ESTAB: /* reject packets which have SYN only */ /* XXX should i also check for TH_ACK ? */ - match = (proto == IPPROTO_TCP && offset == 0 && - (L3HDR(struct tcphdr,ip)->th_flags & - (TH_RST | TH_ACK | TH_SYN)) != TH_SYN); + match = proto == IPPROTO_TCP && offset == 0 && + ( TCP(ulp)->th_flags & + (TH_RST | TH_ACK | TH_SYN)) != TH_SYN; break; case O_LOG: @@ -1822,8 +2114,11 @@ case O_VERREVPATH: /* Outgoing packets automatically pass/match */ - match = ((oif != NULL) || + match = (oif != NULL) || (m->m_pkthdr.rcvif == NULL) || + (is_ipv6 ? + verify_rev_path6(&(args->f_id.src_ip6), + m->m_pkthdr.rcvif) : verify_rev_path(src_ip, m->m_pkthdr.rcvif)); break; @@ -1838,6 +2133,62 @@ /* otherwise no match */ break; + case O_IP6_SRC: + + match = is_ipv6 && + IN6_ARE_ADDR_EQUAL(&args->f_id.src_ip6, + &((ipfw_insn_ip6 *)cmd)->addr6); + break; + + case O_IP6_DST: + match = is_ipv6 && + IN6_ARE_ADDR_EQUAL(&args->f_id.dst_ip6, + &((ipfw_insn_ip6 *)cmd)->addr6); + break; + + case O_IP6_SRC_MASK: + if (is_ipv6) { + ipfw_insn_ip6 *te = (ipfw_insn_ip6 *)cmd; + struct in6_addr p = args->f_id.src_ip6; + + APPLY_MASK(&p, &te->mask6); + match = IN6_ARE_ADDR_EQUAL(&te->addr6, &p); + } + break; + + case O_IP6_DST_MASK: + if (is_ipv6) { + ipfw_insn_ip6 *te = (ipfw_insn_ip6 *)cmd; + struct in6_addr p = args->f_id.dst_ip6; + + APPLY_MASK(&p, &te->mask6); + match = IN6_ARE_ADDR_EQUAL(&te->addr6, &p); + } + break; + + case O_IP6_SRC_ME: + match= is_ipv6 && search_ip6_addr_net(&args->f_id.src_ip6); + break; + + case O_IP6_DST_ME: + match= is_ipv6 && search_ip6_addr_net(&args->f_id.dst_ip6); + break; + + case O_FLOW6ID: + match = is_ipv6 && + flow6id_match(args->f_id.flow_id6, + (ipfw_insn_u32 *) cmd); + break; + + case O_EXT_HDR: + match = is_ipv6 && + (ext_hd & ((ipfw_insn *) cmd)->arg1); + break; + + case O_IP6: + match = is_ipv6; + break; + /* * The second set of opcodes represents 'actions', * i.e. the terminal part of a rule once the packet @@ -1900,7 +2251,7 @@ if (dyn_dir == MATCH_UNKNOWN && (q = lookup_dyn_rule(&args->f_id, &dyn_dir, proto == IPPROTO_TCP ? - L3HDR(struct tcphdr, ip) : NULL)) + TCP(ulp) : NULL)) != NULL) { /* * Found dynamic entry, update stats @@ -1965,7 +2316,7 @@ */ if (hlen > 0 && (proto != IPPROTO_ICMP || - is_icmp_query(ip)) && + is_icmp_query(ICMP(ulp))) && !(m->m_flags & (M_BCAST|M_MCAST)) && !IN_MULTICAST(ntohl(dst_ip.s_addr))) { send_reject(args, cmd->arg1, @@ -2412,6 +2763,10 @@ case O_ESTAB: case O_VERREVPATH: case O_IPSEC: + case O_IP6_SRC_ME: + case O_IP6_DST_ME: + case O_EXT_HDR: + case O_IP6: if (cmdlen != F_INSN_SIZE(ipfw_insn)) goto bad_size; break; @@ -2525,6 +2880,29 @@ return EINVAL; } break; + + case O_IP6_SRC: + case O_IP6_DST: + if (cmdlen != F_INSN_SIZE(struct in6_addr) + F_INSN_SIZE(ipfw_insn)) + goto bad_size; + break; + + case O_FLOW6ID: + if (cmdlen != F_INSN_SIZE(ipfw_insn_u32) + + ((ipfw_insn_u32 *)cmd)->o.arg1) + goto bad_size; + break; + + case O_IP6_SRC_MASK: + case O_IP6_DST_MASK: + if ( !(cmdlen & 1) || cmdlen > 127) + goto bad_size; + break; + case O_ICMP6TYPE: + if( cmdlen != F_INSN_SIZE( ipfw_insn_icmp6 ) ) + goto bad_size; + break; + default: printf("ipfw: opcode %d, unknown opcode\n", cmd->opcode); @@ -2794,7 +3172,7 @@ add_rule(&layer3_chain, &default_rule); ip_fw_default_rule = layer3_chain; - printf("ipfw2 initialized, divert %s, " + printf("ipfw2 (+ipv6) initialized, divert %s, " "rule-based forwarding enabled, default to %s, logging ", #ifdef IPDIVERT "enabled", diff -uNr --exclude=compile src.orig/sys/netinet/ip_fw2.h src/sys/netinet/ip_fw2.h --- src.orig/sys/netinet/ip_fw2.h Thu Jul 17 08:03:39 2003 +++ src/sys/netinet/ip_fw2.h Wed Dec 15 10:05:00 2004 @@ -126,10 +126,32 @@ */ O_IPSEC, /* has ipsec history */ + O_IP6_SRC, /* address without mask */ + O_IP6_SRC_ME, /* my addresses */ + O_IP6_SRC_MASK, /* address with the mask */ + O_IP6_DST, + O_IP6_DST_ME, + O_IP6_DST_MASK, + O_FLOW6ID, /* for flow id tag in the ipv6 pkt */ + O_ICMP6TYPE, /* icmp6 packet type filtering */ + O_EXT_HDR, /* filtering for ipv6 extension header */ + O_IP6, /* Ipv6 protocol presence */ + O_LAST_OPCODE /* not an opcode! */ }; /* + * The extension header are filtered only for presence using a bit vector + * with a flag for each header. + */ + +#define EXT_FRAGMENT 0x1 +#define EXT_HOPOPTS 0x2 +#define EXT_ROUTING 0x4 +#define EXT_AH 0x8 +#define EXT_ESP 0x10 + +/* * Template for instructions. * * ipfw_insn is used for all instructions which require no operands, @@ -265,6 +287,31 @@ u_int32_t log_left; /* how many left to log */ } ipfw_insn_log; +/* Apply ipv6 mask on ipv6 addr */ +#define APPLY_MASK(addr,mask) \ + (addr)->__u6_addr.__u6_addr32[0] &= (mask)->__u6_addr.__u6_addr32[0]; \ + (addr)->__u6_addr.__u6_addr32[1] &= (mask)->__u6_addr.__u6_addr32[1]; \ + (addr)->__u6_addr.__u6_addr32[2] &= (mask)->__u6_addr.__u6_addr32[2]; \ + (addr)->__u6_addr.__u6_addr32[3] &= (mask)->__u6_addr.__u6_addr32[3]; + +/* Structure for ipv6 */ +typedef struct _ipfw_insn_ip6 { + ipfw_insn o; + struct in6_addr addr6; + struct in6_addr mask6; +} ipfw_insn_ip6; + +/* Used to support icmp6 types */ +#define IPFW2_ICMP6_MAXV 7 /* XXX This number is related to the netinet/icmp6.h + * define ICMP6_MAXTYPE + * as follws: n = ICMP6_MAXTYPE/32 + 1 + * Actually the define is 203 + */ +typedef struct _ipfw_insn_icmp6 { + ipfw_insn o; + uint32_t d[IPFW2_ICMP6_MAXV]; +} ipfw_insn_icmp6; + /* * Here we have the structure representing an ipfw rule. * @@ -327,8 +374,15 @@ u_int16_t src_port; u_int8_t proto; u_int8_t flags; /* protocol-specific flags */ + uint8_t addr_type; /* 4 = ipv4, 6 = ipv6, 1=ether ? */ + uint8_t _pad; + struct in6_addr dst_ip6; /* could also store MAC addr! */ + struct in6_addr src_ip6; + u_int32_t flow_id6; }; +#define IS_IP6_FLOW_ID(id) ((id)->addr_type == 6) + /* * Dynamic ipfw rule. */ @@ -384,6 +438,17 @@ #define IP_FW_PORT_DENY_FLAG 0x40000 /* + * Structure for collecting parameters to dummynet for ip6_output forwarding + */ +struct _ip6dn_args { + struct route_in6 ro_or; + int flags_or; + struct ifnet *origifp_or; + struct ifnet *ifp_or; + struct sockaddr_in6 dst_or; +}; + +/* * Arguments for calling ipfw_chk() and dummynet_io(). We put them * all into a structure because this way it is easier and more * efficient to pass variables around and extend the interface. @@ -402,6 +467,8 @@ struct ipfw_flow_id f_id; /* grabbed from IP header */ u_int16_t divert_rule; /* divert cookie */ u_int32_t retval; + + struct _ip6dn_args dummypar; /* dummynet->ip6_output */ }; /* diff -uNr --exclude=compile src.orig/sys/netinet/ip_fw2.h.orig src/sys/netinet/ip_fw2.h.orig --- src.orig/sys/netinet/ip_fw2.h.orig Thu Jan 1 01:00:00 1970 +++ src/sys/netinet/ip_fw2.h.orig Thu Jul 17 08:03:39 2003 @@ -0,0 +1,426 @@ +/* + * Copyright (c) 2002 Luigi Rizzo, Universita` di Pisa + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD: src/sys/netinet/ip_fw2.h,v 1.1.2.4 2003/07/17 06:03:39 luigi Exp $ + */ + +#ifndef _IPFW2_H +#define _IPFW2_H + +/* + * The kernel representation of ipfw rules is made of a list of + * 'instructions' (for all practical purposes equivalent to BPF + * instructions), which specify which fields of the packet + * (or its metadata) should be analysed. + * + * Each instruction is stored in a structure which begins with + * "ipfw_insn", and can contain extra fields depending on the + * instruction type (listed below). + * Note that the code is written so that individual instructions + * have a size which is a multiple of 32 bits. This means that, if + * such structures contain pointers or other 64-bit entities, + * (there is just one instance now) they may end up unaligned on + * 64-bit architectures, so the must be handled with care. + * + * "enum ipfw_opcodes" are the opcodes supported. We can have up + * to 256 different opcodes. + */ + +enum ipfw_opcodes { /* arguments (4 byte each) */ + O_NOP, + + O_IP_SRC, /* u32 = IP */ + O_IP_SRC_MASK, /* ip = IP/mask */ + O_IP_SRC_ME, /* none */ + O_IP_SRC_SET, /* u32=base, arg1=len, bitmap */ + + O_IP_DST, /* u32 = IP */ + O_IP_DST_MASK, /* ip = IP/mask */ + O_IP_DST_ME, /* none */ + O_IP_DST_SET, /* u32=base, arg1=len, bitmap */ + + O_IP_SRCPORT, /* (n)port list:mask 4 byte ea */ + O_IP_DSTPORT, /* (n)port list:mask 4 byte ea */ + O_PROTO, /* arg1=protocol */ + + O_MACADDR2, /* 2 mac addr:mask */ + O_MAC_TYPE, /* same as srcport */ + + O_LAYER2, /* none */ + O_IN, /* none */ + O_FRAG, /* none */ + + O_RECV, /* none */ + O_XMIT, /* none */ + O_VIA, /* none */ + + O_IPOPT, /* arg1 = 2*u8 bitmap */ + O_IPLEN, /* arg1 = len */ + O_IPID, /* arg1 = id */ + + O_IPTOS, /* arg1 = id */ + O_IPPRECEDENCE, /* arg1 = precedence << 5 */ + O_IPTTL, /* arg1 = TTL */ + + O_IPVER, /* arg1 = version */ + O_UID, /* u32 = id */ + O_GID, /* u32 = id */ + O_ESTAB, /* none (tcp established) */ + O_TCPFLAGS, /* arg1 = 2*u8 bitmap */ + O_TCPWIN, /* arg1 = desired win */ + O_TCPSEQ, /* u32 = desired seq. */ + O_TCPACK, /* u32 = desired seq. */ + O_ICMPTYPE, /* u32 = icmp bitmap */ + O_TCPOPTS, /* arg1 = 2*u8 bitmap */ + + O_VERREVPATH, /* none */ + + O_PROBE_STATE, /* none */ + O_KEEP_STATE, /* none */ + O_LIMIT, /* ipfw_insn_limit */ + O_LIMIT_PARENT, /* dyn_type, not an opcode. */ + + /* + * These are really 'actions'. + */ + + O_LOG, /* ipfw_insn_log */ + O_PROB, /* u32 = match probability */ + + O_CHECK_STATE, /* none */ + O_ACCEPT, /* none */ + O_DENY, /* none */ + O_REJECT, /* arg1=icmp arg (same as deny) */ + O_COUNT, /* none */ + O_SKIPTO, /* arg1=next rule number */ + O_PIPE, /* arg1=pipe number */ + O_QUEUE, /* arg1=queue number */ + O_DIVERT, /* arg1=port number */ + O_TEE, /* arg1=port number */ + O_FORWARD_IP, /* fwd sockaddr */ + O_FORWARD_MAC, /* fwd mac */ + + /* + * More opcodes. + */ + O_IPSEC, /* has ipsec history */ + + O_LAST_OPCODE /* not an opcode! */ +}; + +/* + * Template for instructions. + * + * ipfw_insn is used for all instructions which require no operands, + * a single 16-bit value (arg1), or a couple of 8-bit values. + * + * For other instructions which require different/larger arguments + * we have derived structures, ipfw_insn_*. + * + * The size of the instruction (in 32-bit words) is in the low + * 6 bits of "len". The 2 remaining bits are used to implement + * NOT and OR on individual instructions. Given a type, you can + * compute the length to be put in "len" using F_INSN_SIZE(t) + * + * F_NOT negates the match result of the instruction. + * + * F_OR is used to build or blocks. By default, instructions + * are evaluated as part of a logical AND. An "or" block + * { X or Y or Z } contains F_OR set in all but the last + * instruction of the block. A match will cause the code + * to skip past the last instruction of the block. + * + * NOTA BENE: in a couple of places we assume that + * sizeof(ipfw_insn) == sizeof(u_int32_t) + * this needs to be fixed. + * + */ +typedef struct _ipfw_insn { /* template for instructions */ + enum ipfw_opcodes opcode:8; + u_int8_t len; /* numer of 32-byte words */ +#define F_NOT 0x80 +#define F_OR 0x40 +#define F_LEN_MASK 0x3f +#define F_LEN(cmd) ((cmd)->len & F_LEN_MASK) + + u_int16_t arg1; +} ipfw_insn; + +/* + * The F_INSN_SIZE(type) computes the size, in 4-byte words, of + * a given type. + */ +#define F_INSN_SIZE(t) ((sizeof (t))/sizeof(u_int32_t)) + +/* + * This is used to store an array of 16-bit entries (ports etc.) + */ +typedef struct _ipfw_insn_u16 { + ipfw_insn o; + u_int16_t ports[2]; /* there may be more */ +} ipfw_insn_u16; + +/* + * This is used to store an array of 32-bit entries + * (uid, single IPv4 addresses etc.) + */ +typedef struct _ipfw_insn_u32 { + ipfw_insn o; + u_int32_t d[1]; /* one or more */ +} ipfw_insn_u32; + +/* + * This is used to store IP addr-mask pairs. + */ +typedef struct _ipfw_insn_ip { + ipfw_insn o; + struct in_addr addr; + struct in_addr mask; +} ipfw_insn_ip; + +/* + * This is used to forward to a given address (ip). + */ +typedef struct _ipfw_insn_sa { + ipfw_insn o; + struct sockaddr_in sa; +} ipfw_insn_sa; + +/* + * This is used for MAC addr-mask pairs. + */ +typedef struct _ipfw_insn_mac { + ipfw_insn o; + u_char addr[12]; /* dst[6] + src[6] */ + u_char mask[12]; /* dst[6] + src[6] */ +} ipfw_insn_mac; + +/* + * This is used for interface match rules (recv xx, xmit xx). + */ +typedef struct _ipfw_insn_if { + ipfw_insn o; + union { + struct in_addr ip; + int32_t unit; + } p; + char name[IFNAMSIZ]; +} ipfw_insn_if; + +/* + * This is used for pipe and queue actions, which need to store + * a single pointer (which can have different size on different + * architectures. + * Note that, because of previous instructions, pipe_ptr might + * be unaligned in the overall structure, so it needs to be + * manipulated with care. + */ +typedef struct _ipfw_insn_pipe { + ipfw_insn o; + void *pipe_ptr; /* XXX */ +} ipfw_insn_pipe; + +/* + * This is used for limit rules. + */ +typedef struct _ipfw_insn_limit { + ipfw_insn o; + u_int8_t _pad; + u_int8_t limit_mask; /* combination of DYN_* below */ +#define DYN_SRC_ADDR 0x1 +#define DYN_SRC_PORT 0x2 +#define DYN_DST_ADDR 0x4 +#define DYN_DST_PORT 0x8 + + u_int16_t conn_limit; +} ipfw_insn_limit; + +/* + * This is used for log instructions. + */ +typedef struct _ipfw_insn_log { + ipfw_insn o; + u_int32_t max_log; /* how many do we log -- 0 = all */ + u_int32_t log_left; /* how many left to log */ +} ipfw_insn_log; + +/* + * Here we have the structure representing an ipfw rule. + * + * It starts with a general area (with link fields and counters) + * followed by an array of one or more instructions, which the code + * accesses as an array of 32-bit values. + * + * Given a rule pointer r: + * + * r->cmd is the start of the first instruction. + * ACTION_PTR(r) is the start of the first action (things to do + * once a rule matched). + * + * When assembling instruction, remember the following: + * + * + if a rule has a "keep-state" (or "limit") option, then the + * first instruction (at r->cmd) MUST BE an O_PROBE_STATE + * + if a rule has a "log" option, then the first action + * (at ACTION_PTR(r)) MUST be O_LOG + * + * NOTE: we use a simple linked list of rules because we never need + * to delete a rule without scanning the list. We do not use + * queue(3) macros for portability and readability. + */ + +struct ip_fw { + struct ip_fw *next; /* linked list of rules */ + struct ip_fw *next_rule; /* ptr to next [skipto] rule */ + /* 'next_rule' is used to pass up 'set_disable' status */ + + u_int16_t act_ofs; /* offset of action in 32-bit units */ + u_int16_t cmd_len; /* # of 32-bit words in cmd */ + u_int16_t rulenum; /* rule number */ + u_int8_t set; /* rule set (0..31) */ +#define RESVD_SET 31 /* set for default and persistent rules */ + u_int8_t _pad; /* padding */ + + /* These fields are present in all rules. */ + u_int64_t pcnt; /* Packet counter */ + u_int64_t bcnt; /* Byte counter */ + u_int32_t timestamp; /* tv_sec of last match */ + + ipfw_insn cmd[1]; /* storage for commands */ +}; + +#define ACTION_PTR(rule) \ + (ipfw_insn *)( (u_int32_t *)((rule)->cmd) + ((rule)->act_ofs) ) + +#define RULESIZE(rule) (sizeof(struct ip_fw) + \ + ((struct ip_fw *)(rule))->cmd_len * 4 - 4) + +/* + * This structure is used as a flow mask and a flow id for various + * parts of the code. + */ +struct ipfw_flow_id { + u_int32_t dst_ip; + u_int32_t src_ip; + u_int16_t dst_port; + u_int16_t src_port; + u_int8_t proto; + u_int8_t flags; /* protocol-specific flags */ +}; + +/* + * Dynamic ipfw rule. + */ +typedef struct _ipfw_dyn_rule ipfw_dyn_rule; + +struct _ipfw_dyn_rule { + ipfw_dyn_rule *next; /* linked list of rules. */ + struct ip_fw *rule; /* pointer to rule */ + /* 'rule' is used to pass up the rule number (from the parent) */ + + ipfw_dyn_rule *parent; /* pointer to parent rule */ + u_int64_t pcnt; /* packet match counter */ + u_int64_t bcnt; /* byte match counter */ + struct ipfw_flow_id id; /* (masked) flow id */ + u_int32_t expire; /* expire time */ + u_int32_t bucket; /* which bucket in hash table */ + u_int32_t state; /* state of this rule (typically a + * combination of TCP flags) + */ + u_int32_t ack_fwd; /* most recent ACKs in forward */ + u_int32_t ack_rev; /* and reverse directions (used */ + /* to generate keepalives) */ + u_int16_t dyn_type; /* rule type */ + u_int16_t count; /* refcount */ +}; + +/* + * Definitions for IP option names. + */ +#define IP_FW_IPOPT_LSRR 0x01 +#define IP_FW_IPOPT_SSRR 0x02 +#define IP_FW_IPOPT_RR 0x04 +#define IP_FW_IPOPT_TS 0x08 + +/* + * Definitions for TCP option names. + */ +#define IP_FW_TCPOPT_MSS 0x01 +#define IP_FW_TCPOPT_WINDOW 0x02 +#define IP_FW_TCPOPT_SACK 0x04 +#define IP_FW_TCPOPT_TS 0x08 +#define IP_FW_TCPOPT_CC 0x10 + +#define ICMP_REJECT_RST 0x100 /* fake ICMP code (send a TCP RST) */ + +/* + * Main firewall chains definitions and global var's definitions. + */ +#ifdef _KERNEL + +#define IP_FW_PORT_DYNT_FLAG 0x10000 +#define IP_FW_PORT_TEE_FLAG 0x20000 +#define IP_FW_PORT_DENY_FLAG 0x40000 + +/* + * Arguments for calling ipfw_chk() and dummynet_io(). We put them + * all into a structure because this way it is easier and more + * efficient to pass variables around and extend the interface. + */ +struct ip_fw_args { + struct mbuf *m; /* the mbuf chain */ + struct ifnet *oif; /* output interface */ + struct sockaddr_in *next_hop; /* forward address */ + struct ip_fw *rule; /* matching rule */ + struct ether_header *eh; /* for bridged packets */ + + struct route *ro; /* for dummynet */ + struct sockaddr_in *dst; /* for dummynet */ + int flags; /* for dummynet */ + + struct ipfw_flow_id f_id; /* grabbed from IP header */ + u_int16_t divert_rule; /* divert cookie */ + u_int32_t retval; +}; + +/* + * Function definitions. + */ + +/* Firewall hooks */ +struct sockopt; +struct dn_flow_set; + +void flush_pipe_ptrs(struct dn_flow_set *match); /* used by dummynet */ + +typedef int ip_fw_chk_t (struct ip_fw_args *args); +typedef int ip_fw_ctl_t (struct sockopt *); +extern ip_fw_chk_t *ip_fw_chk_ptr; +extern ip_fw_ctl_t *ip_fw_ctl_ptr; +extern int fw_one_pass; +extern int fw_enable; +#define IPFW_LOADED (ip_fw_chk_ptr != NULL) +#endif /* _KERNEL */ + +#endif /* _IPFW2_H */ diff -uNr --exclude=compile src.orig/sys/netinet/raw_ip.c src/sys/netinet/raw_ip.c --- src.orig/sys/netinet/raw_ip.c Tue Sep 16 07:43:56 2003 +++ src/sys/netinet/raw_ip.c Wed Dec 15 10:29:39 2004 @@ -37,6 +37,7 @@ #include "opt_inet6.h" #include "opt_ipsec.h" #include "opt_random_ip_id.h" +#include "opt_ipfw.h" #include <sys/param.h> #include <sys/systm.h> diff -uNr --exclude=compile src.orig/sys/netinet6/ip6_forward.c src/sys/netinet6/ip6_forward.c --- src.orig/sys/netinet6/ip6_forward.c Fri Jan 24 06:11:35 2003 +++ src/sys/netinet6/ip6_forward.c Wed Dec 15 10:42:20 2004 @@ -34,6 +34,7 @@ #include "opt_inet.h" #include "opt_inet6.h" #include "opt_ipsec.h" +#include "opt_ipfw.h" #include <sys/param.h> #include <sys/systm.h> @@ -457,6 +458,7 @@ /* * Check with the firewall... */ +#ifndef IPFW2 if (ip6_fw_enable && ip6_fw_chk_ptr) { u_short port = 0; /* If ipfw says divert, we have to just drop packet */ @@ -467,6 +469,7 @@ if (!m) goto freecopy; } +#endif /* * Fake scoped addresses. Note that even link-local source or diff -uNr --exclude=compile src.orig/sys/netinet6/ip6_input.c src/sys/netinet6/ip6_input.c --- src.orig/sys/netinet6/ip6_input.c Fri Jan 24 06:11:35 2003 +++ src/sys/netinet6/ip6_input.c Wed Dec 15 10:05:00 2004 @@ -65,7 +65,7 @@ * @(#)ip_input.c 8.2 (Berkeley) 1/4/94 */ -#include "opt_ip6fw.h" +#include "opt_ipfw.h" #include "opt_inet.h" #include "opt_inet6.h" #include "opt_ipsec.h" @@ -119,7 +119,12 @@ #define IPSEC #endif /* FAST_IPSEC */ +#ifdef IPFW2 +#include <netinet/ip_fw.h> +#include <netinet/ip_dummynet.h> +#else #include <netinet6/ip6_fw.h> +#endif #include <netinet6/ip6protosw.h> @@ -148,9 +153,11 @@ /* firewall hooks */ +#ifndef IPFW2 ip6_fw_chk_t *ip6_fw_chk_ptr; ip6_fw_ctl_t *ip6_fw_ctl_ptr; int ip6_fw_enable = 1; +#endif /* !IPFW2 */ struct ip6stat ip6stat; @@ -263,6 +270,53 @@ int nxt, ours = 0; struct ifnet *deliverifp = NULL; +#ifdef IPFW2 + int i, hlen; +#ifdef IPDIVERT + u_int32_t divert_info = 0; /* packet divert/tee info */ +#endif + struct ip_fw_args args; + args.eh = NULL; + args.oif = NULL; + args.rule = NULL; + args.divert_rule = 0; /* divert cookie */ + args.next_hop = NULL; + + /* Grab info from MT_TAG mbufs prepended to the chain. */ + for (; m && m->m_type == MT_TAG; m = m->m_next) { + switch(m->_m_tag_id) { + default: + printf("ip6_input: unrecognised MT_TAG tag %d\n", + m->_m_tag_id); + break; + + case PACKET_TAG_DUMMYNET: + args.rule = ((struct dn_pkt *)m)->rule; + break; + + case PACKET_TAG_DIVERT: + args.divert_rule = (int)m->m_hdr.mh_data & 0xffff; + break; + +#if 0 + /* The ipfw2 forwarding is not yet implemented in ipv6 */ + case PACKET_TAG_IPFORWARD: + args.next_hop = (struct sockaddr_in *)m->m_hdr.mh_data; + break; +#endif + } + } + + KASSERT(m != NULL && (m->m_flags & M_PKTHDR) != 0, + ("ip6_input: no HDR")); + + if (args.rule) { /* dummynet already filtered us */ + ip6 = mtod(m, struct ip6_hdr *); + hlen = sizeof (struct ip6_hdr); + goto iphack; + } +#endif /* IPFW2 */ + #ifdef IPSEC /* * should the inner packet be considered authentic? @@ -354,6 +408,7 @@ goto bad; } +iphack: /* * Check if we want to allow this packet to be processed. * Consider it to be bad if not. @@ -375,6 +430,50 @@ /* * Check with the firewall... */ +#ifdef IPFW2 + if (fw_enable && IPFW_LOADED) { + /* + * If we've been forwarded from the output side, then + * skip the firewall a second time + */ + + if (args.next_hop) + ours=1; /* XXX check if this is correct */ + + args.m = m; + i = ip_fw_chk_ptr(&args); + m = args.m; + + if ( (i & IP_FW_PORT_DENY_FLAG) || m == NULL) { /* drop */ + if (m) + m_freem(m); + return; + } + ip6 = mtod(m, struct ip6_hdr *); /* just in case m changed */ + if (i == 0 && args.next_hop == NULL) /* common case */ + goto pass; + if (DUMMYNET_LOADED && (i & IP_FW_PORT_DYNT_FLAG) != 0) { + /* Send packet to the appropriate pipe */ + ip_dn_io_ptr(m, i & 0xffff, DN_TO_IP6_IN, &args); + return; + } +#ifdef IPDIVERT + if (i != 0 && (i & IP_FW_PORT_DYNT_FLAG) == 0) { + /* Divert or tee packet */ + divert_info = i; + ours=1; + } +#endif + if (i == 0 && args.next_hop != NULL) + goto pass; + /* + * if we get here, the packet must be dropped + */ + m_freem(m); + return; + } +pass: +#else /* !IPFW2, use the old firewall */ if (ip6_fw_enable && ip6_fw_chk_ptr) { u_short port = 0; /* If ipfw says divert, we have to just drop packet */ @@ -386,6 +485,7 @@ if (!m) return; } +#endif /* !IPFW2 */ /* * Check against address spoofing/corruption. diff -uNr --exclude=compile src.orig/sys/netinet6/ip6_output.c src/sys/netinet6/ip6_output.c --- src.orig/sys/netinet6/ip6_output.c Sun Mar 28 17:11:57 2004 +++ src/sys/netinet6/ip6_output.c Wed Dec 15 10:05:00 2004 @@ -65,7 +65,7 @@ * @(#)ip_output.c 8.3 (Berkeley) 1/21/94 */ -#include "opt_ip6fw.h" +#include "opt_ipfw.h" #include "opt_inet.h" #include "opt_inet6.h" #include "opt_ipsec.h" @@ -108,7 +108,13 @@ #include <netipsec/key.h> #endif /* FAST_IPSEC */ +#ifdef IPFW2 +#include <netinet/ip_var.h> +#include <netinet/ip_fw.h> +#include <netinet/ip_dummynet.h> +#else /* use old ip6fw */ #include <netinet6/ip6_fw.h> +#endif #include <net/net_osdep.h> @@ -173,6 +179,9 @@ struct route_in6 *ro_pmtu = NULL; int hdrsplit = 0; int needipsec = 0; +#ifdef IPFW2 + struct ip_fw_args args; +#endif #ifdef IPSEC int needipsectun = 0; struct secpolicy *sp = NULL; @@ -187,6 +196,66 @@ ip6 = mtod(m, struct ip6_hdr *); #endif /* FAST_IPSEC */ +#ifdef IPFW2 + args.eh = NULL; + args.rule = NULL; + args.next_hop = NULL; + args.divert_rule = 0; /* divert cookie */ + + /* Grab info from MT_TAG mbufs prepended to the chain. */ + for (; m0 && m0->m_type == MT_TAG; m0 = m0->m_next) { + switch(m0->_m_tag_id) { + default: + printf("ip6_output: unrecognised MT_TAG tag %d\n", + m0->_m_tag_id); + break; + + case PACKET_TAG_DUMMYNET: + /* + * the packet was already tagged, so part of the + * processing was already done, and we need to go down. + * Get parameters from the header. + */ + opt = NULL; + ro = &((struct dn_pkt *)m0)->ip6opt.ro_or; + flags = ((struct dn_pkt *)m0)->ip6opt.flags_or; + im6o = NULL; + origifp = ((struct dn_pkt *)m0)->ip6opt.origifp_or; + ifp = ((struct dn_pkt *)m0)->ip6opt.ifp_or; + dst = &((struct dn_pkt *)m0)->ip6opt.dst_or; + args.rule=((struct dn_pkt *)m0)->rule; + break; + + case PACKET_TAG_DIVERT: + args.divert_rule = (int)m0->m_data & 0xffff; + break; + +#if 0 + /* ipfw2 Forwarding is not yet supported in ipv6 */ + case PACKET_TAG_IPFORWARD: + args.next_hop = (struct sockaddr_in *)m0->m_data; + break; +#endif + } + } + m = m0; + + KASSERT(!m || (m->m_flags & M_PKTHDR) != 0, ("ip6_output: no HDR")); +#ifndef FAST_IPSEC + KASSERT(ro != NULL, ("ip6_output: no route\n")); +#endif + + if (args.rule ) { /* dummynet already saw us */ + ip6 = mtod(m, struct ip6_hdr *); + hlen = sizeof (struct ip6_hdr) ; + if (ro->ro_rt) + ia = ifatoia6(ro->ro_rt->rt_ifa); + bzero(&exthdrs, sizeof(exthdrs)); + ro_pmtu = ro; + goto send_after_dummynet; + } +#endif /* IPFW2 */ + #define MAKE_EXTHDR(hp, mp) \ do { \ if (hp) { \ @@ -765,6 +834,7 @@ } } +send_after_dummynet: /* * Fill the outgoing inteface to tell the upper layer * to increment per-interface statistics. @@ -836,6 +906,68 @@ /* * Check with the firewall... */ +#ifdef IPFW2 + if (fw_enable && IPFW_LOADED && !args.next_hop) { + /* + * Check with the firewall IPFW2... + * but not if we are already being fwd'd from a firewall. + */ + + struct sockaddr_in6 *old = dst; + args.m = m; + args.next_hop = (struct sockaddr_in *) dst; + args.oif = ifp; + off = ip_fw_chk_ptr(&args); + m = args.m; + dst = (struct sockaddr_in6 *) args.next_hop; + + /* + * On return we must do the following: + * m == NULL -> drop the pkt (old interface, deprecated) + * (off & IP_FW_PORT_DENY_FLAG) -> drop the pkt (new interface) + * 1<=off<= 0xffff -> DIVERT + * (off & IP_FW_PORT_DYNT_FLAG) -> send to a DUMMYNET pipe + * (off & IP_FW_PORT_TEE_FLAG) -> TEE the packet + * dst != old -> IPFIREWALL_FORWARD + * off==0, dst==old -> accept + * If some of the above modules are not compiled in, then + * we should't have to check the corresponding condition + * (because the ipfw control socket should not accept + * unsupported rules), but better play safe and drop + * packets in case of doubt. + */ + if ( (off & IP_FW_PORT_DENY_FLAG) || m == NULL) { + if (m) + m_freem(m); + error = EACCES; + goto done; + } + ip6 = mtod(m, struct ip6_hdr *); /* XXX check if necessary */ + if (off == 0 && dst == old) /* common case */ + goto pass6; + if (DUMMYNET_LOADED && (off & IP_FW_PORT_DYNT_FLAG) != 0) { + /* + * pass the pkt to dummynet. Need to include + * pipe number, m, ifp, ro, dst because these are + * not recomputed in the next pass. + * All other parameters have been already used and + * so they are not needed anymore. + * XXX note: if the ifp or ro entry are deleted + * while a pkt is in dummynet, we are in trouble! + */ + args.dummypar.ro_or = *ro; + args.dummypar.flags_or = flags; + args.dummypar.ifp_or = ifp; + args.dummypar.origifp_or = origifp; + args.dummypar.dst_or = *dst; + args.flags = flags; + error = ip_dn_io_ptr(m, off & 0xffff, DN_TO_IP6_OUT, + &args); + goto done; + } + } +pass6: +#else /* !IPFW2 */ if (ip6_fw_enable && ip6_fw_chk_ptr) { u_short port = 0; m->m_pkthdr.rcvif = NULL; /* XXX */ @@ -849,7 +981,7 @@ goto done; } } - +#endif /* !IPFW2 */ /* * If the outgoing packet contains a hop-by-hop options header, * it must be examined and processed even by the source node. @@ -1637,6 +1769,7 @@ break; #endif /* KAME IPSEC */ +#ifndef IPFW2 case IPV6_FW_ADD: case IPV6_FW_DEL: case IPV6_FW_FLUSH: @@ -1657,7 +1790,7 @@ m = *mp; } break; - +#endif /* !IPFW2 */ default: error = ENOPROTOOPT; break; @@ -1797,6 +1930,7 @@ } #endif /* KAME IPSEC */ +#ifndef IPFW2 case IPV6_FW_GET: { struct mbuf *m; @@ -1813,6 +1947,7 @@ m_freem(m); } break; +#endif /* !IPFW2 */ default: error = ENOPROTOOPT; @@ -2241,8 +2376,8 @@ * If an interface address was specified, get a pointer * to its ifnet structure. */ - if (mreq->ipv6mr_interface < 0 - || if_index < mreq->ipv6mr_interface) { + if (mreq->ipv6mr_interface < 0 || + if_index < mreq->ipv6mr_interface) { error = ENXIO; /* XXX EINVAL? */ break; } @@ -2253,7 +2388,7 @@ */ if (IN6_IS_ADDR_MC_LINKLOCAL(&mreq->ipv6mr_multiaddr)) { mreq->ipv6mr_multiaddr.s6_addr16[1] - = htons(mreq->ipv6mr_interface); + = htons(ifp->if_index); } /* * Find the membership in the membership list. --7ZAtKRhVyVSsbBD2--
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?20041221103650.GC25908>