Linux Audio

Check our new training course

Loading...
v4.17
   1#!/usr/bin/python3
   2
   3# Copyright (C) 2017 Netronome Systems, Inc.
 
   4#
   5# This software is licensed under the GNU General License Version 2,
   6# June 1991 as shown in the file COPYING in the top-level directory of this
   7# source tree.
   8#
   9# THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS"
  10# WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING,
  11# BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
  12# FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE
  13# OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME
  14# THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
  15
  16from datetime import datetime
  17import argparse
 
  18import json
  19import os
  20import pprint
  21import random
 
 
  22import string
  23import struct
  24import subprocess
  25import time
 
  26
  27logfile = None
  28log_level = 1
  29skip_extack = False
  30bpf_test_dir = os.path.dirname(os.path.realpath(__file__))
  31pp = pprint.PrettyPrinter()
  32devs = [] # devices we created for clean up
  33files = [] # files to be removed
  34netns = [] # net namespaces to be removed
  35
  36def log_get_sec(level=0):
  37    return "*" * (log_level + level)
  38
  39def log_level_inc(add=1):
  40    global log_level
  41    log_level += add
  42
  43def log_level_dec(sub=1):
  44    global log_level
  45    log_level -= sub
  46
  47def log_level_set(level):
  48    global log_level
  49    log_level = level
  50
  51def log(header, data, level=None):
  52    """
  53    Output to an optional log.
  54    """
  55    if logfile is None:
  56        return
  57    if level is not None:
  58        log_level_set(level)
  59
  60    if not isinstance(data, str):
  61        data = pp.pformat(data)
  62
  63    if len(header):
  64        logfile.write("\n" + log_get_sec() + " ")
  65        logfile.write(header)
  66    if len(header) and len(data.strip()):
  67        logfile.write("\n")
  68    logfile.write(data)
  69
  70def skip(cond, msg):
  71    if not cond:
  72        return
  73    print("SKIP: " + msg)
  74    log("SKIP: " + msg, "", level=1)
  75    os.sys.exit(0)
  76
  77def fail(cond, msg):
  78    if not cond:
  79        return
  80    print("FAIL: " + msg)
  81    log("FAIL: " + msg, "", level=1)
 
 
  82    os.sys.exit(1)
  83
  84def start_test(msg):
  85    log(msg, "", level=1)
  86    log_level_inc()
  87    print(msg)
  88
  89def cmd(cmd, shell=True, include_stderr=False, background=False, fail=True):
  90    """
  91    Run a command in subprocess and return tuple of (retval, stdout);
  92    optionally return stderr as well as third value.
  93    """
  94    proc = subprocess.Popen(cmd, shell=shell, stdout=subprocess.PIPE,
  95                            stderr=subprocess.PIPE)
  96    if background:
  97        msg = "%s START: %s" % (log_get_sec(1),
  98                                datetime.now().strftime("%H:%M:%S.%f"))
  99        log("BKG " + proc.args, msg)
 100        return proc
 101
 102    return cmd_result(proc, include_stderr=include_stderr, fail=fail)
 103
 104def cmd_result(proc, include_stderr=False, fail=False):
 105    stdout, stderr = proc.communicate()
 106    stdout = stdout.decode("utf-8")
 107    stderr = stderr.decode("utf-8")
 108    proc.stdout.close()
 109    proc.stderr.close()
 110
 111    stderr = "\n" + stderr
 112    if stderr[-1] == "\n":
 113        stderr = stderr[:-1]
 114
 115    sec = log_get_sec(1)
 116    log("CMD " + proc.args,
 117        "RETCODE: %d\n%s STDOUT:\n%s%s STDERR:%s\n%s END: %s" %
 118        (proc.returncode, sec, stdout, sec, stderr,
 119         sec, datetime.now().strftime("%H:%M:%S.%f")))
 120
 121    if proc.returncode != 0 and fail:
 122        if len(stderr) > 0 and stderr[-1] == "\n":
 123            stderr = stderr[:-1]
 124        raise Exception("Command failed: %s\n%s" % (proc.args, stderr))
 125
 126    if include_stderr:
 127        return proc.returncode, stdout, stderr
 128    else:
 129        return proc.returncode, stdout
 130
 131def rm(f):
 132    cmd("rm -f %s" % (f))
 133    if f in files:
 134        files.remove(f)
 135
 136def tool(name, args, flags, JSON=True, ns="", fail=True, include_stderr=False):
 137    params = ""
 138    if JSON:
 139        params += "%s " % (flags["json"])
 140
 141    if ns != "":
 142        ns = "ip netns exec %s " % (ns)
 143
 144    if include_stderr:
 145        ret, stdout, stderr = cmd(ns + name + " " + params + args,
 146                                  fail=fail, include_stderr=True)
 147    else:
 148        ret, stdout = cmd(ns + name + " " + params + args,
 149                          fail=fail, include_stderr=False)
 150
 151    if JSON and len(stdout.strip()) != 0:
 152        out = json.loads(stdout)
 153    else:
 154        out = stdout
 155
 156    if include_stderr:
 157        return ret, out, stderr
 158    else:
 159        return ret, out
 160
 161def bpftool(args, JSON=True, ns="", fail=True):
 162    return tool("bpftool", args, {"json":"-p"}, JSON=JSON, ns=ns, fail=fail)
 
 163
 164def bpftool_prog_list(expected=None, ns=""):
 165    _, progs = bpftool("prog show", JSON=True, ns=ns, fail=True)
 
 
 
 
 
 
 166    if expected is not None:
 167        if len(progs) != expected:
 168            fail(True, "%d BPF programs loaded, expected %d" %
 169                 (len(progs), expected))
 170    return progs
 171
 172def bpftool_map_list(expected=None, ns=""):
 173    _, maps = bpftool("map show", JSON=True, ns=ns, fail=True)
 
 
 174    if expected is not None:
 175        if len(maps) != expected:
 176            fail(True, "%d BPF maps loaded, expected %d" %
 177                 (len(maps), expected))
 178    return maps
 179
 180def bpftool_prog_list_wait(expected=0, n_retry=20):
 181    for i in range(n_retry):
 182        nprogs = len(bpftool_prog_list())
 183        if nprogs == expected:
 184            return
 185        time.sleep(0.05)
 186    raise Exception("Time out waiting for program counts to stabilize want %d, have %d" % (expected, nprogs))
 187
 188def bpftool_map_list_wait(expected=0, n_retry=20):
 189    for i in range(n_retry):
 190        nmaps = len(bpftool_map_list())
 191        if nmaps == expected:
 192            return
 193        time.sleep(0.05)
 194    raise Exception("Time out waiting for map counts to stabilize want %d, have %d" % (expected, nmaps))
 195
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 196def ip(args, force=False, JSON=True, ns="", fail=True, include_stderr=False):
 197    if force:
 198        args = "-force " + args
 199    return tool("ip", args, {"json":"-j"}, JSON=JSON, ns=ns,
 200                fail=fail, include_stderr=include_stderr)
 201
 202def tc(args, JSON=True, ns="", fail=True, include_stderr=False):
 203    return tool("tc", args, {"json":"-p"}, JSON=JSON, ns=ns,
 204                fail=fail, include_stderr=include_stderr)
 205
 206def ethtool(dev, opt, args, fail=True):
 207    return cmd("ethtool %s %s %s" % (opt, dev["ifname"], args), fail=fail)
 208
 209def bpf_obj(name, sec=".text", path=bpf_test_dir,):
 210    return "obj %s sec %s" % (os.path.join(path, name), sec)
 211
 212def bpf_pinned(name):
 213    return "pinned %s" % (name)
 214
 215def bpf_bytecode(bytecode):
 216    return "bytecode \"%s\"" % (bytecode)
 217
 218def mknetns(n_retry=10):
 219    for i in range(n_retry):
 220        name = ''.join([random.choice(string.ascii_letters) for i in range(8)])
 221        ret, _ = ip("netns add %s" % (name), fail=False)
 222        if ret == 0:
 223            netns.append(name)
 224            return name
 225    return None
 226
 227def int2str(fmt, val):
 228    ret = []
 229    for b in struct.pack(fmt, val):
 230        ret.append(int(b))
 231    return " ".join(map(lambda x: str(x), ret))
 232
 233def str2int(strtab):
 234    inttab = []
 235    for i in strtab:
 236        inttab.append(int(i, 16))
 237    ba = bytearray(inttab)
 238    if len(strtab) == 4:
 239        fmt = "I"
 240    elif len(strtab) == 8:
 241        fmt = "Q"
 242    else:
 243        raise Exception("String array of len %d can't be unpacked to an int" %
 244                        (len(strtab)))
 245    return struct.unpack(fmt, ba)[0]
 246
 247class DebugfsDir:
 248    """
 249    Class for accessing DebugFS directories as a dictionary.
 250    """
 251
 252    def __init__(self, path):
 253        self.path = path
 254        self._dict = self._debugfs_dir_read(path)
 255
 256    def __len__(self):
 257        return len(self._dict.keys())
 258
 259    def __getitem__(self, key):
 260        if type(key) is int:
 261            key = list(self._dict.keys())[key]
 262        return self._dict[key]
 263
 264    def __setitem__(self, key, value):
 265        log("DebugFS set %s = %s" % (key, value), "")
 266        log_level_inc()
 267
 268        cmd("echo '%s' > %s/%s" % (value, self.path, key))
 269        log_level_dec()
 270
 271        _, out = cmd('cat %s/%s' % (self.path, key))
 272        self._dict[key] = out.strip()
 273
 274    def _debugfs_dir_read(self, path):
 275        dfs = {}
 276
 277        log("DebugFS state for %s" % (path), "")
 278        log_level_inc(add=2)
 279
 280        _, out = cmd('ls ' + path)
 281        for f in out.split():
 
 
 
 282            p = os.path.join(path, f)
 
 
 
 283            if os.path.isfile(p):
 
 
 
 284                _, out = cmd('cat %s/%s' % (path, f))
 285                dfs[f] = out.strip()
 286            elif os.path.isdir(p):
 287                dfs[f] = DebugfsDir(p)
 288            else:
 289                raise Exception("%s is neither file nor directory" % (p))
 290
 291        log_level_dec()
 292        log("DebugFS state", dfs)
 293        log_level_dec()
 294
 295        return dfs
 296
 297class NetdevSim:
 298    """
 299    Class for netdevsim netdevice and its attributes.
 300    """
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 301
 302    def __init__(self):
 303        self.dev = self._netdevsim_create()
 304        devs.append(self)
 
 305
 306        self.ns = ""
 307
 308        self.dfs_dir = '/sys/kernel/debug/netdevsim/%s' % (self.dev['ifname'])
 309        self.dfs_refresh()
 310
 311    def __getitem__(self, key):
 312        return self.dev[key]
 313
 314    def _netdevsim_create(self):
 315        _, old  = ip("link show")
 316        ip("link add sim%d type netdevsim")
 317        _, new  = ip("link show")
 318
 319        for dev in new:
 320            f = filter(lambda x: x["ifname"] == dev["ifname"], old)
 321            if len(list(f)) == 0:
 322                return dev
 323
 324        raise Exception("failed to create netdevsim device")
 325
 326    def remove(self):
 327        devs.remove(self)
 328        ip("link del dev %s" % (self.dev["ifname"]), ns=self.ns)
 329
 330    def dfs_refresh(self):
 331        self.dfs = DebugfsDir(self.dfs_dir)
 332        return self.dfs
 333
 334    def dfs_num_bound_progs(self):
 335        path = os.path.join(self.dfs_dir, "bpf_bound_progs")
 336        _, progs = cmd('ls %s' % (path))
 337        return len(progs.split())
 338
 339    def dfs_get_bound_progs(self, expected):
 340        progs = DebugfsDir(os.path.join(self.dfs_dir, "bpf_bound_progs"))
 341        if expected is not None:
 342            if len(progs) != expected:
 343                fail(True, "%d BPF programs bound, expected %d" %
 344                     (len(progs), expected))
 345        return progs
 346
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 347    def wait_for_flush(self, bound=0, total=0, n_retry=20):
 348        for i in range(n_retry):
 349            nbound = self.dfs_num_bound_progs()
 350            nprogs = len(bpftool_prog_list())
 351            if nbound == bound and nprogs == total:
 352                return
 353            time.sleep(0.05)
 354        raise Exception("Time out waiting for program counts to stabilize want %d/%d, have %d bound, %d loaded" % (bound, total, nbound, nprogs))
 355
 356    def set_ns(self, ns):
 357        name = "1" if ns == "" else ns
 358        ip("link set dev %s netns %s" % (self.dev["ifname"], name), ns=self.ns)
 359        self.ns = ns
 360
 361    def set_mtu(self, mtu, fail=True):
 362        return ip("link set dev %s mtu %d" % (self.dev["ifname"], mtu),
 363                  fail=fail)
 364
 365    def set_xdp(self, bpf, mode, force=False, JSON=True, verbose=False,
 366                fail=True, include_stderr=False):
 367        if verbose:
 368            bpf += " verbose"
 369        return ip("link set dev %s xdp%s %s" % (self.dev["ifname"], mode, bpf),
 370                  force=force, JSON=JSON,
 371                  fail=fail, include_stderr=include_stderr)
 372
 373    def unset_xdp(self, mode, force=False, JSON=True,
 374                  fail=True, include_stderr=False):
 375        return ip("link set dev %s xdp%s off" % (self.dev["ifname"], mode),
 376                  force=force, JSON=JSON,
 377                  fail=fail, include_stderr=include_stderr)
 378
 379    def ip_link_show(self, xdp):
 380        _, link = ip("link show dev %s" % (self['ifname']))
 381        if len(link) > 1:
 382            raise Exception("Multiple objects on ip link show")
 383        if len(link) < 1:
 384            return {}
 385        fail(xdp != "xdp" in link,
 386             "XDP program not reporting in iplink (reported %s, expected %s)" %
 387             ("xdp" in link, xdp))
 388        return link[0]
 389
 390    def tc_add_ingress(self):
 391        tc("qdisc add dev %s ingress" % (self['ifname']))
 392
 393    def tc_del_ingress(self):
 394        tc("qdisc del dev %s ingress" % (self['ifname']))
 395
 396    def tc_flush_filters(self, bound=0, total=0):
 397        self.tc_del_ingress()
 398        self.tc_add_ingress()
 399        self.wait_for_flush(bound=bound, total=total)
 400
 401    def tc_show_ingress(self, expected=None):
 402        # No JSON support, oh well...
 403        flags = ["skip_sw", "skip_hw", "in_hw"]
 404        named = ["protocol", "pref", "chain", "handle", "id", "tag"]
 405
 406        args = "-s filter show dev %s ingress" % (self['ifname'])
 407        _, out = tc(args, JSON=False)
 408
 409        filters = []
 410        lines = out.split('\n')
 411        for line in lines:
 412            words = line.split()
 413            if "handle" not in words:
 414                continue
 415            fltr = {}
 416            for flag in flags:
 417                fltr[flag] = flag in words
 418            for name in named:
 419                try:
 420                    idx = words.index(name)
 421                    fltr[name] = words[idx + 1]
 422                except ValueError:
 423                    pass
 424            filters.append(fltr)
 425
 426        if expected is not None:
 427            fail(len(filters) != expected,
 428                 "%d ingress filters loaded, expected %d" %
 429                 (len(filters), expected))
 430        return filters
 431
 432    def cls_filter_op(self, op, qdisc="ingress", prio=None, handle=None,
 433                      chain=None, cls="", params="",
 434                      fail=True, include_stderr=False):
 435        spec = ""
 436        if prio is not None:
 437            spec += " prio %d" % (prio)
 438        if handle:
 439            spec += " handle %s" % (handle)
 440        if chain is not None:
 441            spec += " chain %d" % (chain)
 442
 443        return tc("filter {op} dev {dev} {qdisc} {spec} {cls} {params}"\
 444                  .format(op=op, dev=self['ifname'], qdisc=qdisc, spec=spec,
 445                          cls=cls, params=params),
 446                  fail=fail, include_stderr=include_stderr)
 447
 448    def cls_bpf_add_filter(self, bpf, op="add", prio=None, handle=None,
 449                           chain=None, da=False, verbose=False,
 450                           skip_sw=False, skip_hw=False,
 451                           fail=True, include_stderr=False):
 452        cls = "bpf " + bpf
 453
 454        params = ""
 455        if da:
 456            params += " da"
 457        if verbose:
 458            params += " verbose"
 459        if skip_sw:
 460            params += " skip_sw"
 461        if skip_hw:
 462            params += " skip_hw"
 463
 464        return self.cls_filter_op(op=op, prio=prio, handle=handle, cls=cls,
 465                                  chain=chain, params=params,
 466                                  fail=fail, include_stderr=include_stderr)
 467
 468    def set_ethtool_tc_offloads(self, enable, fail=True):
 469        args = "hw-tc-offload %s" % ("on" if enable else "off")
 470        return ethtool(self, "-K", args, fail=fail)
 471
 472################################################################################
 473def clean_up():
 474    global files, netns, devs
 475
 476    for dev in devs:
 477        dev.remove()
 478    for f in files:
 479        cmd("rm -f %s" % (f))
 480    for ns in netns:
 481        cmd("ip netns delete %s" % (ns))
 482    files = []
 483    netns = []
 484
 485def pin_prog(file_name, idx=0):
 486    progs = bpftool_prog_list(expected=(idx + 1))
 487    prog = progs[idx]
 488    bpftool("prog pin id %d %s" % (prog["id"], file_name))
 489    files.append(file_name)
 490
 491    return file_name, bpf_pinned(file_name)
 492
 493def pin_map(file_name, idx=0, expected=1):
 494    maps = bpftool_map_list(expected=expected)
 495    m = maps[idx]
 496    bpftool("map pin id %d %s" % (m["id"], file_name))
 497    files.append(file_name)
 498
 499    return file_name, bpf_pinned(file_name)
 500
 501def check_dev_info_removed(prog_file=None, map_file=None):
 502    bpftool_prog_list(expected=0)
 
 503    ret, err = bpftool("prog show pin %s" % (prog_file), fail=False)
 504    fail(ret == 0, "Showing prog with removed device did not fail")
 505    fail(err["error"].find("No such device") == -1,
 506         "Showing prog with removed device expected ENODEV, error is %s" %
 507         (err["error"]))
 508
 509    bpftool_map_list(expected=0)
 510    ret, err = bpftool("map show pin %s" % (map_file), fail=False)
 511    fail(ret == 0, "Showing map with removed device did not fail")
 512    fail(err["error"].find("No such device") == -1,
 513         "Showing map with removed device expected ENODEV, error is %s" %
 514         (err["error"]))
 515
 516def check_dev_info(other_ns, ns, prog_file=None, map_file=None, removed=False):
 517    progs = bpftool_prog_list(expected=1, ns=ns)
 518    prog = progs[0]
 519
 520    fail("dev" not in prog.keys(), "Device parameters not reported")
 521    dev = prog["dev"]
 522    fail("ifindex" not in dev.keys(), "Device parameters not reported")
 523    fail("ns_dev" not in dev.keys(), "Device parameters not reported")
 524    fail("ns_inode" not in dev.keys(), "Device parameters not reported")
 525
 526    if not other_ns:
 527        fail("ifname" not in dev.keys(), "Ifname not reported")
 528        fail(dev["ifname"] != sim["ifname"],
 529             "Ifname incorrect %s vs %s" % (dev["ifname"], sim["ifname"]))
 530    else:
 531        fail("ifname" in dev.keys(), "Ifname is reported for other ns")
 532
 533    maps = bpftool_map_list(expected=2, ns=ns)
 534    for m in maps:
 535        fail("dev" not in m.keys(), "Device parameters not reported")
 536        fail(dev != m["dev"], "Map's device different than program's")
 537
 538def check_extack(output, reference, args):
 539    if skip_extack:
 540        return
 541    lines = output.split("\n")
 542    comp = len(lines) >= 2 and lines[1] == reference
 543    fail(not comp, "Missing or incorrect netlink extack message")
 544
 545def check_extack_nsim(output, reference, args):
 546    check_extack(output, "Error: netdevsim: " + reference, args)
 547
 548def check_no_extack(res, needle):
 549    fail((res[1] + res[2]).count(needle) or (res[1] + res[2]).count("Warning:"),
 550         "Found '%s' in command output, leaky extack?" % (needle))
 551
 552def check_verifier_log(output, reference):
 553    lines = output.split("\n")
 554    for l in reversed(lines):
 555        if l == reference:
 556            return
 557    fail(True, "Missing or incorrect message from netdevsim in verifier log")
 558
 
 
 
 
 
 
 
 
 
 559def test_spurios_extack(sim, obj, skip_hw, needle):
 560    res = sim.cls_bpf_add_filter(obj, prio=1, handle=1, skip_hw=skip_hw,
 561                                 include_stderr=True)
 562    check_no_extack(res, needle)
 563    res = sim.cls_bpf_add_filter(obj, op="replace", prio=1, handle=1,
 564                                 skip_hw=skip_hw, include_stderr=True)
 565    check_no_extack(res, needle)
 566    res = sim.cls_filter_op(op="delete", prio=1, handle=1, cls="bpf",
 567                            include_stderr=True)
 568    check_no_extack(res, needle)
 569
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 570
 571# Parse command line
 572parser = argparse.ArgumentParser()
 573parser.add_argument("--log", help="output verbose log to given file")
 574args = parser.parse_args()
 575if args.log:
 576    logfile = open(args.log, 'w+')
 577    logfile.write("# -*-Org-*-")
 578
 579log("Prepare...", "", level=1)
 580log_level_inc()
 581
 582# Check permissions
 583skip(os.getuid() != 0, "test must be run as root")
 584
 585# Check tools
 586ret, progs = bpftool("prog", fail=False)
 587skip(ret != 0, "bpftool not installed")
 588# Check no BPF programs are loaded
 589skip(len(progs) != 0, "BPF programs already loaded on the system")
 
 
 
 
 590
 591# Check netdevsim
 592ret, out = cmd("modprobe netdevsim", fail=False)
 593skip(ret != 0, "netdevsim module could not be loaded")
 
 594
 595# Check debugfs
 596_, out = cmd("mount")
 597if out.find("/sys/kernel/debug type debugfs") == -1:
 598    cmd("mount -t debugfs none /sys/kernel/debug")
 599
 600# Check samples are compiled
 601samples = ["sample_ret0.o", "sample_map_ret0.o"]
 602for s in samples:
 603    ret, out = cmd("ls %s/%s" % (bpf_test_dir, s), fail=False)
 604    skip(ret != 0, "sample %s/%s not found, please compile it" %
 605         (bpf_test_dir, s))
 606
 607# Check if iproute2 is built with libmnl (needed by extack support)
 608_, _, err = cmd("tc qdisc delete dev lo handle 0",
 609                fail=False, include_stderr=True)
 610if err.find("Error: Failed to find qdisc with specified handle.") == -1:
 611    print("Warning: no extack message in iproute2 output, libmnl missing?")
 612    log("Warning: no extack message in iproute2 output, libmnl missing?", "")
 613    skip_extack = True
 614
 615# Check if net namespaces seem to work
 616ns = mknetns()
 617skip(ns is None, "Could not create a net namespace")
 618cmd("ip netns delete %s" % (ns))
 619netns = []
 620
 621try:
 622    obj = bpf_obj("sample_ret0.o")
 623    bytecode = bpf_bytecode("1,6 0 0 4294967295,")
 624
 625    start_test("Test destruction of generic XDP...")
 626    sim = NetdevSim()
 
 627    sim.set_xdp(obj, "generic")
 628    sim.remove()
 629    bpftool_prog_list_wait(expected=0)
 630
 631    sim = NetdevSim()
 
 632    sim.tc_add_ingress()
 633
 634    start_test("Test TC non-offloaded...")
 635    ret, _ = sim.cls_bpf_add_filter(obj, skip_hw=True, fail=False)
 636    fail(ret != 0, "Software TC filter did not load")
 637
 638    start_test("Test TC non-offloaded isn't getting bound...")
 639    ret, _ = sim.cls_bpf_add_filter(obj, fail=False)
 640    fail(ret != 0, "Software TC filter did not load")
 641    sim.dfs_get_bound_progs(expected=0)
 642
 643    sim.tc_flush_filters()
 644
 645    start_test("Test TC offloads are off by default...")
 646    ret, _, err = sim.cls_bpf_add_filter(obj, skip_sw=True,
 647                                         fail=False, include_stderr=True)
 648    fail(ret == 0, "TC filter loaded without enabling TC offloads")
 649    check_extack(err, "Error: TC offload is disabled on net device.", args)
 650    sim.wait_for_flush()
 651
 652    sim.set_ethtool_tc_offloads(True)
 653    sim.dfs["bpf_tc_non_bound_accept"] = "Y"
 654
 655    start_test("Test TC offload by default...")
 656    ret, _ = sim.cls_bpf_add_filter(obj, fail=False)
 657    fail(ret != 0, "Software TC filter did not load")
 658    sim.dfs_get_bound_progs(expected=0)
 659    ingress = sim.tc_show_ingress(expected=1)
 660    fltr = ingress[0]
 661    fail(not fltr["in_hw"], "Filter not offloaded by default")
 662
 663    sim.tc_flush_filters()
 664
 665    start_test("Test TC cBPF bytcode tries offload by default...")
 666    ret, _ = sim.cls_bpf_add_filter(bytecode, fail=False)
 667    fail(ret != 0, "Software TC filter did not load")
 668    sim.dfs_get_bound_progs(expected=0)
 669    ingress = sim.tc_show_ingress(expected=1)
 670    fltr = ingress[0]
 671    fail(not fltr["in_hw"], "Bytecode not offloaded by default")
 672
 673    sim.tc_flush_filters()
 674    sim.dfs["bpf_tc_non_bound_accept"] = "N"
 675
 676    start_test("Test TC cBPF unbound bytecode doesn't offload...")
 677    ret, _, err = sim.cls_bpf_add_filter(bytecode, skip_sw=True,
 678                                         fail=False, include_stderr=True)
 679    fail(ret == 0, "TC bytecode loaded for offload")
 680    check_extack_nsim(err, "netdevsim configured to reject unbound programs.",
 681                      args)
 682    sim.wait_for_flush()
 683
 684    start_test("Test non-0 chain offload...")
 685    ret, _, err = sim.cls_bpf_add_filter(obj, chain=1, prio=1, handle=1,
 686                                         skip_sw=True,
 687                                         fail=False, include_stderr=True)
 688    fail(ret == 0, "Offloaded a filter to chain other than 0")
 689    check_extack(err, "Error: Driver supports only offload of chain 0.", args)
 690    sim.tc_flush_filters()
 691
 692    start_test("Test TC replace...")
 693    sim.cls_bpf_add_filter(obj, prio=1, handle=1)
 694    sim.cls_bpf_add_filter(obj, op="replace", prio=1, handle=1)
 695    sim.cls_filter_op(op="delete", prio=1, handle=1, cls="bpf")
 696
 697    sim.cls_bpf_add_filter(obj, prio=1, handle=1, skip_sw=True)
 698    sim.cls_bpf_add_filter(obj, op="replace", prio=1, handle=1, skip_sw=True)
 699    sim.cls_filter_op(op="delete", prio=1, handle=1, cls="bpf")
 700
 701    sim.cls_bpf_add_filter(obj, prio=1, handle=1, skip_hw=True)
 702    sim.cls_bpf_add_filter(obj, op="replace", prio=1, handle=1, skip_hw=True)
 703    sim.cls_filter_op(op="delete", prio=1, handle=1, cls="bpf")
 704
 705    start_test("Test TC replace bad flags...")
 706    for i in range(3):
 707        for j in range(3):
 708            ret, _ = sim.cls_bpf_add_filter(obj, op="replace", prio=1, handle=1,
 709                                            skip_sw=(j == 1), skip_hw=(j == 2),
 710                                            fail=False)
 711            fail(bool(ret) != bool(j),
 712                 "Software TC incorrect load in replace test, iteration %d" %
 713                 (j))
 714        sim.cls_filter_op(op="delete", prio=1, handle=1, cls="bpf")
 715
 716    start_test("Test spurious extack from the driver...")
 717    test_spurios_extack(sim, obj, False, "netdevsim")
 718    test_spurios_extack(sim, obj, True, "netdevsim")
 719
 720    sim.set_ethtool_tc_offloads(False)
 721
 722    test_spurios_extack(sim, obj, False, "TC offload is disabled")
 723    test_spurios_extack(sim, obj, True, "TC offload is disabled")
 724
 725    sim.set_ethtool_tc_offloads(True)
 726
 727    sim.tc_flush_filters()
 728
 
 
 
 
 
 
 
 
 729    start_test("Test TC offloads work...")
 730    ret, _, err = sim.cls_bpf_add_filter(obj, verbose=True, skip_sw=True,
 731                                         fail=False, include_stderr=True)
 732    fail(ret != 0, "TC filter did not load with TC offloads enabled")
 733    check_verifier_log(err, "[netdevsim] Hello from netdevsim!")
 734
 735    start_test("Test TC offload basics...")
 736    dfs = sim.dfs_get_bound_progs(expected=1)
 737    progs = bpftool_prog_list(expected=1)
 738    ingress = sim.tc_show_ingress(expected=1)
 739
 740    dprog = dfs[0]
 741    prog = progs[0]
 742    fltr = ingress[0]
 743    fail(fltr["skip_hw"], "TC does reports 'skip_hw' on offloaded filter")
 744    fail(not fltr["in_hw"], "TC does not report 'in_hw' for offloaded filter")
 745    fail(not fltr["skip_sw"], "TC does not report 'skip_sw' back")
 746
 747    start_test("Test TC offload is device-bound...")
 748    fail(str(prog["id"]) != fltr["id"], "Program IDs don't match")
 749    fail(prog["tag"] != fltr["tag"], "Program tags don't match")
 750    fail(fltr["id"] != dprog["id"], "Program IDs don't match")
 751    fail(dprog["state"] != "xlated", "Offloaded program state not translated")
 752    fail(dprog["loaded"] != "Y", "Offloaded program is not loaded")
 753
 754    start_test("Test disabling TC offloads is rejected while filters installed...")
 755    ret, _ = sim.set_ethtool_tc_offloads(False, fail=False)
 756    fail(ret == 0, "Driver should refuse to disable TC offloads with filters installed...")
 
 757
 758    start_test("Test qdisc removal frees things...")
 759    sim.tc_flush_filters()
 760    sim.tc_show_ingress(expected=0)
 761
 762    start_test("Test disabling TC offloads is OK without filters...")
 763    ret, _ = sim.set_ethtool_tc_offloads(False, fail=False)
 764    fail(ret != 0,
 765         "Driver refused to disable TC offloads without filters installed...")
 766
 767    sim.set_ethtool_tc_offloads(True)
 768
 769    start_test("Test destroying device gets rid of TC filters...")
 770    sim.cls_bpf_add_filter(obj, skip_sw=True)
 771    sim.remove()
 772    bpftool_prog_list_wait(expected=0)
 773
 774    sim = NetdevSim()
 
 775    sim.set_ethtool_tc_offloads(True)
 776
 777    start_test("Test destroying device gets rid of XDP...")
 778    sim.set_xdp(obj, "offload")
 779    sim.remove()
 780    bpftool_prog_list_wait(expected=0)
 781
 782    sim = NetdevSim()
 
 783    sim.set_ethtool_tc_offloads(True)
 784
 785    start_test("Test XDP prog reporting...")
 786    sim.set_xdp(obj, "drv")
 787    ipl = sim.ip_link_show(xdp=True)
 788    progs = bpftool_prog_list(expected=1)
 789    fail(ipl["xdp"]["prog"]["id"] != progs[0]["id"],
 790         "Loaded program has wrong ID")
 791
 792    start_test("Test XDP prog replace without force...")
 793    ret, _ = sim.set_xdp(obj, "drv", fail=False)
 794    fail(ret == 0, "Replaced XDP program without -force")
 795    sim.wait_for_flush(total=1)
 796
 797    start_test("Test XDP prog replace with force...")
 798    ret, _ = sim.set_xdp(obj, "drv", force=True, fail=False)
 799    fail(ret != 0, "Could not replace XDP program with -force")
 800    bpftool_prog_list_wait(expected=1)
 801    ipl = sim.ip_link_show(xdp=True)
 802    progs = bpftool_prog_list(expected=1)
 803    fail(ipl["xdp"]["prog"]["id"] != progs[0]["id"],
 804         "Loaded program has wrong ID")
 805    fail("dev" in progs[0].keys(),
 806         "Device parameters reported for non-offloaded program")
 807
 808    start_test("Test XDP prog replace with bad flags...")
 809    ret, _, err = sim.set_xdp(obj, "offload", force=True,
 810                              fail=False, include_stderr=True)
 811    fail(ret == 0, "Replaced XDP program with a program in different mode")
 812    check_extack_nsim(err, "program loaded with different flags.", args)
 813    ret, _, err = sim.set_xdp(obj, "", force=True,
 814                              fail=False, include_stderr=True)
 815    fail(ret == 0, "Replaced XDP program with a program in different mode")
 816    check_extack_nsim(err, "program loaded with different flags.", args)
 817
 818    start_test("Test XDP prog remove with bad flags...")
 819    ret, _, err = sim.unset_xdp("offload", force=True,
 820                                fail=False, include_stderr=True)
 821    fail(ret == 0, "Removed program with a bad mode mode")
 822    check_extack_nsim(err, "program loaded with different flags.", args)
 823    ret, _, err = sim.unset_xdp("", force=True,
 824                                fail=False, include_stderr=True)
 825    fail(ret == 0, "Removed program with a bad mode mode")
 826    check_extack_nsim(err, "program loaded with different flags.", args)
 827
 828    start_test("Test MTU restrictions...")
 829    ret, _ = sim.set_mtu(9000, fail=False)
 830    fail(ret == 0,
 831         "Driver should refuse to increase MTU to 9000 with XDP loaded...")
 832    sim.unset_xdp("drv")
 833    bpftool_prog_list_wait(expected=0)
 834    sim.set_mtu(9000)
 835    ret, _, err = sim.set_xdp(obj, "drv", fail=False, include_stderr=True)
 836    fail(ret == 0, "Driver should refuse to load program with MTU of 9000...")
 837    check_extack_nsim(err, "MTU too large w/ XDP enabled.", args)
 838    sim.set_mtu(1500)
 839
 840    sim.wait_for_flush()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 841    start_test("Test XDP offload...")
 842    _, _, err = sim.set_xdp(obj, "offload", verbose=True, include_stderr=True)
 843    ipl = sim.ip_link_show(xdp=True)
 844    link_xdp = ipl["xdp"]["prog"]
 845    progs = bpftool_prog_list(expected=1)
 846    prog = progs[0]
 847    fail(link_xdp["id"] != prog["id"], "Loaded program has wrong ID")
 848    check_verifier_log(err, "[netdevsim] Hello from netdevsim!")
 849
 850    start_test("Test XDP offload is device bound...")
 851    dfs = sim.dfs_get_bound_progs(expected=1)
 852    dprog = dfs[0]
 853
 854    fail(prog["id"] != link_xdp["id"], "Program IDs don't match")
 855    fail(prog["tag"] != link_xdp["tag"], "Program tags don't match")
 856    fail(str(link_xdp["id"]) != dprog["id"], "Program IDs don't match")
 857    fail(dprog["state"] != "xlated", "Offloaded program state not translated")
 858    fail(dprog["loaded"] != "Y", "Offloaded program is not loaded")
 859
 860    start_test("Test removing XDP program many times...")
 861    sim.unset_xdp("offload")
 862    sim.unset_xdp("offload")
 863    sim.unset_xdp("drv")
 864    sim.unset_xdp("drv")
 865    sim.unset_xdp("")
 866    sim.unset_xdp("")
 867    bpftool_prog_list_wait(expected=0)
 868
 869    start_test("Test attempt to use a program for a wrong device...")
 870    sim2 = NetdevSim()
 
 871    sim2.set_xdp(obj, "offload")
 872    pin_file, pinned = pin_prog("/sys/fs/bpf/tmp")
 873
 874    ret, _, err = sim.set_xdp(pinned, "offload",
 875                              fail=False, include_stderr=True)
 876    fail(ret == 0, "Pinned program loaded for a different device accepted")
 877    check_extack_nsim(err, "program bound to different dev.", args)
 878    sim2.remove()
 879    ret, _, err = sim.set_xdp(pinned, "offload",
 880                              fail=False, include_stderr=True)
 881    fail(ret == 0, "Pinned program loaded for a removed device accepted")
 882    check_extack_nsim(err, "xdpoffload of non-bound program.", args)
 883    rm(pin_file)
 884    bpftool_prog_list_wait(expected=0)
 885
 
 
 
 
 886    start_test("Test mixing of TC and XDP...")
 887    sim.tc_add_ingress()
 888    sim.set_xdp(obj, "offload")
 889    ret, _, err = sim.cls_bpf_add_filter(obj, skip_sw=True,
 890                                         fail=False, include_stderr=True)
 891    fail(ret == 0, "Loading TC when XDP active should fail")
 892    check_extack_nsim(err, "driver and netdev offload states mismatch.", args)
 893    sim.unset_xdp("offload")
 894    sim.wait_for_flush()
 895
 896    sim.cls_bpf_add_filter(obj, skip_sw=True)
 897    ret, _, err = sim.set_xdp(obj, "offload", fail=False, include_stderr=True)
 898    fail(ret == 0, "Loading XDP when TC active should fail")
 899    check_extack_nsim(err, "TC program is already loaded.", args)
 900
 901    start_test("Test binding TC from pinned...")
 902    pin_file, pinned = pin_prog("/sys/fs/bpf/tmp")
 903    sim.tc_flush_filters(bound=1, total=1)
 904    sim.cls_bpf_add_filter(pinned, da=True, skip_sw=True)
 905    sim.tc_flush_filters(bound=1, total=1)
 906
 907    start_test("Test binding XDP from pinned...")
 908    sim.set_xdp(obj, "offload")
 909    pin_file, pinned = pin_prog("/sys/fs/bpf/tmp2", idx=1)
 910
 911    sim.set_xdp(pinned, "offload", force=True)
 912    sim.unset_xdp("offload")
 913    sim.set_xdp(pinned, "offload", force=True)
 914    sim.unset_xdp("offload")
 915
 916    start_test("Test offload of wrong type fails...")
 917    ret, _ = sim.cls_bpf_add_filter(pinned, da=True, skip_sw=True, fail=False)
 918    fail(ret == 0, "Managed to attach XDP program to TC")
 919
 920    start_test("Test asking for TC offload of two filters...")
 921    sim.cls_bpf_add_filter(obj, da=True, skip_sw=True)
 922    ret, _, err = sim.cls_bpf_add_filter(obj, da=True, skip_sw=True,
 923                                         fail=False, include_stderr=True)
 924    fail(ret == 0, "Managed to offload two TC filters at the same time")
 925    check_extack_nsim(err, "driver and netdev offload states mismatch.", args)
 926
 927    sim.tc_flush_filters(bound=2, total=2)
 928
 929    start_test("Test if netdev removal waits for translation...")
 930    delay_msec = 500
 931    sim.dfs["bpf_bind_verifier_delay"] = delay_msec
 932    start = time.time()
 933    cmd_line = "tc filter add dev %s ingress bpf %s da skip_sw" % \
 934               (sim['ifname'], obj)
 935    tc_proc = cmd(cmd_line, background=True, fail=False)
 936    # Wait for the verifier to start
 937    while sim.dfs_num_bound_progs() <= 2:
 938        pass
 939    sim.remove()
 940    end = time.time()
 941    ret, _ = cmd_result(tc_proc, fail=False)
 942    time_diff = end - start
 943    log("Time", "start:\t%s\nend:\t%s\ndiff:\t%s" % (start, end, time_diff))
 944
 945    fail(ret == 0, "Managed to load TC filter on a unregistering device")
 946    delay_sec = delay_msec * 0.001
 947    fail(time_diff < delay_sec, "Removal process took %s, expected %s" %
 948         (time_diff, delay_sec))
 949
 950    # Remove all pinned files and reinstantiate the netdev
 951    clean_up()
 952    bpftool_prog_list_wait(expected=0)
 953
 954    sim = NetdevSim()
 955    map_obj = bpf_obj("sample_map_ret0.o")
 
 956    start_test("Test loading program with maps...")
 957    sim.set_xdp(map_obj, "offload", JSON=False) # map fixup msg breaks JSON
 958
 959    start_test("Test bpftool bound info reporting (own ns)...")
 960    check_dev_info(False, "")
 961
 962    start_test("Test bpftool bound info reporting (other ns)...")
 963    ns = mknetns()
 964    sim.set_ns(ns)
 965    check_dev_info(True, "")
 966
 967    start_test("Test bpftool bound info reporting (remote ns)...")
 968    check_dev_info(False, ns)
 969
 970    start_test("Test bpftool bound info reporting (back to own ns)...")
 971    sim.set_ns("")
 972    check_dev_info(False, "")
 973
 974    prog_file, _ = pin_prog("/sys/fs/bpf/tmp_prog")
 975    map_file, _ = pin_map("/sys/fs/bpf/tmp_map", idx=1, expected=2)
 976    sim.remove()
 977
 978    start_test("Test bpftool bound info reporting (removed dev)...")
 979    check_dev_info_removed(prog_file=prog_file, map_file=map_file)
 980
 981    # Remove all pinned files and reinstantiate the netdev
 982    clean_up()
 983    bpftool_prog_list_wait(expected=0)
 984
 985    sim = NetdevSim()
 
 986
 987    start_test("Test map update (no flags)...")
 988    sim.set_xdp(map_obj, "offload", JSON=False) # map fixup msg breaks JSON
 989    maps = bpftool_map_list(expected=2)
 990    array = maps[0] if maps[0]["type"] == "array" else maps[1]
 991    htab = maps[0] if maps[0]["type"] == "hash" else maps[1]
 992    for m in maps:
 993        for i in range(2):
 994            bpftool("map update id %d key %s value %s" %
 995                    (m["id"], int2str("I", i), int2str("Q", i * 3)))
 996
 997    for m in maps:
 998        ret, _ = bpftool("map update id %d key %s value %s" %
 999                         (m["id"], int2str("I", 3), int2str("Q", 3 * 3)),
1000                         fail=False)
1001        fail(ret == 0, "added too many entries")
1002
1003    start_test("Test map update (exists)...")
1004    for m in maps:
1005        for i in range(2):
1006            bpftool("map update id %d key %s value %s exist" %
1007                    (m["id"], int2str("I", i), int2str("Q", i * 3)))
1008
1009    for m in maps:
1010        ret, err = bpftool("map update id %d key %s value %s exist" %
1011                           (m["id"], int2str("I", 3), int2str("Q", 3 * 3)),
1012                           fail=False)
1013        fail(ret == 0, "updated non-existing key")
1014        fail(err["error"].find("No such file or directory") == -1,
1015             "expected ENOENT, error is '%s'" % (err["error"]))
1016
1017    start_test("Test map update (noexist)...")
1018    for m in maps:
1019        for i in range(2):
1020            ret, err = bpftool("map update id %d key %s value %s noexist" %
1021                               (m["id"], int2str("I", i), int2str("Q", i * 3)),
1022                               fail=False)
1023        fail(ret == 0, "updated existing key")
1024        fail(err["error"].find("File exists") == -1,
1025             "expected EEXIST, error is '%s'" % (err["error"]))
1026
1027    start_test("Test map dump...")
1028    for m in maps:
1029        _, entries = bpftool("map dump id %d" % (m["id"]))
1030        for i in range(2):
1031            key = str2int(entries[i]["key"])
1032            fail(key != i, "expected key %d, got %d" % (key, i))
1033            val = str2int(entries[i]["value"])
1034            fail(val != i * 3, "expected value %d, got %d" % (val, i * 3))
1035
1036    start_test("Test map getnext...")
1037    for m in maps:
1038        _, entry = bpftool("map getnext id %d" % (m["id"]))
1039        key = str2int(entry["next_key"])
1040        fail(key != 0, "next key %d, expected %d" % (key, 0))
1041        _, entry = bpftool("map getnext id %d key %s" %
1042                           (m["id"], int2str("I", 0)))
1043        key = str2int(entry["next_key"])
1044        fail(key != 1, "next key %d, expected %d" % (key, 1))
1045        ret, err = bpftool("map getnext id %d key %s" %
1046                           (m["id"], int2str("I", 1)), fail=False)
1047        fail(ret == 0, "got next key past the end of map")
1048        fail(err["error"].find("No such file or directory") == -1,
1049             "expected ENOENT, error is '%s'" % (err["error"]))
1050
1051    start_test("Test map delete (htab)...")
1052    for i in range(2):
1053        bpftool("map delete id %d key %s" % (htab["id"], int2str("I", i)))
1054
1055    start_test("Test map delete (array)...")
1056    for i in range(2):
1057        ret, err = bpftool("map delete id %d key %s" %
1058                           (htab["id"], int2str("I", i)), fail=False)
1059        fail(ret == 0, "removed entry from an array")
1060        fail(err["error"].find("No such file or directory") == -1,
1061             "expected ENOENT, error is '%s'" % (err["error"]))
1062
1063    start_test("Test map remove...")
1064    sim.unset_xdp("offload")
1065    bpftool_map_list_wait(expected=0)
1066    sim.remove()
1067
1068    sim = NetdevSim()
 
