kern/145733: [patch] ipfw flaws with ipv6 fragments

Matthew Luckie mjl at luckie.org.nz
Thu Apr 15 21:00:14 UTC 2010


>Number:         145733
>Category:       kern
>Synopsis:       [patch] ipfw flaws with ipv6 fragments
>Confidential:   no
>Severity:       non-critical
>Priority:       low
>Responsible:    freebsd-bugs
>State:          open
>Quarter:        
>Keywords:       
>Date-Required:
>Class:          sw-bug
>Submitter-Id:   current-users
>Arrival-Date:   Thu Apr 15 21:00:13 UTC 2010
>Closed-Date:
>Last-Modified:
>Originator:     Matthew Luckie
>Release:        FreeBSD 8.0-RELEASE-p1 i386
>Organization:
>Environment:
System: FreeBSD spandex.luckie.org.nz 8.0-RELEASE-p1 FreeBSD 8.0-RELEASE-p1 #1: Fri Dec 4 08:21:29 NZDT 2009 mjl at spandex.luckie.org.nz:/usr/obj/usr/src/sys/spandex i386


	
>Description:

IPFW has two flaws in its handling of IPv6 packets that arrive in
fragments.  First, it will deny an IPv6 packet that arrives with
a fragmentation header which has an offset of zero, but no
further fragments.  This type of packet is explicitly allowed in
RFC 2460:

   In response to an IPv6 packet that is sent to an IPv4 destination
   (i.e., a packet that undergoes translation from IPv6 to IPv4),
   the originating IPv6 node may receive an ICMP Packet Too Big
   message reporting a Next-Hop MTU less than 1280.  In that
   case, the IPv6 node is not required to reduce the size of
   subsequent packets to less than 1280, but must include a
   Fragment header in those packets so that the IPv6-to-IPv4
   translating router can obtain a suitable Identification value
   to use in resulting IPv4 fragments.  Note that this means the
   payload may have to be reduced to 1232 octets (1280 minus 40
   for the IPv6 header and 8 for the Fragment header), and
   smaller still if additional extension headers are used.

The second flaw is that the code allows IPv6 packets that arrive in
fragments to avoid transport-layer rules.  For example, consider this
ruleset:

00001 deny tcp from 2607:f0b0:0:6:209:87:239:67 80 to 2404:138:4002:4000:205:1cff:fe11:beff dst-port 37822
65534 allow ip from any to any
65535 deny ip from any to any

Rule 1 will not be applied to the fragment with offset zero
because the MF bit is intentionally included in the offset
variable used in ipfw_chk, so the check to see if the transport
header is found in fragment zero will fail.  Instead, the rule
will be skipped over, and the next rule which in this example is
an allow will accept the fragment.  Where an administrator might
have expected the traffic to be blocked it will instead be
allowed through the firewall.

>How-To-Repeat:

As above.  I can provide code to reproduce this problem out of band.

>Fix:

Apply this patch.

--- patch-ip_fw2-8.c begins here ---
--- ip_fw2.c.orig	2010-04-16 08:38:52.000000000 +1200
+++ ip_fw2.c	2010-04-16 08:42:03.000000000 +1200
@@ -804,6 +804,7 @@ ipfw_log(struct ip_fw *f, u_int hlen, st
 	char *action;
 	int limit_reached = 0;
 	char action2[40], proto[128], fragment[32];
+	u_short mf = 0;
 
 	fragment[0] = '\0';
 	proto[0] = '\0';
@@ -952,6 +953,8 @@ ipfw_log(struct ip_fw *f, u_int hlen, st
 			snprintf(dst, sizeof(dst), "[%s]",
 			    ip6_sprintf(ip6buf, &args->f_id.dst_ip6));
 
+			mf = offset & IP6F_MORE_FRAG;
+			offset &= IP6F_OFF_MASK;
 			ip6 = (struct ip6_hdr *)ip;
 			tcp = (struct tcphdr *)(((char *)ip) + hlen);
 			udp = (struct udphdr *)(((char *)ip) + hlen);
@@ -1021,13 +1024,13 @@ ipfw_log(struct ip_fw *f, u_int hlen, st
 
 #ifdef INET6
 		if (IS_IP6_FLOW_ID(&(args->f_id))) {
-			if (offset & (IP6F_OFF_MASK | IP6F_MORE_FRAG))
+			if (offset || mf)
 				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) ? "+" : "");
+				    ntohs(offset) << 3,
+				    mf ? "+" : "");
 		} else
 #endif
 		{
@@ -2184,16 +2187,13 @@ ipfw_chk(struct ip_fw_args *args)
 
 	/*
 	 * offset	The offset of a fragment. offset != 0 means that
-	 *	we have a fragment at this offset of an IPv4 packet.
-	 *	offset == 0 means that (if this is an IPv4 packet)
-	 *	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.
+	 *	we have a fragment at this offset.
+	 *	offset == 0 means that this is the first or only fragment.
+	 *
+	 * mf		The MF bit masked out of IPv6 packets.
 	 */
 	u_short offset = 0;
+	u_short mf = 0;
 
 	/*
 	 * Local copies of addresses. They are only valid if we have
@@ -2345,17 +2345,8 @@ do {									\
 				proto = ((struct ip6_frag *)ulp)->ip6f_nxt;
 				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 &
+				mf = ((struct ip6_frag *)ulp)->ip6f_offlg &
 					IP6F_MORE_FRAG;
-				if (offset == 0) {
-					printf("IPFW2: IPV6 - Invalid Fragment "
-					    "Header\n");
-					if (V_fw_deny_unknown_exthdrs)
-					    return (IP_FW_DENY);
-					break;
-				}
 				args->f_id.frag_id6 =
 				    ntohl(((struct ip6_frag *)ulp)->ip6f_ident);
 				ulp = NULL;
@@ -2941,7 +2932,7 @@ check_body:
 			case O_LOG:
 				if (V_fw_verbose)
 					ipfw_log(f, hlen, args, m,
-					    oif, offset, tablearg, ip);
+					    oif, offset|mf, tablearg, ip);
 				match = 1;
 				break;
 
--- patch-ip_fw2-8.c ends here ---


>Release-Note:
>Audit-Trail:
>Unformatted:


More information about the freebsd-bugs mailing list