1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
|
#!/bin/sh
# SPDX-License-Identifier: MIT
#
# Remove qosify qdiscs and ifb devices left behind after qosify exits.
#
# A clean stop only does part of this: interface_clear_qdisc() removes the root
# qdisc, the bpf filters and the ifb device but not clsact, and main() never
# calls qosify_dns_stop(), so ifb-dns stays up. A crash, a SIGKILL or a respawn
# loop leaves the rest too. This script deliberately only touches the devices of
# enabled qosify sections, the ifb names qosify derives from them and qosify's
# own ifb-dns -- never a qdisc or ifb qosify did not create.
#
# `cleanup wait` is for a package removal: qosify is stopped by its own prerm,
# which may run after ours, so the device names are read now and the sweep waits
# up to 30 s for qosify to exit. If it is still running, nothing is touched.
. /lib/functions.sh
. /lib/functions/network.sh
# The lock is an flock on an open fd, not a directory plus an EXIT trap: rpcd
# SIGKILLs this script at its exec timeout (rpc_file_exec_timeout_cb() in
# file.c), the trap never runs, and the leftover directory would then make every
# later run exit 0 without doing anything. The kernel drops an flock on process
# exit however the process dies. The path differs from the old directory so a
# stale one left by an earlier version cannot break the redirect. Without
# busybox flock, run unlocked rather than not at all -- the worst a concurrent
# run can do is repeat a tc delete.
LOCK="/var/lock/qosify-luci-cleanup.lock"
mkdir -p /var/lock
exec 9>"$LOCK"
command -v flock >/dev/null && { flock -n 9 || exit 0; }
# Mirrors interface_ifb_name() in qosify: "ifb-<dev>" while strlen(dev) + 4 is
# below IFNAMSIZ. Longer names take a different branch upstream which we do not
# try to reproduce here.
ifb_name() {
[ "${#1}" -lt 12 ] || return 1
echo "ifb-$1"
}
qosify_filter() {
tc filter show dev "$1" "$2" 2>/dev/null | grep -q ' qosify_'
}
# qosify's filters sit at QOSIFY_PRIO_BASE (0x110 = 272): its bpf classifier on
# egress, and on ingress the classifier plus the DNS and ifb redirects up to 277.
# A clean exit removes them and the root qdisc, so they only survive a crash, and
# then the root cake is qosify's too. Another shaper's root qdisc, and a clsact
# carrying anyone else's filters, are left alone.
clear_dev() {
local dev="$1"
local ifb p
[ -n "$dev" ] || return 0
if [ -e "/sys/class/net/$dev" ]; then
if qosify_filter "$dev" egress; then
tc qdisc del dev "$dev" root 2>/dev/null
tc filter del dev "$dev" egress pref 272 2>/dev/null
fi
if qosify_filter "$dev" ingress; then
for p in 272 273 274 275 276 277; do
tc filter del dev "$dev" ingress pref "$p" 2>/dev/null
done
fi
[ -n "$(tc filter show dev "$dev" ingress 2>/dev/null)$(tc filter show dev "$dev" egress 2>/dev/null)" ] ||
tc qdisc del dev "$dev" clsact 2>/dev/null
fi
# An ifb outlives its parent, so this is not gated on $dev still existing.
# That only helps a `config device`, whose name survives the netdev.
ifb="$(ifb_name "$dev")" || return 0
[ -e "/sys/class/net/$ifb" ] || return 0
ip link set "$ifb" down 2>/dev/null
ip link del "$ifb" 2>/dev/null
}
# add_interface() in qosify.init returns before reading anything else when
# disabled is set, so qosify never touched those devices.
section_enabled() {
local disabled
config_get_bool disabled "$1" disabled 0
[ "$disabled" -eq 0 ]
}
# `config interface` names a netifd interface, not a device; qosify resolves it
# to .l3_device before touching tc, so resolve it the same way. netifd drops
# .l3_device when the interface goes down, so a vanished pppoe device leaves no
# name to derive the ifb from here. qosify clears that one itself: on the next
# up, interface_start() runs interface_clear_qdisc(), which deletes ifb-<dev>
# before cmd_add_ingress() creates it again.
collect_interface() {
local cfg="$1"
local name dev
section_enabled "$cfg" || return 0
config_get name "$cfg" name
[ -n "$name" ] || return 0
network_get_device dev "$name" && DEVS="$DEVS $dev"
}
# `config device` names a netdev directly.
collect_device() {
local cfg="$1"
local name
section_enabled "$cfg" || return 0
config_get name "$cfg" name
[ -n "$name" ] && DEVS="$DEVS $name"
}
DEVS=""
config_load qosify
config_foreach collect_interface interface
config_foreach collect_device device
if [ "$1" = wait ]; then
n=30
while pidof qosify >/dev/null; do
[ "$n" -gt 0 ] || exit 0
n=$((n - 1))
sleep 1
done
fi
for dev in $DEVS; do clear_dev "$dev"; done
# ifb-dns is qosify's fixed DNS ifb, not derived from any section, and a stop
# leaves it up under the kernel's default fq_codel. Deleting its root qdisc only
# re-attaches the default, so drop the device -- only once qosify has exited.
pidof qosify >/dev/null || ip link del ifb-dns 2>/dev/null
exit 0
|