Subject: Re: 802.1Q & ETHER_MAX_LEN
To: None <tech-net@netbsd.org>
From: Jason R Thorpe <thorpej@zembu.com>
List: tech-net
Date: 10/03/2000 12:57:45
--liOOAslEiF7prFVr
Content-Type: text/plain; charset=us-ascii
Content-Disposition: inline

On Tue, Oct 03, 2000 at 09:09:22AM -0700, Jason R Thorpe wrote:

 > Anyway, I'm cooking up a patch for this... stay tuned.

Attached is a preliminary patch, along with support for the
ETHERCAP_VLAN_MTU capability in the `tlp' driver.  Also note
that there is some preliminary work in the direction of supporting
802.1Q VLANs on FDDI, as well.

-- 
        -- Jason R. Thorpe <thorpej@zembu.com>

--liOOAslEiF7prFVr
Content-Type: text/plain; charset=us-ascii
Content-Description: VLAN MTU diff
Content-Disposition: attachment; filename="vlan_mtu.diff"

Index: net/if_ether.h
===================================================================
RCS file: /cvsroot/syssrc/sys/net/if_ether.h,v
retrieving revision 1.18
diff -c -r1.18 if_ether.h
*** net/if_ether.h	2000/09/28 07:15:27	1.18
--- net/if_ether.h	2000/10/03 19:49:04
***************
*** 49,54 ****
--- 49,59 ----
  #define	ETHER_MAX_LEN	1518	/* maximum frame length, including CRC */
  
  /*
+  * Some Ethernet extensions.
+  */
+ #define	ETHER_VLAN_ENCAP_LEN 4	/* length of 802.1Q VLAN encapsulation */
+ 
+ /*
   * Ethernet address - 6 octets
   * this is only used by the ethers(3) functions.
   */
***************
*** 127,135 ****
   */
  struct	ethercom {
  	struct	 ifnet ec_if;			/* network-visible interface */
! 	LIST_HEAD(, ether_multi) ec_multiaddrs;	/* list of ether multicast addrs */
! 	int	 ec_multicnt;			/* length of ec_multiaddrs list */
  };
  
  #ifdef	_KERNEL
  extern u_int8_t etherbroadcastaddr[ETHER_ADDR_LEN];
--- 132,151 ----
   */
  struct	ethercom {
  	struct	 ifnet ec_if;			/* network-visible interface */
! 	LIST_HEAD(, ether_multi) ec_multiaddrs;	/* list of ether multicast
! 						   addrs */
! 	int	 ec_multicnt;			/* length of ec_multiaddrs
! 						   list */
! 	int	 ec_capabilities;		/* capabilities, provided by
! 						   driver */
! 	int	 ec_capenable;			/* tells hardware which
! 						   capabilities to enable */
! 
! 	int	 ec_nvlans;			/* # VLANs on this interface */
  };
+ 
+ #define	ETHERCAP_VLAN_MTU	0x00000001	/* VLAN-compatible MTU */
+ #define	ETHERCAP_VLAN_TAGGING	0x00000002	/* VLAN tag support */
  
  #ifdef	_KERNEL
  extern u_int8_t etherbroadcastaddr[ETHER_ADDR_LEN];
Index: net/if_ethersubr.c
===================================================================
RCS file: /cvsroot/syssrc/sys/net/if_ethersubr.c,v
retrieving revision 1.62
diff -c -r1.62 if_ethersubr.c
*** net/if_ethersubr.c	2000/10/01 23:43:44	1.62
--- net/if_ethersubr.c	2000/10/03 19:49:04
***************
*** 802,809 ****
  void
  ether_ifdetach(struct ifnet *ifp)
  {
  
! 	/* Nothing. */
  }
  
  #if 0
--- 802,814 ----
  void
  ether_ifdetach(struct ifnet *ifp)
  {
+ 	struct sockaddr_dl *sdl = ifp->if_sadl;
  
! 	memset(LLADDR(sdl), 0, ETHER_ADDR_LEN);
! 	sdl->sdl_alen = 0;
! 	sdl->sdl_type = 0;
! 
! 	/* XXX Free multiaddrs */
  }
  
  #if 0
Index: net/if_vlan.c
===================================================================
RCS file: /cvsroot/syssrc/sys/net/if_vlan.c,v
retrieving revision 1.9
diff -c -r1.9 if_vlan.c
*** net/if_vlan.c	2000/10/02 14:41:26	1.9
--- net/if_vlan.c	2000/10/03 19:49:05
***************
*** 83,88 ****
--- 83,90 ----
   *	- Need some way to notify vlan interfaces when the parent
   *	  interface changes MTU.
   *
+  *	- Need to notify VLANs when an interface detaches.
+  *
   *	- Need a way to facilitate parent interfaces that can do
   *	  tag insertion and/or extraction in hardware.
   *
***************
*** 117,122 ****
--- 119,155 ----
  
  extern struct	ifaddr **ifnet_addrs;	/* XXX if.c */
  
+ struct vlan_mc_entry {
+ 	LIST_ENTRY(vlan_mc_entry)	mc_entries;
+ 	/*
+ 	 * A key to identify this entry.  The mc_addr below can't be
+ 	 * used since multiple sockaddr may mapped into the same
+ 	 * ether_multi (e.g., AF_UNSPEC).
+ 	 */
+ 	struct ether_multi		*mc_enm;
+ 	struct sockaddr_storage		mc_addr;
+ };
+ 
+ struct ifvlan {
+ 	union {
+ 		struct ethercom ifvu_ec;
+ 	} ifv_u;
+ 	struct ifnet *ifv_p;	/* parent interface of this vlan */
+ 	struct ifv_linkmib {
+ 		int	ifvm_encaplen;	/* encapsulation length */
+ 		u_int16_t ifvm_proto;	/* encapsulation ethertype */
+ 		u_int16_t ifvm_tag;	/* tag to apply on packets */
+ 	} ifv_mib;
+ 	LIST_HEAD(__vlan_mchead, vlan_mc_entry) ifv_mc_listhead;
+ 	LIST_ENTRY(ifvlan) ifv_list;
+ };
+ 
+ #define	ifv_ec		ifv_u.ifvu_ec
+ 
+ #define	ifv_if		ifv_ec.ec_if
+ #define	ifv_encaplen	ifv_mib.ifvm_encaplen
+ #define	ifv_tag		ifv_mib.ifvm_tag
+ 
  static int	vlan_clone_create(struct if_clone *, int);
  static void	vlan_clone_destroy(struct ifnet *);
  static int	vlan_config(struct ifvlan *, struct ifnet *);
***************
*** 147,153 ****
  {
  	struct ifvlan *ifv;
  	struct ifnet *ifp;
- 	u_int8_t eaddr[ETHER_ADDR_LEN];
  
  	ifv = malloc(sizeof(struct ifvlan), M_DEVBUF, M_WAIT);
  	memset(ifv, 0, sizeof(struct ifvlan));
--- 180,185 ----
***************
*** 162,178 ****
  	ifp->if_ioctl = vlan_ioctl;
  
  	if_attach(ifp);
- 	memset(eaddr, 0, sizeof(eaddr));
- 	ether_ifattach(ifp, eaddr);
  
- 	ifp->if_hdrlen = sizeof(struct ether_vlan_header);
- 	ifp->if_mtu = ETHERMTU - EVL_ENCAPLEN;
- 
- #if NBPFILTER > 0
- 	bpfattach(&ifp->if_bpf, ifp, DLT_EN10MB, 
- 	    sizeof(struct ether_header));
- #endif
- 
  	return (0);
  }
  
--- 194,200 ----
***************
*** 201,238 ****
  static int
  vlan_config(struct ifvlan *ifv, struct ifnet *p)
  {
! 	struct ifaddr *ifa1, *ifa2;
! 	struct sockaddr_dl *sdl1, *sdl2;
  
- 	if (p->if_data.ifi_type != IFT_ETHER)
- 		return (EPROTONOSUPPORT);
  	if (ifv->ifv_p != NULL)
  		return (EBUSY);
  	ifv->ifv_p = p;
! 	ifv->ifv_if.if_mtu = p->if_data.ifi_mtu - EVL_ENCAPLEN;
  	ifv->ifv_if.if_flags = p->if_flags;
  
- 	/*
- 	 * Set up our ``Ethernet address'' to match the underlying
- 	 * physical interface's.
- 	 */
- 	ifa1 = ifnet_addrs[ifv->ifv_if.if_index];
- 	ifa2 = ifnet_addrs[p->if_index];
- 	sdl1 = (struct sockaddr_dl *)ifa1->ifa_addr;
- 	sdl2 = (struct sockaddr_dl *)ifa2->ifa_addr;
- 	sdl1->sdl_type = IFT_ETHER;
- 	sdl1->sdl_alen = ETHER_ADDR_LEN;
- 	memcpy(LLADDR(sdl1), LLADDR(sdl2), ETHER_ADDR_LEN);
- 	memcpy(LLADDR(ifv->ifv_ec.ec_if.if_sadl), LLADDR(sdl2), ETHER_ADDR_LEN);
  	return (0);
  }
  
  static int
  vlan_unconfig(struct ifnet *ifp)
  {
! 	struct ifaddr *ifa;
! 	struct sockaddr_dl *sdl;
! 	struct ifvlan *ifv;
  	int s;
  
  	ifv = ifp->if_softc;
--- 223,296 ----
  static int
  vlan_config(struct ifvlan *ifv, struct ifnet *p)
  {
! 	struct ifnet *ifp = &ifv->ifv_if;
! 	int error;
  
  	if (ifv->ifv_p != NULL)
  		return (EBUSY);
+ 
+ 	switch (p->if_data.ifi_type) {
+ 	case IFT_ETHER:
+ 	    {
+ 		struct ethercom *ec = (void *) p;
+ 
+ 		/*
+ 		 * The parent device MUST support VLAN-compatible MTUs,
+ 		 * that is be able to transmit and receive ETHER_MAX_LEN +
+ 		 * ETHER_VLAN_ENCAP_LEN byte frames.
+ 		 */
+ 		if ((ec->ec_capabilities & ETHERCAP_VLAN_MTU) == 0)
+ 			return (EINVAL);
+ 
+ 		if (ec->ec_nvlans++ == 0) {
+ 			/*
+ 			 * Enable Tx/Rx of VLAN-sized frames.
+ 			 */
+ 			ec->ec_capenable |= ETHERCAP_VLAN_MTU;
+ 			if (p->if_flags & IFF_UP) {
+ 				struct ifreq ifr;
+ 
+ 				ifr.ifr_flags = p->if_flags;
+ 				error = (*p->if_ioctl)(p, SIOCSIFFLAGS,
+ 				    (caddr_t) &ifr);
+ 				if (error) {
+ 					if (ec->ec_nvlans-- == 1)
+ 						ec->ec_capenable &=
+ 						    ~ETHERCAP_VLAN_MTU;
+ 					return (error);
+ 				}
+ 			}
+ 		}
+ 
+ 		ifv->ifv_encaplen = ETHER_VLAN_ENCAP_LEN;
+ 
+ 		/*
+ 		 * We inherit the parent's Ethernet address.
+ 		 */
+ 		ether_ifattach(ifp, LLADDR(p->if_sadl));
+ 		ifp->if_hdrlen = sizeof(struct ether_vlan_header); /* XXX? */
+ #if NBPFILTER > 0
+ 		bpfattach(&ifp->if_bpf, ifp, DLT_EN10MB,
+ 		    sizeof(struct ether_header));
+ #endif
+ 		break;
+ 	    }
+ 
+ 	default:
+ 		return (EPROTONOSUPPORT);
+ 	}
+ 
  	ifv->ifv_p = p;
! 	ifv->ifv_if.if_mtu = p->if_data.ifi_mtu;
  	ifv->ifv_if.if_flags = p->if_flags;
  
  	return (0);
  }
  
  static int
  vlan_unconfig(struct ifnet *ifp)
  {
! 	struct ifvlan *ifv = ifp->if_softc;
  	int s;
  
  	ifv = ifp->if_softc;
***************
*** 249,264 ****
  	vlan_purgemulti(ifv);
  
  	/* Disconnect from parent. */
! 	ifv->ifv_p = NULL;
! 	ifv->ifv_if.if_mtu = ETHERMTU - EVL_ENCAPLEN;
  
! 	/* Clear our MAC address. */
! 	ifa = ifnet_addrs[ifv->ifv_if.if_index];
! 	sdl = (struct sockaddr_dl *)ifa->ifa_addr;
! 	sdl->sdl_type = IFT_ETHER;
! 	sdl->sdl_alen = ETHER_ADDR_LEN;
! 	memset(LLADDR(sdl), 0, ETHER_ADDR_LEN);
! 	memset(LLADDR(ifv->ifv_ec.ec_if.if_sadl), 0, ETHER_ADDR_LEN);
  
  	splx(s);
  	return (0);
--- 307,346 ----
  	vlan_purgemulti(ifv);
  
  	/* Disconnect from parent. */
! 	switch (ifv->ifv_p->if_data.ifi_type) {
! 	case IFT_ETHER:
! 	    {
! 		struct ethercom *ec = (void *) ifv->ifv_p;
! 
! 		if (ec->ec_nvlans-- == 1) {
! 			/*
! 			 * Disable Tx/Rx of VLAN-sized frames.
! 			 */
! 			ec->ec_capenable &= ~ETHERCAP_VLAN_MTU;
! 			if (ifv->ifv_p->if_flags & IFF_UP) {
! 				struct ifreq ifr;
! 
! 				ifr.ifr_flags = ifv->ifv_p->if_flags;
! 				(void) (*ifv->ifv_p->if_ioctl)(ifv->ifv_p,
! 				    SIOCSIFFLAGS, (caddr_t) &ifr);
! 			}
! 		}
! 
! #if NBPFILTER > 0
! 		bpfdetach(ifp); 
! #endif
! 		ether_ifdetach(ifp);
! 		break;
! 	    }
! 
! #ifdef DIAGNOSTIC
! 	default:
! 		panic("vlan_unconfig: impossible");
! #endif
! 	}
  
! 	ifv->ifv_p = NULL;
! 	ifv->ifv_if.if_mtu = ETHERMTU;
  
  	splx(s);
  	return (0);
***************
*** 303,310 ****
  
  	case SIOCSIFMTU:
  		if (ifv->ifv_p != NULL) {
! 			if (ifr->ifr_mtu > ifv->ifv_p->if_mtu - EVL_ENCAPLEN ||
! 			    ifr->ifr_mtu < ETHERMIN + EVL_ENCAPLEN)
  				error = EINVAL;
  			else
  				ifp->if_mtu = ifr->ifr_mtu;
--- 385,392 ----
  
  	case SIOCSIFMTU:
  		if (ifv->ifv_p != NULL) {
! 			if (ifr->ifr_mtu > ifv->ifv_p->if_mtu ||
! 			    ifr->ifr_mtu < ETHERMIN /* XXX */)
  				error = EINVAL;
  			else
  				ifp->if_mtu = ifr->ifr_mtu;
***************
*** 519,525 ****
  		 * XXX Should handle the case where the underlying hardware
  		 * interface can do VLAN tag insertion itself.
  		 */
! 		M_PREPEND(m, EVL_ENCAPLEN, M_DONTWAIT);
  		if (m == NULL) {
  			printf("%s: M_PREPEND failed", ifv->ifv_p->if_xname);
  			ifp->if_ierrors++;
--- 601,607 ----
  		 * XXX Should handle the case where the underlying hardware
  		 * interface can do VLAN tag insertion itself.
  		 */
! 		M_PREPEND(m, ifv->ifv_encaplen, M_DONTWAIT);
  		if (m == NULL) {
  			printf("%s: M_PREPEND failed", ifv->ifv_p->if_xname);
  			ifp->if_ierrors++;
***************
*** 538,544 ****
  		 * Transform the Ethernet header into an Ethernet header
  		 * with 802.1Q encapsulation.
  		 */
! 		memmove(mtod(m, caddr_t), mtod(m, caddr_t) + EVL_ENCAPLEN, 
  		    sizeof(struct ether_header));
  		evl = mtod(m, struct ether_vlan_header *);
  		evl->evl_proto = evl->evl_encap_proto;
--- 620,626 ----
  		 * Transform the Ethernet header into an Ethernet header
  		 * with 802.1Q encapsulation.
  		 */
! 		memmove(mtod(m, caddr_t), mtod(m, caddr_t) + ifv->ifv_encaplen, 
  		    sizeof(struct ether_header));
  		evl = mtod(m, struct ether_vlan_header *);
  		evl->evl_proto = evl->evl_encap_proto;
***************
*** 607,615 ****
  	 * source interface and vlan tag, remove the encapsulation.
  	 */
  	evl->evl_encap_proto = evl->evl_proto;
! 	memmove(mtod(m, caddr_t) + EVL_ENCAPLEN, mtod(m, caddr_t),
! 	    EVL_ENCAPLEN);
! 	m_adj(m, EVL_ENCAPLEN);
  
  	m->m_pkthdr.rcvif = &ifv->ifv_if;
  	ifv->ifv_if.if_ipackets++;
--- 689,697 ----
  	 * source interface and vlan tag, remove the encapsulation.
  	 */
  	evl->evl_encap_proto = evl->evl_proto;
! 	memmove(mtod(m, caddr_t) + ifv->ifv_encaplen, mtod(m, caddr_t),
! 	    ifv->ifv_encaplen);
! 	m_adj(m, ifv->ifv_encaplen);
  
  	m->m_pkthdr.rcvif = &ifv->ifv_if;
  	ifv->ifv_if.if_ipackets++;
Index: net/if_vlanvar.h
===================================================================
RCS file: /cvsroot/syssrc/sys/net/if_vlanvar.h,v
retrieving revision 1.2
diff -c -r1.2 if_vlanvar.h
*** net/if_vlanvar.h	2000/09/28 07:20:56	1.2
--- net/if_vlanvar.h	2000/10/03 19:49:05
***************
*** 70,102 ****
  #ifndef _NET_IF_VLANVAR_H_
  #define	_NET_IF_VLANVAR_H_
  
- #ifdef _KERNEL
- struct vlan_mc_entry {
- 	LIST_ENTRY(vlan_mc_entry)	mc_entries;
- 	/*
- 	 * A key to identify this entry.  The mc_addr below can't be
- 	 * used since multiple sockaddr may mapped into the same
- 	 * ether_multi (e.g., AF_UNSPEC).
- 	 */
- 	struct ether_multi		*mc_enm;
- 	struct sockaddr_storage		mc_addr;
- };
- 
- struct	ifvlan {
- 	struct	ethercom ifv_ec;
- 	struct	ifnet *ifv_p;	/* parent interface of this vlan */
- 	struct	ifv_linkmib {
- 		int	ifvm_parent;
- 		u_int16_t ifvm_proto; /* encapsulation ethertype */
- 		u_int16_t ifvm_tag; /* tag to apply on packets leaving if */
- 	} ifv_mib;
- 	LIST_HEAD(__vlan_mchead, vlan_mc_entry)	ifv_mc_listhead;
- 	LIST_ENTRY(ifvlan)	ifv_list;
- };
- #define	ifv_if		ifv_ec.ec_if
- #define	ifv_tag		ifv_mib.ifvm_tag
- #endif /* _KERNEL */
- 
  struct ether_vlan_header {
  	u_int8_t	evl_dhost[ETHER_ADDR_LEN];
  	u_int8_t	evl_shost[ETHER_ADDR_LEN];
--- 70,75 ----
***************
*** 107,113 ****
  
  #define	EVL_VLANOFTAG(tag)	((tag) & 4095)
  #define	EVL_PRIOFTAG(tag)	(((tag) >> 13) & 7)
- #define	EVL_ENCAPLEN		4
  
  /* When these sorts of interfaces get their own identifier... */
  #define	IFT_8021_VLAN	IFT_PROPVIRTUAL
--- 80,85 ----
Index: dev/ic/tulip.c
===================================================================
RCS file: /cvsroot/syssrc/sys/dev/ic/tulip.c,v
retrieving revision 1.73
diff -c -r1.73 tulip.c
*** dev/ic/tulip.c	2000/10/03 04:32:00	1.73
--- dev/ic/tulip.c	2000/10/03 19:49:08
***************
*** 503,508 ****
--- 503,513 ----
  	ifp->if_watchdog = tlp_watchdog;
  
  	/*
+ 	 * We can support 802.1Q VLAN-sized frames.
+ 	 */
+ 	sc->sc_ethercom.ec_capabilities |= ETHERCAP_VLAN_MTU;
+ 
+ 	/*
  	 * Attach the interface.
  	 */
  	if_attach(ifp);
***************
*** 1831,1836 ****
--- 1836,1851 ----
  
  	sc->sc_rxint_mask = STATUS_RI|STATUS_RU|STATUS_RWT;
  	sc->sc_txint_mask = STATUS_TI|STATUS_UNF|STATUS_TJT;
+ 
+ 	/*
+ 	 * If 802.1Q VLAN MTU is enabled, we must ignore Receive Watchdog
+ 	 * and Transmit Jabber errors.
+ 	 */
+ 	if (sc->sc_ethercom.ec_capenable & ETHERCAP_VLAN_MTU) {
+ 		sc->sc_inten &= ~(STATUS_RWT | STATUS_TJT);
+ 		sc->sc_rxint_mask &= ~STATUS_RWT;
+ 		sc->sc_txint_mask &= ~STATUS_TJT;
+ 	}
  
  	switch (sc->sc_chip) {
  	case TULIP_CHIP_WB89C840F:

--liOOAslEiF7prFVr--