175 lines · python
1# SPDX-License-Identifier: GPL-2.02# Copyright (c) 2020 SUSE LLC.3 4import collections5import functools6import json7import os8import socket9import subprocess10import unittest11 12 13# Add the source tree of bpftool and /usr/local/sbin to PATH14cur_dir = os.path.dirname(os.path.realpath(__file__))15bpftool_dir = os.path.abspath(os.path.join(cur_dir, "..", "..", "..", "..",16 "tools", "bpf", "bpftool"))17os.environ["PATH"] = bpftool_dir + ":/usr/local/sbin:" + os.environ["PATH"]18 19 20class IfaceNotFoundError(Exception):21 pass22 23 24class UnprivilegedUserError(Exception):25 pass26 27 28def _bpftool(args, json=True):29 _args = ["bpftool"]30 if json:31 _args.append("-j")32 _args.extend(args)33 34 return subprocess.check_output(_args)35 36 37def bpftool(args):38 return _bpftool(args, json=False).decode("utf-8")39 40 41def bpftool_json(args):42 res = _bpftool(args)43 return json.loads(res)44 45 46def get_default_iface():47 for iface in socket.if_nameindex():48 if iface[1] != "lo":49 return iface[1]50 raise IfaceNotFoundError("Could not find any network interface to probe")51 52 53def default_iface(f):54 @functools.wraps(f)55 def wrapper(*args, **kwargs):56 iface = get_default_iface()57 return f(*args, iface, **kwargs)58 return wrapper59 60DMESG_EMITTING_HELPERS = [61 "bpf_probe_write_user",62 "bpf_trace_printk",63 "bpf_trace_vprintk",64 ]65 66class TestBpftool(unittest.TestCase):67 @classmethod68 def setUpClass(cls):69 if os.getuid() != 0:70 raise UnprivilegedUserError(71 "This test suite needs root privileges")72 73 @default_iface74 def test_feature_dev_json(self, iface):75 unexpected_helpers = DMESG_EMITTING_HELPERS76 expected_keys = [77 "syscall_config",78 "program_types",79 "map_types",80 "helpers",81 "misc",82 ]83 84 res = bpftool_json(["feature", "probe", "dev", iface])85 # Check if the result has all expected keys.86 self.assertCountEqual(res.keys(), expected_keys)87 # Check if unexpected helpers are not included in helpers probes88 # result.89 for helpers in res["helpers"].values():90 for unexpected_helper in unexpected_helpers:91 self.assertNotIn(unexpected_helper, helpers)92 93 def test_feature_kernel(self):94 test_cases = [95 bpftool_json(["feature", "probe", "kernel"]),96 bpftool_json(["feature", "probe"]),97 bpftool_json(["feature"]),98 ]99 unexpected_helpers = DMESG_EMITTING_HELPERS100 expected_keys = [101 "syscall_config",102 "system_config",103 "program_types",104 "map_types",105 "helpers",106 "misc",107 ]108 109 for tc in test_cases:110 # Check if the result has all expected keys.111 self.assertCountEqual(tc.keys(), expected_keys)112 # Check if unexpected helpers are not included in helpers probes113 # result.114 for helpers in tc["helpers"].values():115 for unexpected_helper in unexpected_helpers:116 self.assertNotIn(unexpected_helper, helpers)117 118 def test_feature_kernel_full(self):119 test_cases = [120 bpftool_json(["feature", "probe", "kernel", "full"]),121 bpftool_json(["feature", "probe", "full"]),122 ]123 expected_helpers = DMESG_EMITTING_HELPERS124 125 for tc in test_cases:126 # Check if expected helpers are included at least once in any127 # helpers list for any program type. Unfortunately we cannot assume128 # that they will be included in all program types or a specific129 # subset of programs. It depends on the kernel version and130 # configuration.131 found_helpers = False132 133 for helpers in tc["helpers"].values():134 if all(expected_helper in helpers135 for expected_helper in expected_helpers):136 found_helpers = True137 break138 139 self.assertTrue(found_helpers)140 141 def test_feature_kernel_full_vs_not_full(self):142 full_res = bpftool_json(["feature", "probe", "full"])143 not_full_res = bpftool_json(["feature", "probe"])144 not_full_set = set()145 full_set = set()146 147 for helpers in full_res["helpers"].values():148 for helper in helpers:149 full_set.add(helper)150 151 for helpers in not_full_res["helpers"].values():152 for helper in helpers:153 not_full_set.add(helper)154 155 self.assertCountEqual(full_set - not_full_set,156 set(DMESG_EMITTING_HELPERS))157 self.assertCountEqual(not_full_set - full_set, set())158 159 def test_feature_macros(self):160 expected_patterns = [161 r"/\*\*\* System call availability \*\*\*/",162 r"#define HAVE_BPF_SYSCALL",163 r"/\*\*\* eBPF program types \*\*\*/",164 r"#define HAVE.*PROG_TYPE",165 r"/\*\*\* eBPF map types \*\*\*/",166 r"#define HAVE.*MAP_TYPE",167 r"/\*\*\* eBPF helper functions \*\*\*/",168 r"#define HAVE.*HELPER",169 r"/\*\*\* eBPF misc features \*\*\*/",170 ]171 172 res = bpftool(["feature", "probe", "macros"])173 for pattern in expected_patterns:174 self.assertRegex(res, pattern)175