Skip to content

Commit 6a63c20

Browse files
committed
netstacklat: Add script to fill in filter maps externally
Many of the options to filter for a subset of values (--pids, --interfaces, and --cgroups) rely on filling BPF maps with the values to include. When running together with the provided netstacklat user space process, the user space process handles filling these maps with the values passed on the command line. However, when using the netstacklat eBPF programs with some external loader, like the ebpf-exporter, these maps have to be filled by the user in some other manner. To make it easier to use netstacklat with external eBPF loaders, provide the fill_filter_maps.sh script. Rely on bpftool to fill the BPF maps (based on the map names as defined in the netstacklat.bpf.c file). in the script. Make the script support all the current filters that make use of maps, i.e. PIDs (pid), network interfaces (iface) and cgroups (cgroup). The pid option only supports integers. The iface option support either interface names or their ifindex. The cgroup option accepts either the cgroup ID (their inode number) or the full path to the cgroup. Examples: $ ./fill_filter_maps.sh pid 1234 98765 $ ./fill_filter_maps.sh iface veth0 lo 123 $ ./fill_filter_maps.sh cgroup /sys/fs/cgroup/system.slice/prometheus.service/ 12345 Note that for the values in the filter map to actually be used by the netstacklat eBPF programs, the corresponding filter_{pid,ifindex,cgroup} value must be true (by default they're all false). The netstacklat user space process normally takes care of enabling these as needed, but if used with an external loader the easiest way to enable these is probably to just change them in user_config at the start of netstacklat.bpf.c (and recompile). Also note that this script can also be used together with the netstacklat user space loader to add additional values to filter for after the starting the program. However, the netstacklat user space loader automatically minimizes the size of the filter maps since commit "netstacklat: Dynamically configure map sizes". So unless the initial filter values provided as netstacklat CLI arguments resulted in sufficiently large filter maps, the fill_filter_maps.sh script may not be able to successfully add the desired values. Finally, note that as bpftool expects you to feed it the individual bytes of the keys, the order of the bytes will be dependant on the endianess of the machine. Currently only support little-endian machines, big-endian support can be added later if needed. Signed-off-by: Simon Sundberg <simon.sundberg@kau.se>
1 parent 28b96fd commit 6a63c20

File tree

1 file changed

+131
-0
lines changed

1 file changed

+131
-0
lines changed

netstacklat/fill_filter_maps.sh

Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,131 @@
1+
#!/bin/bash
2+
# SPDX-License-Identifier: GPL-2.0-or-later
3+
4+
declare -rA bpf_maps=(
5+
[pid]="netstack_pidfil"
6+
[iface]="netstack_ifinde"
7+
[cgroup]="netstack_cgroup"
8+
)
9+
10+
declare -rA key_converters=(
11+
[pid]=pid_to_bpftool
12+
[iface]=iface_to_bpftool
13+
[cgroup]=cgroup_to_bpftool
14+
)
15+
16+
print_usage()
17+
{
18+
echo "usage: $0 TYPE val1 [val2 val3 val4...]"
19+
echo "TYPE: { $(echo "${!bpf_maps[@]}" | tr ' ' '\|') }"
20+
}
21+
22+
pid_to_bpftool()
23+
{
24+
local val="$1"
25+
26+
uint_to_bpftool_u32 "$val"
27+
}
28+
29+
# Supports ifname or ifindex
30+
iface_to_bpftool()
31+
{
32+
local val="$1"
33+
34+
if ! is_uint "$val"; then
35+
val="$(ifname_to_idx "$val")"
36+
fi
37+
38+
uint_to_bpftool_u32 "$val"
39+
}
40+
41+
# Supports full cgroup path or direct cgroup id (inode)
42+
cgroup_to_bpftool()
43+
{
44+
local val="$1"
45+
46+
if ! is_uint "$val"; then
47+
val="$(cgroup_path_to_id "$val")"
48+
fi
49+
50+
uint_to_bpftool_u64 "$val"
51+
}
52+
53+
is_uint()
54+
{
55+
local val="$1"
56+
57+
[[ "$val" == +([0-9]) ]]
58+
}
59+
60+
ifname_to_idx()
61+
{
62+
local ifname="$1"
63+
local ifindex=0
64+
65+
ifindex="$(ip address show "$ifname" | grep "[0-9]*: ${ifname}:")"
66+
ifindex="${ifindex%%:*}"
67+
68+
if [[ -z "$ifindex" ]]; then
69+
return 1
70+
fi
71+
72+
echo "$ifindex"
73+
}
74+
75+
cgroup_path_to_id()
76+
{
77+
local cpath="$1"
78+
79+
stat -L -c '%i' "$(realpath "$cpath")"
80+
}
81+
82+
# When providing keys/values to bpftool map update, it basically wants one
83+
# argument for each byte in the key/value. So if you have a u32 key (as in any
84+
# array map) and you want to update key 1234, then you will have to provide
85+
# key 0xd2 0x04 0x00 0x00 (1234 in hex split up as the 4 bytes in a u32 in
86+
# little-endian order). These helpers assume you're on a little endian machine.
87+
uint_to_bpftool_u32()
88+
{
89+
local val="$1"
90+
91+
printf "0x%02x 0x%02x 0x%02x 0x%02x\n" \
92+
$((val & 0xff)) $(((val >> 8) & 0xff)) $(((val >> 16) & 0xff)) $(((val >> 24) & 0xff))
93+
}
94+
95+
uint_to_bpftool_u64()
96+
{
97+
printf "0x%02x 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x\n" \
98+
$((val & 0xff)) $(((val >> 8) & 0xff)) $(((val >> 16) & 0xff)) $(((val >> 24) & 0xff)) \
99+
$(((val >> 32) & 0xff)) $(((val >> 40) & 0xff)) $(((val >> 48) & 0xff)) $(((val >> 56) & 0xff))
100+
}
101+
102+
# All the filter maps use a u8 as the value, so just set that single byte to 1
103+
add_to_filter_map()
104+
{
105+
local map="$1"
106+
local key="$2"
107+
108+
bpftool map update name "$map" key $key value 1
109+
}
110+
111+
if (( $# < 2 )); then
112+
print_usage
113+
exit 1
114+
fi
115+
116+
type=$1
117+
if [[ -z "${bpf_maps[$type]}" ]]; then
118+
echo "Error: unrecognized type $type, must be one of: ${!bpf_maps[*]}"
119+
exit 1
120+
fi
121+
122+
map=${bpf_maps[$type]}
123+
converter=${key_converters[$type]}
124+
125+
for val in "${@:2}"; do
126+
key=$($converter "$val")
127+
if ! add_to_filter_map "$map" "$key"; then
128+
echo "Error adding $val ($key) to map $map"
129+
exit 1
130+
fi
131+
done

0 commit comments

Comments
 (0)