tech-net archive

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]

Re: bridges, vlans, and xen, oh my!



>> Conceptually, what I want is a vlan interface that selects for
>> untagged packets on input and does not add any tag on output
> I've been bugged by that in the past, too.  [...]

> It should actually be very easy to do, I just never got around taking
> the time to do it.

It is easy.  I have most of it working now.  The only part that's
missing is some syntactic sugar in ifconfig; with what I have now, you
need to tell ifconfig "vlan 65535" to get an untagged-packets vlan.
(65535 is out of range; vlan tags run from 0 to 4095.)

Patches, relative to the 4.0 source tree, follow my signature.  I think
I've got everything; if these don't work, let me know details and I'll
see if I missed something, or what.

/~\ The ASCII                           der Mouse
\ / Ribbon Campaign
 X  Against HTML                mouse%rodents-montreal.org@localhost
/ \ Email!           7D C8 61 52 5D E7 2D 39  4E F1 31 3E E8 B3 27 4B

diff -u -r base/sys/net/if_ether.h new/sys/net/if_ether.h
--- base/sys/net/if_ether.h     Thu Nov 23 20:04:30 2006
+++ new/sys/net/if_ether.h      Sat Jun 21 00:42:49 2008
@@ -157,6 +157,7 @@
                                                   capabilities to enable */
 
        int     ec_nvlans;                      /* # VLANs on this interface */
+       int     ec_untaggedvlan;                /* "untagged" vlan configured */
 #ifdef MBUFTRACE
        struct  mowner ec_rx_mowner;            /* mbufs received */
        struct  mowner ec_tx_mowner;            /* mbufs transmitted */
diff -u -r base/sys/net/if_ethersubr.c new/sys/net/if_ethersubr.c
--- base/sys/net/if_ethersubr.c Tue Feb 27 18:16:42 2007
+++ new/sys/net/if_ethersubr.c  Sat Jun 21 00:42:50 2008
@@ -773,6 +773,20 @@
        }
 #endif /* NAGR > 0 */
 
+#if NVLAN > 0
+       /*
+        * If an untagged vlan is configured,
+        * vlan_input wants the packet regardless of etype.
+        *
+        * Note that nothing is ever "received" on the parent interface
+        * when an untagged vlan is configured.
+        */
+       if (((struct ethercom *)ifp)->ec_untaggedvlan) {
+               vlan_input(ifp,m);
+               return;
+       }
+#endif /* NVLAN > 0 */
+
        /*
         * Handle protocols that expect to have the Ethernet header
         * (and possibly FCS) intact.
diff -u -r base/sys/net/if_vlan.c new/sys/net/if_vlan.c
--- base/sys/net/if_vlan.c      Wed Nov 15 20:33:40 2006
+++ new/sys/net/if_vlan.c       Sat Jun 21 00:43:50 2008
@@ -177,7 +177,7 @@
 
 static int     vlan_clone_create(struct if_clone *, int);
 static int     vlan_clone_destroy(struct ifnet *);
-static int     vlan_config(struct ifvlan *, struct ifnet *);
+static int     vlan_config(struct ifvlan *, struct ifnet *, u_int16_t);
 static int     vlan_ioctl(struct ifnet *, u_long, caddr_t);
 static void    vlan_start(struct ifnet *);
 static void    vlan_unconfig(struct ifnet *);
@@ -269,7 +269,7 @@
  * Configure a VLAN interface.  Must be called at splnet().
  */
 static int