1069    sim.set_xdp(map_obj, "offload", JSON=False) # map fixup msg breaks JSON
1070    sim.remove()
1071    bpftool_map_list_wait(expected=0)
1072
1073    start_test("Test map creation fail path...")
1074    sim = NetdevSim()
 
1075    sim.dfs["bpf_map_accept"] = "N"
1076    ret, _ = sim.set_xdp(map_obj, "offload", JSON=False, fail=False)
1077    fail(ret == 0,
1078         "netdevsim didn't refuse to create a map with offload disabled")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1079
1080    print("%s: OK" % (os.path.basename(__file__)))
1081
1082finally:
1083    log("Clean up...", "", level=1)
1084    log_level_inc()
1085    clean_up()
v6.9.4
   1#!/usr/bin/env python3
   2
   3# Copyright (C) 2017 Netronome Systems, Inc.
   4# Copyright (c) 2019 Mellanox Technologies. All rights reserved
   5#
   6# This software is licensed under the GNU General License Version 2,
   7# June 1991 as shown in the file COPYING in the top-level directory of this
   8# source tree.
   9#
  10# THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS"
  11# WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING,
  12# BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
  13# FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE
  14# OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME
  15# THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
  16
  17from datetime import datetime
  18import argparse
  19import errno
  20import json
  21import os
  22import pprint
  23import random
  24import re
  25import stat
  26import string
  27import struct
  28import subprocess
  29import time
  30import traceback
  31
  32logfile = None
  33log_level = 1
  34skip_extack = False
  35bpf_test_dir = os.path.dirname(os.path.realpath(__file__))
  36pp = pprint.PrettyPrinter()
  37devs = [] # devices we created for clean up
  38files = [] # files to be removed
  39netns = [] # net namespaces to be removed
  40
  41def log_get_sec(level=0):
  42    return "*" * (log_level + level)
  43
  44def log_level_inc(add=1):
  45    global log_level
  46    log_level += add
  47
  48def log_level_dec(sub=1):
  49    global log_level
  50    log_level -= sub
  51
  52def log_level_set(level):
  53    global log_level
  54    log_level = level
  55
  56def log(header, data, level=None):
  57    """
  58    Output to an optional log.
  59    """
  60    if logfile is None:
  61        return
  62    if level is not None:
  63        log_level_set(level)
  64
  65    if not isinstance(data, str):
  66        data = pp.pformat(data)
  67
  68    if len(header):
  69        logfile.write("\n" + log_get_sec() + " ")
  70        logfile.write(header)
  71    if len(header) and len(data.strip()):
  72        logfile.write("\n")
  73    logfile.write(data)
  74
  75def skip(cond, msg):
  76    if not cond:
  77        return
  78    print("SKIP: " + msg)
  79    log("SKIP: " + msg, "", level=1)
  80    os.sys.exit(0)
  81
  82def fail(cond, msg):
  83    if not cond:
  84        return
  85    print("FAIL: " + msg)
  86    tb = "".join(traceback.extract_stack().format())
  87    print(tb)
  88    log("FAIL: " + msg, tb, level=1)
  89    os.sys.exit(1)
  90
  91def start_test(msg):
  92    log(msg, "", level=1)
  93    log_level_inc()
  94    print(msg)
  95
  96def cmd(cmd, shell=True, include_stderr=False, background=False, fail=True):
  97    """
  98    Run a command in subprocess and return tuple of (retval, stdout);
  99    optionally return stderr as well as third value.
 100    """
 101    proc = subprocess.Popen(cmd, shell=shell, stdout=subprocess.PIPE,
 102                            stderr=subprocess.PIPE)
 103    if background:
 104        msg = "%s START: %s" % (log_get_sec(1),
 105                                datetime.now().strftime("%H:%M:%S.%f"))
 106        log("BKG " + proc.args, msg)
 107        return proc
 108
 109    return cmd_result(proc, include_stderr=include_stderr, fail=fail)
 110
 111def cmd_result(proc, include_stderr=False, fail=False):
 112    stdout, stderr = proc.communicate()
 113    stdout = stdout.decode("utf-8")
 114    stderr = stderr.decode("utf-8")
 115    proc.stdout.close()
 116    proc.stderr.close()
 117
 118    stderr = "\n" + stderr
 119    if stderr[-1] == "\n":
 120        stderr = stderr[:-1]
 121
 122    sec = log_get_sec(1)
 123    log("CMD " + proc.args,
 124        "RETCODE: %d\n%s STDOUT:\n%s%s STDERR:%s\n%s END: %s" %
 125        (proc.returncode, sec, stdout, sec, stderr,
 126         sec, datetime.now().strftime("%H:%M:%S.%f")))
 127
 128    if proc.returncode != 0 and fail:
 129        if len(stderr) > 0 and stderr[-1] == "\n":
 130            stderr = stderr[:-1]
 131        raise Exception("Command failed: %s\n%s" % (proc.args, stderr))
 132
 133    if include_stderr:
 134        return proc.returncode, stdout, stderr
 135    else:
 136        return proc.returncode, stdout
 137
 138def rm(f):
 139    cmd("rm -f %s" % (f))
 140    if f in files:
 141        files.remove(f)
 142
 143def tool(name, args, flags, JSON=True, ns="", fail=True, include_stderr=False):
 144    params = ""
 145    if JSON:
 146        params += "%s " % (flags["json"])
 147
 148    if ns != "":
 149        ns = "ip netns exec %s " % (ns)
 150
 151    if include_stderr:
 152        ret, stdout, stderr = cmd(ns + name + " " + params + args,
 153                                  fail=fail, include_stderr=True)
 154    else:
 155        ret, stdout = cmd(ns + name + " " + params + args,
 156                          fail=fail, include_stderr=False)
 157
 158    if JSON and len(stdout.strip()) != 0:
 159        out = json.loads(stdout)
 160    else:
 161        out = stdout
 162
 163    if include_stderr:
 164        return ret, out, stderr
 165    else:
 166        return ret, out
 167
 168def bpftool(args, JSON=True, ns="", fail=True, include_stderr=False):
 169    return tool("bpftool", args, {"json":"-p"}, JSON=JSON, ns=ns,
 170                fail=fail, include_stderr=include_stderr)
 171
 172def bpftool_prog_list(expected=None, ns="", exclude_orphaned=True):
 173    _, progs = bpftool("prog show", JSON=True, ns=ns, fail=True)
 174    # Remove the base progs
 175    for p in base_progs:
 176        if p in progs:
 177            progs.remove(p)
 178    if exclude_orphaned:
 179        progs = [ p for p in progs if not p['orphaned'] ]
 180    if expected is not None:
 181        if len(progs) != expected:
 182            fail(True, "%d BPF programs loaded, expected %d" %
 183                 (len(progs), expected))
 184    return progs
 185
 186def bpftool_map_list(expected=None, ns=""):
 187    _, maps = bpftool("map show", JSON=True, ns=ns, fail=True)
 188    # Remove the base maps
 189    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]
 190    if expected is not None:
 191        if len(maps) != expected:
 192            fail(True, "%d BPF maps loaded, expected %d" %
 193                 (len(maps), expected))
 194    return maps
 195
 196def bpftool_prog_list_wait(expected=0, n_retry=20):
 197    for i in range(n_retry):
 198        nprogs = len(bpftool_prog_list())
 199        if nprogs == expected:
 200            return
 201        time.sleep(0.05)
 202    raise Exception("Time out waiting for program counts to stabilize want %d, have %d" % (expected, nprogs))
 203
 204def bpftool_map_list_wait(expected=0, n_retry=20):
 205    for i in range(n_retry):
 206        nmaps = len(bpftool_map_list())
 207        if nmaps == expected:
 208            return
 209        time.sleep(0.05)
 210    raise Exception("Time out waiting for map counts to stabilize want %d, have %d" % (expected, nmaps))
 211
 212def bpftool_prog_load(sample, file_name, maps=[], prog_type="xdp", dev=None,
 213                      fail=True, include_stderr=False):
 214    args = "prog load %s %s" % (os.path.join(bpf_test_dir, sample), file_name)
 215    if prog_type is not None:
 216        args += " type " + prog_type
 217    if dev is not None:
 218        args += " dev " + dev
 219    if len(maps):
 220        args += " map " + " map ".join(maps)
 221
 222    res = bpftool(args, fail=fail, include_stderr=include_stderr)
 223    if res[0] == 0:
 224        files.append(file_name)
 225    return res
 226
 227def ip(args, force=False, JSON=True, ns="", fail=True, include_stderr=False):
 228    if force:
 229        args = "-force " + args
 230    return tool("ip", args, {"json":"-j"}, JSON=JSON, ns=ns,
 231                fail=fail, include_stderr=include_stderr)
 232
 233def tc(args, JSON=True, ns="", fail=True, include_stderr=False):
 234    return tool("tc", args, {"json":"-p"}, JSON=JSON, ns=ns,
 235                fail=fail, include_stderr=include_stderr)
 236
 237def ethtool(dev, opt, args, fail=True):
 238    return cmd("ethtool %s %s %s" % (opt, dev["ifname"], args), fail=fail)
 239
 240def bpf_obj(name, sec=".text", path=bpf_test_dir,):
 241    return "obj %s sec %s" % (os.path.join(path, name), sec)
 242
 243def bpf_pinned(name):
 244    return "pinned %s" % (name)
 245
 246def bpf_bytecode(bytecode):
 247    return "bytecode \"%s\"" % (bytecode)
 248
 249def mknetns(n_retry=10):
 250    for i in range(n_retry):
 251        name = ''.join([random.choice(string.ascii_letters) for i in range(8)])
 252        ret, _ = ip("netns add %s" % (name), fail=False)
 253        if ret == 0:
 254            netns.append(name)
 255            return name
 256    return None
 257
 258def int2str(fmt, val):
 259    ret = []
 260    for b in struct.pack(fmt, val):
 261        ret.append(int(b))
 262    return " ".join(map(lambda x: str(x), ret))
 263
 264def str2int(strtab):
 265    inttab = []
 266    for i in strtab:
 267        inttab.append(int(i, 16))
 268    ba = bytearray(inttab)
 269    if len(strtab) == 4:
 270        fmt = "I"
 271    elif len(strtab) == 8:
 272        fmt = "Q"
 273    else:
 274        raise Exception("String array of len %d can't be unpacked to an int" %
 275                        (len(strtab)))
 276    return struct.unpack(fmt, ba)[0]
 277
 278class DebugfsDir:
 279    """
 280    Class for accessing DebugFS directories as a dictionary.
 281    """
 282
 283    def __init__(self, path):
 284        self.path = path
 285        self._dict = self._debugfs_dir_read(path)
 286
 287    def __len__(self):
 288        return len(self._dict.keys())
 289
 290    def __getitem__(self, key):
 291        if type(key) is int:
 292            key = list(self._dict.keys())[key]
 293        return self._dict[key]
 294
 295    def __setitem__(self, key, value):
 296        log("DebugFS set %s = %s" % (key, value), "")
 297        log_level_inc()
 298
 299        cmd("echo '%s' > %s/%s" % (value, self.path, key))
 300        log_level_dec()
 301
 302        _, out = cmd('cat %s/%s' % (self.path, key))
 303        self._dict[key] = out.strip()
 304
 305    def _debugfs_dir_read(self, path):
 306        dfs = {}
 307
 308        log("DebugFS state for %s" % (path), "")
 309        log_level_inc(add=2)
 310
 311        _, out = cmd('ls ' + path)
 312        for f in out.split():
 313            if f == "ports":
 314                continue
 315
 316            p = os.path.join(path, f)
 317            if not os.stat(p).st_mode & stat.S_IRUSR:
 318                continue
 319
 320            if os.path.isfile(p):
 321                # We need to init trap_flow_action_cookie before read it
 322                if f == "trap_flow_action_cookie":
 323                    cmd('echo deadbeef > %s/%s' % (path, f))
 324                _, out = cmd('cat %s/%s' % (path, f))
 325                dfs[f] = out.strip()
 326            elif os.path.isdir(p):
 327                dfs[f] = DebugfsDir(p)
 328            else:
 329                raise Exception("%s is neither file nor directory" % (p))
 330
 331        log_level_dec()
 332        log("DebugFS state", dfs)
 333        log_level_dec()
 334
 335        return dfs
 336
 337class NetdevSimDev:
 338    """
 339    Class for netdevsim bus device and its attributes.
 340    """
 341    @staticmethod
 342    def ctrl_write(path, val):
 343        fullpath = os.path.join("/sys/bus/netdevsim/", path)
 344        try:
 345            with open(fullpath, "w") as f:
 346                f.write(val)
 347        except OSError as e:
 348            log("WRITE %s: %r" % (fullpath, val), -e.errno)
 349            raise e
 350        log("WRITE %s: %r" % (fullpath, val), 0)
 351
 352    def __init__(self, port_count=1):
 353        addr = 0
 354        while True:
 355            try:
 356                self.ctrl_write("new_device", "%u %u" % (addr, port_count))
 357            except OSError as e:
 358                if e.errno == errno.ENOSPC:
 359                    addr += 1
 360                    continue
 361                raise e
 362            break
 363        self.addr = addr
 364
 365        # As probe of netdevsim device might happen from a workqueue,
 366        # so wait here until all netdevs appear.
 367        self.wait_for_netdevs(port_count)
 368
 369        ret, out = cmd("udevadm settle", fail=False)
 370        if ret:
 371            raise Exception("udevadm settle failed")
 372        ifnames = self.get_ifnames()
 373
 
 
 374        devs.append(self)
 375        self.dfs_dir = "/sys/kernel/debug/netdevsim/netdevsim%u/" % addr
 376
 377        self.nsims = []
 378        for port_index in range(port_count):
 379            self.nsims.append(NetdevSim(self, port_index, ifnames[port_index]))
 380
 381    def get_ifnames(self):
 382        ifnames = []
 383        listdir = os.listdir("/sys/bus/netdevsim/devices/netdevsim%u/net/" % self.addr)
 384        for ifname in listdir:
 385            ifnames.append(ifname)
 386        ifnames.sort()
 387        return ifnames
 388
 389    def wait_for_netdevs(self, port_count):
 390        timeout = 5
 391        timeout_start = time.time()
 392
 393        while True:
 394            try:
 395                ifnames = self.get_ifnames()
 396            except FileNotFoundError as e:
 397                ifnames = []
 398            if len(ifnames) == port_count:
 399                break
 400            if time.time() < timeout_start + timeout:
 401                continue
 402            raise Exception("netdevices did not appear within timeout")
 
 403
 404    def dfs_num_bound_progs(self):
 405        path = os.path.join(self.dfs_dir, "bpf_bound_progs")
 406        _, progs = cmd('ls %s' % (path))
 407        return len(progs.split())
 408
 409    def dfs_get_bound_progs(self, expected):
 410        progs = DebugfsDir(os.path.join(self.dfs_dir, "bpf_bound_progs"))
 411        if expected is not None:
 412            if len(progs) != expected:
 413                fail(True, "%d BPF programs bound, expected %d" %
 414                     (len(progs), expected))
 415        return progs
 416
 417    def remove(self):
 418        self.ctrl_write("del_device", "%u" % (self.addr, ))
 419        devs.remove(self)
 420
 421    def remove_nsim(self, nsim):
 422        self.nsims.remove(nsim)
 423        self.ctrl_write("devices/netdevsim%u/del_port" % (self.addr, ),
 424                        "%u" % (nsim.port_index, ))
 425
 426class NetdevSim:
 427    """
 428    Class for netdevsim netdevice and its attributes.
 429    """
 430
 431    def __init__(self, nsimdev, port_index, ifname):
 432        # In case udev renamed the netdev to according to new schema,
 433        # check if the name matches the port_index.
 434        nsimnamere = re.compile("eni\d+np(\d+)")
 435        match = nsimnamere.match(ifname)
 436        if match and int(match.groups()[0]) != port_index + 1:
 437            raise Exception("netdevice name mismatches the expected one")
 438
 439        self.nsimdev = nsimdev
 440        self.port_index = port_index
 441        self.ns = ""
 442        self.dfs_dir = "%s/ports/%u/" % (nsimdev.dfs_dir, port_index)
 443        self.dfs_refresh()
 444        _, [self.dev] = ip("link show dev %s" % ifname)
 445
 446    def __getitem__(self, key):
 447        return self.dev[key]
 448
 449    def remove(self):
 450        self.nsimdev.remove_nsim(self)
 451
 452    def dfs_refresh(self):
 453        self.dfs = DebugfsDir(self.dfs_dir)
 454        return self.dfs
 455
 456    def dfs_read(self, f):
 457        path = os.path.join(self.dfs_dir, f)
 458        _, data = cmd('cat %s' % (path))
 459        return data.strip()
 460
 461    def wait_for_flush(self, bound=0, total=0, n_retry=20):
 462        for i in range(n_retry):
 463            nbound = self.nsimdev.dfs_num_bound_progs()
 464            nprogs = len(bpftool_prog_list())
 465            if nbound == bound and nprogs == total:
 466                return
 467            time.sleep(0.05)
 468        raise Exception("Time out waiting for program counts to stabilize want %d/%d, have %d bound, %d loaded" % (bound, total, nbound, nprogs))
 469
 470    def set_ns(self, ns):
 471        name = "1" if ns == "" else ns
 472        ip("link set dev %s netns %s" % (self.dev["ifname"], name), ns=self.ns)
 473        self.ns = ns
 474
 475    def set_mtu(self, mtu, fail=True):
 476        return ip("link set dev %s mtu %d" % (self.dev["ifname"], mtu),
 477                  fail=fail)
 478
 479    def set_xdp(self, bpf, mode, force=False, JSON=True, verbose=False,
 480                fail=True, include_stderr=False):
 481        if verbose:
 482            bpf += " verbose"
 483        return ip("link set dev %s xdp%s %s" % (self.dev["ifname"], mode, bpf),
 484                  force=force, JSON=JSON,
 485                  fail=fail, include_stderr=include_stderr)
 486
 487    def unset_xdp(self, mode, force=False, JSON=True,
 488                  fail=True, include_stderr=False):
 489        return ip("link set dev %s xdp%s off" % (self.dev["ifname"], mode),
 490                  force=force, JSON=JSON,
 491                  fail=fail, include_stderr=include_stderr)
 492
 493    def ip_link_show(self, xdp):
 494        _, link = ip("link show dev %s" % (self['ifname']))
 495        if len(link) > 1:
 496            raise Exception("Multiple objects on ip link show")
 497        if len(link) < 1:
 498            return {}
 499        fail(xdp != "xdp" in link,
 500             "XDP program not reporting in iplink (reported %s, expected %s)" %
 501             ("xdp" in link, xdp))
 502        return link[0]
 503
 504    def tc_add_ingress(self):
 505        tc("qdisc add dev %s ingress" % (self['ifname']))
 506
 507    def tc_del_ingress(self):
 508        tc("qdisc del dev %s ingress" % (self['ifname']))
 509
 510    def tc_flush_filters(self, bound=0, total=0):
 511        self.tc_del_ingress()
 512        self.tc_add_ingress()
 513        self.wait_for_flush(bound=bound, total=total)
 514
 515    def tc_show_ingress(self, expected=None):
 516        # No JSON support, oh well...
 517        flags = ["skip_sw", "skip_hw", "in_hw"]
 518        named = ["protocol", "pref", "chain", "handle", "id", "tag"]
 519
 520        args = "-s filter show dev %s ingress" % (self['ifname'])
 521        _, out = tc(args, JSON=False)
 522
 523        filters = []
 524        lines = out.split('\n')
 525        for line in lines:
 526            words = line.split()
 527            if "handle" not in words:
 528                continue
 529            fltr = {}
 530            for flag in flags:
 531                fltr[flag] = flag in words
 532            for name in named:
 533                try:
 534                    idx = words.index(name)
 535                    fltr[name] = words[idx + 1]
 536                except ValueError:
 537                    pass
 538            filters.append(fltr)
 539
 540        if expected is not None:
 541            fail(len(filters) != expected,
 542                 "%d ingress filters loaded, expected %d" %
 543                 (len(filters), expected))
 544        return filters
 545
 546    def cls_filter_op(self, op, qdisc="ingress", prio=None, handle=None,
 547                      chain=None, cls="", params="",
 548                      fail=True, include_stderr=False):
 549        spec = ""
 550        if prio is not None:
 551            spec += " prio %d" % (prio)
 552        if handle:
 553            spec += " handle %s" % (handle)
 554        if chain is not None:
 555            spec += " chain %d" % (chain)
 556
 557        return tc("filter {op} dev {dev} {qdisc} {spec} {cls} {params}"\
 558                  .format(op=op, dev=self['ifname'], qdisc=qdisc, spec=spec,
 559                          cls=cls, params=params),
 560                  fail=fail, include_stderr=include_stderr)
 561
 562    def cls_bpf_add_filter(self, bpf, op="add", prio=None, handle=None,
 563                           chain=None, da=False, verbose=False,
 564                           skip_sw=False, skip_hw=False,
 565                           fail=True, include_stderr=False):
 566        cls = "bpf " + bpf
 567
 568        params = ""
 569        if da:
 570            params += " da"
 571        if verbose:
 572            params += " verbose"
 573        if skip_sw:
 574            params += " skip_sw"
 575        if skip_hw:
 576            params += " skip_hw"
 577
 578        return self.cls_filter_op(op=op, prio=prio, handle=handle, cls=cls,
 579                                  chain=chain, params=params,
 580                                  fail=fail, include_stderr=include_stderr)
 581
 582    def set_ethtool_tc_offloads(self, enable, fail=True):
 583        args = "hw-tc-offload %s" % ("on" if enable else "off")
 584        return ethtool(self, "-K", args, fail=fail)
 585
 586################################################################################
 587def clean_up():
 588    global files, netns, devs
 589
 590    for dev in devs:
 591        dev.remove()
 592    for f in files:
 593        cmd("rm -f %s" % (f))
 594    for ns in netns:
 595        cmd("ip netns delete %s" % (ns))
 596    files = []
 597    netns = []
 598
 599def pin_prog(file_name, idx=0):
 600    progs = bpftool_prog_list(expected=(idx + 1))
 601    prog = progs[idx]
 602    bpftool("prog pin id %d %s" % (prog["id"], file_name))
 603    files.append(file_name)
 604
 605    return file_name, bpf_pinned(file_name)
 606
 607def pin_map(file_name, idx=0, expected=1):
 608    maps = bpftool_map_list(expected=expected)
 609    m = maps[idx]
 610    bpftool("map pin id %d %s" % (m["id"], file_name))
 611    files.append(file_name)
 612
 613    return file_name, bpf_pinned(file_name)
 614
 615def check_dev_info_removed(prog_file=None, map_file=None):
 616    bpftool_prog_list(expected=0)
 617    bpftool_prog_list(expected=1, exclude_orphaned=False)
 618    ret, err = bpftool("prog show pin %s" % (prog_file), fail=False)
 619    fail(ret != 0, "failed to show prog with removed device")
 
 
 
 620
 621    bpftool_map_list(expected=0)
 622    ret, err = bpftool("map show pin %s" % (map_file), fail=False)
 623    fail(ret == 0, "Showing map with removed device did not fail")
 624    fail(err["error"].find("No such device") == -1,
 625         "Showing map with removed device expected ENODEV, error is %s" %
 626         (err["error"]))
 627
 628def check_dev_info(other_ns, ns, prog_file=None, map_file=None, removed=False):
 629    progs = bpftool_prog_list(expected=1, ns=ns)
 630    prog = progs[0]
 631
 632    fail("dev" not in prog.keys(), "Device parameters not reported")
 633    dev = prog["dev"]
 634    fail("ifindex" not in dev.keys(), "Device parameters not reported")
 635    fail("ns_dev" not in dev.keys(), "Device parameters not reported")
 636    fail("ns_inode" not in dev.keys(), "Device parameters not reported")
 637
 638    if not other_ns:
 639        fail("ifname" not in dev.keys(), "Ifname not reported")
 640        fail(dev["ifname"] != sim["ifname"],
 641             "Ifname incorrect %s vs %s" % (dev["ifname"], sim["ifname"]))
 642    else:
 643        fail("ifname" in dev.keys(), "Ifname is reported for other ns")
 644
 645    maps = bpftool_map_list(expected=2, ns=ns)
 646    for m in maps:
 647        fail("dev" not in m.keys(), "Device parameters not reported")
 648        fail(dev != m["dev"], "Map's device different than program's")
 649
 650def check_extack(output, reference, args):
 651    if skip_extack:
 652        return
 653    lines = output.split("\n")
 654    comp = len(lines) >= 2 and lines[1] == 'Error: ' + reference
 655    fail(not comp, "Missing or incorrect netlink extack message")
 656
 657def check_extack_nsim(output, reference, args):
 658    check_extack(output, "netdevsim: " + reference, args)
 659
 660def check_no_extack(res, needle):
 661    fail((res[1] + res[2]).count(needle) or (res[1] + res[2]).count("Warning:"),
 662         "Found '%s' in command output, leaky extack?" % (needle))
 663
 664def check_verifier_log(output, reference):
 665    lines = output.split("\n")
 666    for l in reversed(lines):
 667        if l == reference:
 668            return
 669    fail(True, "Missing or incorrect message from netdevsim in verifier log")
 670
 671def check_multi_basic(two_xdps):
 672    fail(two_xdps["mode"] != 4, "Bad mode reported with multiple programs")
 673    fail("prog" in two_xdps, "Base program reported in multi program mode")
 674    fail(len(two_xdps["attached"]) != 2,
 675         "Wrong attached program count with two programs")
 676    fail(two_xdps["attached"][0]["prog"]["id"] ==
 677         two_xdps["attached"][1]["prog"]["id"],
 678         "Offloaded and other programs have the same id")
 679
 680def test_spurios_extack(sim, obj, skip_hw, needle):
 681    res = sim.cls_bpf_add_filter(obj, prio=1, handle=1, skip_hw=skip_hw,
 682                                 include_stderr=True)
 683    check_no_extack(res, needle)
 684    res = sim.cls_bpf_add_filter(obj, op="replace", prio=1, handle=1,
 685                                 skip_hw=skip_hw, include_stderr=True)
 686    check_no_extack(res, needle)
 687    res = sim.cls_filter_op(op="delete", prio=1, handle=1, cls="bpf",
 688                            include_stderr=True)
 689    check_no_extack(res, needle)
 690
 691def test_multi_prog(simdev, sim, obj, modename, modeid):
 692    start_test("Test multi-attachment XDP - %s + offload..." %
 693               (modename or "default", ))
 694    sim.set_xdp(obj, "offload")
 695    xdp = sim.ip_link_show(xdp=True)["xdp"]
 696    offloaded = sim.dfs_read("bpf_offloaded_id")
 697    fail("prog" not in xdp, "Base program not reported in single program mode")
 698    fail(len(xdp["attached"]) != 1,
 699         "Wrong attached program count with one program")
 700
 701    sim.set_xdp(obj, modename)
 702    two_xdps = sim.ip_link_show(xdp=True)["xdp"]
 703
 704    fail(xdp["attached"][0] not in two_xdps["attached"],
 705         "Offload program not reported after other activated")
 706    check_multi_basic(two_xdps)
 707
 708    offloaded2 = sim.dfs_read("bpf_offloaded_id")
 709    fail(offloaded != offloaded2,
 710         "Offload ID changed after loading other program")
 711
 712    start_test("Test multi-attachment XDP - replace...")
 713    ret, _, err = sim.set_xdp(obj, "offload", fail=False, include_stderr=True)
 714    fail(ret == 0, "Replaced one of programs without -force")
 715    check_extack(err, "XDP program already attached.", args)
 716
 717    start_test("Test multi-attachment XDP - remove without mode...")
 718    ret, _, err = sim.unset_xdp("", force=True,
 719                                fail=False, include_stderr=True)
 720    fail(ret == 0, "Removed program without a mode flag")
 721    check_extack(err, "More than one program loaded, unset mode is ambiguous.", args)
 722
 723    sim.unset_xdp("offload")
 724    xdp = sim.ip_link_show(xdp=True)["xdp"]
 725    offloaded = sim.dfs_read("bpf_offloaded_id")
 726
 727    fail(xdp["mode"] != modeid, "Bad mode reported after multiple programs")
 728    fail("prog" not in xdp,
 729         "Base program not reported after multi program mode")
 730    fail(xdp["attached"][0] not in two_xdps["attached"],
 731         "Offload program not reported after other activated")
 732    fail(len(xdp["attached"]) != 1,
 733         "Wrong attached program count with remaining programs")
 734    fail(offloaded != "0", "Offload ID reported with only other program left")
 735
 736    start_test("Test multi-attachment XDP - reattach...")
 737    sim.set_xdp(obj, "offload")
 738    two_xdps = sim.ip_link_show(xdp=True)["xdp"]
 739
 740    fail(xdp["attached"][0] not in two_xdps["attached"],
 741         "Other program not reported after offload activated")
 742    check_multi_basic(two_xdps)
 743
 744    start_test("Test multi-attachment XDP - device remove...")
 745    simdev.remove()
 746
 747    simdev = NetdevSimDev()
 748    sim, = simdev.nsims
 749    sim.set_ethtool_tc_offloads(True)
 750    return [simdev, sim]
 751
 752# Parse command line
 753parser = argparse.ArgumentParser()
 754parser.add_argument("--log", help="output verbose log to given file")
 755args = parser.parse_args()
 756if args.log:
 757    logfile = open(args.log, 'w+')
 758    logfile.write("# -*-Org-*-")
 759
 760log("Prepare...", "", level=1)
 761log_level_inc()
 762
 763# Check permissions
 764skip(os.getuid() != 0, "test must be run as root")
 765
 766# Check tools
 767ret, progs = bpftool("prog", fail=False)
 768skip(ret != 0, "bpftool not installed")
 769base_progs = progs
 770_, base_maps = bpftool("map")
 771base_map_names = [
 772    'pid_iter.rodata', # created on each bpftool invocation
 773    'libbpf_det_bind', # created on each bpftool invocation
 774]
 775
 776# Check netdevsim
 777if not os.path.isdir("/sys/bus/netdevsim/"):
 778    ret, out = cmd("modprobe netdevsim", fail=False)
 779    skip(ret != 0, "netdevsim module could not be loaded")
 780
 781# Check debugfs
 782_, out = cmd("mount")
 783if out.find("/sys/kernel/debug type debugfs") == -1:
 784    cmd("mount -t debugfs none /sys/kernel/debug")
 785
 786# Check samples are compiled
 787samples = ["sample_ret0.bpf.o", "sample_map_ret0.bpf.o"]
 788for s in samples:
 789    ret, out = cmd("ls %s/%s" % (bpf_test_dir, s), fail=False)
 790    skip(ret != 0, "sample %s/%s not found, please compile it" %
 791         (bpf_test_dir, s))
 792
 793# Check if iproute2 is built with libmnl (needed by extack support)
 794_, _, err = cmd("tc qdisc delete dev lo handle 0",
 795                fail=False, include_stderr=True)
 796if err.find("Error: Failed to find qdisc with specified handle.") == -1:
 797    print("Warning: no extack message in iproute2 output, libmnl missing?")
 798    log("Warning: no extack message in iproute2 output, libmnl missing?", "")
 799    skip_extack = True
 800
 801# Check if net namespaces seem to work
 802ns = mknetns()
 803skip(ns is None, "Could not create a net namespace")
 804cmd("ip netns delete %s" % (ns))
 805netns = []
 806
 807try:
 808    obj = bpf_obj("sample_ret0.bpf.o")
 809    bytecode = bpf_bytecode("1,6 0 0 4294967295,")
 810
 811    start_test("Test destruction of generic XDP...")
 812    simdev = NetdevSimDev()
 813    sim, = simdev.nsims
 814    sim.set_xdp(obj, "generic")
 815    simdev.remove()
 816    bpftool_prog_list_wait(expected=0)
 817
 818    simdev = NetdevSimDev()
 819    sim, = simdev.nsims
 820    sim.tc_add_ingress()
 821
 822    start_test("Test TC non-offloaded...")
 823    ret, _ = sim.cls_bpf_add_filter(obj, skip_hw=True, fail=False)
 824    fail(ret != 0, "Software TC filter did not load")
 825
 826    start_test("Test TC non-offloaded isn't getting bound...")
 827    ret, _ = sim.cls_bpf_add_filter(obj, fail=False)
 828    fail(ret != 0, "Software TC filter did not load")
 829    simdev.dfs_get_bound_progs(expected=0)
 830
 831    sim.tc_flush_filters()
 832
 833    start_test("Test TC offloads are off by default...")
 834    ret, _, err = sim.cls_bpf_add_filter(obj, skip_sw=True,
 835                                         fail=False, include_stderr=True)
 836    fail(ret == 0, "TC filter loaded without enabling TC offloads")
 837    check_extack(err, "TC offload is disabled on net device.", args)
 838    sim.wait_for_flush()
 839
 840    sim.set_ethtool_tc_offloads(True)
 841    sim.dfs["bpf_tc_non_bound_accept"] = "Y"
 842
 843    start_test("Test TC offload by default...")
 844    ret, _ = sim.cls_bpf_add_filter(obj, fail=False)
 845    fail(ret != 0, "Software TC filter did not load")
 846    simdev.dfs_get_bound_progs(expected=0)
 847    ingress = sim.tc_show_ingress(expected=1)
 848    fltr = ingress[0]
 849    fail(not fltr["in_hw"], "Filter not offloaded by default")
 850
 851    sim.tc_flush_filters()
 852
 853    start_test("Test TC cBPF bytcode tries offload by default...")
 854    ret, _ = sim.cls_bpf_add_filter(bytecode, fail=False)
 855    fail(ret != 0, "Software TC filter did not load")
 856    simdev.dfs_get_bound_progs(expected=0)
 857    ingress = sim.tc_show_ingress(expected=1)
 858    fltr = ingress[0]
 859    fail(not fltr["in_hw"], "Bytecode not offloaded by default")
 860
 861    sim.tc_flush_filters()
 862    sim.dfs["bpf_tc_non_bound_accept"] = "N"
 863
 864    start_test("Test TC cBPF unbound bytecode doesn't offload...")
 865    ret, _, err = sim.cls_bpf_add_filter(bytecode, skip_sw=True,
 866                                         fail=False, include_stderr=True)
 867    fail(ret == 0, "TC bytecode loaded for offload")
 868    check_extack_nsim(err, "netdevsim configured to reject unbound programs.",
 869                      args)
 870    sim.wait_for_flush()
 871
 872    start_test("Test non-0 chain offload...")
 873    ret, _, err = sim.cls_bpf_add_filter(obj, chain=1, prio=1, handle=1,
 874                                         skip_sw=True,
 875                                         fail=False, include_stderr=True)
 876    fail(ret == 0, "Offloaded a filter to chain other than 0")
 877    check_extack(err, "Driver supports only offload of chain 0.", args)
 878    sim.tc_flush_filters()
 879
 880    start_test("Test TC replace...")
 881    sim.cls_bpf_add_filter(obj, prio=1, handle=1)
 882    sim.cls_bpf_add_filter(obj, op="replace", prio=1, handle=1)
 883    sim.cls_filter_op(op="delete", prio=1, handle=1, cls="bpf")
 884
 885    sim.cls_bpf_add_filter(obj, prio=1, handle=1, skip_sw=True)
 886    sim.cls_bpf_add_filter(obj, op="replace", prio=1, handle=1, skip_sw=True)
 887    sim.cls_filter_op(op="delete", prio=1, handle=1, cls="bpf")
 888
 889    sim.cls_bpf_add_filter(obj, prio=1, handle=1, skip_hw=True)
 890    sim.cls_bpf_add_filter(obj, op="replace", prio=1, handle=1, skip_hw=True)
 891    sim.cls_filter_op(op="delete", prio=1, handle=1, cls="bpf")
 892
 893    start_test("Test TC replace bad flags...")
 894    for i in range(3):
 895        for j in range(3):
 896            ret, _ = sim.cls_bpf_add_filter(obj, op="replace", prio=1, handle=1,
 897                                            skip_sw=(j == 1), skip_hw=(j == 2),
 898                                            fail=False)
 899            fail(bool(ret) != bool(j),
 900                 "Software TC incorrect load in replace test, iteration %d" %
 901                 (j))
 902        sim.cls_filter_op(op="delete", prio=1, handle=1, cls="bpf")
 903
 904    start_test("Test spurious extack from the driver...")
 905    test_spurios_extack(sim, obj, False, "netdevsim")
 906    test_spurios_extack(sim, obj, True, "netdevsim")
 907
 908    sim.set_ethtool_tc_offloads(False)
 909
 910    test_spurios_extack(sim, obj, False, "TC offload is disabled")
 911    test_spurios_extack(sim, obj, True, "TC offload is disabled")
 912
 913    sim.set_ethtool_tc_offloads(True)
 914
 915    sim.tc_flush_filters()
 916
 917    start_test("Test TC offloads failure...")
 918    sim.dfs["dev/bpf_bind_verifier_accept"] = 0
 919    ret, _, err = sim.cls_bpf_add_filter(obj, verbose=True, skip_sw=True,
 920                                         fail=False, include_stderr=True)
 921    fail(ret == 0, "TC filter did not reject with TC offloads enabled")
 922    check_verifier_log(err, "[netdevsim] Hello from netdevsim!")
 923    sim.dfs["dev/bpf_bind_verifier_accept"] = 1
 924
 925    start_test("Test TC offloads work...")
 926    ret, _, err = sim.cls_bpf_add_filter(obj, verbose=True, skip_sw=True,
 927                                         fail=False, include_stderr=True)
 928    fail(ret != 0, "TC filter did not load with TC offloads enabled")
 
 929
 930    start_test("Test TC offload basics...")
 931    dfs = simdev.dfs_get_bound_progs(expected=1)
 932    progs = bpftool_prog_list(expected=1)
 933    ingress = sim.tc_show_ingress(expected=1)
 934
 935    dprog = dfs[0]
 936    prog = progs[0]
 937    fltr = ingress[0]
 938    fail(fltr["skip_hw"], "TC does reports 'skip_hw' on offloaded filter")
 939    fail(not fltr["in_hw"], "TC does not report 'in_hw' for offloaded filter")
 940    fail(not fltr["skip_sw"], "TC does not report 'skip_sw' back")
 941
 942    start_test("Test TC offload is device-bound...")
 943    fail(str(prog["id"]) != fltr["id"], "Program IDs don't match")
 944    fail(prog["tag"] != fltr["tag"], "Program tags don't match")
 945    fail(fltr["id"] != dprog["id"], "Program IDs don't match")
 946    fail(dprog["state"] != "xlated", "Offloaded program state not translated")
 947    fail(dprog["loaded"] != "Y", "Offloaded program is not loaded")
 948
 949    start_test("Test disabling TC offloads is rejected while filters installed...")
 950    ret, _ = sim.set_ethtool_tc_offloads(False, fail=False)
 951    fail(ret == 0, "Driver should refuse to disable TC offloads with filters installed...")
 952    sim.set_ethtool_tc_offloads(True)
 953
 954    start_test("Test qdisc removal frees things...")
 955    sim.tc_flush_filters()
 956    sim.tc_show_ingress(expected=0)
 957
 958    start_test("Test disabling TC offloads is OK without filters...")
 959    ret, _ = sim.set_ethtool_tc_offloads(False, fail=False)
 960    fail(ret != 0,
 961         "Driver refused to disable TC offloads without filters installed...")
 962
 963    sim.set_ethtool_tc_offloads(True)
 964
 965    start_test("Test destroying device gets rid of TC filters...")
 966    sim.cls_bpf_add_filter(obj, skip_sw=True)
 967    simdev.remove()
 968    bpftool_prog_list_wait(expected=0)
 969
 970    simdev = NetdevSimDev()
 971    sim, = simdev.nsims
 972    sim.set_ethtool_tc_offloads(True)
 973
 974    start_test("Test destroying device gets rid of XDP...")
 975    sim.set_xdp(obj, "offload")
 976    simdev.remove()
 977    bpftool_prog_list_wait(expected=0)
 978
 979    simdev = NetdevSimDev()
 980    sim, = simdev.nsims
 981    sim.set_ethtool_tc_offloads(True)
 982
 983    start_test("Test XDP prog reporting...")
 984    sim.set_xdp(obj, "drv")
 985    ipl = sim.ip_link_show(xdp=True)
 986    progs = bpftool_prog_list(expected=1)
 987    fail(ipl["xdp"]["prog"]["id"] != progs[0]["id"],
 988         "Loaded program has wrong ID")
 989
 990    start_test("Test XDP prog replace without force...")
 991    ret, _ = sim.set_xdp(obj, "drv", fail=False)
 992    fail(ret == 0, "Replaced XDP program without -force")
 993    sim.wait_for_flush(total=1)
 994
 995    start_test("Test XDP prog replace with force...")
 996    ret, _ = sim.set_xdp(obj, "drv", force=True, fail=False)
 997    fail(ret != 0, "Could not replace XDP program with -force")
 998    bpftool_prog_list_wait(expected=1)
 999    ipl = sim.ip_link_show(xdp=True)
