NetBSD Problem Report #38457

From ws@tools.de  Fri Apr 18 15:21:40 2008
Return-Path: <ws@tools.de>
Received: from mail.netbsd.org (mail.netbsd.org [204.152.190.11])
	by narn.NetBSD.org (Postfix) with ESMTP id E7F4E63B293
	for <gnats-bugs@gnats.NetBSD.org>; Fri, 18 Apr 2008 15:21:40 +0000 (UTC)
Message-Id: <20080418152135.87B3E6BA9F@sdsl1.tools.de>
Date: Fri, 18 Apr 2008 17:21:35 +0200 (MEST)
From: ws@tools.de
Reply-To: ws@tools.de
To: gnats-bugs@gnats.NetBSD.org
Subject: ipf doesn't handle IPv6 fragments
X-Send-Pr-Version: 3.95

>Number:         38457
>Category:       kern
>Synopsis:       ipf doesn't handle IPv6 fragments
>Confidential:   no
>Severity:       serious
>Priority:       medium
>Responsible:    kern-bug-people
>State:          open
>Class:          sw-bug
>Submitter-Id:   net
>Arrival-Date:   Fri Apr 18 15:25:00 +0000 2008
>Last-Modified:  Tue Nov 04 01:40:02 +0000 2008
>Originator:     Wolfgang Solfrank
>Release:        NetBSD 4.99.58
>Organization:
Tools GmbH
>Environment:
System: NetBSD sdsl1.tools.de 4.99.58 NetBSD 4.99.58 (sdsl1) #0: Thu Apr 10 18:40:23 MEST 2008 ws@sdsl1.tools.de:/src/obj/sys/arch/i386/compile/sdsl1 i386
Architecture: i386
Machine: i386
>Description:

ipf doesn't handle IPv6 fragments.  The first fragment is not handled correctly,
since the fragment header is skipped twice.  The other fragments are not
handled at all.

>How-To-Repeat:

Arrange for some fragmented IPv6 packets to arrive at your box on a port
where you have configured a "keep frags" rule.  Observe that the traffic
doesn't arrive at the destination.

>Fix:

Index: fil.c
===================================================================
RCS file: /cvsroot/src/sys/dist/ipf/netinet/fil.c,v
retrieving revision 1.42
diff -u -r1.42 fil.c
--- fil.c	17 Sep 2007 06:25:21 -0000	1.42
+++ fil.c	18 Apr 2008 14:52:56 -0000
@@ -694,12 +694,11 @@
 	}

 	fin->fin_off = ntohs(frag->ip6f_offlg & IP6F_OFF_MASK);
-	fin->fin_off <<= 3;
 	if (fin->fin_off != 0)
 		fin->fin_flx |= FI_FRAGBODY;

-	fin->fin_dp = (char *)fin->fin_dp + sizeof(*frag);
-	fin->fin_dlen -= sizeof(*frag);
+	if (frag->ip6f_offlg & IP6F_MORE_FRAG)
+		fin->fin_flx |= FI_MOREFRAG;

 	return frag->ip6f_nxt;
 }
@@ -1455,6 +1454,8 @@
 		int morefrag = off & IP_MF;

 		fi->fi_flx |= FI_FRAG;
