From owner-freebsd-current Sat Feb 27 20:30:46 1999 Delivered-To: freebsd-current@freebsd.org Received: from skynet.ctr.columbia.edu (skynet.ctr.columbia.edu [128.59.64.70]) by hub.freebsd.org (Postfix) with SMTP id 1DFB914FA4 for ; Sat, 27 Feb 1999 20:30:36 -0800 (PST) (envelope-from wpaul@skynet.ctr.columbia.edu) Received: (from wpaul@localhost) by skynet.ctr.columbia.edu (8.6.12/8.6.9) id XAA26179 for current@freebsd.org; Sat, 27 Feb 1999 23:37:12 -0500 From: Bill Paul Message-Id: <199902280437.XAA26179@skynet.ctr.columbia.edu> Subject: Request for review: changes to if_vlan.c To: current@freebsd.org Date: Sat, 27 Feb 1999 23:37:10 -0500 (EST) X-Mailer: ELM [version 2.4 PL24] Content-Type: text Content-Length: 9005 Sender: owner-freebsd-current@FreeBSD.ORG Precedence: bulk X-Loop: FreeBSD.ORG 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