Initial import of qos-profiles

Role subpackages set the system-wide default qdisc and TCP congestion
control via /usr/lib/sysctl.d, without pulling in the base package:

  server   fq + bbr        bare metal, VM guests
  vmhost   fq_codel + bbr  hypervisors, container hosts
  router   fq_codel + cubic
  desktop  fq_codel + cubic

The base package applies cake per interface from
/etc/qos-profiles/<iface>.conf, layered over role defaults and
/etc/qos-profiles/defaults.conf:

- LINK_RATE with per-direction overrides, shaped to SHAPE_PERCENT
  (default 95, per direction overridable)
- outbound fairness per source host and diffserv4; inbound via ifb,
  fairness per destination host, besteffort with DSCP wash
- NAT awareness, extra cake args (shared and per direction), raw
  qdisc overrides
- config parsed without executing it, validated before any change,
  previous state undone on each run

Applied by the qos-profiles@<iface> unit (bound to the device) or the
qos-profiles-networkmanager dispatcher subpackage, installed
automatically on hosts with NetworkManager. Includes ready-made
examples for VM hosts, OVH dual-NIC servers, DC and home routers.
This commit is contained in:
zpericic committed 2026-10-10 22:41:12 +02:00
commit 86a0d2cdd1
26 files changed
+672

No files matched your search

