[[qos-dscp-pcp]]
== QoS, DSCP/TOS, Priority and IEEE 802.1q PCP

In many use cases operators want to apply different QoS classes for user plane
vs. control plane traffic.  IP Routers, Ethernet switches and other network gear
can then perform intelligent queue management as required for the respective service.

For example, voice user plane frames need a rather stable and short latency,
while IP user plane and control plane traffic has less critical latency requirements.

=== IP Level (DSCP)

At IP level, different priorities / classes of traffic are expressed
in accordance to <<ietf-rfc2474>> by the DSCP (Differentiated Services Code
Point) field of the IP header.  DSCP resembles the upper 6 bits of the
field formerly known as the TOS bits as per <<ietf-rfc791>>.

On Linux and other operating systems with BSD-style sockets API, the
applications can request a specific DSCP value to be used for packets
generated by those sockets.

Osmocom CNI software such as osmo-bts and osmo-mgw support setting the
DSCP value via VTY commands, see e.g. the `rtp ip-dscp` setting of the
`bts` node in osmo-bts.


=== Packet Priority

In the Linux network stack, every packet is represented by `struct
sk_buff`, which has an associated _priority_.  Furthermore, every socket
through which applications send data have an associated _socket
priority_.  Each time a packet is transmitted through a given socket,
the packet inherits the packet priority from the socket priority.

Furthermore, there is a mapping table that maps DSCP/TOS bits to
priority.  The sixteen different TOS bit values are mapped to priority
values as follows:

.Linux kernel default DSCP/TOS -> priority mapping
[options="header",width="50%"]
|===
|TOS (binary)|DSCP (binary)|Priority (decimal)
|xxx0000x|xxx000|0
|xxx0001x|xxx000|0
|xxx0010x|xxx001|0
|xxx0011x|xxx001|0
|xxx0100x|xxx010|2
|xxx0101x|xxx010|2
|xxx0110x|xxx011|2
|xxx0111x|xxx011|2
|xxx1000x|xxx100|6
|xxx1001x|xxx100|6
|xxx1010x|xxx101|6
|xxx1011x|xxx101|6
|xxx1100x|xxx110|4
|xxx1101x|xxx110|4
|xxx1110x|xxx111|4
|xxx1111x|xxx111|4
|===

This table of default DSCP/TOS -> priority bit mappings cannot be
modified.

However, the per-packet _priority_ values can be set by various means
of network policy, including

* by packet filter rules (iptables, ip6tables, nftables)
** if you use `iptables`, using `CLASSIFY --set-class` in the `mangle` table
** if you use `nftables`, using `meta priority set` in the `mangle` table
* by the application using the SO_PRIORITY socket option (currently not yet supported by Osmocom CNI)


=== Ethernet Level (PCP)

At Ethernet level, different priorities / QoS classes are expressed by
the so-called PCP (Priority Code Point) field in the IEEE 802.1q (VLAN)
header.

NOTE:: This means that PCP functionality requires the use of IEEE 802.q
VLAN. You cannot use PCP without VLAN.

The Linux kernel assigns IEEE 802.1q PCP bits based on a _mapping_
between the _priority_ and the PCP value.  Each VLAN network device
maintains a separate map for both egress (transmit) and ingress
(receive) path.

The current priority mappings can be inspected via the `/proc`
filesystem.  For example, if you have a VLAN device `eth0.9` for
VLAN ID 9 on the net-device `eth0`, you can use the following example:

.Example: Inspecting the current egress QoS map
----
$ sudo cat /proc/net/vlan/eth0.9<1>
eth0.9  VID: 9   REORDER_HDR: 1  dev->priv_flags: 1021
         total frames received       123340
          total bytes received     40668066
      Broadcast/Multicast Rcvd         1106

      total frames transmitted        10499
       total bytes transmitted      1570809
Device: eth0
INGRESS priority mappings: 0:0  1:0  2:0  3:0  4:0  5:0  6:0 7:0 <2>
 EGRESS priority mappings: <3>
----
<1> make sure to specify your specific VLAN interface name here instead of `eth0.9`
<2> ingress priority mappings (all PCP values mapped to priority 0)
<3> egress priority mappings (empty)

As we can see in the above example, there are no egress priority
mappings yet.  Let's create three new mappings, mapping _priority_
value 1 to PCP 1, _priority_ 2 to PCP 2, and _priority_ 3 to PCP 3:

.Example: Creating three new egress QoS mappings
----
$ sudo ip link set dev eth0.9<1> type vlan egress-qos-map 1:1 2:2 3:3 <2>
$ sudo cat /proc/net/vlan/eth0.9 <3>
eth0.9  VID: 9   REORDER_HDR: 1  dev->priv_flags: 1021
         total frames received       123898
          total bytes received     40843611
      Broadcast/Multicast Rcvd         1106

      total frames transmitted        10517
       total bytes transmitted      1574357
Device: eth0
INGRESS priority mappings: 0:0  1:0  2:0  3:0  4:0  5:0  6:0 7:0
 EGRESS priority mappings: 1:1 2:2 3:3 <4>
----
<1> make sure to specify your specific VLAN interface name here instead of `eth0.9`
<2> command to define three new egress QoS maps
<3> command to re-display the current status
<4> three new egress mappings are shown as given in `ip` command

NOTE:: The settings of the `ip` command are volatile and only active until
the next reboot (or the network device or VLAN is removed).  Please refer to
the documentation of your specific Linux distribution in order to find out how
to make such settings persistent by means of an `ifup` hook whenever the interface
comes up.  For CentOS/RHEL 8 this can e.g. be achieved by means of an `/sbin/ifup-local
script` (when using `network-scripts` and not NetworkManager).  For Debian or Ubuntu,
this typically involves adding `up` lines to `/etc/network/interfaces` or a `/etc/network/if-up.d`
script.


=== Putting things together

Assuming one needs to set both the DSCP bits as well as the PCP for
certain traffic, the above-mentioned mechanisms need to be combined as
follows:

. configure the osmocom program to set the DSCP value
. use the default DSCP -> priority mapping, if possible
. configure an egress QoS map to map from priority to PCP

If the desired combination of DSCP + PCP cannot be achieved that way,
due to the rather static default kernel mapping table, one needs to go
one step further:

. configure the osmocom program to set the DSCP value
. use packet filter rules to set the priority based on DSCP
. configure an egress QoS map to map from priority to PCP

include::{srcdir}/chapters/qos-example.adoc[]
