Subject: kern/18102: Network interfaces do not receive VLAN-tagged packet with VLAN id 0.
To: None <gnats-bugs@gnats.netbsd.org>
From: None <haya@ilink.sony.co.jp>
List: netbsd-bugs
Date: 08/29/2002 18:56:18
>Number:         18102
>Category:       kern
>Synopsis:       Network i/f does not receive VLAN-tagged packet w/ VLAN id 0.
>Confidential:   no
>Severity:       non-critical
>Priority:       medium
>Responsible:    kern-bug-people
>State:          open
>Class:          sw-bug
>Submitter-Id:   net
>Arrival-Date:   Thu Aug 29 02:57:00 PDT 2002
>Closed-Date:
>Last-Modified:
>Originator:     HAYAKAWA Koichi
>Release:        NetBSD 1.6E
>Organization:
	
>Environment:
	
	
System: NetBSD trangie 1.6E NetBSD 1.6E (TRANGIE) #20: Tue Aug 20 19:00:22 JST 2002 haya@trangie:/home/haya/src/NetBSD/syssrc/sys/arch/i386/compile/TRANGIE i386
Architecture: i386
Machine: i386
>Description:
	

All VLAN-tagged packets with VLAN id 0 are not received by
NetBSD kernel.

IEEE 802.1D spec says a packet which VLAN tag ID is 0 should
be treated as an untagged packet when it is received.

>How-To-Repeat:
	

1. make NetBSD kernel with pseudodevice vlan enabled.
2. enable a network interface which has vlan capability.
3. send VLAN-tagged packets w/ VLAN id 0 to the network interface.
4. NetBSD wont receive VLAN-tagged packets w/ VLAN id 0.

>Fix:
	

This patch would fix this problem.

-- 
Index: sys/net/if_ethersubr.c
===================================================================
RCS file: /cvsroot/syssrc/sys/net/if_ethersubr.c,v
retrieving revision 1.98
diff -c -r1.98 if_ethersubr.c
*** sys/net/if_ethersubr.c	2002/08/26 01:39:39	1.98
--- sys/net/if_ethersubr.c	2002/08/27 11:38:15
***************
*** 762,798 ****
  	 * see if the device performed the decapsulation and
  	 * provided us with the tag.
  	 */