1000    progs = bpftool_prog_list(expected=1)
1001    fail(ipl["xdp"]["prog"]["id"] != progs[0]["id"],
1002         "Loaded program has wrong ID")
1003    fail("dev" in progs[0].keys(),
1004         "Device parameters reported for non-offloaded program")
1005
1006    start_test("Test XDP prog replace with bad flags...")
1007    ret, _, err = sim.set_xdp(obj, "generic", force=True,
 
 
 
 
1008                              fail=False, include_stderr=True)
1009    fail(ret == 0, "Replaced XDP program with a program in different mode")
1010    check_extack(err,
1011                 "Native and generic XDP can't be active at the same time.",
1012                 args)
 
 
 
 
 
 
 
 
1013
1014    start_test("Test MTU restrictions...")
1015    ret, _ = sim.set_mtu(9000, fail=False)
1016    fail(ret == 0,
1017         "Driver should refuse to increase MTU to 9000 with XDP loaded...")
1018    sim.unset_xdp("drv")
1019    bpftool_prog_list_wait(expected=0)
1020    sim.set_mtu(9000)
1021    ret, _, err = sim.set_xdp(obj, "drv", fail=False, include_stderr=True)
1022    fail(ret == 0, "Driver should refuse to load program with MTU of 9000...")
1023    check_extack_nsim(err, "MTU too large w/ XDP enabled.", args)
1024    sim.set_mtu(1500)
1025
1026    sim.wait_for_flush()
1027    start_test("Test non-offload XDP attaching to HW...")
1028    bpftool_prog_load("sample_ret0.bpf.o", "/sys/fs/bpf/nooffload")
1029    nooffload = bpf_pinned("/sys/fs/bpf/nooffload")
1030    ret, _, err = sim.set_xdp(nooffload, "offload",
1031                              fail=False, include_stderr=True)
1032    fail(ret == 0, "attached non-offloaded XDP program to HW")
1033    check_extack_nsim(err, "xdpoffload of non-bound program.", args)
1034    rm("/sys/fs/bpf/nooffload")
1035
1036    start_test("Test offload XDP attaching to drv...")
1037    bpftool_prog_load("sample_ret0.bpf.o", "/sys/fs/bpf/offload",
1038                      dev=sim['ifname'])
1039    offload = bpf_pinned("/sys/fs/bpf/offload")
1040    ret, _, err = sim.set_xdp(offload, "drv", fail=False, include_stderr=True)
1041    fail(ret == 0, "attached offloaded XDP program to drv")
1042    check_extack(err, "Using offloaded program without HW_MODE flag is not supported.", args)
1043    rm("/sys/fs/bpf/offload")
1044    sim.wait_for_flush()
1045
1046    start_test("Test XDP load failure...")
1047    sim.dfs["dev/bpf_bind_verifier_accept"] = 0
1048    ret, _, err = bpftool_prog_load("sample_ret0.bpf.o", "/sys/fs/bpf/offload",
1049                                 dev=sim['ifname'], fail=False, include_stderr=True)
1050    fail(ret == 0, "verifier should fail on load")
1051    check_verifier_log(err, "[netdevsim] Hello from netdevsim!")
1052    sim.dfs["dev/bpf_bind_verifier_accept"] = 1
1053    sim.wait_for_flush()
1054
1055    start_test("Test XDP offload...")
1056    _, _, err = sim.set_xdp(obj, "offload", verbose=True, include_stderr=True)
1057    ipl = sim.ip_link_show(xdp=True)
1058    link_xdp = ipl["xdp"]["prog"]
1059    progs = bpftool_prog_list(expected=1)
1060    prog = progs[0]
1061    fail(link_xdp["id"] != prog["id"], "Loaded program has wrong ID")
 
