Add support to priority code point (PCP) that is an 3-bit field

which refers to IEEE 802.1p class of service and maps to the frame
priority level.

Values in order of priority are: 1 (Background (lowest)),
0 (Best effort (default)), 2 (Excellent effort),
3 (Critical applications), 4 (Video, < 100ms latency),
5 (Video, < 10ms latency), 6 (Internetwork control) and
7 (Network control (highest)).

Example of usage:
root# ifconfig em0.1 create
root# ifconfig em0.1 vlanpcp 3

Note:
The review D801 includes the pf(4) part, but as discussed with kristof,
we won't commit the pf(4) bits for now.
The credits of the original code is from rwatson.

Differential Revision:	https://reviews.freebsd.org/D801
Reviewed by:	gnn, adrian, loos
Discussed with: rwatson, glebius, kristof
Tested by:	many including Matthew Grooms <mgrooms__shrew.net>
Obtained from:	pfSense
Relnotes:	Yes
This commit is contained in:
Marcelo Araujo 2016-06-06 09:51:58 +00:00
parent ba3049f99c
commit 2ccbbd06d2
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=301496
7 changed files with 182 additions and 17 deletions

View file

@ -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

View file

@ -1,6 +1,10 @@
/*
* Copyright (c) 1999
* Bill Paul <wpaul@ctr.columbia.edu>. All rights reserved.
* Copyright (c) 1999 Bill Paul <wpaul@ctr.columbia.edu>
* 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' ?
"<none>" : 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' ?
"<none>" : 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),

View file

@ -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.

View file

@ -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) \

View file

@ -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 <sys/mbuf.h>
#include <sys/module.h>
#include <sys/rmlock.h>
#include <sys/priv.h>
#include <sys/queue.h>
#include <sys/socket.h>
#include <sys/sockio.h>
@ -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;

View file

@ -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); \

View file

@ -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.