Module Name:    src
Committed By:   maxv
Date:           Thu Mar  8 07:06:13 UTC 2018

Modified Files:
        src/sys/net/npf: npf.h npf_handler.c npf_inet.c

Log Message:
Declare NPC_FMTERR, and use it to kick malformed packets. Several sanity
checks are added in IPv6; after we see the first IPPROTO_FRAGMENT header,
we are allowed to fail to advance, otherwise we kick the packet.

Sent on tech-net@ a few days ago, no response, but I'm committing it now
anyway.


To generate a diff of this commit:
cvs rdiff -u -r1.55 -r1.56 src/sys/net/npf/npf.h
cvs rdiff -u -r1.37 -r1.38 src/sys/net/npf/npf_handler.c \
    src/sys/net/npf/npf_inet.c

Please note that diffs are not public domain; they are subject to the
copyright notices on the relevant files.

Modified files:

Index: src/sys/net/npf/npf.h
diff -u src/sys/net/npf/npf.h:1.55 src/sys/net/npf/npf.h:1.56
--- src/sys/net/npf/npf.h:1.55	Fri Dec 15 21:00:26 2017
+++ src/sys/net/npf/npf.h	Thu Mar  8 07:06:13 2018
@@ -1,4 +1,4 @@
-/*	$NetBSD: npf.h,v 1.55 2017/12/15 21:00:26 maxv Exp $	*/
+/*	$NetBSD: npf.h,v 1.56 2018/03/08 07:06:13 maxv Exp $	*/
 
 /*-
  * Copyright (c) 2009-2014 The NetBSD Foundation, Inc.
@@ -143,6 +143,8 @@ int		nbuf_find_tag(nbuf_t *, uint32_t *)
 
 #define	NPC_ALG_EXEC	0x100	/* ALG execution. */
 
