summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorbz <bz@FreeBSD.org>2005-08-13 11:02:34 +0000
committerbz <bz@FreeBSD.org>2005-08-13 11:02:34 +0000
commit5434a588080f496f3f78c9b62fcc9bc2993449cb (patch)
tree6f00a69da3358c57d462226e8f8fb77137db166c
parent810123c2f688458c9677d8cd08da90460f18926c (diff)
downloadFreeBSD-src-5434a588080f496f3f78c9b62fcc9bc2993449cb.zip
FreeBSD-src-5434a588080f496f3f78c9b62fcc9bc2993449cb.tar.gz
* Add dynamic sysctl for net.inet6.ip6.fw.
* Correct handling of IPv6 Extension Headers. * Add unreach6 code. * Add logging for IPv6. Submitted by: sysctl handling derived from patch from ume needed for ip6fw Obtained from: is_icmp6_query and send_reject6 derived from similar functions of netinet6,ip6fw Reviewed by: ume, gnn; silence on ipfw@ Test setup provided by: CK Software GmbH MFC after: 6 days
-rw-r--r--sbin/ipfw/ipfw.821
-rw-r--r--sbin/ipfw/ipfw2.c71
-rw-r--r--sys/netinet/ip_fw.h5
-rw-r--r--sys/netinet/ip_fw2.c350
4 files changed, 377 insertions, 70 deletions
diff --git a/sbin/ipfw/ipfw.8 b/sbin/ipfw/ipfw.8
index 5754936..2765f6b 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 686174d..ee1b362 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 5c5fd6e..88c13bd 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 807f48d..63d81f7 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;
+ } else {
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));
+
+ 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);
- 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;
+ 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_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:
- case IPPROTO_NONE:
- case IPPROTO_ESP:
+ 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 @@ check_body:
}
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 @@ check_body:
* 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 @@ check_body:
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");
}
OpenPOWER on IntegriCloud