3 # Copyright (C) 2017 Netronome Systems, Inc.
5 # This software is licensed under the GNU General License Version 2,
6 # June 1991 as shown in the file COPYING in the top-level directory of this
9 # THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS"
10 # WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING,
11 # BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
12 # FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE
13 # OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME
14 # THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
16 from datetime import datetime
31 bpf_test_dir = os.path.dirname(os.path.realpath(__file__))
32 pp = pprint.PrettyPrinter()
33 devs = [] # devices we created for clean up
34 files = [] # files to be removed
35 netns = [] # net namespaces to be removed
37 def log_get_sec(level=0):
38 return "*" * (log_level + level)
40 def log_level_inc(add=1):
44 def log_level_dec(sub=1):
48 def log_level_set(level):
52 def log(header, data, level=None):
54 Output to an optional log.
61 if not isinstance(data, str):
62 data = pp.pformat(data)
65 logfile.write("\n" + log_get_sec() + " ")
67 if len(header) and len(data.strip()):
75 log("SKIP: " + msg, "", level=1)
82 tb = "".join(traceback.extract_stack().format())
84 log("FAIL: " + msg, tb, level=1)
92 def cmd(cmd, shell=True, include_stderr=False, background=False, fail=True):
94 Run a command in subprocess and return tuple of (retval, stdout);
95 optionally return stderr as well as third value.
97 proc = subprocess.Popen(cmd, shell=shell, stdout=subprocess.PIPE,
98 stderr=subprocess.PIPE)
100 msg = "%s START: %s" % (log_get_sec(1),
101 datetime.now().strftime("%H:%M:%S.%f"))
102 log("BKG " + proc.args, msg)
105 return cmd_result(proc, include_stderr=include_stderr, fail=fail)
107 def cmd_result(proc, include_stderr=False, fail=False):
108 stdout, stderr = proc.communicate()
109 stdout = stdout.decode("utf-8")
110 stderr = stderr.decode("utf-8")
114 stderr = "\n" + stderr
115 if stderr[-1] == "\n":
119 log("CMD " + proc.args,
120 "RETCODE: %d\n%s STDOUT:\n%s%s STDERR:%s\n%s END: %s" %
121 (proc.returncode, sec, stdout, sec, stderr,
122 sec, datetime.now().strftime("%H:%M:%S.%f")))
124 if proc.returncode != 0 and fail:
125 if len(stderr) > 0 and stderr[-1] == "\n":
127 raise Exception("Command failed: %s\n%s" % (proc.args, stderr))
130 return proc.returncode, stdout, stderr
132 return proc.returncode, stdout
135 cmd("rm -f %s" % (f))
139 def tool(name, args, flags, JSON=True, ns="", fail=True, include_stderr=False):
142 params += "%s " % (flags["json"])
145 ns = "ip netns exec %s " % (ns)
148 ret, stdout, stderr = cmd(ns + name + " " + params + args,
149 fail=fail, include_stderr=True)
151 ret, stdout = cmd(ns + name + " " + params + args,
152 fail=fail, include_stderr=False)
154 if JSON and len(stdout.strip()) != 0:
155 out = json.loads(stdout)
160 return ret, out, stderr
164 def bpftool(args, JSON=True, ns="", fail=True, include_stderr=False):
165 return tool("bpftool", args, {"json":"-p"}, JSON=JSON, ns=ns,
166 fail=fail, include_stderr=include_stderr)
168 def bpftool_prog_list(expected=None, ns=""):
169 _, progs = bpftool("prog show", JSON=True, ns=ns, fail=True)
170 # Remove the base progs
174 if expected is not None:
175 if len(progs) != expected:
176 fail(True, "%d BPF programs loaded, expected %d" %
177 (len(progs), expected))
180 def bpftool_map_list(expected=None, ns=""):
181 _, maps = bpftool("map show", JSON=True, ns=ns, fail=True)
182 # Remove the base maps
186 if expected is not None:
187 if len(maps) != expected:
188 fail(True, "%d BPF maps loaded, expected %d" %
189 (len(maps), expected))
192 def bpftool_prog_list_wait(expected=0, n_retry=20):
193 for i in range(n_retry):
194 nprogs = len(bpftool_prog_list())
195 if nprogs == expected:
198 raise Exception("Time out waiting for program counts to stabilize want %d, have %d" % (expected, nprogs))
200 def bpftool_map_list_wait(expected=0, n_retry=20):
201 for i in range(n_retry):
202 nmaps = len(bpftool_map_list())
203 if nmaps == expected:
206 raise Exception("Time out waiting for map counts to stabilize want %d, have %d" % (expected, nmaps))
208 def bpftool_prog_load(sample, file_name, maps=[], prog_type="xdp", dev=None,
209 fail=True, include_stderr=False):
210 args = "prog load %s %s" % (os.path.join(bpf_test_dir, sample), file_name)
211 if prog_type is not None:
212 args += " type " + prog_type
214 args += " dev " + dev
216 args += " map " + " map ".join(maps)
218 res = bpftool(args, fail=fail, include_stderr=include_stderr)
220 files.append(file_name)
223 def ip(args, force=False, JSON=True, ns="", fail=True, include_stderr=False):
225 args = "-force " + args
226 return tool("ip", args, {"json":"-j"}, JSON=JSON, ns=ns,
227 fail=fail, include_stderr=include_stderr)
229 def tc(args, JSON=True, ns="", fail=True, include_stderr=False):
230 return tool("tc", args, {"json":"-p"}, JSON=JSON, ns=ns,
231 fail=fail, include_stderr=include_stderr)
233 def ethtool(dev, opt, args, fail=True):
234 return cmd("ethtool %s %s %s" % (opt, dev["ifname"], args), fail=fail)
236 def bpf_obj(name, sec=".text", path=bpf_test_dir,):
237 return "obj %s sec %s" % (os.path.join(path, name), sec)
239 def bpf_pinned(name):
240 return "pinned %s" % (name)
242 def bpf_bytecode(bytecode):
243 return "bytecode \"%s\"" % (bytecode)
245 def mknetns(n_retry=10):
246 for i in range(n_retry):
247 name = ''.join([random.choice(string.ascii_letters) for i in range(8)])
248 ret, _ = ip("netns add %s" % (name), fail=False)
254 def int2str(fmt, val):
256 for b in struct.pack(fmt, val):
258 return " ".join(map(lambda x: str(x), ret))
263 inttab.append(int(i, 16))
264 ba = bytearray(inttab)
267 elif len(strtab) == 8:
270 raise Exception("String array of len %d can't be unpacked to an int" %
272 return struct.unpack(fmt, ba)[0]
276 Class for accessing DebugFS directories as a dictionary.
279 def __init__(self, path):
281 self._dict = self._debugfs_dir_read(path)
284 return len(self._dict.keys())
286 def __getitem__(self, key):
288 key = list(self._dict.keys())[key]
289 return self._dict[key]
291 def __setitem__(self, key, value):
292 log("DebugFS set %s = %s" % (key, value), "")
295 cmd("echo '%s' > %s/%s" % (value, self.path, key))
298 _, out = cmd('cat %s/%s' % (self.path, key))
299 self._dict[key] = out.strip()
301 def _debugfs_dir_read(self, path):
304 log("DebugFS state for %s" % (path), "")
307 _, out = cmd('ls ' + path)
308 for f in out.split():
309 p = os.path.join(path, f)
310 if os.path.isfile(p):
311 _, out = cmd('cat %s/%s' % (path, f))
313 elif os.path.isdir(p):
314 dfs[f] = DebugfsDir(p)
316 raise Exception("%s is neither file nor directory" % (p))
319 log("DebugFS state", dfs)
326 Class for netdevsim netdevice and its attributes.
329 def __init__(self, link=None):
332 self.dev = self._netdevsim_create()
337 self.dfs_dir = '/sys/kernel/debug/netdevsim/%s' % (self.dev['ifname'])
338 self.sdev_dir = self.dfs_dir + '/sdev/'
341 def __getitem__(self, key):
344 def _netdevsim_create(self):
345 link = "" if self.link is None else "link " + self.link.dev['ifname']
346 _, old = ip("link show")
347 ip("link add sim%d {link} type netdevsim".format(link=link))
348 _, new = ip("link show")
351 f = filter(lambda x: x["ifname"] == dev["ifname"], old)
352 if len(list(f)) == 0:
355 raise Exception("failed to create netdevsim device")
359 ip("link del dev %s" % (self.dev["ifname"]), ns=self.ns)
361 def dfs_refresh(self):
362 self.dfs = DebugfsDir(self.dfs_dir)
365 def dfs_read(self, f):
366 path = os.path.join(self.dfs_dir, f)
367 _, data = cmd('cat %s' % (path))
370 def dfs_num_bound_progs(self):
371 path = os.path.join(self.sdev_dir, "bpf_bound_progs")
372 _, progs = cmd('ls %s' % (path))
373 return len(progs.split())
375 def dfs_get_bound_progs(self, expected):
376 progs = DebugfsDir(os.path.join(self.sdev_dir, "bpf_bound_progs"))
377 if expected is not None:
378 if len(progs) != expected:
379 fail(True, "%d BPF programs bound, expected %d" %
380 (len(progs), expected))
383 def wait_for_flush(self, bound=0, total=0, n_retry=20):
384 for i in range(n_retry):
385 nbound = self.dfs_num_bound_progs()
386 nprogs = len(bpftool_prog_list())
387 if nbound == bound and nprogs == total:
390 raise Exception("Time out waiting for program counts to stabilize want %d/%d, have %d bound, %d loaded" % (bound, total, nbound, nprogs))
392 def set_ns(self, ns):
393 name = "1" if ns == "" else ns
394 ip("link set dev %s netns %s" % (self.dev["ifname"], name), ns=self.ns)
397 def set_mtu(self, mtu, fail=True):
398 return ip("link set dev %s mtu %d" % (self.dev["ifname"], mtu),
401 def set_xdp(self, bpf, mode, force=False, JSON=True, verbose=False,
402 fail=True, include_stderr=False):
405 return ip("link set dev %s xdp%s %s" % (self.dev["ifname"], mode, bpf),
406 force=force, JSON=JSON,
407 fail=fail, include_stderr=include_stderr)
409 def unset_xdp(self, mode, force=False, JSON=True,
410 fail=True, include_stderr=False):
411 return ip("link set dev %s xdp%s off" % (self.dev["ifname"], mode),
412 force=force, JSON=JSON,
413 fail=fail, include_stderr=include_stderr)
415 def ip_link_show(self, xdp):
416 _, link = ip("link show dev %s" % (self['ifname']))
418 raise Exception("Multiple objects on ip link show")
421 fail(xdp != "xdp" in link,
422 "XDP program not reporting in iplink (reported %s, expected %s)" %
423 ("xdp" in link, xdp))
426 def tc_add_ingress(self):
427 tc("qdisc add dev %s ingress" % (self['ifname']))
429 def tc_del_ingress(self):
430 tc("qdisc del dev %s ingress" % (self['ifname']))
432 def tc_flush_filters(self, bound=0, total=0):
433 self.tc_del_ingress()
434 self.tc_add_ingress()
435 self.wait_for_flush(bound=bound, total=total)
437 def tc_show_ingress(self, expected=None):
438 # No JSON support, oh well...
439 flags = ["skip_sw", "skip_hw", "in_hw"]
440 named = ["protocol", "pref", "chain", "handle", "id", "tag"]
442 args = "-s filter show dev %s ingress" % (self['ifname'])
443 _, out = tc(args, JSON=False)
446 lines = out.split('\n')
449 if "handle" not in words:
453 fltr[flag] = flag in words
456 idx = words.index(name)
457 fltr[name] = words[idx + 1]
462 if expected is not None:
463 fail(len(filters) != expected,
464 "%d ingress filters loaded, expected %d" %
465 (len(filters), expected))
468 def cls_filter_op(self, op, qdisc="ingress", prio=None, handle=None,
469 chain=None, cls="", params="",
470 fail=True, include_stderr=False):
473 spec += " prio %d" % (prio)
475 spec += " handle %s" % (handle)
476 if chain is not None:
477 spec += " chain %d" % (chain)
479 return tc("filter {op} dev {dev} {qdisc} {spec} {cls} {params}"\
480 .format(op=op, dev=self['ifname'], qdisc=qdisc, spec=spec,
481 cls=cls, params=params),
482 fail=fail, include_stderr=include_stderr)
484 def cls_bpf_add_filter(self, bpf, op="add", prio=None, handle=None,
485 chain=None, da=False, verbose=False,
486 skip_sw=False, skip_hw=False,
487 fail=True, include_stderr=False):
500 return self.cls_filter_op(op=op, prio=prio, handle=handle, cls=cls,
501 chain=chain, params=params,
502 fail=fail, include_stderr=include_stderr)
504 def set_ethtool_tc_offloads(self, enable, fail=True):
505 args = "hw-tc-offload %s" % ("on" if enable else "off")
506 return ethtool(self, "-K", args, fail=fail)
508 ################################################################################
510 global files, netns, devs
515 cmd("rm -f %s" % (f))
517 cmd("ip netns delete %s" % (ns))
521 def pin_prog(file_name, idx=0):
522 progs = bpftool_prog_list(expected=(idx + 1))
524 bpftool("prog pin id %d %s" % (prog["id"], file_name))
525 files.append(file_name)
527 return file_name, bpf_pinned(file_name)
529 def pin_map(file_name, idx=0, expected=1):
530 maps = bpftool_map_list(expected=expected)
532 bpftool("map pin id %d %s" % (m["id"], file_name))
533 files.append(file_name)
535 return file_name, bpf_pinned(file_name)
537 def check_dev_info_removed(prog_file=None, map_file=None):
538 bpftool_prog_list(expected=0)
539 ret, err = bpftool("prog show pin %s" % (prog_file), fail=False)
540 fail(ret == 0, "Showing prog with removed device did not fail")
541 fail(err["error"].find("No such device") == -1,
542 "Showing prog with removed device expected ENODEV, error is %s" %
545 bpftool_map_list(expected=0)
546 ret, err = bpftool("map show pin %s" % (map_file), fail=False)
547 fail(ret == 0, "Showing map with removed device did not fail")
548 fail(err["error"].find("No such device") == -1,
549 "Showing map with removed device expected ENODEV, error is %s" %
552 def check_dev_info(other_ns, ns, prog_file=None, map_file=None, removed=False):
553 progs = bpftool_prog_list(expected=1, ns=ns)
556 fail("dev" not in prog.keys(), "Device parameters not reported")
558 fail("ifindex" not in dev.keys(), "Device parameters not reported")
559 fail("ns_dev" not in dev.keys(), "Device parameters not reported")
560 fail("ns_inode" not in dev.keys(), "Device parameters not reported")
563 fail("ifname" not in dev.keys(), "Ifname not reported")
564 fail(dev["ifname"] != sim["ifname"],
565 "Ifname incorrect %s vs %s" % (dev["ifname"], sim["ifname"]))
567 fail("ifname" in dev.keys(), "Ifname is reported for other ns")
569 maps = bpftool_map_list(expected=2, ns=ns)
571 fail("dev" not in m.keys(), "Device parameters not reported")
572 fail(dev != m["dev"], "Map's device different than program's")
574 def check_extack(output, reference, args):
577 lines = output.split("\n")
578 comp = len(lines) >= 2 and lines[1] == 'Error: ' + reference
579 fail(not comp, "Missing or incorrect netlink extack message")
581 def check_extack_nsim(output, reference, args):
582 check_extack(output, "netdevsim: " + reference, args)
584 def check_no_extack(res, needle):
585 fail((res[1] + res[2]).count(needle) or (res[1] + res[2]).count("Warning:"),
586 "Found '%s' in command output, leaky extack?" % (needle))
588 def check_verifier_log(output, reference):
589 lines = output.split("\n")
590 for l in reversed(lines):
593 fail(True, "Missing or incorrect message from netdevsim in verifier log")
595 def check_multi_basic(two_xdps):
596 fail(two_xdps["mode"] != 4, "Bad mode reported with multiple programs")
597 fail("prog" in two_xdps, "Base program reported in multi program mode")
598 fail(len(two_xdps["attached"]) != 2,
599 "Wrong attached program count with two programs")
600 fail(two_xdps["attached"][0]["prog"]["id"] ==
601 two_xdps["attached"][1]["prog"]["id"],
602 "Offloaded and other programs have the same id")
604 def test_spurios_extack(sim, obj, skip_hw, needle):
605 res = sim.cls_bpf_add_filter(obj, prio=1, handle=1, skip_hw=skip_hw,
607 check_no_extack(res, needle)
608 res = sim.cls_bpf_add_filter(obj, op="replace", prio=1, handle=1,
609 skip_hw=skip_hw, include_stderr=True)
610 check_no_extack(res, needle)
611 res = sim.cls_filter_op(op="delete", prio=1, handle=1, cls="bpf",
613 check_no_extack(res, needle)
615 def test_multi_prog(sim, obj, modename, modeid):
616 start_test("Test multi-attachment XDP - %s + offload..." %
617 (modename or "default", ))
618 sim.set_xdp(obj, "offload")
619 xdp = sim.ip_link_show(xdp=True)["xdp"]
620 offloaded = sim.dfs_read("bpf_offloaded_id")
621 fail("prog" not in xdp, "Base program not reported in single program mode")
622 fail(len(xdp["attached"]) != 1,
623 "Wrong attached program count with one program")
625 sim.set_xdp(obj, modename)
626 two_xdps = sim.ip_link_show(xdp=True)["xdp"]
628 fail(xdp["attached"][0] not in two_xdps["attached"],
629 "Offload program not reported after other activated")
630 check_multi_basic(two_xdps)
632 offloaded2 = sim.dfs_read("bpf_offloaded_id")
633 fail(offloaded != offloaded2,
634 "Offload ID changed after loading other program")
636 start_test("Test multi-attachment XDP - replace...")
637 ret, _, err = sim.set_xdp(obj, "offload", fail=False, include_stderr=True)
638 fail(ret == 0, "Replaced one of programs without -force")
639 check_extack(err, "XDP program already attached.", args)
641 if modename == "" or modename == "drv":
642 othermode = "" if modename == "drv" else "drv"
643 start_test("Test multi-attachment XDP - detach...")
644 ret, _, err = sim.unset_xdp(othermode, force=True,
645 fail=False, include_stderr=True)
646 fail(ret == 0, "Removed program with a bad mode")
647 check_extack(err, "program loaded with different flags.", args)
649 sim.unset_xdp("offload")
650 xdp = sim.ip_link_show(xdp=True)["xdp"]
651 offloaded = sim.dfs_read("bpf_offloaded_id")
653 fail(xdp["mode"] != modeid, "Bad mode reported after multiple programs")
654 fail("prog" not in xdp,
655 "Base program not reported after multi program mode")
656 fail(xdp["attached"][0] not in two_xdps["attached"],
657 "Offload program not reported after other activated")
658 fail(len(xdp["attached"]) != 1,
659 "Wrong attached program count with remaining programs")
660 fail(offloaded != "0", "Offload ID reported with only other program left")
662 start_test("Test multi-attachment XDP - reattach...")
663 sim.set_xdp(obj, "offload")
664 two_xdps = sim.ip_link_show(xdp=True)["xdp"]
666 fail(xdp["attached"][0] not in two_xdps["attached"],
667 "Other program not reported after offload activated")
668 check_multi_basic(two_xdps)
670 start_test("Test multi-attachment XDP - device remove...")
674 sim.set_ethtool_tc_offloads(True)
678 parser = argparse.ArgumentParser()
679 parser.add_argument("--log", help="output verbose log to given file")
680 args = parser.parse_args()
682 logfile = open(args.log, 'w+')
683 logfile.write("# -*-Org-*-")
685 log("Prepare...", "", level=1)
689 skip(os.getuid() != 0, "test must be run as root")
692 ret, progs = bpftool("prog", fail=False)
693 skip(ret != 0, "bpftool not installed")
695 _, base_maps = bpftool("map")
698 ret, out = cmd("modprobe netdevsim", fail=False)
699 skip(ret != 0, "netdevsim module could not be loaded")
702 _, out = cmd("mount")
703 if out.find("/sys/kernel/debug type debugfs") == -1:
704 cmd("mount -t debugfs none /sys/kernel/debug")
706 # Check samples are compiled
707 samples = ["sample_ret0.o", "sample_map_ret0.o"]
709 ret, out = cmd("ls %s/%s" % (bpf_test_dir, s), fail=False)
710 skip(ret != 0, "sample %s/%s not found, please compile it" %
713 # Check if iproute2 is built with libmnl (needed by extack support)
714 _, _, err = cmd("tc qdisc delete dev lo handle 0",
715 fail=False, include_stderr=True)
716 if err.find("Error: Failed to find qdisc with specified handle.") == -1:
717 print("Warning: no extack message in iproute2 output, libmnl missing?")
718 log("Warning: no extack message in iproute2 output, libmnl missing?", "")
721 # Check if net namespaces seem to work
723 skip(ns is None, "Could not create a net namespace")
724 cmd("ip netns delete %s" % (ns))
728 obj = bpf_obj("sample_ret0.o")
729 bytecode = bpf_bytecode("1,6 0 0 4294967295,")
731 start_test("Test destruction of generic XDP...")
733 sim.set_xdp(obj, "generic")
735 bpftool_prog_list_wait(expected=0)
740 start_test("Test TC non-offloaded...")
741 ret, _ = sim.cls_bpf_add_filter(obj, skip_hw=True, fail=False)
742 fail(ret != 0, "Software TC filter did not load")
744 start_test("Test TC non-offloaded isn't getting bound...")
745 ret, _ = sim.cls_bpf_add_filter(obj, fail=False)
746 fail(ret != 0, "Software TC filter did not load")
747 sim.dfs_get_bound_progs(expected=0)
749 sim.tc_flush_filters()
751 start_test("Test TC offloads are off by default...")
752 ret, _, err = sim.cls_bpf_add_filter(obj, skip_sw=True,
753 fail=False, include_stderr=True)
754 fail(ret == 0, "TC filter loaded without enabling TC offloads")
755 check_extack(err, "TC offload is disabled on net device.", args)
758 sim.set_ethtool_tc_offloads(True)
759 sim.dfs["bpf_tc_non_bound_accept"] = "Y"
761 start_test("Test TC offload by default...")
762 ret, _ = sim.cls_bpf_add_filter(obj, fail=False)
763 fail(ret != 0, "Software TC filter did not load")
764 sim.dfs_get_bound_progs(expected=0)
765 ingress = sim.tc_show_ingress(expected=1)
767 fail(not fltr["in_hw"], "Filter not offloaded by default")
769 sim.tc_flush_filters()
771 start_test("Test TC cBPF bytcode tries offload by default...")
772 ret, _ = sim.cls_bpf_add_filter(bytecode, fail=False)
773 fail(ret != 0, "Software TC filter did not load")
774 sim.dfs_get_bound_progs(expected=0)
775 ingress = sim.tc_show_ingress(expected=1)
777 fail(not fltr["in_hw"], "Bytecode not offloaded by default")
779 sim.tc_flush_filters()
780 sim.dfs["bpf_tc_non_bound_accept"] = "N"
782 start_test("Test TC cBPF unbound bytecode doesn't offload...")
783 ret, _, err = sim.cls_bpf_add_filter(bytecode, skip_sw=True,
784 fail=False, include_stderr=True)
785 fail(ret == 0, "TC bytecode loaded for offload")
786 check_extack_nsim(err, "netdevsim configured to reject unbound programs.",
790 start_test("Test non-0 chain offload...")
791 ret, _, err = sim.cls_bpf_add_filter(obj, chain=1, prio=1, handle=1,
793 fail=False, include_stderr=True)
794 fail(ret == 0, "Offloaded a filter to chain other than 0")
795 check_extack(err, "Driver supports only offload of chain 0.", args)
796 sim.tc_flush_filters()
798 start_test("Test TC replace...")
799 sim.cls_bpf_add_filter(obj, prio=1, handle=1)
800 sim.cls_bpf_add_filter(obj, op="replace", prio=1, handle=1)
801 sim.cls_filter_op(op="delete", prio=1, handle=1, cls="bpf")
803 sim.cls_bpf_add_filter(obj, prio=1, handle=1, skip_sw=True)
804 sim.cls_bpf_add_filter(obj, op="replace", prio=1, handle=1, skip_sw=True)
805 sim.cls_filter_op(op="delete", prio=1, handle=1, cls="bpf")
807 sim.cls_bpf_add_filter(obj, prio=1, handle=1, skip_hw=True)
808 sim.cls_bpf_add_filter(obj, op="replace", prio=1, handle=1, skip_hw=True)
809 sim.cls_filter_op(op="delete", prio=1, handle=1, cls="bpf")
811 start_test("Test TC replace bad flags...")
814 ret, _ = sim.cls_bpf_add_filter(obj, op="replace", prio=1, handle=1,
815 skip_sw=(j == 1), skip_hw=(j == 2),
817 fail(bool(ret) != bool(j),
818 "Software TC incorrect load in replace test, iteration %d" %
820 sim.cls_filter_op(op="delete", prio=1, handle=1, cls="bpf")
822 start_test("Test spurious extack from the driver...")
823 test_spurios_extack(sim, obj, False, "netdevsim")
824 test_spurios_extack(sim, obj, True, "netdevsim")
826 sim.set_ethtool_tc_offloads(False)
828 test_spurios_extack(sim, obj, False, "TC offload is disabled")
829 test_spurios_extack(sim, obj, True, "TC offload is disabled")
831 sim.set_ethtool_tc_offloads(True)
833 sim.tc_flush_filters()
835 start_test("Test TC offloads work...")
836 ret, _, err = sim.cls_bpf_add_filter(obj, verbose=True, skip_sw=True,
837 fail=False, include_stderr=True)
838 fail(ret != 0, "TC filter did not load with TC offloads enabled")
839 check_verifier_log(err, "[netdevsim] Hello from netdevsim!")
841 start_test("Test TC offload basics...")
842 dfs = sim.dfs_get_bound_progs(expected=1)
843 progs = bpftool_prog_list(expected=1)
844 ingress = sim.tc_show_ingress(expected=1)
849 fail(fltr["skip_hw"], "TC does reports 'skip_hw' on offloaded filter")
850 fail(not fltr["in_hw"], "TC does not report 'in_hw' for offloaded filter")
851 fail(not fltr["skip_sw"], "TC does not report 'skip_sw' back")
853 start_test("Test TC offload is device-bound...")
854 fail(str(prog["id"]) != fltr["id"], "Program IDs don't match")
855 fail(prog["tag"] != fltr["tag"], "Program tags don't match")
856 fail(fltr["id"] != dprog["id"], "Program IDs don't match")
857 fail(dprog["state"] != "xlated", "Offloaded program state not translated")
858 fail(dprog["loaded"] != "Y", "Offloaded program is not loaded")
860 start_test("Test disabling TC offloads is rejected while filters installed...")
861 ret, _ = sim.set_ethtool_tc_offloads(False, fail=False)
862 fail(ret == 0, "Driver should refuse to disable TC offloads with filters installed...")
864 start_test("Test qdisc removal frees things...")
865 sim.tc_flush_filters()
866 sim.tc_show_ingress(expected=0)
868 start_test("Test disabling TC offloads is OK without filters...")
869 ret, _ = sim.set_ethtool_tc_offloads(False, fail=False)
871 "Driver refused to disable TC offloads without filters installed...")
873 sim.set_ethtool_tc_offloads(True)
875 start_test("Test destroying device gets rid of TC filters...")
876 sim.cls_bpf_add_filter(obj, skip_sw=True)
878 bpftool_prog_list_wait(expected=0)
881 sim.set_ethtool_tc_offloads(True)
883 start_test("Test destroying device gets rid of XDP...")
884 sim.set_xdp(obj, "offload")
886 bpftool_prog_list_wait(expected=0)
889 sim.set_ethtool_tc_offloads(True)
891 start_test("Test XDP prog reporting...")
892 sim.set_xdp(obj, "drv")
893 ipl = sim.ip_link_show(xdp=True)
894 progs = bpftool_prog_list(expected=1)
895 fail(ipl["xdp"]["prog"]["id"] != progs[0]["id"],
896 "Loaded program has wrong ID")
898 start_test("Test XDP prog replace without force...")
899 ret, _ = sim.set_xdp(obj, "drv", fail=False)
900 fail(ret == 0, "Replaced XDP program without -force")
901 sim.wait_for_flush(total=1)
903 start_test("Test XDP prog replace with force...")
904 ret, _ = sim.set_xdp(obj, "drv", force=True, fail=False)
905 fail(ret != 0, "Could not replace XDP program with -force")
906 bpftool_prog_list_wait(expected=1)
907 ipl = sim.ip_link_show(xdp=True)
908 progs = bpftool_prog_list(expected=1)
909 fail(ipl["xdp"]["prog"]["id"] != progs[0]["id"],
910 "Loaded program has wrong ID")
911 fail("dev" in progs[0].keys(),
912 "Device parameters reported for non-offloaded program")
914 start_test("Test XDP prog replace with bad flags...")
915 ret, _, err = sim.set_xdp(obj, "generic", force=True,
916 fail=False, include_stderr=True)
917 fail(ret == 0, "Replaced XDP program with a program in different mode")
919 "native and generic XDP can't be active at the same time.",
921 ret, _, err = sim.set_xdp(obj, "", force=True,
922 fail=False, include_stderr=True)
923 fail(ret == 0, "Replaced XDP program with a program in different mode")
924 check_extack(err, "program loaded with different flags.", args)
926 start_test("Test XDP prog remove with bad flags...")
927 ret, _, err = sim.unset_xdp("", force=True,
928 fail=False, include_stderr=True)
929 fail(ret == 0, "Removed program with a bad mode")
930 check_extack(err, "program loaded with different flags.", args)
932 start_test("Test MTU restrictions...")
933 ret, _ = sim.set_mtu(9000, fail=False)
935 "Driver should refuse to increase MTU to 9000 with XDP loaded...")
937 bpftool_prog_list_wait(expected=0)
939 ret, _, err = sim.set_xdp(obj, "drv", fail=False, include_stderr=True)
940 fail(ret == 0, "Driver should refuse to load program with MTU of 9000...")
941 check_extack_nsim(err, "MTU too large w/ XDP enabled.", args)
945 start_test("Test non-offload XDP attaching to HW...")
946 bpftool_prog_load("sample_ret0.o", "/sys/fs/bpf/nooffload")
947 nooffload = bpf_pinned("/sys/fs/bpf/nooffload")
948 ret, _, err = sim.set_xdp(nooffload, "offload",
949 fail=False, include_stderr=True)
950 fail(ret == 0, "attached non-offloaded XDP program to HW")
951 check_extack_nsim(err, "xdpoffload of non-bound program.", args)
952 rm("/sys/fs/bpf/nooffload")
954 start_test("Test offload XDP attaching to drv...")
955 bpftool_prog_load("sample_ret0.o", "/sys/fs/bpf/offload",
957 offload = bpf_pinned("/sys/fs/bpf/offload")
958 ret, _, err = sim.set_xdp(offload, "drv", fail=False, include_stderr=True)
959 fail(ret == 0, "attached offloaded XDP program to drv")
960 check_extack(err, "using device-bound program without HW_MODE flag is not supported.", args)
961 rm("/sys/fs/bpf/offload")
964 start_test("Test XDP offload...")
965 _, _, err = sim.set_xdp(obj, "offload", verbose=True, include_stderr=True)
966 ipl = sim.ip_link_show(xdp=True)
967 link_xdp = ipl["xdp"]["prog"]
968 progs = bpftool_prog_list(expected=1)
970 fail(link_xdp["id"] != prog["id"], "Loaded program has wrong ID")
971 check_verifier_log(err, "[netdevsim] Hello from netdevsim!")
973 start_test("Test XDP offload is device bound...")
974 dfs = sim.dfs_get_bound_progs(expected=1)
977 fail(prog["id"] != link_xdp["id"], "Program IDs don't match")
978 fail(prog["tag"] != link_xdp["tag"], "Program tags don't match")
979 fail(str(link_xdp["id"]) != dprog["id"], "Program IDs don't match")
980 fail(dprog["state"] != "xlated", "Offloaded program state not translated")
981 fail(dprog["loaded"] != "Y", "Offloaded program is not loaded")
983 start_test("Test removing XDP program many times...")
984 sim.unset_xdp("offload")
985 sim.unset_xdp("offload")
990 bpftool_prog_list_wait(expected=0)
992 start_test("Test attempt to use a program for a wrong device...")
994 sim2.set_xdp(obj, "offload")
995 pin_file, pinned = pin_prog("/sys/fs/bpf/tmp")
997 ret, _, err = sim.set_xdp(pinned, "offload",
998 fail=False, include_stderr=True)
999 fail(ret == 0, "Pinned program loaded for a different device accepted")
1000 check_extack_nsim(err, "program bound to different dev.", args)
1002 ret, _, err = sim.set_xdp(pinned, "offload",
1003 fail=False, include_stderr=True)
1004 fail(ret == 0, "Pinned program loaded for a removed device accepted")
1005 check_extack_nsim(err, "xdpoffload of non-bound program.", args)
1007 bpftool_prog_list_wait(expected=0)
1009 sim = test_multi_prog(sim, obj, "", 1)
1010 sim = test_multi_prog(sim, obj, "drv", 1)
1011 sim = test_multi_prog(sim, obj, "generic", 2)
1013 start_test("Test mixing of TC and XDP...")
1014 sim.tc_add_ingress()
1015 sim.set_xdp(obj, "offload")
1016 ret, _, err = sim.cls_bpf_add_filter(obj, skip_sw=True,
1017 fail=False, include_stderr=True)
1018 fail(ret == 0, "Loading TC when XDP active should fail")
1019 check_extack_nsim(err, "driver and netdev offload states mismatch.", args)
1020 sim.unset_xdp("offload")
1021 sim.wait_for_flush()
1023 sim.cls_bpf_add_filter(obj, skip_sw=True)
1024 ret, _, err = sim.set_xdp(obj, "offload", fail=False, include_stderr=True)
1025 fail(ret == 0, "Loading XDP when TC active should fail")
1026 check_extack_nsim(err, "TC program is already loaded.", args)
1028 start_test("Test binding TC from pinned...")
1029 pin_file, pinned = pin_prog("/sys/fs/bpf/tmp")
1030 sim.tc_flush_filters(bound=1, total=1)
1031 sim.cls_bpf_add_filter(pinned, da=True, skip_sw=True)
1032 sim.tc_flush_filters(bound=1, total=1)
1034 start_test("Test binding XDP from pinned...")
1035 sim.set_xdp(obj, "offload")
1036 pin_file, pinned = pin_prog("/sys/fs/bpf/tmp2", idx=1)
1038 sim.set_xdp(pinned, "offload", force=True)
1039 sim.unset_xdp("offload")
1040 sim.set_xdp(pinned, "offload", force=True)
1041 sim.unset_xdp("offload")
1043 start_test("Test offload of wrong type fails...")
1044 ret, _ = sim.cls_bpf_add_filter(pinned, da=True, skip_sw=True, fail=False)
1045 fail(ret == 0, "Managed to attach XDP program to TC")
1047 start_test("Test asking for TC offload of two filters...")
1048 sim.cls_bpf_add_filter(obj, da=True, skip_sw=True)
1049 ret, _, err = sim.cls_bpf_add_filter(obj, da=True, skip_sw=True,
1050 fail=False, include_stderr=True)
1051 fail(ret == 0, "Managed to offload two TC filters at the same time")
1052 check_extack_nsim(err, "driver and netdev offload states mismatch.", args)
1054 sim.tc_flush_filters(bound=2, total=2)
1056 start_test("Test if netdev removal waits for translation...")
1058 sim.dfs["sdev/bpf_bind_verifier_delay"] = delay_msec
1060 cmd_line = "tc filter add dev %s ingress bpf %s da skip_sw" % \
1061 (sim['ifname'], obj)
1062 tc_proc = cmd(cmd_line, background=True, fail=False)
1063 # Wait for the verifier to start
1064 while sim.dfs_num_bound_progs() <= 2:
1068 ret, _ = cmd_result(tc_proc, fail=False)
1069 time_diff = end - start
1070 log("Time", "start:\t%s\nend:\t%s\ndiff:\t%s" % (start, end, time_diff))
1072 fail(ret == 0, "Managed to load TC filter on a unregistering device")
1073 delay_sec = delay_msec * 0.001
1074 fail(time_diff < delay_sec, "Removal process took %s, expected %s" %
1075 (time_diff, delay_sec))
1077 # Remove all pinned files and reinstantiate the netdev
1079 bpftool_prog_list_wait(expected=0)
1082 map_obj = bpf_obj("sample_map_ret0.o")
1083 start_test("Test loading program with maps...")
1084 sim.set_xdp(map_obj, "offload", JSON=False) # map fixup msg breaks JSON
1086 start_test("Test bpftool bound info reporting (own ns)...")
1087 check_dev_info(False, "")
1089 start_test("Test bpftool bound info reporting (other ns)...")
1092 check_dev_info(True, "")
1094 start_test("Test bpftool bound info reporting (remote ns)...")
1095 check_dev_info(False, ns)
1097 start_test("Test bpftool bound info reporting (back to own ns)...")
1099 check_dev_info(False, "")
1101 prog_file, _ = pin_prog("/sys/fs/bpf/tmp_prog")
1102 map_file, _ = pin_map("/sys/fs/bpf/tmp_map", idx=1, expected=2)
1105 start_test("Test bpftool bound info reporting (removed dev)...")
1106 check_dev_info_removed(prog_file=prog_file, map_file=map_file)
1108 # Remove all pinned files and reinstantiate the netdev
1110 bpftool_prog_list_wait(expected=0)
1114 start_test("Test map update (no flags)...")
1115 sim.set_xdp(map_obj, "offload", JSON=False) # map fixup msg breaks JSON
1116 maps = bpftool_map_list(expected=2)
1117 array = maps[0] if maps[0]["type"] == "array" else maps[1]
1118 htab = maps[0] if maps[0]["type"] == "hash" else maps[1]
1121 bpftool("map update id %d key %s value %s" %
1122 (m["id"], int2str("I", i), int2str("Q", i * 3)))
1125 ret, _ = bpftool("map update id %d key %s value %s" %
1126 (m["id"], int2str("I", 3), int2str("Q", 3 * 3)),
1128 fail(ret == 0, "added too many entries")
1130 start_test("Test map update (exists)...")
1133 bpftool("map update id %d key %s value %s exist" %
1134 (m["id"], int2str("I", i), int2str("Q", i * 3)))
1137 ret, err = bpftool("map update id %d key %s value %s exist" %
1138 (m["id"], int2str("I", 3), int2str("Q", 3 * 3)),
1140 fail(ret == 0, "updated non-existing key")
1141 fail(err["error"].find("No such file or directory") == -1,
1142 "expected ENOENT, error is '%s'" % (err["error"]))
1144 start_test("Test map update (noexist)...")
1147 ret, err = bpftool("map update id %d key %s value %s noexist" %
1148 (m["id"], int2str("I", i), int2str("Q", i * 3)),
1150 fail(ret == 0, "updated existing key")
1151 fail(err["error"].find("File exists") == -1,
1152 "expected EEXIST, error is '%s'" % (err["error"]))
1154 start_test("Test map dump...")
1156 _, entries = bpftool("map dump id %d" % (m["id"]))
1158 key = str2int(entries[i]["key"])
1159 fail(key != i, "expected key %d, got %d" % (key, i))
1160 val = str2int(entries[i]["value"])
1161 fail(val != i * 3, "expected value %d, got %d" % (val, i * 3))
1163 start_test("Test map getnext...")
1165 _, entry = bpftool("map getnext id %d" % (m["id"]))
1166 key = str2int(entry["next_key"])
1167 fail(key != 0, "next key %d, expected %d" % (key, 0))
1168 _, entry = bpftool("map getnext id %d key %s" %
1169 (m["id"], int2str("I", 0)))
1170 key = str2int(entry["next_key"])
1171 fail(key != 1, "next key %d, expected %d" % (key, 1))
1172 ret, err = bpftool("map getnext id %d key %s" %
1173 (m["id"], int2str("I", 1)), fail=False)
1174 fail(ret == 0, "got next key past the end of map")
1175 fail(err["error"].find("No such file or directory") == -1,
1176 "expected ENOENT, error is '%s'" % (err["error"]))
1178 start_test("Test map delete (htab)...")
1180 bpftool("map delete id %d key %s" % (htab["id"], int2str("I", i)))
1182 start_test("Test map delete (array)...")
1184 ret, err = bpftool("map delete id %d key %s" %
1185 (htab["id"], int2str("I", i)), fail=False)
1186 fail(ret == 0, "removed entry from an array")
1187 fail(err["error"].find("No such file or directory") == -1,
1188 "expected ENOENT, error is '%s'" % (err["error"]))
1190 start_test("Test map remove...")
1191 sim.unset_xdp("offload")
1192 bpftool_map_list_wait(expected=0)
1196 sim.set_xdp(map_obj, "offload", JSON=False) # map fixup msg breaks JSON
1198 bpftool_map_list_wait(expected=0)
1200 start_test("Test map creation fail path...")
1202 sim.dfs["bpf_map_accept"] = "N"
1203 ret, _ = sim.set_xdp(map_obj, "offload", JSON=False, fail=False)
1205 "netdevsim didn't refuse to create a map with offload disabled")
1209 start_test("Test multi-dev ASIC program reuse...")
1212 simB2 = NetdevSim(link=simB1)
1213 simB3 = NetdevSim(link=simB1)
1214 sims = (simA, simB1, simB2, simB3)
1215 simB = (simB1, simB2, simB3)
1217 bpftool_prog_load("sample_map_ret0.o", "/sys/fs/bpf/nsimA",
1219 progA = bpf_pinned("/sys/fs/bpf/nsimA")
1220 bpftool_prog_load("sample_map_ret0.o", "/sys/fs/bpf/nsimB",
1221 dev=simB1['ifname'])
1222 progB = bpf_pinned("/sys/fs/bpf/nsimB")
1224 simA.set_xdp(progA, "offload", JSON=False)
1226 d.set_xdp(progB, "offload", JSON=False)
1228 start_test("Test multi-dev ASIC cross-dev replace...")
1229 ret, _ = simA.set_xdp(progB, "offload", force=True, JSON=False, fail=False)
1230 fail(ret == 0, "cross-ASIC program allowed")
1232 ret, _ = d.set_xdp(progA, "offload", force=True, JSON=False, fail=False)
1233 fail(ret == 0, "cross-ASIC program allowed")
1235 start_test("Test multi-dev ASIC cross-dev install...")
1237 d.unset_xdp("offload")
1239 ret, _, err = simA.set_xdp(progB, "offload", force=True, JSON=False,
1240 fail=False, include_stderr=True)
1241 fail(ret == 0, "cross-ASIC program allowed")
1242 check_extack_nsim(err, "program bound to different dev.", args)
1244 ret, _, err = d.set_xdp(progA, "offload", force=True, JSON=False,
1245 fail=False, include_stderr=True)
1246 fail(ret == 0, "cross-ASIC program allowed")
1247 check_extack_nsim(err, "program bound to different dev.", args)
1249 start_test("Test multi-dev ASIC cross-dev map reuse...")
1251 mapA = bpftool("prog show %s" % (progA))[1]["map_ids"][0]
1252 mapB = bpftool("prog show %s" % (progB))[1]["map_ids"][0]
1254 ret, _ = bpftool_prog_load("sample_map_ret0.o", "/sys/fs/bpf/nsimB_",
1255 dev=simB3['ifname'],
1256 maps=["idx 0 id %d" % (mapB)],
1258 fail(ret != 0, "couldn't reuse a map on the same ASIC")
1259 rm("/sys/fs/bpf/nsimB_")
1261 ret, _, err = bpftool_prog_load("sample_map_ret0.o", "/sys/fs/bpf/nsimA_",
1263 maps=["idx 0 id %d" % (mapB)],
1264 fail=False, include_stderr=True)
1265 fail(ret == 0, "could reuse a map on a different ASIC")
1266 fail(err.count("offload device mismatch between prog and map") == 0,
1267 "error message missing for cross-ASIC map")
1269 ret, _, err = bpftool_prog_load("sample_map_ret0.o", "/sys/fs/bpf/nsimB_",
1270 dev=simB1['ifname'],
1271 maps=["idx 0 id %d" % (mapA)],
1272 fail=False, include_stderr=True)
1273 fail(ret == 0, "could reuse a map on a different ASIC")
1274 fail(err.count("offload device mismatch between prog and map") == 0,
1275 "error message missing for cross-ASIC map")
1277 start_test("Test multi-dev ASIC cross-dev destruction...")
1278 bpftool_prog_list_wait(expected=2)
1281 bpftool_prog_list_wait(expected=1)
1283 ifnameB = bpftool("prog show %s" % (progB))[1]["dev"]["ifname"]
1284 fail(ifnameB != simB1['ifname'], "program not bound to originial device")
1286 bpftool_prog_list_wait(expected=1)
1288 start_test("Test multi-dev ASIC cross-dev destruction - move...")
1289 ifnameB = bpftool("prog show %s" % (progB))[1]["dev"]["ifname"]
1290 fail(ifnameB not in (simB2['ifname'], simB3['ifname']),
1291 "program not bound to remaining devices")
1294 ifnameB = bpftool("prog show %s" % (progB))[1]["dev"]["ifname"]
1295 fail(ifnameB != simB3['ifname'], "program not bound to remaining device")
1298 bpftool_prog_list_wait(expected=0)
1300 start_test("Test multi-dev ASIC cross-dev destruction - orphaned...")
1301 ret, out = bpftool("prog show %s" % (progB), fail=False)
1302 fail(ret == 0, "got information about orphaned program")
1303 fail("error" not in out, "no error reported for get info on orphaned")
1304 fail(out["error"] != "can't get prog info: No such device",
1305 "wrong error for get info on orphaned")
1307 print("%s: OK" % (os.path.basename(__file__)))
1310 log("Clean up...", "", level=1)