+#define	NPC_FMTERR	0x200	/* Format error. */
+
 #define	NPC_IP46	(NPC_IP4|NPC_IP6)
 
 typedef struct {

Index: src/sys/net/npf/npf_handler.c
diff -u src/sys/net/npf/npf_handler.c:1.37 src/sys/net/npf/npf_handler.c:1.38
--- src/sys/net/npf/npf_handler.c:1.37	Sun Feb 19 20:27:22 2017
+++ src/sys/net/npf/npf_handler.c	Thu Mar  8 07:06:13 2018
@@ -1,4 +1,4 @@
-/*	$NetBSD: npf_handler.c,v 1.37 2017/02/19 20:27:22 christos Exp $	*/
+/*	$NetBSD: npf_handler.c,v 1.38 2018/03/08 07:06:13 maxv Exp $	*/
 
 /*-
  * Copyright (c) 2009-2013 The NetBSD Foundation, Inc.
@@ -37,7 +37,7 @@
 
 #ifdef _KERNEL
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: npf_handler.c,v 1.37 2017/02/19 20:27:22 christos Exp $");
+__KERNEL_RCSID(0, "$NetBSD: npf_handler.c,v 1.38 2018/03/08 07:06:13 maxv Exp $");
 
 #include <sys/types.h>
 #include <sys/param.h>
@@ -109,7 +109,7 @@ npf_reassembly(npf_t *npf, npf_cache_t *
 	nbuf_init(npf, nbuf, *mp, nbuf->nb_ifp);
 	npc->npc_info = 0;
 
-	if (npf_cache_all(npc) & NPC_IPFRAG) {
+	if (npf_cache_all(npc) & (NPC_IPFRAG|NPC_FMTERR)) {
 		return EINVAL;
 	}
 	npf_stats_inc(npf, NPF_STAT_REASSEMBLY);
@@ -154,8 +154,16 @@ npf_packet_handler(npf_t *npf, struct mb
 	error = 0;
 	rp = NULL;
 
-	/* Cache everything.  Determine whether it is an IP fragment. */
+	/* Cache everything. */
 	flags = npf_cache_all(&npc);
+
+	/* If error on the format, leave quickly. */
+	if (flags & NPC_FMTERR) {
+		error = EINVAL;
+		goto fastout;
+	}
+
+	/* Determine whether it is an IP fragment. */
 	if (__predict_false(flags & NPC_IPFRAG)) {
 		/*
 		 * We pass IPv6 fragments unconditionally
@@ -308,6 +316,7 @@ out:
 		error = ENETUNREACH;
 	}
 
+fastout:
 	if (*mp) {
 		/* Free the mbuf chain. */
 		m_freem(*mp);
Index: src/sys/net/npf/npf_inet.c
diff -u src/sys/net/npf/npf_inet.c:1.37 src/sys/net/npf/npf_inet.c:1.38
--- src/sys/net/npf/npf_inet.c:1.37	Sun Feb 19 20:27:22 2017
+++ src/sys/net/npf/npf_inet.c	Thu Mar  8 07:06:13 2018
@@ -1,4 +1,4 @@
-/*	$NetBSD: npf_inet.c,v 1.37 2017/02/19 20:27:22 christos Exp $	*/
+/*	$NetBSD: npf_inet.c,v 1.38 2018/03/08 07:06:13 maxv Exp $	*/
 
 /*-
  * Copyright (c) 2009-2014 The NetBSD Foundation, Inc.
@@ -40,7 +40,7 @@
 
 #ifdef _KERNEL
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: npf_inet.c,v 1.37 2017/02/19 20:27:22 christos Exp $");
+__KERNEL_RCSID(0, "$NetBSD: npf_inet.c,v 1.38 2018/03/08 07:06:13 maxv Exp $");
 
 #include <sys/param.h>
 #include <sys/types.h>
@@ -328,12 +328,12 @@ npf_cache_ip(npf_cache_t *npc, nbuf_t *n
 
 		ip = nbuf_ensure_contig(nbuf, sizeof(struct ip));
 		if (ip == NULL) {
-			return 0;
+			return NPC_FMTERR;
 		}
 
 		/* Check header length and fragment offset. */
 		if ((u_int)(ip->ip_hl << 2) < sizeof(struct ip)) {
-			return 0;
+			return NPC_FMTERR;
 		}
 		if (ip->ip_off & ~htons(IP_DF | IP_RF)) {
 			/* Note fragmentation. */
@@ -357,10 +357,14 @@ npf_cache_ip(npf_cache_t *npc, nbuf_t *n
 		struct ip6_ext *ip6e;
 		struct ip6_frag *ip6f;
 		size_t off, hlen;
+		int frag_present;
+		bool is_frag;
+		uint8_t onxt;
+		int fragoff;
 
 		ip6 = nbuf_ensure_contig(nbuf, sizeof(struct ip6_hdr));
 		if (ip6 == NULL) {
-			return 0;
+			return NPC_FMTERR;
 		}
 
 		/* Set initial next-protocol value. */
@@ -368,16 +372,14 @@ npf_cache_ip(npf_cache_t *npc, nbuf_t *n
 		npc->npc_proto = ip6->ip6_nxt;
 		npc->npc_hlen = hlen;
 
+		frag_present = 0;
+		is_frag = false;
+
 		/*
 		 * Advance by the length of the current header.
 		 */
 		off = nbuf_offset(nbuf);
-		while (nbuf_advance(nbuf, hlen, 0) != NULL) {
-			ip6e = nbuf_ensure_contig(nbuf, sizeof(*ip6e));
-			if (ip6e == NULL) {
-				return 0;
-			}
-
+		while ((ip6e = nbuf_advance(nbuf, hlen, sizeof(*ip6e))) != NULL) {
 			/*
 			 * Determine whether we are going to continue.
 			 */
@@ -388,9 +390,23 @@ npf_cache_ip(npf_cache_t *npc, nbuf_t *n
 				hlen = (ip6e->ip6e_len + 1) << 3;
 				break;
 			case IPPROTO_FRAGMENT:
+				if (frag_present++)
+					return NPC_FMTERR;
 				ip6f = nbuf_ensure_contig(nbuf, sizeof(*ip6f));
 				if (ip6f == NULL)
-					return 0;
+					return NPC_FMTERR;
+
+				hlen = sizeof(struct ip6_frag);
+
+				/* RFC6946: Skip dummy fragments. */
+				fragoff = ntohs(ip6f->ip6f_offlg & IP6F_OFF_MASK);
+				if (fragoff == 0 &&
+				    !(ip6f->ip6f_offlg & IP6F_MORE_FRAG)) {
+					break;
+				}
+
+				is_frag = true;
+
 				/*
 				 * We treat the first fragment as a regular
 				 * packet and then we pass the rest of the
@@ -399,10 +415,9 @@ npf_cache_ip(npf_cache_t *npc, nbuf_t *n
 				 * be able to reassembled, if not they will
 				 * be ignored. We can do better later.
 				 */
-				if (ntohs(ip6f->ip6f_offlg & IP6F_OFF_MASK) != 0)
+				if (fragoff != 0)
 					flags |= NPC_IPFRAG;
 
-				hlen = sizeof(struct ip6_frag);
 				break;
 			case IPPROTO_AH:
 				hlen = (ip6e->ip6e_len + 2) << 2;
@@ -415,11 +430,24 @@ npf_cache_ip(npf_cache_t *npc, nbuf_t *n
 			if (!hlen) {
 				break;
 			}
+			onxt = npc->npc_proto;
 			npc->npc_proto = ip6e->ip6e_nxt;
 			npc->npc_hlen += hlen;
 		}
 
 		/*
+		 * We failed to advance. If we are not a fragment, that's
+		 * a format error and we leave. Otherwise, restore npc_hlen
+		 * and npc_proto to their previous (and correct) values.
+		 */
+		if (ip6e == NULL) {
+			if (!is_frag)
+				return NPC_FMTERR;
+			npc->npc_proto = onxt;
+			npc->npc_hlen -= hlen;
+		}
+
+		/*
 		 * Re-fetch the header pointers (nbufs might have been
 		 * reallocated).  Restore the original offset (if any).
 		 */
@@ -469,7 +497,8 @@ again:
 	 * fragmented, then we cannot look into L4.
 	 */
 	flags = npf_cache_ip(npc, nbuf);
-	if ((flags & NPC_IP46) == 0 || (flags & NPC_IPFRAG) != 0) {
+	if ((flags & NPC_IP46) == 0 || (flags & NPC_IPFRAG) != 0 ||
+	    (flags & NPC_FMTERR) != 0) {
 		nbuf_unset_flag(nbuf, NBUF_DATAREF_RESET);
 		npc->npc_info |= flags;
 		return flags;

Reply via email to