diff --git a/sbin/ipfw/ipfw.8 b/sbin/ipfw/ipfw.8 index 5754936f393f..2765f6b3f795 100644 --- a/sbin/ipfw/ipfw.8 +++ b/sbin/ipfw/ipfw.8 @@ -1,7 +1,7 @@ .\" .\" $FreeBSD$ .\" -.Dd June 30, 2005 +.Dd August 13, 2005 .Dt IPFW 8 .Os .Sh NAME @@ -710,6 +710,10 @@ Synonym for Discard packets that match this rule, and if the packet is a TCP packet, try to send a TCP reset (RST) notice. The search terminates. +.It Cm reset6 +Discard packets that match this rule, and if the +packet is a TCP packet, try to send a TCP reset (RST) notice. +The search terminates. .It Cm skipto Ar number Skip all subsequent rules numbered less than .Ar number . @@ -736,6 +740,17 @@ is a number from 0 to 255, or one of these aliases: or .Cm precedence-cutoff . The search terminates. +.It Cm unreach6 Ar code +Discard packets that match this rule, and try to send an ICMPv6 +unreachable notice with code +.Ar code , +where +.Ar code +is a number from 0, 1, 3 or 4, or one of these aliases: +.Cm no-route, admin-prohib, address +or +.Cm port . +The search terminates. .It Cm netgraph Ar cookie Divert packet into netgraph with given .Ar cookie . @@ -1036,6 +1051,8 @@ Hop-to-hop options .Pq Cm hopopt , Source routing .Pq Cm route , +Destination options +.Pq Cm dstopt , IPSec authentication headers .Pq Cm ah , and IPSec encapsulated security payload headers @@ -2018,6 +2035,8 @@ reinjected into the firewall at the next rule. Enables verbose messages. .It Em net.inet.ip.fw.verbose_limit : No 0 Limits the number of messages produced by a verbose firewall. +.It Em net.inet6.ip6.fw.deny_unknown_exthdrs : No 1 +If enabled packets with unknown IPv6 Extension Headers will be denied. .It Em net.link.ether.ipfw : No 0 Controls whether layer-2 packets are passed to .Nm . diff --git a/sbin/ipfw/ipfw2.c b/sbin/ipfw/ipfw2.c index 686174d207e1..ee1b3628e9e0 100644 --- a/sbin/ipfw/ipfw2.c +++ b/sbin/ipfw/ipfw2.c @@ -277,6 +277,8 @@ enum tokens { TOK_SRCIP6, TOK_IPV4, + TOK_UNREACH6, + TOK_RESET6, }; struct _s_x dummynet_params[] = { @@ -326,7 +328,9 @@ struct _s_x rule_actions[] = { { "deny", TOK_DENY }, { "drop", TOK_DENY }, { "reject", TOK_REJECT }, + { "reset6", TOK_RESET6 }, { "reset", TOK_RESET }, + { "unreach6", TOK_UNREACH6 }, { "unreach", TOK_UNREACH }, { "check-state", TOK_CHECKSTATE }, { "//", TOK_COMMENT }, @@ -851,6 +855,40 @@ print_reject_code(uint16_t code) printf("unreach %u", code); } +static struct _s_x icmp6codes[] = { + { "no-route", ICMP6_DST_UNREACH_NOROUTE }, + { "admin-prohib", ICMP6_DST_UNREACH_ADMIN }, + { "address", ICMP6_DST_UNREACH_ADDR }, + { "port", ICMP6_DST_UNREACH_NOPORT }, + { NULL, 0 } +}; + +static void +fill_unreach6_code(u_short *codep, char *str) +{ + int val; + char *s; + + val = strtoul(str, &s, 0); + if (s == str || *s != '\0' || val >= 0x100) + val = match_token(icmp6codes, str); + if (val < 0) + errx(EX_DATAERR, "unknown ICMPv6 unreachable code ``%s''", str); + *codep = val; + return; +} + +static void +print_unreach6_code(uint16_t code) +{ + char const *s = match_value(icmp6codes, code); + + if (s != NULL) + printf("unreach6 %s", s); + else + printf("unreach6 %u", code); +} + /* * Returns the number of bits set (from left) in a contiguous bitmask, * or -1 if the mask is not contiguous. @@ -1169,6 +1207,7 @@ static struct _s_x ext6hdrcodes[] = { { "frag", EXT_FRAGMENT }, { "hopopt", EXT_HOPOPTS }, { "route", EXT_ROUTING }, + { "dstopt", EXT_DSTOPTS }, { "ah", EXT_AH }, { "esp", EXT_ESP }, { NULL, 0 } @@ -1199,6 +1238,10 @@ fill_ext6hdr( ipfw_insn *cmd, char *av) cmd->arg1 |= EXT_ROUTING; break; + case EXT_DSTOPTS: + cmd->arg1 |= EXT_DSTOPTS; + break; + case EXT_AH: cmd->arg1 |= EXT_AH; break; @@ -1237,6 +1280,10 @@ print_ext6hdr( ipfw_insn *cmd ) printf("%crouting options", sep); sep = ','; } + if (cmd->arg1 & EXT_DSTOPTS ) { + printf("%cdestination options", sep); + sep = ','; + } if (cmd->arg1 & EXT_AH ) { printf("%cauthentication header", sep); sep = ','; @@ -1406,6 +1453,13 @@ show_ipfw(struct ip_fw *rule, int pcwidth, int bcwidth) print_reject_code(cmd->arg1); break; + case O_UNREACH6: + if (cmd->arg1 == ICMP6_UNREACH_RST) + printf("reset6"); + else + print_unreach6_code(cmd->arg1); + break; + case O_SKIPTO: printf("skipto %u", cmd->arg1); break; @@ -2495,8 +2549,9 @@ 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 | unreach CODE | skipto N |\n" -" {divert|tee} PORT | forward ADDR | pipe N | queue N\n" +"ACTION: check-state | allow | count | deny | unreach{,6} CODE |\n" +" skipto N | {divert|tee} PORT | forward ADDR |\n" +" pipe N | queue N\n" "PARAMS: [log [logamount LOGLIMIT]] [altq QUEUE_NAME]\n" "ADDR: [ MAC dst src ether_type ] \n" " [ ip from IPADDR [ PORT ] to IPADDR [ PORTLIST ] ]\n" @@ -3754,6 +3809,11 @@ add(int ac, char *av[]) action->arg1 = ICMP_REJECT_RST; break; + case TOK_RESET6: + action->opcode = O_UNREACH6; + action->arg1 = ICMP6_UNREACH_RST; + break; + case TOK_UNREACH: action->opcode = O_REJECT; NEED1("missing reject code"); @@ -3761,6 +3821,13 @@ add(int ac, char *av[]) ac--; av++; break; + case TOK_UNREACH6: + action->opcode = O_UNREACH6; + NEED1("missing unreach code"); + fill_unreach6_code(&action->arg1, *av); + ac--; av++; + break; + case TOK_COUNT: action->opcode = O_COUNT; break; diff --git a/sys/netinet/ip_fw.h b/sys/netinet/ip_fw.h index 5c5fd6e524d2..88c13bde31b7 100644 --- a/sys/netinet/ip_fw.h +++ b/sys/netinet/ip_fw.h @@ -155,6 +155,8 @@ enum ipfw_opcodes { /* arguments (4 byte each) */ O_IP4, + O_UNREACH6, /* arg1=icmpv6 code arg (deny) */ + O_LAST_OPCODE /* not an opcode! */ }; @@ -167,6 +169,7 @@ enum ipfw_opcodes { /* arguments (4 byte each) */ #define EXT_ROUTING 0x4 #define EXT_AH 0x8 #define EXT_ESP 0x10 +#define EXT_DSTOPTS 0x20 /* * Template for instructions. @@ -403,6 +406,7 @@ struct ipfw_flow_id { struct in6_addr dst_ip6; /* could also store MAC addr! */ struct in6_addr src_ip6; u_int32_t flow_id6; + u_int32_t frag_id6; }; #define IS_IP6_FLOW_ID(id) ((id)->addr_type == 6) @@ -451,6 +455,7 @@ struct _ipfw_dyn_rule { #define IP_FW_TCPOPT_CC 0x10 #define ICMP_REJECT_RST 0x100 /* fake ICMP code (send a TCP RST) */ +#define ICMP6_UNREACH_RST 0x100 /* fake ICMPv6 code (send a TCP RST) */ /* * These are used for lookup tables. diff --git a/sys/netinet/ip_fw2.c b/sys/netinet/ip_fw2.c index 807f48d2cef9..63d81f79a061 100644 --- a/sys/netinet/ip_fw2.c +++ b/sys/netinet/ip_fw2.c @@ -34,6 +34,7 @@ #if !defined(KLD_MODULE) #include "opt_ipfw.h" +#include "opt_ip6fw.h" #include "opt_ipdn.h" #include "opt_inet.h" #include "opt_inet6.h" @@ -322,6 +323,15 @@ SYSCTL_INT(_net_inet_ip_fw, OID_AUTO, dyn_short_lifetime, CTLFLAG_RW, SYSCTL_INT(_net_inet_ip_fw, OID_AUTO, dyn_keepalive, CTLFLAG_RW, &dyn_keepalive, 0, "Enable keepalives for dyn. rules"); +/* + * IPv6 specific variables + */ +SYSCTL_DECL(_net_inet6_ip6); + +static struct sysctl_ctx_list ip6_fw_sysctl_ctx; +static struct sysctl_oid *ip6_fw_sysctl_tree; + +static int fw_deny_unknown_exthdrs = 1; #endif /* SYSCTL_NODE */ @@ -662,7 +672,88 @@ hash_packet6(struct ipfw_flow_id *id) (id->dst_port) ^ (id->src_port) ^ (id->flow_id6); return i; } -/* end of ipv6 opcodes */ + +static int +is_icmp6_query(int icmp6_type) +{ + if ((icmp6_type <= ICMP6_MAXTYPE) && + (icmp6_type == ICMP6_ECHO_REQUEST || + icmp6_type == ICMP6_MEMBERSHIP_QUERY || + icmp6_type == ICMP6_WRUREQUEST || + icmp6_type == ICMP6_FQDN_QUERY || + icmp6_type == ICMP6_NI_QUERY)) + return (1); + + return (0); +} + +static void +send_reject6(struct ip_fw_args *args, int code, u_short offset, u_int hlen) +{ + if (code == ICMP6_UNREACH_RST && offset == 0 && + args->f_id.proto == IPPROTO_TCP) { + struct ip6_hdr *ip6; + struct tcphdr *tcp; + tcp_seq ack, seq; + int flags; + struct { + struct ip6_hdr ip6; + struct tcphdr th; + } ti; + + if (args->m->m_len < (hlen+sizeof(struct tcphdr))) { + args->m = m_pullup(args->m, hlen+sizeof(struct tcphdr)); + if (args->m == NULL) + return; + } + + ip6 = mtod(args->m, struct ip6_hdr *); + tcp = (struct tcphdr *)(mtod(args->m, char *) + hlen); + + if ((tcp->th_flags & TH_RST) != 0) { + m_freem(args->m); + return; + } + + ti.ip6 = *ip6; + ti.th = *tcp; + ti.th.th_seq = ntohl(ti.th.th_seq); + ti.th.th_ack = ntohl(ti.th.th_ack); + ti.ip6.ip6_nxt = IPPROTO_TCP; + + if (ti.th.th_flags & TH_ACK) { + ack = 0; + seq = ti.th.th_ack; + flags = TH_RST; + } else { + ack = ti.th.th_seq; + if (((args->m)->m_flags & M_PKTHDR) != 0) { + ack += (args->m)->m_pkthdr.len - hlen + - (ti.th.th_off << 2); + } else if (ip6->ip6_plen) { + ack += ntohs(ip6->ip6_plen) + sizeof(*ip6) + - hlen - (ti.th.th_off << 2); + } else { + m_freem(args->m); + return; + } + if (tcp->th_flags & TH_SYN) + ack++; + seq = 0; + flags = TH_RST|TH_ACK; + } + bcopy(&ti, ip6, sizeof(ti)); + tcp_respond(NULL, ip6, (struct tcphdr *)(ip6 + 1), + args->m, ack, seq, flags); + + } else if (code != ICMP6_UNREACH_RST) { /* Send an ICMPv6 unreach. */ + icmp6_error(args->m, ICMP6_DST_UNREACH, code, 0); + + } else + m_freem(args->m); + + args->m = NULL; +} #endif /* INET6 */ @@ -676,12 +767,13 @@ static u_int64_t norule_counter; /* counter for ipfw_log(NULL...) */ * XXX this function alone takes about 2Kbytes of code! */ static void -ipfw_log(struct ip_fw *f, u_int hlen, struct ether_header *eh, - struct mbuf *m, struct ifnet *oif) +ipfw_log(struct ip_fw *f, u_int hlen, struct ip_fw_args *args, + struct mbuf *m, struct ifnet *oif, u_short offset) { + struct ether_header *eh = args->eh; char *action; int limit_reached = 0; - char action2[40], proto[48], fragment[28]; + char action2[40], proto[128], fragment[32]; fragment[0] = '\0'; proto[0] = '\0'; @@ -729,6 +821,14 @@ ipfw_log(struct ip_fw *f, u_int hlen, struct ether_header *eh, cmd->arg1); break; + case O_UNREACH6: + if (cmd->arg1==ICMP6_UNREACH_RST) + action = "Reset"; + else + snprintf(SNPARGS(action2, 0), "Unreach %d", + cmd->arg1); + break; + case O_ACCEPT: action = "Accept"; break; @@ -782,78 +882,123 @@ ipfw_log(struct ip_fw *f, u_int hlen, struct ether_header *eh, if (hlen == 0) { /* non-ip */ snprintf(SNPARGS(proto, 0), "MAC"); + } else { - struct ip *ip = mtod(m, struct ip *); - /* these three are all aliases to the same thing */ - struct icmphdr *const icmp = L3HDR(struct icmphdr, ip); - struct tcphdr *const tcp = (struct tcphdr *)icmp; - struct udphdr *const udp = (struct udphdr *)icmp; - - int ip_off, offset, ip_len; - int len; + char src[48], dst[48]; + struct icmphdr *icmp; + struct tcphdr *tcp; + struct udphdr *udp; + /* Initialize to make compiler happy. */ + struct ip *ip = NULL; +#ifdef INET6 + struct ip6_hdr *ip6 = NULL; + struct icmp6_hdr *icmp6; +#endif + src[0] = '\0'; + dst[0] = '\0'; +#ifdef INET6 + if (args->f_id.addr_type == 6) { + snprintf(src, sizeof(src), "[%s]", + ip6_sprintf(&args->f_id.src_ip6)); + snprintf(dst, sizeof(dst), "[%s]", + ip6_sprintf(&args->f_id.dst_ip6)); - if (eh != NULL) { /* layer 2 packets are as on the wire */ - ip_off = ntohs(ip->ip_off); - ip_len = ntohs(ip->ip_len); - } else { - ip_off = ip->ip_off; - ip_len = ip->ip_len; + ip6 = (struct ip6_hdr *)mtod(m, struct ip6_hdr *); + tcp = (struct tcphdr *)(mtod(args->m, char *) + hlen); + udp = (struct udphdr *)(mtod(args->m, char *) + hlen); + } else +#endif + { + ip = mtod(m, struct ip *); + tcp = L3HDR(struct tcphdr, ip); + udp = L3HDR(struct udphdr, ip); + + inet_ntoa_r(ip->ip_src, src); + inet_ntoa_r(ip->ip_dst, dst); } - offset = ip_off & IP_OFFMASK; - switch (ip->ip_p) { + + switch (args->f_id.proto) { case IPPROTO_TCP: - len = snprintf(SNPARGS(proto, 0), "TCP %s", - inet_ntoa(ip->ip_src)); + len = snprintf(SNPARGS(proto, 0), "TCP %s", src); if (offset == 0) snprintf(SNPARGS(proto, len), ":%d %s:%d", ntohs(tcp->th_sport), - inet_ntoa(ip->ip_dst), + dst, ntohs(tcp->th_dport)); else - snprintf(SNPARGS(proto, len), " %s", - inet_ntoa(ip->ip_dst)); + snprintf(SNPARGS(proto, len), " %s", dst); break; case IPPROTO_UDP: - len = snprintf(SNPARGS(proto, 0), "UDP %s", - inet_ntoa(ip->ip_src)); + len = snprintf(SNPARGS(proto, 0), "UDP %s", src); if (offset == 0) snprintf(SNPARGS(proto, len), ":%d %s:%d", ntohs(udp->uh_sport), - inet_ntoa(ip->ip_dst), + dst, ntohs(udp->uh_dport)); else - snprintf(SNPARGS(proto, len), " %s", - inet_ntoa(ip->ip_dst)); + snprintf(SNPARGS(proto, len), " %s", dst); break; case IPPROTO_ICMP: + icmp = L3HDR(struct icmphdr, ip); if (offset == 0) len = snprintf(SNPARGS(proto, 0), "ICMP:%u.%u ", icmp->icmp_type, icmp->icmp_code); else len = snprintf(SNPARGS(proto, 0), "ICMP "); - len += snprintf(SNPARGS(proto, len), "%s", - inet_ntoa(ip->ip_src)); - snprintf(SNPARGS(proto, len), " %s", - inet_ntoa(ip->ip_dst)); + len += snprintf(SNPARGS(proto, len), "%s", src); + snprintf(SNPARGS(proto, len), " %s", dst); break; - +#ifdef INET6 + case IPPROTO_ICMPV6: + icmp6 = (struct icmp6_hdr *)(mtod(args->m, char *) + hlen); + if (offset == 0) + len = snprintf(SNPARGS(proto, 0), + "ICMPv6:%u.%u ", + icmp6->icmp6_type, icmp6->icmp6_code); + else + len = snprintf(SNPARGS(proto, 0), "ICMPv6 "); + len += snprintf(SNPARGS(proto, len), "%s", src); + snprintf(SNPARGS(proto, len), " %s", dst); + break; +#endif default: - len = snprintf(SNPARGS(proto, 0), "P:%d %s", ip->ip_p, - inet_ntoa(ip->ip_src)); - snprintf(SNPARGS(proto, len), " %s", - inet_ntoa(ip->ip_dst)); + len = snprintf(SNPARGS(proto, 0), "P:%d %s", + args->f_id.proto, src); + snprintf(SNPARGS(proto, len), " %s", dst); break; } - if (ip_off & (IP_MF | IP_OFFMASK)) - snprintf(SNPARGS(fragment, 0), " (frag %d:%d@%d%s)", - ntohs(ip->ip_id), ip_len - (ip->ip_hl << 2), - offset << 3, - (ip_off & IP_MF) ? "+" : ""); +#ifdef INET6 + if (args->f_id.addr_type == 6) { + if (offset & (IP6F_OFF_MASK | IP6F_MORE_FRAG)) + snprintf(SNPARGS(fragment, 0), + " (frag %08x:%d@%d%s)", + args->f_id.frag_id6, + ntohs(ip6->ip6_plen) - hlen, + ntohs(offset & IP6F_OFF_MASK) << 3, + (offset & IP6F_MORE_FRAG) ? "+" : ""); + } else +#endif + { + int ip_off, ip_len; + if (eh != NULL) { /* layer 2 packets are as on the wire */ + ip_off = ntohs(ip->ip_off); + ip_len = ntohs(ip->ip_len); + } else { + ip_off = ip->ip_off; + ip_len = ip->ip_len; + } + if (ip_off & (IP_MF | IP_OFFMASK)) + snprintf(SNPARGS(fragment, 0), + " (frag %d:%d@%d%s)", + 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, @@ -1483,7 +1628,7 @@ send_pkt(struct ipfw_flow_id *id, u_int32_t seq, u_int32_t ack, int flags) * sends a reject message, consuming the mbuf passed as an argument. */ static void -send_reject(struct ip_fw_args *args, int code, int offset, int ip_len) +send_reject(struct ip_fw_args *args, int code, u_short offset, int ip_len) { if (code != ICMP_REJECT_RST) { /* Send an ICMP unreach */ @@ -1947,6 +2092,11 @@ ipfw_chk(struct ip_fw_args *args) * we have a fragment at this offset of an IPv4 packet. * offset == 0 means that (if this is an IPv4 packet) * this is the first or only fragment. + * For IPv6 offset == 0 means there is no Fragment Header. + * If offset != 0 for IPv6 always use correct mask to + * get the correct offset because we add IP6F_MORE_FRAG + * to be able to dectect the first fragment which would + * otherwise have offset = 0. */ u_short offset = 0; @@ -1998,6 +2148,7 @@ ipfw_chk(struct ip_fw_args *args) pktlen = m->m_pkthdr.len; proto = args->f_id.proto = 0; /* mark f_id invalid */ + /* XXX 0 is a valid proto: IP/IPv6 Hop-by-Hop Option */ /* * PULLUP_TO(len, p, T) makes sure that len + sizeof(T) is contiguous, @@ -2046,54 +2197,92 @@ do { \ src_port = UDP(ulp)->uh_sport; break; - case IPPROTO_HOPOPTS: + case IPPROTO_HOPOPTS: /* RFC 2460 */ PULLUP_TO(hlen, ulp, struct ip6_hbh); ext_hd |= EXT_HOPOPTS; - hlen += sizeof(struct ip6_hbh); + hlen += (((struct ip6_hbh *)ulp)->ip6h_len + 1) << 3; proto = ((struct ip6_hbh *)ulp)->ip6h_nxt; ulp = NULL; break; - case IPPROTO_ROUTING: + case IPPROTO_ROUTING: /* RFC 2460 */ PULLUP_TO(hlen, ulp, struct ip6_rthdr); + if (((struct ip6_rthdr *)ulp)->ip6r_type != 0) { + printf("IPFW2: IPV6 - Unknown Routing " + "Header type(%d)\n", + ((struct ip6_rthdr *)ulp)->ip6r_type); + if (fw_deny_unknown_exthdrs) + return (IP_FW_DENY); + break; + } ext_hd |= EXT_ROUTING; - hlen += sizeof(struct ip6_rthdr); + hlen += (((struct ip6_rthdr *)ulp)->ip6r_len + 1) << 3; proto = ((struct ip6_rthdr *)ulp)->ip6r_nxt; ulp = NULL; break; - case IPPROTO_FRAGMENT: + case IPPROTO_FRAGMENT: /* RFC 2460 */ 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 ? */ + offset = ((struct ip6_frag *)ulp)->ip6f_offlg & + IP6F_OFF_MASK; + /* Add IP6F_MORE_FRAG for offset of first + * fragment to be != 0. */ + offset |= ((struct ip6_frag *)ulp)->ip6f_offlg & + IP6F_MORE_FRAG; + if (offset == 0) { + printf("IPFW2: IPV6 - Invalid Fragment " + "Header\n"); + if (fw_deny_unknown_exthdrs) + return (IP_FW_DENY); + break; + } + args->f_id.frag_id6 = + ntohl(((struct ip6_frag *)ulp)->ip6f_ident); + ulp = NULL; break; - case IPPROTO_AH: - case IPPROTO_NONE: - case IPPROTO_ESP: + case IPPROTO_DSTOPTS: /* RFC 2460 */ + PULLUP_TO(hlen, ulp, struct ip6_hbh); + ext_hd |= EXT_DSTOPTS; + hlen += (((struct ip6_hbh *)ulp)->ip6h_len + 1) << 3; + proto = ((struct ip6_hbh *)ulp)->ip6h_nxt; + ulp = NULL; + break; + + case IPPROTO_AH: /* RFC 2402 */ 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); + hlen += (((struct ip6_ext *)ulp)->ip6e_len + 2) << 2; proto = ((struct ip6_ext *)ulp)->ip6e_nxt; ulp = NULL; break; + case IPPROTO_ESP: /* RFC 2406 */ + PULLUP_TO(hlen, ulp, uint32_t); /* SPI, Seq# */ + /* Anything past Seq# is variable length and + * data past this ext. header is encrypted. */ + ext_hd |= EXT_ESP; + break; + + case IPPROTO_NONE: /* RFC 2460 */ + PULLUP_TO(hlen, ulp, struct ip6_ext); + /* Packet ends here. if ip6e_len!=0 octets + * must be ignored. */ + break; + case IPPROTO_OSPFIGP: /* XXX OSPF header check? */ PULLUP_TO(hlen, ulp, struct ip6_ext); break; default: - printf( "IPFW2: IPV6 - Unknown Extension Header (%d)\n", - proto); - return 0; /* deny */ + printf("IPFW2: IPV6 - Unknown Extension " + "Header(%d), ext_hd=%x\n", proto, ext_hd); + if (fw_deny_unknown_exthdrs) + return (IP_FW_DENY); break; } /*switch */ } @@ -2101,7 +2290,7 @@ do { \ 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); + args->f_id.flow_id6 = ntohl(mtod(m, struct ip6_hdr *)->ip6_flow); } else if (pktlen >= sizeof(struct ip) && (args->eh == NULL || ntohs(args->eh->ether_type) == ETHERTYPE_IP) && mtod(m, struct ip *)->ip_v == 4) { @@ -2604,8 +2793,8 @@ do { \ } case O_LOG: - if (fw_verbose && !is_ipv6) - ipfw_log(f, hlen, args->eh, m, oif); + if (fw_verbose) + ipfw_log(f, hlen, args, m, oif, offset); match = 1; break; @@ -2873,7 +3062,6 @@ do { \ * if the packet is not ICMP (or is an ICMP * query), and it is not multicast/broadcast. */ - /* XXX: IPv6 missing!?! */ if (hlen > 0 && is_ipv4 && (proto != IPPROTO_ICMP || is_icmp_query(ICMP(ulp))) && @@ -2884,6 +3072,19 @@ do { \ m = args->m; } /* FALLTHROUGH */ +#ifdef INET6 + case O_UNREACH6: + if (hlen > 0 && is_ipv6 && + (proto != IPPROTO_ICMPV6 || + (is_icmp6_query(args->f_id.flags) == 1)) && + !(m->m_flags & (M_BCAST|M_MCAST)) && + !IN6_IS_ADDR_MULTICAST(&args->f_id.dst_ip6)) { + send_reject6(args, cmd->arg1, + offset, hlen); + m = args->m; + } + /* FALLTHROUGH */ +#endif case O_DENY: retval = IP_FW_DENY; goto done; @@ -3504,6 +3705,7 @@ check_ipfw_struct(struct ip_fw *rule, int size) case O_ACCEPT: case O_DENY: case O_REJECT: + case O_UNREACH6: case O_SKIPTO: check_size: if (cmdlen != F_INSN_SIZE(ipfw_insn)) @@ -3931,6 +4133,16 @@ ipfw_init(void) struct ip_fw default_rule; int error; + /* Setup IPv6 fw sysctl tree. */ + sysctl_ctx_init(&ip6_fw_sysctl_ctx); + ip6_fw_sysctl_tree = SYSCTL_ADD_NODE(&ip6_fw_sysctl_ctx, + SYSCTL_STATIC_CHILDREN(_net_inet6_ip6), OID_AUTO, "fw", + CTLFLAG_RW | CTLFLAG_SECURE, 0, "Firewall"); + SYSCTL_ADD_INT(&ip6_fw_sysctl_ctx, SYSCTL_CHILDREN(ip6_fw_sysctl_tree), + OID_AUTO, "deny_unknown_exthdrs", CTLFLAG_RW | CTLFLAG_SECURE, + &fw_deny_unknown_exthdrs, 0, + "Deny packets with unknown IPv6 Extension Headers"); + layer3_chain.rules = NULL; layer3_chain.want_write = 0; layer3_chain.busy_count = 0; @@ -4022,5 +4234,9 @@ ipfw_destroy(void) IPFW_DYN_LOCK_DESTROY(); uma_zdestroy(ipfw_dyn_rule_zone); IPFW_LOCK_DESTROY(&layer3_chain); + + /* Free IPv6 fw sysctl tree. */ + sysctl_ctx_free(&ip6_fw_sysctl_ctx); + printf("IP firewall unloaded\n"); }