! 	if (ec->ec_nvlans &&
! 	    (n = m_aux_find(m, AF_LINK, ETHERTYPE_VLAN)) != NULL) {
  #if NVLAN > 0
  		/*
! 		 * vlan_input() will either recursively call ether_input()
! 		 * or drop the packet.
  		 */
- 		vlan_input(ifp, m);
  #else
  		m_freem(m);
- #endif
  		return;
  	}
  
  	/*
  	 * Handle protocols that expect to have the Ethernet header
  	 * (and possibly FCS) intact.
  	 */
  	switch (etype) {
- #if NVLAN > 0
- 	case ETHERTYPE_VLAN:
- 		/*
- 		 * vlan_input() will either recursively call ether_input()
- 		 * or drop the packet.
- 		 */
- 		if (((struct ethercom *)ifp)->ec_nvlans != 0)
- 			vlan_input(ifp, m);
- 		else
- 			m_freem(m);
- 		return;
- #endif /* NVLAN > 0 */
  #if NPPPOE > 0
  	case ETHERTYPE_PPPOEDISC:
  	case ETHERTYPE_PPPOE:
--- 762,846 ----
  	 * see if the device performed the decapsulation and
  	 * provided us with the tag.
  	 */
! 	if ((n = m_aux_find(m, AF_LINK, ETHERTYPE_VLAN)) != NULL) {
  #if NVLAN > 0
+ 		u_int tag;
+ 
+ 		/* m contains a normal ethernet frame, the tag is in m_aux */
+ 		tag = EVL_VLANOFTAG(*mtod(n, int *));
+ 		m_aux_delete(m, n);
+ 
+ 		if (tag != 0) {
+ 			/* VLAN-tagged frame (IEEE 802.1D 3.18) */
+ 			if (ec->ec_nvlans > 0) {
+ 				vlan_input(ifp, m, tag);
+ 			} else {
+ 				m_freem(m);
+ 			}
+ 			return;
+ 		}
  		/*
! 		 * priority-tagged frame (IEEE 802.1D 3.8) must be
! 		 * treated same as non-tagged frame.
  		 */
  #else
  		m_freem(m);
  		return;
+ #endif
  	}
  
+ 	if (etype == ETHERTYPE_VLAN) {
+ #if NVLAN > 0
+ 		u_int tag;
+ 		struct ether_vlan_header *evl;
+ 
+ 		if (m->m_len < sizeof(struct ether_vlan_header) &&
+ 		    (m = m_pullup(m,
+ 			sizeof(struct ether_vlan_header))) == NULL) {
+ 			printf("%s: no memory for VLAN header, "
+ 			    "dropping packet.\n", ifp->if_xname);
+ 			return;
+ 		}
+ 		evl = mtod(m, struct ether_vlan_header *);
+ 		KASSERT(ntohs(evl->evl_encap_proto) == ETHERTYPE_VLAN);
+ 
+ 		tag = EVL_VLANOFTAG(ntohs(evl->evl_tag));
+ 
+ 		/* Find a VLAN-tagged frame but no VLAN if */
+ 		if (tag != 0 && ec->ec_nvlans == 0) {
+ 			m_freem(m);
+ 			return;
+ 		}
+ 
+ 		/*
+ 		 * Restore the original ethertype.  We'll remove
+ 		 * the encapsulation.
+ 		 */
+ 		etype = evl->evl_encap_proto = evl->evl_proto;
+ 
+ 		memmove(mtod(m, caddr_t) + ETHER_VLAN_ENCAP_LEN,
+ 		    mtod(m, caddr_t), sizeof(struct ether_header));
+ 		m_adj(m, ETHER_VLAN_ENCAP_LEN);
+ 
+ 		if (tag != 0) {
+ 			/* VLAN-tagged frame (IEEE 802.1D 3.18) */
+ 			vlan_input(ifp, m, tag);
+ 			return;
+ 		}
+ 		/*
+ 		 * priority-tagged frame (IEEE 802.1D 3.8) must be
+ 		 * treated same as non-tagged frame.
+ 		 */
+ #else
+ 		m_freem(m);
+ #endif /* NVLAN > 0 */
+ 	}
+ 
  	/*
  	 * Handle protocols that expect to have the Ethernet header
  	 * (and possibly FCS) intact.
  	 */
  	switch (etype) {
  #if NPPPOE > 0
  	case ETHERTYPE_PPPOEDISC:
  	case ETHERTYPE_PPPOE:
Index: sys/net/if_vlan.c
===================================================================
RCS file: /cvsroot/syssrc/sys/net/if_vlan.c,v
retrieving revision 1.34
diff -c -r1.34 if_vlan.c
*** sys/net/if_vlan.c	2002/06/11 06:00:57	1.34
--- sys/net/if_vlan.c	2002/08/27 11:38:16
***************
*** 812,882 ****
   * the parent's input routine.
   */
  void
! vlan_input(struct ifnet *ifp, struct mbuf *m)
  {
  	struct ifvlan *ifv;
- 	u_int tag;
- 	struct mbuf *n;
  
! 	n = m_aux_find(m, AF_LINK, ETHERTYPE_VLAN);
! 	if (n) {
! 		/* m contains a normal ethernet frame, the tag is in m_aux */
! 		tag = *mtod(n, int *);
! 		m_aux_delete(m, n);
! 		for (ifv = LIST_FIRST(&ifv_list); ifv != NULL;
! 		    ifv = LIST_NEXT(ifv, ifv_list))
! 			if (ifp == ifv->ifv_p && tag == ifv->ifv_tag)
! 				break;
! 	} else {
! 		switch (ifp->if_type) {
! 		case IFT_ETHER:
! 		    {
! 			struct ether_vlan_header *evl;
! 
! 			if (m->m_len < sizeof(struct ether_vlan_header) &&
! 			    (m = m_pullup(m,
! 			     sizeof(struct ether_vlan_header))) == NULL) {
! 				printf("%s: no memory for VLAN header, "
! 				    "dropping packet.\n", ifp->if_xname);
! 				return;
! 			}
! 			evl = mtod(m, struct ether_vlan_header *);
! 			KASSERT(ntohs(evl->evl_encap_proto) == ETHERTYPE_VLAN);
! 
! 			tag = EVL_VLANOFTAG(ntohs(evl->evl_tag));
! 
! 			/*
! 			 * Restore the original ethertype.  We'll remove
! 			 * the encapsulation after we've found the vlan
! 			 * interface corresponding to the tag.
! 			 */
! 			evl->evl_encap_proto = evl->evl_proto;
  			break;
- 		    }
- 
- 		default:
- 			tag = (u_int) -1;	/* XXX GCC */
- #ifdef DIAGNOSTIC
- 			panic("vlan_input: impossible");
- #endif
- 		}
- 
- 		for (ifv = LIST_FIRST(&ifv_list); ifv != NULL;
- 		     ifv = LIST_NEXT(ifv, ifv_list))
- 			if (ifp == ifv->ifv_p && tag == ifv->ifv_tag)
- 				break;
- 
- 
- 		/*
- 		 * Now, remove the encapsulation header.  The original
- 		 * header has already been fixed up above.
- 		 */
- 		if (ifv) {
- 			memmove(mtod(m, caddr_t) + ifv->ifv_encaplen,
- 			    mtod(m, caddr_t), sizeof(struct ether_header));
- 			m_adj(m, ifv->ifv_encaplen);
- 		}
- 	}
  
  	if (ifv == NULL ||
  	    (ifv->ifv_if.if_flags & (IFF_UP|IFF_RUNNING)) !=
--- 812,825 ----
   * the parent's input routine.
   */
  void
! vlan_input(struct ifnet *ifp, struct mbuf *m, u_int tag)
  {
  	struct ifvlan *ifv;
  
! 	for (ifv = LIST_FIRST(&ifv_list); ifv != NULL;
! 	     ifv = LIST_NEXT(ifv, ifv_list))
! 		if (ifp == ifv->ifv_p && tag == ifv->ifv_tag)
  			break;
  
  	if (ifv == NULL ||
  	    (ifv->ifv_if.if_flags & (IFF_UP|IFF_RUNNING)) !=
Index: sys/net/if_vlanvar.h
===================================================================
RCS file: /cvsroot/syssrc/sys/net/if_vlanvar.h,v
retrieving revision 1.4
diff -c -r1.4 if_vlanvar.h
*** sys/net/if_vlanvar.h	2000/10/03 23:50:52	1.4
--- sys/net/if_vlanvar.h	2002/08/27 11:38:16
***************
*** 91,97 ****
  #define	SIOCGETVLAN	SIOCGIFGENERIC
  
  #ifdef _KERNEL
! void	vlan_input(struct ifnet *, struct mbuf *);
  void	vlan_ifdetach(struct ifnet *);
  #endif	/* _KERNEL */
  
--- 91,97 ----
  #define	SIOCGETVLAN	SIOCGIFGENERIC
  
  #ifdef _KERNEL
! void	vlan_input(struct ifnet *, struct mbuf *, u_int);
  void	vlan_ifdetach(struct ifnet *);
  #endif	/* _KERNEL */
  
-- 
>Release-Note:
>Audit-Trail:
>Unformatted: