3 # Copyright (C) 2017 Netronome Systems, Inc.
4 # Copyright (c) 2019 Mellanox Technologies. All rights reserved
6 # This software is licensed under the GNU General License Version 2,
7 # June 1991 as shown in the file COPYING in the top-level directory of this
10 # THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS"
11 # WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING,
12 # BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
13 # FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE
14 # OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME
15 # THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
17 from datetime import datetime
32 from lib.py import NetdevSim, NetdevSimDev
38 bpf_test_dir = os.path.dirname(os.path.realpath(__file__))
39 pp = pprint.PrettyPrinter()
40 devs = [] # devices we created for clean up
41 files = [] # files to be removed
42 netns = [] # net namespaces to be removed
44 def log_get_sec(level=0):
45 return "*" * (log_level + level)
47 def log_level_inc(add=1):
51 def log_level_dec(sub=1):
55 def log_level_set(level):
59 def log(header, data, level=None):
61 Output to an optional log.
68 if not isinstance(data, str):
69 data = pp.pformat(data)
72 logfile.write("\n" + log_get_sec() + " ")
74 if len(header) and len(data.strip()):
82 log("SKIP: " + msg, "", level=1)
89 tb = "".join(traceback.extract_stack().format())
91 log("FAIL: " + msg, tb, level=1)
99 def cmd(cmd, shell=True, include_stderr=False, background=False, fail=True):
101 Run a command in subprocess and return tuple of (retval, stdout);
102 optionally return stderr as well as third value.
104 proc = subprocess.Popen(cmd, shell=shell, stdout=subprocess.PIPE,
105 stderr=subprocess.PIPE)
107 msg = "%s START: %s" % (log_get_sec(1),
108 datetime.now().strftime("%H:%M:%S.%f"))
109 log("BKG " + proc.args, msg)
112 return cmd_result(proc, include_stderr=include_stderr, fail=fail)
114 def cmd_result(proc, include_stderr=False, fail=False):
115 stdout, stderr = proc.communicate()
116 stdout = stdout.decode("utf-8")
117 stderr = stderr.decode("utf-8")
121 stderr = "\n" + stderr
122 if stderr[-1] == "\n":
126 log("CMD " + proc.args,
127 "RETCODE: %d\n%s STDOUT:\n%s%s STDERR:%s\n%s END: %s" %
128 (proc.returncode, sec, stdout, sec, stderr,
129 sec, datetime.now().strftime("%H:%M:%S.%f")))
131 if proc.returncode != 0 and fail:
132 if len(stderr) > 0 and stderr[-1] == "\n":
134 raise Exception("Command failed: %s\n%s" % (proc.args, stderr))
137 return proc.returncode, stdout, stderr
139 return proc.returncode, stdout
142 cmd("rm -f %s" % (f))
146 def tool(name, args, flags, JSON=True, ns="", fail=True, include_stderr=False):
149 params += "%s " % (flags["json"])
152 ns = "ip netns exec %s " % (ns)
157 ret, stdout, stderr = cmd(ns + name + " " + params + args,
158 fail=fail, include_stderr=True)
160 ret, stdout = cmd(ns + name + " " + params + args,
161 fail=fail, include_stderr=False)
163 if JSON and len(stdout.strip()) != 0:
164 out = json.loads(stdout)
169 return ret, out, stderr
173 def bpftool(args, JSON=True, ns="", fail=True, include_stderr=False):
174 return tool("bpftool", args, {"json":"-p"}, JSON=JSON, ns=ns,
175 fail=fail, include_stderr=include_stderr)
177 def bpftool_prog_list(expected=None, ns="", exclude_orphaned=True):
178 _, progs = bpftool("prog show", JSON=True, ns=ns, fail=True)
179 # Remove the base progs
184 progs = [ p for p in progs if not p['orphaned'] ]
185 if expected is not None:
186 if len(progs) != expected:
187 fail(True, "%d BPF programs loaded, expected %d" %
188 (len(progs), expected))
191 def bpftool_map_list(expected=None, ns=""):
192 _, maps = bpftool("map show", JSON=True, ns=ns, fail=True)
193 # Remove the base maps
194 maps = [m for m in maps if m not in base_maps and m.get('name') and m.get('name') not in base_map_names]
195 if expected is not None:
196 if len(maps) != expected:
197 fail(True, "%d BPF maps loaded, expected %d" %
198 (len(maps), expected))
201 def bpftool_prog_list_wait(expected=0, n_retry=20):
202 for i in range(n_retry):
203 nprogs = len(bpftool_prog_list())
204 if nprogs == expected:
207 raise Exception("Time out waiting for program counts to stabilize want %d, have %d" % (expected, nprogs))
209 def bpftool_map_list_wait(expected=0, n_retry=20, ns=""):
210 for i in range(n_retry):
211 maps = bpftool_map_list(ns=ns)
212 if len(maps) == expected:
215 raise Exception("Time out waiting for map counts to stabilize want %d, have %d" % (expected, nmaps))
217 def bpftool_prog_load(sample, file_name, maps=[], prog_type="xdp", dev=None,
218 fail=True, include_stderr=False, dev_bind=None):
219 args = "prog load %s %s" % (os.path.join(bpf_test_dir, sample), file_name)
220 if prog_type is not None:
221 args += " type " + prog_type
223 args += " dev " + dev
224 elif dev_bind is not None:
225 args += " xdpmeta_dev " + dev_bind
227 args += " map " + " map ".join(maps)
229 res = bpftool(args, fail=fail, include_stderr=include_stderr)
231 files.append(file_name)
234 def ip(args, force=False, JSON=True, ns="", fail=True, include_stderr=False):
236 args = "-force " + args
237 return tool("ip", args, {"json":"-j"}, JSON=JSON, ns=ns,
238 fail=fail, include_stderr=include_stderr)
240 def tc(args, JSON=True, ns="", fail=True, include_stderr=False):
241 return tool("tc", args, {"json":"-p"}, JSON=JSON, ns=ns,
242 fail=fail, include_stderr=include_stderr)
244 def ethtool(dev, opt, args, fail=True):
245 return cmd("ethtool %s %s %s" % (opt, dev["ifname"], args), fail=fail)
247 def bpf_obj(name, sec="xdp", path=bpf_test_dir,):
248 return "obj %s sec %s" % (os.path.join(path, name), sec)
250 def bpf_pinned(name):
251 return "pinned %s" % (name)
253 def bpf_bytecode(bytecode):
254 return "bytecode \"%s\"" % (bytecode)
256 def mknetns(n_retry=10):
257 for i in range(n_retry):
258 name = ''.join([random.choice(string.ascii_letters) for i in range(8)])
259 ret, _ = ip("netns add %s" % (name), fail=False)
265 def int2str(fmt, val):
267 for b in struct.pack(fmt, val):
269 return " ".join(map(lambda x: str(x), ret))
274 inttab.append(int(i, 16))
275 ba = bytearray(inttab)
278 elif len(strtab) == 8:
281 raise Exception("String array of len %d can't be unpacked to an int" %
283 return struct.unpack(fmt, ba)[0]
287 Class for accessing DebugFS directories as a dictionary.
290 def __init__(self, path):
292 self._dict = self._debugfs_dir_read(path)
295 return len(self._dict.keys())
297 def __getitem__(self, key):
299 key = list(self._dict.keys())[key]
300 return self._dict[key]
302 def __setitem__(self, key, value):
303 log("DebugFS set %s = %s" % (key, value), "")
306 cmd("echo '%s' > %s/%s" % (value, self.path, key))
309 _, out = cmd('cat %s/%s' % (self.path, key))
310 self._dict[key] = out.strip()
312 def _debugfs_dir_read(self, path):
315 log("DebugFS state for %s" % (path), "")
318 _, out = cmd('ls ' + path)
319 for f in out.split():
323 p = os.path.join(path, f)
324 if not os.stat(p).st_mode & stat.S_IRUSR:
327 if os.path.isfile(p):
328 # We need to init trap_flow_action_cookie before read it
329 if f == "trap_flow_action_cookie":
330 cmd('echo deadbeef > %s/%s' % (path, f))
331 _, out = cmd('cat %s/%s' % (path, f))
333 elif os.path.isdir(p):
334 dfs[f] = DebugfsDir(p)
336 raise Exception("%s is neither file nor directory" % (p))
339 log("DebugFS state", dfs)
344 class BpfNetdevSimDev(NetdevSimDev):
346 Class for netdevsim bus device and its attributes.
348 def __init__(self, port_count=1, ns=None):
349 super().__init__(port_count, ns=ns)
352 def _make_port(self, port_index, ifname):
353 return BpfNetdevSim(self, port_index, ifname, self.ns)
355 def dfs_num_bound_progs(self):
356 path = os.path.join(self.dfs_dir, "bpf_bound_progs")
357 _, progs = cmd('ls %s' % (path))
358 return len(progs.split())
360 def dfs_get_bound_progs(self, expected):
361 progs = DebugfsDir(os.path.join(self.dfs_dir, "bpf_bound_progs"))
362 if expected is not None:
363 if len(progs) != expected:
364 fail(True, "%d BPF programs bound, expected %d" %
365 (len(progs), expected))
373 class BpfNetdevSim(NetdevSim):
375 Class for netdevsim netdevice and its attributes.
378 def __init__(self, nsimdev, port_index, ifname, ns=None):
379 super().__init__(nsimdev, port_index, ifname, ns=ns)
381 self.dfs_dir = "%s/ports/%u/" % (nsimdev.dfs_dir, port_index)
384 def __getitem__(self, key):
388 self.nsimdev.remove_nsim(self)
390 def dfs_refresh(self):
391 self.dfs = DebugfsDir(self.dfs_dir)
394 def dfs_read(self, f):
395 path = os.path.join(self.dfs_dir, f)
396 _, data = cmd('cat %s' % (path))
399 def wait_for_flush(self, bound=0, total=0, n_retry=20):
400 for i in range(n_retry):
401 nbound = self.nsimdev.dfs_num_bound_progs()
402 nprogs = len(bpftool_prog_list())
403 if nbound == bound and nprogs == total:
406 raise Exception("Time out waiting for program counts to stabilize want %d/%d, have %d bound, %d loaded" % (bound, total, nbound, nprogs))
408 def set_ns(self, ns):
409 name = ns if ns else "1"
410 ip("link set dev %s netns %s" % (self.dev["ifname"], name), ns=self.ns)
413 def set_mtu(self, mtu, fail=True):
414 return ip("link set dev %s mtu %d" % (self.dev["ifname"], mtu),
417 def set_xdp(self, bpf, mode, force=False, JSON=True, verbose=False,
418 fail=True, include_stderr=False):
421 return ip("link set dev %s xdp%s %s" % (self.dev["ifname"], mode, bpf),
422 force=force, JSON=JSON,
423 fail=fail, include_stderr=include_stderr)
425 def unset_xdp(self, mode, force=False, JSON=True,
426 fail=True, include_stderr=False):
427 return ip("link set dev %s xdp%s off" % (self.dev["ifname"], mode),
428 force=force, JSON=JSON,
429 fail=fail, include_stderr=include_stderr)
431 def ip_link_show(self, xdp):
432 _, link = ip("link show dev %s" % (self['ifname']))
434 raise Exception("Multiple objects on ip link show")
437 fail(xdp != "xdp" in link,
438 "XDP program not reporting in iplink (reported %s, expected %s)" %
439 ("xdp" in link, xdp))
442 def tc_add_ingress(self):
443 tc("qdisc add dev %s ingress" % (self['ifname']))
445 def tc_del_ingress(self):
446 tc("qdisc del dev %s ingress" % (self['ifname']))
448 def tc_flush_filters(self, bound=0, total=0):
449 self.tc_del_ingress()
450 self.tc_add_ingress()
451 self.wait_for_flush(bound=bound, total=total)
453 def tc_show_ingress(self, expected=None):
454 # No JSON support, oh well...
455 flags = ["skip_sw", "skip_hw", "in_hw"]
456 named = ["protocol", "pref", "chain", "handle", "id", "tag"]
458 args = "-s filter show dev %s ingress" % (self['ifname'])
459 _, out = tc(args, JSON=False)
462 lines = out.split('\n')
465 if "handle" not in words:
469 fltr[flag] = flag in words
472 idx = words.index(name)
473 fltr[name] = words[idx + 1]
478 if expected is not None:
479 fail(len(filters) != expected,
480 "%d ingress filters loaded, expected %d" %
481 (len(filters), expected))
484 def cls_filter_op(self, op, qdisc="ingress", prio=None, handle=None,
485 chain=None, cls="", params="",
486 fail=True, include_stderr=False):
489 spec += " prio %d" % (prio)
491 spec += " handle %s" % (handle)
492 if chain is not None:
493 spec += " chain %d" % (chain)
495 return tc("filter {op} dev {dev} {qdisc} {spec} {cls} {params}"\
496 .format(op=op, dev=self['ifname'], qdisc=qdisc, spec=spec,
497 cls=cls, params=params),
498 fail=fail, include_stderr=include_stderr)
500 def cls_bpf_add_filter(self, bpf, op="add", prio=None, handle=None,
501 chain=None, da=False, verbose=False,
502 skip_sw=False, skip_hw=False,
503 fail=True, include_stderr=False):
516 return self.cls_filter_op(op=op, prio=prio, handle=handle, cls=cls,
517 chain=chain, params=params,
518 fail=fail, include_stderr=include_stderr)
520 def set_ethtool_tc_offloads(self, enable, fail=True):
521 args = "hw-tc-offload %s" % ("on" if enable else "off")
522 return ethtool(self, "-K", args, fail=fail)
524 ################################################################################
526 global files, netns, devs
531 cmd("rm -f %s" % (f))
533 cmd("ip netns delete %s" % (ns))
537 def pin_prog(file_name, idx=0):
538 progs = bpftool_prog_list(expected=(idx + 1))
540 bpftool("prog pin id %d %s" % (prog["id"], file_name))
541 files.append(file_name)
543 return file_name, bpf_pinned(file_name)
545 def pin_map(file_name, idx=0, expected=1):
546 maps = bpftool_map_list_wait(expected=expected)
548 bpftool("map pin id %d %s" % (m["id"], file_name))
549 files.append(file_name)
551 return file_name, bpf_pinned(file_name)
553 def check_dev_info_removed(prog_file=None, map_file=None):
554 bpftool_prog_list(expected=0)
555 bpftool_prog_list(expected=1, exclude_orphaned=False)
556 ret, err = bpftool("prog show pin %s" % (prog_file), fail=False)
557 fail(ret != 0, "failed to show prog with removed device")
559 bpftool_map_list_wait(expected=0)
560 ret, err = bpftool("map show pin %s" % (map_file), fail=False)
561 fail(ret == 0, "Showing map with removed device did not fail")
562 fail(err["error"].find("No such device") == -1,
563 "Showing map with removed device expected ENODEV, error is %s" %
566 def check_dev_info(other_ns, ns, prog_file=None, map_file=None, removed=False):
567 progs = bpftool_prog_list(expected=1, ns=ns)
570 fail("dev" not in prog.keys(), "Device parameters not reported")
572 fail("ifindex" not in dev.keys(), "Device parameters not reported")
573 fail("ns_dev" not in dev.keys(), "Device parameters not reported")
574 fail("ns_inode" not in dev.keys(), "Device parameters not reported")
577 fail("ifname" not in dev.keys(), "Ifname not reported")
578 fail(dev["ifname"] != sim["ifname"],
579 "Ifname incorrect %s vs %s" % (dev["ifname"], sim["ifname"]))
581 fail("ifname" in dev.keys(), "Ifname is reported for other ns")
583 maps = bpftool_map_list_wait(expected=2, ns=ns)
585 fail("dev" not in m.keys(), "Device parameters not reported")
586 fail(dev != m["dev"], "Map's device different than program's")
588 def check_extack(output, reference, args):
591 lines = output.split("\n")
592 comp = len(lines) >= 2 and lines[1] == 'Error: ' + reference
593 fail(not comp, "Missing or incorrect netlink extack message")
595 def check_extack_nsim(output, reference, args):
596 check_extack(output, "netdevsim: " + reference, args)
598 def check_no_extack(res, needle):
599 haystack = (res[1] + res[2]).strip()
600 fail(haystack.count(needle) or haystack.count("Warning:"),
601 "Unexpected command output, leaky extack? ('%s', '%s')" % (needle, haystack))
603 def check_verifier_log(output, reference):
604 lines = output.split("\n")
605 for l in reversed(lines):
608 fail(True, "Missing or incorrect message from netdevsim in verifier log")
610 def check_multi_basic(two_xdps):
611 fail(two_xdps["mode"] != 4, "Bad mode reported with multiple programs")
612 fail("prog" in two_xdps, "Base program reported in multi program mode")
613 fail(len(two_xdps["attached"]) != 2,
614 "Wrong attached program count with two programs")
615 fail(two_xdps["attached"][0]["prog"]["id"] ==
616 two_xdps["attached"][1]["prog"]["id"],
617 "Offloaded and other programs have the same id")
619 def test_spurios_extack(sim, obj, skip_hw, needle):
620 res = sim.cls_bpf_add_filter(obj, prio=1, handle=1, skip_hw=skip_hw,
622 check_no_extack(res, needle)
623 res = sim.cls_bpf_add_filter(obj, op="replace", prio=1, handle=1,
624 skip_hw=skip_hw, include_stderr=True)
625 check_no_extack(res, needle)
626 res = sim.cls_filter_op(op="delete", prio=1, handle=1, cls="bpf",
628 check_no_extack(res, needle)
630 def test_multi_prog(simdev, sim, obj, modename, modeid):
631 start_test("Test multi-attachment XDP - %s + offload..." %
632 (modename or "default", ))
633 sim.set_xdp(obj, "offload")
634 xdp = sim.ip_link_show(xdp=True)["xdp"]
635 offloaded = sim.dfs_read("bpf_offloaded_id")
636 fail("prog" not in xdp, "Base program not reported in single program mode")
637 fail(len(xdp["attached"]) != 1,
638 "Wrong attached program count with one program")
640 sim.set_xdp(obj, modename)
641 two_xdps = sim.ip_link_show(xdp=True)["xdp"]
643 fail(xdp["attached"][0] not in two_xdps["attached"],
644 "Offload program not reported after other activated")
645 check_multi_basic(two_xdps)
647 offloaded2 = sim.dfs_read("bpf_offloaded_id")
648 fail(offloaded != offloaded2,
649 "Offload ID changed after loading other program")
651 start_test("Test multi-attachment XDP - replace...")
652 ret, _, err = sim.set_xdp(obj, "offload", fail=False, include_stderr=True)
653 fail(ret == 0, "Replaced one of programs without -force")
654 check_extack(err, "XDP program already attached.", args)
656 start_test("Test multi-attachment XDP - remove without mode...")
657 ret, _, err = sim.unset_xdp("", force=True,
658 fail=False, include_stderr=True)
659 fail(ret == 0, "Removed program without a mode flag")
660 check_extack(err, "More than one program loaded, unset mode is ambiguous.", args)
662 sim.unset_xdp("offload")
663 xdp = sim.ip_link_show(xdp=True)["xdp"]
664 offloaded = sim.dfs_read("bpf_offloaded_id")
666 fail(xdp["mode"] != modeid, "Bad mode reported after multiple programs")
667 fail("prog" not in xdp,
668 "Base program not reported after multi program mode")
669 fail(xdp["attached"][0] not in two_xdps["attached"],
670 "Offload program not reported after other activated")
671 fail(len(xdp["attached"]) != 1,
672 "Wrong attached program count with remaining programs")
673 fail(offloaded != "0", "Offload ID reported with only other program left")
675 start_test("Test multi-attachment XDP - reattach...")
676 sim.set_xdp(obj, "offload")
677 two_xdps = sim.ip_link_show(xdp=True)["xdp"]
679 fail(xdp["attached"][0] not in two_xdps["attached"],
680 "Other program not reported after offload activated")
681 check_multi_basic(two_xdps)
683 start_test("Test multi-attachment XDP - device remove...")
686 simdev = BpfNetdevSimDev()
688 sim.set_ethtool_tc_offloads(True)
692 parser = argparse.ArgumentParser()
693 parser.add_argument("--log", help="output verbose log to given file")
694 args = parser.parse_args()
696 logfile = open(args.log, 'w+')
697 logfile.write("# -*-Org-*-")
699 log("Prepare...", "", level=1)
703 skip(os.getuid() != 0, "test must be run as root")
706 ret, progs = bpftool("prog", fail=False)
707 skip(ret != 0, "bpftool not installed")
709 _, base_maps = bpftool("map")
711 'pid_iter.rodata', # created on each bpftool invocation
712 'libbpf_det_bind', # created on each bpftool invocation
716 if not os.path.isdir("/sys/bus/netdevsim/"):
717 ret, out = cmd("modprobe netdevsim", fail=False)
718 skip(ret != 0, "netdevsim module could not be loaded")
721 _, out = cmd("mount")
722 if out.find("/sys/kernel/debug type debugfs") == -1:
723 cmd("mount -t debugfs none /sys/kernel/debug")
725 # Check samples are compiled
726 samples = ["sample_ret0.bpf.o", "sample_map_ret0.bpf.o"]
728 ret, out = cmd("ls %s/%s" % (bpf_test_dir, s), fail=False)
729 skip(ret != 0, "sample %s/%s not found, please compile it" %
732 # Check if iproute2 is built with libmnl (needed by extack support)
733 _, _, err = cmd("tc qdisc delete dev lo handle 0",
734 fail=False, include_stderr=True)
735 if err.find("Error: Failed to find qdisc with specified handle.") == -1:
736 print("Warning: no extack message in iproute2 output, libmnl missing?")
737 log("Warning: no extack message in iproute2 output, libmnl missing?", "")
740 # Check if net namespaces seem to work
742 skip(ns is None, "Could not create a net namespace")
743 cmd("ip netns delete %s" % (ns))
747 obj = bpf_obj("sample_ret0.bpf.o")
748 bytecode = bpf_bytecode("1,6 0 0 4294967295,")
750 start_test("Test destruction of generic XDP...")
751 simdev = BpfNetdevSimDev()
753 sim.set_xdp(obj, "generic")
755 bpftool_prog_list_wait(expected=0)
757 simdev = BpfNetdevSimDev()
761 start_test("Test TC non-offloaded...")
762 ret, _ = sim.cls_bpf_add_filter(obj, skip_hw=True, fail=False)
763 fail(ret != 0, "Software TC filter did not load")
765 start_test("Test TC non-offloaded isn't getting bound...")
766 ret, _ = sim.cls_bpf_add_filter(obj, fail=False)
767 fail(ret != 0, "Software TC filter did not load")
768 simdev.dfs_get_bound_progs(expected=0)
770 sim.tc_flush_filters()
772 start_test("Test TC offloads are off by default...")
773 ret, _, err = sim.cls_bpf_add_filter(obj, skip_sw=True,
774 fail=False, include_stderr=True)
775 fail(ret == 0, "TC filter loaded without enabling TC offloads")
776 check_extack(err, "TC offload is disabled on net device.", args)
779 sim.set_ethtool_tc_offloads(True)
780 sim.dfs["bpf_tc_non_bound_accept"] = "Y"
782 start_test("Test TC offload by default...")
783 ret, _ = sim.cls_bpf_add_filter(obj, fail=False)
784 fail(ret != 0, "Software TC filter did not load")
785 simdev.dfs_get_bound_progs(expected=0)
786 ingress = sim.tc_show_ingress(expected=1)
788 fail(not fltr["in_hw"], "Filter not offloaded by default")
790 sim.tc_flush_filters()
792 start_test("Test TC cBPF bytcode tries offload by default...")
793 ret, _ = sim.cls_bpf_add_filter(bytecode, fail=False)
794 fail(ret != 0, "Software TC filter did not load")
795 simdev.dfs_get_bound_progs(expected=0)
796 ingress = sim.tc_show_ingress(expected=1)
798 fail(not fltr["in_hw"], "Bytecode not offloaded by default")
800 sim.tc_flush_filters()
801 sim.dfs["bpf_tc_non_bound_accept"] = "N"
803 start_test("Test TC cBPF unbound bytecode doesn't offload...")
804 ret, _, err = sim.cls_bpf_add_filter(bytecode, skip_sw=True,
805 fail=False, include_stderr=True)
806 fail(ret == 0, "TC bytecode loaded for offload")
807 check_extack_nsim(err, "netdevsim configured to reject unbound programs.",
811 start_test("Test non-0 chain offload...")
812 ret, _, err = sim.cls_bpf_add_filter(obj, chain=1, prio=1, handle=1,
814 fail=False, include_stderr=True)
815 fail(ret == 0, "Offloaded a filter to chain other than 0")
816 check_extack(err, "Driver supports only offload of chain 0.", args)
817 sim.tc_flush_filters()
819 start_test("Test TC replace...")
820 sim.cls_bpf_add_filter(obj, prio=1, handle=1)
821 sim.cls_bpf_add_filter(obj, op="replace", prio=1, handle=1)
822 sim.cls_filter_op(op="delete", prio=1, handle=1, cls="bpf")
824 sim.cls_bpf_add_filter(obj, prio=1, handle=1, skip_sw=True)
825 sim.cls_bpf_add_filter(obj, op="replace", prio=1, handle=1, skip_sw=True)
826 sim.cls_filter_op(op="delete", prio=1, handle=1, cls="bpf")
828 sim.cls_bpf_add_filter(obj, prio=1, handle=1, skip_hw=True)
829 sim.cls_bpf_add_filter(obj, op="replace", prio=1, handle=1, skip_hw=True)
830 sim.cls_filter_op(op="delete", prio=1, handle=1, cls="bpf")
832 start_test("Test TC replace bad flags...")
835 ret, _ = sim.cls_bpf_add_filter(obj, op="replace", prio=1, handle=1,
836 skip_sw=(j == 1), skip_hw=(j == 2),
838 fail(bool(ret) != bool(j),
839 "Software TC incorrect load in replace test, iteration %d" %
841 sim.cls_filter_op(op="delete", prio=1, handle=1, cls="bpf")
843 start_test("Test spurious extack from the driver...")
844 test_spurios_extack(sim, obj, False, "netdevsim")
845 test_spurios_extack(sim, obj, True, "netdevsim")
847 sim.set_ethtool_tc_offloads(False)
849 test_spurios_extack(sim, obj, False, "TC offload is disabled")
850 test_spurios_extack(sim, obj, True, "TC offload is disabled")
852 sim.set_ethtool_tc_offloads(True)
854 sim.tc_flush_filters()
856 start_test("Test TC offloads failure...")
857 sim.dfs["dev/bpf_bind_verifier_accept"] = 0
858 ret, _, err = sim.cls_bpf_add_filter(obj, verbose=True, skip_sw=True,
859 fail=False, include_stderr=True)
860 fail(ret == 0, "TC filter did not reject with TC offloads enabled")
861 check_verifier_log(err, "[netdevsim] Hello from netdevsim!")
862 sim.dfs["dev/bpf_bind_verifier_accept"] = 1
864 start_test("Test TC offloads work...")
865 ret, _, err = sim.cls_bpf_add_filter(obj, verbose=True, skip_sw=True,
866 fail=False, include_stderr=True)
867 fail(ret != 0, "TC filter did not load with TC offloads enabled")
869 start_test("Test TC offload basics...")
870 dfs = simdev.dfs_get_bound_progs(expected=1)
871 progs = bpftool_prog_list(expected=1)
872 ingress = sim.tc_show_ingress(expected=1)
877 fail(fltr["skip_hw"], "TC does reports 'skip_hw' on offloaded filter")
878 fail(not fltr["in_hw"], "TC does not report 'in_hw' for offloaded filter")
879 fail(not fltr["skip_sw"], "TC does not report 'skip_sw' back")
881 start_test("Test TC offload is device-bound...")
882 fail(str(prog["id"]) != fltr["id"], "Program IDs don't match")
883 fail(prog["tag"] != fltr["tag"], "Program tags don't match")
884 fail(fltr["id"] != dprog["id"], "Program IDs don't match")
885 fail(dprog["state"] != "xlated", "Offloaded program state not translated")
886 fail(dprog["loaded"] != "Y", "Offloaded program is not loaded")
888 start_test("Test disabling TC offloads is rejected while filters installed...")
889 ret, _ = sim.set_ethtool_tc_offloads(False, fail=False)
890 fail(ret == 0, "Driver should refuse to disable TC offloads with filters installed...")
891 sim.set_ethtool_tc_offloads(True)
893 start_test("Test qdisc removal frees things...")
894 sim.tc_flush_filters()
895 sim.tc_show_ingress(expected=0)
897 start_test("Test disabling TC offloads is OK without filters...")
898 ret, _ = sim.set_ethtool_tc_offloads(False, fail=False)
900 "Driver refused to disable TC offloads without filters installed...")
902 sim.set_ethtool_tc_offloads(True)
904 start_test("Test destroying device gets rid of TC filters...")
905 sim.cls_bpf_add_filter(obj, skip_sw=True)
907 bpftool_prog_list_wait(expected=0)
909 simdev = BpfNetdevSimDev()
911 sim.set_ethtool_tc_offloads(True)
913 start_test("Test destroying device gets rid of XDP...")
914 sim.set_xdp(obj, "offload")
916 bpftool_prog_list_wait(expected=0)
918 simdev = BpfNetdevSimDev()
920 sim.set_ethtool_tc_offloads(True)
922 start_test("Test XDP prog reporting...")
923 sim.set_xdp(obj, "drv")
924 ipl = sim.ip_link_show(xdp=True)
925 progs = bpftool_prog_list(expected=1)
926 fail(ipl["xdp"]["prog"]["id"] != progs[0]["id"],
927 "Loaded program has wrong ID")
929 start_test("Test XDP prog replace without force...")
930 ret, _ = sim.set_xdp(obj, "drv", fail=False)
931 fail(ret == 0, "Replaced XDP program without -force")
932 sim.wait_for_flush(total=1)
934 start_test("Test XDP prog replace with force...")
935 ret, _ = sim.set_xdp(obj, "drv", force=True, fail=False)
936 fail(ret != 0, "Could not replace XDP program with -force")
937 bpftool_prog_list_wait(expected=1)
938 ipl = sim.ip_link_show(xdp=True)
939 progs = bpftool_prog_list(expected=1)
940 fail(ipl["xdp"]["prog"]["id"] != progs[0]["id"],
941 "Loaded program has wrong ID")
942 fail("dev" in progs[0].keys(),
943 "Device parameters reported for non-offloaded program")
945 start_test("Test XDP prog replace with bad flags...")
946 ret, _, err = sim.set_xdp(obj, "generic", force=True,
947 fail=False, include_stderr=True)
948 fail(ret == 0, "Replaced XDP program with a program in different mode")
950 "Native and generic XDP can't be active at the same time.",
953 start_test("Test MTU restrictions...")
954 ret, _ = sim.set_mtu(9000, fail=False)
956 "Driver should refuse to increase MTU to 9000 with XDP loaded...")
958 bpftool_prog_list_wait(expected=0)
960 ret, _, err = sim.set_xdp(obj, "drv", fail=False, include_stderr=True)
961 fail(ret == 0, "Driver should refuse to load program with MTU of 9000...")
962 check_extack_nsim(err, "MTU too large w/ XDP enabled.", args)
966 start_test("Test non-offload XDP attaching to HW...")
967 bpftool_prog_load("sample_ret0.bpf.o", "/sys/fs/bpf/nooffload")
968 nooffload = bpf_pinned("/sys/fs/bpf/nooffload")
969 ret, _, err = sim.set_xdp(nooffload, "offload",
970 fail=False, include_stderr=True)
971 fail(ret == 0, "attached non-offloaded XDP program to HW")
972 check_extack_nsim(err, "xdpoffload of non-bound program.", args)
973 rm("/sys/fs/bpf/nooffload")
975 start_test("Test offload XDP attaching to drv...")
976 bpftool_prog_load("sample_ret0.bpf.o", "/sys/fs/bpf/offload",
978 offload = bpf_pinned("/sys/fs/bpf/offload")
979 ret, _, err = sim.set_xdp(offload, "drv", fail=False, include_stderr=True)
980 fail(ret == 0, "attached offloaded XDP program to drv")
981 check_extack(err, "Using offloaded program without HW_MODE flag is not supported.", args)
982 rm("/sys/fs/bpf/offload")
985 bpftool_prog_load("sample_ret0.bpf.o", "/sys/fs/bpf/devbound",
986 dev_bind=sim['ifname'])
987 devbound = bpf_pinned("/sys/fs/bpf/devbound")
988 start_test("Test dev-bound program in generic mode...")
989 ret, _, err = sim.set_xdp(devbound, "generic", fail=False, include_stderr=True)
990 fail(ret == 0, "devbound program in generic mode allowed")
991 check_extack(err, "Can't attach device-bound programs in generic mode.", args)
992 rm("/sys/fs/bpf/devbound")
995 start_test("Test XDP load failure...")
996 sim.dfs["dev/bpf_bind_verifier_accept"] = 0
997 ret, _, err = bpftool_prog_load("sample_ret0.bpf.o", "/sys/fs/bpf/offload",
998 dev=sim['ifname'], fail=False, include_stderr=True)
999 fail(ret == 0, "verifier should fail on load")
1000 check_verifier_log(err, "[netdevsim] Hello from netdevsim!")
1001 sim.dfs["dev/bpf_bind_verifier_accept"] = 1
1002 sim.wait_for_flush()
1004 start_test("Test XDP offload...")
1005 _, _, err = sim.set_xdp(obj, "offload", verbose=True, include_stderr=True)
1006 ipl = sim.ip_link_show(xdp=True)
1007 link_xdp = ipl["xdp"]["prog"]
1008 progs = bpftool_prog_list(expected=1)
1010 fail(link_xdp["id"] != prog["id"], "Loaded program has wrong ID")
1012 start_test("Test XDP offload is device bound...")
1013 dfs = simdev.dfs_get_bound_progs(expected=1)
1016 fail(prog["id"] != link_xdp["id"], "Program IDs don't match")
1017 fail(prog["tag"] != link_xdp["tag"], "Program tags don't match")
1018 fail(str(link_xdp["id"]) != dprog["id"], "Program IDs don't match")
1019 fail(dprog["state"] != "xlated", "Offloaded program state not translated")
1020 fail(dprog["loaded"] != "Y", "Offloaded program is not loaded")
1022 start_test("Test removing XDP program many times...")
1023 sim.unset_xdp("offload")
1024 sim.unset_xdp("offload")
1025 sim.unset_xdp("drv")
1026 sim.unset_xdp("drv")
1029 bpftool_prog_list_wait(expected=0)
1031 start_test("Test attempt to use a program for a wrong device...")
1032 simdev2 = BpfNetdevSimDev()
1033 sim2, = simdev2.nsims
1034 sim2.set_xdp(obj, "offload")
1035 pin_file, pinned = pin_prog("/sys/fs/bpf/tmp")
1037 ret, _, err = sim.set_xdp(pinned, "offload",
1038 fail=False, include_stderr=True)
1039 fail(ret == 0, "Pinned program loaded for a different device accepted")
1040 check_extack(err, "Program bound to different device.", args)
1042 ret, _, err = sim.set_xdp(pinned, "offload",
1043 fail=False, include_stderr=True)
1044 fail(ret == 0, "Pinned program loaded for a removed device accepted")
1045 check_extack(err, "Program bound to different device.", args)
1047 bpftool_prog_list_wait(expected=0)
1049 simdev, sim = test_multi_prog(simdev, sim, obj, "", 1)
1050 simdev, sim = test_multi_prog(simdev, sim, obj, "drv", 1)
1051 simdev, sim = test_multi_prog(simdev, sim, obj, "generic", 2)
1053 start_test("Test mixing of TC and XDP...")
1054 sim.tc_add_ingress()
1055 sim.set_xdp(obj, "offload")
1056 ret, _, err = sim.cls_bpf_add_filter(obj, skip_sw=True,
1057 fail=False, include_stderr=True)
1058 fail(ret == 0, "Loading TC when XDP active should fail")
1059 check_extack_nsim(err, "driver and netdev offload states mismatch.", args)
1060 sim.unset_xdp("offload")
1061 sim.wait_for_flush()
1063 sim.cls_bpf_add_filter(obj, skip_sw=True)
1064 ret, _, err = sim.set_xdp(obj, "offload", fail=False, include_stderr=True)
1065 fail(ret == 0, "Loading XDP when TC active should fail")
1066 check_extack_nsim(err, "TC program is already loaded.", args)
1068 start_test("Test binding TC from pinned...")
1069 pin_file, pinned = pin_prog("/sys/fs/bpf/tmp")
1070 sim.tc_flush_filters(bound=1, total=1)
1071 sim.cls_bpf_add_filter(pinned, da=True, skip_sw=True)
1072 sim.tc_flush_filters(bound=1, total=1)
1074 start_test("Test binding XDP from pinned...")
1075 sim.set_xdp(obj, "offload")
1076 pin_file, pinned = pin_prog("/sys/fs/bpf/tmp2", idx=1)
1078 sim.set_xdp(pinned, "offload", force=True)
1079 sim.unset_xdp("offload")
1080 sim.set_xdp(pinned, "offload", force=True)
1081 sim.unset_xdp("offload")
1083 start_test("Test offload of wrong type fails...")
1084 ret, _ = sim.cls_bpf_add_filter(pinned, da=True, skip_sw=True, fail=False)
1085 fail(ret == 0, "Managed to attach XDP program to TC")
1087 start_test("Test asking for TC offload of two filters...")
1088 sim.cls_bpf_add_filter(obj, da=True, skip_sw=True)
1089 ret, _, err = sim.cls_bpf_add_filter(obj, da=True, skip_sw=True,
1090 fail=False, include_stderr=True)
1091 fail(ret == 0, "Managed to offload two TC filters at the same time")
1092 check_extack_nsim(err, "driver and netdev offload states mismatch.", args)
1094 sim.tc_flush_filters(bound=2, total=2)
1096 start_test("Test if netdev removal waits for translation...")
1098 sim.dfs["dev/bpf_bind_verifier_delay"] = delay_msec
1100 cmd_line = "tc filter add dev %s ingress bpf %s da skip_sw" % \
1101 (sim['ifname'], obj)
1102 tc_proc = cmd(cmd_line, background=True, fail=False)
1103 # Wait for the verifier to start
1104 while simdev.dfs_num_bound_progs() <= 2:
1108 ret, _ = cmd_result(tc_proc, fail=False)
1109 time_diff = end - start
1110 log("Time", "start:\t%s\nend:\t%s\ndiff:\t%s" % (start, end, time_diff))
1112 fail(ret == 0, "Managed to load TC filter on a unregistering device")
1113 delay_sec = delay_msec * 0.001
1114 fail(time_diff < delay_sec, "Removal process took %s, expected %s" %
1115 (time_diff, delay_sec))
1117 # Remove all pinned files and reinstantiate the netdev
1119 bpftool_prog_list_wait(expected=0)
1121 simdev = BpfNetdevSimDev()
1123 map_obj = bpf_obj("sample_map_ret0.bpf.o")
1124 start_test("Test loading program with maps...")
1125 sim.set_xdp(map_obj, "offload", JSON=False) # map fixup msg breaks JSON
1127 start_test("Test bpftool bound info reporting (own ns)...")
1128 check_dev_info(False, "")
1130 start_test("Test bpftool bound info reporting (other ns)...")
1133 check_dev_info(True, "")
1135 start_test("Test bpftool bound info reporting (remote ns)...")
1136 check_dev_info(False, ns)
1138 start_test("Test bpftool bound info reporting (back to own ns)...")
1140 check_dev_info(False, "")
1142 prog_file, _ = pin_prog("/sys/fs/bpf/tmp_prog")
1143 map_file, _ = pin_map("/sys/fs/bpf/tmp_map", idx=1, expected=2)
1146 start_test("Test bpftool bound info reporting (removed dev)...")
1147 check_dev_info_removed(prog_file=prog_file, map_file=map_file)
1149 # Remove all pinned files and reinstantiate the netdev
1151 bpftool_prog_list_wait(expected=0)
1153 simdev = BpfNetdevSimDev()
1156 start_test("Test map update (no flags)...")
1157 sim.set_xdp(map_obj, "offload", JSON=False) # map fixup msg breaks JSON
1158 maps = bpftool_map_list_wait(expected=2)
1159 array = maps[0] if maps[0]["type"] == "array" else maps[1]
1160 htab = maps[0] if maps[0]["type"] == "hash" else maps[1]
1163 bpftool("map update id %d key %s value %s" %
1164 (m["id"], int2str("I", i), int2str("Q", i * 3)))
1167 ret, _ = bpftool("map update id %d key %s value %s" %
1168 (m["id"], int2str("I", 3), int2str("Q", 3 * 3)),
1170 fail(ret == 0, "added too many entries")
1172 start_test("Test map update (exists)...")
1175 bpftool("map update id %d key %s value %s exist" %
1176 (m["id"], int2str("I", i), int2str("Q", i * 3)))
1179 ret, err = bpftool("map update id %d key %s value %s exist" %
1180 (m["id"], int2str("I", 3), int2str("Q", 3 * 3)),
1182 fail(ret == 0, "updated non-existing key")
1183 fail(err["error"].find("No such file or directory") == -1,
1184 "expected ENOENT, error is '%s'" % (err["error"]))
1186 start_test("Test map update (noexist)...")
1189 ret, err = bpftool("map update id %d key %s value %s noexist" %
1190 (m["id"], int2str("I", i), int2str("Q", i * 3)),
1192 fail(ret == 0, "updated existing key")
1193 fail(err["error"].find("File exists") == -1,
1194 "expected EEXIST, error is '%s'" % (err["error"]))
1196 start_test("Test map dump...")
1198 _, entries = bpftool("map dump id %d" % (m["id"]))
1200 key = str2int(entries[i]["key"])
1201 fail(key != i, "expected key %d, got %d" % (key, i))
1202 val = str2int(entries[i]["value"])
1203 fail(val != i * 3, "expected value %d, got %d" % (val, i * 3))
1205 start_test("Test map getnext...")
1207 _, entry = bpftool("map getnext id %d" % (m["id"]))
1208 key = str2int(entry["next_key"])
1209 fail(key != 0, "next key %d, expected %d" % (key, 0))
1210 _, entry = bpftool("map getnext id %d key %s" %
1211 (m["id"], int2str("I", 0)))
1212 key = str2int(entry["next_key"])
1213 fail(key != 1, "next key %d, expected %d" % (key, 1))
1214 ret, err = bpftool("map getnext id %d key %s" %
1215 (m["id"], int2str("I", 1)), fail=False)
1216 fail(ret == 0, "got next key past the end of map")
1217 fail(err["error"].find("No such file or directory") == -1,
1218 "expected ENOENT, error is '%s'" % (err["error"]))
1220 start_test("Test map delete (htab)...")
1222 bpftool("map delete id %d key %s" % (htab["id"], int2str("I", i)))
1224 start_test("Test map delete (array)...")
1226 ret, err = bpftool("map delete id %d key %s" %
1227 (htab["id"], int2str("I", i)), fail=False)
1228 fail(ret == 0, "removed entry from an array")
1229 fail(err["error"].find("No such file or directory") == -1,
1230 "expected ENOENT, error is '%s'" % (err["error"]))
1232 start_test("Test map remove...")
1233 sim.unset_xdp("offload")
1234 bpftool_map_list_wait(expected=0)
1237 simdev = BpfNetdevSimDev()
1239 sim.set_xdp(map_obj, "offload", JSON=False) # map fixup msg breaks JSON
1241 bpftool_map_list_wait(expected=0)
1243 start_test("Test map creation fail path...")
1244 simdev = BpfNetdevSimDev()
1246 sim.dfs["bpf_map_accept"] = "N"
1247 ret, _ = sim.set_xdp(map_obj, "offload", JSON=False, fail=False)
1249 "netdevsim didn't refuse to create a map with offload disabled")
1253 start_test("Test multi-dev ASIC program reuse...")
1254 simdevA = BpfNetdevSimDev()
1255 simA, = simdevA.nsims
1256 simdevB = BpfNetdevSimDev(3)
1257 simB1, simB2, simB3 = simdevB.nsims
1258 sims = (simA, simB1, simB2, simB3)
1259 simB = (simB1, simB2, simB3)
1261 bpftool_prog_load("sample_map_ret0.bpf.o", "/sys/fs/bpf/nsimA",
1263 progA = bpf_pinned("/sys/fs/bpf/nsimA")
1264 bpftool_prog_load("sample_map_ret0.bpf.o", "/sys/fs/bpf/nsimB",
1265 dev=simB1['ifname'])
1266 progB = bpf_pinned("/sys/fs/bpf/nsimB")
1268 simA.set_xdp(progA, "offload", JSON=False)
1269 for d in simdevB.nsims:
1270 d.set_xdp(progB, "offload", JSON=False)
1272 start_test("Test multi-dev ASIC cross-dev replace...")
1273 ret, _ = simA.set_xdp(progB, "offload", force=True, JSON=False, fail=False)
1274 fail(ret == 0, "cross-ASIC program allowed")
1275 for d in simdevB.nsims:
1276 ret, _ = d.set_xdp(progA, "offload", force=True, JSON=False, fail=False)
1277 fail(ret == 0, "cross-ASIC program allowed")
1279 start_test("Test multi-dev ASIC cross-dev install...")
1281 d.unset_xdp("offload")
1283 ret, _, err = simA.set_xdp(progB, "offload", force=True, JSON=False,
1284 fail=False, include_stderr=True)
1285 fail(ret == 0, "cross-ASIC program allowed")
1286 check_extack(err, "Program bound to different device.", args)
1287 for d in simdevB.nsims:
1288 ret, _, err = d.set_xdp(progA, "offload", force=True, JSON=False,
1289 fail=False, include_stderr=True)
1290 fail(ret == 0, "cross-ASIC program allowed")
1291 check_extack(err, "Program bound to different device.", args)
1293 start_test("Test multi-dev ASIC cross-dev map reuse...")
1295 mapA = bpftool("prog show %s" % (progA))[1]["map_ids"][0]
1296 mapB = bpftool("prog show %s" % (progB))[1]["map_ids"][0]
1298 ret, _ = bpftool_prog_load("sample_map_ret0.bpf.o", "/sys/fs/bpf/nsimB_",
1299 dev=simB3['ifname'],
1300 maps=["idx 0 id %d" % (mapB)],
1302 fail(ret != 0, "couldn't reuse a map on the same ASIC")
1303 rm("/sys/fs/bpf/nsimB_")
1305 ret, _, err = bpftool_prog_load("sample_map_ret0.bpf.o", "/sys/fs/bpf/nsimA_",
1307 maps=["idx 0 id %d" % (mapB)],
1308 fail=False, include_stderr=True)
1309 fail(ret == 0, "could reuse a map on a different ASIC")
1310 fail(err.count("offload device mismatch between prog and map") == 0,
1311 "error message missing for cross-ASIC map")
1313 ret, _, err = bpftool_prog_load("sample_map_ret0.bpf.o", "/sys/fs/bpf/nsimB_",
1314 dev=simB1['ifname'],
1315 maps=["idx 0 id %d" % (mapA)],
1316 fail=False, include_stderr=True)
1317 fail(ret == 0, "could reuse a map on a different ASIC")
1318 fail(err.count("offload device mismatch between prog and map") == 0,
1319 "error message missing for cross-ASIC map")
1321 start_test("Test multi-dev ASIC cross-dev destruction...")
1322 bpftool_prog_list_wait(expected=2)
1325 bpftool_prog_list_wait(expected=1)
1327 ifnameB = bpftool("prog show %s" % (progB))[1]["dev"]["ifname"]
1328 fail(ifnameB != simB1['ifname'], "program not bound to original device")
1330 bpftool_prog_list_wait(expected=1)
1332 start_test("Test multi-dev ASIC cross-dev destruction - move...")
1333 ifnameB = bpftool("prog show %s" % (progB))[1]["dev"]["ifname"]
1334 fail(ifnameB not in (simB2['ifname'], simB3['ifname']),
1335 "program not bound to remaining devices")
1338 ifnameB = bpftool("prog show %s" % (progB))[1]["dev"]["ifname"]
1339 fail(ifnameB != simB3['ifname'], "program not bound to remaining device")
1343 bpftool_prog_list_wait(expected=0)
1345 start_test("Test multi-dev ASIC cross-dev destruction - orphaned...")
1346 ret, out = bpftool("prog show %s" % (progB), fail=False)
1347 fail(ret != 0, "couldn't get information about orphaned program")
1349 print("%s: OK" % (os.path.basename(__file__)))
1352 log("Clean up...", "", level=1)