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
30 bpf_test_dir = os.path.dirname(os.path.realpath(__file__))
31 pp = pprint.PrettyPrinter()
32 devs = [] # devices we created for clean up
33 files = [] # files to be removed
34 netns = [] # net namespaces to be removed
36 def log_get_sec(level=0):
37 return "*" * (log_level + level)
39 def log_level_inc(add=1):
43 def log_level_dec(sub=1):
47 def log_level_set(level):
51 def log(header, data, level=None):
53 Output to an optional log.
60 if not isinstance(data, str):
61 data = pp.pformat(data)
64 logfile.write("\n" + log_get_sec() + " ")
66 if len(header) and len(data.strip()):
74 log("SKIP: " + msg, "", level=1)
81 log("FAIL: " + msg, "", level=1)
89 def cmd(cmd, shell=True, include_stderr=False, background=False, fail=True):
91 Run a command in subprocess and return tuple of (retval, stdout);
92 optionally return stderr as well as third value.
94 proc = subprocess.Popen(cmd, shell=shell, stdout=subprocess.PIPE,
95 stderr=subprocess.PIPE)
97 msg = "%s START: %s" % (log_get_sec(1),
98 datetime.now().strftime("%H:%M:%S.%f"))
99 log("BKG " + proc.args, msg)
102 return cmd_result(proc, include_stderr=include_stderr, fail=fail)
104 def cmd_result(proc, include_stderr=False, fail=False):
105 stdout, stderr = proc.communicate()
106 stdout = stdout.decode("utf-8")
107 stderr = stderr.decode("utf-8")
111 stderr = "\n" + stderr
112 if stderr[-1] == "\n":
116 log("CMD " + proc.args,
117 "RETCODE: %d\n%s STDOUT:\n%s%s STDERR:%s\n%s END: %s" %
118 (proc.returncode, sec, stdout, sec, stderr,
119 sec, datetime.now().strftime("%H:%M:%S.%f")))
121 if proc.returncode != 0 and fail:
122 if len(stderr) > 0 and stderr[-1] == "\n":
124 raise Exception("Command failed: %s\n%s" % (proc.args, stderr))
127 return proc.returncode, stdout, stderr
129 return proc.returncode, stdout
132 cmd("rm -f %s" % (f))
136 def tool(name, args, flags, JSON=True, ns="", fail=True, include_stderr=False):
139 params += "%s " % (flags["json"])
142 ns = "ip netns exec %s " % (ns)
145 ret, stdout, stderr = cmd(ns + name + " " + params + args,
146 fail=fail, include_stderr=True)
148 ret, stdout = cmd(ns + name + " " + params + args,
149 fail=fail, include_stderr=False)
151 if JSON and len(stdout.strip()) != 0:
152 out = json.loads(stdout)
157 return ret, out, stderr
161 def bpftool(args, JSON=True, ns="", fail=True):
162 return tool("bpftool", args, {"json":"-p"}, JSON=JSON, ns=ns, fail=fail)
164 def bpftool_prog_list(expected=None, ns=""):
165 _, progs = bpftool("prog show", JSON=True, ns=ns, fail=True)
166 # Remove the base progs
170 if expected is not None:
171 if len(progs) != expected:
172 fail(True, "%d BPF programs loaded, expected %d" %
173 (len(progs), expected))
176 def bpftool_map_list(expected=None, ns=""):
177 _, maps = bpftool("map show", JSON=True, ns=ns, fail=True)
178 # Remove the base maps
182 if expected is not None:
183 if len(maps) != expected:
184 fail(True, "%d BPF maps loaded, expected %d" %
185 (len(maps), expected))
188 def bpftool_prog_list_wait(expected=0, n_retry=20):
189 for i in range(n_retry):
190 nprogs = len(bpftool_prog_list())
191 if nprogs == expected:
194 raise Exception("Time out waiting for program counts to stabilize want %d, have %d" % (expected, nprogs))
196 def bpftool_map_list_wait(expected=0, n_retry=20):
197 for i in range(n_retry):
198 nmaps = len(bpftool_map_list())
199 if nmaps == expected:
202 raise Exception("Time out waiting for map counts to stabilize want %d, have %d" % (expected, nmaps))
204 def ip(args, force=False, JSON=True, ns="", fail=True, include_stderr=False):
206 args = "-force " + args
207 return tool("ip", args, {"json":"-j"}, JSON=JSON, ns=ns,
208 fail=fail, include_stderr=include_stderr)
210 def tc(args, JSON=True, ns="", fail=True, include_stderr=False):
211 return tool("tc", args, {"json":"-p"}, JSON=JSON, ns=ns,
212 fail=fail, include_stderr=include_stderr)
214 def ethtool(dev, opt, args, fail=True):
215 return cmd("ethtool %s %s %s" % (opt, dev["ifname"], args), fail=fail)
217 def bpf_obj(name, sec=".text", path=bpf_test_dir,):
218 return "obj %s sec %s" % (os.path.join(path, name), sec)
220 def bpf_pinned(name):
221 return "pinned %s" % (name)
223 def bpf_bytecode(bytecode):
224 return "bytecode \"%s\"" % (bytecode)
226 def mknetns(n_retry=10):
227 for i in range(n_retry):
228 name = ''.join([random.choice(string.ascii_letters) for i in range(8)])
229 ret, _ = ip("netns add %s" % (name), fail=False)
235 def int2str(fmt, val):
237 for b in struct.pack(fmt, val):
239 return " ".join(map(lambda x: str(x), ret))
244 inttab.append(int(i, 16))
245 ba = bytearray(inttab)
248 elif len(strtab) == 8:
251 raise Exception("String array of len %d can't be unpacked to an int" %
253 return struct.unpack(fmt, ba)[0]
257 Class for accessing DebugFS directories as a dictionary.
260 def __init__(self, path):
262 self._dict = self._debugfs_dir_read(path)
265 return len(self._dict.keys())
267 def __getitem__(self, key):
269 key = list(self._dict.keys())[key]
270 return self._dict[key]
272 def __setitem__(self, key, value):
273 log("DebugFS set %s = %s" % (key, value), "")
276 cmd("echo '%s' > %s/%s" % (value, self.path, key))
279 _, out = cmd('cat %s/%s' % (self.path, key))
280 self._dict[key] = out.strip()
282 def _debugfs_dir_read(self, path):
285 log("DebugFS state for %s" % (path), "")
288 _, out = cmd('ls ' + path)
289 for f in out.split():
290 p = os.path.join(path, f)
291 if os.path.isfile(p):
292 _, out = cmd('cat %s/%s' % (path, f))
294 elif os.path.isdir(p):
295 dfs[f] = DebugfsDir(p)
297 raise Exception("%s is neither file nor directory" % (p))
300 log("DebugFS state", dfs)
307 Class for netdevsim netdevice and its attributes.
311 self.dev = self._netdevsim_create()
316 self.dfs_dir = '/sys/kernel/debug/netdevsim/%s' % (self.dev['ifname'])
319 def __getitem__(self, key):
322 def _netdevsim_create(self):
323 _, old = ip("link show")
324 ip("link add sim%d type netdevsim")
325 _, new = ip("link show")
328 f = filter(lambda x: x["ifname"] == dev["ifname"], old)
329 if len(list(f)) == 0:
332 raise Exception("failed to create netdevsim device")
336 ip("link del dev %s" % (self.dev["ifname"]), ns=self.ns)
338 def dfs_refresh(self):
339 self.dfs = DebugfsDir(self.dfs_dir)
342 def dfs_num_bound_progs(self):
343 path = os.path.join(self.dfs_dir, "bpf_bound_progs")
344 _, progs = cmd('ls %s' % (path))
345 return len(progs.split())
347 def dfs_get_bound_progs(self, expected):
348 progs = DebugfsDir(os.path.join(self.dfs_dir, "bpf_bound_progs"))
349 if expected is not None:
350 if len(progs) != expected:
351 fail(True, "%d BPF programs bound, expected %d" %
352 (len(progs), expected))
355 def wait_for_flush(self, bound=0, total=0, n_retry=20):
356 for i in range(n_retry):
357 nbound = self.dfs_num_bound_progs()
358 nprogs = len(bpftool_prog_list())
359 if nbound == bound and nprogs == total:
362 raise Exception("Time out waiting for program counts to stabilize want %d/%d, have %d bound, %d loaded" % (bound, total, nbound, nprogs))
364 def set_ns(self, ns):
365 name = "1" if ns == "" else ns
366 ip("link set dev %s netns %s" % (self.dev["ifname"], name), ns=self.ns)
369 def set_mtu(self, mtu, fail=True):
370 return ip("link set dev %s mtu %d" % (self.dev["ifname"], mtu),
373 def set_xdp(self, bpf, mode, force=False, JSON=True, verbose=False,
374 fail=True, include_stderr=False):
377 return ip("link set dev %s xdp%s %s" % (self.dev["ifname"], mode, bpf),
378 force=force, JSON=JSON,
379 fail=fail, include_stderr=include_stderr)
381 def unset_xdp(self, mode, force=False, JSON=True,
382 fail=True, include_stderr=False):
383 return ip("link set dev %s xdp%s off" % (self.dev["ifname"], mode),
384 force=force, JSON=JSON,
385 fail=fail, include_stderr=include_stderr)
387 def ip_link_show(self, xdp):
388 _, link = ip("link show dev %s" % (self['ifname']))
390 raise Exception("Multiple objects on ip link show")
393 fail(xdp != "xdp" in link,
394 "XDP program not reporting in iplink (reported %s, expected %s)" %
395 ("xdp" in link, xdp))
398 def tc_add_ingress(self):
399 tc("qdisc add dev %s ingress" % (self['ifname']))
401 def tc_del_ingress(self):
402 tc("qdisc del dev %s ingress" % (self['ifname']))
404 def tc_flush_filters(self, bound=0, total=0):
405 self.tc_del_ingress()
406 self.tc_add_ingress()
407 self.wait_for_flush(bound=bound, total=total)
409 def tc_show_ingress(self, expected=None):
410 # No JSON support, oh well...
411 flags = ["skip_sw", "skip_hw", "in_hw"]
412 named = ["protocol", "pref", "chain", "handle", "id", "tag"]
414 args = "-s filter show dev %s ingress" % (self['ifname'])
415 _, out = tc(args, JSON=False)
418 lines = out.split('\n')
421 if "handle" not in words:
425 fltr[flag] = flag in words
428 idx = words.index(name)
429 fltr[name] = words[idx + 1]
434 if expected is not None:
435 fail(len(filters) != expected,
436 "%d ingress filters loaded, expected %d" %
437 (len(filters), expected))
440 def cls_filter_op(self, op, qdisc="ingress", prio=None, handle=None,
441 chain=None, cls="", params="",
442 fail=True, include_stderr=False):
445 spec += " prio %d" % (prio)
447 spec += " handle %s" % (handle)
448 if chain is not None:
449 spec += " chain %d" % (chain)
451 return tc("filter {op} dev {dev} {qdisc} {spec} {cls} {params}"\
452 .format(op=op, dev=self['ifname'], qdisc=qdisc, spec=spec,
453 cls=cls, params=params),
454 fail=fail, include_stderr=include_stderr)
456 def cls_bpf_add_filter(self, bpf, op="add", prio=None, handle=None,
457 chain=None, da=False, verbose=False,
458 skip_sw=False, skip_hw=False,
459 fail=True, include_stderr=False):
472 return self.cls_filter_op(op=op, prio=prio, handle=handle, cls=cls,
473 chain=chain, params=params,
474 fail=fail, include_stderr=include_stderr)
476 def set_ethtool_tc_offloads(self, enable, fail=True):
477 args = "hw-tc-offload %s" % ("on" if enable else "off")
478 return ethtool(self, "-K", args, fail=fail)
480 ################################################################################
482 global files, netns, devs
487 cmd("rm -f %s" % (f))
489 cmd("ip netns delete %s" % (ns))
493 def pin_prog(file_name, idx=0):
494 progs = bpftool_prog_list(expected=(idx + 1))
496 bpftool("prog pin id %d %s" % (prog["id"], file_name))
497 files.append(file_name)
499 return file_name, bpf_pinned(file_name)
501 def pin_map(file_name, idx=0, expected=1):
502 maps = bpftool_map_list(expected=expected)
504 bpftool("map pin id %d %s" % (m["id"], file_name))
505 files.append(file_name)
507 return file_name, bpf_pinned(file_name)
509 def check_dev_info_removed(prog_file=None, map_file=None):
510 bpftool_prog_list(expected=0)
511 ret, err = bpftool("prog show pin %s" % (prog_file), fail=False)
512 fail(ret == 0, "Showing prog with removed device did not fail")
513 fail(err["error"].find("No such device") == -1,
514 "Showing prog with removed device expected ENODEV, error is %s" %
517 bpftool_map_list(expected=0)
518 ret, err = bpftool("map show pin %s" % (map_file), fail=False)
519 fail(ret == 0, "Showing map with removed device did not fail")
520 fail(err["error"].find("No such device") == -1,
521 "Showing map with removed device expected ENODEV, error is %s" %
524 def check_dev_info(other_ns, ns, prog_file=None, map_file=None, removed=False):
525 progs = bpftool_prog_list(expected=1, ns=ns)
528 fail("dev" not in prog.keys(), "Device parameters not reported")
530 fail("ifindex" not in dev.keys(), "Device parameters not reported")
531 fail("ns_dev" not in dev.keys(), "Device parameters not reported")
532 fail("ns_inode" not in dev.keys(), "Device parameters not reported")
535 fail("ifname" not in dev.keys(), "Ifname not reported")
536 fail(dev["ifname"] != sim["ifname"],
537 "Ifname incorrect %s vs %s" % (dev["ifname"], sim["ifname"]))
539 fail("ifname" in dev.keys(), "Ifname is reported for other ns")
541 maps = bpftool_map_list(expected=2, ns=ns)
543 fail("dev" not in m.keys(), "Device parameters not reported")
544 fail(dev != m["dev"], "Map's device different than program's")
546 def check_extack(output, reference, args):
549 lines = output.split("\n")
550 comp = len(lines) >= 2 and lines[1] == 'Error: ' + reference
551 fail(not comp, "Missing or incorrect netlink extack message")
553 def check_extack_nsim(output, reference, args):
554 check_extack(output, "netdevsim: " + reference, args)
556 def check_no_extack(res, needle):
557 fail((res[1] + res[2]).count(needle) or (res[1] + res[2]).count("Warning:"),
558 "Found '%s' in command output, leaky extack?" % (needle))
560 def check_verifier_log(output, reference):
561 lines = output.split("\n")
562 for l in reversed(lines):
565 fail(True, "Missing or incorrect message from netdevsim in verifier log")
567 def test_spurios_extack(sim, obj, skip_hw, needle):
568 res = sim.cls_bpf_add_filter(obj, prio=1, handle=1, skip_hw=skip_hw,
570 check_no_extack(res, needle)
571 res = sim.cls_bpf_add_filter(obj, op="replace", prio=1, handle=1,
572 skip_hw=skip_hw, include_stderr=True)
573 check_no_extack(res, needle)
574 res = sim.cls_filter_op(op="delete", prio=1, handle=1, cls="bpf",
576 check_no_extack(res, needle)
580 parser = argparse.ArgumentParser()
581 parser.add_argument("--log", help="output verbose log to given file")
582 args = parser.parse_args()
584 logfile = open(args.log, 'w+')
585 logfile.write("# -*-Org-*-")
587 log("Prepare...", "", level=1)
591 skip(os.getuid() != 0, "test must be run as root")
594 ret, progs = bpftool("prog", fail=False)
595 skip(ret != 0, "bpftool not installed")
597 _, base_maps = bpftool("map")
600 ret, out = cmd("modprobe netdevsim", fail=False)
601 skip(ret != 0, "netdevsim module could not be loaded")
604 _, out = cmd("mount")
605 if out.find("/sys/kernel/debug type debugfs") == -1:
606 cmd("mount -t debugfs none /sys/kernel/debug")
608 # Check samples are compiled
609 samples = ["sample_ret0.o", "sample_map_ret0.o"]
611 ret, out = cmd("ls %s/%s" % (bpf_test_dir, s), fail=False)
612 skip(ret != 0, "sample %s/%s not found, please compile it" %
615 # Check if iproute2 is built with libmnl (needed by extack support)
616 _, _, err = cmd("tc qdisc delete dev lo handle 0",
617 fail=False, include_stderr=True)
618 if err.find("Error: Failed to find qdisc with specified handle.") == -1:
619 print("Warning: no extack message in iproute2 output, libmnl missing?")
620 log("Warning: no extack message in iproute2 output, libmnl missing?", "")
623 # Check if net namespaces seem to work
625 skip(ns is None, "Could not create a net namespace")
626 cmd("ip netns delete %s" % (ns))
630 obj = bpf_obj("sample_ret0.o")
631 bytecode = bpf_bytecode("1,6 0 0 4294967295,")
633 start_test("Test destruction of generic XDP...")
635 sim.set_xdp(obj, "generic")
637 bpftool_prog_list_wait(expected=0)
642 start_test("Test TC non-offloaded...")
643 ret, _ = sim.cls_bpf_add_filter(obj, skip_hw=True, fail=False)
644 fail(ret != 0, "Software TC filter did not load")
646 start_test("Test TC non-offloaded isn't getting bound...")
647 ret, _ = sim.cls_bpf_add_filter(obj, fail=False)
648 fail(ret != 0, "Software TC filter did not load")
649 sim.dfs_get_bound_progs(expected=0)
651 sim.tc_flush_filters()
653 start_test("Test TC offloads are off by default...")
654 ret, _, err = sim.cls_bpf_add_filter(obj, skip_sw=True,
655 fail=False, include_stderr=True)
656 fail(ret == 0, "TC filter loaded without enabling TC offloads")
657 check_extack(err, "TC offload is disabled on net device.", args)
660 sim.set_ethtool_tc_offloads(True)
661 sim.dfs["bpf_tc_non_bound_accept"] = "Y"
663 start_test("Test TC offload by default...")
664 ret, _ = sim.cls_bpf_add_filter(obj, fail=False)
665 fail(ret != 0, "Software TC filter did not load")
666 sim.dfs_get_bound_progs(expected=0)
667 ingress = sim.tc_show_ingress(expected=1)
669 fail(not fltr["in_hw"], "Filter not offloaded by default")
671 sim.tc_flush_filters()
673 start_test("Test TC cBPF bytcode tries offload by default...")
674 ret, _ = sim.cls_bpf_add_filter(bytecode, fail=False)
675 fail(ret != 0, "Software TC filter did not load")
676 sim.dfs_get_bound_progs(expected=0)
677 ingress = sim.tc_show_ingress(expected=1)
679 fail(not fltr["in_hw"], "Bytecode not offloaded by default")
681 sim.tc_flush_filters()
682 sim.dfs["bpf_tc_non_bound_accept"] = "N"
684 start_test("Test TC cBPF unbound bytecode doesn't offload...")
685 ret, _, err = sim.cls_bpf_add_filter(bytecode, skip_sw=True,
686 fail=False, include_stderr=True)
687 fail(ret == 0, "TC bytecode loaded for offload")
688 check_extack_nsim(err, "netdevsim configured to reject unbound programs.",
692 start_test("Test non-0 chain offload...")
693 ret, _, err = sim.cls_bpf_add_filter(obj, chain=1, prio=1, handle=1,
695 fail=False, include_stderr=True)
696 fail(ret == 0, "Offloaded a filter to chain other than 0")
697 check_extack(err, "Driver supports only offload of chain 0.", args)
698 sim.tc_flush_filters()
700 start_test("Test TC replace...")
701 sim.cls_bpf_add_filter(obj, prio=1, handle=1)
702 sim.cls_bpf_add_filter(obj, op="replace", prio=1, handle=1)
703 sim.cls_filter_op(op="delete", prio=1, handle=1, cls="bpf")
705 sim.cls_bpf_add_filter(obj, prio=1, handle=1, skip_sw=True)
706 sim.cls_bpf_add_filter(obj, op="replace", prio=1, handle=1, skip_sw=True)
707 sim.cls_filter_op(op="delete", prio=1, handle=1, cls="bpf")
709 sim.cls_bpf_add_filter(obj, prio=1, handle=1, skip_hw=True)
710 sim.cls_bpf_add_filter(obj, op="replace", prio=1, handle=1, skip_hw=True)
711 sim.cls_filter_op(op="delete", prio=1, handle=1, cls="bpf")
713 start_test("Test TC replace bad flags...")
716 ret, _ = sim.cls_bpf_add_filter(obj, op="replace", prio=1, handle=1,
717 skip_sw=(j == 1), skip_hw=(j == 2),
719 fail(bool(ret) != bool(j),
720 "Software TC incorrect load in replace test, iteration %d" %
722 sim.cls_filter_op(op="delete", prio=1, handle=1, cls="bpf")
724 start_test("Test spurious extack from the driver...")
725 test_spurios_extack(sim, obj, False, "netdevsim")
726 test_spurios_extack(sim, obj, True, "netdevsim")
728 sim.set_ethtool_tc_offloads(False)
730 test_spurios_extack(sim, obj, False, "TC offload is disabled")
731 test_spurios_extack(sim, obj, True, "TC offload is disabled")
733 sim.set_ethtool_tc_offloads(True)
735 sim.tc_flush_filters()
737 start_test("Test TC offloads work...")
738 ret, _, err = sim.cls_bpf_add_filter(obj, verbose=True, skip_sw=True,
739 fail=False, include_stderr=True)
740 fail(ret != 0, "TC filter did not load with TC offloads enabled")
741 check_verifier_log(err, "[netdevsim] Hello from netdevsim!")
743 start_test("Test TC offload basics...")
744 dfs = sim.dfs_get_bound_progs(expected=1)
745 progs = bpftool_prog_list(expected=1)
746 ingress = sim.tc_show_ingress(expected=1)
751 fail(fltr["skip_hw"], "TC does reports 'skip_hw' on offloaded filter")
752 fail(not fltr["in_hw"], "TC does not report 'in_hw' for offloaded filter")
753 fail(not fltr["skip_sw"], "TC does not report 'skip_sw' back")
755 start_test("Test TC offload is device-bound...")
756 fail(str(prog["id"]) != fltr["id"], "Program IDs don't match")
757 fail(prog["tag"] != fltr["tag"], "Program tags don't match")
758 fail(fltr["id"] != dprog["id"], "Program IDs don't match")
759 fail(dprog["state"] != "xlated", "Offloaded program state not translated")
760 fail(dprog["loaded"] != "Y", "Offloaded program is not loaded")
762 start_test("Test disabling TC offloads is rejected while filters installed...")
763 ret, _ = sim.set_ethtool_tc_offloads(False, fail=False)
764 fail(ret == 0, "Driver should refuse to disable TC offloads with filters installed...")
766 start_test("Test qdisc removal frees things...")
767 sim.tc_flush_filters()
768 sim.tc_show_ingress(expected=0)
770 start_test("Test disabling TC offloads is OK without filters...")
771 ret, _ = sim.set_ethtool_tc_offloads(False, fail=False)
773 "Driver refused to disable TC offloads without filters installed...")
775 sim.set_ethtool_tc_offloads(True)
777 start_test("Test destroying device gets rid of TC filters...")
778 sim.cls_bpf_add_filter(obj, skip_sw=True)
780 bpftool_prog_list_wait(expected=0)
783 sim.set_ethtool_tc_offloads(True)
785 start_test("Test destroying device gets rid of XDP...")
786 sim.set_xdp(obj, "offload")
788 bpftool_prog_list_wait(expected=0)
791 sim.set_ethtool_tc_offloads(True)
793 start_test("Test XDP prog reporting...")
794 sim.set_xdp(obj, "drv")
795 ipl = sim.ip_link_show(xdp=True)
796 progs = bpftool_prog_list(expected=1)
797 fail(ipl["xdp"]["prog"]["id"] != progs[0]["id"],
798 "Loaded program has wrong ID")
800 start_test("Test XDP prog replace without force...")
801 ret, _ = sim.set_xdp(obj, "drv", fail=False)
802 fail(ret == 0, "Replaced XDP program without -force")
803 sim.wait_for_flush(total=1)
805 start_test("Test XDP prog replace with force...")
806 ret, _ = sim.set_xdp(obj, "drv", force=True, fail=False)
807 fail(ret != 0, "Could not replace XDP program with -force")
808 bpftool_prog_list_wait(expected=1)
809 ipl = sim.ip_link_show(xdp=True)
810 progs = bpftool_prog_list(expected=1)
811 fail(ipl["xdp"]["prog"]["id"] != progs[0]["id"],
812 "Loaded program has wrong ID")
813 fail("dev" in progs[0].keys(),
814 "Device parameters reported for non-offloaded program")
816 start_test("Test XDP prog replace with bad flags...")
817 ret, _, err = sim.set_xdp(obj, "", force=True,
818 fail=False, include_stderr=True)
819 fail(ret == 0, "Replaced XDP program with a program in different mode")
820 check_extack(err, "program loaded with different flags.", args)
822 start_test("Test XDP prog remove with bad flags...")
823 ret, _, err = sim.unset_xdp("", force=True,
824 fail=False, include_stderr=True)
825 fail(ret == 0, "Removed program with a bad mode")
826 check_extack(err, "program loaded with different flags.", args)
828 start_test("Test MTU restrictions...")
829 ret, _ = sim.set_mtu(9000, fail=False)
831 "Driver should refuse to increase MTU to 9000 with XDP loaded...")
833 bpftool_prog_list_wait(expected=0)
835 ret, _, err = sim.set_xdp(obj, "drv", fail=False, include_stderr=True)
836 fail(ret == 0, "Driver should refuse to load program with MTU of 9000...")
837 check_extack_nsim(err, "MTU too large w/ XDP enabled.", args)
841 start_test("Test XDP offload...")
842 _, _, err = sim.set_xdp(obj, "offload", verbose=True, include_stderr=True)
843 ipl = sim.ip_link_show(xdp=True)
844 link_xdp = ipl["xdp"]["prog"]
845 progs = bpftool_prog_list(expected=1)
847 fail(link_xdp["id"] != prog["id"], "Loaded program has wrong ID")
848 check_verifier_log(err, "[netdevsim] Hello from netdevsim!")
850 start_test("Test XDP offload is device bound...")
851 dfs = sim.dfs_get_bound_progs(expected=1)
854 fail(prog["id"] != link_xdp["id"], "Program IDs don't match")
855 fail(prog["tag"] != link_xdp["tag"], "Program tags don't match")
856 fail(str(link_xdp["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 removing XDP program many times...")
861 sim.unset_xdp("offload")
862 sim.unset_xdp("offload")
867 bpftool_prog_list_wait(expected=0)
869 start_test("Test attempt to use a program for a wrong device...")
871 sim2.set_xdp(obj, "offload")
872 pin_file, pinned = pin_prog("/sys/fs/bpf/tmp")
874 ret, _, err = sim.set_xdp(pinned, "offload",
875 fail=False, include_stderr=True)
876 fail(ret == 0, "Pinned program loaded for a different device accepted")
877 check_extack_nsim(err, "program bound to different dev.", args)
879 ret, _, err = sim.set_xdp(pinned, "offload",
880 fail=False, include_stderr=True)
881 fail(ret == 0, "Pinned program loaded for a removed device accepted")
882 check_extack_nsim(err, "xdpoffload of non-bound program.", args)
884 bpftool_prog_list_wait(expected=0)
886 start_test("Test mixing of TC and XDP...")
888 sim.set_xdp(obj, "offload")
889 ret, _, err = sim.cls_bpf_add_filter(obj, skip_sw=True,
890 fail=False, include_stderr=True)
891 fail(ret == 0, "Loading TC when XDP active should fail")
892 check_extack_nsim(err, "driver and netdev offload states mismatch.", args)
893 sim.unset_xdp("offload")
896 sim.cls_bpf_add_filter(obj, skip_sw=True)
897 ret, _, err = sim.set_xdp(obj, "offload", fail=False, include_stderr=True)
898 fail(ret == 0, "Loading XDP when TC active should fail")
899 check_extack_nsim(err, "TC program is already loaded.", args)
901 start_test("Test binding TC from pinned...")
902 pin_file, pinned = pin_prog("/sys/fs/bpf/tmp")
903 sim.tc_flush_filters(bound=1, total=1)
904 sim.cls_bpf_add_filter(pinned, da=True, skip_sw=True)
905 sim.tc_flush_filters(bound=1, total=1)
907 start_test("Test binding XDP from pinned...")
908 sim.set_xdp(obj, "offload")
909 pin_file, pinned = pin_prog("/sys/fs/bpf/tmp2", idx=1)
911 sim.set_xdp(pinned, "offload", force=True)
912 sim.unset_xdp("offload")
913 sim.set_xdp(pinned, "offload", force=True)
914 sim.unset_xdp("offload")
916 start_test("Test offload of wrong type fails...")
917 ret, _ = sim.cls_bpf_add_filter(pinned, da=True, skip_sw=True, fail=False)
918 fail(ret == 0, "Managed to attach XDP program to TC")
920 start_test("Test asking for TC offload of two filters...")
921 sim.cls_bpf_add_filter(obj, da=True, skip_sw=True)
922 ret, _, err = sim.cls_bpf_add_filter(obj, da=True, skip_sw=True,
923 fail=False, include_stderr=True)
924 fail(ret == 0, "Managed to offload two TC filters at the same time")
925 check_extack_nsim(err, "driver and netdev offload states mismatch.", args)
927 sim.tc_flush_filters(bound=2, total=2)
929 start_test("Test if netdev removal waits for translation...")
931 sim.dfs["bpf_bind_verifier_delay"] = delay_msec
933 cmd_line = "tc filter add dev %s ingress bpf %s da skip_sw" % \
935 tc_proc = cmd(cmd_line, background=True, fail=False)
936 # Wait for the verifier to start
937 while sim.dfs_num_bound_progs() <= 2:
941 ret, _ = cmd_result(tc_proc, fail=False)
942 time_diff = end - start
943 log("Time", "start:\t%s\nend:\t%s\ndiff:\t%s" % (start, end, time_diff))
945 fail(ret == 0, "Managed to load TC filter on a unregistering device")
946 delay_sec = delay_msec * 0.001
947 fail(time_diff < delay_sec, "Removal process took %s, expected %s" %
948 (time_diff, delay_sec))
950 # Remove all pinned files and reinstantiate the netdev
952 bpftool_prog_list_wait(expected=0)
955 map_obj = bpf_obj("sample_map_ret0.o")
956 start_test("Test loading program with maps...")
957 sim.set_xdp(map_obj, "offload", JSON=False) # map fixup msg breaks JSON
959 start_test("Test bpftool bound info reporting (own ns)...")
960 check_dev_info(False, "")
962 start_test("Test bpftool bound info reporting (other ns)...")
965 check_dev_info(True, "")
967 start_test("Test bpftool bound info reporting (remote ns)...")
968 check_dev_info(False, ns)
970 start_test("Test bpftool bound info reporting (back to own ns)...")
972 check_dev_info(False, "")
974 prog_file, _ = pin_prog("/sys/fs/bpf/tmp_prog")
975 map_file, _ = pin_map("/sys/fs/bpf/tmp_map", idx=1, expected=2)
978 start_test("Test bpftool bound info reporting (removed dev)...")
979 check_dev_info_removed(prog_file=prog_file, map_file=map_file)
981 # Remove all pinned files and reinstantiate the netdev
983 bpftool_prog_list_wait(expected=0)
987 start_test("Test map update (no flags)...")
988 sim.set_xdp(map_obj, "offload", JSON=False) # map fixup msg breaks JSON
989 maps = bpftool_map_list(expected=2)
990 array = maps[0] if maps[0]["type"] == "array" else maps[1]
991 htab = maps[0] if maps[0]["type"] == "hash" else maps[1]
994 bpftool("map update id %d key %s value %s" %
995 (m["id"], int2str("I", i), int2str("Q", i * 3)))
998 ret, _ = bpftool("map update id %d key %s value %s" %
999 (m["id"], int2str("I", 3), int2str("Q", 3 * 3)),
1001 fail(ret == 0, "added too many entries")
1003 start_test("Test map update (exists)...")
1006 bpftool("map update id %d key %s value %s exist" %
1007 (m["id"], int2str("I", i), int2str("Q", i * 3)))
1010 ret, err = bpftool("map update id %d key %s value %s exist" %
1011 (m["id"], int2str("I", 3), int2str("Q", 3 * 3)),
1013 fail(ret == 0, "updated non-existing key")
1014 fail(err["error"].find("No such file or directory") == -1,
1015 "expected ENOENT, error is '%s'" % (err["error"]))
1017 start_test("Test map update (noexist)...")
1020 ret, err = bpftool("map update id %d key %s value %s noexist" %
1021 (m["id"], int2str("I", i), int2str("Q", i * 3)),
1023 fail(ret == 0, "updated existing key")
1024 fail(err["error"].find("File exists") == -1,
1025 "expected EEXIST, error is '%s'" % (err["error"]))
1027 start_test("Test map dump...")
1029 _, entries = bpftool("map dump id %d" % (m["id"]))
1031 key = str2int(entries[i]["key"])
1032 fail(key != i, "expected key %d, got %d" % (key, i))
1033 val = str2int(entries[i]["value"])
1034 fail(val != i * 3, "expected value %d, got %d" % (val, i * 3))
1036 start_test("Test map getnext...")
1038 _, entry = bpftool("map getnext id %d" % (m["id"]))
1039 key = str2int(entry["next_key"])
1040 fail(key != 0, "next key %d, expected %d" % (key, 0))
1041 _, entry = bpftool("map getnext id %d key %s" %
1042 (m["id"], int2str("I", 0)))
1043 key = str2int(entry["next_key"])
1044 fail(key != 1, "next key %d, expected %d" % (key, 1))
1045 ret, err = bpftool("map getnext id %d key %s" %
1046 (m["id"], int2str("I", 1)), fail=False)
1047 fail(ret == 0, "got next key past the end of map")
1048 fail(err["error"].find("No such file or directory") == -1,
1049 "expected ENOENT, error is '%s'" % (err["error"]))
1051 start_test("Test map delete (htab)...")
1053 bpftool("map delete id %d key %s" % (htab["id"], int2str("I", i)))
1055 start_test("Test map delete (array)...")
1057 ret, err = bpftool("map delete id %d key %s" %
1058 (htab["id"], int2str("I", i)), fail=False)
1059 fail(ret == 0, "removed entry from an array")
1060 fail(err["error"].find("No such file or directory") == -1,
1061 "expected ENOENT, error is '%s'" % (err["error"]))
1063 start_test("Test map remove...")
1064 sim.unset_xdp("offload")
1065 bpftool_map_list_wait(expected=0)
1069 sim.set_xdp(map_obj, "offload", JSON=False) # map fixup msg breaks JSON
1071 bpftool_map_list_wait(expected=0)
1073 start_test("Test map creation fail path...")
1075 sim.dfs["bpf_map_accept"] = "N"
1076 ret, _ = sim.set_xdp(map_obj, "offload", JSON=False, fail=False)
1078 "netdevsim didn't refuse to create a map with offload disabled")
1080 print("%s: OK" % (os.path.basename(__file__)))
1083 log("Clean up...", "", level=1)