Skip to content

Commit caf9522

Browse files
qmonnetdavem330
authored andcommitted
selftests/bpf: add checks on extack messages for eBPF hw offload tests
Add checks to test that netlink extack messages are correctly displayed in some expected error cases for eBPF offload to netdevsim with TC and XDP. iproute2 may be built without libmnl support, in which case the extack messages will not be reported. Try to detect this condition, and when enountered print a mild warning to the user and skip the extack validation. Signed-off-by: Quentin Monnet <[email protected]> Reviewed-by: Jakub Kicinski <[email protected]> Signed-off-by: Jakub Kicinski <[email protected]> Signed-off-by: David S. Miller <[email protected]>
1 parent 728461f commit caf9522

File tree

1 file changed

+86
-26
lines changed

1 file changed

+86
-26
lines changed

tools/testing/selftests/bpf/test_offload.py

Lines changed: 86 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626

2727
logfile = None
2828
log_level = 1
29+
skip_extack = False
2930
bpf_test_dir = os.path.dirname(os.path.realpath(__file__))
3031
pp = pprint.PrettyPrinter()
3132
devs = [] # devices we created for clean up
@@ -132,17 +133,28 @@ def rm(f):
132133
if f in files:
133134
files.remove(f)
134135

135-
def tool(name, args, flags, JSON=True, ns="", fail=True):
136+
def tool(name, args, flags, JSON=True, ns="", fail=True, include_stderr=False):
136137
params = ""
137138
if JSON:
138139
params += "%s " % (flags["json"])
139140

140141
if ns != "":
141142
ns = "ip netns exec %s " % (ns)
142143

143-
ret, out = cmd(ns + name + " " + params + args, fail=fail)
144-
if JSON and len(out.strip()) != 0:
145-
return ret, json.loads(out)
144+
if include_stderr:
145+
ret, stdout, stderr = cmd(ns + name + " " + params + args,
146+
fail=fail, include_stderr=True)
147+
else:
148+
ret, stdout = cmd(ns + name + " " + params + args,
149+
fail=fail, include_stderr=False)
150+
151+
if JSON and len(stdout.strip()) != 0:
152+
out = json.loads(stdout)
153+
else:
154+
out = stdout
155+
156+
if include_stderr:
157+
return ret, out, stderr
146158
else:
147159
return ret, out
148160

@@ -181,13 +193,15 @@ def bpftool_map_list_wait(expected=0, n_retry=20):
181193
time.sleep(0.05)
182194
raise Exception("Time out waiting for map counts to stabilize want %d, have %d" % (expected, nmaps))
183195

184-
def ip(args, force=False, JSON=True, ns="", fail=True):
196+
def ip(args, force=False, JSON=True, ns="", fail=True, include_stderr=False):
185197
if force:
186198
args = "-force " + args
187-
return tool("ip", args, {"json":"-j"}, JSON=JSON, ns=ns, fail=fail)
199+
return tool("ip", args, {"json":"-j"}, JSON=JSON, ns=ns,
200+
fail=fail, include_stderr=include_stderr)
188201

189-
def tc(args, JSON=True, ns="", fail=True):
190-
return tool("tc", args, {"json":"-p"}, JSON=JSON, ns=ns, fail=fail)
202+
def tc(args, JSON=True, ns="", fail=True, include_stderr=False):
203+
return tool("tc", args, {"json":"-p"}, JSON=JSON, ns=ns,
204+
fail=fail, include_stderr=include_stderr)
191205

192206
def ethtool(dev, opt, args, fail=True):
193207
return cmd("ethtool %s %s %s" % (opt, dev["ifname"], args), fail=fail)
@@ -348,13 +362,17 @@ def set_mtu(self, mtu, fail=True):
348362
return ip("link set dev %s mtu %d" % (self.dev["ifname"], mtu),
349363
fail=fail)
350364

351-
def set_xdp(self, bpf, mode, force=False, JSON=True, fail=True):
365+
def set_xdp(self, bpf, mode, force=False, JSON=True,
366+
fail=True, include_stderr=False):
352367
return ip("link set dev %s xdp%s %s" % (self.dev["ifname"], mode, bpf),
353-
force=force, JSON=JSON, fail=fail)
368+
force=force, JSON=JSON,
369+
fail=fail, include_stderr=include_stderr)
354370

355-
def unset_xdp(self, mode, force=False, JSON=True, fail=True):
371+
def unset_xdp(self, mode, force=False, JSON=True,
372+
fail=True, include_stderr=False):
356373
return ip("link set dev %s xdp%s off" % (self.dev["ifname"], mode),
357-
force=force, JSON=JSON, fail=fail)
374+
force=force, JSON=JSON,
375+
fail=fail, include_stderr=include_stderr)
358376