-vlan_config(struct ifvlan *ifv, struct ifnet *p)
+vlan_config(struct ifvlan *ifv, struct ifnet *p, u_int16_t tag)
 {
        struct ifnet *ifp = &ifv->ifv_if;
        int error;
@@ -277,6 +277,8 @@
        if (ifv->ifv_p != NULL)
                return (EBUSY);
 
+       ifv->ifv_tag = tag;
+
        switch (p->if_type) {
        case IFT_ETHER:
            {
@@ -286,6 +288,9 @@
                ifv->ifv_encaplen = ETHER_VLAN_ENCAP_LEN;
                ifv->ifv_mintu = ETHERMIN;
 
+               if (tag == EVL_UNTAGGED)
+                       ec->ec_untaggedvlan = 1;
+
                /*
                 * If the parent supports the VLAN_MTU capability,
                 * i.e. can Tx/Rx larger than ETHER_MAX_LEN frames,
@@ -368,6 +373,7 @@
 vlan_unconfig(struct ifnet *ifp)
 {
        struct ifvlan *ifv = ifp->if_softc;
+       struct ifvlan *ifv2;
 
        if (ifv->ifv_p == NULL)
                return;
@@ -399,6 +405,17 @@
                        }
                }
 
+               if (ifv->ifv_tag == EVL_UNTAGGED) {
+                       ec->ec_untaggedvlan = 0;
+                       for (ifv2 = LIST_FIRST(&ifv_list); ifv2 != NULL;
+                           ifv2 = LIST_NEXT(ifv2, ifv_list))
+                               if ( (ifv2->ifv_p == ifv->ifv_p) &&
+                                    (ifv2->ifv_tag == EVL_UNTAGGED) ) {
+                                       ec->ec_untaggedvlan = 1;
+                                       break;
+                               }
+               }
+
                ether_ifdetach(ifp);
                vlan_reset_linkname(ifp);
                break;
@@ -526,17 +543,17 @@
                        vlan_unconfig(ifp);
                        break;
                }
-               if (vlr.vlr_tag != EVL_VLANOFTAG(vlr.vlr_tag)) {
-                       error = EINVAL;          /* check for valid tag */
+               if ( (vlr.vlr_tag != EVL_VLANOFTAG(vlr.vlr_tag)) &&
+                    (vlr.vlr_tag != EVL_UNTAGGED) ) { /* check for valid tag */
+                       error = EINVAL;
                        break;
                }
                if ((pr = ifunit(vlr.vlr_parent)) == 0) {
                        error = ENOENT;
                        break;
                }
-               if ((error = vlan_config(ifv, pr)) != 0)
+               if ((error = vlan_config(ifv, pr, vlr.vlr_tag)) != 0)
                        break;
-               ifv->ifv_tag = vlr.vlr_tag;
                ifp->if_flags |= IFF_RUNNING;
 
                /* Update promiscuous mode, if necessary. */
@@ -738,85 +755,90 @@
                        bpf_mtap(ifp->if_bpf, m);
 #endif
                /*
-                * If the parent can insert the tag itself, just mark
-                * the tag in the mbuf header.
+                * EVL_UNTAGGED means "don't tag" on output.
                 */
-               if (ec->ec_capabilities & ETHERCAP_VLAN_HWTAGGING) {
-                       struct m_tag *mtag;
-
-                       mtag = m_tag_get(PACKET_TAG_VLAN, sizeof(u_int),
-                           M_NOWAIT);
-                       if (mtag == NULL) {
-                               ifp->if_oerrors++;
-                               m_freem(m);
-                               continue;
-                       }
-                       *(u_int *)(mtag + 1) = ifv->ifv_tag;
-                       m_tag_prepend(m, mtag);
-               } else {
+               if (ifv->ifv_tag != EVL_UNTAGGED) {
                        /*
-                        * insert the tag ourselves
+                        * If the parent can insert the tag itself, just mark
+                        * the tag in the mbuf header.
                         */
-                       M_PREPEND(m, ifv->ifv_encaplen, M_DONTWAIT);
-                       if (m == NULL) {
-                               printf("%s: unable to prepend encap header",
-                                   ifv->ifv_p->if_xname);
-                               ifp->if_oerrors++;
-                               continue;
-                       }
-
-                       switch (p->if_type) {
-                       case IFT_ETHER:
-                           {
-                               struct ether_vlan_header *evl;
+                       if (ec->ec_capabilities & ETHERCAP_VLAN_HWTAGGING) {
+                               struct m_tag *mtag;
 
-                               if (m->m_len < sizeof(struct ether_vlan_header))
-                                       m = m_pullup(m,
-                                           sizeof(struct ether_vlan_header));
-                               if (m == NULL) {
-                                       printf("%s: unable to pullup encap "
-                                           "header", ifv->ifv_p->if_xname);
+                               mtag = m_tag_get(PACKET_TAG_VLAN, sizeof(u_int),
+                                   M_NOWAIT);
+                               if (mtag == NULL) {
                                        ifp->if_oerrors++;
+                                       m_freem(m);
                                        continue;
                                }
-
-                               /*
-                                * 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;
-                               evl->evl_encap_proto = htons(ETHERTYPE_VLAN);
-                               evl->evl_tag = htons(ifv->ifv_tag);
-
+                               *(u_int *)(mtag + 1) = ifv->ifv_tag;
+                               m_tag_prepend(m, mtag);
+                       } else {
                                /*
-                                * To cater for VLAN-aware layer 2 ethernet
-                                * switches which may need to strip the tag
-                                * before forwarding the packet, make sure
-                                * the packet+tag is at least 68 bytes long.
-                                * This is necessary because our parent will
-                                * only pad to 64 bytes (ETHER_MIN_LEN) and
-                                * some switches will not pad by themselves
-                                * after deleting a tag.
+                                * insert the tag ourselves
                                 */
-                               if (m->m_pkthdr.len <
-                                   (ETHER_MIN_LEN + ETHER_VLAN_ENCAP_LEN)) {
-                                       m_copyback(m, m->m_pkthdr.len,
-                                           (ETHER_MIN_LEN +
-                                            ETHER_VLAN_ENCAP_LEN) -
-                                            m->m_pkthdr.len,
-                                           vlan_zero_pad_buff);
+                               M_PREPEND(m, ifv->ifv_encaplen, M_DONTWAIT);
+                               if (m == NULL) {
+                                       printf("%s: unable to prepend encap 
header",
+                                           ifv->ifv_p->if_xname);
+                                       ifp->if_oerrors++;
+                                       continue;
                                }
-                               break;
-                           }
+
+                               switch (p->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));
+                                       if (m == NULL) {
+                                               printf("%s: unable to pullup 
encap "
+                                                   "header", 
ifv->ifv_p->if_xname);
+                                               ifp->if_oerrors++;
+                                               continue;
+                                       }
+
+                                       /*
+                                        * 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;
+                                       evl->evl_encap_proto = 
htons(ETHERTYPE_VLAN);
+                                       evl->evl_tag = htons(ifv->ifv_tag);
+
+                                       /*
+                                        * To cater for VLAN-aware layer 2 
ethernet
+                                        * switches which may need to strip the 
tag
+                                        * before forwarding the packet, make 
sure
+                                        * the packet+tag is at least 68 bytes 
long.
+                                        * This is necessary because our parent 
will
+                                        * only pad to 64 bytes (ETHER_MIN_LEN) 
and
+                                        * some switches will not pad by 
themselves
+                                        * after deleting a tag.
+                                        */
+                                       if (m->m_pkthdr.len <
+                                           (ETHER_MIN_LEN + 
ETHER_VLAN_ENCAP_LEN)) {
+                                               m_copyback(m, m->m_pkthdr.len,
+                                                   (ETHER_MIN_LEN +
+                                                    ETHER_VLAN_ENCAP_LEN) -
+                                                    m->m_pkthdr.len,
+                                                   vlan_zero_pad_buff);
+                                       }
+                                       break;
+                                   }
 
 #ifdef DIAGNOSTIC
-                       default:
-                               panic("vlan_start: impossible");
+                               default:
+                                       panic("vlan_start: impossible");
 #endif
+                               }
                        }
                }
 
@@ -870,16 +892,20 @@
                                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;
+                       if ( ((struct ethercom *)ifp)->ec_untaggedvlan &&
+                            (ntohs(evl->evl_encap_proto) != ETHERTYPE_VLAN) ) {
+                               tag = EVL_UNTAGGED;
+                       } else {
+                               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;
                    }
 
@@ -905,10 +931,10 @@
        }
 
        /*
-        * Now, remove the encapsulation header.  The original
-        * header has already been fixed up above.
+        * Now, remove the encapsulation header (except for untagged vlans).
+        * The original header has already been fixed up above.
         */
-       if (mtag == NULL) {
+       if ((mtag == NULL) && (tag != EVL_UNTAGGED)) {
                memmove(mtod(m, caddr_t) + ifv->ifv_encaplen,
                    mtod(m, caddr_t), sizeof(struct ether_header));
                m_adj(m, ifv->ifv_encaplen);
diff -u -r base/sys/net/if_vlanvar.h new/sys/net/if_vlanvar.h
--- base/sys/net/if_vlanvar.h   Sun Dec 11 07:24:51 2005
+++ new/sys/net/if_vlanvar.h    Sat Jun 21 00:43:51 2008
@@ -80,6 +80,8 @@
 
 #define        EVL_VLANOFTAG(tag)      ((tag) & 4095)
 #define        EVL_PRIOFTAG(tag)       (((tag) >> 13) & 7)
+/* This does not appear in packets; it is for vlr_tag/ifv_tag use. */
+#define EVL_UNTAGGED           65535
 
 /* Configuration structure for SIOCSETVLAN and SIOCGETVLAN ioctls. */
 struct vlanreq {


Home | Main Index | Thread Index | Old Index