Skip site navigation (1)Skip section navigation (2)
Date:      Sat, 27 Feb 1999 23:37:10 -0500 (EST)
From:      Bill Paul <wpaul@skynet.ctr.columbia.edu>
To:        current@freebsd.org
Subject:   Request for review: changes to if_vlan.c
Message-ID:  <199902280437.XAA26179@skynet.ctr.columbia.edu>

next in thread | raw e-mail | index | archive | help
While trying to figure out a way to support VLANs with the Alteon
gigabit NIC, I stumled across /sys/net/if_vlan.c. It seems simple
enough: pseudo IP interfaces are created which interacts with an
underlying physical interface and fixes up frame headers to deal
with VLAN tag encapsulation and extraction.

Unfortunately, the code totally fails to take into account the
possibility that the underlying interface can perform VLAN tag
insertion and extraction all by itself. The Alteon gigabit NIC is
just such an example: if you select 'VLAN assist' when configuring
the send/receive rings, the firmware will handle all the VLAN
naughty bits for you.

However, I still want to be able to use the VLAN pseudo interfaces
that if_vlan.c. provides, so to that end I tweaked it a little to
handle interfaces that do their own VLAN tag handling. The diffs
are appended to this message. What I did was change vlan_output()
so that if the IFF_LINK1 flag is set on the vlan pseudo interface,
the packet will be forwarded unmolested to the interface's output
routine, with a pointer to the vlan's ifnet structure in the mbuf
packet header. The underlying driver code can then track down the
vlan interface's structure information and find the vlan tag number
(which it needs to provide in the TX descriptor).

Reception is another issue; the existing vlan_input() routine expects
that it can find the 'parent' driver's ifnet structure in the mbuf
packet header and tries to extract the vlan tag directly from the
ethernet header (which it then uses to track down the associated
vlan pseudo interface). With the Alteon NIC, the driver's receive
routine already knows the tag number, but in order to get it to the
vlan code it would have to mangle the header on the packet that it
just received, which is pointless given that the firmware already went
to the trouble of finding it to begin with. Consequently, I added a
second input routine, vlan_input_tag(), which works almost like the
original vlan_input() except that the tag can pe specified explicitly
as an argument.

Lastly, I found what I think is a bug in vlan_output(). If the code
finds that the parent interface's output queue is full, it does
an IF_QFULL(), but then goes on try and queue the packet with
IF_ENQUEUE() anyway. I think this is wrong, and I inserted a
'continue' statement rahter than let the code fall through like
it was before. I'm not certain this is the correct fix though...
possibly vlan_output() should return with ENOBUFS.

Interested persons should review the diffs and pipe up if they have
some passionate argument argument against them.

Also, I should point out that while if_vlan provides the necessary
kernel hackery to implement VLANs, there isn't any user space utility
for configuring vlan interfaces (ifconfig doesn't seem to have any
vlan-specific code that I could see, and is no vlanconfig). There
also is no vlan(4) man page. If whoever originally added the vlan
code has plans to fix these two things, then by all means do so,
otherwise I'm going to take it upon myself to hack up ifconfig and
write the man pages myself.

-Bill

-- 
=============================================================================
-Bill Paul            (212) 854-6020 | System Manager, Master of Unix-Fu
Work:         wpaul@ctr.columbia.edu | Center for Telecommunications Research
Home:  wpaul@skynet.ctr.columbia.edu | Columbia University, New York City
=============================================================================
 "It is not I who am crazy; it is I who am mad!" - Ren Hoek, "Space Madness"
=============================================================================

*** if_vlan.c.orig	Sat Feb 27 22:52:58 1999
--- if_vlan.c	Sat Feb 27 23:04:41 1999
***************
*** 39,44 ****
--- 39,48 ----
   * ether_output() left on our output queue queue when it calls
   * if_start(), rewrite them for use by the real outgoing interface,
   * and ask it to send them.
+  *
+  * XXX It's incorrect to assume that we must always kludge up
+  * headers on the physical device's behalf: some devices support
+  * VLAN tag insersion and extraction in firmware.
   */
  
  #include "vlan.h"
***************
*** 113,124 ****
  		ifp->if_resolvemulti = 0;
  	}
  }
! PSEUDO_SET(vlaninit, if_vlan);
  
  static void
  vlan_ifinit(void *foo)
  {
! 	;
  }
  
  static void
--- 117,128 ----
  		ifp->if_resolvemulti = 0;
  	}
  }
! PSEUDO_SET(vlaninit, if_vlan)
  
  static void
  vlan_ifinit(void *foo)
  {
! 	return;
  }
  
  static void
***************
*** 142,163 ****
  			bpf_mtap(ifp, m);
  #endif /* NBPFILTER > 0 */
  
- 		M_PREPEND(m, EVL_ENCAPLEN, M_DONTWAIT);
- 		if (m == 0)
- 			continue;
- 		/* M_PREPEND takes care of m_len, m_pkthdr.len for us */
- 
  		/*
! 		 * Transform the Ethernet header into an Ethernet header
! 		 * with 802.1Q encapsulation.
  		 */
