o eliminate separate callback interface for h/w tagged input packets; instead

drivers "tag packets" with an m_tag and the input packet handling recognizes
  such packets and does the right thing
o track the number of active vlans on an interface; this lets lots of places
  only do vlan-specific processing when needed
o track changes to ether_ifdetach/ether_ifattach
o track bpf changes
o eliminate the use of M_PROTO1 for communicating to drivers about tagged
  packets
o eliminate the use of IFF_LINK0 for drivers communicating to the vlan code
  that they support h/w tagging; replaced by explicit interface capabilities
o add ifnet capabilities for h/w tagging and support of "large mtu's"
o use new interface capabilities to auto-configure use of large mtu's and h/w
  tagging
o add support for proper handling of promiscuous mode
o document driver/vlan communication conventions

Reviewed by:	many
Approved by:	re
This commit is contained in:
Sam Leffler 2002-11-14 23:43:16 +00:00
parent 76cfd3001b
commit a3814acf84
2 changed files with 301 additions and 163 deletions

View File

@ -39,19 +39,6 @@
* ether_output() left on our output 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 insertion and extraction in firmware. For these cases,
* one can change the behavior of the vlan interface by setting
* the LINK0 flag on it (that is setting the vlan interface's LINK0
* flag, _not_ the parent's LINK0 flag; we try to leave the parent
* alone). If the interface has the LINK0 flag set, then it will
* not modify the ethernet header on output, because the parent
* can do that for itself. On input, the parent can call vlan_input_tag()
* directly in order to supply us with an incoming mbuf and the vlan
* tag value that goes with it.
*/
#include "opt_inet.h"
@ -82,6 +69,34 @@
#define VLANNAME "vlan"
struct vlan_mc_entry {
struct ether_addr mc_addr;
SLIST_ENTRY(vlan_mc_entry) mc_entries;
};
struct ifvlan {
struct arpcom ifv_ac; /* make this an interface */
struct ifnet *ifv_p; /* parent inteface of this vlan */
struct ifv_linkmib {
int ifvm_parent;
int ifvm_encaplen; /* encapsulation length */
int ifvm_mtufudge; /* MTU fudged by this much */
int ifvm_mintu; /* min transmission unit */
u_int16_t ifvm_proto; /* encapsulation ethertype */
u_int16_t ifvm_tag; /* tag to apply on packets leaving if */
} ifv_mib;
SLIST_HEAD(__vlan_mchead, vlan_mc_entry) vlan_mc_listhead;
LIST_ENTRY(ifvlan) ifv_list;
int ifv_flags;
};
#define ifv_if ifv_ac.ac_if
#define ifv_tag ifv_mib.ifvm_tag
#define ifv_encaplen ifv_mib.ifvm_encaplen
#define ifv_mtufudge ifv_mib.ifvm_mtufudge
#define ifv_mintu ifv_mib.ifvm_mintu
#define IFVF_PROMISC 0x01 /* promiscuous mode enabled */
SYSCTL_DECL(_net_link);
SYSCTL_NODE(_net_link, IFT_L2VLAN, vlan, CTLFLAG_RW, 0, "IEEE 802.1Q VLAN");
SYSCTL_NODE(_net_link_vlan, PF_LINK, link, CTLFLAG_RW, 0, "for consistency");
@ -93,9 +108,7 @@ static int vlan_clone_create(struct if_clone *, int);
static void vlan_clone_destroy(struct ifnet *);
static void vlan_start(struct ifnet *ifp);
static void vlan_ifinit(void *foo);
static int vlan_input(struct ether_header *eh, struct mbuf *m);
static int vlan_input_tag(struct ether_header *eh, struct mbuf *m,
u_int16_t t);
static void vlan_input(struct ifnet *ifp, struct mbuf *m);
static int vlan_ioctl(struct ifnet *ifp, u_long cmd, caddr_t addr);
static int vlan_setmulti(struct ifnet *ifp);
static int vlan_unconfig(struct ifnet *ifp);
@ -169,6 +182,21 @@ vlan_setmulti(struct ifnet *ifp)
return(0);
}
/*
* VLAN support can be loaded as a module. The only place in the
* system that's intimately aware of this is ether_input. We hook
* into this code through vlan_input_p which is defined there and
* set here. Noone else in the system should be aware of this so
* we use an explicit reference here.
*
* NB: Noone should ever need to check if vlan_input_p is null or
* not. This is because interfaces have a count of the number
* of active vlans (if_nvlans) and this should never be bumped
* except by vlan_config--which is in this module so therefore
* the module must be loaded and vlan_input_p must be non-NULL.
*/
extern void (*vlan_input_p)(struct ifnet *, struct mbuf *);
static int
vlan_modevent(module_t mod, int type, void *data)
{
@ -177,13 +205,11 @@ vlan_modevent(module_t mod, int type, void *data)
case MOD_LOAD:
LIST_INIT(&ifv_list);
vlan_input_p = vlan_input;
vlan_input_tag_p = vlan_input_tag;
if_clone_attach(&vlan_cloner);
break;
case MOD_UNLOAD:
if_clone_detach(&vlan_cloner);
vlan_input_p = NULL;
vlan_input_tag_p = NULL;
while (!LIST_EMPTY(&ifv_list))
vlan_clone_destroy(&LIST_FIRST(&ifv_list)->ifv_if);
break;
@ -225,13 +251,12 @@ vlan_clone_create(struct if_clone *ifc, int unit)
ifp->if_init = vlan_ifinit;
ifp->if_start = vlan_start;
ifp->if_ioctl = vlan_ioctl;
ifp->if_output = ether_output;
ifp->if_snd.ifq_maxlen = ifqmaxlen;
ether_ifattach(ifp, ETHER_BPF_SUPPORTED);
ether_ifattach(ifp, ifv->ifv_ac.ac_enaddr);
/* Now undo some of the damage... */
ifp->if_baudrate = 0;
ifp->if_data.ifi_type = IFT_L2VLAN;
ifp->if_data.ifi_hdrlen = EVL_ENCAPLEN;
ifp->if_type = IFT_L2VLAN;
ifp->if_hdrlen = ETHER_VLAN_ENCAP_LEN;
return (0);
}
@ -247,7 +272,7 @@ vlan_clone_destroy(struct ifnet *ifp)
vlan_unconfig(ifp);
splx(s);
ether_ifdetach(ifp, ETHER_BPF_SUPPORTED);
ether_ifdetach(ifp);
free(ifv, M_VLAN);
}
@ -274,8 +299,7 @@ vlan_start(struct ifnet *ifp)
IF_DEQUEUE(&ifp->if_snd, m);
if (m == 0)
break;
if (ifp->if_bpf)
bpf_mtap(ifp, m);
BPF_MTAP(ifp, m);
/*
* Do not run parent's if_start() if the parent is not up,
@ -284,53 +308,54 @@ vlan_start(struct ifnet *ifp)
if ((p->if_flags & (IFF_UP | IFF_RUNNING)) !=
(IFF_UP | IFF_RUNNING)) {
m_freem(m);
ifp->if_data.ifi_collisions++;
ifp->if_collisions++;
continue;
}
/*
* If the LINK0 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.
*
* Note: we also set the M_PROTO1 flag in the mbuf to let
* the parent driver know that the rcvif pointer is really
* valid. We need to do this because sometimes mbufs will
* be allocated by other parts of the system that contain
* garbage in the rcvif pointer. Using the M_PROTO1 flag
* lets the driver perform a proper sanity check and avoid
* following potentially bogus rcvif pointers off into
* never-never land.
* If underlying interface can do VLAN tag insertion itself,
* 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 attach a
* packet tag that holds it.
*/
if (ifp->if_flags & IFF_LINK0) {
m->m_pkthdr.rcvif = ifp;
m->m_flags |= M_PROTO1;
if (ifp->if_capabilities & IFCAP_VLAN_HWTAGGING) {
struct m_tag *mtag = m_tag_alloc(MTAG_VLAN,
MTAG_VLAN_TAG,
sizeof (u_int),
M_DONTWAIT);
if (mtag == NULL) {
ifp->if_oerrors++;
m_freem(m);
continue;
}
*(u_int*)(mtag+1) = ifv->ifv_tag;
m_tag_prepend(m, mtag);
} else {
M_PREPEND(m, EVL_ENCAPLEN, M_DONTWAIT);
M_PREPEND(m, ifv->ifv_encaplen, M_DONTWAIT);
if (m == NULL) {
if_printf(ifp, "M_PREPEND failed");
if_printf(ifp, "unable to prepend VLAN header");
ifp->if_ierrors++;
continue;
}
/* M_PREPEND takes care of m_len, m_pkthdr.len for us */
m = m_pullup(m, ETHER_HDR_LEN + EVL_ENCAPLEN);
if (m == NULL) {
if_printf(ifp, "m_pullup failed");
ifp->if_ierrors++;
continue;
if (m->m_len < sizeof(*evl)) {
m = m_pullup(m, sizeof(*evl));
if (m == NULL) {
if_printf(ifp,
"cannot pullup VLAN header");
ifp->if_ierrors++;
continue;
}
}
/*
* 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));
bcopy(mtod(m, char *) + ifv->ifv_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(ETHERTYPE_VLAN);
@ -355,91 +380,81 @@ vlan_start(struct ifnet *ifp)
return;
}
static int
vlan_input_tag(struct ether_header *eh, struct mbuf *m, u_int16_t t)
static void
vlan_input(struct ifnet *ifp, struct mbuf *m)
{
struct ether_vlan_header *evl;
struct ifvlan *ifv;
struct m_tag *mtag;
u_int tag;
/*
* Fake up a header and send the packet to the physical interface's
* bpf tap if active.
*/
if (m->m_pkthdr.rcvif->if_bpf != NULL) {
struct m_hdr mh;
struct ether_vlan_header evh;
mtag = m_tag_locate(m, MTAG_VLAN, MTAG_VLAN_TAG, NULL);
if (mtag != NULL) {
/*
* Packet is tagged, m contains a normal
* Ethernet frame; the tag is stored out-of-band.
*/
tag = *(u_int*)(mtag+1);
m_tag_delete(m, mtag);
} else {
switch (ifp->if_type) {
case IFT_ETHER:
if (m->m_len < sizeof (*evl) &&
(m = m_pullup(m, sizeof (*evl))) == NULL) {
if_printf(ifp, "cannot pullup VLAN header\n");
return;
}
evl = mtod(m, struct ether_vlan_header *);
KASSERT(ntohs(evl->evl_encap_proto) == ETHERTYPE_VLAN,
("vlan_input: bad encapsulated protocols (%u)",
ntohs(evl->evl_encap_proto)));
bcopy(eh, &evh, 2*ETHER_ADDR_LEN);
evh.evl_encap_proto = htons(ETHERTYPE_VLAN);
evh.evl_tag = htons(t);
evh.evl_proto = eh->ether_type;
tag = EVL_VLANOFTAG(ntohs(evl->evl_tag));
/* This kludge is OK; BPF treats the "mbuf" as read-only */
mh.mh_next = m;
mh.mh_data = (char *)&evh;
mh.mh_len = ETHER_HDR_LEN + EVL_ENCAPLEN;
bpf_mtap(m->m_pkthdr.rcvif, (struct mbuf *)&mh);
/*
* 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;
default:
tag = (u_int) -1;
#ifdef DIAGNOSTIC
panic("vlan_input: unsupported if type %u", ifp->if_type);
#endif
break;
}
}
for (ifv = LIST_FIRST(&ifv_list); ifv != NULL;
ifv = LIST_NEXT(ifv, ifv_list)) {
if (m->m_pkthdr.rcvif == ifv->ifv_p
&& ifv->ifv_tag == t)
ifv = LIST_NEXT(ifv, ifv_list))
if (ifp == ifv->ifv_p && tag == ifv->ifv_tag)
break;
}
if (ifv == NULL || (ifv->ifv_if.if_flags & IFF_UP) == 0) {
m->m_pkthdr.rcvif->if_noproto++;
m_freem(m);
return -1; /* So the parent can take note */
ifp->if_noproto++;
return;
}
if (mtag == NULL) {
/*
* Packet had an in-line encapsulation header;
* remove it. The original header has already
* been fixed up above.
*/
bcopy(mtod(m, caddr_t),
mtod(m, caddr_t) + ETHER_VLAN_ENCAP_LEN,
sizeof (struct ether_header));
m_adj(m, ETHER_VLAN_ENCAP_LEN);
}
/*
* Having found a valid vlan interface corresponding to
* the given source interface and vlan tag, run the
* the real packet through ether_input().
*/
m->m_pkthdr.rcvif = &ifv->ifv_if;
ifv->ifv_if.if_ipackets++;
ether_input(&ifv->ifv_if, eh, m);
return 0;
}
static int
vlan_input(struct ether_header *eh, struct mbuf *m)
{
struct ifvlan *ifv;
for (ifv = LIST_FIRST(&ifv_list); ifv != NULL;
ifv = LIST_NEXT(ifv, ifv_list)) {
if (m->m_pkthdr.rcvif == ifv->ifv_p
&& (EVL_VLANOFTAG(ntohs(*mtod(m, u_int16_t *)))
== ifv->ifv_tag))
break;
}
if (ifv == NULL || (ifv->ifv_if.if_flags & IFF_UP) == 0) {
m->m_pkthdr.rcvif->if_noproto++;
m_freem(m);
return -1; /* so ether_input can take note */
}
/*
* Having found a valid vlan interface corresponding to
* the given source interface and vlan tag, remove the
* encapsulation, and run the real packet through
* ether_input() a second time (it had better be
* reentrant!).
*/
m->m_pkthdr.rcvif = &ifv->ifv_if;
eh->ether_type = mtod(m, u_int16_t *)[1];
m->m_data += EVL_ENCAPLEN;
m->m_len -= EVL_ENCAPLEN;
m->m_pkthdr.len -= EVL_ENCAPLEN;
ifv->ifv_if.if_ipackets++;
ether_input(&ifv->ifv_if, eh, m);
return 0;
/* Pass it back through the parent's input routine. */
(*ifp->if_input)(&ifv->ifv_if, m);
}
static int
@ -452,12 +467,50 @@ vlan_config(struct ifvlan *ifv, struct ifnet *p)
return EPROTONOSUPPORT;
if (ifv->ifv_p)
return EBUSY;
ifv->ifv_p = p;
if (p->if_data.ifi_hdrlen == sizeof(struct ether_vlan_header))
ifv->ifv_if.if_mtu = p->if_mtu;
else
ifv->ifv_if.if_mtu = p->if_data.ifi_mtu - EVL_ENCAPLEN;
ifv->ifv_encaplen = ETHER_VLAN_ENCAP_LEN;
ifv->ifv_mintu = ETHERMIN;
ifv->ifv_flags = 0;
/*
* If the parent supports the VLAN_MTU capability,
* i.e. can Tx/Rx larger than ETHER_MAX_LEN frames,
* enable it.
*/
p->if_nvlans++;
if (p->if_nvlans == 1 && (p->if_capabilities & IFCAP_VLAN_MTU) != 0) {
/*
* Enable Tx/Rx of VLAN-sized frames.
*/
p->if_capenable |= IFCAP_VLAN_MTU;
if (p->if_flags & IFF_UP) {
struct ifreq ifr;
int error;
ifr.ifr_flags = p->if_flags;
error = (*p->if_ioctl)(p, SIOCSIFFLAGS,
(caddr_t) &ifr);
if (error) {
p->if_nvlans--;
if (p->if_nvlans == 0)
p->if_capenable &= ~IFCAP_VLAN_MTU;
return (error);
}
}
ifv->ifv_mtufudge = 0;
} else if ((p->if_capabilities & IFCAP_VLAN_MTU) == 0) {
/*
* Fudge the MTU by the encapsulation size. This
* makes us incompatible with strictly compliant
* 802.1Q implementations, but allows us to use
* the feature with other NetBSD implementations,
* which might still be useful.
*/
ifv->ifv_mtufudge = ifv->ifv_encaplen;
}
ifv->ifv_p = p;
ifv->ifv_if.if_mtu = p->if_mtu - ifv->ifv_mtufudge;
/*
* Copy only a selected subset of flags from the parent.
* Other flags are none of our business.
@ -465,6 +518,14 @@ vlan_config(struct ifvlan *ifv, struct ifnet *p)
ifv->ifv_if.if_flags = (p->if_flags &
(IFF_BROADCAST | IFF_MULTICAST | IFF_SIMPLEX | IFF_POINTOPOINT));
/*
* If the parent interface can do hardware-assisted
* VLAN encapsulation, then propagate its hardware-
* assisted checksumming flags.
*/
if (p->if_capabilities & IFCAP_VLAN_HWTAGGING)
ifv->ifv_if.if_capabilities |= p->if_capabilities & IFCAP_HWCSUM;
/*
* Set up our ``Ethernet address'' to reflect the underlying
* physical interface's.
@ -524,11 +585,26 @@ vlan_unconfig(struct ifnet *ifp)
SLIST_REMOVE_HEAD(&ifv->vlan_mc_listhead, mc_entries);
free(mc, M_VLAN);
}
p->if_nvlans--;
if (p->if_nvlans == 0) {
/*
* Disable Tx/Rx of VLAN-sized frames.
*/
p->if_capenable &= ~IFCAP_VLAN_MTU;
if (p->if_flags & IFF_UP) {
struct ifreq ifr;
ifr.ifr_flags = p->if_flags;
(*p->if_ioctl)(p, SIOCSIFFLAGS, (caddr_t) &ifr);
}
}
}
/* Disconnect from parent. */
ifv->ifv_p = NULL;
ifv->ifv_if.if_mtu = ETHERMTU;
ifv->ifv_if.if_mtu = ETHERMTU; /* XXX why not 0? */
ifv->ifv_flags = 0;
/* Clear our MAC address. */
ifa = ifaddr_byindex(ifv->ifv_if.if_index);
@ -541,6 +617,29 @@ vlan_unconfig(struct ifnet *ifp)
return 0;
}
static int
vlan_set_promisc(struct ifnet *ifp)
{
struct ifvlan *ifv = ifp->if_softc;
int error = 0;
if ((ifp->if_flags & IFF_PROMISC) != 0) {
if ((ifv->ifv_flags & IFVF_PROMISC) == 0) {
error = ifpromisc(ifv->ifv_p, 1);
if (error == 0)
ifv->ifv_flags |= IFVF_PROMISC;
}
} else {
if ((ifv->ifv_flags & IFVF_PROMISC) != 0) {
error = ifpromisc(ifv->ifv_p, 0);
if (error == 0)
ifv->ifv_flags &= ~IFVF_PROMISC;
}
}
return (error);
}
static int
vlan_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
{
@ -583,14 +682,17 @@ vlan_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
case SIOCSIFMTU:
/*
* Set the interface MTU.
* This is bogus. The underlying interface might support
* jumbo frames.
*/
if (ifr->ifr_mtu > ETHERMTU) {
if (ifv->ifv_p != NULL) {
if (ifr->ifr_mtu >
(ifv->ifv_p->if_mtu - ifv->ifv_mtufudge) ||
ifr->ifr_mtu <
(ifv->ifv_mintu - ifv->ifv_mtufudge))
error = EINVAL;
else
ifp->if_mtu = ifr->ifr_mtu;
} else
error = EINVAL;
} else {
ifp->if_mtu = ifr->ifr_mtu;
}
break;
case SIOCSETVLAN:
@ -617,6 +719,9 @@ vlan_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
break;
ifv->ifv_tag = vlr.vlr_tag;
ifp->if_flags |= IFF_RUNNING;
/* Update promiscuous mode, if necessary. */
vlan_set_promisc(ifp);
break;
case SIOCGETVLAN:
@ -631,15 +736,13 @@ vlan_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
case SIOCSIFFLAGS:
/*
* We don't support promiscuous mode
* right now because it would require help from the
* underlying drivers, which hasn't been implemented.
* For promiscuous mode, we enable promiscuous mode on
* the parent if we need promiscuous on the VLAN interface.
*/
if (ifr->ifr_flags & (IFF_PROMISC)) {
ifp->if_flags &= ~(IFF_PROMISC);
error = EINVAL;
}
if (ifv->ifv_p != NULL)
error = vlan_set_promisc(ifp);
break;
case SIOCADDMULTI:
case SIOCDELMULTI:
error = vlan_setmulti(ifp);

View File

@ -32,27 +32,6 @@
#ifndef _NET_IF_VLAN_VAR_H_
#define _NET_IF_VLAN_VAR_H_ 1
#ifdef _KERNEL
struct vlan_mc_entry {
struct ether_addr mc_addr;
SLIST_ENTRY(vlan_mc_entry) mc_entries;
};
struct ifvlan {
struct arpcom ifv_ac; /* make this an interface */
struct ifnet *ifv_p; /* parent inteface 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;
SLIST_HEAD(__vlan_mchead, vlan_mc_entry) vlan_mc_listhead;
LIST_ENTRY(ifvlan) ifv_list;
};
#define ifv_if ifv_ac.ac_if
#define ifv_tag ifv_mib.ifvm_tag
#endif /* _KERNEL */
struct ether_vlan_header {
u_char evl_dhost[ETHER_ADDR_LEN];
u_char evl_shost[ETHER_ADDR_LEN];
@ -63,7 +42,6 @@ struct ether_vlan_header {
#define EVL_VLANOFTAG(tag) ((tag) & 4095)
#define EVL_PRIOFTAG(tag) (((tag) >> 13) & 7)
#define EVL_ENCAPLEN 4 /* length in octets of encapsulation */
/* sysctl(3) tags, for compatibility purposes */
#define VLANCTL_PROTO 1
@ -79,4 +57,61 @@ struct vlanreq {
#define SIOCSETVLAN SIOCSIFGENERIC
#define SIOCGETVLAN SIOCGIFGENERIC
#ifdef _KERNEL
/*
* Drivers that are capable of adding and removing the VLAN header
* in hardware indicate they support this by marking IFCAP_VLAN_HWTAGGING
* in if_capabilities. Drivers for hardware that is also capable
* of handling larger MTU's that may include a software-appended
* VLAN header w/o lowering the normal MTU should mark IFCAP_VLA_MTU
* in if_capabilities; this notfies the VLAN code it can leave the
* MTU on the vlan interface at the normal setting.
*/
/*
* Drivers that support hardware VLAN tagging pass a packet's tag
* up through the stack by appending a packet tag with this value.
* Output is handled likewise, the driver must locate the packet
* tag to extract the VLAN tag. The following macros are used to
* do this work. On input, do:
*
* VLAN_INPUT_TAG(ifp, m, tag,);
*
* to mark the packet m with the specified VLAN tag. The last
* parameter provides code to execute in case of an error. On
* output the driver should check ifnet to see if any VLANs are
* in use and only then check for a packet tag; this is done with:
*
* struct m_tag *mtag;
* mtag = VLAN_OUTPUT_TAG(ifp, m);
* if (mtag != NULL) {
* ... = VLAN_TAG_VALUE(mtag);
* ... pass tag to hardware ...
* }
*
* Note that a driver must indicate it supports hardware VLAN
* tagging by marking IFCAP_VLAN_HWTAGGING in if_capabilities.
*/
#define MTAG_VLAN 1035328035
#define MTAG_VLAN_TAG 0 /* tag of VLAN interface */
#define VLAN_INPUT_TAG(_ifp, _m, _t, _errcase) do { \
struct m_tag *mtag; \
mtag = m_tag_alloc(MTAG_VLAN, MTAG_VLAN_TAG, \
sizeof (u_int), M_DONTWAIT); \
if (mtag == NULL) { \
(_ifp)->if_ierrors++; \
m_freem(_m); \
_errcase; \
} \
*(u_int *)(mtag+1) = (_t); \
m_tag_prepend((_m), mtag); \
} while (0)
#define VLAN_OUTPUT_TAG(_ifp, _m) \
((_ifp)->if_nvlans != 0 ? \
m_tag_locate((_m), MTAG_VLAN, MTAG_VLAN_TAG, NULL) : NULL)
#define VLAN_TAG_VALUE(_mt) (*(u_int *)((_mt)+1))
#endif /* _KERNEL */
#endif /* _NET_IF_VLAN_VAR_H_ */