Skip to content

Commit

Permalink
selftests/bpf: add checks on extack messages for eBPF hw offload tests
Browse files Browse the repository at this point in the history
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 <quentin.monnet@netronome.com>
Reviewed-by: Jakub Kicinski <jakub.kicinski@netronome.com>
Signed-off-by: Jakub Kicinski <jakub.kicinski@netronome.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
  • Loading branch information
Quentin Monnet authored and David S. Miller committed Jan 24, 2018
1 parent 728461f commit caf9522
Showing 1 changed file with 86 additions and 26 deletions.
112 changes: 86 additions & 26 deletions tools/testing/selftests/bpf/test_offload.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@

logfile = None
log_level = 1
skip_extack = False
bpf_test_dir = os.path.dirname(os.path.realpath(__file__))
pp = pprint.PrettyPrinter()
devs = [] # devices we created for clean up
Expand Down Expand Up @@ -132,17 +133,28 @@ def rm(f):
if f in files:
files.remove(f)

def tool(name, args, flags, JSON=True, ns="", fail=True):
def tool(name, args, flags, JSON=True, ns="", fail=True, include_stderr=False):
params = ""
if JSON:
params += "%s " % (flags["json"])

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

ret, out = cmd(ns + name + " " + params + args, fail=fail)
if JSON and len(out.strip()) != 0:
return ret, json.loads(out)
if include_stderr:
ret, stdout, stderr = cmd(ns + name + " " + params + args,
fail=fail, include_stderr=True)
else:
ret, stdout = cmd(ns + name + " " + params + args,
fail=fail, include_stderr=False)

if JSON and len(stdout.strip()) != 0:
out = json.loads(stdout)
else:
out = stdout

if include_stderr:
return ret, out, stderr
else:
return ret, out

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

def ip(args, force=False, JSON=True, ns="", fail=True):
def ip(args, force=False, JSON=True, ns="", fail=True, include_stderr=False):
if force:
args = "-force " + args
return tool("ip", args, {"json":"-j"}, JSON=JSON, ns=ns, fail=fail)
return tool("ip", args, {"json":"-j"}, JSON=JSON, ns=ns,
fail=fail, include_stderr=include_stderr)

def tc(args, JSON=True, ns="", fail=True):
return tool("tc", args, {"json":"-p"}, JSON=JSON, ns=ns, fail=fail)
def tc(args, JSON=True, ns="", fail=True, include_stderr=False):
return tool("tc", args, {"json":"-p"}, JSON=JSON, ns=ns,
fail=fail, include_stderr=include_stderr)

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

def set_xdp(self, bpf, mode, force=False, JSON=True, fail=True):
def set_xdp(self, bpf, mode, force=False, JSON=True,
fail=True, include_stderr=False):
return ip("link set dev %s xdp%s %s" % (self.dev["ifname"], mode, bpf),
force=force, JSON=JSON, fail=fail)
force=force, JSON=JSON,
fail=fail, include_stderr=include_stderr)

def unset_xdp(self, mode, force=False, JSON=True, fail=True):
def unset_xdp(self, mode, force=False, JSON=True,
fail=True, include_stderr=False):
return ip("link set dev %s xdp%s off" % (self.dev["ifname"], mode),
force=force, JSON=JSON, fail=fail)
force=force, JSON=JSON,
fail=fail, include_stderr=include_stderr)

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

def cls_bpf_add_filter(self, bpf, da=False, skip_sw=False, skip_hw=False,
fail=True):
fail=True, include_stderr=False):
params = ""
if da:
params += " da"
Expand All @@ -419,7 +437,8 @@ def cls_bpf_add_filter(self, bpf, da=False, skip_sw=False, skip_hw=False,
if skip_hw:
params += " skip_hw"
return tc("filter add dev %s ingress bpf %s %s" %
(self['ifname'], bpf, params), fail=fail)
(self['ifname'], bpf, params),
fail=fail, include_stderr=include_stderr)

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

def check_extack(output, reference, args):
if skip_extack:
return
lines = output.split("\n")
comp = len(lines) >= 2 and lines[1] == reference
fail(not comp, "Missing or incorrect netlink extack message")

def check_extack_nsim(output, reference, args):
check_extack(output, "Error: netdevsim: " + reference, args)

