Back to home page

OSCL-LXR

 
 

    


0001 #!/usr/bin/env python3
0002 
0003 # Copyright (C) 2017 Netronome Systems, Inc.
0004 # Copyright (c) 2019 Mellanox Technologies. All rights reserved
0005 #
0006 # This software is licensed under the GNU General License Version 2,
0007 # June 1991 as shown in the file COPYING in the top-level directory of this
0008 # source tree.
0009 #
0010 # THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS"
0011 # WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING,
0012 # BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
0013 # FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE
0014 # OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME
0015 # THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
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 = [] # devices we created for clean up
0038 files = [] # files to be removed
0039 netns = [] # net namespaces to be removed
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     # Remove the base progs
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     # Remove the base maps
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                 # We need to init trap_flow_action_cookie before read it
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         # As probe of netdevsim device might happen from a workqueue,
0364         # so wait here until all netdevs appear.
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         # In case udev renamed the netdev to according to new schema,
0431         # check if the name matches the port_index.
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         # No JSON support, oh well...
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 # Parse command line
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 # Check permissions
0764 skip(os.getuid() != 0, "test must be run as root")
0765 
0766 # Check tools
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' # created on each bpftool invocation
0773 ]
0774 
0775 # Check netdevsim
0776 ret, out = cmd("modprobe netdevsim", fail=False)
0777 skip(ret != 0, "netdevsim module could not be loaded")
0778 
0779 # Check debugfs
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 # Check samples are compiled
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 # Check if iproute2 is built with libmnl (needed by extack support)
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 # Check if net namespaces seem to work
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     # Wait for the verifier to start
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     # Remove all pinned files and reinstantiate the netdev
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) # map fixup msg breaks JSON
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     # Remove all pinned files and reinstantiate the netdev
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) # map fixup msg breaks JSON
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) # map fixup msg breaks JSON
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()