This patch adds a number of 802.1q-related features to the FreeBSD network stack. It will likely be committed as a series of commits rather than as a single commit as presented in this patch. 1. Modify ifconfig(8) and vlan(4) to allow a default vlan interface priority to be configured using "ifconfig vlanX vlanpcp Y". By default, interfaces use a priority of 0 (best effort), retaining their current behaviour. A new privilege, PRIV_NET_SETVLANPCP, is added to control reconfiguration of VLAN priorities. 2. Modify vlan(4) to optionally preserve received vlan pcp fields from 802.1q-encapsulated packets in m_tags. This behaviour is controlled by net.link.vlan.mtag_pcp, which is disabled by default. When enabled, there is additional per-packet memory allocation, which we may wish to revisit in the future. 3. Modify vlan(4) to optionally set the pcp field on a transmitted 802.1q-encapsulated packet based on an m_tag. Note that this is not the same m_tag as the received m_tag, so when a received mbuf is reused (due to ICMP reflection, IP forwarding, etc), the newly transmitted packet will not automatically inherit the received priority. This behaviour is also controlled by net.link.vlan.mtag_pcp, and disabled by default. 4. Modify pf(4) and pfctl(8) to allow 802.1q priorities, encapsulated in m_tags, to be matched against and set in pf rules. A typical rule might look like this: pass out on vlan0 proto tcp from any to any port 22 ieee8021q-setpcp ee Which sets the 802.1q priority on SSH packets sent via vlan0 to ee -- excellent effort, rather than using a default set on the interface via ifconfig(8). As supported by pfctl(8), it is currently possible only to exactly match priorities; however, pf(4) implements range comparisons, and it would make sense to extend pfctl(8) to parse and program such comparisons, as is already done for ports. This change is intended to be API, ABI, KPI, and KBI compliant for an as-is MFC -- with one exception: one public symbol is renamed in the VLAN code, and a compatibility symbol must be added before a merge to a -STABLE branch. Sponsored by: ADARA Networks, Inc. Reviewed by: bz, rrs MFC after: 1 month Index: sys/ofed/include/rdma/ib_addr.h =================================================================== --- sys/ofed/include/rdma/ib_addr.h (revision 230032) +++ sys/ofed/include/rdma/ib_addr.h (working copy) @@ -154,11 +154,11 @@ static inline u16 rdma_vlan_dev_vlan_id(const stru return dev->priv_flags & IFF_802_1Q_VLAN ? vlan_dev_vlan_id(dev) : 0xffff; #else - uint16_t tag; + uint16_t vid; - if (VLAN_TAG(__DECONST(struct ifnet *, dev), &tag) != 0) + if (VLAN_VID(__DECONST(struct ifnet *, dev), &vid) != 0) return 0xffff; - return tag; + return vid; #endif } Index: sys/dev/cxgbe/t4_l2t.c =================================================================== --- sys/dev/cxgbe/t4_l2t.c (revision 230032) +++ sys/dev/cxgbe/t4_l2t.c (working copy) @@ -679,7 +679,7 @@ t4_l2t_get(struct port_info *pi, struct ifnet *ifp e->lle = NULL; atomic_store_rel_int(&e->refcnt, 1); if (ifp->if_type == IFT_L2VLAN) - VLAN_TAG(ifp, &e->vlan); + VLAN_VID(ifp, &e->vlan); else e->vlan = VLAN_NONE; e->next = d->l2tab[hash].first; Index: sys/net/if.c =================================================================== --- sys/net/if.c (revision 230032) +++ sys/net/if.c (working copy) @@ -1870,7 +1870,7 @@ void (*vlan_link_state_p)(struct ifnet *); /* XXX: void (*vlan_trunk_cap_p)(struct ifnet *); /* XXX: private from if_vlan */ struct ifnet *(*vlan_trunkdev_p)(struct ifnet *); struct ifnet *(*vlan_devat_p)(struct ifnet *, uint16_t); -int (*vlan_tag_p)(struct ifnet *, uint16_t *); +int (*vlan_vid_p)(struct ifnet *, uint16_t *); int (*vlan_setcookie_p)(struct ifnet *, void *); void *(*vlan_cookie_p)(struct ifnet *); Index: sys/net/if.h =================================================================== --- sys/net/if.h (revision 230032) +++ sys/net/if.h (working copy) @@ -330,6 +330,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 */ @@ -347,6 +348,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) \ Index: sys/net/if_vlan_var.h =================================================================== --- sys/net/if_vlan_var.h (revision 230032) +++ sys/net/if_vlan_var.h (working copy) @@ -81,6 +81,9 @@ struct ether_vlan_header { /* * Configuration structure for SIOCSETVLAN and SIOCGETVLAN ioctls. + * + * XXXRW: Note that despite being named vlr_tag, the field below should really + * be named vlr_vid. */ struct vlanreq { char vlr_parent[IFNAMSIZ]; @@ -89,6 +92,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 @@ -126,6 +146,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); \ @@ -133,8 +163,8 @@ struct vlanreq { #define VLAN_TRUNKDEV(_ifp) \ (_ifp)->if_type == IFT_L2VLAN ? (*vlan_trunkdev_p)((_ifp)) : NULL -#define VLAN_TAG(_ifp, _vid) \ - (_ifp)->if_type == IFT_L2VLAN ? (*vlan_tag_p)((_ifp), (_vid)) : EINVAL +#define VLAN_VID(_ifp, _vid) \ + (_ifp)->if_type == IFT_L2VLAN ? (*vlan_vid_p)((_ifp), (_vid)) : EINVAL #define VLAN_COOKIE(_ifp) \ (_ifp)->if_type == IFT_L2VLAN ? (*vlan_cookie_p)((_ifp)) : NULL #define VLAN_SETCOOKIE(_ifp, _cookie) \ @@ -146,7 +176,7 @@ struct vlanreq { extern void (*vlan_trunk_cap_p)(struct ifnet *); extern struct ifnet *(*vlan_trunkdev_p)(struct ifnet *); extern struct ifnet *(*vlan_devat_p)(struct ifnet *, uint16_t); -extern int (*vlan_tag_p)(struct ifnet *, uint16_t *); +extern int (*vlan_vid_p)(struct ifnet *, uint16_t *); extern int (*vlan_setcookie_p)(struct ifnet *, void *); extern void *(*vlan_cookie_p)(struct ifnet *); Index: sys/net/if_vlan.c =================================================================== --- sys/net/if_vlan.c (revision 230032) +++ sys/net/if_vlan.c (working copy) @@ -1,6 +1,10 @@ /*- * 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 * granted, provided that both the above copyright notice and this @@ -50,6 +54,7 @@ __FBSDID("$FreeBSD$"); #include #include #include +#include #include #include #include @@ -108,13 +113,19 @@ struct ifvlan { uint16_t ifvm_proto; /* encapsulation ethertype */ uint16_t ifvm_tag; /* tag to apply on packets leaving if */ } ifv_mib; + + /* + * These fields are authoritative configuration state; ifvm_tag caches + * a precomputed tag to report via the MIB. + */ + uint16_t ifv_vid; /* VLAN Identifier (VID). */ + uint8_t ifv_pcp; /* Priority Code Point (PCP). */ SLIST_HEAD(, vlan_mc_entry) vlan_mc_listhead; #ifndef VLAN_ARRAY LIST_ENTRY(ifvlan) ifv_list; #endif }; #define ifv_proto ifv_mib.ifvm_proto -#define ifv_vid 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 @@ -139,6 +150,15 @@ static int soft_pad = 0; SYSCTL_INT(_net_link_vlan, OID_AUTO, soft_pad, CTLFLAG_RW, &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 MALLOC_DEFINE(M_VLAN, VLANNAME, "802.1Q Virtual LAN Interface"); static eventhandler_tag ifdetach_tag; @@ -598,6 +618,16 @@ restart: } /* + * Recalculate the cached VLAN tag exposed via the MIB. + */ +static void +vlan_tag_recalculate(struct ifvlan *ifv) +{ + + ifv->ifv_mib.ifvm_tag = EVL_MAKETAG(ifv->ifv_vid, ifv->ifv_pcp, 0); +} + +/* * Return the trunk device for a virtual interface. */ static struct ifnet * @@ -619,12 +649,9 @@ vlan_trunkdev(struct ifnet *ifp) /* * Return the 12-bit VLAN VID for this interface, for use by external * components such as Infiniband. - * - * XXXRW: Note that the function name here is historical; it should be named - * vlan_vid(). */ static int -vlan_tag(struct ifnet *ifp, uint16_t *vidp) +vlan_vid(struct ifnet *ifp, uint16_t *vidp) { struct ifvlan *ifv; @@ -720,7 +747,7 @@ vlan_modevent(module_t mod, int type, void *data) vlan_trunkdev_p = vlan_trunkdev; vlan_cookie_p = vlan_cookie; vlan_setcookie_p = vlan_setcookie; - vlan_tag_p = vlan_tag; + vlan_vid_p = vlan_vid; vlan_devat_p = vlan_devat; #ifndef VIMAGE if_clone_attach(&vlan_cloner); @@ -745,7 +772,7 @@ vlan_modevent(module_t mod, int type, void *data) vlan_link_state_p = NULL; vlan_trunk_cap_p = NULL; vlan_trunkdev_p = NULL; - vlan_tag_p = NULL; + vlan_vid_p = NULL; vlan_cookie_p = vlan_cookie; vlan_setcookie_p = vlan_setcookie; vlan_devat_p = NULL; @@ -871,8 +898,7 @@ vlan_clone_create(struct if_clone *ifc, char *name * The first technique is preferred; the latter two are * supported for backwards compatibilty. * - * XXXRW: Note historic use of the word "tag" here. New ioctls may be - * called for. + * XXXRW: Note historic use of the word "tag" here. */ if (params) { error = copyin(params, &vlr, sizeof(vlr)); @@ -1019,7 +1045,9 @@ vlan_transmit(struct ifnet *ifp, struct mbuf *m) { struct ifvlan *ifv; struct ifnet *p; + struct m_tag *mtag; int error, len, mcast; + uint16_t tag; ifv = ifp->if_softc; p = PARENT(ifv); @@ -1073,12 +1101,21 @@ vlan_transmit(struct ifnet *ifp, struct mbuf *m) * 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. + * + * Only look for (and use) priority information if that is enabled; + * avoid the cost of an mtag locate if not. */ + 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 = EVL_MAKETAG(ifv->ifv_vid, ifv->ifv_pcp, 0); + 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"); ifp->if_oerrors++; @@ -1112,7 +1149,8 @@ vlan_input(struct ifnet *ifp, struct mbuf *m) { struct ifvlantrunk *trunk = ifp->if_vlantrunk; struct ifvlan *ifv; - uint16_t vid; + struct m_tag *mtag; + uint16_t tag, vid; KASSERT(trunk != NULL, ("%s: no trunk", __func__)); @@ -1121,7 +1159,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; @@ -1137,7 +1175,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 @@ -1161,6 +1199,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)) { @@ -1171,6 +1211,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); + ifp->if_ierrors++; + return; + } + m_tag_prepend(m, mtag); + } + *(uint8_t *)(mtag + 1) = EVL_PRIOFTAG(tag); + } + m->m_pkthdr.rcvif = ifv->ifv_ifp; ifv->ifv_ifp->if_ipackets++; @@ -1219,6 +1281,9 @@ exists: } 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; @@ -1681,6 +1746,34 @@ vlan_ioctl(struct ifnet *ifp, u_long cmd, caddr_t error = vlan_setmulti(ifp); 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; Index: sys/contrib/pf/net/pf.c =================================================================== --- sys/contrib/pf/net/pf.c (revision 230032) +++ sys/contrib/pf/net/pf.c (working copy) @@ -95,6 +95,8 @@ __FBSDID("$FreeBSD$"); #include #include +#include +#include #include #include #ifdef __FreeBSD__ @@ -2713,6 +2715,36 @@ pf_match_uid(u_int8_t op, uid_t a1, uid_t a2, uid_ } int +pf_match_ieee8021q_pcp(u_int8_t op, u_int8_t pcp1, u_int8_t pcp2, + struct mbuf *m) +{ + struct m_tag *mtag; + uint8_t mpcp; + + /* + * Packets without 802.1q headers are treated as having a PCP of 0 + * (best effort). + */ + mtag = m_tag_locate(m, MTAG_8021Q, MTAG_8021Q_PCP_IN, NULL); + if (mtag != NULL) + mpcp = *(uint8_t *)(mtag + 1); + else + mpcp = IEEE8021Q_PCP_BE; + + /* + * 802.1q uses a non-traditional ordering, in which 1 < 0, allowing + * default 0-tagged ("best effort") traffic to take precedence over + * 1-tagged ("background") traffic. Renumber both PCP arguments + * before making a comparison so that we can use boring arithmetic + * operators. + */ + pcp1 = ((pcp1 == 0) ? 1 : ((pcp1 == 1) ? 0 : pcp1)); + pcp2 = ((pcp2 == 0) ? 1 : ((pcp2 == 1) ? 0 : pcp2)); + mpcp = ((mpcp == 0) ? 1 : ((mpcp == 1) ? 0 : mpcp)); + return (pf_match(op, pcp1, pcp2, mpcp)); +} + +int pf_match_gid(u_int8_t op, gid_t a1, gid_t a2, gid_t g) { if (g == GID_MAX && op != PF_OP_EQ && op != PF_OP_NE) @@ -2768,6 +2800,26 @@ pf_tag_packet(struct mbuf *m, int tag, int rtablei return (0); } +int +pf_ieee8021q_setpcp(struct mbuf *m, struct pf_rule *r) +{ + struct m_tag *mtag; + + KASSERT(r->ieee8021q_pcp.setpcp & SETPCP_VALID, + ("%s with invalid setpcp", __func__)); + + mtag = m_tag_locate(m, MTAG_8021Q, MTAG_8021Q_PCP_OUT, NULL); + if (mtag == NULL) { + mtag = m_tag_alloc(MTAG_8021Q, MTAG_8021Q_PCP_OUT, + sizeof(uint8_t), M_NOWAIT); + if (mtag == NULL) + return (ENOMEM); + m_tag_prepend(m, mtag); + } + *(uint8_t *)(mtag + 1) = (r->ieee8021q_pcp.setpcp & SETPCP_PCP_MASK); + return (0); +} + void pf_step_into_anchor(int *depth, struct pf_ruleset **rs, int n, struct pf_rule **r, struct pf_rule **a, int *match) @@ -3578,6 +3630,10 @@ pf_test_rule(struct pf_rule **rm, struct pf_state !pf_match_gid(r->gid.op, r->gid.gid[0], r->gid.gid[1], pd->lookup.gid)) r = TAILQ_NEXT(r, entries); + else if (r->ieee8021q_pcp.op && + !pf_match_ieee8021q_pcp(r->ieee8021q_pcp.op, + r->ieee8021q_pcp.pcp[0], r->ieee8021q_pcp.pcp[1], m)) + r = TAILQ_NEXT(r, entries); else if (r->prob && #ifdef __FreeBSD__ r->prob <= arc4random()) @@ -6900,6 +6956,15 @@ done: pf_tag_packet(m, s ? s->tag : 0, r->rtableid); #endif + if ((r->ieee8021q_pcp.setpcp & SETPCP_VALID) && + pf_ieee8021q_setpcp(m, r)) { + action = PF_DROP; + REASON_SET(&reason, PFRES_MEMORY); + log = 1; + DPFPRINTF(PF_DEBUG_MISC, + ("pf: failed to allocate 802.1q mtag\n")); + } + if (dir == PF_IN && s && s->key[PF_SK_STACK]) #ifdef __FreeBSD__ pd.pf_mtag->statekey = s->key[PF_SK_STACK]; Index: sys/contrib/pf/net/pfvar.h =================================================================== --- sys/contrib/pf/net/pfvar.h (revision 230032) +++ sys/contrib/pf/net/pfvar.h (working copy) @@ -435,6 +435,14 @@ struct pf_rule_gid { u_int8_t op; }; +struct pf_rule_ieee8021q_pcp { + u_int8_t pcp[2]; + u_int8_t op; +#define SETPCP_VALID 0x80 /* Set if PCP value in field is valid. */ +#define SETPCP_PCP_MASK 0x07 /* Mask to retrieve pcp if SETPCP_VALID. */ + u_int8_t setpcp; +}; + struct pf_rule_addr { struct pf_addr_wrap addr; u_int16_t port[2]; @@ -629,7 +637,7 @@ struct pf_rule { u_int32_t src_nodes; u_int32_t max_src_nodes; u_int32_t max_src_states; - u_int32_t spare1; /* netgraph */ + struct pf_rule_ieee8021q_pcp ieee8021q_pcp; /* spare1 */ u_int32_t max_src_conn; struct { u_int32_t limit; @@ -1960,6 +1968,8 @@ int pf_match(u_int8_t, u_int32_t, u_int32_t, u_int int pf_match_port(u_int8_t, u_int16_t, u_int16_t, u_int16_t); int pf_match_uid(u_int8_t, uid_t, uid_t, uid_t); int pf_match_gid(u_int8_t, gid_t, gid_t, gid_t); +int pf_match_ieee8021q_pcp(u_int8_t, u_int8_t, u_int8_t, struct mbuf *); +int pf_ieee8021q_setpcp(struct mbuf *m, struct pf_rule *r); void pf_normalize_init(void); int pf_normalize_ip(struct mbuf **, int, struct pfi_kif *, u_short *, Index: sys/sys/priv.h =================================================================== --- sys/sys/priv.h (revision 230032) +++ sys/sys/priv.h (working copy) @@ -338,6 +338,7 @@ #define PRIV_NET_SETIFVNET 417 /* Move interface to vnet. */ #define PRIV_NET_SETIFDESCR 418 /* Set interface description. */ #define PRIV_NET_SETIFFIB 419 /* Set interface fib. */ +#define PRIV_NET_SETVLANPCP 420 /* Set VLAN priority. */ /* * 802.11-related privileges. Index: sbin/ifconfig/ifconfig.8 =================================================================== --- sbin/ifconfig/ifconfig.8 (revision 230008) +++ sbin/ifconfig/ifconfig.8 (working copy) @@ -2375,9 +2375,9 @@ The following parameters are specific to .Xr vlan 4 interfaces: .Bl -tag -width indent -.It Cm vlan Ar vlan_tag -Set the VLAN tag value to -.Ar vlan_tag . +.It Cm vlan Ar vid +Set the VLAN VID value to +.Ar vid . This value is a 12-bit VLAN Identifier (VID) which is used to create an 802.1Q VLAN header for packets sent from the .Xr vlan 4 @@ -2444,6 +2444,21 @@ down. The .Ar iface argument is useless and hence deprecated. +.It Cm vlanpcp Ar pcp +Set the VLAN priority to +.Ar pcp . +This value is a 3-bit number used to set the PCP field in an 802.1Q VLAN +header for packets sent from the +.Xr vlan 4 +interface. +Interfaces default to a priority of 0 when created, selecting best-effort +delivery. +Values between 1 and 7 may also be used, with 1 selecting lowest +priority, and 7 highest. +Subsystems such as +.Xr pf 4 +may select alternative 802.1q priorities, which will override the interface +priority. .El .Pp The following parameters are used to configure @@ -2646,6 +2661,7 @@ tried to alter an interface's configuration. .Xr carp 4 , .Xr gif 4 , .Xr netintro 4 , +.Xr pf 4 , .Xr pfsync 4 , .Xr polling 4 , .Xr vlan 4 , Index: sbin/ifconfig/ifvlan.c =================================================================== --- sbin/ifconfig/ifvlan.c (revision 230008) +++ sbin/ifconfig/ifvlan.c (working copy) @@ -1,7 +1,11 @@ /* - * 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 * are met: @@ -79,10 +83,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 @@ -93,7 +101,7 @@ vlan_create(int s, struct ifreq *ifr) * One or both parameters were specified, make sure both. */ if (params.vlr_tag == NOTAG) - errx(1, "must specify a tag for vlan create"); + errx(1, "must specify a VID for vlan create"); if (params.vlr_parent[0] == '\0') errx(1, "must specify a parent device for vlan create"); ifr->ifr_data = (caddr_t) ¶ms; @@ -120,7 +128,7 @@ vlan_set(int s, struct ifreq *ifr) } static -DECL_CMD_FUNC(setvlantag, val, d) +DECL_CMD_FUNC(setvlanvid, val, d) { struct vlanreq vreq; u_long ul; @@ -150,6 +158,22 @@ DECL_CMD_FUNC(setvlandev, val, d) } 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) { struct vlanreq vreq; @@ -168,11 +192,12 @@ DECL_CMD_FUNC(unsetvlandev, val, d) } static struct cmd vlan_cmds[] = { - DEF_CLONE_CMD_ARG("vlan", setvlantag), + DEF_CLONE_CMD_ARG("vlan", setvlanvid), DEF_CLONE_CMD_ARG("vlandev", setvlandev), /* NB: non-clone cmds */ - DEF_CMD_ARG("vlan", setvlantag), + DEF_CMD_ARG("vlan", setvlanvid), DEF_CMD_ARG("vlandev", setvlandev), + DEF_CMD_ARG("vlanpcp", setvlanpcp), /* XXX For compatibility. Should become DEF_CMD() some day. */ DEF_CMD_OPTARG("-vlandev", unsetvlandev), DEF_CMD("vlanmtu", IFCAP_VLAN_MTU, setifcap), Index: contrib/pf/pfctl/parse.y =================================================================== --- contrib/pf/pfctl/parse.y (revision 230008) +++ contrib/pf/pfctl/parse.y (working copy) @@ -34,6 +34,8 @@ __FBSDID("$FreeBSD$"); #include #include #include +#include +#include #include #include #include @@ -235,6 +237,11 @@ struct filter_opts { char *tag; char *match_tag; u_int8_t match_tag_not; + struct { + uint8_t pcp[2]; + uint8_t op; + uint8_t setpcp; + } ieee8021q_pcp; u_int rtableid; struct { struct node_host *addr; @@ -450,6 +457,7 @@ int parseport(char *, struct range *r, int); %token MAXSRCCONN MAXSRCCONNRATE OVERLOAD FLUSH SLOPPY PFLOW %token TAGGED TAG IFBOUND FLOATING STATEPOLICY STATEDEFAULTS ROUTE SETTOS %token DIVERTTO DIVERTREPLY +%token IEEE8021QPCP IEEE8021QSETPCP %token STRING %token NUMBER %token PORTBINARY @@ -822,6 +830,11 @@ anchorrule : ANCHOR anchorname dir quick interface r.prob = $9.prob; r.rtableid = $9.rtableid; + r.ieee8021q_pcp.pcp[0] = $9.ieee8021q_pcp.pcp[0]; + r.ieee8021q_pcp.pcp[1] = $9.ieee8021q_pcp.pcp[1]; + r.ieee8021q_pcp.op = $9.ieee8021q_pcp.op; + r.ieee8021q_pcp.setpcp = $9.ieee8021q_pcp.setpcp; + if ($9.tag) if (strlcpy(r.tagname, $9.tag, PF_TAG_NAME_SIZE) >= PF_TAG_NAME_SIZE) { @@ -1875,6 +1888,11 @@ pfrule : action dir logquick interface route af p r.prob = $9.prob; r.rtableid = $9.rtableid; + r.ieee8021q_pcp.pcp[0] = $9.ieee8021q_pcp.pcp[0]; + r.ieee8021q_pcp.pcp[1] = $9.ieee8021q_pcp.pcp[1]; + r.ieee8021q_pcp.op = $9.ieee8021q_pcp.op; + r.ieee8021q_pcp.setpcp = $9.ieee8021q_pcp.setpcp; + r.af = $6; if ($9.tag) if (strlcpy(r.tagname, $9.tag, @@ -2402,8 +2420,101 @@ filter_opt : USER uids { filter_opts.divert.port = 1; /* some random value */ #endif } + | IEEE8021QPCP STRING { + u_int pcp; + + /* + * XXXRW: More complete set of operations, similar to + * ports. + */ + if (!strcmp($2, "be")) + pcp = IEEE8021Q_PCP_BE; + else if (!strcmp($2, "bk")) + pcp = IEEE8021Q_PCP_BK; + else if (!strcmp($2, "ee")) + pcp = IEEE8021Q_PCP_EE; + else if (!strcmp($2, "ca")) + pcp = IEEE8021Q_PCP_CA; + else if (!strcmp($2, "vi")) + pcp = IEEE8021Q_PCP_VI; + else if (!strcmp($2, "vo")) + pcp = IEEE8021Q_PCP_VO; + else if (!strcmp($2, "ic")) + pcp = IEEE8021Q_PCP_IC; + else if (!strcmp($2, "nc")) + pcp = IEEE8021Q_PCP_NC; + else + pcp = 8; /* flag bad argument */ + if (pcp > 7) { + yyerror("invalid ieee8021q_pcp value %s", $2); + free($2); + YYERROR; + } + free($2); + filter_opts.ieee8021q_pcp.pcp[0] = pcp; + filter_opts.ieee8021q_pcp.pcp[1] = 0; + filter_opts.ieee8021q_pcp.op = PF_OP_EQ; + } + | IEEE8021QPCP NUMBER { + u_int pcp; + + pcp = $2; + if (pcp > 7) { + yyerror("invalid ieee8021q_pcp value %u", pcp); + YYERROR; + } + filter_opts.ieee8021q_pcp.pcp[0] = pcp; + filter_opts.ieee8021q_pcp.pcp[1] = 0; + filter_opts.ieee8021q_pcp.op = PF_OP_EQ; + } ; + | IEEE8021QSETPCP STRING { + u_int pcp; + /* + * XXXRW: More complete set of operations, similar to + * ports. + */ + if (!strcmp($2, "be")) + pcp = IEEE8021Q_PCP_BE; + else if (!strcmp($2, "bk")) + pcp = IEEE8021Q_PCP_BK; + else if (!strcmp($2, "ee")) + pcp = IEEE8021Q_PCP_EE; + else if (!strcmp($2, "ca")) + pcp = IEEE8021Q_PCP_CA; + else if (!strcmp($2, "vi")) + pcp = IEEE8021Q_PCP_VI; + else if (!strcmp($2, "vo")) + pcp = IEEE8021Q_PCP_VO; + else if (!strcmp($2, "ic")) + pcp = IEEE8021Q_PCP_IC; + else if (!strcmp($2, "nc")) + pcp = IEEE8021Q_PCP_NC; + else + pcp = 8; /* flag bad argument */ + if (pcp > 7) { + yyerror("invalid ieee8021q_setpcp value %s", + $2); + free($2); + YYERROR; + } + free($2); + filter_opts.ieee8021q_pcp.setpcp = pcp | SETPCP_VALID; + } + | IEEE8021QSETPCP NUMBER { + u_int pcp; + + pcp = $2; + if (pcp > 7) { + yyerror("invalid ieee8021q_setpcp value %u", + pcp); + YYERROR; + } + filter_opts.ieee8021q_pcp.setpcp = pcp | SETPCP_VALID; + } + ; + probability : STRING { char *e; double p = strtod($1, &e); @@ -5284,6 +5395,8 @@ lookup(char *s) { "hostid", HOSTID}, { "icmp-type", ICMPTYPE}, { "icmp6-type", ICMP6TYPE}, + { "ieee8021q-pcp", IEEE8021QPCP}, + { "ieee8021q-setpcp", IEEE8021QSETPCP}, { "if-bound", IFBOUND}, { "in", IN}, { "include", INCLUDE}, Index: contrib/pf/pfctl/pfctl_parser.c =================================================================== --- contrib/pf/pfctl/pfctl_parser.c (revision 230008) +++ contrib/pf/pfctl/pfctl_parser.c (working copy) @@ -40,6 +40,8 @@ __FBSDID("$FreeBSD$"); #include #include #include +#include +#include #include #include #include @@ -65,6 +67,8 @@ __FBSDID("$FreeBSD$"); void print_op (u_int8_t, const char *, const char *); void print_port (u_int8_t, u_int16_t, u_int16_t, const char *, int); void print_ugid (u_int8_t, unsigned, unsigned, const char *, unsigned); +void print_ieee8021q_pcp (u_int8_t, uint8_t, uint8_t); +void print_ieee8021q_setpcp (u_int8_t); void print_flags (u_int8_t); void print_fromto(struct pf_rule_addr *, pf_osfp_t, struct pf_rule_addr *, u_int8_t, u_int8_t, int, int); @@ -353,7 +357,48 @@ print_ugid(u_int8_t op, unsigned u1, unsigned u2, print_op(op, a1, a2); } +static const char * +ieee8021q_pcp_name(u_int8_t pcp) +{ + const char *s; + + if (pcp == IEEE8021Q_PCP_BE) + s = "be"; + else if (pcp == IEEE8021Q_PCP_BK) + s = "bk"; + else if (pcp == IEEE8021Q_PCP_EE) + s = "ee"; + else if (pcp == IEEE8021Q_PCP_CA) + s = "ca"; + else if (pcp == IEEE8021Q_PCP_VI) + s = "vi"; + else if (pcp == IEEE8021Q_PCP_VO) + s = "vo"; + else if (pcp == IEEE8021Q_PCP_IC) + s = "ic"; + else if (pcp == IEEE8021Q_PCP_NC) + s = "nc"; + else + s = "??"; + return (s); +} + void +print_ieee8021q_pcp(u_int8_t op, u_int8_t pcp0, u_int8_t pcp1) +{ + + printf(" ieee8021q-pcp"); + print_op(op, ieee8021q_pcp_name(pcp0), ieee8021q_pcp_name(pcp1)); +} + +void +print_ieee8021q_setpcp(u_int8_t pcp) +{ + + printf(" ieee8021q-setpcp %s", ieee8021q_pcp_name(pcp)); +} + +void print_flags(u_int8_t f) { int i; @@ -1018,6 +1063,13 @@ print_rule(struct pf_rule *r, const char *anchor_c } if (r->rtableid != -1) printf(" rtable %u", r->rtableid); + if (r->ieee8021q_pcp.op != 0) + print_ieee8021q_pcp(r->ieee8021q_pcp.op, + r->ieee8021q_pcp.pcp[0], r->ieee8021q_pcp.pcp[1]); + if (r->ieee8021q_pcp.setpcp & SETPCP_VALID) + print_ieee8021q_setpcp(r->ieee8021q_pcp.setpcp & + SETPCP_PCP_MASK); + if (r->divert.port) { #ifdef __FreeBSD__ printf(" divert-to %u", ntohs(r->divert.port)); Index: contrib/pf/man/pf.conf.5 =================================================================== --- contrib/pf/man/pf.conf.5 (revision 230008) +++ contrib/pf/man/pf.conf.5 (working copy) @@ -1862,6 +1862,33 @@ keyword. .It Ar rtable Aq Ar number Used to select an alternate routing table for the routing lookup. Only effective before the route lookup happened, i.e. when filtering inbound. +.It Ar ieee8021q-pcp Aq Ar pcp +Used to select packets carrying an 802.1q priority tag matching +.Ar pcp ; +possibly values are +.Em bk +(background -- 1 (lowest)), +.Em be +(best effort -- 0, (default)), +.Em ee +(excellent effort -- 2), +.Em ca +(critical applications -- 3), +.Em vi +(video, < 100ms latency -- 4), +.Em vo +(video, < 10ms latency -- 5), +.Em ic +(Internetwork control -- 6), +.Em nc +(Network control -- 7 highest)). +Further discussion of 802.1q may be found in +.Xr ifconfig 8 . +.It Ar ieee8021q-setpcp Aq Ar pcp +Used to set the 802.1q priority tag on a packet, if transmitted over an +802.1q-enabled interface. +Possible values are selected from the same set of priorities as +.Ar ieee8021q-pcp . .It Xo Ar divert-to Aq Ar host .Ar port Aq Ar port .Xc @@ -3088,6 +3115,7 @@ Rules with a route label do not match any traffic. .Xr protocols 5 , .Xr services 5 , .Xr ftp-proxy 8 , +.Xr ifconfig 8 , .Xr pfctl 8 , .Xr pflogd 8 , .Xr route 8