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--