+		if (morefrag)
+			fi->fi_flx |= FI_MOREFRAG;
 		off &= IP_OFFMASK;
 		if (off != 0) {
 			fin->fin_flx |= FI_FRAGBODY;
Index: ip_fil.h
===================================================================
RCS file: /cvsroot/src/sys/dist/ipf/netinet/ip_fil.h,v
retrieving revision 1.15
diff -u -r1.15 ip_fil.h
--- ip_fil.h	2 Oct 2007 06:15:11 -0000	1.15
+++ ip_fil.h	18 Apr 2008 14:52:56 -0000
@@ -271,6 +271,7 @@
 #define	FI_V6EXTHDR	0x10000
 #define	FI_COALESCE	0x20000
 #define	FI_NEWNAT	0x40000
+#define	FI_MOREFRAG	0x80000
 #define	FI_NOCKSUM	0x20000000	/* don't do a L4 checksum validation */
 #define	FI_DONTCACHE	0x40000000	/* don't cache the result */
 #define	FI_IGNORE	0x80000000
Index: ip_frag.c
===================================================================
RCS file: /cvsroot/src/sys/dist/ipf/netinet/ip_frag.c,v
retrieving revision 1.8
diff -u -r1.8 ip_frag.c
--- ip_frag.c	2 Oct 2007 06:15:12 -0000	1.8
+++ ip_frag.c	18 Apr 2008 14:52:56 -0000
@@ -134,6 +134,7 @@
 u_long	fr_ticks = 0;


+static INLINE int ipfr_index __P((fr_info_t *, ipfr_t *));
 static ipfr_t *ipfr_newfrag __P((fr_info_t *, u_32_t, ipfr_t **));
 static ipfr_t *fr_fraglookup __P((fr_info_t *, ipfr_t **));
 static void fr_fragdelete __P((ipfr_t *, ipfr_t ***));
@@ -218,6 +219,76 @@


 /* ------------------------------------------------------------------------ */
+/* Function:	ipfr_index						    */
+/* Returns:	int	- index in fragment table for given packet	    */
+/* Parameters:	fin(I)	- pointer to packet information			    */
+/*		frag(O) - pointer to ipfr_t structure to fill		    */
+/*									    */
+/* Compute the index in the fragment table while filling the per packet	    */
+/* part of the fragment state.						    */
+/* ------------------------------------------------------------------------ */
+static INLINE int ipfr_index(fin, frag)
+fr_info_t *fin;
+ipfr_t *frag;
+{
+	u_int idx;
+
+	/*
+	 * For fragments, we record protocol, packet id, TOS and both IP#'s
+	 * (these should all be the same for all fragments of a packet).
+	 *
+	 * build up a hash value to index the table with.
+	 */
+
+#ifdef	USE_INET6
+	if (fin->fin_v == 6) {
+		ip6_t *ip6 = (ip6_t *)fin->fin_ip;
+
+		frag->ipfr_p = fin->fin_fi.fi_p;
+		frag->ipfr_id = fin->fin_id;
+		frag->ipfr_tos = ip6->ip6_flow & IPV6_FLOWINFO_MASK;
+		frag->ipfr_src.in6 = ip6->ip6_src;
+		frag->ipfr_dst.in6 = ip6->ip6_dst;
+	} else
+#endif
+	{
+		ip_t *ip = fin->fin_ip;
+
+		frag->ipfr_p = ip->ip_p;
+		frag->ipfr_id = ip->ip_id;
+		frag->ipfr_tos = ip->ip_tos;
+		frag->ipfr_src.in4.s_addr = ip->ip_src.s_addr;
+		frag->ipfr_src.i6[1] = 0;
+		frag->ipfr_src.i6[2] = 0;
+		frag->ipfr_src.i6[3] = 0;
+		frag->ipfr_dst.in4.s_addr = ip->ip_dst.s_addr;
+		frag->ipfr_dst.i6[1] = 0;
+		frag->ipfr_dst.i6[2] = 0;
+		frag->ipfr_dst.i6[3] = 0;
+	}
+	frag->ipfr_ifp = fin->fin_ifp;
+	frag->ipfr_optmsk = fin->fin_fi.fi_optmsk & IPF_OPTCOPY;
+	frag->ipfr_secmsk = fin->fin_fi.fi_secmsk;
+	frag->ipfr_auth = fin->fin_fi.fi_auth;
+
+	idx = frag->ipfr_p;
+	idx += frag->ipfr_id;
+	idx += frag->ipfr_src.i6[0];
+	idx += frag->ipfr_src.i6[1];
+	idx += frag->ipfr_src.i6[2];
+	idx += frag->ipfr_src.i6[3];
+	idx += frag->ipfr_dst.i6[0];
+	idx += frag->ipfr_dst.i6[1];
+	idx += frag->ipfr_dst.i6[2];
+	idx += frag->ipfr_dst.i6[3];
+	idx *= 127;
+	idx %= IPFT_SIZE;
+
+	return idx;
+}
+
+
+/* ------------------------------------------------------------------------ */
 /* Function:    ipfr_newfrag                                                */
 /* Returns:     ipfr_t * - pointer to fragment cache state info or NULL     */
 /* Parameters:  fin(I)   - pointer to packet information                    */
@@ -234,7 +305,6 @@
 	ipfr_t *fra, frag;
 	u_int idx, off;
 	frentry_t *fr;
-	ip_t *ip;

 	if (ipfr_inuse >= IPFT_SIZE)
 		return NULL;
@@ -242,28 +312,11 @@
 	if ((fin->fin_flx & (FI_FRAG|FI_BAD)) != FI_FRAG)
 		return NULL;

-	ip = fin->fin_ip;
-
 	if (pass & FR_FRSTRICT)
 		if (fin->fin_off != 0)
 			return NULL;

-	frag.ipfr_p = ip->ip_p;
-	idx = ip->ip_p;
-	frag.ipfr_id = ip->ip_id;
-	idx += ip->ip_id;
-	frag.ipfr_tos = ip->ip_tos;
-	frag.ipfr_src.s_addr = ip->ip_src.s_addr;
-	idx += ip->ip_src.s_addr;
-	frag.ipfr_dst.s_addr = ip->ip_dst.s_addr;
-	idx += ip->ip_dst.s_addr;
-	frag.ipfr_ifp = fin->fin_ifp;
-	idx *= 127;
-	idx %= IPFT_SIZE;
-
-	frag.ipfr_optmsk = fin->fin_fi.fi_optmsk & IPF_OPTCOPY;
-	frag.ipfr_secmsk = fin->fin_fi.fi_secmsk;
-	frag.ipfr_auth = fin->fin_fi.fi_auth;
+	idx = ipfr_index(fin, &frag);

 	/*
 	 * first, make sure it isn't already there...
@@ -309,7 +362,7 @@
 	/*
 	 * Compute the offset of the expected start of the next packet.
 	 */
-	off = ip->ip_off & IP_OFFMASK;
+	off = fin->fin_off >> 3;
 	if (off == 0)
 		fra->ipfr_seen0 = 1;
 	fra->ipfr_off = off + (fin->fin_dlen >> 3);
@@ -334,7 +387,7 @@
 {
 	ipfr_t	*fra;

-	if ((fin->fin_v != 4) || (fr_frag_lock != 0))
+	if (fr_frag_lock != 0)
 		return -1;

 	WRITE_ENTER(&ipf_frag);
@@ -368,7 +421,7 @@
 {
 	ipfr_t	*fra;

-	if ((fin->fin_v != 4) || (fr_frag_lock != 0))
+	if (fr_frag_lock != 0)
 		return 0;

 	WRITE_ENTER(&ipf_natfrag);
@@ -401,7 +454,7 @@
 {
 	ipfr_t	*fra;

-	if ((fin->fin_v != 4) || (fr_frag_lock))
+	if (fr_frag_lock)
 		return 0;

 	WRITE_ENTER(&ipf_ipidfrag);
@@ -434,34 +487,11 @@
 {
 	ipfr_t *f, frag;
 	u_int idx;
-	ip_t *ip;

 	if ((fin->fin_flx & (FI_FRAG|FI_BAD)) != FI_FRAG)
 		return NULL;

-	/*
-	 * For fragments, we record protocol, packet id, TOS and both IP#'s
-	 * (these should all be the same for all fragments of a packet).
-	 *
-	 * build up a hash value to index the table with.
-	 */
-	ip = fin->fin_ip;
-	frag.ipfr_p = ip->ip_p;
-	idx = ip->ip_p;
-	frag.ipfr_id = ip->ip_id;
-	idx += ip->ip_id;
-	frag.ipfr_tos = ip->ip_tos;
-	frag.ipfr_src.s_addr = ip->ip_src.s_addr;
-	idx += ip->ip_src.s_addr;
-	frag.ipfr_dst.s_addr = ip->ip_dst.s_addr;
-	idx += ip->ip_dst.s_addr;
-	frag.ipfr_ifp = fin->fin_ifp;
-	idx *= 127;
-	idx %= IPFT_SIZE;
-
-	frag.ipfr_optmsk = fin->fin_fi.fi_optmsk & IPF_OPTCOPY;
-	frag.ipfr_secmsk = fin->fin_fi.fi_secmsk;
-	frag.ipfr_auth = fin->fin_fi.fi_auth;
+	idx = ipfr_index(fin, &frag);

 	/*
 	 * check the table, careful to only compare the right amount of data
@@ -492,7 +522,7 @@
 			 * because a fragmented packet is never resent with
 			 * the same IP ID# (or shouldn't).
 			 */
-			off = ip->ip_off & IP_OFFMASK;
+			off = fin->fin_off >> 3;
 			if (f->ipfr_seen0) {
 				if (off == 0) {
 					ATOMIC_INCL(ipfr_stats.ifs_retrans0);
@@ -526,7 +556,7 @@
 			 * last (in order), shrink expiration time.
 			 */
 			if (off == f->ipfr_off) {
-				if (!(ip->ip_off & IP_MF))
+				if (!(fin->fin_flx & FI_MOREFRAG))
 					f->ipfr_ttl = fr_ticks + 1;
 				f->ipfr_off = (fin->fin_dlen >> 3) + off;
 			} else if (f->ipfr_pass & FR_FRSTRICT)
@@ -552,7 +582,7 @@
 	nat_t	*nat;
 	ipfr_t	*ipf;

-	if ((fin->fin_v != 4) || (fr_frag_lock) || !ipfr_natlist)
+	if ((fr_frag_lock) || !ipfr_natlist)
 		return NULL;
 	READ_ENTER(&ipf_natfrag);
 	ipf = fr_fraglookup(fin, ipfr_nattab);
@@ -586,7 +616,7 @@
 	ipfr_t	*ipf;
 	u_32_t	id;

-	if ((fin->fin_v != 4) || (fr_frag_lock) || !ipfr_ipidlist)
+	if ((fr_frag_lock) || !ipfr_ipidlist)
 		return 0xffffffff;

 	READ_ENTER(&ipf_ipidfrag);
@@ -619,7 +649,7 @@
 	ipfr_t	*fra;
 	u_32_t pass;

-	if ((fin->fin_v != 4) || (fr_frag_lock) || (ipfr_list == NULL))
+	if ((fr_frag_lock) || (ipfr_list == NULL))
 		return NULL;

 	READ_ENTER(&ipf_frag);
Index: ip_frag.h
===================================================================
RCS file: /cvsroot/src/sys/dist/ipf/netinet/ip_frag.h,v
retrieving revision 1.4
diff -u -r1.4 ip_frag.h
--- ip_frag.h	14 Apr 2007 20:34:36 -0000	1.4
+++ ip_frag.h	18 Apr 2008 14:52:56 -0000
@@ -29,14 +29,14 @@
 	 * therefore important for this set to remain together.
 	 */
 	void	*ipfr_ifp;
-	struct	in_addr	ipfr_src;
-	struct	in_addr	ipfr_dst;
+	i6addr_t ipfr_src;
+	i6addr_t ipfr_dst;
 	u_32_t	ipfr_optmsk;
 	u_short	ipfr_secmsk;
 	u_short	ipfr_auth;
-	u_short	ipfr_id;
-	u_char	ipfr_p;
-	u_char	ipfr_tos;
+	u_32_t	ipfr_id;
+	u_32_t	ipfr_p;
+	u_32_t	ipfr_tos;
 	u_32_t	ipfr_pass;
 } ipfr_t;


>Release-Note:

>Audit-Trail:

From: darrenr@NetBSD.org (Darren Reed)
To: ws@tools.de
Cc: gnats-bugs@NetBSD.org
Subject: Re: kern/38457
Date: Tue,  4 Nov 2008 01:38:53 +0000 (UTC)

 IPFilter version 4 doesn't handle ipv6 fragments - this is a known
 issue, hence the "if fin_v != 4" parts of the code.

>Unformatted:

NetBSD Home
NetBSD PR Database Search

(Contact us) $NetBSD: query-full-pr,v 1.39 2013/11/01 18:47:49 spz Exp $
$NetBSD: gnats_config.sh,v 1.8 2006/05/07 09:23:38 tsutsui Exp $
Copyright © 1994-2014 The NetBSD Foundation, Inc. ALL RIGHTS RESERVED.