359377
def ip_link_show(self, xdp):
360378
_, link = ip("link show dev %s" % (self['ifname']))
@@ -410,7 +428,7 @@ def tc_show_ingress(self, expected=None):
410428
return filters
411429

412430
def cls_bpf_add_filter(self, bpf, da=False, skip_sw=False, skip_hw=False,
413-
fail=True):
431+
fail=True, include_stderr=False):
414432
params = ""
415433
if da:
416434
params += " da"
@@ -419,7 +437,8 @@ def cls_bpf_add_filter(self, bpf, da=False, skip_sw=False, skip_hw=False,
419437
if skip_hw:
420438
params += " skip_hw"
421439
return tc("filter add dev %s ingress bpf %s %s" %
422-
(self['ifname'], bpf, params), fail=fail)
440+
(self['ifname'], bpf, params),
441+
fail=fail, include_stderr=include_stderr)
423442

424443
def set_ethtool_tc_offloads(self, enable, fail=True):
425444
args = "hw-tc-offload %s" % ("on" if enable else "off")
@@ -491,6 +510,16 @@ def check_dev_info(other_ns, ns, prog_file=None, map_file=None, removed=False):
491510
fail("dev" not in m.keys(), "Device parameters not reported")
492511
fail(dev != m["dev"], "Map's device different than program's")
493512

513+
def check_extack(output, reference, args):
514+
if skip_extack:
515+
return
516+
lines = output.split("\n")
517+
comp = len(lines) >= 2 and lines[1] == reference
518+
fail(not comp, "Missing or incorrect netlink extack message")
519+
520+
def check_extack_nsim(output, reference, args):
521+
check_extack(output, "Error: netdevsim: " + reference, args)
522+
494523
# Parse command line
495524
parser = argparse.ArgumentParser()
496525
parser.add_argument("--log", help="output verbose log to given file")
@@ -527,6 +556,14 @@ def check_dev_info(other_ns, ns, prog_file=None, map_file=None, removed=False):
527556
skip(ret != 0, "sample %s/%s not found, please compile it" %
528557
(bpf_test_dir, s))
529558

559+
# Check if iproute2 is built with libmnl (needed by extack support)
560+
_, _, err = cmd("tc qdisc delete dev lo handle 0",
561+
fail=False, include_stderr=True)
562+
if err.find("Error: Failed to find qdisc with specified handle.") == -1:
563+
print("Warning: no extack message in iproute2 output, libmnl missing?")
564+
log("Warning: no extack message in iproute2 output, libmnl missing?", "")
565+
skip_extack = True
566+
530567
# Check if net namespaces seem to work
531568
ns = mknetns()
532569
skip(ns is None, "Could not create a net namespace")
@@ -558,8 +595,10 @@ def check_dev_info(other_ns, ns, prog_file=None, map_file=None, removed=False):
558595
sim.tc_flush_filters()
559596

560597
start_test("Test TC offloads are off by default...")
561-
ret, _ = sim.cls_bpf_add_filter(obj, skip_sw=True, fail=False)
598+
ret, _, err = sim.cls_bpf_add_filter(obj, skip_sw=True,
599+
fail=False, include_stderr=True)
562600
fail(ret == 0, "TC filter loaded without enabling TC offloads")
601+
check_extack(err, "Error: TC offload is disabled on net device.", args)
563602
sim.wait_for_flush()
564603

565604
sim.set_ethtool_tc_offloads(True)
@@ -587,8 +626,11 @@ def check_dev_info(other_ns, ns, prog_file=None, map_file=None, removed=False):
587626
sim.dfs["bpf_tc_non_bound_accept"] = "N"
588627

589628
start_test("Test TC cBPF unbound bytecode doesn't offload...")
590-
ret, _ = sim.cls_bpf_add_filter(bytecode, skip_sw=True, fail=False)
629+
ret, _, err = sim.cls_bpf_add_filter(bytecode, skip_sw=True,
630+
fail=False, include_stderr=True)
591631
fail(ret == 0, "TC bytecode loaded for offload")
632+
check_extack_nsim(err, "netdevsim configured to reject unbound programs.",
633+
args)
592634
sim.wait_for_flush()
593635

594636
start_test("Test TC offloads work...")
@@ -669,16 +711,24 @@ def check_dev_info(other_ns, ns, prog_file=None, map_file=None, removed=False):
669711
"Device parameters reported for non-offloaded program")
670712

671713
start_test("Test XDP prog replace with bad flags...")
672-
ret, _ = sim.set_xdp(obj, "offload", force=True, fail=False)
714+
ret, _, err = sim.set_xdp(obj, "offload", force=True,
715+
fail=False, include_stderr=True)
673716
fail(ret == 0, "Replaced XDP program with a program in different mode")
674-
ret, _ = sim.set_xdp(obj, "", force=True, fail=False)
717+
check_extack_nsim(err, "program loaded with different flags.", args)
718+
ret, _, err = sim.set_xdp(obj, "", force=True,
719+
fail=False, include_stderr=True)
675720
fail(ret == 0, "Replaced XDP program with a program in different mode")
721+
check_extack_nsim(err, "program loaded with different flags.", args)
676722