! 		bcopy(mtod(m, char *) + EVL_ENCAPLEN, mtod(m, char *),
! 		      sizeof(struct ether_header));
! 		evl = mtod(m, struct ether_vlan_header *);
! 		evl->evl_proto = evl->evl_encap_proto;
! 		evl->evl_encap_proto = htons(vlan_proto);
! 		evl->evl_tag = htons(ifv->ifv_tag);
! 		printf("vlan_start: %*D\n", sizeof *evl, (char *)evl, ":");
  
  		/*
  		 * Send it, precisely as ether_output() would have.
--- 146,183 ----
  			bpf_mtap(ifp, m);
  #endif /* NBPFILTER > 0 */
  
  		/*
! 		 * If the LINK1 flag is set, it means the underlying interface
! 		 * can do VLAN tag insertion itself and doesn't require us to
! 	 	 * create a special header for it. In this case, we just pass
! 		 * the packet along. However, we need some way to tell the
! 		 * interface where the packet came from so that it knows how
! 		 * to find the VLAN tag to use, so we set the rcvif in the
! 		 * mbuf header to our ifnet.
  		 */
! 		if (ifp->if_flags & IFF_LINK1) {
! 			m->m_pkthdr.rcvif = ifp;
! 		} else {
! 			M_PREPEND(m, EVL_ENCAPLEN, M_DONTWAIT);
! 			if (m == 0)
! 				continue;
! 			/* M_PREPEND takes care of m_len, m_pkthdr.len for us */
! 
! 			/*
! 			 * Transform the Ethernet header into an Ethernet header
! 			 * with 802.1Q encapsulation.
! 			 */
! 			bcopy(mtod(m, char *) + EVL_ENCAPLEN, mtod(m, char *),
! 			      sizeof(struct ether_header));
! 			evl = mtod(m, struct ether_vlan_header *);
! 			evl->evl_proto = evl->evl_encap_proto;
! 			evl->evl_encap_proto = htons(vlan_proto);
! 			evl->evl_tag = htons(ifv->ifv_tag);
! #ifdef DEBUG
! 			printf("vlan_start: %*D\n", sizeof *evl,
! 			    (char *)evl, ":");
! #endif
! 		}
  
  		/*
  		 * Send it, precisely as ether_output() would have.
***************
*** 166,177 ****
--- 186,244 ----
  		if (IF_QFULL(&p->if_snd)) {
  			IF_DROP(&p->if_snd);
  				/* XXX stats */
+ 			continue;
  		}
  		IF_ENQUEUE(&p->if_snd, m);
  		if ((p->if_flags & IFF_OACTIVE) == 0)
  			p->if_start(p);
  	}
  	ifp->if_flags &= ~IFF_OACTIVE;
+ 
+ 	return;
+ }
+ 
+ void
+ vlan_input_t(struct ether_header *eh, struct mbuf *m, u_int16_t t)
+ {
+ 	int i;
+ 	struct ifvlan *ifv;
+ 
+ 	for (i = 0; i < NVLAN; i++) {
+ 		ifv = &ifv_softc[i];
+ 		if (ifv->ifv_tag == t)
+ 			break;
+ 	}
+ 
+ 	if (i >= NVLAN || (ifv->ifv_if.if_flags & IFF_UP) == 0) {
+ 		m_freem(m);
+ 		ifv->ifv_p->if_data.ifi_noproto++;
+ 		return;
+ 	}
+ 
+ 	/*
+ 	 * Having found a valid vlan interface corresponding to
+ 	 * the given source interface and vlan tag, run the
+ 	 * the real packet through ethert_input().
+ 	 */
+ 	m->m_pkthdr.rcvif = &ifv->ifv_if;
+ 
+ #if NBPFILTER > 0
+ 	if (ifv->ifv_if.if_bpf) {
+ 		/*
+ 		 * Do the usual BPF fakery.  Note that we don't support
+ 		 * promiscuous mode here, since it would require the
+ 		 * drivers to know about VLANs and we're not ready for
+ 		 * that yet.
+ 		 */
+ 		struct mbuf m0;
+ 		m0.m_next = m;
+ 		m0.m_len = sizeof(struct ether_header);
+ 		m0.m_data = (char *)eh;
+ 		bpf_mtap(&ifv->ifv_if, &m0);
+ 	}
+ #endif
+ 	ether_input(&ifv->ifv_if, eh, m);
+ 	return;
  }
  
  int
***************
*** 248,254 ****
  
  	/*
  	 * Set up our ``Ethernet address'' to reflect the underlying
! 	 * physical interface's.
  	 */
  	ifa1 = ifnet_addrs[ifv->ifv_if.if_index - 1];
  	ifa2 = ifnet_addrs[p->if_index - 1];
--- 315,321 ----
  
  	/*
  	 * Set up our ``Ethernet address'' to reflect the underlying
! 	 * physical interfaces.
  	 */
  	ifa1 = ifnet_addrs[ifv->ifv_if.if_index - 1];
  	ifa2 = ifnet_addrs[p->if_index - 1];
***************
*** 303,308 ****
--- 370,377 ----
  	case SIOCSIFMTU:
  		/*
  		 * Set the interface MTU.
+ 		 * This is bogus. The underlying interface might support
+ 	 	 * jumbo frames.
  		 */
  		if (ifr->ifr_mtu > ETHERMTU) {
  			error = EINVAL;
*** if_vlan_var.h.orig	Sat Feb 27 22:52:58 1999
--- if_vlan_var.h	Sat Feb 27 22:53:21 1999
***************
*** 79,84 ****
--- 79,86 ----
  /* shared with if_ethersubr.c: */
  extern	u_int vlan_proto;
  extern	int vlan_input(struct ether_header *eh, struct mbuf *m);
+ extern	void vlan_input_tag(struct ether_header *eh,
+ 			struct mbuf *m, u_int16_t t);
  #endif
  
  #endif /* _NET_IF_VLAN_VAR_H_ */


To Unsubscribe: send mail to majordomo@FreeBSD.org
with "unsubscribe freebsd-current" in the body of the message




Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?199902280437.XAA26179>