+16
View File
@@ -0,0 +1,16 @@
#!/usr/bin/bash
# NetworkManager dispatcher: apply qos-profiles when an interface with
# /etc/qos-profiles/<iface>.conf comes up, clean up when it is removed.
# Both the device and its IP interface are checked (eth0 and ppp0 for PPPoE).
action=$2
ifaces=("$1")
[[ -n ${DEVICE_IP_IFACE:-} && $DEVICE_IP_IFACE != "$1" ]] && ifaces+=("$DEVICE_IP_IFACE")
for iface in "${ifaces[@]}"; do
[[ -n $iface && -f /etc/qos-profiles/$iface.conf ]] || continue
case $action in
up | reapply) /usr/libexec/qos-profiles/qos-apply start "$iface" ;;
down) [[ -e /sys/class/net/$iface ]] || /usr/libexec/qos-profiles/qos-apply stop "$iface" ;;
esac
done
+3
View File
@@ -0,0 +1,3 @@
# qos-profiles-desktop: workstation. Same as the Fedora defaults, pinned explicitly.
-net.core.default_qdisc = fq_codel
-net.ipv4.tcp_congestion_control = cubic
+4
View File
@@ -0,0 +1,4 @@
# qos-profiles-router: forwarding router.
# fq_codel on all interfaces; shape the WAN with cake in /etc/qos-profiles/<iface>.conf.
-net.core.default_qdisc = fq_codel
-net.ipv4.tcp_congestion_control = cubic
+4
View File
@@ -0,0 +1,4 @@
# qos-profiles-server: bare-metal server or VM guest.
# Traffic is mostly locally generated TCP: fq paces per socket, BBR uses the pacing.
-net.core.default_qdisc = fq
-net.ipv4.tcp_congestion_control = bbr
+6
View File
@@ -0,0 +1,6 @@
# qos-profiles-vmhost: hypervisor / container host.
# Traffic is mostly forwarded from guests: fq_codel keeps queue delay low for it.
# BBR still serves the host's own TCP and rootful containers.
# Per-guest fairness on the uplink: /etc/qos-profiles/<iface>.conf with cake.
-net.core.default_qdisc = fq_codel
-net.ipv4.tcp_congestion_control = bbr
+96
View File
@@ -0,0 +1,96 @@
# qos-profiles
Default qdisc and TCP congestion control per machine role, plus optional
per-interface qdiscs (cake, with ifb for inbound traffic).
## Roles
Install exactly one role subpackage:
| Subpackage | default_qdisc | congestion control | cake NAT | Use for |
| ---------------------- | ------------- | ------------------ | -------- | -------------------------------------------- |
| `qos-profiles-server` | fq | bbr | no | bare-metal servers without guests, VM guests |
| `qos-profiles-vmhost` | fq_codel | bbr | no | hypervisors, container hosts |
| `qos-profiles-router` | fq_codel | cubic | yes | routers |
| `qos-profiles-desktop` | fq_codel | cubic | no | workstations |
The sysctl file is `/usr/lib/sysctl.d/60-qos-profiles-<role>.conf`; override it
in `/etc/sysctl.d/`. `default_qdisc` only applies to interfaces created after
it is set, so reboot after installing or switching roles. Congestion control
is system-wide (or per route), not per interface.
## Per-interface qdiscs
Needs the `qos-profiles` package; the role packages don't pull it in.
Create `/etc/qos-profiles/<iface>.conf`, see
`/usr/share/qos-profiles/example.conf` for all variables and
`/usr/share/qos-profiles/examples/` for ready-made setups:
| Example | Setup |
| --------------------------- | -------------------------------------------------- |
| `defaults.conf` | site-wide `/etc/qos-profiles/defaults.conf` |
| `vmhost-routed-uplink.conf` | VM host, guests routed via the host uplink (EVPN) |
| `vmhost-egress-only.conf` | VM host, outbound fairness only at NIC line rate |
| `vmhost-nat-guests.conf` | VM/container host with masqueraded guests |
| `ovh-public.conf` | dual-NIC dedicated server, public NIC |
| `ovh-vrack-private.conf` | private NIC capped by the provider, outbound only |
| `dc-router-transit.conf` | datacenter router, transit uplink, no NAT |
| `router-ftth.conf` | fibre with Ethernet handoff, NAT |
| `router-vdsl-pppoe.conf` | VDSL2 with PPPoE on `ppp0`, NAT, ACK filtering |
| `router-docsis.conf` | cable, NAT, ACK filtering |
| `backup-nic-raw.conf` | raw `fq` qdisc with a per-flow cap instead of cake |
Minimal example:
```
LINK_RATE=1gbit
```
This shapes both directions to 95% of the link rate with cake. Outbound,
traffic is shared per source host and DiffServ classes are applied
(`diffserv4`). Inbound, traffic is shared per destination host, DSCP marks
are ignored (`besteffort`) and cleared (`wash`), since they come from the
internet. `EGRESS_SHAPE_PERCENT` and `INGRESS_SHAPE_PERCENT` set the
percentage per direction.
Configuration is read in order, later overriding earlier:
1. `/usr/lib/qos-profiles/role-<role>.conf` — installed role package
2. `/etc/qos-profiles/defaults.conf` — site-wide
3. `/etc/qos-profiles/<iface>.conf` — the interface
It is applied by either:
- `qos-profiles-networkmanager` (installed automatically with NetworkManager)
when NetworkManager brings the interface up, or
- `systemctl enable --now qos-profiles@<iface>` for interfaces NetworkManager
doesn't manage. It runs whenever the device appears.
Both may be active; they're serialised and idempotent. To apply changes run
`systemctl restart qos-profiles@<iface>` or
`/usr/libexec/qos-profiles/qos-apply start <iface>`. Stopping restores the
default qdisc and removes the ifb device.
Inbound shaping replaces any existing `clsact` qdisc on the interface (and its
tc/BPF filters).
## DiffServ
`diffserv4` sorts outbound packets by DSCP into bulk (CS1), best effort, video
(AF4x, CS3, ...) and voice (EF, CS6, ...). Higher classes get priority only up
to a share of the rate, so they can't starve the others. Marking backup or
replication traffic CS1 (e.g. nftables `ip dscp set cs1`) makes it yield to
everything else.
## Topologies
- **VM host, VMs routed via the host uplink (EVPN anycast gateway):** cake on
the uplink NIC gives each guest an equal share. VXLAN-encapsulated traffic on
the same NIC is seen by its outer header (the VTEP address), so it shares as
a single host.
- **VM host, VMs bridged to a switch, routing elsewhere:** the WAN bottleneck
is at the router; put cake on the router WAN (`qos-profiles-router`). The
host only needs the role package.
- **Host with separate public and private NICs:** configure the public NIC only.
- **Bond or VLAN uplink:** these devices have no queue of their own; put cake
on the physical NIC, or set a rate explicitly.
+9
View File
@@ -0,0 +1,9 @@
# Dedicated backup/storage NIC with a raw qdisc instead of cake.
# Install as: /etc/qos-profiles/<backup nic>.conf
#
# fq with a per-flow cap: a single large transfer can't fill the link, while
# many parallel flows still can. EGRESS replaces everything cake-related for
# that direction; inbound is left alone.
EGRESS="fq maxrate 500mbit"
INGRESS_RATE=none
+10
View File
@@ -0,0 +1,10 @@
# Datacenter router, transit uplink. Hosts behind it have public addresses
# (no NAT); VM hosts are on L2 behind this router and need no shaping.
# Install as: /etc/qos-profiles/<transit>.conf (role: qos-profiles-router)
#
# Fair share per server in both directions. Cake uses a single queue: fine at
# 1 Gbit/s, check CPU load before using it above 10 Gbit/s.
LINK_RATE=1gbit
NAT=no
CAKE_ARGS="ethernet"
+11
View File
@@ -0,0 +1,11 @@
# Site-wide defaults, for all configured interfaces.
# Install as: /etc/qos-profiles/defaults.conf
#
# Only overrides what differs from the role package defaults; each
# /etc/qos-profiles/<iface>.conf can still override these.
# Leave more headroom inbound, where shaping is less precise.
INGRESS_SHAPE_PERCENT=90
# All our uplinks are plain Ethernet.
CAKE_ARGS="ethernet"
+8
View File
@@ -0,0 +1,8 @@
# Dedicated server with separate public and private NICs (e.g. OVH), public
# side. Configure only the public NIC; the private one keeps the default qdisc.
# Install as: /etc/qos-profiles/<public nic>.conf
#
# Use the bandwidth the provider guarantees, which can be lower than the NIC
# speed (e.g. 1 Gbit/s NIC with 500 Mbit/s public bandwidth).
LINK_RATE=500mbit
+11
View File
@@ -0,0 +1,11 @@
# Private network NIC capped by the provider below the NIC speed (e.g. OVH
# vRack limited to 2 Gbit/s on a 10 Gbit/s NIC), used for replication and
# backups between our own hosts.
# Install as: /etc/qos-profiles/<private nic>.conf
#
# Shape outbound only, so the queue stays here instead of the provider
# dropping at its policer. Traffic is our own, so DiffServ marks are trusted:
# mark bulk transfers CS1 to let them yield.
EGRESS_RATE=2gbit
INGRESS_RATE=none
+11
View File
@@ -0,0 +1,11 @@
# Router on cable (DOCSIS), asymmetric, NAT.
# Install as: /etc/qos-profiles/<wan>.conf (role: qos-profiles-router)
#
# Cable ISPs usually provision a bit above the advertised rate; start with the
# advertised rates and lower SHAPE_PERCENT if latency still rises under load.
EGRESS_RATE=50mbit
INGRESS_RATE=500mbit
INGRESS_SHAPE_PERCENT=90
CAKE_ARGS="docsis"
EGRESS_CAKE_ARGS="ack-filter"
+9
View File
@@ -0,0 +1,9 @@
# Office/home router on fibre (FTTH/GPON) with an Ethernet handoff and NAT.
# Install as: /etc/qos-profiles/<wan>.conf (role: qos-profiles-router)
#
# Use the subscribed rates. If the ISP tags the WAN with a VLAN, put this on
# the physical NIC and add "ether-vlan".
EGRESS_RATE=250mbit
INGRESS_RATE=500mbit
CAKE_ARGS="ethernet"
+13
View File
@@ -0,0 +1,13 @@
# Router on VDSL2 with PPPoE, asymmetric, NAT.
# Install as: /etc/qos-profiles/ppp0.conf (role: qos-profiles-router)
#
# Configure the PPPoE interface (ppp0), not the Ethernet below it. Use the
# DSL sync rates from the modem, they're higher than the usable rate; the
# pppoe-ptm overhead accounts for the rest.
# ack-filter drops redundant TCP ACKs on the slow upstream.
EGRESS_RATE=20mbit
INGRESS_RATE=100mbit
INGRESS_SHAPE_PERCENT=90
CAKE_ARGS="pppoe-ptm"
EGRESS_CAKE_ARGS="ack-filter"
+9
View File
@@ -0,0 +1,9 @@
# VM host where only outbound fairness matters and the NIC line rate is the
# real limit (provider delivers the full NIC speed).
# Install as: /etc/qos-profiles/<uplink>.conf (role: qos-profiles-vmhost)
#
# Cake runs unlimited: no shaping cost, the NIC's byte queue limits keep the
# queue in cake. Inbound is left alone.
EGRESS_RATE=unlimited
INGRESS_RATE=none
+9
View File
@@ -0,0 +1,9 @@
# VM or container host with masqueraded guests (libvirt NAT network, rootful
# containers on a NAT bridge).
# Install as: /etc/qos-profiles/<uplink>.conf (role: qos-profiles-vmhost)
#
# With NAT=yes cake looks up the conntrack entry and shares per internal
# guest address instead of the single public one.
LINK_RATE=1gbit
NAT=yes
+9
View File
@@ -0,0 +1,9 @@
# VM host, guests routed through the host uplink (EVPN, anycast gateway on
# the host). Fair share per guest in both directions.
# Install as: /etc/qos-profiles/<uplink>.conf (role: qos-profiles-vmhost)
#
# Outbound is shared per guest source IP, inbound per guest destination IP.
# VXLAN traffic between hosts on the same NIC shares as a single host (cake
# only sees the outer VTEP addresses).
LINK_RATE=1gbit
+50
View File
@@ -0,0 +1,50 @@
# qos-profiles interface configuration, all variables. Ready-made setups are
# in /usr/share/qos-profiles/examples/.
# Copy to /etc/qos-profiles/<iface>.conf. Applied by the NetworkManager
# dispatcher when the interface comes up, or without NetworkManager by:
# systemctl enable --now qos-profiles@<iface>
#
# Defaults come from the installed role package (/usr/lib/qos-profiles/) and
# /etc/qos-profiles/defaults.conf. Use a physical NIC: bond and VLAN devices
# have no queue of their own.
# Full link rate in both directions (bit, kbit, mbit, gbit, tbit).
# Cake shapes to SHAPE_PERCENT of it.
#LINK_RATE=1gbit
# Per-direction link rates, override LINK_RATE.
# EGRESS_RATE: "unlimited" = cake without shaping (default without LINK_RATE,
# fine while the NIC line rate is the bottleneck), "none" = off
# INGRESS_RATE: "none" = no inbound shaping (default without LINK_RATE)
#EGRESS_RATE=100mbit
#INGRESS_RATE=none
# Percentage of the link rate to shape to, so the queue builds here and not
# in the upstream equipment. Per-direction values override SHAPE_PERCENT.
#SHAPE_PERCENT=95
#EGRESS_SHAPE_PERCENT=95
#INGRESS_SHAPE_PERCENT=90
# DiffServ classes: besteffort, diffserv3, diffserv4, diffserv8, precedence.
# Inbound marks come from the internet, so they're ignored by default and
# cleared before forwarding (INGRESS_WASH=yes).
#DIFFSERV=diffserv4
#INGRESS_DIFFSERV=besteffort
#INGRESS_WASH=yes
# Fairness per host behind NAT on this machine (yes/no).
#NAT=no
# Extra cake options for both directions: link overhead (ethernet, pppoe-ptm,
# docsis, ...), rtt, or a different fairness mode (flows, triple-isolate, ...).
# Per-direction options are added after CAKE_ARGS.
#CAKE_ARGS="ethernet"
#EGRESS_CAKE_ARGS="ack-filter"
#INGRESS_CAKE_ARGS=""
# ifb device for inbound shaping, default ifb-<iface> (max 15 characters).
#IFB=ifb-wan
# Raw qdisc specs; when set, replace everything above for that direction.
#EGRESS="fq_codel target 3ms"
#INGRESS="cake besteffort dual-dsthost ingress bandwidth 450mbit"
Executable
+176
View File
@@ -0,0 +1,176 @@
#!/usr/bin/bash
# Apply or remove per-interface qdiscs (cake, with ifb for inbound shaping).
#
# Configuration is read, later files overriding earlier ones, from:
# /usr/lib/qos-profiles/role-*.conf defaults of the installed role package
# /etc/qos-profiles/defaults.conf site-wide defaults
# /etc/qos-profiles/<iface>.conf the interface
# See /usr/share/doc/qos-profiles/README.md for the variables.
set -euo pipefail
shopt -s nullglob
KEYS=(LINK_RATE EGRESS_RATE INGRESS_RATE
SHAPE_PERCENT EGRESS_SHAPE_PERCENT INGRESS_SHAPE_PERCENT
DIFFSERV INGRESS_DIFFSERV INGRESS_WASH NAT
CAKE_ARGS EGRESS_CAKE_ARGS INGRESS_CAKE_ARGS IFB EGRESS INGRESS)
CONF_DIR=/etc/qos-profiles
ROLE_DIR=/usr/lib/qos-profiles
STATE_DIR=/run/qos-profiles
die() {
echo "${0##*/}: $*" >&2
exit 1
}
# Parse KEY=value lines without executing them. Values may be "quoted",
# 'quoted' or bare; a trailing # comment is allowed.
load() {
local file=$1 line key val n=0
while IFS= read -r line || [[ -n $line ]]; do
n=$((n + 1))
[[ $line =~ ^[[:space:]]*(#|$) ]] && continue
[[ $line =~ ^[[:space:]]*([A-Z_]+)=(.*)$ ]] || die "$file:$n: invalid line"
key=${BASH_REMATCH[1]}
val=${BASH_REMATCH[2]}
[[ " ${KEYS[*]} " == *" $key "* ]] || die "$file:$n: unknown variable $key"
if [[ $val =~ ^\"([^\"]*)\"[[:space:]]*(#.*)?$ || $val =~ ^\'([^\']*)\'[[:space:]]*(#.*)?$ ]]; then
val=${BASH_REMATCH[1]}
elif [[ $val =~ ^([^[:space:]#\"\']*)[[:space:]]*(#.*)?$ ]]; then
val=${BASH_REMATCH[1]}
else
die "$file:$n: invalid value for $key"
fi
printf -v "$key" '%s' "$val"
done <"$file"
}
# Usage: yesno <variable> <keyword if yes> <keyword if no>
yesno() {
case ${!1,,} in
yes) echo "$2" ;;
no) echo "$3" ;;
*) die "$1 must be yes or no" ;;
esac
}
# Usage: percent <variable>
percent() {
[[ ${!1} =~ ^[0-9]+$ ]] && ((${!1} >= 1 && ${!1} <= 100)) || die "$1 must be 1-100"
echo "${!1}"
}
# Usage: shaped_rate <link rate> <percent>
# Link rate (e.g. 1gbit, 2.5Gbit, 500mbit) -> percent of it in kbit.
shaped_rate() {
awk -v r="${1,,}" -v p="$2" 'BEGIN {
n = r; sub(/[a-z]+$/, "", n); u = substr(r, length(n) + 1)
m["bit"] = 1; m[""] = 1; m["kbit"] = 1e3; m["mbit"] = 1e6; m["gbit"] = 1e9; m["tbit"] = 1e12
if (n !~ /^[0-9]+(\.[0-9]+)?$/ || !(u in m) || n == 0) exit 1
printf "%dkbit\n", n * m[u] * p / 100 / 1000
}' || die "invalid rate: $1"
}
# Build the qdisc for one direction into the named array.
# Usage: build <array> <raw spec> <link rate> <percent> <cake args> <diffserv> <isolation> [extra...]
build() {
local -n spec=$1
local raw=$2 rate=$3 pct=$4 args=$5 diffserv=$6 isolation=$7 bw extra
shift 7
spec=()
if [[ -n $raw ]]; then
read -ra spec <<<"$raw"
return
fi
[[ -n $rate && $rate != none ]] || return 0
spec=(cake "$diffserv" "$isolation" "$@")
if [[ $rate == unlimited ]]; then
spec+=(unlimited)
else
bw=$(shaped_rate "$rate" "$pct")
spec+=(bandwidth "$bw")
fi
read -ra extra <<<"$CAKE_ARGS $args"
spec+=("${extra[@]}")
}
# Undo whatever the last start applied, as recorded in the state file.
cleanup() {
local state=$STATE_DIR/$dev key val
[[ -f $state ]] || return 0
while IFS='=' read -r key val; do
case $key in
egress) tc qdisc del dev "$dev" root 2>/dev/null || true ;;
ifb)
tc qdisc del dev "$dev" clsact 2>/dev/null || true
ip link del dev "$val" 2>/dev/null || true
;;
esac
done <"$state"
rm -f "$state"
}
start() {
local -a egress ingress
local state=$STATE_DIR/$dev ifb egress_pct ingress_pct wash nat
[[ -d /sys/class/net/$dev ]] || die "no such interface: $dev"
: "${EGRESS_SHAPE_PERCENT:=$SHAPE_PERCENT}" "${INGRESS_SHAPE_PERCENT:=$SHAPE_PERCENT}"
egress_pct=$(percent EGRESS_SHAPE_PERCENT)
ingress_pct=$(percent INGRESS_SHAPE_PERCENT)
wash=$(yesno INGRESS_WASH wash nowash)
nat=$(yesno NAT nat nonat)
# Egress without any rate runs cake unlimited (the NIC is the bottleneck);
# ingress without a rate is not shaped, cake needs one there.
build egress "$EGRESS" "${EGRESS_RATE:-${LINK_RATE:-unlimited}}" \
"$egress_pct" "$EGRESS_CAKE_ARGS" "$DIFFSERV" dual-srchost "$nat"
build ingress "$INGRESS" "${INGRESS_RATE:-$LINK_RATE}" \
"$ingress_pct" "$INGRESS_CAKE_ARGS" "$INGRESS_DIFFSERV" dual-dsthost "$nat" \
ingress "$wash"
ifb=${IFB:-ifb-$dev}
((${#ingress[@]} == 0 || ${#ifb} <= 15)) ||
die "ifb name '$ifb' too long, set IFB= in $CONF_DIR/$dev.conf"
cleanup
if ((${#egress[@]})); then
tc qdisc replace dev "$dev" root "${egress[@]}"
echo egress=1 >>"$state"
fi
if ((${#ingress[@]})); then
ip link add name "$ifb" type ifb
echo "ifb=$ifb" >>"$state"
ip link set dev "$ifb" up
tc qdisc replace dev "$ifb" root "${ingress[@]}"
tc qdisc replace dev "$dev" clsact
tc filter add dev "$dev" ingress protocol all matchall \
action mirred egress redirect dev "$ifb"
fi
}
[[ $# -eq 2 ]] || die "usage: ${0##*/} start|stop <interface>"
action=$1
dev=$2
[[ $dev =~ ^[^/[:space:]]{1,15}$ && $dev != . && $dev != .. ]] || die "invalid interface name: $dev"
LINK_RATE= EGRESS_RATE= INGRESS_RATE=
SHAPE_PERCENT=95 EGRESS_SHAPE_PERCENT= INGRESS_SHAPE_PERCENT=
DIFFSERV=diffserv4 INGRESS_DIFFSERV=besteffort INGRESS_WASH=yes NAT=no
CAKE_ARGS= EGRESS_CAKE_ARGS= INGRESS_CAKE_ARGS= IFB= EGRESS= INGRESS=
for f in "$ROLE_DIR"/role-*.conf "$CONF_DIR/defaults.conf" "$CONF_DIR/$dev.conf"; do
[[ -f $f ]] && load "$f"
done
# Serialise the systemd unit and the NetworkManager dispatcher.
mkdir -p "$STATE_DIR"
exec 9>"$STATE_DIR/$dev.lock"
flock 9
case $action in
start) start ;;
stop) cleanup ;;
*) die "unknown action: $action" ;;
esac
+2
View File
@@ -0,0 +1,2 @@
# qos-profiles creates ifb devices on demand; don't create unused ifb0/ifb1.
options ifb numifbs=0
+170
View File
@@ -0,0 +1,170 @@
Summary: Queueing discipline and TCP congestion control profiles
Name: qos-profiles
Version: 1
Release: 1%{?dist}
License: GPL-2.0-or-later
URL: http://gitea.infomaas.com/rpms-fedora-ims/%{name}
BuildArch: noarch
Source0: README.md
Source1: qos-apply
Source2: qos-profiles@.service
Source3: qos-profiles-modprobe.conf
Source4: example.conf
Source5: 50-qos-profiles
Source10: 60-qos-profiles-server.conf
Source11: 60-qos-profiles-vmhost.conf
Source12: 60-qos-profiles-router.conf
Source13: 60-qos-profiles-desktop.conf
Source20: role-server.conf
Source21: role-vmhost.conf
Source22: role-router.conf
Source23: role-desktop.conf
Source30: example-backup-nic-raw.conf
Source31: example-dc-router-transit.conf
Source32: example-defaults.conf
Source33: example-ovh-public.conf
Source34: example-ovh-vrack-private.conf
Source35: example-router-docsis.conf
Source36: example-router-ftth.conf
Source37: example-router-vdsl-pppoe.conf
Source38: example-vmhost-egress-only.conf
Source39: example-vmhost-nat-guests.conf
Source40: example-vmhost-routed-uplink.conf
BuildRequires: systemd-rpm-macros
Requires: gawk
Requires: iproute
Requires: iproute-tc
Requires: util-linux-core
Recommends: (%{name}-networkmanager if NetworkManager)
%{?systemd_requires}
%description
Per-interface queueing disciplines (cake, with ifb for inbound shaping)
configured in /etc/qos-profiles/<interface>.conf and applied by the
qos-profiles@<interface> systemd unit or the NetworkManager dispatcher.
The role subpackages set the default qdisc, TCP congestion control and
per-interface defaults for servers, VM hosts, routers and desktops.
%package networkmanager
Summary: NetworkManager dispatcher for qos-profiles
Requires: %{name} = %{version}-%{release}
Requires: NetworkManager
%description networkmanager
Applies /etc/qos-profiles/<interface>.conf when NetworkManager brings the
interface up, without enabling the qos-profiles@<interface> unit.
%package server
Summary: Queueing profile for servers and VM guests
Provides: %{name}-role
Conflicts: %{name}-role
%description server
Sets fq as the default qdisc and BBR as TCP congestion control, for machines
that mostly send their own TCP traffic: bare-metal servers and VM guests.
%package vmhost
Summary: Queueing profile for VM and container hosts
Provides: %{name}-role
Conflicts: %{name}-role
%description vmhost
Sets fq_codel as the default qdisc for traffic forwarded from guests and BBR as
TCP congestion control for the host's own traffic. Configure cake on the
uplink for fairness per guest.
%package router
Summary: Queueing profile for routers
Provides: %{name}-role
Conflicts: %{name}-role
%description router
Sets fq_codel as the default qdisc and CUBIC as TCP congestion control, and
enables cake NAT awareness by default. Configure cake on the WAN with the
link rate.
%package desktop
Summary: Queueing profile for desktops
Provides: %{name}-role
Conflicts: %{name}-role
%description desktop
Sets fq_codel as the default qdisc and CUBIC as TCP congestion control.
%prep
cp -p %{SOURCE0} .
%install
install -Dpm 755 %{SOURCE1} %{buildroot}%{_libexecdir}/%{name}/qos-apply
install -Dpm 644 %{SOURCE2} %{buildroot}%{_unitdir}/qos-profiles@.service
install -Dpm 644 %{SOURCE3} %{buildroot}%{_modprobedir}/%{name}.conf
install -Dpm 644 %{SOURCE4} %{buildroot}%{_datadir}/%{name}/example.conf
install -Dpm 755 %{SOURCE5} %{buildroot}%{_prefix}/lib/NetworkManager/dispatcher.d/50-qos-profiles
install -dm 755 %{buildroot}%{_sysconfdir}/%{name}
for f in %{SOURCE30} %{SOURCE31} %{SOURCE32} %{SOURCE33} %{SOURCE34} %{SOURCE35} \
%{SOURCE36} %{SOURCE37} %{SOURCE38} %{SOURCE39} %{SOURCE40}; do
n=$(basename "$f")
install -Dpm 644 "$f" %{buildroot}%{_datadir}/%{name}/examples/"${n#example-}"
done
for f in %{SOURCE10} %{SOURCE11} %{SOURCE12} %{SOURCE13}; do
install -Dpm 644 "$f" %{buildroot}%{_sysctldir}/"$(basename "$f")"
done
for f in %{SOURCE20} %{SOURCE21} %{SOURCE22} %{SOURCE23}; do
install -Dpm 644 "$f" %{buildroot}%{_prefix}/lib/%{name}/"$(basename "$f")"
done
%post
%systemd_post qos-profiles@.service
%preun
%systemd_preun qos-profiles@.service
%post server
%sysctl_apply 60-qos-profiles-server.conf
%post vmhost
%sysctl_apply 60-qos-profiles-vmhost.conf
%post router
%sysctl_apply 60-qos-profiles-router.conf
%post desktop
%sysctl_apply 60-qos-profiles-desktop.conf
%files
%doc README.md
%{_libexecdir}/%{name}/
%{_unitdir}/qos-profiles@.service
%{_modprobedir}/%{name}.conf
%{_datadir}/%{name}/
%dir %{_prefix}/lib/%{name}
%dir %{_sysconfdir}/%{name}
%files networkmanager
%{_prefix}/lib/NetworkManager/dispatcher.d/50-qos-profiles
%files server
%{_sysctldir}/60-qos-profiles-server.conf
%dir %{_prefix}/lib/%{name}
%{_prefix}/lib/%{name}/role-server.conf
%files vmhost
%{_sysctldir}/60-qos-profiles-vmhost.conf
%dir %{_prefix}/lib/%{name}
%{_prefix}/lib/%{name}/role-vmhost.conf
%files router
%{_sysctldir}/60-qos-profiles-router.conf
%dir %{_prefix}/lib/%{name}
%{_prefix}/lib/%{name}/role-router.conf
%files desktop
%{_sysctldir}/60-qos-profiles-desktop.conf
%dir %{_prefix}/lib/%{name}
%{_prefix}/lib/%{name}/role-desktop.conf
%changelog
* Sat Oct 10 2026 Zoran Pericic <zpericic@netst.org> - 1-1
- Initial release
+15
View File
@@ -0,0 +1,15 @@
[Unit]
Description=Queueing disciplines for %I
Documentation=file:///usr/share/doc/qos-profiles/README.md
BindsTo=sys-subsystem-net-devices-%i.device
After=sys-subsystem-net-devices-%i.device
ConditionPathExists=/etc/qos-profiles/%I.conf
[Service]
Type=oneshot
RemainAfterExit=yes
ExecStart=/usr/libexec/qos-profiles/qos-apply start %I
ExecStop=/usr/libexec/qos-profiles/qos-apply stop %I
[Install]
WantedBy=sys-subsystem-net-devices-%i.device
+5
View File
@@ -0,0 +1,5 @@
# Interface defaults of qos-profiles-desktop, override in /etc/qos-profiles/.
DIFFSERV=diffserv4
INGRESS_DIFFSERV=besteffort
INGRESS_WASH=yes
NAT=no
+6
View File
@@ -0,0 +1,6 @@
# Interface defaults of qos-profiles-router, override in /etc/qos-profiles/.
# nat: cake sees LAN addresses behind masquerade, for per-host fairness.
DIFFSERV=diffserv4
INGRESS_DIFFSERV=besteffort
INGRESS_WASH=yes
NAT=yes
+5
View File
@@ -0,0 +1,5 @@
# Interface defaults of qos-profiles-server, override in /etc/qos-profiles/.
DIFFSERV=diffserv4
INGRESS_DIFFSERV=besteffort
INGRESS_WASH=yes
NAT=no
+5
View File
@@ -0,0 +1,5 @@
# Interface defaults of qos-profiles-vmhost, override in /etc/qos-profiles/.
DIFFSERV=diffserv4
INGRESS_DIFFSERV=besteffort
INGRESS_WASH=yes
NAT=no