xref: /OK3568_Linux_fs/kernel/tools/testing/selftests/bpf/test_bpftool.py (revision 4882a59341e53eb6f0b4789bf948001014eff981)
1*4882a593Smuzhiyun# SPDX-License-Identifier: GPL-2.0
2*4882a593Smuzhiyun# Copyright (c) 2020 SUSE LLC.
3*4882a593Smuzhiyun
4*4882a593Smuzhiyunimport collections
5*4882a593Smuzhiyunimport functools
6*4882a593Smuzhiyunimport json
7*4882a593Smuzhiyunimport os
8*4882a593Smuzhiyunimport socket
9*4882a593Smuzhiyunimport subprocess
10*4882a593Smuzhiyunimport unittest
11*4882a593Smuzhiyun
12*4882a593Smuzhiyun
13*4882a593Smuzhiyun# Add the source tree of bpftool and /usr/local/sbin to PATH
14*4882a593Smuzhiyuncur_dir = os.path.dirname(os.path.realpath(__file__))
15*4882a593Smuzhiyunbpftool_dir = os.path.abspath(os.path.join(cur_dir, "..", "..", "..", "..",
16*4882a593Smuzhiyun                                           "tools", "bpf", "bpftool"))
17*4882a593Smuzhiyunos.environ["PATH"] = bpftool_dir + ":/usr/local/sbin:" + os.environ["PATH"]
18*4882a593Smuzhiyun
19*4882a593Smuzhiyun
20*4882a593Smuzhiyunclass IfaceNotFoundError(Exception):
21*4882a593Smuzhiyun    pass
22*4882a593Smuzhiyun
23*4882a593Smuzhiyun
24*4882a593Smuzhiyunclass UnprivilegedUserError(Exception):
25*4882a593Smuzhiyun    pass
26*4882a593Smuzhiyun
27*4882a593Smuzhiyun
28*4882a593Smuzhiyundef _bpftool(args, json=True):
29*4882a593Smuzhiyun    _args = ["bpftool"]
30*4882a593Smuzhiyun    if json:
31*4882a593Smuzhiyun        _args.append("-j")
32*4882a593Smuzhiyun    _args.extend(args)
33*4882a593Smuzhiyun
34*4882a593Smuzhiyun    return subprocess.check_output(_args)
35*4882a593Smuzhiyun
36*4882a593Smuzhiyun
37*4882a593Smuzhiyundef bpftool(args):
38*4882a593Smuzhiyun    return _bpftool(args, json=False).decode("utf-8")
39*4882a593Smuzhiyun
40*4882a593Smuzhiyun
41*4882a593Smuzhiyundef bpftool_json(args):
42*4882a593Smuzhiyun    res = _bpftool(args)
43*4882a593Smuzhiyun    return json.loads(res)
44*4882a593Smuzhiyun
45*4882a593Smuzhiyun
46*4882a593Smuzhiyundef get_default_iface():
47*4882a593Smuzhiyun    for iface in socket.if_nameindex():
48*4882a593Smuzhiyun        if iface[1] != "lo":
49*4882a593Smuzhiyun            return iface[1]
50*4882a593Smuzhiyun    raise IfaceNotFoundError("Could not find any network interface to probe")
51*4882a593Smuzhiyun
52*4882a593Smuzhiyun
53*4882a593Smuzhiyundef default_iface(f):
54*4882a593Smuzhiyun    @functools.wraps(f)
55*4882a593Smuzhiyun    def wrapper(*args, **kwargs):
56*4882a593Smuzhiyun        iface = get_default_iface()
57*4882a593Smuzhiyun        return f(*args, iface, **kwargs)
58*4882a593Smuzhiyun    return wrapper
59*4882a593Smuzhiyun
60*4882a593Smuzhiyun
61*4882a593Smuzhiyunclass TestBpftool(unittest.TestCase):
62*4882a593Smuzhiyun    @classmethod
63*4882a593Smuzhiyun    def setUpClass(cls):
64*4882a593Smuzhiyun        if os.getuid() != 0:
65*4882a593Smuzhiyun            raise UnprivilegedUserError(
66*4882a593Smuzhiyun                "This test suite needs root privileges")
67*4882a593Smuzhiyun
68*4882a593Smuzhiyun    @default_iface
69*4882a593Smuzhiyun    def test_feature_dev_json(self, iface):
70*4882a593Smuzhiyun        unexpected_helpers = [
71*4882a593Smuzhiyun            "bpf_probe_write_user",
72*4882a593Smuzhiyun            "bpf_trace_printk",
73*4882a593Smuzhiyun        ]
74*4882a593Smuzhiyun        expected_keys = [
75*4882a593Smuzhiyun            "syscall_config",
76*4882a593Smuzhiyun            "program_types",
77*4882a593Smuzhiyun            "map_types",
78*4882a593Smuzhiyun            "helpers",
79*4882a593Smuzhiyun            "misc",
80*4882a593Smuzhiyun        ]
81*4882a593Smuzhiyun
82*4882a593Smuzhiyun        res = bpftool_json(["feature", "probe", "dev", iface])
83*4882a593Smuzhiyun        # Check if the result has all expected keys.
84*4882a593Smuzhiyun        self.assertCountEqual(res.keys(), expected_keys)
85*4882a593Smuzhiyun        # Check if unexpected helpers are not included in helpers probes
86*4882a593Smuzhiyun        # result.
87*4882a593Smuzhiyun        for helpers in res["helpers"].values():
88*4882a593Smuzhiyun            for unexpected_helper in unexpected_helpers:
89*4882a593Smuzhiyun                self.assertNotIn(unexpected_helper, helpers)
90*4882a593Smuzhiyun
91*4882a593Smuzhiyun    def test_feature_kernel(self):
92*4882a593Smuzhiyun        test_cases = [
93*4882a593Smuzhiyun            bpftool_json(["feature", "probe", "kernel"]),
94*4882a593Smuzhiyun            bpftool_json(["feature", "probe"]),
95*4882a593Smuzhiyun            bpftool_json(["feature"]),
96*4882a593Smuzhiyun        ]
97*4882a593Smuzhiyun        unexpected_helpers = [
98*4882a593Smuzhiyun            "bpf_probe_write_user",
99*4882a593Smuzhiyun            "bpf_trace_printk",
100*4882a593Smuzhiyun        ]
101*4882a593Smuzhiyun        expected_keys = [
102*4882a593Smuzhiyun            "syscall_config",
103*4882a593Smuzhiyun            "system_config",
104*4882a593Smuzhiyun            "program_types",
105*4882a593Smuzhiyun            "map_types",
106*4882a593Smuzhiyun            "helpers",
107*4882a593Smuzhiyun            "misc",
108*4882a593Smuzhiyun        ]
109*4882a593Smuzhiyun
110*4882a593Smuzhiyun        for tc in test_cases:
111*4882a593Smuzhiyun            # Check if the result has all expected keys.
112*4882a593Smuzhiyun            self.assertCountEqual(tc.keys(), expected_keys)
113*4882a593Smuzhiyun            # Check if unexpected helpers are not included in helpers probes
114*4882a593Smuzhiyun            # result.
115*4882a593Smuzhiyun            for helpers in tc["helpers"].values():
116*4882a593Smuzhiyun                for unexpected_helper in unexpected_helpers:
117*4882a593Smuzhiyun                    self.assertNotIn(unexpected_helper, helpers)
118*4882a593Smuzhiyun
119*4882a593Smuzhiyun    def test_feature_kernel_full(self):
120*4882a593Smuzhiyun        test_cases = [
121*4882a593Smuzhiyun            bpftool_json(["feature", "probe", "kernel", "full"]),
122*4882a593Smuzhiyun            bpftool_json(["feature", "probe", "full"]),
123*4882a593Smuzhiyun        ]
124*4882a593Smuzhiyun        expected_helpers = [
125*4882a593Smuzhiyun            "bpf_probe_write_user",
126*4882a593Smuzhiyun            "bpf_trace_printk",
127*4882a593Smuzhiyun        ]
128*4882a593Smuzhiyun
129*4882a593Smuzhiyun        for tc in test_cases:
130*4882a593Smuzhiyun            # Check if expected helpers are included at least once in any
131*4882a593Smuzhiyun            # helpers list for any program type. Unfortunately we cannot assume
132*4882a593Smuzhiyun            # that they will be included in all program types or a specific
133*4882a593Smuzhiyun            # subset of programs. It depends on the kernel version and
134*4882a593Smuzhiyun            # configuration.
135*4882a593Smuzhiyun            found_helpers = False
136*4882a593Smuzhiyun
137*4882a593Smuzhiyun            for helpers in tc["helpers"].values():
138*4882a593Smuzhiyun                if all(expected_helper in helpers
139*4882a593Smuzhiyun                       for expected_helper in expected_helpers):
140*4882a593Smuzhiyun                    found_helpers = True
141*4882a593Smuzhiyun                    break
142*4882a593Smuzhiyun
143*4882a593Smuzhiyun            self.assertTrue(found_helpers)
144*4882a593Smuzhiyun
145*4882a593Smuzhiyun    def test_feature_kernel_full_vs_not_full(self):
146*4882a593Smuzhiyun        full_res = bpftool_json(["feature", "probe", "full"])
147*4882a593Smuzhiyun        not_full_res = bpftool_json(["feature", "probe"])
148*4882a593Smuzhiyun        not_full_set = set()
149*4882a593Smuzhiyun        full_set = set()
150*4882a593Smuzhiyun
151*4882a593Smuzhiyun        for helpers in full_res["helpers"].values():
152*4882a593Smuzhiyun            for helper in helpers:
153*4882a593Smuzhiyun                full_set.add(helper)
154*4882a593Smuzhiyun
155*4882a593Smuzhiyun        for helpers in not_full_res["helpers"].values():
156*4882a593Smuzhiyun            for helper in helpers:
157*4882a593Smuzhiyun                not_full_set.add(helper)
158*4882a593Smuzhiyun
159*4882a593Smuzhiyun        self.assertCountEqual(full_set - not_full_set,
160*4882a593Smuzhiyun                                {"bpf_probe_write_user", "bpf_trace_printk"})
161*4882a593Smuzhiyun        self.assertCountEqual(not_full_set - full_set, set())
162*4882a593Smuzhiyun
163*4882a593Smuzhiyun    def test_feature_macros(self):
164*4882a593Smuzhiyun        expected_patterns = [
165*4882a593Smuzhiyun            r"/\*\*\* System call availability \*\*\*/",
166*4882a593Smuzhiyun            r"#define HAVE_BPF_SYSCALL",
167*4882a593Smuzhiyun            r"/\*\*\* eBPF program types \*\*\*/",
168*4882a593Smuzhiyun            r"#define HAVE.*PROG_TYPE",
169*4882a593Smuzhiyun            r"/\*\*\* eBPF map types \*\*\*/",
170*4882a593Smuzhiyun            r"#define HAVE.*MAP_TYPE",
171*4882a593Smuzhiyun            r"/\*\*\* eBPF helper functions \*\*\*/",
172*4882a593Smuzhiyun            r"#define HAVE.*HELPER",
173*4882a593Smuzhiyun            r"/\*\*\* eBPF misc features \*\*\*/",
174*4882a593Smuzhiyun        ]
175*4882a593Smuzhiyun
176*4882a593Smuzhiyun        res = bpftool(["feature", "probe", "macros"])
177*4882a593Smuzhiyun        for pattern in expected_patterns:
178*4882a593Smuzhiyun            self.assertRegex(res, pattern)
179