Bug 9888 - tun device without protocol info header fails under IPv6
Summary: tun device without protocol info header fails under IPv6
Status: REJECTED WILL_NOT_FIX
Alias: None
Product: Networking
Classification: Unclassified
Component: IPV6 (show other bugs)
Hardware: All Linux
: P1 low
Assignee: Hideaki YOSHIFUJI
URL:
Keywords:
Depends on:
Blocks:
 
Reported: 2008-02-04 13:46 UTC by Steve Zabele
Modified: 2009-03-24 04:30 UTC (History)
2 users (show)

See Also:
Kernel Version: >=2.6.23
Subsystem:
Regression: ---
Bisected commit-id:


Attachments
TUN_NO_PI and IPv6 fix (683 bytes, patch)
2008-02-07 21:32 UTC, Hideaki YOSHIFUJI
Details | Diff
TUN_NO_PI and IPv6 update (1.00 KB, patch)
2008-03-18 13:27 UTC, Steve Zabele
Details | Diff

Description Steve Zabele 2008-02-04 13:46:12 UTC
Latest working kernel version: None known -- appears to be a historic bug
Earliest failing kernel version: All
Distribution:
Hardware Environment:
Software Environment:
Problem Description:

Steps to reproduce: 

Open a tun device as type TUN, set the TUN_NO_PI flag, and try sending an IPv6 packet. The packet appears at the interface under tcpdumps, but propagates no further. This is because the default protocol info used for tun devices where the TUN_NO_PI flag is set assumes IPv4 as can be seen by the initialization at the top of the tun_get_user function in drivers/net/tun.c file given by

	struct tun_pi pi = { 0, __constant_htons(ETH_P_IP) };

This can easily be fixed by adding a quick check at the top of tun_get_user. Basically the code that used to read

	if (!(tun->flags & TUN_NO_PI)) {
		if ((len -= sizeof(pi)) > count)
			return -EINVAL;

		if(memcpy_fromiovec((void *)&pi, iv, sizeof(pi)))
			return -EFAULT;
	}

when changed to read

	if (!(tun->flags & TUN_NO_PI)) {
		if ((len -= sizeof(pi)) > count)
			return -EINVAL;

		if(memcpy_fromiovec((void *)&pi, iv, sizeof(pi)))
			return -EFAULT;
	}
	else {
	  /* Fixup default pi if IPv6 rather than IPv4 */
	  if (((tun->flags & TUN_TYPE_MASK) == TUN_TUN_DEV) &&
              (*(char *)(iv->iov_base)      == 0x60)) {
	    pi.proto = __constant_htons(ETH_P_IPV6);
	  }
	}

fixes the problem. 

How do we get this in as part of the maintained codebase??

Thanks

Steve
Comment 1 Anonymous Emailer 2008-02-04 14:53:15 UTC
Reply-To: akpm@linux-foundation.org

On Mon,  4 Feb 2008 13:46:13 -0800 (PST)
bugme-daemon@bugzilla.kernel.org wrote:

> http://bugzilla.kernel.org/show_bug.cgi?id=9888
> 
>            Summary: tun device without protocol info header fails under IPv6
>            Product: Networking
>            Version: 2.5
>      KernelVersion: >=2.6.23
>           Platform: All
>         OS/Version: Linux
>               Tree: Mainline
>             Status: NEW
>           Severity: low
>           Priority: P1
>          Component: IPV6
>         AssignedTo: yoshfuji@linux-ipv6.org
>         ReportedBy: steve.zabele@baesystems.com
> 
> 
> Latest working kernel version: None known -- appears to be a historic bug
> Earliest failing kernel version: All
> Distribution:
> Hardware Environment:
> Software Environment:
> Problem Description:
> 
> Steps to reproduce: 
> 
> Open a tun device as type TUN, set the TUN_NO_PI flag, and try sending an
> IPv6
> packet. The packet appears at the interface under tcpdumps, but propagates no
> further. This is because the default protocol info used for tun devices where
> the TUN_NO_PI flag is set assumes IPv4 as can be seen by the initialization
> at
> the top of the tun_get_user function in drivers/net/tun.c file given by
> 
>         struct tun_pi pi = { 0, __constant_htons(ETH_P_IP) };
> 
> This can easily be fixed by adding a quick check at the top of tun_get_user.
> Basically the code that used to read
> 
>         if (!(tun->flags & TUN_NO_PI)) {
>                 if ((len -= sizeof(pi)) > count)
>                         return -EINVAL;
> 
>                 if(memcpy_fromiovec((void *)&pi, iv, sizeof(pi)))
>                         return -EFAULT;
>         }
> 
> when changed to read
> 
>         if (!(tun->flags & TUN_NO_PI)) {
>                 if ((len -= sizeof(pi)) > count)
>                         return -EINVAL;
> 
>                 if(memcpy_fromiovec((void *)&pi, iv, sizeof(pi)))
>                         return -EFAULT;
>         }
>         else {
>           /* Fixup default pi if IPv6 rather than IPv4 */
>           if (((tun->flags & TUN_TYPE_MASK) == TUN_TUN_DEV) &&
>               (*(char *)(iv->iov_base)      == 0x60)) {
>             pi.proto = __constant_htons(ETH_P_IPV6);
>           }
>         }
> 
> fixes the problem. 
> 
> How do we get this in as part of the maintained codebase??
> 

