diff --git a/sbin/ifconfig/ifconfig.8 b/sbin/ifconfig/ifconfig.8 index ef9bc2b78bea..27f38e9ff82c 100644 --- a/sbin/ifconfig/ifconfig.8 +++ b/sbin/ifconfig/ifconfig.8 @@ -2614,6 +2614,29 @@ Note that and .Cm vlandev must both be set at the same time. +.It Cm vlanpcp Ar priority_code_point +Priority code point +.Pq Dv PCP +is an 3-bit field which refers to the IEEE 802.1p +class of service and maps to the frame priority level. +.Pp +Values in order of priority are: +.Cm 1 +.Pq Dv Background (lowest) , +.Cm 0 +.Pq Dv Best effort (default) , +.Cm 2 +.Pq Dv Excellent effort , +.Cm 3 +.Pq Dv Critical applications , +.Cm 4 +.Pq Dv Video, < 100ms latency , +.Cm 5 +.Pq Dv Video, < 10ms latency , +.Cm 6 +.Pq Dv Internetwork control , +.Cm 7 +.Pq Dv Network control (highest) . .It Cm vlandev Ar iface Associate the physical interface .Ar iface diff --git a/sbin/ifconfig/ifvlan.c b/sbin/ifconfig/ifvlan.c index 107b87bfeac3..3b37160c3c85 100644 --- a/sbin/ifconfig/ifvlan.c +++ b/sbin/ifconfig/ifvlan.c @@ -1,6 +1,10 @@ /* - * Copyright (c) 1999 - * Bill Paul . All rights reserved. + * Copyright (c) 1999 Bill Paul + * Copyright (c) 2012 ADARA Networks, Inc. + * All rights reserved. + * + * Portions of this software were developed by Robert N. M. Watson under + * contract to ADARA Networks, Inc. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -78,10 +82,14 @@ vlan_status(int s) { struct vlanreq vreq; - if (getvlan(s, &ifr, &vreq) != -1) - printf("\tvlan: %d parent interface: %s\n", - vreq.vlr_tag, vreq.vlr_parent[0] == '\0' ? - "" : vreq.vlr_parent); + if (getvlan(s, &ifr, &vreq) == -1) + return; + printf("\tvlan: %d", vreq.vlr_tag); + if (ioctl(s, SIOCGVLANPCP, (caddr_t)&ifr) != -1) + printf(" vlanpcp: %u", ifr.ifr_vlan_pcp); + printf(" parent interface: %s", vreq.vlr_parent[0] == '\0' ? + "" : vreq.vlr_parent); + printf("\n"); } static void @@ -148,6 +156,22 @@ DECL_CMD_FUNC(setvlandev, val, d) vlan_set(s, &ifr); } +static +DECL_CMD_FUNC(setvlanpcp, val, d) +{ + u_long ul; + char *endp; + + ul = strtoul(val, &endp, 0); + if (*endp != '\0') + errx(1, "invalid value for vlanpcp"); + if (ul > 7) + errx(1, "value for vlanpcp out of range"); + ifr.ifr_vlan_pcp = ul; + if (ioctl(s, SIOCSVLANPCP, (caddr_t)&ifr) == -1) + err(1, "SIOCSVLANPCP"); +} + static DECL_CMD_FUNC(unsetvlandev, val, d) { @@ -169,6 +193,7 @@ DECL_CMD_FUNC(unsetvlandev, val, d) static struct cmd vlan_cmds[] = { DEF_CLONE_CMD_ARG("vlan", setvlantag), DEF_CLONE_CMD_ARG("vlandev", setvlandev), + DEF_CMD_ARG("vlanpcp", setvlanpcp), /* NB: non-clone cmds */ DEF_CMD_ARG("vlan", setvlantag), DEF_CMD_ARG("vlandev", setvlandev), diff --git a/share/man/man4/vlan.4 b/share/man/man4/vlan.4 index efdae27775e0..a47f68ca730e 100644 --- a/share/man/man4/vlan.4 +++ b/share/man/man4/vlan.4 @@ -203,5 +203,3 @@ can be corrected manually if used in conjunction with such a parent interface. .Sh SEE ALSO .Xr ifconfig 8 , .Xr sysctl 8 -.Sh BUGS -No 802.1Q features except VLAN tagging are implemented. diff --git a/sys/net/if.h b/sys/net/if.h index 792704afd223..98ae0a823cd3 100644 --- a/sys/net/if.h +++ b/sys/net/if.h @@ -393,6 +393,7 @@ struct ifreq { caddr_t ifru_data; int ifru_cap[2]; u_int ifru_fib; + u_char ifru_vlan_pcp; } ifr_ifru; #define ifr_addr ifr_ifru.ifru_addr /* address */ #define ifr_dstaddr ifr_ifru.ifru_dstaddr /* other end of p-to-p link */ @@ -410,6 +411,7 @@ struct ifreq { #define ifr_curcap ifr_ifru.ifru_cap[1] /* current capabilities */ #define ifr_index ifr_ifru.ifru_index /* interface index */ #define ifr_fib ifr_ifru.ifru_fib /* interface fib */ +#define ifr_vlan_pcp ifr_ifru.ifru_vlan_pcp /* VLAN priority */ }; #define _SIZEOF_ADDR_IFREQ(ifr) \ diff --git a/sys/net/if_vlan.c b/sys/net/if_vlan.c index ffa534920f36..415be01e47c5 100644 --- a/sys/net/if_vlan.c +++ b/sys/net/if_vlan.c @@ -1,5 +1,9 @@ /*- * Copyright 1998 Massachusetts Institute of Technology + * Copyright 2012 ADARA Networks, Inc. + * + * Portions of this software were developed by Robert N. M. Watson under + * contract to ADARA Networks, Inc. * * Permission to use, copy, modify, and distribute this software and * its documentation for any purpose and without fee is hereby @@ -29,8 +33,7 @@ /* * if_vlan.c - pseudo-device driver for IEEE 802.1Q virtual LANs. - * Might be extended some day to also handle IEEE 802.1p priority - * tagging. This is sort of sneaky in the implementation, since + * This is sort of sneaky in the implementation, since * we need to pretend to be enough of an Ethernet implementation * to make arp work. The way we do this is by telling everyone * that we are an Ethernet, and then catch the packets that @@ -52,6 +55,7 @@ __FBSDID("$FreeBSD$"); #include #include #include +#include #include #include #include @@ -114,6 +118,8 @@ struct ifvlan { int ifvm_mintu; /* min transmission unit */ uint16_t ifvm_proto; /* encapsulation ethertype */ uint16_t ifvm_tag; /* tag to apply on packets leaving if */ + uint16_t ifvm_vid; /* VLAN ID */ + uint8_t ifvm_pcp; /* Priority Code Point (PCP). */ } ifv_mib; SLIST_HEAD(, vlan_mc_entry) vlan_mc_listhead; #ifndef VLAN_ARRAY @@ -121,7 +127,9 @@ struct ifvlan { #endif }; #define ifv_proto ifv_mib.ifvm_proto -#define ifv_vid ifv_mib.ifvm_tag +#define ifv_tag ifv_mib.ifvm_tag +#define ifv_vid ifv_mib.ifvm_vid +#define ifv_pcp ifv_mib.ifvm_pcp #define ifv_encaplen ifv_mib.ifvm_encaplen #define ifv_mtufudge ifv_mib.ifvm_mtufudge #define ifv_mintu ifv_mib.ifvm_mintu @@ -147,6 +155,15 @@ static VNET_DEFINE(int, soft_pad); SYSCTL_INT(_net_link_vlan, OID_AUTO, soft_pad, CTLFLAG_RW | CTLFLAG_VNET, &VNET_NAME(soft_pad), 0, "pad short frames before tagging"); +/* + * For now, make preserving PCP via an mbuf tag optional, as it increases + * per-packet memory allocations and frees. In the future, it would be + * preferable to reuse ether_vtag for this, or similar. + */ +static int vlan_mtag_pcp = 0; +SYSCTL_INT(_net_link_vlan, OID_AUTO, mtag_pcp, CTLFLAG_RW, &vlan_mtag_pcp, 0, + "Retain VLAN PCP information as packets are passed up the stack"); + static const char vlanname[] = "vlan"; static MALLOC_DEFINE(M_VLAN, vlanname, "802.1Q Virtual LAN Interface"); @@ -696,6 +713,16 @@ vlan_devat(struct ifnet *ifp, uint16_t vid) return (ifp); } +/* + * Recalculate the cached VLAN tag exposed via the MIB. + */ +static void +vlan_tag_recalculate(struct ifvlan *ifv) +{ + + ifv->ifv_tag = EVL_MAKETAG(ifv->ifv_vid, ifv->ifv_pcp, 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 @@ -1009,6 +1036,8 @@ vlan_transmit(struct ifnet *ifp, struct mbuf *m) { struct ifvlan *ifv; struct ifnet *p; + struct m_tag *mtag; + uint16_t tag; int error, len, mcast; ifv = ifp->if_softc; @@ -1064,11 +1093,16 @@ vlan_transmit(struct ifnet *ifp, struct mbuf *m) * knows how to find the VLAN tag to use, so we attach a * packet tag that holds it. */ + if (vlan_mtag_pcp && (mtag = m_tag_locate(m, MTAG_8021Q, + MTAG_8021Q_PCP_OUT, NULL)) != NULL) + tag = EVL_MAKETAG(ifv->ifv_vid, *(uint8_t *)(mtag + 1), 0); + else + tag = ifv->ifv_tag; if (p->if_capenable & IFCAP_VLAN_HWTAGGING) { - m->m_pkthdr.ether_vtag = ifv->ifv_vid; + m->m_pkthdr.ether_vtag = tag; m->m_flags |= M_VLANTAG; } else { - m = ether_vlanencap(m, ifv->ifv_vid); + m = ether_vlanencap(m, tag); if (m == NULL) { if_printf(ifp, "unable to prepend VLAN header\n"); if_inc_counter(ifp, IFCOUNTER_OERRORS, 1); @@ -1103,7 +1137,8 @@ vlan_input(struct ifnet *ifp, struct mbuf *m) struct ifvlantrunk *trunk = ifp->if_vlantrunk; struct ifvlan *ifv; TRUNK_LOCK_READER; - uint16_t vid; + struct m_tag *mtag; + uint16_t vid, tag; KASSERT(trunk != NULL, ("%s: no trunk", __func__)); @@ -1112,7 +1147,7 @@ vlan_input(struct ifnet *ifp, struct mbuf *m) * Packet is tagged, but m contains a normal * Ethernet frame; the tag is stored out-of-band. */ - vid = EVL_VLANOFTAG(m->m_pkthdr.ether_vtag); + tag = m->m_pkthdr.ether_vtag; m->m_flags &= ~M_VLANTAG; } else { struct ether_vlan_header *evl; @@ -1128,7 +1163,7 @@ vlan_input(struct ifnet *ifp, struct mbuf *m) return; } evl = mtod(m, struct ether_vlan_header *); - vid = EVL_VLANOFTAG(ntohs(evl->evl_tag)); + tag = ntohs(evl->evl_tag); /* * Remove the 802.1q header by copying the Ethernet @@ -1152,6 +1187,8 @@ vlan_input(struct ifnet *ifp, struct mbuf *m) } } + vid = EVL_VLANOFTAG(tag); + TRUNK_RLOCK(trunk); ifv = vlan_gethash(trunk, vid); if (ifv == NULL || !UP_AND_RUNNING(ifv->ifv_ifp)) { @@ -1162,6 +1199,28 @@ vlan_input(struct ifnet *ifp, struct mbuf *m) } TRUNK_RUNLOCK(trunk); + if (vlan_mtag_pcp) { + /* + * While uncommon, it is possible that we will find a 802.1q + * packet encapsulated inside another packet that also had an + * 802.1q header. For example, ethernet tunneled over IPSEC + * arriving over ethernet. In that case, we replace the + * existing 802.1q PCP m_tag value. + */ + mtag = m_tag_locate(m, MTAG_8021Q, MTAG_8021Q_PCP_IN, NULL); + if (mtag == NULL) { + mtag = m_tag_alloc(MTAG_8021Q, MTAG_8021Q_PCP_IN, + sizeof(uint8_t), M_NOWAIT); + if (mtag == NULL) { + m_freem(m); + if_inc_counter(ifp, IFCOUNTER_IERRORS, 1); + return; + } + m_tag_prepend(m, mtag); + } + *(uint8_t *)(mtag + 1) = EVL_PRIOFTAG(tag); + } + m->m_pkthdr.rcvif = ifv->ifv_ifp; if_inc_counter(ifv->ifv_ifp, IFCOUNTER_IPACKETS, 1); @@ -1201,7 +1260,7 @@ vlan_config(struct ifvlan *ifv, struct ifnet *p, uint16_t vid) vlan_inithash(trunk); VLAN_LOCK(); if (p->if_vlantrunk != NULL) { - /* A race that that is very unlikely to be hit. */ + /* A race that is very unlikely to be hit. */ vlan_freehash(trunk); free(trunk, M_VLAN); goto exists; @@ -1218,6 +1277,8 @@ vlan_config(struct ifvlan *ifv, struct ifnet *p, uint16_t vid) } ifv->ifv_vid = vid; /* must set this before vlan_inshash() */ + ifv->ifv_pcp = 0; /* Default: best effort delivery. */ + vlan_tag_recalculate(ifv); error = vlan_inshash(trunk, ifv); if (error) goto done; @@ -1705,6 +1766,34 @@ vlan_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data) } break; + case SIOCGVLANPCP: +#ifdef VIMAGE + if (ifp->if_vnet != ifp->if_home_vnet) { + error = EPERM; + break; + } +#endif + ifr->ifr_vlan_pcp = ifv->ifv_pcp; + break; + + case SIOCSVLANPCP: +#ifdef VIMAGE + if (ifp->if_vnet != ifp->if_home_vnet) { + error = EPERM; + break; + } +#endif + error = priv_check(curthread, PRIV_NET_SETVLANPCP); + if (error) + break; + if (ifr->ifr_vlan_pcp > 7) { + error = EINVAL; + break; + } + ifv->ifv_pcp = ifr->ifr_vlan_pcp; + vlan_tag_recalculate(ifv); + break; + default: error = EINVAL; break; diff --git a/sys/net/if_vlan_var.h b/sys/net/if_vlan_var.h index ed4753ecfe45..6b20d142741c 100644 --- a/sys/net/if_vlan_var.h +++ b/sys/net/if_vlan_var.h @@ -73,6 +73,23 @@ struct vlanreq { #define SIOCSETVLAN SIOCSIFGENERIC #define SIOCGETVLAN SIOCGIFGENERIC +#define SIOCGVLANPCP _IOWR('i', 152, struct ifreq) /* Get VLAN PCP */ +#define SIOCSVLANPCP _IOW('i', 153, struct ifreq) /* Set VLAN PCP */ + +/* + * Names for 802.1q priorities ("802.1p"). Notice that in this scheme, + * (0 < 1), allowing default 0-tagged traffic to take priority over background + * tagged traffic. + */ +#define IEEE8021Q_PCP_BK 1 /* Background (lowest) */ +#define IEEE8021Q_PCP_BE 0 /* Best effort (default) */ +#define IEEE8021Q_PCP_EE 2 /* Excellent effort */ +#define IEEE8021Q_PCP_CA 3 /* Critical applications */ +#define IEEE8021Q_PCP_VI 4 /* Video, < 100ms latency */ +#define IEEE8021Q_PCP_VO 5 /* Video, < 10ms latency */ +#define IEEE8021Q_PCP_IC 6 /* Internetwork control */ +#define IEEE8021Q_PCP_NC 7 /* Network control (highest) */ + #ifdef _KERNEL /* * Drivers that are capable of adding and removing the VLAN header @@ -110,6 +127,16 @@ struct vlanreq { * if_capabilities. */ +/* + * The 802.1q code may also tag mbufs with the PCP (priority) field for use in + * other layers of the stack, in which case an m_tag will be used. This is + * semantically quite different from use of the ether_vtag field, which is + * defined only between the device driver and VLAN layer. + */ +#define MTAG_8021Q 1326104895 +#define MTAG_8021Q_PCP_IN 0 /* Input priority. */ +#define MTAG_8021Q_PCP_OUT 1 /* Output priority. */ + #define VLAN_CAPABILITIES(_ifp) do { \ if ((_ifp)->if_vlantrunk != NULL) \ (*vlan_trunk_cap_p)(_ifp); \ diff --git a/sys/sys/priv.h b/sys/sys/priv.h index b1eaeceb1113..86ea05e92ac3 100644 --- a/sys/sys/priv.h +++ b/sys/sys/priv.h @@ -342,6 +342,7 @@ #define PRIV_NET_SETIFDESCR 418 /* Set interface description. */ #define PRIV_NET_SETIFFIB 419 /* Set interface fib. */ #define PRIV_NET_VXLAN 420 /* Administer vxlan. */ +#define PRIV_NET_SETVLANPCP 421 /* Set VLAN priority. */ /* * 802.11-related privileges.