From 8195404bede1d21ab1f42ded0a5fc9f0b266ccde Mon Sep 17 00:00:00 2001 From: Brooks Davis Date: Mon, 18 Apr 2005 18:35:05 +0000 Subject: [PATCH] Add IPv6 support to IPFW and Dummynet. Submitted by: Mariano Tortoriello and Raffaele De Lorenzo (via luigi) --- sbin/ipfw/ipfw.8 | 152 ++++++-- sbin/ipfw/ipfw2.c | 733 ++++++++++++++++++++++++++++++++++++-- sys/netinet/ip_dummynet.c | 83 ++++- sys/netinet/ip_dummynet.h | 3 + sys/netinet/ip_fw.h | 67 ++++ sys/netinet/ip_fw2.c | 365 +++++++++++++++++-- sys/netinet/ip_fw_pfil.c | 35 +- sys/netinet6/ip6_output.c | 26 ++ 8 files changed, 1351 insertions(+), 113 deletions(-) diff --git a/sbin/ipfw/ipfw.8 b/sbin/ipfw/ipfw.8 index 3f4bc9a06b3a..a5a8bc2af952 100644 --- a/sbin/ipfw/ipfw.8 +++ b/sbin/ipfw/ipfw.8 @@ -346,18 +346,18 @@ in the protocol stack, under control of several sysctl variables. These places and variables are shown below, and it is important to have this picture in mind in order to design a correct ruleset. .Bd -literal -offset indent - ^ to upper layers V - | | - +----------->-----------+ - ^ V - [ip_input] [ip_output] net.inet.ip.fw.enable=1 - | | - ^ V -[ether_demux] [ether_output_frame] net.link.ether.ipfw=1 - | | - +-->--[bdg_forward]-->--+ net.link.ether.bridge_ipfw=1 - ^ V - | to devices | + ^ to upper layers V + | | + +----------->-----------+ + ^ V + [ip(6)_input] [ip(6)_output] net.inet.ip.fw.enable=1 + | | + ^ V + [ether_demux] [ether_output_frame] net.link.ether.ipfw=1 + | | + +-->--[bdg_forward]-->--+ net.link.ether.bridge_ipfw=1 + ^ V + | to devices | .Ed .Pp As can be noted from the above picture, the number of @@ -375,13 +375,17 @@ is invoked from but the same packets will have the MAC header stripped off when .Nm is invoked from -.Cm ip_input() . +.Cm ip_input() +or +.Cm ip6_input() . .Pp Also note that each packet is always checked against the complete ruleset, irrespective of the place where the check occurs, or the source of the packet. If a rule contains some match patterns or actions which are not valid for the place of invocation (e.g.\& trying to match a MAC header within -.Fn ip_input ) , +.Cm ip_input +or +.Cm ip6_input ), the match pattern will not match, but a .Cm not operator in front of such patterns @@ -448,7 +452,7 @@ for filtering packets, among the following: .Bl -tag -width "Source and dest. addresses and ports" -offset XXX -compact .It Layer-2 header fields When available -.It IPv4 Protocol +.It IPv4 and IPv6 Protocol TCP, UDP, ICMP, etc. .It Source and dest. addresses and ports .It Direction @@ -461,6 +465,10 @@ Version, type of service, datagram length, identification, fragment flag (non-zero IP offset), Time To Live .It IP options +.It IPv6 Extension headers +Fragmentation, Hop-by-Hop options, +source routing, IPSec options. +.It IPv6 Flow-ID .It Misc. TCP header fields TCP flags (SYN, FIN, ACK, RST, etc.), sequence number, acknowledgment number, @@ -468,6 +476,8 @@ window .It TCP options .It ICMP types for ICMP packets +.It ICMP6 types +for ICMP6 packets .It User/group ID When the packet can be associated with a local socket. .It Divert status @@ -806,7 +816,7 @@ compatibility with .Nm ipfw1 . In .Nm ipfw2 -any match pattern (including MAC headers, IPv4 protocols, +any match pattern (including MAC headers, IP protocols, addresses and ports) can be specified in the .Ar options section. @@ -815,11 +825,13 @@ Rule fields have the following meaning: .Bl -tag -width indent .It Ar proto : protocol | Cm { Ar protocol Cm or ... } .It Ar protocol : Oo Cm not Oc Ar protocol-name | protocol-number -An IPv4 protocol specified by number or name +An IP protocol specified by number or name (for a complete list see .Pa /etc/protocols ) . The -.Cm ip +.Cm ip , +.Cm ip6 , +.Cm ipv6 , or .Cm all keywords mean any protocol will match. @@ -840,7 +852,7 @@ The second format with multiple addresses) is provided for convenience only and its use is discouraged. .It Ar addr : Oo Cm not Oc Bro -.Cm any | me | +.Cm any | me | me6 .Cm table Ns Pq Ar number Ns Op , Ns Ar value .Ar | addr-list | addr-set .Brc @@ -848,10 +860,12 @@ its use is discouraged. matches any IP address. .It Cm me matches any IP address configured on an interface in the system. +.It Cm me6 +matches any IPv6 address configured on an interface in the system. The address list is evaluated at the time the packet is analysed. .It Cm table Ns Pq Ar number Ns Op , Ns Ar value -Matches any IP address for which an entry exists in the lookup table +Matches any IPv4 address for which an entry exists in the lookup table .Ar number . If an optional 32-bit unsigned .Ar value @@ -918,6 +932,30 @@ As an example, an address specified as 1.2.3.4/24{128,35-55,89} will match the following IP addresses: .br 1.2.3.128, 1.2.3.35 to 1.2.3.55, 1.2.3.89 . +.It Ar addr6-list : ip6-addr Ns Op Ns , Ns Ar addr6-list +.It Ar ip6-addr : +A host or subnet specified one of the following ways: +.Pp +.Bl -tag -width indent +.It Ar numeric-ip | hostname +Matches a single IPv6 address as allowed by +.Xr inet_pton 3 +or a hostname. +Hostnames are resolved at the time the rule is added to the firewall +list. +.It Ar addr Ns / Ns Ar masklen +Matches all IPv6 addresses with base +.Ar addr +(specified as allowed by +.Xr inet_pton +or a hostname) +and mask width of +.Cm masklen +bits. +.El +.Pp +No support for sets of IPv6 addresses is provided because IPv6 addresses +are typically random past the initial prefix. .It Ar ports : Bro Ar port | port Ns \&- Ns Ar port Ns Brc Ns Op , Ns Ar ports For protocols which support port numbers (such as TCP and UDP), optional .Cm ports @@ -986,13 +1024,36 @@ input for delivery. Matches only packets going from a divert socket back outward to the IP stack output for delivery. .It Cm dst-ip Ar ip-address -Matches IP packets whose destination IP is one of the address(es) +Matches IPv4 packets whose destination IP is one of the address(es) +specified as argument. +.It Bro Cm dst-ip6 | dst-ipv6 Brc Ar ip6-address +Matches IPv6 packets whose destination IP is one of the address(es) specified as argument. .It Cm dst-port Ar ports Matches IP packets whose destination port is one of the port(s) specified as argument. .It Cm established Matches TCP packets that have the RST or ACK bits set. +.It Cm ext6hdr Ar header +Matches IPv6 packets containing the extended header given by +.Ar header . +Supported headers are: +.Pp +Fragment, +.Pq Cm frag , +Hop-to-hop options +.Pq Cm hopopt , +Source routing +.Pq Cm route , +IPSec authentication headers +.Pq Cm ah , +and IPSec encapsulated security payload headers +.Pq Cm esp . +.It Cm flow-id Ar labels +Matches IPv6 packets containing any of the flow labels given in +.Ar labels . +.Ar labels +is a comma seperate list of numeric flow labels. .It Cm frag Matches packets that are fragments and not the first fragment of an IP datagram. @@ -1047,6 +1108,12 @@ address mask request .Pq Cm 17 and address mask reply .Pq Cm 18 . +.It Cm icmp6types Ar types +Matches ICMP6 packets whose ICMP6 type is in the list of +.Ar types . +The list may be specified as any combination of +individual types (numeric) separated by commas. +.Em Ranges are not allowed. .It Cm in | out Matches incoming or outgoing packets, respectively. .Cm in @@ -1057,7 +1124,7 @@ are mutually exclusive (in fact, is implemented as .Cm not in Ns No ). .It Cm ipid Ar id-list -Matches IP packets whose +Matches IPv4 packets whose .Cm ip_id field has value included in .Ar id-list , @@ -1072,7 +1139,7 @@ which is either a single value or a list of values or ranges specified in the same way as .Ar ports . .It Cm ipoptions Ar spec -Matches packets whose IP header contains the comma separated list of +Matches packets whose IPv4 header contains the comma separated list of options specified in .Ar spec . The supported IP options are: @@ -1089,7 +1156,7 @@ The absence of a particular option may be denoted with a .Ql \&! . .It Cm ipprecedence Ar precedence -Matches IP packets whose precedence field is equal to +Matches IPv4 packets whose precedence field is equal to .Ar precedence . .It Cm ipsec Matches packets that have IPSEC history associated with them @@ -1111,7 +1178,7 @@ rules are handled as if with no .Cm ipsec flag. .It Cm iptos Ar spec -Matches IP packets whose +Matches IPv4 packets whose .Cm tos field contains the comma separated list of service types specified in @@ -1132,7 +1199,7 @@ The absence of a particular type may be denoted with a .Ql \&! . .It Cm ipttl Ar ttl-list -Matches IP packets whose time to live is included in +Matches IPv4 packets whose time to live is included in .Ar ttl-list , which is either a single value or a list of values or ranges specified in the same way as @@ -1160,6 +1227,8 @@ set of parameters as specified in the rule. One or more of source and destination addresses and ports can be specified. +Currently, +only IPv4 flows are supported. .It Cm { MAC | mac } Ar dst-mac src-mac Match packets with a given .Ar dst-mac @@ -1212,7 +1281,7 @@ and they are always printed as hexadecimal (unless the .Cm -N option is used, in which case symbolic resolution will be attempted). .It Cm proto Ar protocol -Matches packets with the corresponding IPv4 protocol. +Matches packets with the corresponding IP protocol. .It Cm recv | xmit | via Brq Ar ifX | Ar if Ns Cm * | Ar ipno | Ar any Matches packets received, transmitted or going through, respectively, the interface specified by exact name @@ -1260,8 +1329,11 @@ Matches TCP packets that have the SYN bit set but no ACK bit. This is the short form of .Dq Li tcpflags\ syn,!ack . .It Cm src-ip Ar ip-address -Matches IP packets whose source IP is one of the address(es) -specified as argument. +Matches IPv4 packets whose source IP is one of the address(es) +specified as an argument. +.It Cm src-ip6 Ar ip6-address +Matches IPv6 packets whose source IP is one of the address(es) +specified as an argument. .It Cm src-port Ar ports Matches IP packets whose source port is one of the port(s) specified as argument. @@ -1388,7 +1460,7 @@ connected networks instead of all source addresses. .Sh LOOKUP TABLES Lookup tables are useful to handle large sparse address sets, typically from a hundred to several thousands of entries. -There could be 128 different lookup tables, numbered 0 to 127. +There may be up to 128 different lookup tables, numbered 0 to 127. .Pp Each entry is represented by an .Ar addr Ns Op / Ns Ar masklen @@ -1422,6 +1494,8 @@ or flushed Internally, each table is stored in a Radix tree, the same way as the routing table (see .Xr route 4 ) . +.Pp +Lookup tables currently support IPv4 addresses only. .Sh SETS OF RULES Each rule belongs to one of 32 different .Em sets @@ -1694,9 +1768,12 @@ with different weights might be connected to the same pipe). Available mask specifiers are a combination of one or more of the following: .Pp .Cm dst-ip Ar mask , +.Cm dst-ip6 Ar mask , .Cm src-ip Ar mask , +.Cm src-ip6 Ar mask , .Cm dst-port Ar mask , .Cm src-port Ar mask , +.Cm flow-id Ar mask , .Cm proto Ar mask or .Cm all , @@ -1767,6 +1844,14 @@ specifies the expected maximum packet size, only used when queue thresholds are in bytes (defaults to 1500, must be greater than zero). .El .El +.Pp +When used with IPv6 data, dummynet currently has several limitations. +First, debug.mpsafenet=0 must be set. +Second, the information necessicary to route link-local packets to an +interface is not avalable after processing by dummynet so those packets +are dropped in the output path. +Care should be taken to insure that link-local packets are not passed to +dummynet. .Sh CHECKLIST Here are some important points to consider when designing your rules: @@ -2092,6 +2177,9 @@ The following option for .Nm dummynet pipes/queues is not supported: .Cm noerror . +.It IPv6 Support +There was no IPv6 support in +.Nm ipfw1 . .El .Sh EXAMPLES There are far too many possible uses of @@ -2396,6 +2484,8 @@ Work on .Xr dummynet 4 traffic shaper supported by Akamba Corp. .Sh BUGS +Use of dummynet with IPv6 requires that debug.mpsafenet be set to 0. +.Pp The syntax has grown over the years and sometimes it might be confusing. Unfortunately, backward compatibility prevents cleaning up mistakes made in the definition of the syntax. @@ -2424,3 +2514,5 @@ If a packet is reinserted in this manner, later rules may be incorrectly applied, making the order of .Cm divert rules in the rule sequence very important. +.Pp +Dummynet drops all packets with IPv6 link-local addresses. diff --git a/sbin/ipfw/ipfw2.c b/sbin/ipfw/ipfw2.c index 4e60e5b8f324..fb2f67d2275b 100644 --- a/sbin/ipfw/ipfw2.c +++ b/sbin/ipfw/ipfw2.c @@ -49,10 +49,12 @@ #include #include +#include /* def. of struct route */ #include #include #include #include +#include #include #include #include @@ -266,6 +268,13 @@ enum tokens { TOK_DROPTAIL, TOK_PROTO, TOK_WEIGHT, + + TOK_IPV6, + TOK_FLOWID, + TOK_ICMP6TYPES, + TOK_EXT6HDR, + TOK_DSTIP6, + TOK_SRCIP6, }; struct _s_x dummynet_params[] = { @@ -288,6 +297,11 @@ struct _s_x dummynet_params[] = { { "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 */ }; @@ -375,6 +389,16 @@ struct _s_x rule_options[] = { { "versrcreach", TOK_VERSRCREACH }, { "antispoof", TOK_ANTISPOOF }, { "ipsec", TOK_IPSEC }, + { "icmp6type", TOK_ICMP6TYPES }, + { "icmp6types", TOK_ICMP6TYPES }, + { "ext6hdr", TOK_EXT6HDR}, + { "flow-id", TOK_FLOWID}, + { "ipv6", TOK_IPV6}, + { "ip6", TOK_IPV6}, + { "dst-ipv6", TOK_DSTIP6}, + { "dst-ip6", TOK_DSTIP6}, + { "src-ipv6", TOK_SRCIP6}, + { "src-ip6", TOK_SRCIP6}, { "//", TOK_COMMENT }, { "not", TOK_NOT }, /* pseudo option */ @@ -1025,6 +1049,199 @@ 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(" 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"); + /* + * 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(" ipv6 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 }, + { "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 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_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 @@ -1043,6 +1260,7 @@ print_icmptypes(ipfw_insn_u32 *cmd) #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) @@ -1065,6 +1283,9 @@ show_prerequisites(int *flags, int want, int cmd) return; } if ( !(*flags & HAVE_OPTIONS)) { + /* XXX BED: !(*flags & HAVE_PROTO) in patch */ + if ( !(*flags & HAVE_PROTO6) && (want & HAVE_PROTO6)) + printf(" ipv6"); if ( !(*flags & HAVE_PROTO) && (want & HAVE_PROTO)) printf(" ip"); if ( !(*flags & HAVE_SRCIP) && (want & HAVE_SRCIP)) @@ -1329,6 +1550,37 @@ show_ipfw(struct ip_fw *rule, int pcwidth, int bcwidth) 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: + show_prerequisites(&flags, HAVE_PROTO|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; + 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: @@ -1340,14 +1592,15 @@ show_ipfw(struct ip_fw *rule, int pcwidth, int bcwidth) 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; - pe = getprotobynumber(cmd->arg1); + if (proto != 41) /* XXX: IPv6 is special */ + pe = getprotobynumber(cmd->arg1); if (flags & HAVE_OPTIONS) printf(" proto"); if (pe) @@ -1558,6 +1811,18 @@ show_ipfw(struct ip_fw *rule, int pcwidth, int bcwidth) } 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); @@ -1655,29 +1920,48 @@ static void list_queues(struct dn_flow_set *fs, struct dn_flow_queue *q) { int l; + int index_printed, indexes = 0; + char buff[255]; + struct protoent *pe; - 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); + + /* Print IPv4 flows */ + index_printed = 0; for (l = 0; l < fs->rq_elements; l++) { struct in_addr ina; - struct protoent *pe; - ina.s_addr = htonl(q[l].id.src_ip); + /* XXX: Should check for IPv4 flows */ + if (IS_IP6_FLOW_ID(&(q[l].id))) + continue; + + if (!index_printed) { + index_printed = 1; + if (indexes > 0) /* currently a no-op */ + printf("\n"); + indexes++; + 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); + + 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); @@ -1690,6 +1974,50 @@ list_queues(struct dn_flow_set *fs, struct dn_flow_queue *q) printf(" S %20qd F %20qd\n", q[l].S, q[l].F); } + + /* Print IPv6 flows */ + index_printed = 0; + for (l = 0; l < fs->rq_elements; l++) { + if (!IS_IP6_FLOW_ID(&(q[l].id))) + continue; + + if (!index_printed) { + index_printed = 1; + if (indexes > 0) + printf("\n"); + indexes++; + 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 != NULL) + 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); + } } static void @@ -2082,7 +2410,7 @@ list(int ac, char *av[], int show_counters) 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) @@ -2132,19 +2460,24 @@ help(void) "table N {add ip[/bits] [value] | delete ip[/bits] | flush | list}\n" "\n" "RULE-BODY: check-state [PARAMS] | ACTION [PARAMS] ADDR [OPTION_LIST]\n" -"ACTION: check-state | allow | count | deny | reject | skipto N |\n" +"ACTION: check-state | allow | count | deny | unreach CODE | skipto N |\n" " {divert|tee} PORT | forward ADDR | pipe N | queue N\n" "PARAMS: [log [logamount LOGLIMIT]] [altq QUEUE_NAME]\n" "ADDR: [ MAC dst src ether_type ] \n" -" [ from IPADDR [ PORT ] to IPADDR [ PORTLIST ] ]\n" +" [ ip from IPADDR [ PORT ] to IPADDR [ PORTLIST ] ]\n" +" [ ipv6|ip6 from IP6ADDR [ PORT ] to IP6ADDR [ PORTLIST ] ]\n" "IPADDR: [not] { any | me | ip/bits{x,y,z} | table(t[,v]) | IPLIST }\n" +"IP6ADDR: [not] { any | me | me6 | ip6/bits | IP6LIST }\n" +"IP6LIST: { ip6 | ip6/bits }[,IP6LIST]\n" "IPLIST: { ip | ip/bits | ip:mask }[,IPLIST]\n" "OPTION_LIST: OPTION [OPTION_LIST]\n" "OPTION: bridged | diverted | diverted-loopback | diverted-output |\n" -" {dst-ip|src-ip} ADDR | {dst-port|src-port} LIST |\n" +" {dst-ip|src-ip} IPADDR | {dst-ip6|src-ip6|dst-ipv6|src-ipv6} IP6ADDR |\n" +" {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" +" icmp6types LIST | ext6hdr LIST | flow-id N[,N] |\n" " mac ... | mac-type LIST | proto LIST | {recv|xmit|via} {IF|IPADDR} |\n" " setup | {tcpack|tcpseq|tcpwin} NN | tcpflags SPEC | tcpoptions SPEC |\n" " tcpdatalen LIST | verrevpath | versrcreach | antispoof\n" @@ -2170,7 +2503,6 @@ lookup_host (char *host, struct in_addr *ipaddr) * 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 IP. Actually returns an empty instruction. * me returns O_IP_*_ME * 1.2.3.4 single IP address * 1.2.3.4:5.6.7.8 address:mask @@ -2357,6 +2689,231 @@ 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 */ +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; + + memset(mask, 0, sizeof(struct in6_addr)); + p = (u_char *) mask; + for (; n > 0; p++, n -= 8) { + if (n >= 8) + *p = 0xff; + else + *p = minimask[n]; + } + return; +} + + +/* + * 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. + */ + 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 */ + ; + 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 */ + ; + 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. @@ -2468,7 +3025,6 @@ config_pipe(int ac, char **av) struct dn_pipe p; int i; char *end; - uint32_t a; void *par = NULL; memset(&p, 0, sizeof p); @@ -2531,16 +3087,15 @@ config_pipe(int ac, char **av) */ 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; tok = match_token(dummynet_params, *av); ac--; av++; @@ -2554,6 +3109,9 @@ config_pipe(int ac, char **av) 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; @@ -2565,6 +3123,18 @@ config_pipe(int ac, char **av) 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; @@ -2584,7 +3154,8 @@ config_pipe(int ac, char **av) errx(EX_USAGE, "mask: value missing"); if (*av[0] == '/') { a = strtoul(av[0]+1, &end, 0); - a = (a == 32) ? ~0 : (1 << a) - 1; + if (pa6 == NULL) + a = (a == 32) ? ~0 : (1 << a) - 1; } else a = strtoul(av[0], &end, 0); if (p32 != NULL) @@ -2594,6 +3165,17 @@ config_pipe(int ac, char **av) errx(EX_DATAERR, "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 > 0xFF) errx(EX_DATAERR, @@ -2918,21 +3500,25 @@ add_mactype(ipfw_insn *cmd, int ac, char *av) } 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 (_substrcmp(av, "all") == 0) ; /* 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 (strcmp(av, "ipv6") == 0 || strcmp(av, "ip6")) + *proto = IPPROTO_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; } @@ -2983,6 +3569,42 @@ add_ports(ipfw_insn *cmd, char *av, u_char proto, int opcode) return NULL; } +static ipfw_insn * +add_src(ipfw_insn *cmd, char *av, u_char proto) +{ + struct in6_addr a; + + if (proto == IPPROTO_IPV6 || strcmp(av, "me6") == 0 || + inet_pton(AF_INET6, av, &a)) + return add_srcip6(cmd, av); + /* XXX: should check for IPv4, not !IPv6 */ + if (proto == IPPROTO_IP || strcmp(av, "me") == 0 || + !inet_pton(AF_INET6, av, &a)) + return add_srcip(cmd, av); + if (strcmp(av, "any") != 0) + return cmd; + + return NULL; +} + +static ipfw_insn * +add_dst(ipfw_insn *cmd, char *av, u_char proto) +{ + struct in6_addr a; + + if (proto == IPPROTO_IPV6 || strcmp(av, "me6") == 0 || + inet_pton(AF_INET6, av, &a)) + return add_dstip6(cmd, av); + /* XXX: should check for IPv4, not !IPv6 */ + if (proto == IPPROTO_IP || strcmp(av, "me") == 0 || + !inet_pton(AF_INET6, av, &a)) + return add_dstip(cmd, av); + if (strcmp(av, "any") != 0) + return cmd; + + return NULL; +} + /* * Parse arguments and assemble the microinstructions which make up a rule. * Rules are added into the 'rulebuf' and then copied in the correct order @@ -3327,7 +3949,7 @@ add(int ac, char *av[]) OR_START(get_proto); NOT_BLOCK; NEED1("missing protocol"); - if (add_proto(cmd, *av)) { + if (add_proto(cmd, *av, &proto)) { av++; ac--; if (F_LEN(cmd) == 0) /* plain IP */ proto = 0; @@ -3355,13 +3977,14 @@ add(int ac, char *av[]) OR_START(source_ip); NOT_BLOCK; /* optional "not" */ NEED1("missing source address"); - if (add_srcip(cmd, *av)) { + if (add_src(cmd, *av, proto)) { 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); /* @@ -3390,13 +4013,14 @@ add(int ac, char *av[]) OR_START(dest_ip); NOT_BLOCK; /* optional "not" */ NEED1("missing dst address"); - if (add_dstip(cmd, *av)) { + if (add_dst(cmd, *av, proto)) { 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); /* @@ -3513,6 +4137,12 @@ add(int ac, char *av[]) fill_icmptypes((ipfw_insn_u32 *)cmd, *av); 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"); @@ -3717,8 +4347,9 @@ add(int ac, char *av[]) 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''", @@ -3739,6 +4370,20 @@ add(int ac, char *av[]) } 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 (_substrcmp(*av, "any") == 0 || @@ -3787,6 +4432,24 @@ add(int ac, char *av[]) fill_cmd(cmd, O_IPSEC, 0, 0); break; + case TOK_IPV6: + fill_cmd(cmd, O_IP6, 0, 0); + 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; + case TOK_COMMENT: fill_comment(cmd, ac, av); av += ac; diff --git a/sys/netinet/ip_dummynet.c b/sys/netinet/ip_dummynet.c index 0a93a2f0c9f1..ccea5ea6d891 100644 --- a/sys/netinet/ip_dummynet.c +++ b/sys/netinet/ip_dummynet.c @@ -77,6 +77,9 @@ #include /* for struct arpcom */ #include +#include /* for ip6_input, ip6_output prototypes */ +#include + /* * We keep a private variable for the simulation time, but we could * probably use an existing one ("softticks" in sys/kern/kern_timeout.c) @@ -461,6 +464,14 @@ transmit_event(struct dn_pipe *pipe) ip_input(m) ; break ; + case DN_TO_IP6_IN: + ip6_input(m) ; + break ; + + case DN_TO_IP6_OUT: + (void)ip6_output(m, NULL, NULL, pkt->flags, NULL, NULL, NULL); + break ; + case DN_TO_BDG_FWD : /* * The bridge requires/assumes the Ethernet header is @@ -898,37 +909,80 @@ find_queue(struct dn_flow_set *fs, struct ipfw_flow_id *id) { 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 */ - 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 ); + 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 ; @@ -1200,8 +1254,9 @@ dummynet_io(struct mbuf *m, int dir, struct ip_fw_args *fwa) pkt->dn_dir = dir ; pkt->ifp = fwa->oif; - if (dir == DN_TO_IP_OUT) + if (dir == DN_TO_IP_OUT || dir == DN_TO_IP6_OUT) pkt->flags = fwa->flags; + if (q->head == NULL) q->head = m; else @@ -2015,7 +2070,7 @@ static void ip_dn_init(void) { if (bootverbose) - printf("DUMMYNET initialized (011031)\n"); + printf("DUMMYNET with IPv6 initialized (040826)\n"); DUMMYNET_LOCK_INIT(); diff --git a/sys/netinet/ip_dummynet.h b/sys/netinet/ip_dummynet.h index d9c6aaa70396..947f0816a9c7 100644 --- a/sys/netinet/ip_dummynet.h +++ b/sys/netinet/ip_dummynet.h @@ -124,10 +124,13 @@ struct dn_pkt_tag { #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 */ int flags ; /* flags, for ip_output (IPv6 ?) */ + struct _ip6dn_args ip6opt; /* XXX ipv6 options */ }; #endif /* _KERNEL */ diff --git a/sys/netinet/ip_fw.h b/sys/netinet/ip_fw.h index 0da6f43c5a11..5b196d6bf38c 100644 --- a/sys/netinet/ip_fw.h +++ b/sys/netinet/ip_fw.h @@ -137,6 +137,16 @@ enum ipfw_opcodes { /* arguments (4 byte each) */ O_ALTQ, /* u32 = altq classif. qid */ O_DIVERTED, /* arg1=bitmap (1:loop, 2:out) */ O_TCPDATALEN, /* arg1 = tcp data len */ + 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, /* * actions for ng_ipfw @@ -147,6 +157,16 @@ enum ipfw_opcodes { /* arguments (4 byte each) */ 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. * @@ -291,6 +311,30 @@ typedef struct _ipfw_insn_log { 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 */ +typedef struct _ipfw_insn_icmp6 { + ipfw_insn o; + uint32_t d[7]; /* XXX This number si related to the netinet/icmp6.h + * define ICMP6_MAXTYPE + * as follows: n = ICMP6_MAXTYPE/32 + 1 + * Actually is 203 + */ +} ipfw_insn_icmp6; + /* * Here we have the structure representing an ipfw rule. * @@ -354,8 +398,14 @@ struct ipfw_flow_id { 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 ? */ + 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. */ @@ -438,6 +488,21 @@ enum { #define IP_FW_DIVERT_LOOPBACK_FLAG 0x00080000 #define IP_FW_DIVERT_OUTPUT_FLAG 0x00100000 +/* + * Structure for collecting parameters to dummynet for ip6_output forwarding + */ +struct _ip6dn_args { + struct ip6_pktopts *opt_or; + struct route_in6 ro_or; + int flags_or; + struct ip6_moptions *im6o_or; + struct ifnet *origifp_or; + struct ifnet *ifp_or; + struct sockaddr_in6 dst_or; + u_long mtu_or; + struct route_in6 ro_pmtu_or; +}; + /* * Arguments for calling ipfw_chk() and dummynet_io(). We put them * all into a structure because this way it is easier and more @@ -455,6 +520,8 @@ struct ip_fw_args { struct ipfw_flow_id f_id; /* grabbed from IP header */ u_int32_t cookie; /* a cookie depending on rule action */ struct inpcb *inp; + + struct _ip6dn_args dummypar; /* dummynet->ip6_output */ }; /* diff --git a/sys/netinet/ip_fw2.c b/sys/netinet/ip_fw2.c index 563ac5220ff1..97f27ca2d338 100644 --- a/sys/netinet/ip_fw2.c +++ b/sys/netinet/ip_fw2.c @@ -86,6 +86,9 @@ #include #endif +#include +#include + #include /* XXX for ETHERTYPE_IP */ #include /* XXX for in_cksum */ @@ -325,6 +328,7 @@ SYSCTL_INT(_net_inet_ip_fw, OID_AUTO, dyn_keepalive, CTLFLAG_RW, #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 icmp *icmp, ipfw_insn_u32 *cmd) @@ -555,6 +559,83 @@ verify_path(struct in_addr src, struct ifnet *ifp) 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); + } + 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...) */ @@ -773,7 +854,8 @@ hash_packet(struct ipfw_flow_id *id) { 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; } @@ -912,19 +994,40 @@ lookup_dyn_rule_locked(struct ipfw_flow_id *pkt, int *match_direction, } if (pkt->proto == q->id.proto && q->dyn_type != O_LIMIT_PARENT) { - if (pkt->src_ip == q->id.src_ip && - pkt->dst_ip == q->id.dst_ip && + 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 (pkt->src_ip == q->id.dst_ip && - pkt->dst_ip == q->id.src_ip && - pkt->src_port == q->id.dst_port && - pkt->dst_port == q->id.src_port ) { - dir = MATCH_REVERSE; - 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 && + pkt->dst_port == q->id.dst_port ) { + dir = MATCH_FORWARD; + break; + } + if (pkt->src_ip == q->id.dst_ip && + pkt->dst_ip == q->id.src_ip && + pkt->src_port == q->id.dst_port && + pkt->dst_port == q->id.src_port ) { + dir = MATCH_REVERSE; + break; + } } } next: @@ -1122,15 +1225,25 @@ lookup_dyn_parent(struct ipfw_flow_id *pkt, struct ip_fw *rule) IPFW_DYN_LOCK_ASSERT(); if (ipfw_dyn_v) { + 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; @@ -1204,10 +1317,17 @@ install_state(struct ip_fw *rule, ipfw_insn_limit *cmd, id.dst_port = id.src_port = 0; id.proto = args->f_id.proto; - 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 (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) @@ -1831,6 +1951,10 @@ ipfw_chk(struct ip_fw_args *args) * 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 (IP_FW_PASS); /* accept */ @@ -1855,15 +1979,95 @@ do { \ p = (mtod(m, char *) + (len)); \ } while (0) - /* Identify IP packets and fill up veriables. */ - if (pktlen >= sizeof(struct ip) && + /* Identify IP packets and fill up variables. */ + if (pktlen >= sizeof(struct ip6_hdr) && + (args->eh == NULL || 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; + + /* Search extension headers to find upper layer protocols */ + while (ulp == NULL) { + switch (proto) { + case IPPROTO_ICMPV6: + PULLUP_TO(hlen, ulp, struct icmp6_hdr); + args->f_id.flags = ICMP6(ulp)->icmp6_type; + break; + + case IPPROTO_TCP: + PULLUP_TO(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: + PULLUP_TO(hlen, ulp, struct udphdr); + dst_port = UDP(ulp)->uh_dport; + src_port = UDP(ulp)->uh_sport; + break; + + case IPPROTO_HOPOPTS: + PULLUP_TO(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: + PULLUP_TO(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: + PULLUP_TO(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: + PULLUP_TO(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 */ + } + 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); + /* hlen != 0 is used to detect ipv4 packets, so clear it now */ + hlen = 0; + } else if (pktlen >= sizeof(struct ip) && (args->eh == NULL || ntohs(args->eh->ether_type) == ETHERTYPE_IP) && mtod(m, struct ip *)->ip_v == 4) { ip = mtod(m, struct ip *); hlen = ip->ip_hl << 2; -#ifdef NOTYET args->f_id.addr_type = 4; -#endif /* * Collect parameters into local variables for faster matching. @@ -2027,11 +2231,13 @@ do { \ case O_JAIL: /* * We only check offset == 0 && proto != 0, - * as this ensures that we have an IPv4 + * as this ensures that we have a * packet with the ports info. */ if (offset!=0) break; + if (is_ipv6) /* XXX to be fixed later */ + break; if (proto == IPPROTO_TCP || proto == IPPROTO_UDP) match = check_uidgid( @@ -2086,7 +2292,7 @@ do { \ break; case O_FRAG: - match = (hlen > 0 && offset != 0); + match = (offset != 0); break; case O_IN: /* "out" is "not in" */ @@ -2195,7 +2401,7 @@ do { \ 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) @@ -2218,12 +2424,22 @@ do { \ icmptype_match(ICMP(ulp), (ipfw_insn_u32 *)cmd) ); break; + case O_ICMP6TYPE: + match = is_ipv6 && offset == 0 && + proto==IPPROTO_ICMPV6 && + icmp6type_match( + ICMP6(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: @@ -2237,9 +2453,9 @@ do { \ 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; @@ -2254,12 +2470,12 @@ do { \ 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_TCPDATALEN: @@ -2357,8 +2573,12 @@ do { \ case O_VERREVPATH: /* Outgoing packets automatically pass/match */ - match = (hlen > 0 && ((oif != NULL) || + /* XXX BED: verify_path was verify_rev_path in the diff... */ + match = ((oif != NULL) || (m->m_pkthdr.rcvif == NULL) || + (is_ipv6 ? + verify_rev_path6(&(args->f_id.src_ip6), + m->m_pkthdr.rcvif) : verify_path(src_ip, m->m_pkthdr.rcvif))); break; @@ -2389,6 +2609,60 @@ do { \ /* 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 @@ -3030,6 +3304,10 @@ check_ipfw_struct(struct ip_fw *rule, int size) case O_VERSRCREACH: case O_ANTISPOOF: 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; @@ -3177,6 +3455,29 @@ check_ipfw_struct(struct ip_fw *rule, int size) 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); @@ -3577,7 +3878,7 @@ ipfw_init(void) } ip_fw_default_rule = layer3_chain.rules; - printf("ipfw2 initialized, divert %s, " + printf("ipfw2 (+ipv6) initialized, divert %s, " "rule-based forwarding " #ifdef IPFIREWALL_FORWARD "enabled, " diff --git a/sys/netinet/ip_fw_pfil.c b/sys/netinet/ip_fw_pfil.c index 10a01eaa60f2..ab1ff5596d1c 100644 --- a/sys/netinet/ip_fw_pfil.c +++ b/sys/netinet/ip_fw_pfil.c @@ -30,6 +30,7 @@ #include "opt_ipfw.h" #include "opt_ipdn.h" #include "opt_inet.h" +#include "opt_inet6.h" #ifndef INET #error IPFIREWALL requires INET. #endif /* INET */ @@ -155,7 +156,10 @@ ipfw_check_in(void *arg, struct mbuf **m0, struct ifnet *ifp, int dir, case IP_FW_DUMMYNET: if (!DUMMYNET_LOADED) goto drop; - ip_dn_io_ptr(*m0, DN_TO_IP_IN, &args); + if (mtod(*m0, struct ip *)->ip_v == 4) + ip_dn_io_ptr(*m0, DN_TO_IP_IN, &args); + else if (mtod(*m0, struct ip *)->ip_v == 6) + ip_dn_io_ptr(*m0, DN_TO_IP6_IN, &args); *m0 = NULL; return 0; /* packet consumed */ @@ -278,7 +282,10 @@ ipfw_check_out(void *arg, struct mbuf **m0, struct ifnet *ifp, int dir, case IP_FW_DUMMYNET: if (!DUMMYNET_LOADED) break; - ip_dn_io_ptr(*m0, DN_TO_IP_OUT, &args); + if (mtod(*m0, struct ip *)->ip_v == 4) + ip_dn_io_ptr(*m0, DN_TO_IP_OUT, &args); + else if (mtod(*m0, struct ip *)->ip_v == 6) + ip_dn_io_ptr(*m0, DN_TO_IP6_OUT, &args); *m0 = NULL; return 0; /* packet consumed */ @@ -410,6 +417,9 @@ static int ipfw_hook(void) { struct pfil_head *pfh_inet; +#ifdef INET6 + struct pfil_head *pfh_inet6; +#endif if (ipfw_pfil_hooked) return EEXIST; @@ -417,9 +427,18 @@ ipfw_hook(void) pfh_inet = pfil_head_get(PFIL_TYPE_AF, AF_INET); if (pfh_inet == NULL) return ENOENT; +#ifdef INET6 + pfh_inet6 = pfil_head_get(PFIL_TYPE_AF, AF_INET6); + if (pfh_inet6 == NULL) + return ENOENT; +#endif pfil_add_hook(ipfw_check_in, NULL, PFIL_IN | PFIL_WAITOK, pfh_inet); pfil_add_hook(ipfw_check_out, NULL, PFIL_OUT | PFIL_WAITOK, pfh_inet); +#ifdef INET6 + pfil_add_hook(ipfw_check_in, NULL, PFIL_IN | PFIL_WAITOK, pfh_inet6); + pfil_add_hook(ipfw_check_out, NULL, PFIL_OUT | PFIL_WAITOK, pfh_inet6); +#endif return 0; } @@ -428,6 +447,9 @@ static int ipfw_unhook(void) { struct pfil_head *pfh_inet; +#ifdef INET6 + struct pfil_head *pfh_inet6; +#endif if (!ipfw_pfil_hooked) return ENOENT; @@ -435,9 +457,18 @@ ipfw_unhook(void) pfh_inet = pfil_head_get(PFIL_TYPE_AF, AF_INET); if (pfh_inet == NULL) return ENOENT; +#ifdef INET6 + pfh_inet6 = pfil_head_get(PFIL_TYPE_AF, AF_INET6); + if (pfh_inet6 == NULL) + return ENOENT; +#endif pfil_remove_hook(ipfw_check_in, NULL, PFIL_IN | PFIL_WAITOK, pfh_inet); pfil_remove_hook(ipfw_check_out, NULL, PFIL_OUT | PFIL_WAITOK, pfh_inet); +#ifdef INET6 + pfil_remove_hook(ipfw_check_in, NULL, PFIL_IN | PFIL_WAITOK, pfh_inet6); + pfil_remove_hook(ipfw_check_out, NULL, PFIL_OUT | PFIL_WAITOK, pfh_inet6); +#endif return 0; } diff --git a/sys/netinet6/ip6_output.c b/sys/netinet6/ip6_output.c index 30f5959f5fc6..36cfb289f50b 100644 --- a/sys/netinet6/ip6_output.c +++ b/sys/netinet6/ip6_output.c @@ -78,6 +78,7 @@ #include #include +#include #include #include @@ -167,6 +168,7 @@ ip6_output(m0, opt, ro, flags, im6o, ifpp, inp) int hlen, tlen, len, off; struct route_in6 ip6route; struct sockaddr_in6 *dst; + struct in6_addr odst; int error = 0; struct in6_ifaddr *ia = NULL; u_long mtu; @@ -524,6 +526,7 @@ skip_ipsec2:; ro = &opt->ip6po_route; dst = (struct sockaddr_in6 *)&ro->ro_dst; +again: /* * If there is a cached route, * check that it is to the same destination @@ -937,12 +940,35 @@ skip_ipsec2:; if (inet6_pfil_hook.ph_busy_count == -1) goto passout; + odst = ip6->ip6_dst; /* Run through list of hooks for output packets. */ error = pfil_run_hooks(&inet6_pfil_hook, &m, ifp, PFIL_OUT, inp); if (error != 0 || m == NULL) goto done; ip6 = mtod(m, struct ip6_hdr *); + /* See if destination IP address was changed by packet filter. */ + if (!IN6_ARE_ADDR_EQUAL(&odst, &ip6->ip6_dst)) { + m->m_flags |= M_SKIP_FIREWALL; + /* If destination is now ourself drop to ip6_input(). */ + if (in6_localaddr(&ip6->ip6_dst)) { + if (m->m_pkthdr.rcvif == NULL) + m->m_pkthdr.rcvif = loif; + if (m->m_pkthdr.csum_flags & CSUM_DELAY_DATA) { + m->m_pkthdr.csum_flags |= + CSUM_DATA_VALID | CSUM_PSEUDO_HDR; + m->m_pkthdr.csum_data = 0xffff; + } + m->m_pkthdr.csum_flags |= + CSUM_IP_CHECKED | CSUM_IP_VALID; + error = netisr_queue(NETISR_IPV6, m); + goto done; + } else + goto again; /* Redo the routing table lookup. */ + } + + /* XXX: IPFIREWALL_FORWARD */ + passout: /* * Send the packet to the outgoing interface.