Linux Audio

Check our new training course

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