NetBSD Problem Report #44403

From mouse@Sparkle.Rodents-Montreal.ORG  Mon Jan 17 09:43:21 2011
Return-Path: <mouse@Sparkle.Rodents-Montreal.ORG>
Received: from mail.netbsd.org (mail.netbsd.org [204.152.190.11])
	by www.NetBSD.org (Postfix) with ESMTP id 5D83163B883
	for <gnats-bugs@gnats.NetBSD.org>; Mon, 17 Jan 2011 09:43:21 +0000 (UTC)
Message-Id: <201101170943.EAA29721@Sparkle.Rodents-Montreal.ORG>
Date: Mon, 17 Jan 2011 04:43:19 -0500 (EST)
From: der Mouse <mouse@Rodents-Montreal.ORG>
Reply-To: mouse@Rodents-Montreal.ORG
To: gnats-bugs@gnats.NetBSD.org
Subject: [dM] TUN_PREPADDR broken for IPv6
X-Send-Pr-Version: 3.95

>Number:         44403
>Category:       kern
>Synopsis:       TUN_PREPADDR works only with AF_INET
>Confidential:   no
>Severity:       serious
>Priority:       low
>Responsible:    kern-bug-people
>State:          open
>Class:          sw-bug
>Submitter-Id:   net
>Arrival-Date:   Mon Jan 17 09:45:00 +0000 2011
>Originator:     der Mouse
>Release:        NetBSD 4.0.1
>Organization:
	Dis-
>Environment:
	Any 4.0.1, likely other versions too.
>Description:
	When TUN_PREPADDR is turned on on a tun interface (which means
	using the TUNSLMODE ioctl on the control device), it silently
	stops passing anything but AF_INET packets.  In particular, it
	drops AF_INET6 packets.

	Careful code inspection reveals what's going on: the control
	structure in tun_output causes the "drop if not INET" test,
	which is carefully skipped if TUN_IFHEAD is set, to be
	performed when TUN_PREPADDR is set.

	In my case, this broke my house IPv6 connectivity when I moved
	one end of the tunnel which provides its v6 uplink to 4.0.1
	(from 3.1, into the if_tun.c of which I had hacked more or less
	the same PREPADDR support which appears in stock 4.0.1, only
	without this bug, because I didn't implement TUN_IFHEAD there).
>How-To-Repeat:
	Turn on TUN_PREPADDR with TUNSLMODE and watch your v6 traffic
	silently vanish before the userland process even sees it.  This
	makes TUN_PREPADDR substantially less useful than its design
	purpose (which was to provide not only the address family, as
	TUN_IFHEAD does, but the rest of the destination address as
	well, thus making TUNSIFMODE specifying IFF_BROADCAST useful).
>Fix:
	This works for me:

	--- OLD/if_tun.c	2011-01-17 04:19:59.000000000 -0500
	+++ NEW/if_tun.c	2011-01-17 04:20:30.000000000 -0500
	@@ -496,6 +496,7 @@
	 #if defined(INET) || defined(INET6)
	 	int		mlen;
	 	uint32_t	*af;
	+	int		non_inet_ok;
	 #endif
	 	ALTQ_DECL(struct altq_pktattr pktattr;)

	@@ -530,6 +531,7 @@
	 	case AF_INET:
	 #endif
	 #if defined(INET) || defined(INET6)
	+		non_inet_ok = 0;
	 		if (tp->tun_flags & TUN_PREPADDR) {
	 			/* Simple link-layer header */
	 			M_PREPEND(m0, dst->sa_len, M_DONTWAIT);
	@@ -539,8 +541,8 @@
	 				goto out;
	 			}
	 			bcopy(dst, mtod(m0, char *), dst->sa_len);
	+			non_inet_ok = 1;
	 		}
	-
	 		if (tp->tun_flags & TUN_IFHEAD) {
	 			/* Prepend the address family */
	 			M_PREPEND(m0, sizeof(*af), M_DONTWAIT);
	@@ -551,8 +553,10 @@
	 			}
	 			af = mtod(m0,uint32_t *);
	 			*af = htonl(dst->sa_family);
	-		} else {
	-#ifdef INET     
	+			non_inet_ok = 1;
	+		}
	+		if (! non_inet_ok) {
	+#ifdef INET
	 			if (dst->sa_family != AF_INET)
	 #endif
	 			{

	It should be enough to just add an "else" before the if that
	tests TUN_IFHEAD, because TUN_PREADDR and TUN_IFHEAD should
	never both be set (when setting either, the other is cleared).
	However, this strikes me as more robust, mostly in case someone
	someday decides to allow them to both be set.  It also makes
	what is going on more explicit to a reader of the code.

/~\ The ASCII				  Mouse
\ / Ribbon Campaign
 X  Against HTML		mouse@rodents-montreal.org
/ \ Email!	     7D C8 61 52 5D E7 2D 39  4E F1 31 3E E8 B3 27 4B

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-2007 The NetBSD Foundation, Inc. ALL RIGHTS RESERVED.