0001
0002
0003
0004
0005
0006
0007
0008
0009
0010
0011
0012
0013
0014
0015
0016
0017 from datetime import datetime
0018 import argparse
0019 import errno
0020 import json
0021 import os
0022 import pprint
0023 import random
0024 import re
0025 import stat
0026 import string
0027 import struct
0028 import subprocess
0029 import time
0030 import traceback
0031
0032 logfile = None
0033 log_level = 1
0034 skip_extack = False
0035 bpf_test_dir = os.path.dirname(os.path.realpath(__file__))
0036 pp = pprint.PrettyPrinter()
0037 devs = []
0038 files = []
0039 netns = []
0040
0041 def log_get_sec(level=0):
0042 return "*" * (log_level + level)
0043
0044 def log_level_inc(add=1):
0045 global log_level
0046 log_level += add
0047
0048 def log_level_dec(sub=1):
0049 global log_level
0050 log_level -= sub
0051
0052 def log_level_set(level):
0053 global log_level
0054 log_level = level
0055
0056 def log(header, data, level=None):
0057 """
0058 Output to an optional log.
0059 """
0060 if logfile is None:
0061 return
0062 if level is not None:
0063 log_level_set(level)
0064
0065 if not isinstance(data, str):
0066 data = pp.pformat(data)
0067
0068 if len(header):
0069 logfile.write("\n" + log_get_sec() + " ")
0070 logfile.write(header)
0071 if len(header) and len(data.strip()):
0072 logfile.write("\n")
0073 logfile.write(data)
0074
0075 def skip(cond, msg):
0076 if not cond:
0077 return
0078 print("SKIP: " + msg)
0079 log("SKIP: " + msg, "", level=1)
0080 os.sys.exit(0)
0081
0082 def fail(cond, msg):
0083 if not cond:
0084 return
0085 print("FAIL: " + msg)
0086 tb = "".join(traceback.extract_stack().format())
0087 print(tb)
0088 log("FAIL: " + msg, tb, level=1)
0089 os.sys.exit(1)
0090
0091 def start_test(msg):
0092 log(msg, "", level=1)
0093 log_level_inc()
0094 print(msg)
0095
0096 def cmd(cmd, shell=True, include_stderr=False, background=False, fail=True):
0097 """
0098 Run a command in subprocess and return tuple of (retval, stdout);
0099 optionally return stderr as well as third value.
0100 """
0101 proc = subprocess.Popen(cmd, shell=shell, stdout=subprocess.PIPE,
0102 stderr=subprocess.PIPE)
0103 if background:
0104 msg = "%s START: %s" % (log_get_sec(1),
0105 datetime.now().strftime("%H:%M:%S.%f"))
0106 log("BKG " + proc.args, msg)
0107 return proc
0108
0109 return cmd_result(proc, include_stderr=include_stderr, fail=fail)
0110
0111 def cmd_result(proc, include_stderr=False, fail=False):
0112 stdout, stderr = proc.communicate()
0113 stdout = stdout.decode("utf-8")
0114 stderr = stderr.decode("utf-8")
0115 proc.stdout.close()
0116 proc.stderr.close()
0117
0118 stderr = "\n" + stderr
0119 if stderr[-1] == "\n":
0120 stderr = stderr[:-1]
0121
0122 sec = log_get_sec(1)
0123 log("CMD " + proc.args,
0124 "RETCODE: %d\n%s STDOUT:\n%s%s STDERR:%s\n%s END: %s" %
0125 (proc.returncode, sec, stdout, sec, stderr,
0126 sec, datetime.now().strftime("%H:%M:%S.%f")))
0127
0128 if proc.returncode != 0 and fail:
0129 if len(stderr) > 0 and stderr[-1] == "\n":
0130 stderr = stderr[:-1]
0131 raise Exception("Command failed: %s\n%s" % (proc.args, stderr))
0132
0133 if include_stderr:
0134 return proc.returncode, stdout, stderr
0135 else:
0136 return proc.returncode, stdout
0137
0138 def rm(f):
0139 cmd("rm -f %s" % (f))
0140 if f in files:
0141 files.remove(f)
0142
0143 def tool(name, args, flags, JSON=True, ns="", fail=True, include_stderr=False):
0144 params = ""
0145 if JSON:
0146 params += "%s " % (flags["json"])
0147
0148 if ns != "":
0149 ns = "ip netns exec %s " % (ns)
0150
0151 if include_stderr:
0152 ret, stdout, stderr = cmd(ns + name + " " + params + args,
0153 fail=fail, include_stderr=True)
0154 else:
0155 ret, stdout = cmd(ns + name + " " + params + args,
0156 fail=fail, include_stderr=False)
0157
0158 if JSON and len(stdout.strip()) != 0:
0159 out = json.loads(stdout)
0160 else:
0161 out = stdout
0162
0163 if include_stderr:
0164 return ret, out, stderr
0165 else:
0166 return ret, out
0167
0168 def bpftool(args, JSON=True, ns="", fail=True, include_stderr=False):
0169 return tool("bpftool", args, {"json":"-p"}, JSON=JSON, ns=ns,
0170 fail=fail, include_stderr=include_stderr)
0171
0172 def bpftool_prog_list(expected=None, ns=""):
0173 _, progs = bpftool("prog show", JSON=True, ns=ns, fail=True)
0174
0175 for p in base_progs:
0176 if p in progs:
0177 progs.remove(p)
0178 if expected is not None:
0179 if len(progs) != expected:
0180 fail(True, "%d BPF programs loaded, expected %d" %
0181 (len(progs), expected))
0182 return progs
0183
0184 def bpftool_map_list(expected=None, ns=""):
0185 _, maps = bpftool("map show", JSON=True, ns=ns, fail=True)
0186
0187 maps = [m for m in maps if m not in base_maps and m.get('name') and m.get('name') not in base_map_names]
0188 if expected is not None:
0189 if len(maps) != expected:
0190 fail(True, "%d BPF maps loaded, expected %d" %
0191 (len(maps), expected))
0192 return maps
0193
0194 def bpftool_prog_list_wait(expected=0, n_retry=20):
0195 for i in range(n_retry):
0196 nprogs = len(bpftool_prog_list())
0197 if nprogs == expected:
0198 return
0199 time.sleep(0.05)
0200 raise Exception("Time out waiting for program counts to stabilize want %d, have %d" % (expected, nprogs))
0201
0202 def bpftool_map_list_wait(expected=0, n_retry=20):
0203 for i in range(n_retry):
0204 nmaps = len(bpftool_map_list())
0205 if nmaps == expected:
0206 return
0207 time.sleep(0.05)
0208 raise Exception("Time out waiting for map counts to stabilize want %d, have %d" % (expected, nmaps))
0209
0210 def bpftool_prog_load(sample, file_name, maps=[], prog_type="xdp", dev=None,
0211 fail=True, include_stderr=False):
0212 args = "prog load %s %s" % (os.path.join(bpf_test_dir, sample), file_name)
0213 if prog_type is not None:
0214 args += " type " + prog_type
0215 if dev is not None:
0216 args += " dev " + dev
0217 if len(maps):
0218 args += " map " + " map ".join(maps)
0219
0220 res = bpftool(args, fail=fail, include_stderr=include_stderr)
0221 if res[0] == 0:
0222 files.append(file_name)
0223 return res
0224
0225 def ip(args, force=False, JSON=True, ns="", fail=True, include_stderr=False):
0226 if force:
0227 args = "-force " + args
0228 return tool("ip", args, {"json":"-j"}, JSON=JSON, ns=ns,
0229 fail=fail, include_stderr=include_stderr)
0230
0231 def tc(args, JSON=True, ns="", fail=True, include_stderr=False):
0232 return tool("tc", args, {"json":"-p"}, JSON=JSON, ns=ns,
0233 fail=fail, include_stderr=include_stderr)
0234
0235 def ethtool(dev, opt, args, fail=True):
0236 return cmd("ethtool %s %s %s" % (opt, dev["ifname"], args), fail=fail)
0237
0238 def bpf_obj(name, sec=".text", path=bpf_test_dir,):
0239 return "obj %s sec %s" % (os.path.join(path, name), sec)
0240
0241 def bpf_pinned(name):
0242 return "pinned %s" % (name)
0243
0244 def bpf_bytecode(bytecode):
0245 return "bytecode \"%s\"" % (bytecode)
0246
0247 def mknetns(n_retry=10):
0248 for i in range(n_retry):
0249 name = ''.join([random.choice(string.ascii_letters) for i in range(8)])
0250 ret, _ = ip("netns add %s" % (name), fail=False)
0251 if ret == 0:
0252 netns.append(name)
0253 return name
0254 return None
0255
0256 def int2str(fmt, val):
0257 ret = []
0258 for b in struct.pack(fmt, val):
0259 ret.append(int(b))
0260 return " ".join(map(lambda x: str(x), ret))
0261
0262 def str2int(strtab):
0263 inttab = []
0264 for i in strtab:
0265 inttab.append(int(i, 16))
0266 ba = bytearray(inttab)
0267 if len(strtab) == 4:
0268 fmt = "I"
0269 elif len(strtab) == 8:
0270 fmt = "Q"
0271 else:
0272 raise Exception("String array of len %d can't be unpacked to an int" %
0273 (len(strtab)))
0274 return struct.unpack(fmt, ba)[0]
0275
0276 class DebugfsDir:
0277 """
0278 Class for accessing DebugFS directories as a dictionary.
0279 """
0280
0281 def __init__(self, path):
0282 self.path = path
0283 self._dict = self._debugfs_dir_read(path)
0284
0285 def __len__(self):
0286 return len(self._dict.keys())
0287
0288 def __getitem__(self, key):
0289 if type(key) is int:
0290 key = list(self._dict.keys())[key]
0291 return self._dict[key]
0292
0293 def __setitem__(self, key, value):
0294 log("DebugFS set %s = %s" % (key, value), "")
0295 log_level_inc()
0296
0297 cmd("echo '%s' > %s/%s" % (value, self.path, key))
0298 log_level_dec()
0299
0300 _, out = cmd('cat %s/%s' % (self.path, key))
0301 self._dict[key] = out.strip()
0302
0303 def _debugfs_dir_read(self, path):
0304 dfs = {}
0305
0306 log("DebugFS state for %s" % (path), "")
0307 log_level_inc(add=2)
0308
0309 _, out = cmd('ls ' + path)
0310 for f in out.split():
0311 if f == "ports":
0312 continue
0313
0314 p = os.path.join(path, f)
0315 if not os.stat(p).st_mode & stat.S_IRUSR:
0316 continue
0317
0318 if os.path.isfile(p):
0319
0320 if f == "trap_flow_action_cookie":
0321 cmd('echo deadbeef > %s/%s' % (path, f))
0322 _, out = cmd('cat %s/%s' % (path, f))
0323 dfs[f] = out.strip()
0324 elif os.path.isdir(p):
0325 dfs[f] = DebugfsDir(p)
0326 else:
0327 raise Exception("%s is neither file nor directory" % (p))
0328
0329 log_level_dec()
0330 log("DebugFS state", dfs)
0331 log_level_dec()
0332
0333 return dfs
0334
0335 class NetdevSimDev:
0336 """
0337 Class for netdevsim bus device and its attributes.
0338 """
0339 @staticmethod
0340 def ctrl_write(path, val):
0341 fullpath = os.path.join("/sys/bus/netdevsim/", path)
0342 try:
0343 with open(fullpath, "w") as f:
0344 f.write(val)
0345 except OSError as e:
0346 log("WRITE %s: %r" % (fullpath, val), -e.errno)
0347 raise e
0348 log("WRITE %s: %r" % (fullpath, val), 0)
0349
0350 def __init__(self, port_count=1):
0351 addr = 0
0352 while True:
0353 try:
0354 self.ctrl_write("new_device", "%u %u" % (addr, port_count))
0355 except OSError as e:
0356 if e.errno == errno.ENOSPC:
0357 addr += 1
0358 continue
0359 raise e
0360 break
0361 self.addr = addr
0362
0363
0364
0365 self.wait_for_netdevs(port_count)
0366
0367 ret, out = cmd("udevadm settle", fail=False)
0368 if ret:
0369 raise Exception("udevadm settle failed")
0370 ifnames = self.get_ifnames()
0371
0372 devs.append(self)
0373 self.dfs_dir = "/sys/kernel/debug/netdevsim/netdevsim%u/" % addr
0374
0375 self.nsims = []
0376 for port_index in range(port_count):
0377 self.nsims.append(NetdevSim(self, port_index, ifnames[port_index]))
0378
0379 def get_ifnames(self):
0380 ifnames = []
0381 listdir = os.listdir("/sys/bus/netdevsim/devices/netdevsim%u/net/" % self.addr)
0382 for ifname in listdir:
0383 ifnames.append(ifname)
0384 ifnames.sort()
0385 return ifnames
0386
0387 def wait_for_netdevs(self, port_count):
0388 timeout = 5
0389 timeout_start = time.time()
0390
0391 while True:
0392 try:
0393 ifnames = self.get_ifnames()
0394 except FileNotFoundError as e:
0395 ifnames = []
0396 if len(ifnames) == port_count:
0397 break
0398 if time.time() < timeout_start + timeout:
0399 continue
0400 raise Exception("netdevices did not appear within timeout")
0401
0402 def dfs_num_bound_progs(self):
0403 path = os.path.join(self.dfs_dir, "bpf_bound_progs")
0404 _, progs = cmd('ls %s' % (path))
0405 return len(progs.split())
0406
0407 def dfs_get_bound_progs(self, expected):
0408 progs = DebugfsDir(os.path.join(self.dfs_dir, "bpf_bound_progs"))
0409 if expected is not None:
0410 if len(progs) != expected:
0411 fail(True, "%d BPF programs bound, expected %d" %
0412 (len(progs), expected))
0413 return progs
0414
0415 def remove(self):
0416 self.ctrl_write("del_device", "%u" % (self.addr, ))
0417 devs.remove(self)
0418
0419 def remove_nsim(self, nsim):
0420 self.nsims.remove(nsim)
0421 self.ctrl_write("devices/netdevsim%u/del_port" % (self.addr, ),
0422 "%u" % (nsim.port_index, ))
0423
0424 class NetdevSim:
0425 """
0426 Class for netdevsim netdevice and its attributes.
0427 """
0428
0429 def __init__(self, nsimdev, port_index, ifname):
0430
0431
0432 nsimnamere = re.compile("eni\d+np(\d+)")
0433 match = nsimnamere.match(ifname)
0434 if match and int(match.groups()[0]) != port_index + 1:
0435 raise Exception("netdevice name mismatches the expected one")
0436
0437 self.nsimdev = nsimdev
0438 self.port_index = port_index
0439 self.ns = ""
0440 self.dfs_dir = "%s/ports/%u/" % (nsimdev.dfs_dir, port_index)
0441 self.dfs_refresh()
0442 _, [self.dev] = ip("link show dev %s" % ifname)
0443
0444 def __getitem__(self, key):
0445 return self.dev[key]
0446
0447 def remove(self):
0448 self.nsimdev.remove_nsim(self)
0449
0450 def dfs_refresh(self):
0451 self.dfs = DebugfsDir(self.dfs_dir)
0452 return self.dfs
0453
0454 def dfs_read(self, f):
0455 path = os.path.join(self.dfs_dir, f)
0456 _, data = cmd('cat %s' % (path))
0457 return data.strip()
0458
0459 def wait_for_flush(self, bound=0, total=0, n_retry=20):
0460 for i in range(n_retry):
0461 nbound = self.nsimdev.dfs_num_bound_progs()
0462 nprogs = len(bpftool_prog_list())
0463 if nbound == bound and nprogs == total:
0464 return
0465 time.sleep(0.05)
0466 raise Exception("Time out waiting for program counts to stabilize want %d/%d, have %d bound, %d loaded" % (bound, total, nbound, nprogs))
0467
0468 def set_ns(self, ns):
0469 name = "1" if ns == "" else ns
0470 ip("link set dev %s netns %s" % (self.dev["ifname"], name), ns=self.ns)
0471 self.ns = ns
0472
0473 def set_mtu(self, mtu, fail=True):
0474 return ip("link set dev %s mtu %d" % (self.dev["ifname"], mtu),
0475 fail=fail)
0476
0477 def set_xdp(self, bpf, mode, force=False, JSON=True, verbose=False,
0478 fail=True, include_stderr=False):
0479 if verbose:
0480 bpf += " verbose"
0481 return ip("link set dev %s xdp%s %s" % (self.dev["ifname"], mode, bpf),
0482 force=force, JSON=JSON,
0483 fail=fail, include_stderr=include_stderr)
0484
0485 def unset_xdp(self, mode, force=False, JSON=True,
0486 fail=True, include_stderr=False):
0487 return ip("link set dev %s xdp%s off" % (self.dev["ifname"], mode),
0488 force=force, JSON=JSON,
0489 fail=fail, include_stderr=include_stderr)
0490
0491 def ip_link_show(self, xdp):
0492 _, link = ip("link show dev %s" % (self['ifname']))
0493 if len(link) > 1:
0494 raise Exception("Multiple objects on ip link show")
0495 if len(link) < 1:
0496 return {}
0497 fail(xdp != "xdp" in link,
0498 "XDP program not reporting in iplink (reported %s, expected %s)" %
0499 ("xdp" in link, xdp))
0500 return link[0]
0501
0502 def tc_add_ingress(self):
0503 tc("qdisc add dev %s ingress" % (self['ifname']))
0504
0505 def tc_del_ingress(self):
0506 tc("qdisc del dev %s ingress" % (self['ifname']))
0507
0508 def tc_flush_filters(self, bound=0, total=0):
0509 self.tc_del_ingress()
0510 self.tc_add_ingress()
0511 self.wait_for_flush(bound=bound, total=total)
0512
0513 def tc_show_ingress(self, expected=None):
0514
0515 flags = ["skip_sw", "skip_hw", "in_hw"]
0516 named = ["protocol", "pref", "chain", "handle", "id", "tag"]
0517
0518 args = "-s filter show dev %s ingress" % (self['ifname'])
0519 _, out = tc(args, JSON=False)
0520
0521 filters = []
0522 lines = out.split('\n')
0523 for line in lines:
0524 words = line.split()
0525 if "handle" not in words:
0526 continue
0527 fltr = {}
0528 for flag in flags:
0529 fltr[flag] = flag in words
0530 for name in named:
0531 try:
0532 idx = words.index(name)
0533 fltr[name] = words[idx + 1]
0534 except ValueError:
0535 pass
0536 filters.append(fltr)
0537
0538 if expected is not None:
0539 fail(len(filters) != expected,
0540 "%d ingress filters loaded, expected %d" %
0541 (len(filters), expected))
0542 return filters
0543
0544 def cls_filter_op(self, op, qdisc="ingress", prio=None, handle=None,
0545 chain=None, cls="", params="",
0546 fail=True, include_stderr=False):
0547 spec = ""
0548 if prio is not None:
0549 spec += " prio %d" % (prio)
0550 if handle:
0551 spec += " handle %s" % (handle)
0552 if chain is not None:
0553 spec += " chain %d" % (chain)
0554
0555 return tc("filter {op} dev {dev} {qdisc} {spec} {cls} {params}"\
0556 .format(op=op, dev=self['ifname'], qdisc=qdisc, spec=spec,
0557 cls=cls, params=params),
0558 fail=fail, include_stderr=include_stderr)
0559
0560 def cls_bpf_add_filter(self, bpf, op="add", prio=None, handle=None,
0561 chain=None, da=False, verbose=False,
0562 skip_sw=False, skip_hw=False,
0563 fail=True, include_stderr=False):
0564 cls = "bpf " + bpf
0565
0566 params = ""
0567 if da:
0568 params += " da"
0569 if verbose:
0570 params += " verbose"
0571 if skip_sw:
0572 params += " skip_sw"
0573 if skip_hw:
0574 params += " skip_hw"
0575
0576 return self.cls_filter_op(op=op, prio=prio, handle=handle, cls=cls,
0577 chain=chain, params=params,
0578 fail=fail, include_stderr=include_stderr)
0579
0580 def set_ethtool_tc_offloads(self, enable, fail=True):
0581 args = "hw-tc-offload %s" % ("on" if enable else "off")
0582 return ethtool(self, "-K", args, fail=fail)
0583
0584
0585 def clean_up():
0586 global files, netns, devs
0587
0588 for dev in devs:
0589 dev.remove()
0590 for f in files:
0591 cmd("rm -f %s" % (f))
0592 for ns in netns:
0593 cmd("ip netns delete %s" % (ns))
0594 files = []
0595 netns = []
0596
0597 def pin_prog(file_name, idx=0):
0598 progs = bpftool_prog_list(expected=(idx + 1))
0599 prog = progs[idx]
0600 bpftool("prog pin id %d %s" % (prog["id"], file_name))
0601 files.append(file_name)
0602
0603 return file_name, bpf_pinned(file_name)
0604
0605 def pin_map(file_name, idx=0, expected=1):
0606 maps = bpftool_map_list(expected=expected)
0607 m = maps[idx]
0608 bpftool("map pin id %d %s" % (m["id"], file_name))
0609 files.append(file_name)
0610
0611 return file_name, bpf_pinned(file_name)
0612
0613 def check_dev_info_removed(prog_file=None, map_file=None):
0614 bpftool_prog_list(expected=0)
0615 ret, err = bpftool("prog show pin %s" % (prog_file), fail=False)
0616 fail(ret == 0, "Showing prog with removed device did not fail")
0617 fail(err["error"].find("No such device") == -1,
0618 "Showing prog with removed device expected ENODEV, error is %s" %
0619 (err["error"]))
0620
0621 bpftool_map_list(expected=0)
0622 ret, err = bpftool("map show pin %s" % (map_file), fail=False)
0623 fail(ret == 0, "Showing map with removed device did not fail")
0624 fail(err["error"].find("No such device") == -1,
0625 "Showing map with removed device expected ENODEV, error is %s" %
0626 (err["error"]))
0627
0628 def check_dev_info(other_ns, ns, prog_file=None, map_file=None, removed=False):
0629 progs = bpftool_prog_list(expected=1, ns=ns)
0630 prog = progs[0]
0631
0632 fail("dev" not in prog.keys(), "Device parameters not reported")
0633 dev = prog["dev"]
0634 fail("ifindex" not in dev.keys(), "Device parameters not reported")
0635 fail("ns_dev" not in dev.keys(), "Device parameters not reported")
0636 fail("ns_inode" not in dev.keys(), "Device parameters not reported")
0637
0638 if not other_ns:
0639 fail("ifname" not in dev.keys(), "Ifname not reported")
0640 fail(dev["ifname"] != sim["ifname"],
0641 "Ifname incorrect %s vs %s" % (dev["ifname"], sim["ifname"]))
0642 else:
0643 fail("ifname" in dev.keys(), "Ifname is reported for other ns")
0644
0645 maps = bpftool_map_list(expected=2, ns=ns)
0646 for m in maps:
0647 fail("dev" not in m.keys(), "Device parameters not reported")
0648 fail(dev != m["dev"], "Map's device different than program's")
0649
0650 def check_extack(output, reference, args):
0651 if skip_extack:
0652 return
0653 lines = output.split("\n")
0654 comp = len(lines) >= 2 and lines[1] == 'Error: ' + reference
0655 fail(not comp, "Missing or incorrect netlink extack message")
0656
0657 def check_extack_nsim(output, reference, args):
0658 check_extack(output, "netdevsim: " + reference, args)
0659
0660 def check_no_extack(res, needle):
0661 fail((res[1] + res[2]).count(needle) or (res[1] + res[2]).count("Warning:"),
0662 "Found '%s' in command output, leaky extack?" % (needle))
0663
0664 def check_verifier_log(output, reference):
0665 lines = output.split("\n")
0666 for l in reversed(lines):
0667 if l == reference:
0668 return
0669 fail(True, "Missing or incorrect message from netdevsim in verifier log")
0670
0671 def check_multi_basic(two_xdps):
0672 fail(two_xdps["mode"] != 4, "Bad mode reported with multiple programs")
0673 fail("prog" in two_xdps, "Base program reported in multi program mode")
0674 fail(len(two_xdps["attached"]) != 2,
0675 "Wrong attached program count with two programs")
0676 fail(two_xdps["attached"][0]["prog"]["id"] ==
0677 two_xdps["attached"][1]["prog"]["id"],
0678 "Offloaded and other programs have the same id")
0679
0680 def test_spurios_extack(sim, obj, skip_hw, needle):
0681 res = sim.cls_bpf_add_filter(obj, prio=1, handle=1, skip_hw=skip_hw,
0682 include_stderr=True)
0683 check_no_extack(res, needle)
0684 res = sim.cls_bpf_add_filter(obj, op="replace", prio=1, handle=1,
0685 skip_hw=skip_hw, include_stderr=True)
0686 check_no_extack(res, needle)
0687 res = sim.cls_filter_op(op="delete", prio=1, handle=1, cls="bpf",
0688 include_stderr=True)
0689 check_no_extack(res, needle)
0690
0691 def test_multi_prog(simdev, sim, obj, modename, modeid):
0692 start_test("Test multi-attachment XDP - %s + offload..." %
0693 (modename or "default", ))
0694 sim.set_xdp(obj, "offload")
0695 xdp = sim.ip_link_show(xdp=True)["xdp"]
0696 offloaded = sim.dfs_read("bpf_offloaded_id")
0697 fail("prog" not in xdp, "Base program not reported in single program mode")
0698 fail(len(xdp["attached"]) != 1,
0699 "Wrong attached program count with one program")
0700
0701 sim.set_xdp(obj, modename)
0702 two_xdps = sim.ip_link_show(xdp=True)["xdp"]
0703
0704 fail(xdp["attached"][0] not in two_xdps["attached"],
0705 "Offload program not reported after other activated")
0706 check_multi_basic(two_xdps)
0707
0708 offloaded2 = sim.dfs_read("bpf_offloaded_id")
0709 fail(offloaded != offloaded2,
0710 "Offload ID changed after loading other program")
0711
0712 start_test("Test multi-attachment XDP - replace...")
0713 ret, _, err = sim.set_xdp(obj, "offload", fail=False, include_stderr=True)
0714 fail(ret == 0, "Replaced one of programs without -force")
0715 check_extack(err, "XDP program already attached.", args)
0716
0717 start_test("Test multi-attachment XDP - remove without mode...")
0718 ret, _, err = sim.unset_xdp("", force=True,
0719 fail=False, include_stderr=True)
0720 fail(ret == 0, "Removed program without a mode flag")
0721 check_extack(err, "More than one program loaded, unset mode is ambiguous.", args)
0722
0723 sim.unset_xdp("offload")
0724 xdp = sim.ip_link_show(xdp=True)["xdp"]
0725 offloaded = sim.dfs_read("bpf_offloaded_id")
0726
0727 fail(xdp["mode"] != modeid, "Bad mode reported after multiple programs")
0728 fail("prog" not in xdp,
0729 "Base program not reported after multi program mode")
0730 fail(xdp["attached"][0] not in two_xdps["attached"],
0731 "Offload program not reported after other activated")
0732 fail(len(xdp["attached"]) != 1,
0733 "Wrong attached program count with remaining programs")
0734 fail(offloaded != "0", "Offload ID reported with only other program left")
0735
0736 start_test("Test multi-attachment XDP - reattach...")
0737 sim.set_xdp(obj, "offload")
0738 two_xdps = sim.ip_link_show(xdp=True)["xdp"]
0739
0740 fail(xdp["attached"][0] not in two_xdps["attached"],
0741 "Other program not reported after offload activated")
0742 check_multi_basic(two_xdps)
0743
0744 start_test("Test multi-attachment XDP - device remove...")
0745 simdev.remove()
0746
0747 simdev = NetdevSimDev()
0748 sim, = simdev.nsims
0749 sim.set_ethtool_tc_offloads(True)
0750 return [simdev, sim]
0751
0752
0753 parser = argparse.ArgumentParser()
0754 parser.add_argument("--log", help="output verbose log to given file")
0755 args = parser.parse_args()
0756 if args.log:
0757 logfile = open(args.log, 'w+')
0758 logfile.write("# -*-Org-*-")
0759
0760 log("Prepare...", "", level=1)
0761 log_level_inc()
0762
0763
0764 skip(os.getuid() != 0, "test must be run as root")
0765
0766
0767 ret, progs = bpftool("prog", fail=False)
0768 skip(ret != 0, "bpftool not installed")
0769 base_progs = progs
0770 _, base_maps = bpftool("map")
0771 base_map_names = [
0772 'pid_iter.rodata'
0773 ]
0774
0775
0776 ret, out = cmd("modprobe netdevsim", fail=False)
0777 skip(ret != 0, "netdevsim module could not be loaded")
0778
0779
0780 _, out = cmd("mount")
0781 if out.find("/sys/kernel/debug type debugfs") == -1:
0782 cmd("mount -t debugfs none /sys/kernel/debug")
0783
0784
0785 samples = ["sample_ret0.o", "sample_map_ret0.o"]
0786 for s in samples:
0787 ret, out = cmd("ls %s/%s" % (bpf_test_dir, s), fail=False)
0788 skip(ret != 0, "sample %s/%s not found, please compile it" %
0789 (bpf_test_dir, s))
0790
0791
0792 _, _, err = cmd("tc qdisc delete dev lo handle 0",
0793 fail=False, include_stderr=True)
0794 if err.find("Error: Failed to find qdisc with specified handle.") == -1:
0795 print("Warning: no extack message in iproute2 output, libmnl missing?")
0796 log("Warning: no extack message in iproute2 output, libmnl missing?", "")
0797 skip_extack = True
0798
0799
0800 ns = mknetns()
0801 skip(ns is None, "Could not create a net namespace")
0802 cmd("ip netns delete %s" % (ns))
0803 netns = []
0804
0805 try:
0806 obj = bpf_obj("sample_ret0.o")
0807 bytecode = bpf_bytecode("1,6 0 0 4294967295,")
0808
0809 start_test("Test destruction of generic XDP...")
0810 simdev = NetdevSimDev()
0811 sim, = simdev.nsims
0812 sim.set_xdp(obj, "generic")
0813 simdev.remove()
0814 bpftool_prog_list_wait(expected=0)
0815
0816 simdev = NetdevSimDev()
0817 sim, = simdev.nsims
0818 sim.tc_add_ingress()
0819
0820 start_test("Test TC non-offloaded...")
0821 ret, _ = sim.cls_bpf_add_filter(obj, skip_hw=True, fail=False)
0822 fail(ret != 0, "Software TC filter did not load")
0823
0824 start_test("Test TC non-offloaded isn't getting bound...")
0825 ret, _ = sim.cls_bpf_add_filter(obj, fail=False)
0826 fail(ret != 0, "Software TC filter did not load")
0827 simdev.dfs_get_bound_progs(expected=0)
0828
0829 sim.tc_flush_filters()
0830
0831 start_test("Test TC offloads are off by default...")
0832 ret, _, err = sim.cls_bpf_add_filter(obj, skip_sw=True,
0833 fail=False, include_stderr=True)
0834 fail(ret == 0, "TC filter loaded without enabling TC offloads")
0835 check_extack(err, "TC offload is disabled on net device.", args)
0836 sim.wait_for_flush()
0837
0838 sim.set_ethtool_tc_offloads(True)
0839 sim.dfs["bpf_tc_non_bound_accept"] = "Y"
0840
0841 start_test("Test TC offload by default...")
0842 ret, _ = sim.cls_bpf_add_filter(obj, fail=False)
0843 fail(ret != 0, "Software TC filter did not load")
0844 simdev.dfs_get_bound_progs(expected=0)
0845 ingress = sim.tc_show_ingress(expected=1)
0846 fltr = ingress[0]
0847 fail(not fltr["in_hw"], "Filter not offloaded by default")
0848
0849 sim.tc_flush_filters()
0850
0851 start_test("Test TC cBPF bytcode tries offload by default...")
0852 ret, _ = sim.cls_bpf_add_filter(bytecode, fail=False)
0853 fail(ret != 0, "Software TC filter did not load")
0854 simdev.dfs_get_bound_progs(expected=0)
0855 ingress = sim.tc_show_ingress(expected=1)
0856 fltr = ingress[0]
0857 fail(not fltr["in_hw"], "Bytecode not offloaded by default")
0858
0859 sim.tc_flush_filters()
0860 sim.dfs["bpf_tc_non_bound_accept"] = "N"
0861
0862 start_test("Test TC cBPF unbound bytecode doesn't offload...")
0863 ret, _, err = sim.cls_bpf_add_filter(bytecode, skip_sw=True,
0864 fail=False, include_stderr=True)
0865 fail(ret == 0, "TC bytecode loaded for offload")
0866 check_extack_nsim(err, "netdevsim configured to reject unbound programs.",
0867 args)
0868 sim.wait_for_flush()
0869
0870 start_test("Test non-0 chain offload...")
0871 ret, _, err = sim.cls_bpf_add_filter(obj, chain=1, prio=1, handle=1,
0872 skip_sw=True,
0873 fail=False, include_stderr=True)
0874 fail(ret == 0, "Offloaded a filter to chain other than 0")
0875 check_extack(err, "Driver supports only offload of chain 0.", args)
0876 sim.tc_flush_filters()
0877
0878 start_test("Test TC replace...")
0879 sim.cls_bpf_add_filter(obj, prio=1, handle=1)
0880 sim.cls_bpf_add_filter(obj, op="replace", prio=1, handle=1)
0881 sim.cls_filter_op(op="delete", prio=1, handle=1, cls="bpf")
0882
0883 sim.cls_bpf_add_filter(obj, prio=1, handle=1, skip_sw=True)
0884 sim.cls_bpf_add_filter(obj, op="replace", prio=1, handle=1, skip_sw=True)
0885 sim.cls_filter_op(op="delete", prio=1, handle=1, cls="bpf")
0886
0887 sim.cls_bpf_add_filter(obj, prio=1, handle=1, skip_hw=True)
0888 sim.cls_bpf_add_filter(obj, op="replace", prio=1, handle=1, skip_hw=True)
0889 sim.cls_filter_op(op="delete", prio=1, handle=1, cls="bpf")
0890
0891 start_test("Test TC replace bad flags...")
0892 for i in range(3):
0893 for j in range(3):
0894 ret, _ = sim.cls_bpf_add_filter(obj, op="replace", prio=1, handle=1,
0895 skip_sw=(j == 1), skip_hw=(j == 2),
0896 fail=False)
0897 fail(bool(ret) != bool(j),
0898 "Software TC incorrect load in replace test, iteration %d" %
0899 (j))
0900 sim.cls_filter_op(op="delete", prio=1, handle=1, cls="bpf")
0901
0902 start_test("Test spurious extack from the driver...")
0903 test_spurios_extack(sim, obj, False, "netdevsim")
0904 test_spurios_extack(sim, obj, True, "netdevsim")
0905
0906 sim.set_ethtool_tc_offloads(False)
0907
0908 test_spurios_extack(sim, obj, False, "TC offload is disabled")
0909 test_spurios_extack(sim, obj, True, "TC offload is disabled")
0910
0911 sim.set_ethtool_tc_offloads(True)
0912
0913 sim.tc_flush_filters()
0914
0915 start_test("Test TC offloads failure...")
0916 sim.dfs["dev/bpf_bind_verifier_accept"] = 0
0917 ret, _, err = sim.cls_bpf_add_filter(obj, verbose=True, skip_sw=True,
0918 fail=False, include_stderr=True)
0919 fail(ret == 0, "TC filter did not reject with TC offloads enabled")
0920 check_verifier_log(err, "[netdevsim] Hello from netdevsim!")
0921 sim.dfs["dev/bpf_bind_verifier_accept"] = 1
0922
0923 start_test("Test TC offloads work...")
0924 ret, _, err = sim.cls_bpf_add_filter(obj, verbose=True, skip_sw=True,
0925 fail=False, include_stderr=True)
0926 fail(ret != 0, "TC filter did not load with TC offloads enabled")
0927
0928 start_test("Test TC offload basics...")
0929 dfs = simdev.dfs_get_bound_progs(expected=1)
0930 progs = bpftool_prog_list(expected=1)
0931 ingress = sim.tc_show_ingress(expected=1)
0932
0933 dprog = dfs[0]
0934 prog = progs[0]
0935 fltr = ingress[0]
0936 fail(fltr["skip_hw"], "TC does reports 'skip_hw' on offloaded filter")
0937 fail(not fltr["in_hw"], "TC does not report 'in_hw' for offloaded filter")
0938 fail(not fltr["skip_sw"], "TC does not report 'skip_sw' back")
0939
0940 start_test("Test TC offload is device-bound...")
0941 fail(str(prog["id"]) != fltr["id"], "Program IDs don't match")
0942 fail(prog["tag"] != fltr["tag"], "Program tags don't match")
0943 fail(fltr["id"] != dprog["id"], "Program IDs don't match")
0944 fail(dprog["state"] != "xlated", "Offloaded program state not translated")
0945 fail(dprog["loaded"] != "Y", "Offloaded program is not loaded")
0946
0947 start_test("Test disabling TC offloads is rejected while filters installed...")
0948 ret, _ = sim.set_ethtool_tc_offloads(False, fail=False)
0949 fail(ret == 0, "Driver should refuse to disable TC offloads with filters installed...")
0950 sim.set_ethtool_tc_offloads(True)
0951
0952 start_test("Test qdisc removal frees things...")
0953 sim.tc_flush_filters()
0954 sim.tc_show_ingress(expected=0)
0955
0956 start_test("Test disabling TC offloads is OK without filters...")
0957 ret, _ = sim.set_ethtool_tc_offloads(False, fail=False)
0958 fail(ret != 0,
0959 "Driver refused to disable TC offloads without filters installed...")
0960
0961 sim.set_ethtool_tc_offloads(True)
0962
0963 start_test("Test destroying device gets rid of TC filters...")
0964 sim.cls_bpf_add_filter(obj, skip_sw=True)
0965 simdev.remove()
0966 bpftool_prog_list_wait(expected=0)
0967
0968 simdev = NetdevSimDev()
0969 sim, = simdev.nsims
0970 sim.set_ethtool_tc_offloads(True)
0971
0972 start_test("Test destroying device gets rid of XDP...")
0973 sim.set_xdp(obj, "offload")
0974 simdev.remove()
0975 bpftool_prog_list_wait(expected=0)
0976
0977 simdev = NetdevSimDev()
0978 sim, = simdev.nsims
0979 sim.set_ethtool_tc_offloads(True)
0980
0981 start_test("Test XDP prog reporting...")
0982 sim.set_xdp(obj, "drv")
0983 ipl = sim.ip_link_show(xdp=True)
0984 progs = bpftool_prog_list(expected=1)
0985 fail(ipl["xdp"]["prog"]["id"] != progs[0]["id"],
0986 "Loaded program has wrong ID")
0987
0988 start_test("Test XDP prog replace without force...")
0989 ret, _ = sim.set_xdp(obj, "drv", fail=False)
0990 fail(ret == 0, "Replaced XDP program without -force")
0991 sim.wait_for_flush(total=1)
0992
0993 start_test("Test XDP prog replace with force...")
0994 ret, _ = sim.set_xdp(obj, "drv", force=True, fail=False)
0995 fail(ret != 0, "Could not replace XDP program with -force")
0996 bpftool_prog_list_wait(expected=1)
0997 ipl = sim.ip_link_show(xdp=True)
0998 progs = bpftool_prog_list(expected=1)
0999 fail(ipl["xdp"]["prog"]["id"] != progs[0]["id"],
1000 "Loaded program has wrong ID")
1001 fail("dev" in progs[0].keys(),
1002 "Device parameters reported for non-offloaded program")
1003
1004 start_test("Test XDP prog replace with bad flags...")
1005 ret, _, err = sim.set_xdp(obj, "generic", force=True,
1006 fail=False, include_stderr=True)
1007 fail(ret == 0, "Replaced XDP program with a program in different mode")
1008 check_extack(err,
1009 "Native and generic XDP can't be active at the same time.",
1010 args)
1011
1012 start_test("Test MTU restrictions...")
1013 ret, _ = sim.set_mtu(9000, fail=False)
1014 fail(ret == 0,
1015 "Driver should refuse to increase MTU to 9000 with XDP loaded...")
1016 sim.unset_xdp("drv")
1017 bpftool_prog_list_wait(expected=0)
1018 sim.set_mtu(9000)
1019 ret, _, err = sim.set_xdp(obj, "drv", fail=False, include_stderr=True)
1020 fail(ret == 0, "Driver should refuse to load program with MTU of 9000...")
1021 check_extack_nsim(err, "MTU too large w/ XDP enabled.", args)
1022 sim.set_mtu(1500)
1023
1024 sim.wait_for_flush()
1025 start_test("Test non-offload XDP attaching to HW...")
1026 bpftool_prog_load("sample_ret0.o", "/sys/fs/bpf/nooffload")
1027 nooffload = bpf_pinned("/sys/fs/bpf/nooffload")
1028 ret, _, err = sim.set_xdp(nooffload, "offload",
1029 fail=False, include_stderr=True)
1030 fail(ret == 0, "attached non-offloaded XDP program to HW")
1031 check_extack_nsim(err, "xdpoffload of non-bound program.", args)
1032 rm("/sys/fs/bpf/nooffload")
1033
1034 start_test("Test offload XDP attaching to drv...")
1035 bpftool_prog_load("sample_ret0.o", "/sys/fs/bpf/offload",
1036 dev=sim['ifname'])
1037 offload = bpf_pinned("/sys/fs/bpf/offload")
1038 ret, _, err = sim.set_xdp(offload, "drv", fail=False, include_stderr=True)
1039 fail(ret == 0, "attached offloaded XDP program to drv")
1040 check_extack(err, "Using device-bound program without HW_MODE flag is not supported.", args)
1041 rm("/sys/fs/bpf/offload")
1042 sim.wait_for_flush()
1043
1044 start_test("Test XDP load failure...")
1045 sim.dfs["dev/bpf_bind_verifier_accept"] = 0
1046 ret, _, err = bpftool_prog_load("sample_ret0.o", "/sys/fs/bpf/offload",
1047 dev=sim['ifname'], fail=False, include_stderr=True)
1048 fail(ret == 0, "verifier should fail on load")
1049 check_verifier_log(err, "[netdevsim] Hello from netdevsim!")
1050 sim.dfs["dev/bpf_bind_verifier_accept"] = 1
1051 sim.wait_for_flush()
1052
1053 start_test("Test XDP offload...")
1054 _, _, err = sim.set_xdp(obj, "offload", verbose=True, include_stderr=True)
1055 ipl = sim.ip_link_show(xdp=True)
1056 link_xdp = ipl["xdp"]["prog"]
1057 progs = bpftool_prog_list(expected=1)
1058 prog = progs[0]
1059 fail(link_xdp["id"] != prog["id"], "Loaded program has wrong ID")
1060
1061 start_test("Test XDP offload is device bound...")
1062 dfs = simdev.dfs_get_bound_progs(expected=1)
1063 dprog = dfs[0]
1064
1065 fail(prog["id"] != link_xdp["id"], "Program IDs don't match")
1066 fail(prog["tag"] != link_xdp["tag"], "Program tags don't match")
1067 fail(str(link_xdp["id"]) != dprog["id"], "Program IDs don't match")
1068 fail(dprog["state"] != "xlated", "Offloaded program state not translated")
1069 fail(dprog["loaded"] != "Y", "Offloaded program is not loaded")
1070
1071 start_test("Test removing XDP program many times...")
1072 sim.unset_xdp("offload")
1073 sim.unset_xdp("offload")
1074 sim.unset_xdp("drv")
1075 sim.unset_xdp("drv")
1076 sim.unset_xdp("")
1077 sim.unset_xdp("")
1078 bpftool_prog_list_wait(expected=0)
1079
1080 start_test("Test attempt to use a program for a wrong device...")
1081 simdev2 = NetdevSimDev()
1082 sim2, = simdev2.nsims
1083 sim2.set_xdp(obj, "offload")
1084 pin_file, pinned = pin_prog("/sys/fs/bpf/tmp")
1085
1086 ret, _, err = sim.set_xdp(pinned, "offload",
1087 fail=False, include_stderr=True)
1088 fail(ret == 0, "Pinned program loaded for a different device accepted")
1089 check_extack_nsim(err, "program bound to different dev.", args)
1090 simdev2.remove()
1091 ret, _, err = sim.set_xdp(pinned, "offload",
1092 fail=False, include_stderr=True)
1093 fail(ret == 0, "Pinned program loaded for a removed device accepted")
1094 check_extack_nsim(err, "xdpoffload of non-bound program.", args)
1095 rm(pin_file)
1096 bpftool_prog_list_wait(expected=0)
1097
1098 simdev, sim = test_multi_prog(simdev, sim, obj, "", 1)
1099 simdev, sim = test_multi_prog(simdev, sim, obj, "drv", 1)
1100 simdev, sim = test_multi_prog(simdev, sim, obj, "generic", 2)
1101
1102 start_test("Test mixing of TC and XDP...")
1103 sim.tc_add_ingress()
1104 sim.set_xdp(obj, "offload")
1105 ret, _, err = sim.cls_bpf_add_filter(obj, skip_sw=True,
1106 fail=False, include_stderr=True)
1107 fail(ret == 0, "Loading TC when XDP active should fail")
1108 check_extack_nsim(err, "driver and netdev offload states mismatch.", args)
1109 sim.unset_xdp("offload")
1110 sim.wait_for_flush()
1111
1112 sim.cls_bpf_add_filter(obj, skip_sw=True)
1113 ret, _, err = sim.set_xdp(obj, "offload", fail=False, include_stderr=True)
1114 fail(ret == 0, "Loading XDP when TC active should fail")
1115 check_extack_nsim(err, "TC program is already loaded.", args)
1116
1117 start_test("Test binding TC from pinned...")
1118 pin_file, pinned = pin_prog("/sys/fs/bpf/tmp")
1119 sim.tc_flush_filters(bound=1, total=1)
1120 sim.cls_bpf_add_filter(pinned, da=True, skip_sw=True)
1121 sim.tc_flush_filters(bound=1, total=1)
1122
1123 start_test("Test binding XDP from pinned...")
1124 sim.set_xdp(obj, "offload")
1125 pin_file, pinned = pin_prog("/sys/fs/bpf/tmp2", idx=1)
1126
1127 sim.set_xdp(pinned, "offload", force=True)
1128 sim.unset_xdp("offload")
1129 sim.set_xdp(pinned, "offload", force=True)
1130 sim.unset_xdp("offload")
1131
1132 start_test("Test offload of wrong type fails...")
1133 ret, _ = sim.cls_bpf_add_filter(pinned, da=True, skip_sw=True, fail=False)
1134 fail(ret == 0, "Managed to attach XDP program to TC")
1135
1136 start_test("Test asking for TC offload of two filters...")
1137 sim.cls_bpf_add_filter(obj, da=True, skip_sw=True)
1138 ret, _, err = sim.cls_bpf_add_filter(obj, da=True, skip_sw=True,
1139 fail=False, include_stderr=True)
1140 fail(ret == 0, "Managed to offload two TC filters at the same time")
1141 check_extack_nsim(err, "driver and netdev offload states mismatch.", args)
1142
1143 sim.tc_flush_filters(bound=2, total=2)
1144
1145 start_test("Test if netdev removal waits for translation...")
1146 delay_msec = 500
1147 sim.dfs["dev/bpf_bind_verifier_delay"] = delay_msec
1148 start = time.time()
1149 cmd_line = "tc filter add dev %s ingress bpf %s da skip_sw" % \
1150 (sim['ifname'], obj)
1151 tc_proc = cmd(cmd_line, background=True, fail=False)
1152
1153 while simdev.dfs_num_bound_progs() <= 2:
1154 pass
1155 simdev.remove()
1156 end = time.time()
1157 ret, _ = cmd_result(tc_proc, fail=False)
1158 time_diff = end - start
1159 log("Time", "start:\t%s\nend:\t%s\ndiff:\t%s" % (start, end, time_diff))
1160
1161 fail(ret == 0, "Managed to load TC filter on a unregistering device")
1162 delay_sec = delay_msec * 0.001
1163 fail(time_diff < delay_sec, "Removal process took %s, expected %s" %
1164 (time_diff, delay_sec))
1165
1166
1167 clean_up()
1168 bpftool_prog_list_wait(expected=0)
1169
1170 simdev = NetdevSimDev()
1171 sim, = simdev.nsims
1172 map_obj = bpf_obj("sample_map_ret0.o")
1173 start_test("Test loading program with maps...")
1174 sim.set_xdp(map_obj, "offload", JSON=False)
1175
1176 start_test("Test bpftool bound info reporting (own ns)...")
1177 check_dev_info(False, "")
1178
1179 start_test("Test bpftool bound info reporting (other ns)...")
1180 ns = mknetns()
1181 sim.set_ns(ns)
1182 check_dev_info(True, "")
1183
1184 start_test("Test bpftool bound info reporting (remote ns)...")
1185 check_dev_info(False, ns)
1186
1187 start_test("Test bpftool bound info reporting (back to own ns)...")
1188 sim.set_ns("")
1189 check_dev_info(False, "")
1190
1191 prog_file, _ = pin_prog("/sys/fs/bpf/tmp_prog")
1192 map_file, _ = pin_map("/sys/fs/bpf/tmp_map", idx=1, expected=2)
1193 simdev.remove()
1194
1195 start_test("Test bpftool bound info reporting (removed dev)...")
1196 check_dev_info_removed(prog_file=prog_file, map_file=map_file)
1197
1198
1199 clean_up()
1200 bpftool_prog_list_wait(expected=0)
1201
1202 simdev = NetdevSimDev()
1203 sim, = simdev.nsims
1204
1205 start_test("Test map update (no flags)...")
1206 sim.set_xdp(map_obj, "offload", JSON=False)
1207 maps = bpftool_map_list(expected=2)
1208 array = maps[0] if maps[0]["type"] == "array" else maps[1]
1209 htab = maps[0] if maps[0]["type"] == "hash" else maps[1]
1210 for m in maps:
1211 for i in range(2):
1212 bpftool("map update id %d key %s value %s" %
1213 (m["id"], int2str("I", i), int2str("Q", i * 3)))
1214
1215 for m in maps:
1216 ret, _ = bpftool("map update id %d key %s value %s" %
1217 (m["id"], int2str("I", 3), int2str("Q", 3 * 3)),
1218 fail=False)
1219 fail(ret == 0, "added too many entries")
1220
1221 start_test("Test map update (exists)...")
1222 for m in maps:
1223 for i in range(2):
1224 bpftool("map update id %d key %s value %s exist" %
1225 (m["id"], int2str("I", i), int2str("Q", i * 3)))
1226
1227 for m in maps:
1228 ret, err = bpftool("map update id %d key %s value %s exist" %
1229 (m["id"], int2str("I", 3), int2str("Q", 3 * 3)),
1230 fail=False)
1231 fail(ret == 0, "updated non-existing key")
1232 fail(err["error"].find("No such file or directory") == -1,
1233 "expected ENOENT, error is '%s'" % (err["error"]))
1234
1235 start_test("Test map update (noexist)...")
1236 for m in maps:
1237 for i in range(2):
1238 ret, err = bpftool("map update id %d key %s value %s noexist" %
1239 (m["id"], int2str("I", i), int2str("Q", i * 3)),
1240 fail=False)
1241 fail(ret == 0, "updated existing key")
1242 fail(err["error"].find("File exists") == -1,
1243 "expected EEXIST, error is '%s'" % (err["error"]))
1244
1245 start_test("Test map dump...")
1246 for m in maps:
1247 _, entries = bpftool("map dump id %d" % (m["id"]))
1248 for i in range(2):
1249 key = str2int(entries[i]["key"])
1250 fail(key != i, "expected key %d, got %d" % (key, i))
1251 val = str2int(entries[i]["value"])
1252 fail(val != i * 3, "expected value %d, got %d" % (val, i * 3))
1253
1254 start_test("Test map getnext...")
1255 for m in maps:
1256 _, entry = bpftool("map getnext id %d" % (m["id"]))
1257 key = str2int(entry["next_key"])
1258 fail(key != 0, "next key %d, expected %d" % (key, 0))
1259 _, entry = bpftool("map getnext id %d key %s" %
1260 (m["id"], int2str("I", 0)))
1261 key = str2int(entry["next_key"])
1262 fail(key != 1, "next key %d, expected %d" % (key, 1))
1263 ret, err = bpftool("map getnext id %d key %s" %
1264 (m["id"], int2str("I", 1)), fail=False)
1265 fail(ret == 0, "got next key past the end of map")
1266 fail(err["error"].find("No such file or directory") == -1,
1267 "expected ENOENT, error is '%s'" % (err["error"]))
1268
1269 start_test("Test map delete (htab)...")
1270 for i in range(2):
1271 bpftool("map delete id %d key %s" % (htab["id"], int2str("I", i)))
1272
1273 start_test("Test map delete (array)...")
1274 for i in range(2):
1275 ret, err = bpftool("map delete id %d key %s" %
1276 (htab["id"], int2str("I", i)), fail=False)
1277 fail(ret == 0, "removed entry from an array")
1278 fail(err["error"].find("No such file or directory") == -1,
1279 "expected ENOENT, error is '%s'" % (err["error"]))
1280
1281 start_test("Test map remove...")
1282 sim.unset_xdp("offload")
1283 bpftool_map_list_wait(expected=0)
1284 simdev.remove()
1285
1286 simdev = NetdevSimDev()
1287 sim, = simdev.nsims
1288 sim.set_xdp(map_obj, "offload", JSON=False)
1289 simdev.remove()
1290 bpftool_map_list_wait(expected=0)
1291
1292 start_test("Test map creation fail path...")
1293 simdev = NetdevSimDev()
1294 sim, = simdev.nsims
1295 sim.dfs["bpf_map_accept"] = "N"
1296 ret, _ = sim.set_xdp(map_obj, "offload", JSON=False, fail=False)
1297 fail(ret == 0,
1298 "netdevsim didn't refuse to create a map with offload disabled")
1299
1300 simdev.remove()
1301
1302 start_test("Test multi-dev ASIC program reuse...")
1303 simdevA = NetdevSimDev()
1304 simA, = simdevA.nsims
1305 simdevB = NetdevSimDev(3)
1306 simB1, simB2, simB3 = simdevB.nsims
1307 sims = (simA, simB1, simB2, simB3)
1308 simB = (simB1, simB2, simB3)
1309
1310 bpftool_prog_load("sample_map_ret0.o", "/sys/fs/bpf/nsimA",
1311 dev=simA['ifname'])
1312 progA = bpf_pinned("/sys/fs/bpf/nsimA")
1313 bpftool_prog_load("sample_map_ret0.o", "/sys/fs/bpf/nsimB",
1314 dev=simB1['ifname'])
1315 progB = bpf_pinned("/sys/fs/bpf/nsimB")
1316
1317 simA.set_xdp(progA, "offload", JSON=False)
1318 for d in simdevB.nsims:
1319 d.set_xdp(progB, "offload", JSON=False)
1320
1321 start_test("Test multi-dev ASIC cross-dev replace...")
1322 ret, _ = simA.set_xdp(progB, "offload", force=True, JSON=False, fail=False)
1323 fail(ret == 0, "cross-ASIC program allowed")
1324 for d in simdevB.nsims:
1325 ret, _ = d.set_xdp(progA, "offload", force=True, JSON=False, fail=False)
1326 fail(ret == 0, "cross-ASIC program allowed")
1327
1328 start_test("Test multi-dev ASIC cross-dev install...")
1329 for d in sims:
1330 d.unset_xdp("offload")
1331
1332 ret, _, err = simA.set_xdp(progB, "offload", force=True, JSON=False,
1333 fail=False, include_stderr=True)
1334 fail(ret == 0, "cross-ASIC program allowed")
1335 check_extack_nsim(err, "program bound to different dev.", args)
1336 for d in simdevB.nsims:
1337 ret, _, err = d.set_xdp(progA, "offload", force=True, JSON=False,
1338 fail=False, include_stderr=True)
1339 fail(ret == 0, "cross-ASIC program allowed")
1340 check_extack_nsim(err, "program bound to different dev.", args)
1341
1342 start_test("Test multi-dev ASIC cross-dev map reuse...")
1343
1344 mapA = bpftool("prog show %s" % (progA))[1]["map_ids"][0]
1345 mapB = bpftool("prog show %s" % (progB))[1]["map_ids"][0]
1346
1347 ret, _ = bpftool_prog_load("sample_map_ret0.o", "/sys/fs/bpf/nsimB_",
1348 dev=simB3['ifname'],
1349 maps=["idx 0 id %d" % (mapB)],
1350 fail=False)
1351 fail(ret != 0, "couldn't reuse a map on the same ASIC")
1352 rm("/sys/fs/bpf/nsimB_")
1353
1354 ret, _, err = bpftool_prog_load("sample_map_ret0.o", "/sys/fs/bpf/nsimA_",
1355 dev=simA['ifname'],
1356 maps=["idx 0 id %d" % (mapB)],
1357 fail=False, include_stderr=True)
1358 fail(ret == 0, "could reuse a map on a different ASIC")
1359 fail(err.count("offload device mismatch between prog and map") == 0,
1360 "error message missing for cross-ASIC map")
1361
1362 ret, _, err = bpftool_prog_load("sample_map_ret0.o", "/sys/fs/bpf/nsimB_",
1363 dev=simB1['ifname'],
1364 maps=["idx 0 id %d" % (mapA)],
1365 fail=False, include_stderr=True)
1366 fail(ret == 0, "could reuse a map on a different ASIC")
1367 fail(err.count("offload device mismatch between prog and map") == 0,
1368 "error message missing for cross-ASIC map")
1369
1370 start_test("Test multi-dev ASIC cross-dev destruction...")
1371 bpftool_prog_list_wait(expected=2)
1372
1373 simdevA.remove()
1374 bpftool_prog_list_wait(expected=1)
1375
1376 ifnameB = bpftool("prog show %s" % (progB))[1]["dev"]["ifname"]
1377 fail(ifnameB != simB1['ifname'], "program not bound to original device")
1378 simB1.remove()
1379 bpftool_prog_list_wait(expected=1)
1380
1381 start_test("Test multi-dev ASIC cross-dev destruction - move...")
1382 ifnameB = bpftool("prog show %s" % (progB))[1]["dev"]["ifname"]
1383 fail(ifnameB not in (simB2['ifname'], simB3['ifname']),
1384 "program not bound to remaining devices")
1385
1386 simB2.remove()
1387 ifnameB = bpftool("prog show %s" % (progB))[1]["dev"]["ifname"]
1388 fail(ifnameB != simB3['ifname'], "program not bound to remaining device")
1389
1390 simB3.remove()
1391 simdevB.remove()
1392 bpftool_prog_list_wait(expected=0)
1393
1394 start_test("Test multi-dev ASIC cross-dev destruction - orphaned...")
1395 ret, out = bpftool("prog show %s" % (progB), fail=False)
1396 fail(ret == 0, "got information about orphaned program")
1397 fail("error" not in out, "no error reported for get info on orphaned")
1398 fail(out["error"] != "can't get prog info: No such device",
1399 "wrong error for get info on orphaned")
1400
1401 print("%s: OK" % (os.path.basename(__file__)))
1402
1403 finally:
1404 log("Clean up...", "", level=1)
1405 log_level_inc()
1406 clean_up()