# Parse command line
parser = argparse.ArgumentParser()
parser.add_argument("--log", help="output verbose log to given file")
Expand Down Expand Up @@ -527,6 +556,14 @@ def check_dev_info(other_ns, ns, prog_file=None, map_file=None, removed=False):
skip(ret != 0, "sample %s/%s not found, please compile it" %
(bpf_test_dir, s))

# Check if iproute2 is built with libmnl (needed by extack support)
_, _, err = cmd("tc qdisc delete dev lo handle 0",
fail=False, include_stderr=True)
if err.find("Error: Failed to find qdisc with specified handle.") == -1:
print("Warning: no extack message in iproute2 output, libmnl missing?")
log("Warning: no extack message in iproute2 output, libmnl missing?", "")
skip_extack = True

# Check if net namespaces seem to work
ns = mknetns()
skip(ns is None, "Could not create a net namespace")
Expand Down Expand Up @@ -558,8 +595,10 @@ def check_dev_info(other_ns, ns, prog_file=None, map_file=None, removed=False):
sim.tc_flush_filters()

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

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

start_test("Test TC cBPF unbound bytecode doesn't offload...")
ret, _ = sim.cls_bpf_add_filter(bytecode, skip_sw=True, fail=False)
ret, _, err = sim.cls_bpf_add_filter(bytecode, skip_sw=True,
fail=False, include_stderr=True)
fail(ret == 0, "TC bytecode loaded for offload")
check_extack_nsim(err, "netdevsim configured to reject unbound programs.",
args)
sim.wait_for_flush()

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

start_test("Test XDP prog replace with bad flags...")
ret, _ = sim.set_xdp(obj, "offload", force=True, fail=False)
ret, _, err = sim.set_xdp(obj, "offload", force=True,
fail=False, include_stderr=True)
fail(ret == 0, "Replaced XDP program with a program in different mode")
ret, _ = sim.set_xdp(obj, "", force=True, fail=False)
check_extack_nsim(err, "program loaded with different flags.", args)
ret, _, err = sim.set_xdp(obj, "", force=True,
fail=False, include_stderr=True)
fail(ret == 0, "Replaced XDP program with a program in different mode")
check_extack_nsim(err, "program loaded with different flags.", args)

start_test("Test XDP prog remove with bad flags...")
ret, _ = sim.unset_xdp("offload", force=True, fail=False)
ret, _, err = sim.unset_xdp("offload", force=True,
fail=False, include_stderr=True)
fail(ret == 0, "Removed program with a bad mode mode")
ret, _ = sim.unset_xdp("", force=True, fail=False)
check_extack_nsim(err, "program loaded with different flags.", args)
ret, _, err = sim.unset_xdp("", force=True,
fail=False, include_stderr=True)
fail(ret == 0, "Removed program with a bad mode mode")
check_extack_nsim(err, "program loaded with different flags.", args)

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

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

ret, _ = sim.set_xdp(pinned, "offload", fail=False)
ret, _, err = sim.set_xdp(pinned, "offload",
fail=False, include_stderr=True)
fail(ret == 0, "Pinned program loaded for a different device accepted")
check_extack_nsim(err, "program bound to different dev.", args)
sim2.remove()
ret, _ = sim.set_xdp(pinned, "offload", fail=False)
ret, _, err = sim.set_xdp(pinned, "offload",
fail=False, include_stderr=True)
fail(ret == 0, "Pinned program loaded for a removed device accepted")
check_extack_nsim(err, "xdpoffload of non-bound program.", args)
rm(pin_file)
bpftool_prog_list_wait(expected=0)

start_test("Test mixing of TC and XDP...")
sim.tc_add_ingress()
sim.set_xdp(obj, "offload")
ret, _ = sim.cls_bpf_add_filter(obj, skip_sw=True, fail=False)
ret, _, err = sim.cls_bpf_add_filter(obj, skip_sw=True,
fail=False, include_stderr=True)
fail(ret == 0, "Loading TC when XDP active should fail")
check_extack_nsim(err, "driver and netdev offload states mismatch.", args)
sim.unset_xdp("offload")
sim.wait_for_flush()

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

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

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

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

Expand Down

0 comments on commit caf9522

Please sign in to comment.