1062
1063    start_test("Test XDP offload is device bound...")
1064    dfs = simdev.dfs_get_bound_progs(expected=1)
1065    dprog = dfs[0]
1066
1067    fail(prog["id"] != link_xdp["id"], "Program IDs don't match")
1068    fail(prog["tag"] != link_xdp["tag"], "Program tags don't match")
1069    fail(str(link_xdp["id"]) != dprog["id"], "Program IDs don't match")
1070    fail(dprog["state"] != "xlated", "Offloaded program state not translated")
1071    fail(dprog["loaded"] != "Y", "Offloaded program is not loaded")
1072
1073    start_test("Test removing XDP program many times...")
1074    sim.unset_xdp("offload")
1075    sim.unset_xdp("offload")
1076    sim.unset_xdp("drv")
1077    sim.unset_xdp("drv")
1078    sim.unset_xdp("")
1079    sim.unset_xdp("")
1080    bpftool_prog_list_wait(expected=0)
1081
1082    start_test("Test attempt to use a program for a wrong device...")
1083    simdev2 = NetdevSimDev()
1084    sim2, = simdev2.nsims
1085    sim2.set_xdp(obj, "offload")
1086    pin_file, pinned = pin_prog("/sys/fs/bpf/tmp")
1087
1088    ret, _, err = sim.set_xdp(pinned, "offload",
1089                              fail=False, include_stderr=True)
1090    fail(ret == 0, "Pinned program loaded for a different device accepted")
1091    check_extack(err, "Program bound to different device.", args)
1092    simdev2.remove()
1093    ret, _, err = sim.set_xdp(pinned, "offload",
1094                              fail=False, include_stderr=True)
1095    fail(ret == 0, "Pinned program loaded for a removed device accepted")
1096    check_extack(err, "Program bound to different device.", args)
1097    rm(pin_file)
1098    bpftool_prog_list_wait(expected=0)
1099
1100    simdev, sim = test_multi_prog(simdev, sim, obj, "", 1)
1101    simdev, sim = test_multi_prog(simdev, sim, obj, "drv", 1)
1102    simdev, sim = test_multi_prog(simdev, sim, obj, "generic", 2)
1103
1104    start_test("Test mixing of TC and XDP...")
1105    sim.tc_add_ingress()
1106    sim.set_xdp(obj, "offload")
1107    ret, _, err = sim.cls_bpf_add_filter(obj, skip_sw=True,
1108                                         fail=False, include_stderr=True)
1109    fail(ret == 0, "Loading TC when XDP active should fail")
1110    check_extack_nsim(err, "driver and netdev offload states mismatch.", args)
1111    sim.unset_xdp("offload")
1112    sim.wait_for_flush()
1113
1114    sim.cls_bpf_add_filter(obj, skip_sw=True)
1115    ret, _, err = sim.set_xdp(obj, "offload", fail=False, include_stderr=True)
1116    fail(ret == 0, "Loading XDP when TC active should fail")
1117    check_extack_nsim(err, "TC program is already loaded.", args)
1118
1119    start_test("Test binding TC from pinned...")
1120    pin_file, pinned = pin_prog("/sys/fs/bpf/tmp")
1121    sim.tc_flush_filters(bound=1, total=1)
1122    sim.cls_bpf_add_filter(pinned, da=True, skip_sw=True)
1123    sim.tc_flush_filters(bound=1, total=1)
1124
1125    start_test("Test binding XDP from pinned...")
1126    sim.set_xdp(obj, "offload")
1127    pin_file, pinned = pin_prog("/sys/fs/bpf/tmp2", idx=1)
1128
1129    sim.set_xdp(pinned, "offload", force=True)
1130    sim.unset_xdp("offload")
1131    sim.set_xdp(pinned, "offload", force=True)
1132    sim.unset_xdp("offload")
1133
1134    start_test("Test offload of wrong type fails...")
1135    ret, _ = sim.cls_bpf_add_filter(pinned, da=True, skip_sw=True, fail=False)
1136    fail(ret == 0, "Managed to attach XDP program to TC")
1137
1138    start_test("Test asking for TC offload of two filters...")
1139    sim.cls_bpf_add_filter(obj, da=True, skip_sw=True)
1140    ret, _, err = sim.cls_bpf_add_filter(obj, da=True, skip_sw=True,
1141                                         fail=False, include_stderr=True)
1142    fail(ret == 0, "Managed to offload two TC filters at the same time")
1143    check_extack_nsim(err, "driver and netdev offload states mismatch.", args)
1144
1145    sim.tc_flush_filters(bound=2, total=2)
1146
1147    start_test("Test if netdev removal waits for translation...")
1148    delay_msec = 500
1149    sim.dfs["dev/bpf_bind_verifier_delay"] = delay_msec
1150    start = time.time()
1151    cmd_line = "tc filter add dev %s ingress bpf %s da skip_sw" % \
1152               (sim['ifname'], obj)
1153    tc_proc = cmd(cmd_line, background=True, fail=False)
1154    # Wait for the verifier to start
1155    while simdev.dfs_num_bound_progs() <= 2:
1156        pass
1157    simdev.remove()
1158    end = time.time()
1159    ret, _ = cmd_result(tc_proc, fail=False)
1160    time_diff = end - start
1161    log("Time", "start:\t%s\nend:\t%s\ndiff:\t%s" % (start, end, time_diff))
1162
1163    fail(ret == 0, "Managed to load TC filter on a unregistering device")
1164    delay_sec = delay_msec * 0.001
1165    fail(time_diff < delay_sec, "Removal process took %s, expected %s" %
1166         (time_diff, delay_sec))
1167
1168    # Remove all pinned files and reinstantiate the netdev
1169    clean_up()
1170    bpftool_prog_list_wait(expected=0)
1171
1172    simdev = NetdevSimDev()
1173    sim, = simdev.nsims
1174    map_obj = bpf_obj("sample_map_ret0.bpf.o")
1175    start_test("Test loading program with maps...")
1176    sim.set_xdp(map_obj, "offload", JSON=False) # map fixup msg breaks JSON
1177
1178    start_test("Test bpftool bound info reporting (own ns)...")
1179    check_dev_info(False, "")
1180
1181    start_test("Test bpftool bound info reporting (other ns)...")
1182    ns = mknetns()
1183    sim.set_ns(ns)
1184    check_dev_info(True, "")
1185
1186    start_test("Test bpftool bound info reporting (remote ns)...")
1187    check_dev_info(False, ns)
1188
1189    start_test("Test bpftool bound info reporting (back to own ns)...")
1190    sim.set_ns("")
1191    check_dev_info(False, "")
1192
1193    prog_file, _ = pin_prog("/sys/fs/bpf/tmp_prog")
1194    map_file, _ = pin_map("/sys/fs/bpf/tmp_map", idx=1, expected=2)
1195    simdev.remove()
1196
1197    start_test("Test bpftool bound info reporting (removed dev)...")
1198    check_dev_info_removed(prog_file=prog_file, map_file=map_file)
1199
1200    # Remove all pinned files and reinstantiate the netdev
1201    clean_up()
1202    bpftool_prog_list_wait(expected=0)
1203
1204    simdev = NetdevSimDev()
1205    sim, = simdev.nsims
1206
1207    start_test("Test map update (no flags)...")
1208    sim.set_xdp(map_obj, "offload", JSON=False) # map fixup msg breaks JSON
1209    maps = bpftool_map_list(expected=2)
1210    array = maps[0] if maps[0]["type"] == "array" else maps[1]
1211    htab = maps[0] if maps[0]["type"] == "hash" else maps[1]
1212    for m in maps:
1213        for i in range(2):
1214            bpftool("map update id %d key %s value %s" %
1215                    (m["id"], int2str("I", i), int2str("Q", i * 3)))
1216
1217    for m in maps:
1218        ret, _ = bpftool("map update id %d key %s value %s" %
1219                         (m["id"], int2str("I", 3), int2str("Q", 3 * 3)),
1220                         fail=False)
1221        fail(ret == 0, "added too many entries")
1222
1223    start_test("Test map update (exists)...")
1224    for m in maps:
1225        for i in range(2):
1226            bpftool("map update id %d key %s value %s exist" %
1227                    (m["id"], int2str("I", i), int2str("Q", i * 3)))
1228
1229    for m in maps:
1230        ret, err = bpftool("map update id %d key %s value %s exist" %
1231                           (m["id"], int2str("I", 3), int2str("Q", 3 * 3)),
1232                           fail=False)
1233        fail(ret == 0, "updated non-existing key")
1234        fail(err["error"].find("No such file or directory") == -1,
1235             "expected ENOENT, error is '%s'" % (err["error"]))
1236
1237    start_test("Test map update (noexist)...")
1238    for m in maps:
1239        for i in range(2):
1240            ret, err = bpftool("map update id %d key %s value %s noexist" %
1241                               (m["id"], int2str("I", i), int2str("Q", i * 3)),
1242                               fail=False)
1243        fail(ret == 0, "updated existing key")
1244        fail(err["error"].find("File exists") == -1,
1245             "expected EEXIST, error is '%s'" % (err["error"]))
1246
1247    start_test("Test map dump...")
1248    for m in maps:
1249        _, entries = bpftool("map dump id %d" % (m["id"]))
1250        for i in range(2):
1251            key = str2int(entries[i]["key"])
1252            fail(key != i, "expected key %d, got %d" % (key, i))
1253            val = str2int(entries[i]["value"])
1254            fail(val != i * 3, "expected value %d, got %d" % (val, i * 3))
1255
1256    start_test("Test map getnext...")
1257    for m in maps:
1258        _, entry = bpftool("map getnext id %d" % (m["id"]))
1259        key = str2int(entry["next_key"])
1260        fail(key != 0, "next key %d, expected %d" % (key, 0))
1261        _, entry = bpftool("map getnext id %d key %s" %
1262                           (m["id"], int2str("I", 0)))
1263        key = str2int(entry["next_key"])
1264        fail(key != 1, "next key %d, expected %d" % (key, 1))
1265        ret, err = bpftool("map getnext id %d key %s" %
1266                           (m["id"], int2str("I", 1)), fail=False)
1267        fail(ret == 0, "got next key past the end of map")
1268        fail(err["error"].find("No such file or directory") == -1,
1269             "expected ENOENT, error is '%s'" % (err["error"]))
1270
1271    start_test("Test map delete (htab)...")
1272    for i in range(2):
1273        bpftool("map delete id %d key %s" % (htab["id"], int2str("I", i)))
1274
1275    start_test("Test map delete (array)...")
1276    for i in range(2):
1277        ret, err = bpftool("map delete id %d key %s" %
1278                           (htab["id"], int2str("I", i)), fail=False)
1279        fail(ret == 0, "removed entry from an array")
1280        fail(err["error"].find("No such file or directory") == -1,
1281             "expected ENOENT, error is '%s'" % (err["error"]))
1282
1283    start_test("Test map remove...")
1284    sim.unset_xdp("offload")
1285    bpftool_map_list_wait(expected=0)
1286    simdev.remove()
1287
1288    simdev = NetdevSimDev()
1289    sim, = simdev.nsims
1290    sim.set_xdp(map_obj, "offload", JSON=False) # map fixup msg breaks JSON
1291    simdev.remove()
1292    bpftool_map_list_wait(expected=0)
1293
1294    start_test("Test map creation fail path...")
1295    simdev = NetdevSimDev()
1296    sim, = simdev.nsims
1297    sim.dfs["bpf_map_accept"] = "N"
1298    ret, _ = sim.set_xdp(map_obj, "offload", JSON=False, fail=False)
1299    fail(ret == 0,
1300         "netdevsim didn't refuse to create a map with offload disabled")
1301
1302    simdev.remove()
1303
1304    start_test("Test multi-dev ASIC program reuse...")
1305    simdevA = NetdevSimDev()
1306    simA, = simdevA.nsims
1307    simdevB = NetdevSimDev(3)
1308    simB1, simB2, simB3 = simdevB.nsims
1309    sims = (simA, simB1, simB2, simB3)
1310    simB = (simB1, simB2, simB3)
1311
1312    bpftool_prog_load("sample_map_ret0.bpf.o", "/sys/fs/bpf/nsimA",
1313                      dev=simA['ifname'])
1314    progA = bpf_pinned("/sys/fs/bpf/nsimA")
1315    bpftool_prog_load("sample_map_ret0.bpf.o", "/sys/fs/bpf/nsimB",
1316                      dev=simB1['ifname'])
1317    progB = bpf_pinned("/sys/fs/bpf/nsimB")
1318
1319    simA.set_xdp(progA, "offload", JSON=False)
1320    for d in simdevB.nsims:
1321        d.set_xdp(progB, "offload", JSON=False)
1322
1323    start_test("Test multi-dev ASIC cross-dev replace...")
1324    ret, _ = simA.set_xdp(progB, "offload", force=True, JSON=False, fail=False)
1325    fail(ret == 0, "cross-ASIC program allowed")
1326    for d in simdevB.nsims:
1327        ret, _ = d.set_xdp(progA, "offload", force=True, JSON=False, fail=False)
1328        fail(ret == 0, "cross-ASIC program allowed")
1329
1330    start_test("Test multi-dev ASIC cross-dev install...")
1331    for d in sims:
1332        d.unset_xdp("offload")
1333
1334    ret, _, err = simA.set_xdp(progB, "offload", force=True, JSON=False,
1335                               fail=False, include_stderr=True)
1336    fail(ret == 0, "cross-ASIC program allowed")
1337    check_extack(err, "Program bound to different device.", args)
1338    for d in simdevB.nsims:
1339        ret, _, err = d.set_xdp(progA, "offload", force=True, JSON=False,
1340                                fail=False, include_stderr=True)
1341        fail(ret == 0, "cross-ASIC program allowed")
1342        check_extack(err, "Program bound to different device.", args)
1343
1344    start_test("Test multi-dev ASIC cross-dev map reuse...")
1345
1346    mapA = bpftool("prog show %s" % (progA))[1]["map_ids"][0]
1347    mapB = bpftool("prog show %s" % (progB))[1]["map_ids"][0]
1348
1349    ret, _ = bpftool_prog_load("sample_map_ret0.bpf.o", "/sys/fs/bpf/nsimB_",
1350                               dev=simB3['ifname'],
1351                               maps=["idx 0 id %d" % (mapB)],
1352                               fail=False)
1353    fail(ret != 0, "couldn't reuse a map on the same ASIC")
1354    rm("/sys/fs/bpf/nsimB_")
1355
1356    ret, _, err = bpftool_prog_load("sample_map_ret0.bpf.o", "/sys/fs/bpf/nsimA_",
1357                                    dev=simA['ifname'],
1358                                    maps=["idx 0 id %d" % (mapB)],
1359                                    fail=False, include_stderr=True)
1360    fail(ret == 0, "could reuse a map on a different ASIC")
1361    fail(err.count("offload device mismatch between prog and map") == 0,
1362         "error message missing for cross-ASIC map")
1363
1364    ret, _, err = bpftool_prog_load("sample_map_ret0.bpf.o", "/sys/fs/bpf/nsimB_",
1365                                    dev=simB1['ifname'],
1366                                    maps=["idx 0 id %d" % (mapA)],
1367                                    fail=False, include_stderr=True)
1368    fail(ret == 0, "could reuse a map on a different ASIC")
1369    fail(err.count("offload device mismatch between prog and map") == 0,
1370         "error message missing for cross-ASIC map")
1371
1372    start_test("Test multi-dev ASIC cross-dev destruction...")
1373    bpftool_prog_list_wait(expected=2)
1374
1375    simdevA.remove()
1376    bpftool_prog_list_wait(expected=1)
1377
1378    ifnameB = bpftool("prog show %s" % (progB))[1]["dev"]["ifname"]
1379    fail(ifnameB != simB1['ifname'], "program not bound to original device")
1380    simB1.remove()
1381    bpftool_prog_list_wait(expected=1)
1382
1383    start_test("Test multi-dev ASIC cross-dev destruction - move...")
1384    ifnameB = bpftool("prog show %s" % (progB))[1]["dev"]["ifname"]
1385    fail(ifnameB not in (simB2['ifname'], simB3['ifname']),
1386         "program not bound to remaining devices")
1387
1388    simB2.remove()
1389    ifnameB = bpftool("prog show %s" % (progB))[1]["dev"]["ifname"]
1390    fail(ifnameB != simB3['ifname'], "program not bound to remaining device")
1391
1392    simB3.remove()
1393    simdevB.remove()
1394    bpftool_prog_list_wait(expected=0)
1395
1396    start_test("Test multi-dev ASIC cross-dev destruction - orphaned...")
1397    ret, out = bpftool("prog show %s" % (progB), fail=False)
1398    fail(ret != 0, "couldn't get information about orphaned program")
1399
1400    print("%s: OK" % (os.path.basename(__file__)))
1401
1402finally:
1403    log("Clean up...", "", level=1)
1404    log_level_inc()
1405    clean_up()