677723
start_test("Test XDP prog remove with bad flags...")
678-
ret, _ = sim.unset_xdp("offload", force=True, fail=False)
724+
ret, _, err = sim.unset_xdp("offload", force=True,
725+
fail=False, include_stderr=True)
679726
fail(ret == 0, "Removed program with a bad mode mode")
680-
ret, _ = sim.unset_xdp("", force=True, fail=False)
727+
check_extack_nsim(err, "program loaded with different flags.", args)
728+
ret, _, err = sim.unset_xdp("", force=True,
729+
fail=False, include_stderr=True)
681730
fail(ret == 0, "Removed program with a bad mode mode")
731+
check_extack_nsim(err, "program loaded with different flags.", args)
682732

683733
start_test("Test MTU restrictions...")
684734
ret, _ = sim.set_mtu(9000, fail=False)
@@ -687,8 +737,9 @@ def check_dev_info(other_ns, ns, prog_file=None, map_file=None, removed=False):
687737
sim.unset_xdp("drv")
688738
bpftool_prog_list_wait(expected=0)
689739
sim.set_mtu(9000)
690-
ret, _ = sim.set_xdp(obj, "drv", fail=False)
740+
ret, _, err = sim.set_xdp(obj, "drv", fail=False, include_stderr=True)
691741
fail(ret == 0, "Driver should refuse to load program with MTU of 9000...")
742+
check_extack_nsim(err, "MTU too large w/ XDP enabled.", args)
692743
sim.set_mtu(1500)
693744

694745
sim.wait_for_flush()
@@ -724,25 +775,32 @@ def check_dev_info(other_ns, ns, prog_file=None, map_file=None, removed=False):
724775
sim2.set_xdp(obj, "offload")
725776
pin_file, pinned = pin_prog("/sys/fs/bpf/tmp")
726777

727-
ret, _ = sim.set_xdp(pinned, "offload", fail=False)
778+
ret, _, err = sim.set_xdp(pinned, "offload",
779+
fail=False, include_stderr=True)
728780
fail(ret == 0, "Pinned program loaded for a different device accepted")
781+
check_extack_nsim(err, "program bound to different dev.", args)
729782
sim2.remove()
730-
ret, _ = sim.set_xdp(pinned, "offload", fail=False)
783+
ret, _, err = sim.set_xdp(pinned, "offload",
784+
fail=False, include_stderr=True)
731785
fail(ret == 0, "Pinned program loaded for a removed device accepted")
786+
check_extack_nsim(err, "xdpoffload of non-bound program.", args)
732787
rm(pin_file)
733788
bpftool_prog_list_wait(expected=0)
734789

735790
start_test("Test mixing of TC and XDP...")
736791
sim.tc_add_ingress()
737792
sim.set_xdp(obj, "offload")
738-
ret, _ = sim.cls_bpf_add_filter(obj, skip_sw=True, fail=False)
793+
ret, _, err = sim.cls_bpf_add_filter(obj, skip_sw=True,
794+
fail=False, include_stderr=True)
739795
fail(ret == 0, "Loading TC when XDP active should fail")
796+
check_extack_nsim(err, "driver and netdev offload states mismatch.", args)
740797
sim.unset_xdp("offload")
741798
sim.wait_for_flush()
742799

743800
sim.cls_bpf_add_filter(obj, skip_sw=True)
744-
ret, _ = sim.set_xdp(obj, "offload", fail=False)
801+
ret, _, err = sim.set_xdp(obj, "offload", fail=False, include_stderr=True)
745802
fail(ret == 0, "Loading XDP when TC active should fail")
803+
check_extack_nsim(err, "TC program is already loaded.", args)
746804

747805
start_test("Test binding TC from pinned...")
748806
pin_file, pinned = pin_prog("/sys/fs/bpf/tmp")
@@ -765,8 +823,10 @@ def check_dev_info(other_ns, ns, prog_file=None, map_file=None, removed=False):
765823

766824
start_test("Test asking for TC offload of two filters...")
767825
sim.cls_bpf_add_filter(obj, da=True, skip_sw=True)
768-
ret, _ = sim.cls_bpf_add_filter(obj, da=True, skip_sw=True, fail=False)
826+
ret, _, err = sim.cls_bpf_add_filter(obj, da=True, skip_sw=True,
827+
fail=False, include_stderr=True)
769828
fail(ret == 0, "Managed to offload two TC filters at the same time")
829+
check_extack_nsim(err, "driver and netdev offload states mismatch.", args)
770830

771831
sim.tc_flush_filters(bound=2, total=2)
772832

0 commit comments

Comments
 (0)