Please email a tested patch prepared as described in

	Documentation/SubmittingPatches
	Documentation/SubmitChecklist
	http://www.zip.com.au/~akpm/linux/patches/stuff/tpp.txt

to

	Maxim Krasnyansky <maxk@qualcomm.com>
	"David S. Miller" <davem@davemloft.net>
	Andrew Morton <akpm@linux-foundation.org>
	netdev@vger.kernel.org

thanks.
Comment 2 Anonymous Emailer 2008-02-07 20:58:51 UTC
Reply-To: maxk@qualcomm.com

Andrew Morton wrote:
> On Mon,  4 Feb 2008 13:46:13 -0800 (PST)
> bugme-daemon@bugzilla.kernel.org wrote:
>>
>> Open a tun device as type TUN, set the TUN_NO_PI flag, and try sending an
>> IPv6
>> packet. The packet appears at the interface under tcpdumps, but propagates
>> no
>> further. This is because the default protocol info used for tun devices
>> where
>> the TUN_NO_PI flag is set assumes IPv4 as can be seen by the initialization
>> at
>> the top of the tun_get_user function in drivers/net/tun.c file given by
>>
>>         struct tun_pi pi = { 0, __constant_htons(ETH_P_IP) };
>>
>> This can easily be fixed by adding a quick check at the top of tun_get_user.
>> Basically the code that used to read
>>
>>         if (!(tun->flags & TUN_NO_PI)) {
>>                 if ((len -= sizeof(pi)) > count)
>>                         return -EINVAL;
>>
>>                 if(memcpy_fromiovec((void *)&pi, iv, sizeof(pi)))
>>                         return -EFAULT;
>>         }
>>
>> when changed to read
>>
>>         if (!(tun->flags & TUN_NO_PI)) {
>>                 if ((len -= sizeof(pi)) > count)
>>                         return -EINVAL;
>>
>>                 if(memcpy_fromiovec((void *)&pi, iv, sizeof(pi)))
>>                         return -EFAULT;
>>         }
>>         else {
>>           /* Fixup default pi if IPv6 rather than IPv4 */
>>           if (((tun->flags & TUN_TYPE_MASK) == TUN_TUN_DEV) &&
>>               (*(char *)(iv->iov_base)      == 0x60)) {
>>             pi.proto = __constant_htons(ETH_P_IPV6);
>>           }
>>         }
>>
>> fixes the problem. 
>>
>> How do we get this in as part of the maintained codebase??
>>
> 
> Please email a tested patch prepared as described in
> 
>       Documentation/SubmittingPatches
>       Documentation/SubmitChecklist
>       http://www.zip.com.au/~akpm/linux/patches/stuff/tpp.txt
> 
> to
> 
>       Maxim Krasnyansky <maxk@qualcomm.com>
>       "David S. Miller" <davem@davemloft.net>
>       Andrew Morton <akpm@linux-foundation.org>
>       netdev@vger.kernel.org

btw I'd be ok with this fix. But I guess the questions is why not use 
struct tun_pi in the apps instead ?

Max
Comment 3 Hideaki YOSHIFUJI 2008-02-07 21:32:02 UTC
Created attachment 14747 [details]
TUN_NO_PI and IPv6 fix
Comment 4 Steve Zabele 2008-03-18 13:27:08 UTC
Created attachment 15333 [details]
TUN_NO_PI and IPv6 update

The memcpy_fromiovec used in the previous patch advances the iovec internal pointer and decrements the packet length, such that subsequent attempts to pull the packet from the user space buffer finds that the buffer is now too short -- generating an -EFAULT. The enclosed patch instead users copy_from_user and correctly leaves the iovec unmodified
Comment 5 Natalie Protasevich 2008-06-01 22:55:36 UTC
Steve,
Can you please submit the patch to LKML and to Maxim (maxk@qualcomm.com) via email (if you haven't already)?

Note You need to log in before you can comment on or make changes to this bug.