1c25ce589SFinn Behrens#!/usr/bin/env python3
2417ec264SJakub Kicinski
3417ec264SJakub Kicinski# Copyright (C) 2017 Netronome Systems, Inc.
4e05b2d14SJiri Pirko# Copyright (c) 2019 Mellanox Technologies. All rights reserved
5417ec264SJakub Kicinski#
6417ec264SJakub Kicinski# This software is licensed under the GNU General License Version 2,
7417ec264SJakub Kicinski# June 1991 as shown in the file COPYING in the top-level directory of this
8417ec264SJakub Kicinski# source tree.
9417ec264SJakub Kicinski#
10417ec264SJakub Kicinski# THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS"
11417ec264SJakub Kicinski# WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING,
12417ec264SJakub Kicinski# BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
13417ec264SJakub Kicinski# FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE
14417ec264SJakub Kicinski# OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME
15417ec264SJakub Kicinski# THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
16417ec264SJakub Kicinski
17417ec264SJakub Kicinskifrom datetime import datetime
18417ec264SJakub Kicinskiimport argparse
19e05b2d14SJiri Pirkoimport errno
20417ec264SJakub Kicinskiimport json
21417ec264SJakub Kicinskiimport os
22417ec264SJakub Kicinskiimport pprint
23752d7b45SJakub Kicinskiimport random
24e05b2d14SJiri Pirkoimport re
258101e069SJakub Kicinskiimport stat
26752d7b45SJakub Kicinskiimport string
277fedbb7cSJakub Kicinskiimport struct
28417ec264SJakub Kicinskiimport subprocess
29417ec264SJakub Kicinskiimport time
30d7f35638SJakub Kicinskiimport traceback
31417ec264SJakub Kicinski
32417ec264SJakub Kicinskilogfile = None
33417ec264SJakub Kicinskilog_level = 1
34caf95228SQuentin Monnetskip_extack = False
35417ec264SJakub Kicinskibpf_test_dir = os.path.dirname(os.path.realpath(__file__))
36417ec264SJakub Kicinskipp = pprint.PrettyPrinter()
37417ec264SJakub Kicinskidevs = [] # devices we created for clean up
38417ec264SJakub Kicinskifiles = [] # files to be removed
39752d7b45SJakub Kicinskinetns = [] # net namespaces to be removed
40417ec264SJakub Kicinski
41417ec264SJakub Kicinskidef log_get_sec(level=0):
42417ec264SJakub Kicinski    return "*" * (log_level + level)
43417ec264SJakub Kicinski
44417ec264SJakub Kicinskidef log_level_inc(add=1):
45417ec264SJakub Kicinski    global log_level
46417ec264SJakub Kicinski    log_level += add
47417ec264SJakub Kicinski
48417ec264SJakub Kicinskidef log_level_dec(sub=1):
49417ec264SJakub Kicinski    global log_level
50417ec264SJakub Kicinski    log_level -= sub
51417ec264SJakub Kicinski
52417ec264SJakub Kicinskidef log_level_set(level):
53417ec264SJakub Kicinski    global log_level
54417ec264SJakub Kicinski    log_level = level
55417ec264SJakub Kicinski
56417ec264SJakub Kicinskidef log(header, data, level=None):
57417ec264SJakub Kicinski    """
58417ec264SJakub Kicinski    Output to an optional log.
59417ec264SJakub Kicinski    """
60417ec264SJakub Kicinski    if logfile is None:
61417ec264SJakub Kicinski        return
62417ec264SJakub Kicinski    if level is not None:
63417ec264SJakub Kicinski        log_level_set(level)
64417ec264SJakub Kicinski
65417ec264SJakub Kicinski    if not isinstance(data, str):
66417ec264SJakub Kicinski        data = pp.pformat(data)
67417ec264SJakub Kicinski
68417ec264SJakub Kicinski    if len(header):
69417ec264SJakub Kicinski        logfile.write("\n" + log_get_sec() + " ")
70417ec264SJakub Kicinski        logfile.write(header)
71417ec264SJakub Kicinski    if len(header) and len(data.strip()):
72417ec264SJakub Kicinski        logfile.write("\n")
73417ec264SJakub Kicinski    logfile.write(data)
74417ec264SJakub Kicinski
75417ec264SJakub Kicinskidef skip(cond, msg):
76417ec264SJakub Kicinski    if not cond:
77417ec264SJakub Kicinski        return
78417ec264SJakub Kicinski    print("SKIP: " + msg)
79417ec264SJakub Kicinski    log("SKIP: " + msg, "", level=1)
80417ec264SJakub Kicinski    os.sys.exit(0)
81417ec264SJakub Kicinski
82417ec264SJakub Kicinskidef fail(cond, msg):
83417ec264SJakub Kicinski    if not cond:
84417ec264SJakub Kicinski        return
85417ec264SJakub Kicinski    print("FAIL: " + msg)
86d7f35638SJakub Kicinski    tb = "".join(traceback.extract_stack().format())
87d7f35638SJakub Kicinski    print(tb)
88d7f35638SJakub Kicinski    log("FAIL: " + msg, tb, level=1)
89417ec264SJakub Kicinski    os.sys.exit(1)
90417ec264SJakub Kicinski
91417ec264SJakub Kicinskidef start_test(msg):
92417ec264SJakub Kicinski    log(msg, "", level=1)
93417ec264SJakub Kicinski    log_level_inc()
94417ec264SJakub Kicinski    print(msg)
95417ec264SJakub Kicinski
96417ec264SJakub Kicinskidef cmd(cmd, shell=True, include_stderr=False, background=False, fail=True):
97417ec264SJakub Kicinski    """
98417ec264SJakub Kicinski    Run a command in subprocess and return tuple of (retval, stdout);
99417ec264SJakub Kicinski    optionally return stderr as well as third value.
100417ec264SJakub Kicinski    """
101417ec264SJakub Kicinski    proc = subprocess.Popen(cmd, shell=shell, stdout=subprocess.PIPE,
102417ec264SJakub Kicinski                            stderr=subprocess.PIPE)
103417ec264SJakub Kicinski    if background:
104417ec264SJakub Kicinski        msg = "%s START: %s" % (log_get_sec(1),
105417ec264SJakub Kicinski                                datetime.now().strftime("%H:%M:%S.%f"))
106417ec264SJakub Kicinski        log("BKG " + proc.args, msg)
107417ec264SJakub Kicinski        return proc
108417ec264SJakub Kicinski
109417ec264SJakub Kicinski    return cmd_result(proc, include_stderr=include_stderr, fail=fail)
110417ec264SJakub Kicinski
111417ec264SJakub Kicinskidef cmd_result(proc, include_stderr=False, fail=False):
112417ec264SJakub Kicinski    stdout, stderr = proc.communicate()
113417ec264SJakub Kicinski    stdout = stdout.decode("utf-8")
114417ec264SJakub Kicinski    stderr = stderr.decode("utf-8")
115417ec264SJakub Kicinski    proc.stdout.close()
116417ec264SJakub Kicinski    proc.stderr.close()
117417ec264SJakub Kicinski
118417ec264SJakub Kicinski    stderr = "\n" + stderr
119417ec264SJakub Kicinski    if stderr[-1] == "\n":
120417ec264SJakub Kicinski        stderr = stderr[:-1]
121417ec264SJakub Kicinski
122417ec264SJakub Kicinski    sec = log_get_sec(1)
123417ec264SJakub Kicinski    log("CMD " + proc.args,
124417ec264SJakub Kicinski        "RETCODE: %d\n%s STDOUT:\n%s%s STDERR:%s\n%s END: %s" %
125417ec264SJakub Kicinski        (proc.returncode, sec, stdout, sec, stderr,
126417ec264SJakub Kicinski         sec, datetime.now().strftime("%H:%M:%S.%f")))
127417ec264SJakub Kicinski
128417ec264SJakub Kicinski    if proc.returncode != 0 and fail:
129417ec264SJakub Kicinski        if len(stderr) > 0 and stderr[-1] == "\n":
130417ec264SJakub Kicinski            stderr = stderr[:-1]
131417ec264SJakub Kicinski        raise Exception("Command failed: %s\n%s" % (proc.args, stderr))
132417ec264SJakub Kicinski
133417ec264SJakub Kicinski    if include_stderr:
134417ec264SJakub Kicinski        return proc.returncode, stdout, stderr
135417ec264SJakub Kicinski    else:
136417ec264SJakub Kicinski        return proc.returncode, stdout
137417ec264SJakub Kicinski
138417ec264SJakub Kicinskidef rm(f):
139417ec264SJakub Kicinski    cmd("rm -f %s" % (f))
140417ec264SJakub Kicinski    if f in files:
141417ec264SJakub Kicinski        files.remove(f)
142417ec264SJakub Kicinski
143caf95228SQuentin Monnetdef tool(name, args, flags, JSON=True, ns="", fail=True, include_stderr=False):
144417ec264SJakub Kicinski    params = ""
145417ec264SJakub Kicinski    if JSON:
146417ec264SJakub Kicinski        params += "%s " % (flags["json"])
147417ec264SJakub Kicinski
148752d7b45SJakub Kicinski    if ns != "":
149752d7b45SJakub Kicinski        ns = "ip netns exec %s " % (ns)
150752d7b45SJakub Kicinski
151caf95228SQuentin Monnet    if include_stderr:
152caf95228SQuentin Monnet        ret, stdout, stderr = cmd(ns + name + " " + params + args,
153caf95228SQuentin Monnet                                  fail=fail, include_stderr=True)
154caf95228SQuentin Monnet    else:
155caf95228SQuentin Monnet        ret, stdout = cmd(ns + name + " " + params + args,
156caf95228SQuentin Monnet                          fail=fail, include_stderr=False)
157caf95228SQuentin Monnet
158caf95228SQuentin Monnet    if JSON and len(stdout.strip()) != 0:
159caf95228SQuentin Monnet        out = json.loads(stdout)
160caf95228SQuentin Monnet    else:
161caf95228SQuentin Monnet        out = stdout
162caf95228SQuentin Monnet
163caf95228SQuentin Monnet    if include_stderr:
164caf95228SQuentin Monnet        return ret, out, stderr
165417ec264SJakub Kicinski    else:
166417ec264SJakub Kicinski        return ret, out
167417ec264SJakub Kicinski
1687736b6edSJakub Kicinskidef bpftool(args, JSON=True, ns="", fail=True, include_stderr=False):
1697736b6edSJakub Kicinski    return tool("bpftool", args, {"json":"-p"}, JSON=JSON, ns=ns,
1707736b6edSJakub Kicinski                fail=fail, include_stderr=include_stderr)
171417ec264SJakub Kicinski
172752d7b45SJakub Kicinskidef bpftool_prog_list(expected=None, ns=""):
173752d7b45SJakub Kicinski    _, progs = bpftool("prog show", JSON=True, ns=ns, fail=True)
17447cf52a2SJakub Kicinski    # Remove the base progs
17547cf52a2SJakub Kicinski    for p in base_progs:
17647cf52a2SJakub Kicinski        if p in progs:
17747cf52a2SJakub Kicinski            progs.remove(p)
178417ec264SJakub Kicinski    if expected is not None:
179417ec264SJakub Kicinski        if len(progs) != expected:
180417ec264SJakub Kicinski            fail(True, "%d BPF programs loaded, expected %d" %
181417ec264SJakub Kicinski                 (len(progs), expected))
182417ec264SJakub Kicinski    return progs
183417ec264SJakub Kicinski
1847fedbb7cSJakub Kicinskidef bpftool_map_list(expected=None, ns=""):
1857fedbb7cSJakub Kicinski    _, maps = bpftool("map show", JSON=True, ns=ns, fail=True)
18647cf52a2SJakub Kicinski    # Remove the base maps
187891663acSYauheni Kaliuta    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]
1887fedbb7cSJakub Kicinski    if expected is not None:
1897fedbb7cSJakub Kicinski        if len(maps) != expected:
1907fedbb7cSJakub Kicinski            fail(True, "%d BPF maps loaded, expected %d" %
1917fedbb7cSJakub Kicinski                 (len(maps), expected))
1927fedbb7cSJakub Kicinski    return maps
1937fedbb7cSJakub Kicinski
194417ec264SJakub Kicinskidef bpftool_prog_list_wait(expected=0, n_retry=20):
195417ec264SJakub Kicinski    for i in range(n_retry):
196417ec264SJakub Kicinski        nprogs = len(bpftool_prog_list())
197417ec264SJakub Kicinski        if nprogs == expected:
198417ec264SJakub Kicinski            return
199417ec264SJakub Kicinski        time.sleep(0.05)
200417ec264SJakub Kicinski    raise Exception("Time out waiting for program counts to stabilize want %d, have %d" % (expected, nprogs))
201417ec264SJakub Kicinski
2027fedbb7cSJakub Kicinskidef bpftool_map_list_wait(expected=0, n_retry=20):
2037fedbb7cSJakub Kicinski    for i in range(n_retry):
2047fedbb7cSJakub Kicinski        nmaps = len(bpftool_map_list())
2057fedbb7cSJakub Kicinski        if nmaps == expected:
2067fedbb7cSJakub Kicinski            return
2077fedbb7cSJakub Kicinski        time.sleep(0.05)
2087fedbb7cSJakub Kicinski    raise Exception("Time out waiting for map counts to stabilize want %d, have %d" % (expected, nmaps))
2097fedbb7cSJakub Kicinski
2107736b6edSJakub Kicinskidef bpftool_prog_load(sample, file_name, maps=[], prog_type="xdp", dev=None,
2117736b6edSJakub Kicinski                      fail=True, include_stderr=False):
2127736b6edSJakub Kicinski    args = "prog load %s %s" % (os.path.join(bpf_test_dir, sample), file_name)
2137736b6edSJakub Kicinski    if prog_type is not None:
2147736b6edSJakub Kicinski        args += " type " + prog_type
2157736b6edSJakub Kicinski    if dev is not None:
2167736b6edSJakub Kicinski        args += " dev " + dev
2177736b6edSJakub Kicinski    if len(maps):
2187736b6edSJakub Kicinski        args += " map " + " map ".join(maps)
2197736b6edSJakub Kicinski
2207736b6edSJakub Kicinski    res = bpftool(args, fail=fail, include_stderr=include_stderr)
2217736b6edSJakub Kicinski    if res[0] == 0:
2227736b6edSJakub Kicinski        files.append(file_name)
2237736b6edSJakub Kicinski    return res
2247736b6edSJakub Kicinski
225caf95228SQuentin Monnetdef ip(args, force=False, JSON=True, ns="", fail=True, include_stderr=False):
226417ec264SJakub Kicinski    if force:
227417ec264SJakub Kicinski        args = "-force " + args
228caf95228SQuentin Monnet    return tool("ip", args, {"json":"-j"}, JSON=JSON, ns=ns,
229caf95228SQuentin Monnet                fail=fail, include_stderr=include_stderr)
230417ec264SJakub Kicinski
231caf95228SQuentin Monnetdef tc(args, JSON=True, ns="", fail=True, include_stderr=False):
232caf95228SQuentin Monnet    return tool("tc", args, {"json":"-p"}, JSON=JSON, ns=ns,
233caf95228SQuentin Monnet                fail=fail, include_stderr=include_stderr)
234417ec264SJakub Kicinski
235417ec264SJakub Kicinskidef ethtool(dev, opt, args, fail=True):
236417ec264SJakub Kicinski    return cmd("ethtool %s %s %s" % (opt, dev["ifname"], args), fail=fail)
237417ec264SJakub Kicinski
238417ec264SJakub Kicinskidef bpf_obj(name, sec=".text", path=bpf_test_dir,):
239417ec264SJakub Kicinski    return "obj %s sec %s" % (os.path.join(path, name), sec)
240417ec264SJakub Kicinski
241417ec264SJakub Kicinskidef bpf_pinned(name):
242417ec264SJakub Kicinski    return "pinned %s" % (name)
243417ec264SJakub Kicinski
244417ec264SJakub Kicinskidef bpf_bytecode(bytecode):
245417ec264SJakub Kicinski    return "bytecode \"%s\"" % (bytecode)
246417ec264SJakub Kicinski
247752d7b45SJakub Kicinskidef mknetns(n_retry=10):
248752d7b45SJakub Kicinski    for i in range(n_retry):
249752d7b45SJakub Kicinski        name = ''.join([random.choice(string.ascii_letters) for i in range(8)])
250752d7b45SJakub Kicinski        ret, _ = ip("netns add %s" % (name), fail=False)
251752d7b45SJakub Kicinski        if ret == 0:
252752d7b45SJakub Kicinski            netns.append(name)
253752d7b45SJakub Kicinski            return name
254752d7b45SJakub Kicinski    return None
255752d7b45SJakub Kicinski
2567fedbb7cSJakub Kicinskidef int2str(fmt, val):
2577fedbb7cSJakub Kicinski    ret = []
2587fedbb7cSJakub Kicinski    for b in struct.pack(fmt, val):
2597fedbb7cSJakub Kicinski        ret.append(int(b))
2607fedbb7cSJakub Kicinski    return " ".join(map(lambda x: str(x), ret))
2617fedbb7cSJakub Kicinski
2627fedbb7cSJakub Kicinskidef str2int(strtab):
2637fedbb7cSJakub Kicinski    inttab = []
2647fedbb7cSJakub Kicinski    for i in strtab:
2657fedbb7cSJakub Kicinski        inttab.append(int(i, 16))
2667fedbb7cSJakub Kicinski    ba = bytearray(inttab)
2677fedbb7cSJakub Kicinski    if len(strtab) == 4:
2687fedbb7cSJakub Kicinski        fmt = "I"
2697fedbb7cSJakub Kicinski    elif len(strtab) == 8:
2707fedbb7cSJakub Kicinski        fmt = "Q"
2717fedbb7cSJakub Kicinski    else:
2727fedbb7cSJakub Kicinski        raise Exception("String array of len %d can't be unpacked to an int" %
2737fedbb7cSJakub Kicinski                        (len(strtab)))
2747fedbb7cSJakub Kicinski    return struct.unpack(fmt, ba)[0]
2757fedbb7cSJakub Kicinski
276417ec264SJakub Kicinskiclass DebugfsDir:
277417ec264SJakub Kicinski    """
278417ec264SJakub Kicinski    Class for accessing DebugFS directories as a dictionary.
279417ec264SJakub Kicinski    """
280417ec264SJakub Kicinski
281417ec264SJakub Kicinski    def __init__(self, path):
282417ec264SJakub Kicinski        self.path = path
283417ec264SJakub Kicinski        self._dict = self._debugfs_dir_read(path)
284417ec264SJakub Kicinski
285417ec264SJakub Kicinski    def __len__(self):
286417ec264SJakub Kicinski        return len(self._dict.keys())
287417ec264SJakub Kicinski
288417ec264SJakub Kicinski    def __getitem__(self, key):
289417ec264SJakub Kicinski        if type(key) is int:
290417ec264SJakub Kicinski            key = list(self._dict.keys())[key]
291417ec264SJakub Kicinski        return self._dict[key]
292417ec264SJakub Kicinski
293417ec264SJakub Kicinski    def __setitem__(self, key, value):
294417ec264SJakub Kicinski        log("DebugFS set %s = %s" % (key, value), "")
295417ec264SJakub Kicinski        log_level_inc()
296417ec264SJakub Kicinski
297417ec264SJakub Kicinski        cmd("echo '%s' > %s/%s" % (value, self.path, key))
298417ec264SJakub Kicinski        log_level_dec()
299417ec264SJakub Kicinski
300417ec264SJakub Kicinski        _, out = cmd('cat %s/%s' % (self.path, key))
301417ec264SJakub Kicinski        self._dict[key] = out.strip()
302417ec264SJakub Kicinski
303417ec264SJakub Kicinski    def _debugfs_dir_read(self, path):
304417ec264SJakub Kicinski        dfs = {}
305417ec264SJakub Kicinski
306417ec264SJakub Kicinski        log("DebugFS state for %s" % (path), "")
307417ec264SJakub Kicinski        log_level_inc(add=2)
308417ec264SJakub Kicinski
309417ec264SJakub Kicinski        _, out = cmd('ls ' + path)
310417ec264SJakub Kicinski        for f in out.split():
311ab1d0cc0SJiri Pirko            if f == "ports":
312ab1d0cc0SJiri Pirko                continue
3138101e069SJakub Kicinski
314417ec264SJakub Kicinski            p = os.path.join(path, f)
31556c1291eSDaniel Borkmann            if not os.stat(p).st_mode & stat.S_IRUSR:
31656c1291eSDaniel Borkmann                continue
31756c1291eSDaniel Borkmann
31856c1291eSDaniel Borkmann            if os.path.isfile(p):
3194bbca662SHangbin Liu                # We need to init trap_flow_action_cookie before read it
3204bbca662SHangbin Liu                if f == "trap_flow_action_cookie":
3214bbca662SHangbin Liu                    cmd('echo deadbeef > %s/%s' % (path, f))
322417ec264SJakub Kicinski                _, out = cmd('cat %s/%s' % (path, f))
323417ec264SJakub Kicinski                dfs[f] = out.strip()
324417ec264SJakub Kicinski            elif os.path.isdir(p):
325417ec264SJakub Kicinski                dfs[f] = DebugfsDir(p)
326417ec264SJakub Kicinski            else:
327417ec264SJakub Kicinski                raise Exception("%s is neither file nor directory" % (p))
328417ec264SJakub Kicinski
329417ec264SJakub Kicinski        log_level_dec()
330417ec264SJakub Kicinski        log("DebugFS state", dfs)
331417ec264SJakub Kicinski        log_level_dec()
332417ec264SJakub Kicinski
333417ec264SJakub Kicinski        return dfs
334417ec264SJakub Kicinski
335e05b2d14SJiri Pirkoclass NetdevSimDev:
336e05b2d14SJiri Pirko    """
337e05b2d14SJiri Pirko    Class for netdevsim bus device and its attributes.
338e05b2d14SJiri Pirko    """
339acceca8dSJakub Kicinski    @staticmethod
340acceca8dSJakub Kicinski    def ctrl_write(path, val):
341acceca8dSJakub Kicinski        fullpath = os.path.join("/sys/bus/netdevsim/", path)
342acceca8dSJakub Kicinski        try:
343acceca8dSJakub Kicinski            with open(fullpath, "w") as f:
344acceca8dSJakub Kicinski                f.write(val)
345acceca8dSJakub Kicinski        except OSError as e:
346acceca8dSJakub Kicinski            log("WRITE %s: %r" % (fullpath, val), -e.errno)
347acceca8dSJakub Kicinski            raise e
348acceca8dSJakub Kicinski        log("WRITE %s: %r" % (fullpath, val), 0)
349e05b2d14SJiri Pirko
350e05b2d14SJiri Pirko    def __init__(self, port_count=1):
351e05b2d14SJiri Pirko        addr = 0
352e05b2d14SJiri Pirko        while True:
353e05b2d14SJiri Pirko            try:
354acceca8dSJakub Kicinski                self.ctrl_write("new_device", "%u %u" % (addr, port_count))
355e05b2d14SJiri Pirko            except OSError as e:
356e05b2d14SJiri Pirko                if e.errno == errno.ENOSPC:
357e05b2d14SJiri Pirko                    addr += 1
358e05b2d14SJiri Pirko                    continue
359e05b2d14SJiri Pirko                raise e
360e05b2d14SJiri Pirko            break
361e05b2d14SJiri Pirko        self.addr = addr
362e05b2d14SJiri Pirko
363e05b2d14SJiri Pirko        # As probe of netdevsim device might happen from a workqueue,
364e05b2d14SJiri Pirko        # so wait here until all netdevs appear.
365e05b2d14SJiri Pirko        self.wait_for_netdevs(port_count)
366e05b2d14SJiri Pirko
367e05b2d14SJiri Pirko        ret, out = cmd("udevadm settle", fail=False)
368e05b2d14SJiri Pirko        if ret:
369e05b2d14SJiri Pirko            raise Exception("udevadm settle failed")
370e05b2d14SJiri Pirko        ifnames = self.get_ifnames()
371e05b2d14SJiri Pirko
372e05b2d14SJiri Pirko        devs.append(self)
373e05b2d14SJiri Pirko        self.dfs_dir = "/sys/kernel/debug/netdevsim/netdevsim%u/" % addr
374e05b2d14SJiri Pirko
375e05b2d14SJiri Pirko        self.nsims = []
376e05b2d14SJiri Pirko        for port_index in range(port_count):
377e05b2d14SJiri Pirko            self.nsims.append(NetdevSim(self, port_index, ifnames[port_index]))
378e05b2d14SJiri Pirko
379e05b2d14SJiri Pirko    def get_ifnames(self):
380e05b2d14SJiri Pirko        ifnames = []
381e05b2d14SJiri Pirko        listdir = os.listdir("/sys/bus/netdevsim/devices/netdevsim%u/net/" % self.addr)
382e05b2d14SJiri Pirko        for ifname in listdir:
383e05b2d14SJiri Pirko            ifnames.append(ifname)
384e05b2d14SJiri Pirko        ifnames.sort()
385e05b2d14SJiri Pirko        return ifnames
386e05b2d14SJiri Pirko
387e05b2d14SJiri Pirko    def wait_for_netdevs(self, port_count):
388e05b2d14SJiri Pirko        timeout = 5
389e05b2d14SJiri Pirko        timeout_start = time.time()
390e05b2d14SJiri Pirko
391e05b2d14SJiri Pirko        while True:
392e05b2d14SJiri Pirko            try:
393e05b2d14SJiri Pirko                ifnames = self.get_ifnames()
394e05b2d14SJiri Pirko            except FileNotFoundError as e:
395e05b2d14SJiri Pirko                ifnames = []
396e05b2d14SJiri Pirko            if len(ifnames) == port_count:
397e05b2d14SJiri Pirko                break
398e05b2d14SJiri Pirko            if time.time() < timeout_start + timeout:
399e05b2d14SJiri Pirko                continue
400e05b2d14SJiri Pirko            raise Exception("netdevices did not appear within timeout")
401e05b2d14SJiri Pirko
402e05b2d14SJiri Pirko    def dfs_num_bound_progs(self):
403e05b2d14SJiri Pirko        path = os.path.join(self.dfs_dir, "bpf_bound_progs")
404e05b2d14SJiri Pirko        _, progs = cmd('ls %s' % (path))
405e05b2d14SJiri Pirko        return len(progs.split())
406e05b2d14SJiri Pirko
407e05b2d14SJiri Pirko    def dfs_get_bound_progs(self, expected):
408e05b2d14SJiri Pirko        progs = DebugfsDir(os.path.join(self.dfs_dir, "bpf_bound_progs"))
409e05b2d14SJiri Pirko        if expected is not None:
410e05b2d14SJiri Pirko            if len(progs) != expected:
411e05b2d14SJiri Pirko                fail(True, "%d BPF programs bound, expected %d" %
412e05b2d14SJiri Pirko                     (len(progs), expected))
413e05b2d14SJiri Pirko        return progs
414e05b2d14SJiri Pirko
415e05b2d14SJiri Pirko    def remove(self):
416acceca8dSJakub Kicinski        self.ctrl_write("del_device", "%u" % (self.addr, ))
417e05b2d14SJiri Pirko        devs.remove(self)
418e05b2d14SJiri Pirko
419e05b2d14SJiri Pirko    def remove_nsim(self, nsim):
420e05b2d14SJiri Pirko        self.nsims.remove(nsim)
421acceca8dSJakub Kicinski        self.ctrl_write("devices/netdevsim%u/del_port" % (self.addr, ),
422acceca8dSJakub Kicinski                        "%u" % (nsim.port_index, ))
423e05b2d14SJiri Pirko
424417ec264SJakub Kicinskiclass NetdevSim:
425417ec264SJakub Kicinski    """
426417ec264SJakub Kicinski    Class for netdevsim netdevice and its attributes.
427417ec264SJakub Kicinski    """
428417ec264SJakub Kicinski
429e05b2d14SJiri Pirko    def __init__(self, nsimdev, port_index, ifname):
430e05b2d14SJiri Pirko        # In case udev renamed the netdev to according to new schema,
431e05b2d14SJiri Pirko        # check if the name matches the port_index.
432e05b2d14SJiri Pirko        nsimnamere = re.compile("eni\d+np(\d+)")
433e05b2d14SJiri Pirko        match = nsimnamere.match(ifname)
434e05b2d14SJiri Pirko        if match and int(match.groups()[0]) != port_index + 1:
435e05b2d14SJiri Pirko            raise Exception("netdevice name mismatches the expected one")
4367736b6edSJakub Kicinski
437e05b2d14SJiri Pirko        self.nsimdev = nsimdev
438e05b2d14SJiri Pirko        self.port_index = port_index
439752d7b45SJakub Kicinski        self.ns = ""
440e05b2d14SJiri Pirko        self.dfs_dir = "%s/ports/%u/" % (nsimdev.dfs_dir, port_index)
441417ec264SJakub Kicinski        self.dfs_refresh()
442e05b2d14SJiri Pirko        _, [self.dev] = ip("link show dev %s" % ifname)
443417ec264SJakub Kicinski
444417ec264SJakub Kicinski    def __getitem__(self, key):
445417ec264SJakub Kicinski        return self.dev[key]
446417ec264SJakub Kicinski
447417ec264SJakub Kicinski    def remove(self):
448e05b2d14SJiri Pirko        self.nsimdev.remove_nsim(self)
449417ec264SJakub Kicinski
450417ec264SJakub Kicinski    def dfs_refresh(self):
451417ec264SJakub Kicinski        self.dfs = DebugfsDir(self.dfs_dir)
452417ec264SJakub Kicinski        return self.dfs
453417ec264SJakub Kicinski
45499dadb6eSJakub Kicinski    def dfs_read(self, f):
45599dadb6eSJakub Kicinski        path = os.path.join(self.dfs_dir, f)
45699dadb6eSJakub Kicinski        _, data = cmd('cat %s' % (path))
45799dadb6eSJakub Kicinski        return data.strip()
45899dadb6eSJakub Kicinski
459417ec264SJakub Kicinski    def wait_for_flush(self, bound=0, total=0, n_retry=20):
460417ec264SJakub Kicinski        for i in range(n_retry):
461e05b2d14SJiri Pirko            nbound = self.nsimdev.dfs_num_bound_progs()
462417ec264SJakub Kicinski            nprogs = len(bpftool_prog_list())
463417ec264SJakub Kicinski            if nbound == bound and nprogs == total:
464417ec264SJakub Kicinski                return
465417ec264SJakub Kicinski            time.sleep(0.05)
466417ec264SJakub Kicinski        raise Exception("Time out waiting for program counts to stabilize want %d/%d, have %d bound, %d loaded" % (bound, total, nbound, nprogs))
467417ec264SJakub Kicinski
468752d7b45SJakub Kicinski    def set_ns(self, ns):
469752d7b45SJakub Kicinski        name = "1" if ns == "" else ns
470752d7b45SJakub Kicinski        ip("link set dev %s netns %s" % (self.dev["ifname"], name), ns=self.ns)
471752d7b45SJakub Kicinski        self.ns = ns
472752d7b45SJakub Kicinski
473417ec264SJakub Kicinski    def set_mtu(self, mtu, fail=True):
474417ec264SJakub Kicinski        return ip("link set dev %s mtu %d" % (self.dev["ifname"], mtu),
475417ec264SJakub Kicinski                  fail=fail)
476417ec264SJakub Kicinski
4779045bdc8SQuentin Monnet    def set_xdp(self, bpf, mode, force=False, JSON=True, verbose=False,
478caf95228SQuentin Monnet                fail=True, include_stderr=False):
4799045bdc8SQuentin Monnet        if verbose:
4809045bdc8SQuentin Monnet            bpf += " verbose"
481417ec264SJakub Kicinski        return ip("link set dev %s xdp%s %s" % (self.dev["ifname"], mode, bpf),
482caf95228SQuentin Monnet                  force=force, JSON=JSON,
483caf95228SQuentin Monnet                  fail=fail, include_stderr=include_stderr)
484417ec264SJakub Kicinski
485caf95228SQuentin Monnet    def unset_xdp(self, mode, force=False, JSON=True,
486caf95228SQuentin Monnet                  fail=True, include_stderr=False):
487417ec264SJakub Kicinski        return ip("link set dev %s xdp%s off" % (self.dev["ifname"], mode),
488caf95228SQuentin Monnet                  force=force, JSON=JSON,
489caf95228SQuentin Monnet                  fail=fail, include_stderr=include_stderr)
490417ec264SJakub Kicinski
491417ec264SJakub Kicinski    def ip_link_show(self, xdp):
492417ec264SJakub Kicinski        _, link = ip("link show dev %s" % (self['ifname']))
493417ec264SJakub Kicinski        if len(link) > 1:
494417ec264SJakub Kicinski            raise Exception("Multiple objects on ip link show")
495417ec264SJakub Kicinski        if len(link) < 1:
496417ec264SJakub Kicinski            return {}
497417ec264SJakub Kicinski        fail(xdp != "xdp" in link,
498417ec264SJakub Kicinski             "XDP program not reporting in iplink (reported %s, expected %s)" %
499417ec264SJakub Kicinski             ("xdp" in link, xdp))
500417ec264SJakub Kicinski        return link[0]
501417ec264SJakub Kicinski
502417ec264SJakub Kicinski    def tc_add_ingress(self):
503417ec264SJakub Kicinski        tc("qdisc add dev %s ingress" % (self['ifname']))
504417ec264SJakub Kicinski
505417ec264SJakub Kicinski    def tc_del_ingress(self):
506417ec264SJakub Kicinski        tc("qdisc del dev %s ingress" % (self['ifname']))
507417ec264SJakub Kicinski
508417ec264SJakub Kicinski    def tc_flush_filters(self, bound=0, total=0):
509417ec264SJakub Kicinski        self.tc_del_ingress()
510417ec264SJakub Kicinski        self.tc_add_ingress()
511417ec264SJakub Kicinski        self.wait_for_flush(bound=bound, total=total)
512417ec264SJakub Kicinski
513417ec264SJakub Kicinski    def tc_show_ingress(self, expected=None):
514417ec264SJakub Kicinski        # No JSON support, oh well...
515417ec264SJakub Kicinski        flags = ["skip_sw", "skip_hw", "in_hw"]
516417ec264SJakub Kicinski        named = ["protocol", "pref", "chain", "handle", "id", "tag"]
517417ec264SJakub Kicinski
518417ec264SJakub Kicinski        args = "-s filter show dev %s ingress" % (self['ifname'])
519417ec264SJakub Kicinski        _, out = tc(args, JSON=False)
520417ec264SJakub Kicinski
521417ec264SJakub Kicinski        filters = []
522417ec264SJakub Kicinski        lines = out.split('\n')
523417ec264SJakub Kicinski        for line in lines:
524417ec264SJakub Kicinski            words = line.split()
525417ec264SJakub Kicinski            if "handle" not in words:
526417ec264SJakub Kicinski                continue
527417ec264SJakub Kicinski            fltr = {}
528417ec264SJakub Kicinski            for flag in flags:
529417ec264SJakub Kicinski                fltr[flag] = flag in words
530417ec264SJakub Kicinski            for name in named:
531417ec264SJakub Kicinski                try:
532417ec264SJakub Kicinski                    idx = words.index(name)
533417ec264SJakub Kicinski                    fltr[name] = words[idx + 1]
534417ec264SJakub Kicinski                except ValueError:
535417ec264SJakub Kicinski                    pass
536417ec264SJakub Kicinski            filters.append(fltr)
537417ec264SJakub Kicinski
538417ec264SJakub Kicinski        if expected is not None:
539417ec264SJakub Kicinski            fail(len(filters) != expected,
540417ec264SJakub Kicinski                 "%d ingress filters loaded, expected %d" %
541417ec264SJakub Kicinski                 (len(filters), expected))
542417ec264SJakub Kicinski        return filters
543417ec264SJakub Kicinski
5446d2d58f1SJakub Kicinski    def cls_filter_op(self, op, qdisc="ingress", prio=None, handle=None,
545baf6a07eSJakub Kicinski                      chain=None, cls="", params="",
5466d2d58f1SJakub Kicinski                      fail=True, include_stderr=False):
5476d2d58f1SJakub Kicinski        spec = ""
5486d2d58f1SJakub Kicinski        if prio is not None:
5496d2d58f1SJakub Kicinski            spec += " prio %d" % (prio)
5506d2d58f1SJakub Kicinski        if handle:
5516d2d58f1SJakub Kicinski            spec += " handle %s" % (handle)
552baf6a07eSJakub Kicinski        if chain is not None:
553baf6a07eSJakub Kicinski            spec += " chain %d" % (chain)
5546d2d58f1SJakub Kicinski
5556d2d58f1SJakub Kicinski        return tc("filter {op} dev {dev} {qdisc} {spec} {cls} {params}"\
5566d2d58f1SJakub Kicinski                  .format(op=op, dev=self['ifname'], qdisc=qdisc, spec=spec,
5576d2d58f1SJakub Kicinski                          cls=cls, params=params),
5586d2d58f1SJakub Kicinski                  fail=fail, include_stderr=include_stderr)
5596d2d58f1SJakub Kicinski
5606d2d58f1SJakub Kicinski    def cls_bpf_add_filter(self, bpf, op="add", prio=None, handle=None,
561baf6a07eSJakub Kicinski                           chain=None, da=False, verbose=False,
5626d2d58f1SJakub Kicinski                           skip_sw=False, skip_hw=False,
5636d2d58f1SJakub Kicinski                           fail=True, include_stderr=False):
5646d2d58f1SJakub Kicinski        cls = "bpf " + bpf
5656d2d58f1SJakub Kicinski
566417ec264SJakub Kicinski        params = ""
567417ec264SJakub Kicinski        if da:
568417ec264SJakub Kicinski            params += " da"
5699045bdc8SQuentin Monnet        if verbose:
5709045bdc8SQuentin Monnet            params += " verbose"
571417ec264SJakub Kicinski        if skip_sw:
572417ec264SJakub Kicinski            params += " skip_sw"
573417ec264SJakub Kicinski        if skip_hw:
574417ec264SJakub Kicinski            params += " skip_hw"
5756d2d58f1SJakub Kicinski
5766d2d58f1SJakub Kicinski        return self.cls_filter_op(op=op, prio=prio, handle=handle, cls=cls,
577baf6a07eSJakub Kicinski                                  chain=chain, params=params,
578caf95228SQuentin Monnet                                  fail=fail, include_stderr=include_stderr)
579417ec264SJakub Kicinski
580417ec264SJakub Kicinski    def set_ethtool_tc_offloads(self, enable, fail=True):
581417ec264SJakub Kicinski        args = "hw-tc-offload %s" % ("on" if enable else "off")
582417ec264SJakub Kicinski        return ethtool(self, "-K", args, fail=fail)
583417ec264SJakub Kicinski
584417ec264SJakub Kicinski################################################################################
585417ec264SJakub Kicinskidef clean_up():
5867fedbb7cSJakub Kicinski    global files, netns, devs
5877fedbb7cSJakub Kicinski
588417ec264SJakub Kicinski    for dev in devs:
589417ec264SJakub Kicinski        dev.remove()
590417ec264SJakub Kicinski    for f in files:
591417ec264SJakub Kicinski        cmd("rm -f %s" % (f))
592752d7b45SJakub Kicinski    for ns in netns:
593752d7b45SJakub Kicinski        cmd("ip netns delete %s" % (ns))
5947fedbb7cSJakub Kicinski    files = []
5957fedbb7cSJakub Kicinski    netns = []
596417ec264SJakub Kicinski
597417ec264SJakub Kicinskidef pin_prog(file_name, idx=0):
598417ec264SJakub Kicinski    progs = bpftool_prog_list(expected=(idx + 1))
599417ec264SJakub Kicinski    prog = progs[idx]
600417ec264SJakub Kicinski    bpftool("prog pin id %d %s" % (prog["id"], file_name))
601417ec264SJakub Kicinski    files.append(file_name)
602417ec264SJakub Kicinski
603417ec264SJakub Kicinski    return file_name, bpf_pinned(file_name)
604417ec264SJakub Kicinski
6057fedbb7cSJakub Kicinskidef pin_map(file_name, idx=0, expected=1):
6067fedbb7cSJakub Kicinski    maps = bpftool_map_list(expected=expected)
6077fedbb7cSJakub Kicinski    m = maps[idx]
6087fedbb7cSJakub Kicinski    bpftool("map pin id %d %s" % (m["id"], file_name))
6097fedbb7cSJakub Kicinski    files.append(file_name)
6107fedbb7cSJakub Kicinski
6117fedbb7cSJakub Kicinski    return file_name, bpf_pinned(file_name)
6127fedbb7cSJakub Kicinski
6137fedbb7cSJakub Kicinskidef check_dev_info_removed(prog_file=None, map_file=None):
614752d7b45SJakub Kicinski    bpftool_prog_list(expected=0)
6157fedbb7cSJakub Kicinski    ret, err = bpftool("prog show pin %s" % (prog_file), fail=False)
616752d7b45SJakub Kicinski    fail(ret == 0, "Showing prog with removed device did not fail")
617752d7b45SJakub Kicinski    fail(err["error"].find("No such device") == -1,
618752d7b45SJakub Kicinski         "Showing prog with removed device expected ENODEV, error is %s" %
619752d7b45SJakub Kicinski         (err["error"]))
6207fedbb7cSJakub Kicinski
6217fedbb7cSJakub Kicinski    bpftool_map_list(expected=0)
6227fedbb7cSJakub Kicinski    ret, err = bpftool("map show pin %s" % (map_file), fail=False)
6237fedbb7cSJakub Kicinski    fail(ret == 0, "Showing map with removed device did not fail")
6247fedbb7cSJakub Kicinski    fail(err["error"].find("No such device") == -1,
6257fedbb7cSJakub Kicinski         "Showing map with removed device expected ENODEV, error is %s" %
6267fedbb7cSJakub Kicinski         (err["error"]))
6277fedbb7cSJakub Kicinski
6287fedbb7cSJakub Kicinskidef check_dev_info(other_ns, ns, prog_file=None, map_file=None, removed=False):
6297fedbb7cSJakub Kicinski    progs = bpftool_prog_list(expected=1, ns=ns)
630752d7b45SJakub Kicinski    prog = progs[0]
631752d7b45SJakub Kicinski
632752d7b45SJakub Kicinski    fail("dev" not in prog.keys(), "Device parameters not reported")
633752d7b45SJakub Kicinski    dev = prog["dev"]
634752d7b45SJakub Kicinski    fail("ifindex" not in dev.keys(), "Device parameters not reported")
635752d7b45SJakub Kicinski    fail("ns_dev" not in dev.keys(), "Device parameters not reported")
636752d7b45SJakub Kicinski    fail("ns_inode" not in dev.keys(), "Device parameters not reported")
637752d7b45SJakub Kicinski
6387fedbb7cSJakub Kicinski    if not other_ns:
639752d7b45SJakub Kicinski        fail("ifname" not in dev.keys(), "Ifname not reported")
640752d7b45SJakub Kicinski        fail(dev["ifname"] != sim["ifname"],
641752d7b45SJakub Kicinski             "Ifname incorrect %s vs %s" % (dev["ifname"], sim["ifname"]))
642752d7b45SJakub Kicinski    else:
643752d7b45SJakub Kicinski        fail("ifname" in dev.keys(), "Ifname is reported for other ns")
6447fedbb7cSJakub Kicinski
6457fedbb7cSJakub Kicinski    maps = bpftool_map_list(expected=2, ns=ns)
6467fedbb7cSJakub Kicinski    for m in maps:
6477fedbb7cSJakub Kicinski        fail("dev" not in m.keys(), "Device parameters not reported")
6487fedbb7cSJakub Kicinski        fail(dev != m["dev"], "Map's device different than program's")
649752d7b45SJakub Kicinski
650caf95228SQuentin Monnetdef check_extack(output, reference, args):
651caf95228SQuentin Monnet    if skip_extack:
652caf95228SQuentin Monnet        return
653caf95228SQuentin Monnet    lines = output.split("\n")
654219f860dSJakub Kicinski    comp = len(lines) >= 2 and lines[1] == 'Error: ' + reference
655caf95228SQuentin Monnet    fail(not comp, "Missing or incorrect netlink extack message")
656caf95228SQuentin Monnet
657caf95228SQuentin Monnetdef check_extack_nsim(output, reference, args):
658219f860dSJakub Kicinski    check_extack(output, "netdevsim: " + reference, args)
659caf95228SQuentin Monnet
6602fb89a38SJakub Kicinskidef check_no_extack(res, needle):
6612fb89a38SJakub Kicinski    fail((res[1] + res[2]).count(needle) or (res[1] + res[2]).count("Warning:"),
6622fb89a38SJakub Kicinski         "Found '%s' in command output, leaky extack?" % (needle))
6632fb89a38SJakub Kicinski
6649045bdc8SQuentin Monnetdef check_verifier_log(output, reference):
6659045bdc8SQuentin Monnet    lines = output.split("\n")
6669045bdc8SQuentin Monnet    for l in reversed(lines):
6679045bdc8SQuentin Monnet        if l == reference:
6689045bdc8SQuentin Monnet            return
6699045bdc8SQuentin Monnet    fail(True, "Missing or incorrect message from netdevsim in verifier log")
6709045bdc8SQuentin Monnet
67142a40e84SJakub Kicinskidef check_multi_basic(two_xdps):
67242a40e84SJakub Kicinski    fail(two_xdps["mode"] != 4, "Bad mode reported with multiple programs")
67342a40e84SJakub Kicinski    fail("prog" in two_xdps, "Base program reported in multi program mode")
67442a40e84SJakub Kicinski    fail(len(two_xdps["attached"]) != 2,
67542a40e84SJakub Kicinski         "Wrong attached program count with two programs")
67642a40e84SJakub Kicinski    fail(two_xdps["attached"][0]["prog"]["id"] ==
67742a40e84SJakub Kicinski         two_xdps["attached"][1]["prog"]["id"],
67842a40e84SJakub Kicinski         "Offloaded and other programs have the same id")
67942a40e84SJakub Kicinski
6802fb89a38SJakub Kicinskidef test_spurios_extack(sim, obj, skip_hw, needle):
6812fb89a38SJakub Kicinski    res = sim.cls_bpf_add_filter(obj, prio=1, handle=1, skip_hw=skip_hw,
6822fb89a38SJakub Kicinski                                 include_stderr=True)
6832fb89a38SJakub Kicinski    check_no_extack(res, needle)
6842fb89a38SJakub Kicinski    res = sim.cls_bpf_add_filter(obj, op="replace", prio=1, handle=1,
6852fb89a38SJakub Kicinski                                 skip_hw=skip_hw, include_stderr=True)
6862fb89a38SJakub Kicinski    check_no_extack(res, needle)
6872fb89a38SJakub Kicinski    res = sim.cls_filter_op(op="delete", prio=1, handle=1, cls="bpf",
6882fb89a38SJakub Kicinski                            include_stderr=True)
6892fb89a38SJakub Kicinski    check_no_extack(res, needle)
6902fb89a38SJakub Kicinski
691e05b2d14SJiri Pirkodef test_multi_prog(simdev, sim, obj, modename, modeid):
69206ea9e63SJakub Kicinski    start_test("Test multi-attachment XDP - %s + offload..." %
69306ea9e63SJakub Kicinski               (modename or "default", ))
69406ea9e63SJakub Kicinski    sim.set_xdp(obj, "offload")
69506ea9e63SJakub Kicinski    xdp = sim.ip_link_show(xdp=True)["xdp"]
69606ea9e63SJakub Kicinski    offloaded = sim.dfs_read("bpf_offloaded_id")
69706ea9e63SJakub Kicinski    fail("prog" not in xdp, "Base program not reported in single program mode")
69806ea9e63SJakub Kicinski    fail(len(xdp["attached"]) != 1,
69906ea9e63SJakub Kicinski         "Wrong attached program count with one program")
70006ea9e63SJakub Kicinski
70106ea9e63SJakub Kicinski    sim.set_xdp(obj, modename)
70206ea9e63SJakub Kicinski    two_xdps = sim.ip_link_show(xdp=True)["xdp"]
70306ea9e63SJakub Kicinski
70406ea9e63SJakub Kicinski    fail(xdp["attached"][0] not in two_xdps["attached"],
70506ea9e63SJakub Kicinski         "Offload program not reported after other activated")
70642a40e84SJakub Kicinski    check_multi_basic(two_xdps)
70742a40e84SJakub Kicinski
70842a40e84SJakub Kicinski    offloaded2 = sim.dfs_read("bpf_offloaded_id")
70906ea9e63SJakub Kicinski    fail(offloaded != offloaded2,
71006ea9e63SJakub Kicinski         "Offload ID changed after loading other program")
71106ea9e63SJakub Kicinski
71206ea9e63SJakub Kicinski    start_test("Test multi-attachment XDP - replace...")
71306ea9e63SJakub Kicinski    ret, _, err = sim.set_xdp(obj, "offload", fail=False, include_stderr=True)
71406ea9e63SJakub Kicinski    fail(ret == 0, "Replaced one of programs without -force")
71506ea9e63SJakub Kicinski    check_extack(err, "XDP program already attached.", args)
71606ea9e63SJakub Kicinski
7170b5b6e74SToke Høiland-Jørgensen    start_test("Test multi-attachment XDP - remove without mode...")
7180b5b6e74SToke Høiland-Jørgensen    ret, _, err = sim.unset_xdp("", force=True,
71906ea9e63SJakub Kicinski                                fail=False, include_stderr=True)
7200b5b6e74SToke Høiland-Jørgensen    fail(ret == 0, "Removed program without a mode flag")
7210b5b6e74SToke Høiland-Jørgensen    check_extack(err, "More than one program loaded, unset mode is ambiguous.", args)
72206ea9e63SJakub Kicinski
72306ea9e63SJakub Kicinski    sim.unset_xdp("offload")
72406ea9e63SJakub Kicinski    xdp = sim.ip_link_show(xdp=True)["xdp"]
72506ea9e63SJakub Kicinski    offloaded = sim.dfs_read("bpf_offloaded_id")
72606ea9e63SJakub Kicinski
72706ea9e63SJakub Kicinski    fail(xdp["mode"] != modeid, "Bad mode reported after multiple programs")
72806ea9e63SJakub Kicinski    fail("prog" not in xdp,
72906ea9e63SJakub Kicinski         "Base program not reported after multi program mode")
73006ea9e63SJakub Kicinski    fail(xdp["attached"][0] not in two_xdps["attached"],
73106ea9e63SJakub Kicinski         "Offload program not reported after other activated")
73206ea9e63SJakub Kicinski    fail(len(xdp["attached"]) != 1,
73306ea9e63SJakub Kicinski         "Wrong attached program count with remaining programs")
73406ea9e63SJakub Kicinski    fail(offloaded != "0", "Offload ID reported with only other program left")
73506ea9e63SJakub Kicinski
73642a40e84SJakub Kicinski    start_test("Test multi-attachment XDP - reattach...")
73706ea9e63SJakub Kicinski    sim.set_xdp(obj, "offload")
73842a40e84SJakub Kicinski    two_xdps = sim.ip_link_show(xdp=True)["xdp"]
73942a40e84SJakub Kicinski
74042a40e84SJakub Kicinski    fail(xdp["attached"][0] not in two_xdps["attached"],
74142a40e84SJakub Kicinski         "Other program not reported after offload activated")
74242a40e84SJakub Kicinski    check_multi_basic(two_xdps)
74342a40e84SJakub Kicinski
74442a40e84SJakub Kicinski    start_test("Test multi-attachment XDP - device remove...")
745e05b2d14SJiri Pirko    simdev.remove()
74606ea9e63SJakub Kicinski
747e05b2d14SJiri Pirko    simdev = NetdevSimDev()
748e05b2d14SJiri Pirko    sim, = simdev.nsims
74906ea9e63SJakub Kicinski    sim.set_ethtool_tc_offloads(True)
750e05b2d14SJiri Pirko    return [simdev, sim]
7512fb89a38SJakub Kicinski
752417ec264SJakub Kicinski# Parse command line
753417ec264SJakub Kicinskiparser = argparse.ArgumentParser()
754417ec264SJakub Kicinskiparser.add_argument("--log", help="output verbose log to given file")
755417ec264SJakub Kicinskiargs = parser.parse_args()
756417ec264SJakub Kicinskiif args.log:
757417ec264SJakub Kicinski    logfile = open(args.log, 'w+')
758417ec264SJakub Kicinski    logfile.write("# -*-Org-*-")
759417ec264SJakub Kicinski
760417ec264SJakub Kicinskilog("Prepare...", "", level=1)
761417ec264SJakub Kicinskilog_level_inc()
762417ec264SJakub Kicinski
763417ec264SJakub Kicinski# Check permissions
764417ec264SJakub Kicinskiskip(os.getuid() != 0, "test must be run as root")
765417ec264SJakub Kicinski
766417ec264SJakub Kicinski# Check tools
767417ec264SJakub Kicinskiret, progs = bpftool("prog", fail=False)
768417ec264SJakub Kicinskiskip(ret != 0, "bpftool not installed")
76947cf52a2SJakub Kicinskibase_progs = progs
77047cf52a2SJakub Kicinski_, base_maps = bpftool("map")
7718158cad1SToke Høiland-Jørgensenbase_map_names = [
772e60db051SStanislav Fomichev    'pid_iter.rodata', # created on each bpftool invocation
773e60db051SStanislav Fomichev    'libbpf_det_bind', # created on each bpftool invocation
7748158cad1SToke Høiland-Jørgensen]
775417ec264SJakub Kicinski
776417ec264SJakub Kicinski# Check netdevsim
777e60db051SStanislav Fomichevif not os.path.isdir("/sys/bus/netdevsim/"):
778417ec264SJakub Kicinski    ret, out = cmd("modprobe netdevsim", fail=False)
779417ec264SJakub Kicinski    skip(ret != 0, "netdevsim module could not be loaded")
780417ec264SJakub Kicinski
781417ec264SJakub Kicinski# Check debugfs
782417ec264SJakub Kicinski_, out = cmd("mount")
783417ec264SJakub Kicinskiif out.find("/sys/kernel/debug type debugfs") == -1:
784417ec264SJakub Kicinski    cmd("mount -t debugfs none /sys/kernel/debug")
785417ec264SJakub Kicinski
786417ec264SJakub Kicinski# Check samples are compiled
787afef88e6SDaniel Müllersamples = ["sample_ret0.bpf.o", "sample_map_ret0.bpf.o"]
788417ec264SJakub Kicinskifor s in samples:
789417ec264SJakub Kicinski    ret, out = cmd("ls %s/%s" % (bpf_test_dir, s), fail=False)
790417ec264SJakub Kicinski    skip(ret != 0, "sample %s/%s not found, please compile it" %
791417ec264SJakub Kicinski         (bpf_test_dir, s))
792417ec264SJakub Kicinski
793caf95228SQuentin Monnet# Check if iproute2 is built with libmnl (needed by extack support)
794caf95228SQuentin Monnet_, _, err = cmd("tc qdisc delete dev lo handle 0",
795caf95228SQuentin Monnet                fail=False, include_stderr=True)
796caf95228SQuentin Monnetif err.find("Error: Failed to find qdisc with specified handle.") == -1:
797caf95228SQuentin Monnet    print("Warning: no extack message in iproute2 output, libmnl missing?")
798caf95228SQuentin Monnet    log("Warning: no extack message in iproute2 output, libmnl missing?", "")
799caf95228SQuentin Monnet    skip_extack = True
800caf95228SQuentin Monnet
801752d7b45SJakub Kicinski# Check if net namespaces seem to work
802752d7b45SJakub Kicinskins = mknetns()
803752d7b45SJakub Kicinskiskip(ns is None, "Could not create a net namespace")
804752d7b45SJakub Kicinskicmd("ip netns delete %s" % (ns))
805752d7b45SJakub Kicinskinetns = []
806752d7b45SJakub Kicinski
807417ec264SJakub Kicinskitry:
808afef88e6SDaniel Müller    obj = bpf_obj("sample_ret0.bpf.o")
809417ec264SJakub Kicinski    bytecode = bpf_bytecode("1,6 0 0 4294967295,")
810417ec264SJakub Kicinski
811417ec264SJakub Kicinski    start_test("Test destruction of generic XDP...")
812e05b2d14SJiri Pirko    simdev = NetdevSimDev()
813e05b2d14SJiri Pirko    sim, = simdev.nsims
814417ec264SJakub Kicinski    sim.set_xdp(obj, "generic")
815e05b2d14SJiri Pirko    simdev.remove()
816417ec264SJakub Kicinski    bpftool_prog_list_wait(expected=0)
817417ec264SJakub Kicinski
818e05b2d14SJiri Pirko    simdev = NetdevSimDev()
819e05b2d14SJiri Pirko    sim, = simdev.nsims
820417ec264SJakub Kicinski    sim.tc_add_ingress()
821417ec264SJakub Kicinski
822417ec264SJakub Kicinski    start_test("Test TC non-offloaded...")
823417ec264SJakub Kicinski    ret, _ = sim.cls_bpf_add_filter(obj, skip_hw=True, fail=False)
824417ec264SJakub Kicinski    fail(ret != 0, "Software TC filter did not load")
825417ec264SJakub Kicinski
826417ec264SJakub Kicinski    start_test("Test TC non-offloaded isn't getting bound...")
827417ec264SJakub Kicinski    ret, _ = sim.cls_bpf_add_filter(obj, fail=False)
828417ec264SJakub Kicinski    fail(ret != 0, "Software TC filter did not load")
829e05b2d14SJiri Pirko    simdev.dfs_get_bound_progs(expected=0)
830417ec264SJakub Kicinski
831417ec264SJakub Kicinski    sim.tc_flush_filters()
832417ec264SJakub Kicinski
833417ec264SJakub Kicinski    start_test("Test TC offloads are off by default...")
834caf95228SQuentin Monnet    ret, _, err = sim.cls_bpf_add_filter(obj, skip_sw=True,
835caf95228SQuentin Monnet                                         fail=False, include_stderr=True)
836417ec264SJakub Kicinski    fail(ret == 0, "TC filter loaded without enabling TC offloads")
837219f860dSJakub Kicinski    check_extack(err, "TC offload is disabled on net device.", args)
838417ec264SJakub Kicinski    sim.wait_for_flush()
839417ec264SJakub Kicinski
840417ec264SJakub Kicinski    sim.set_ethtool_tc_offloads(True)
841417ec264SJakub Kicinski    sim.dfs["bpf_tc_non_bound_accept"] = "Y"
842417ec264SJakub Kicinski
843417ec264SJakub Kicinski    start_test("Test TC offload by default...")
844417ec264SJakub Kicinski    ret, _ = sim.cls_bpf_add_filter(obj, fail=False)
845417ec264SJakub Kicinski    fail(ret != 0, "Software TC filter did not load")
846e05b2d14SJiri Pirko    simdev.dfs_get_bound_progs(expected=0)
847417ec264SJakub Kicinski    ingress = sim.tc_show_ingress(expected=1)
848417ec264SJakub Kicinski    fltr = ingress[0]
849417ec264SJakub Kicinski    fail(not fltr["in_hw"], "Filter not offloaded by default")
850417ec264SJakub Kicinski
851417ec264SJakub Kicinski    sim.tc_flush_filters()
852417ec264SJakub Kicinski
853417ec264SJakub Kicinski    start_test("Test TC cBPF bytcode tries offload by default...")
854417ec264SJakub Kicinski    ret, _ = sim.cls_bpf_add_filter(bytecode, fail=False)
855417ec264SJakub Kicinski    fail(ret != 0, "Software TC filter did not load")
856e05b2d14SJiri Pirko    simdev.dfs_get_bound_progs(expected=0)
857417ec264SJakub Kicinski    ingress = sim.tc_show_ingress(expected=1)
858417ec264SJakub Kicinski    fltr = ingress[0]
859417ec264SJakub Kicinski    fail(not fltr["in_hw"], "Bytecode not offloaded by default")
860417ec264SJakub Kicinski
861417ec264SJakub Kicinski    sim.tc_flush_filters()
862417ec264SJakub Kicinski    sim.dfs["bpf_tc_non_bound_accept"] = "N"
863417ec264SJakub Kicinski
864417ec264SJakub Kicinski    start_test("Test TC cBPF unbound bytecode doesn't offload...")
865caf95228SQuentin Monnet    ret, _, err = sim.cls_bpf_add_filter(bytecode, skip_sw=True,
866caf95228SQuentin Monnet                                         fail=False, include_stderr=True)
867417ec264SJakub Kicinski    fail(ret == 0, "TC bytecode loaded for offload")
868caf95228SQuentin Monnet    check_extack_nsim(err, "netdevsim configured to reject unbound programs.",
869caf95228SQuentin Monnet                      args)
870417ec264SJakub Kicinski    sim.wait_for_flush()
871417ec264SJakub Kicinski
872baf6a07eSJakub Kicinski    start_test("Test non-0 chain offload...")
873baf6a07eSJakub Kicinski    ret, _, err = sim.cls_bpf_add_filter(obj, chain=1, prio=1, handle=1,
874baf6a07eSJakub Kicinski                                         skip_sw=True,
875baf6a07eSJakub Kicinski                                         fail=False, include_stderr=True)
876baf6a07eSJakub Kicinski    fail(ret == 0, "Offloaded a filter to chain other than 0")
877219f860dSJakub Kicinski    check_extack(err, "Driver supports only offload of chain 0.", args)
878baf6a07eSJakub Kicinski    sim.tc_flush_filters()
879baf6a07eSJakub Kicinski
8806d2d58f1SJakub Kicinski    start_test("Test TC replace...")
8816d2d58f1SJakub Kicinski    sim.cls_bpf_add_filter(obj, prio=1, handle=1)
8826d2d58f1SJakub Kicinski    sim.cls_bpf_add_filter(obj, op="replace", prio=1, handle=1)
8836d2d58f1SJakub Kicinski    sim.cls_filter_op(op="delete", prio=1, handle=1, cls="bpf")
8846d2d58f1SJakub Kicinski
8856d2d58f1SJakub Kicinski    sim.cls_bpf_add_filter(obj, prio=1, handle=1, skip_sw=True)
8866d2d58f1SJakub Kicinski    sim.cls_bpf_add_filter(obj, op="replace", prio=1, handle=1, skip_sw=True)
8876d2d58f1SJakub Kicinski    sim.cls_filter_op(op="delete", prio=1, handle=1, cls="bpf")
8886d2d58f1SJakub Kicinski
8896d2d58f1SJakub Kicinski    sim.cls_bpf_add_filter(obj, prio=1, handle=1, skip_hw=True)
8906d2d58f1SJakub Kicinski    sim.cls_bpf_add_filter(obj, op="replace", prio=1, handle=1, skip_hw=True)
8916d2d58f1SJakub Kicinski    sim.cls_filter_op(op="delete", prio=1, handle=1, cls="bpf")
8926d2d58f1SJakub Kicinski
8936d2d58f1SJakub Kicinski    start_test("Test TC replace bad flags...")
8946d2d58f1SJakub Kicinski    for i in range(3):
8956d2d58f1SJakub Kicinski        for j in range(3):
8966d2d58f1SJakub Kicinski            ret, _ = sim.cls_bpf_add_filter(obj, op="replace", prio=1, handle=1,
8976d2d58f1SJakub Kicinski                                            skip_sw=(j == 1), skip_hw=(j == 2),
8986d2d58f1SJakub Kicinski                                            fail=False)
8996d2d58f1SJakub Kicinski            fail(bool(ret) != bool(j),
9006d2d58f1SJakub Kicinski                 "Software TC incorrect load in replace test, iteration %d" %
9016d2d58f1SJakub Kicinski                 (j))
9026d2d58f1SJakub Kicinski        sim.cls_filter_op(op="delete", prio=1, handle=1, cls="bpf")
9036d2d58f1SJakub Kicinski
9042fb89a38SJakub Kicinski    start_test("Test spurious extack from the driver...")
9052fb89a38SJakub Kicinski    test_spurios_extack(sim, obj, False, "netdevsim")
9062fb89a38SJakub Kicinski    test_spurios_extack(sim, obj, True, "netdevsim")
9072fb89a38SJakub Kicinski
9082fb89a38SJakub Kicinski    sim.set_ethtool_tc_offloads(False)
9092fb89a38SJakub Kicinski
9102fb89a38SJakub Kicinski    test_spurios_extack(sim, obj, False, "TC offload is disabled")
9112fb89a38SJakub Kicinski    test_spurios_extack(sim, obj, True, "TC offload is disabled")
9122fb89a38SJakub Kicinski
9132fb89a38SJakub Kicinski    sim.set_ethtool_tc_offloads(True)
9142fb89a38SJakub Kicinski
9156d2d58f1SJakub Kicinski    sim.tc_flush_filters()
9166d2d58f1SJakub Kicinski
917d8b5e76aSToke Høiland-Jørgensen    start_test("Test TC offloads failure...")
918d8b5e76aSToke Høiland-Jørgensen    sim.dfs["dev/bpf_bind_verifier_accept"] = 0
919d8b5e76aSToke Høiland-Jørgensen    ret, _, err = sim.cls_bpf_add_filter(obj, verbose=True, skip_sw=True,
920d8b5e76aSToke Høiland-Jørgensen                                         fail=False, include_stderr=True)
921d8b5e76aSToke Høiland-Jørgensen    fail(ret == 0, "TC filter did not reject with TC offloads enabled")
922d8b5e76aSToke Høiland-Jørgensen    check_verifier_log(err, "[netdevsim] Hello from netdevsim!")
923d8b5e76aSToke Høiland-Jørgensen    sim.dfs["dev/bpf_bind_verifier_accept"] = 1
924d8b5e76aSToke Høiland-Jørgensen
925417ec264SJakub Kicinski    start_test("Test TC offloads work...")
9269045bdc8SQuentin Monnet    ret, _, err = sim.cls_bpf_add_filter(obj, verbose=True, skip_sw=True,
9279045bdc8SQuentin Monnet                                         fail=False, include_stderr=True)
928417ec264SJakub Kicinski    fail(ret != 0, "TC filter did not load with TC offloads enabled")
929417ec264SJakub Kicinski
930417ec264SJakub Kicinski    start_test("Test TC offload basics...")
931e05b2d14SJiri Pirko    dfs = simdev.dfs_get_bound_progs(expected=1)
932417ec264SJakub Kicinski    progs = bpftool_prog_list(expected=1)
933417ec264SJakub Kicinski    ingress = sim.tc_show_ingress(expected=1)
934417ec264SJakub Kicinski
935417ec264SJakub Kicinski    dprog = dfs[0]
936417ec264SJakub Kicinski    prog = progs[0]
937417ec264SJakub Kicinski    fltr = ingress[0]
938417ec264SJakub Kicinski    fail(fltr["skip_hw"], "TC does reports 'skip_hw' on offloaded filter")
939417ec264SJakub Kicinski    fail(not fltr["in_hw"], "TC does not report 'in_hw' for offloaded filter")
940417ec264SJakub Kicinski    fail(not fltr["skip_sw"], "TC does not report 'skip_sw' back")
941417ec264SJakub Kicinski
942417ec264SJakub Kicinski    start_test("Test TC offload is device-bound...")
943417ec264SJakub Kicinski    fail(str(prog["id"]) != fltr["id"], "Program IDs don't match")
944417ec264SJakub Kicinski    fail(prog["tag"] != fltr["tag"], "Program tags don't match")
945417ec264SJakub Kicinski    fail(fltr["id"] != dprog["id"], "Program IDs don't match")
946417ec264SJakub Kicinski    fail(dprog["state"] != "xlated", "Offloaded program state not translated")
947417ec264SJakub Kicinski    fail(dprog["loaded"] != "Y", "Offloaded program is not loaded")
948417ec264SJakub Kicinski
949417ec264SJakub Kicinski    start_test("Test disabling TC offloads is rejected while filters installed...")
950417ec264SJakub Kicinski    ret, _ = sim.set_ethtool_tc_offloads(False, fail=False)
951417ec264SJakub Kicinski    fail(ret == 0, "Driver should refuse to disable TC offloads with filters installed...")
952766e62b7SToke Høiland-Jørgensen    sim.set_ethtool_tc_offloads(True)
953417ec264SJakub Kicinski
954417ec264SJakub Kicinski    start_test("Test qdisc removal frees things...")
955417ec264SJakub Kicinski    sim.tc_flush_filters()
956417ec264SJakub Kicinski    sim.tc_show_ingress(expected=0)
957417ec264SJakub Kicinski
958417ec264SJakub Kicinski    start_test("Test disabling TC offloads is OK without filters...")
959417ec264SJakub Kicinski    ret, _ = sim.set_ethtool_tc_offloads(False, fail=False)
960417ec264SJakub Kicinski    fail(ret != 0,
961417ec264SJakub Kicinski         "Driver refused to disable TC offloads without filters installed...")
962417ec264SJakub Kicinski
963417ec264SJakub Kicinski    sim.set_ethtool_tc_offloads(True)
964417ec264SJakub Kicinski
965417ec264SJakub Kicinski    start_test("Test destroying device gets rid of TC filters...")
966417ec264SJakub Kicinski    sim.cls_bpf_add_filter(obj, skip_sw=True)
967e05b2d14SJiri Pirko    simdev.remove()
968417ec264SJakub Kicinski    bpftool_prog_list_wait(expected=0)
969417ec264SJakub Kicinski
970e05b2d14SJiri Pirko    simdev = NetdevSimDev()
971e05b2d14SJiri Pirko    sim, = simdev.nsims
972417ec264SJakub Kicinski    sim.set_ethtool_tc_offloads(True)
973417ec264SJakub Kicinski
974417ec264SJakub Kicinski    start_test("Test destroying device gets rid of XDP...")
975417ec264SJakub Kicinski    sim.set_xdp(obj, "offload")
976e05b2d14SJiri Pirko    simdev.remove()
977417ec264SJakub Kicinski    bpftool_prog_list_wait(expected=0)
978417ec264SJakub Kicinski
979e05b2d14SJiri Pirko    simdev = NetdevSimDev()
980e05b2d14SJiri Pirko    sim, = simdev.nsims
981417ec264SJakub Kicinski    sim.set_ethtool_tc_offloads(True)
982417ec264SJakub Kicinski
983417ec264SJakub Kicinski    start_test("Test XDP prog reporting...")
984417ec264SJakub Kicinski    sim.set_xdp(obj, "drv")
985417ec264SJakub Kicinski    ipl = sim.ip_link_show(xdp=True)
986417ec264SJakub Kicinski    progs = bpftool_prog_list(expected=1)
987417ec264SJakub Kicinski    fail(ipl["xdp"]["prog"]["id"] != progs[0]["id"],
988417ec264SJakub Kicinski         "Loaded program has wrong ID")
989417ec264SJakub Kicinski
990417ec264SJakub Kicinski    start_test("Test XDP prog replace without force...")
991417ec264SJakub Kicinski    ret, _ = sim.set_xdp(obj, "drv", fail=False)
992417ec264SJakub Kicinski    fail(ret == 0, "Replaced XDP program without -force")
993417ec264SJakub Kicinski    sim.wait_for_flush(total=1)
994417ec264SJakub Kicinski
995417ec264SJakub Kicinski    start_test("Test XDP prog replace with force...")
996417ec264SJakub Kicinski    ret, _ = sim.set_xdp(obj, "drv", force=True, fail=False)
997417ec264SJakub Kicinski    fail(ret != 0, "Could not replace XDP program with -force")
998417ec264SJakub Kicinski    bpftool_prog_list_wait(expected=1)
999417ec264SJakub Kicinski    ipl = sim.ip_link_show(xdp=True)
1000417ec264SJakub Kicinski    progs = bpftool_prog_list(expected=1)
1001417ec264SJakub Kicinski    fail(ipl["xdp"]["prog"]["id"] != progs[0]["id"],
1002417ec264SJakub Kicinski         "Loaded program has wrong ID")
1003752d7b45SJakub Kicinski    fail("dev" in progs[0].keys(),
1004752d7b45SJakub Kicinski         "Device parameters reported for non-offloaded program")
1005417ec264SJakub Kicinski
1006417ec264SJakub Kicinski    start_test("Test XDP prog replace with bad flags...")
100799dadb6eSJakub Kicinski    ret, _, err = sim.set_xdp(obj, "generic", force=True,
100899dadb6eSJakub Kicinski                              fail=False, include_stderr=True)
100999dadb6eSJakub Kicinski    fail(ret == 0, "Replaced XDP program with a program in different mode")
101052158f00SJakub Kicinski    check_extack(err,
1011852c2ee3SToke Høiland-Jørgensen                 "Native and generic XDP can't be active at the same time.",
101252158f00SJakub Kicinski                 args)
1013417ec264SJakub Kicinski
1014417ec264SJakub Kicinski    start_test("Test MTU restrictions...")
1015417ec264SJakub Kicinski    ret, _ = sim.set_mtu(9000, fail=False)
1016417ec264SJakub Kicinski    fail(ret == 0,
1017417ec264SJakub Kicinski         "Driver should refuse to increase MTU to 9000 with XDP loaded...")
1018417ec264SJakub Kicinski    sim.unset_xdp("drv")
1019417ec264SJakub Kicinski    bpftool_prog_list_wait(expected=0)
1020417ec264SJakub Kicinski    sim.set_mtu(9000)
1021caf95228SQuentin Monnet    ret, _, err = sim.set_xdp(obj, "drv", fail=False, include_stderr=True)
1022417ec264SJakub Kicinski    fail(ret == 0, "Driver should refuse to load program with MTU of 9000...")
1023caf95228SQuentin Monnet    check_extack_nsim(err, "MTU too large w/ XDP enabled.", args)
1024417ec264SJakub Kicinski    sim.set_mtu(1500)
1025417ec264SJakub Kicinski
1026417ec264SJakub Kicinski    sim.wait_for_flush()
10277736b6edSJakub Kicinski    start_test("Test non-offload XDP attaching to HW...")
1028afef88e6SDaniel Müller    bpftool_prog_load("sample_ret0.bpf.o", "/sys/fs/bpf/nooffload")
10297736b6edSJakub Kicinski    nooffload = bpf_pinned("/sys/fs/bpf/nooffload")
10307736b6edSJakub Kicinski    ret, _, err = sim.set_xdp(nooffload, "offload",
10317736b6edSJakub Kicinski                              fail=False, include_stderr=True)
10327736b6edSJakub Kicinski    fail(ret == 0, "attached non-offloaded XDP program to HW")
10337736b6edSJakub Kicinski    check_extack_nsim(err, "xdpoffload of non-bound program.", args)
10347736b6edSJakub Kicinski    rm("/sys/fs/bpf/nooffload")
10357736b6edSJakub Kicinski
10367736b6edSJakub Kicinski    start_test("Test offload XDP attaching to drv...")
1037afef88e6SDaniel Müller    bpftool_prog_load("sample_ret0.bpf.o", "/sys/fs/bpf/offload",
10387736b6edSJakub Kicinski                      dev=sim['ifname'])
10397736b6edSJakub Kicinski    offload = bpf_pinned("/sys/fs/bpf/offload")
10407736b6edSJakub Kicinski    ret, _, err = sim.set_xdp(offload, "drv", fail=False, include_stderr=True)
10417736b6edSJakub Kicinski    fail(ret == 0, "attached offloaded XDP program to drv")
1042*40535704SStanislav Fomichev    check_extack(err, "Using offloaded program without HW_MODE flag is not supported.", args)
10437736b6edSJakub Kicinski    rm("/sys/fs/bpf/offload")
10447736b6edSJakub Kicinski    sim.wait_for_flush()
10457736b6edSJakub Kicinski
1046d8b5e76aSToke Høiland-Jørgensen    start_test("Test XDP load failure...")
1047d8b5e76aSToke Høiland-Jørgensen    sim.dfs["dev/bpf_bind_verifier_accept"] = 0
1048afef88e6SDaniel Müller    ret, _, err = bpftool_prog_load("sample_ret0.bpf.o", "/sys/fs/bpf/offload",
1049d8b5e76aSToke Høiland-Jørgensen                                 dev=sim['ifname'], fail=False, include_stderr=True)
1050d8b5e76aSToke Høiland-Jørgensen    fail(ret == 0, "verifier should fail on load")
1051d8b5e76aSToke Høiland-Jørgensen    check_verifier_log(err, "[netdevsim] Hello from netdevsim!")
1052d8b5e76aSToke Høiland-Jørgensen    sim.dfs["dev/bpf_bind_verifier_accept"] = 1
1053d8b5e76aSToke Høiland-Jørgensen    sim.wait_for_flush()
1054d8b5e76aSToke Høiland-Jørgensen
1055417ec264SJakub Kicinski    start_test("Test XDP offload...")
10569045bdc8SQuentin Monnet    _, _, err = sim.set_xdp(obj, "offload", verbose=True, include_stderr=True)
1057417ec264SJakub Kicinski    ipl = sim.ip_link_show(xdp=True)
1058417ec264SJakub Kicinski    link_xdp = ipl["xdp"]["prog"]
1059417ec264SJakub Kicinski    progs = bpftool_prog_list(expected=1)
1060417ec264SJakub Kicinski    prog = progs[0]
1061417ec264SJakub Kicinski    fail(link_xdp["id"] != prog["id"], "Loaded program has wrong ID")
1062417ec264SJakub Kicinski
1063417ec264SJakub Kicinski    start_test("Test XDP offload is device bound...")
1064e05b2d14SJiri Pirko    dfs = simdev.dfs_get_bound_progs(expected=1)
1065417ec264SJakub Kicinski    dprog = dfs[0]
1066417ec264SJakub Kicinski
1067417ec264SJakub Kicinski    fail(prog["id"] != link_xdp["id"], "Program IDs don't match")
1068417ec264SJakub Kicinski    fail(prog["tag"] != link_xdp["tag"], "Program tags don't match")
1069417ec264SJakub Kicinski    fail(str(link_xdp["id"]) != dprog["id"], "Program IDs don't match")
1070417ec264SJakub Kicinski    fail(dprog["state"] != "xlated", "Offloaded program state not translated")
1071417ec264SJakub Kicinski    fail(dprog["loaded"] != "Y", "Offloaded program is not loaded")
1072417ec264SJakub Kicinski
1073417ec264SJakub Kicinski    start_test("Test removing XDP program many times...")
1074417ec264SJakub Kicinski    sim.unset_xdp("offload")
1075417ec264SJakub Kicinski    sim.unset_xdp("offload")
1076417ec264SJakub Kicinski    sim.unset_xdp("drv")
1077417ec264SJakub Kicinski    sim.unset_xdp("drv")
1078417ec264SJakub Kicinski    sim.unset_xdp("")
1079417ec264SJakub Kicinski    sim.unset_xdp("")
1080417ec264SJakub Kicinski    bpftool_prog_list_wait(expected=0)
1081417ec264SJakub Kicinski
1082417ec264SJakub Kicinski    start_test("Test attempt to use a program for a wrong device...")
1083e05b2d14SJiri Pirko    simdev2 = NetdevSimDev()
1084e05b2d14SJiri Pirko    sim2, = simdev2.nsims
1085417ec264SJakub Kicinski    sim2.set_xdp(obj, "offload")
1086417ec264SJakub Kicinski    pin_file, pinned = pin_prog("/sys/fs/bpf/tmp")
1087417ec264SJakub Kicinski
1088caf95228SQuentin Monnet    ret, _, err = sim.set_xdp(pinned, "offload",
1089caf95228SQuentin Monnet                              fail=False, include_stderr=True)
1090417ec264SJakub Kicinski    fail(ret == 0, "Pinned program loaded for a different device accepted")
1091*40535704SStanislav Fomichev    check_extack(err, "Program bound to different device.", args)
1092e05b2d14SJiri Pirko    simdev2.remove()
1093caf95228SQuentin Monnet    ret, _, err = sim.set_xdp(pinned, "offload",
1094caf95228SQuentin Monnet                              fail=False, include_stderr=True)
1095417ec264SJakub Kicinski    fail(ret == 0, "Pinned program loaded for a removed device accepted")
1096*40535704SStanislav Fomichev    check_extack(err, "Program bound to different device.", args)
1097417ec264SJakub Kicinski    rm(pin_file)
1098417ec264SJakub Kicinski    bpftool_prog_list_wait(expected=0)
1099417ec264SJakub Kicinski
1100e05b2d14SJiri Pirko    simdev, sim = test_multi_prog(simdev, sim, obj, "", 1)
1101e05b2d14SJiri Pirko    simdev, sim = test_multi_prog(simdev, sim, obj, "drv", 1)
1102e05b2d14SJiri Pirko    simdev, sim = test_multi_prog(simdev, sim, obj, "generic", 2)
110399dadb6eSJakub Kicinski
1104417ec264SJakub Kicinski    start_test("Test mixing of TC and XDP...")
1105417ec264SJakub Kicinski    sim.tc_add_ingress()
1106417ec264SJakub Kicinski    sim.set_xdp(obj, "offload")
1107caf95228SQuentin Monnet    ret, _, err = sim.cls_bpf_add_filter(obj, skip_sw=True,
1108caf95228SQuentin Monnet                                         fail=False, include_stderr=True)
1109417ec264SJakub Kicinski    fail(ret == 0, "Loading TC when XDP active should fail")
1110caf95228SQuentin Monnet    check_extack_nsim(err, "driver and netdev offload states mismatch.", args)
1111417ec264SJakub Kicinski    sim.unset_xdp("offload")
1112417ec264SJakub Kicinski    sim.wait_for_flush()
1113417ec264SJakub Kicinski
1114417ec264SJakub Kicinski    sim.cls_bpf_add_filter(obj, skip_sw=True)
1115caf95228SQuentin Monnet    ret, _, err = sim.set_xdp(obj, "offload", fail=False, include_stderr=True)
1116417ec264SJakub Kicinski    fail(ret == 0, "Loading XDP when TC active should fail")
1117caf95228SQuentin Monnet    check_extack_nsim(err, "TC program is already loaded.", args)
1118417ec264SJakub Kicinski
1119417ec264SJakub Kicinski    start_test("Test binding TC from pinned...")
1120417ec264SJakub Kicinski    pin_file, pinned = pin_prog("/sys/fs/bpf/tmp")
1121417ec264SJakub Kicinski    sim.tc_flush_filters(bound=1, total=1)
1122417ec264SJakub Kicinski    sim.cls_bpf_add_filter(pinned, da=True, skip_sw=True)
1123417ec264SJakub Kicinski    sim.tc_flush_filters(bound=1, total=1)
1124417ec264SJakub Kicinski
1125417ec264SJakub Kicinski    start_test("Test binding XDP from pinned...")
1126417ec264SJakub Kicinski    sim.set_xdp(obj, "offload")
1127417ec264SJakub Kicinski    pin_file, pinned = pin_prog("/sys/fs/bpf/tmp2", idx=1)
1128417ec264SJakub Kicinski
1129417ec264SJakub Kicinski    sim.set_xdp(pinned, "offload", force=True)
1130417ec264SJakub Kicinski    sim.unset_xdp("offload")
1131417ec264SJakub Kicinski    sim.set_xdp(pinned, "offload", force=True)
1132417ec264SJakub Kicinski    sim.unset_xdp("offload")
1133417ec264SJakub Kicinski
1134417ec264SJakub Kicinski    start_test("Test offload of wrong type fails...")
1135417ec264SJakub Kicinski    ret, _ = sim.cls_bpf_add_filter(pinned, da=True, skip_sw=True, fail=False)
1136417ec264SJakub Kicinski    fail(ret == 0, "Managed to attach XDP program to TC")
1137417ec264SJakub Kicinski
1138417ec264SJakub Kicinski    start_test("Test asking for TC offload of two filters...")
1139417ec264SJakub Kicinski    sim.cls_bpf_add_filter(obj, da=True, skip_sw=True)
1140caf95228SQuentin Monnet    ret, _, err = sim.cls_bpf_add_filter(obj, da=True, skip_sw=True,
1141caf95228SQuentin Monnet                                         fail=False, include_stderr=True)
1142fba961abSDavid S. Miller    fail(ret == 0, "Managed to offload two TC filters at the same time")
1143caf95228SQuentin Monnet    check_extack_nsim(err, "driver and netdev offload states mismatch.", args)
1144417ec264SJakub Kicinski
1145417ec264SJakub Kicinski    sim.tc_flush_filters(bound=2, total=2)
1146417ec264SJakub Kicinski
1147417ec264SJakub Kicinski    start_test("Test if netdev removal waits for translation...")
1148417ec264SJakub Kicinski    delay_msec = 500
1149d514f41eSJiri Pirko    sim.dfs["dev/bpf_bind_verifier_delay"] = delay_msec
1150417ec264SJakub Kicinski    start = time.time()
1151417ec264SJakub Kicinski    cmd_line = "tc filter add dev %s ingress bpf %s da skip_sw" % \
1152417ec264SJakub Kicinski               (sim['ifname'], obj)
1153417ec264SJakub Kicinski    tc_proc = cmd(cmd_line, background=True, fail=False)
1154417ec264SJakub Kicinski    # Wait for the verifier to start
1155e05b2d14SJiri Pirko    while simdev.dfs_num_bound_progs() <= 2:
1156417ec264SJakub Kicinski        pass
1157e05b2d14SJiri Pirko    simdev.remove()
1158417ec264SJakub Kicinski    end = time.time()
1159417ec264SJakub Kicinski    ret, _ = cmd_result(tc_proc, fail=False)
1160417ec264SJakub Kicinski    time_diff = end - start
1161417ec264SJakub Kicinski    log("Time", "start:\t%s\nend:\t%s\ndiff:\t%s" % (start, end, time_diff))
1162417ec264SJakub Kicinski
1163417ec264SJakub Kicinski    fail(ret == 0, "Managed to load TC filter on a unregistering device")
1164417ec264SJakub Kicinski    delay_sec = delay_msec * 0.001
1165417ec264SJakub Kicinski    fail(time_diff < delay_sec, "Removal process took %s, expected %s" %
1166417ec264SJakub Kicinski         (time_diff, delay_sec))
1167417ec264SJakub Kicinski
1168752d7b45SJakub Kicinski    # Remove all pinned files and reinstantiate the netdev
1169752d7b45SJakub Kicinski    clean_up()
1170752d7b45SJakub Kicinski    bpftool_prog_list_wait(expected=0)
1171752d7b45SJakub Kicinski
1172e05b2d14SJiri Pirko    simdev = NetdevSimDev()
1173e05b2d14SJiri Pirko    sim, = simdev.nsims
1174afef88e6SDaniel Müller    map_obj = bpf_obj("sample_map_ret0.bpf.o")
11757fedbb7cSJakub Kicinski    start_test("Test loading program with maps...")
11767fedbb7cSJakub Kicinski    sim.set_xdp(map_obj, "offload", JSON=False) # map fixup msg breaks JSON
1177752d7b45SJakub Kicinski
1178752d7b45SJakub Kicinski    start_test("Test bpftool bound info reporting (own ns)...")
1179752d7b45SJakub Kicinski    check_dev_info(False, "")
1180752d7b45SJakub Kicinski
1181752d7b45SJakub Kicinski    start_test("Test bpftool bound info reporting (other ns)...")
1182752d7b45SJakub Kicinski    ns = mknetns()
1183752d7b45SJakub Kicinski    sim.set_ns(ns)
1184752d7b45SJakub Kicinski    check_dev_info(True, "")
1185752d7b45SJakub Kicinski
1186752d7b45SJakub Kicinski    start_test("Test bpftool bound info reporting (remote ns)...")
1187752d7b45SJakub Kicinski    check_dev_info(False, ns)
1188752d7b45SJakub Kicinski
1189752d7b45SJakub Kicinski    start_test("Test bpftool bound info reporting (back to own ns)...")
1190752d7b45SJakub Kicinski    sim.set_ns("")
1191752d7b45SJakub Kicinski    check_dev_info(False, "")
1192752d7b45SJakub Kicinski
11937fedbb7cSJakub Kicinski    prog_file, _ = pin_prog("/sys/fs/bpf/tmp_prog")
11947fedbb7cSJakub Kicinski    map_file, _ = pin_map("/sys/fs/bpf/tmp_map", idx=1, expected=2)
1195e05b2d14SJiri Pirko    simdev.remove()
1196752d7b45SJakub Kicinski
1197752d7b45SJakub Kicinski    start_test("Test bpftool bound info reporting (removed dev)...")
11987fedbb7cSJakub Kicinski    check_dev_info_removed(prog_file=prog_file, map_file=map_file)
11997fedbb7cSJakub Kicinski
12007fedbb7cSJakub Kicinski    # Remove all pinned files and reinstantiate the netdev
12017fedbb7cSJakub Kicinski    clean_up()
12027fedbb7cSJakub Kicinski    bpftool_prog_list_wait(expected=0)
12037fedbb7cSJakub Kicinski
1204e05b2d14SJiri Pirko    simdev = NetdevSimDev()
1205e05b2d14SJiri Pirko    sim, = simdev.nsims
12067fedbb7cSJakub Kicinski
12077fedbb7cSJakub Kicinski    start_test("Test map update (no flags)...")
12087fedbb7cSJakub Kicinski    sim.set_xdp(map_obj, "offload", JSON=False) # map fixup msg breaks JSON
12097fedbb7cSJakub Kicinski    maps = bpftool_map_list(expected=2)
12107fedbb7cSJakub Kicinski    array = maps[0] if maps[0]["type"] == "array" else maps[1]
12117fedbb7cSJakub Kicinski    htab = maps[0] if maps[0]["type"] == "hash" else maps[1]
12127fedbb7cSJakub Kicinski    for m in maps:
12137fedbb7cSJakub Kicinski        for i in range(2):
12147fedbb7cSJakub Kicinski            bpftool("map update id %d key %s value %s" %
12157fedbb7cSJakub Kicinski                    (m["id"], int2str("I", i), int2str("Q", i * 3)))
12167fedbb7cSJakub Kicinski
12177fedbb7cSJakub Kicinski    for m in maps:
12187fedbb7cSJakub Kicinski        ret, _ = bpftool("map update id %d key %s value %s" %
12197fedbb7cSJakub Kicinski                         (m["id"], int2str("I", 3), int2str("Q", 3 * 3)),
12207fedbb7cSJakub Kicinski                         fail=False)
12217fedbb7cSJakub Kicinski        fail(ret == 0, "added too many entries")
12227fedbb7cSJakub Kicinski
12237fedbb7cSJakub Kicinski    start_test("Test map update (exists)...")
12247fedbb7cSJakub Kicinski    for m in maps:
12257fedbb7cSJakub Kicinski        for i in range(2):
12267fedbb7cSJakub Kicinski            bpftool("map update id %d key %s value %s exist" %
12277fedbb7cSJakub Kicinski                    (m["id"], int2str("I", i), int2str("Q", i * 3)))
12287fedbb7cSJakub Kicinski
12297fedbb7cSJakub Kicinski    for m in maps:
12307fedbb7cSJakub Kicinski        ret, err = bpftool("map update id %d key %s value %s exist" %
12317fedbb7cSJakub Kicinski                           (m["id"], int2str("I", 3), int2str("Q", 3 * 3)),
12327fedbb7cSJakub Kicinski                           fail=False)
12337fedbb7cSJakub Kicinski        fail(ret == 0, "updated non-existing key")
12347fedbb7cSJakub Kicinski        fail(err["error"].find("No such file or directory") == -1,
12357fedbb7cSJakub Kicinski             "expected ENOENT, error is '%s'" % (err["error"]))
12367fedbb7cSJakub Kicinski
12377fedbb7cSJakub Kicinski    start_test("Test map update (noexist)...")
12387fedbb7cSJakub Kicinski    for m in maps:
12397fedbb7cSJakub Kicinski        for i in range(2):
12407fedbb7cSJakub Kicinski            ret, err = bpftool("map update id %d key %s value %s noexist" %
12417fedbb7cSJakub Kicinski                               (m["id"], int2str("I", i), int2str("Q", i * 3)),
12427fedbb7cSJakub Kicinski                               fail=False)
12437fedbb7cSJakub Kicinski        fail(ret == 0, "updated existing key")
12447fedbb7cSJakub Kicinski        fail(err["error"].find("File exists") == -1,
12457fedbb7cSJakub Kicinski             "expected EEXIST, error is '%s'" % (err["error"]))
12467fedbb7cSJakub Kicinski
12477fedbb7cSJakub Kicinski    start_test("Test map dump...")
12487fedbb7cSJakub Kicinski    for m in maps:
12497fedbb7cSJakub Kicinski        _, entries = bpftool("map dump id %d" % (m["id"]))
12507fedbb7cSJakub Kicinski        for i in range(2):
12517fedbb7cSJakub Kicinski            key = str2int(entries[i]["key"])
12527fedbb7cSJakub Kicinski            fail(key != i, "expected key %d, got %d" % (key, i))
12537fedbb7cSJakub Kicinski            val = str2int(entries[i]["value"])
12547fedbb7cSJakub Kicinski            fail(val != i * 3, "expected value %d, got %d" % (val, i * 3))
12557fedbb7cSJakub Kicinski
12567fedbb7cSJakub Kicinski    start_test("Test map getnext...")
12577fedbb7cSJakub Kicinski    for m in maps:
12587fedbb7cSJakub Kicinski        _, entry = bpftool("map getnext id %d" % (m["id"]))
12597fedbb7cSJakub Kicinski        key = str2int(entry["next_key"])
12607fedbb7cSJakub Kicinski        fail(key != 0, "next key %d, expected %d" % (key, 0))
12617fedbb7cSJakub Kicinski        _, entry = bpftool("map getnext id %d key %s" %
12627fedbb7cSJakub Kicinski                           (m["id"], int2str("I", 0)))
12637fedbb7cSJakub Kicinski        key = str2int(entry["next_key"])
12647fedbb7cSJakub Kicinski        fail(key != 1, "next key %d, expected %d" % (key, 1))
12657fedbb7cSJakub Kicinski        ret, err = bpftool("map getnext id %d key %s" %
12667fedbb7cSJakub Kicinski                           (m["id"], int2str("I", 1)), fail=False)
12677fedbb7cSJakub Kicinski        fail(ret == 0, "got next key past the end of map")
12687fedbb7cSJakub Kicinski        fail(err["error"].find("No such file or directory") == -1,
12697fedbb7cSJakub Kicinski             "expected ENOENT, error is '%s'" % (err["error"]))
12707fedbb7cSJakub Kicinski
12717fedbb7cSJakub Kicinski    start_test("Test map delete (htab)...")
12727fedbb7cSJakub Kicinski    for i in range(2):
12737fedbb7cSJakub Kicinski        bpftool("map delete id %d key %s" % (htab["id"], int2str("I", i)))
12747fedbb7cSJakub Kicinski
12757fedbb7cSJakub Kicinski    start_test("Test map delete (array)...")
12767fedbb7cSJakub Kicinski    for i in range(2):
12777fedbb7cSJakub Kicinski        ret, err = bpftool("map delete id %d key %s" %
12787fedbb7cSJakub Kicinski                           (htab["id"], int2str("I", i)), fail=False)
12797fedbb7cSJakub Kicinski        fail(ret == 0, "removed entry from an array")
12807fedbb7cSJakub Kicinski        fail(err["error"].find("No such file or directory") == -1,
12817fedbb7cSJakub Kicinski             "expected ENOENT, error is '%s'" % (err["error"]))
12827fedbb7cSJakub Kicinski
12837fedbb7cSJakub Kicinski    start_test("Test map remove...")
12847fedbb7cSJakub Kicinski    sim.unset_xdp("offload")
12857fedbb7cSJakub Kicinski    bpftool_map_list_wait(expected=0)
1286e05b2d14SJiri Pirko    simdev.remove()
12877fedbb7cSJakub Kicinski
1288e05b2d14SJiri Pirko    simdev = NetdevSimDev()
1289e05b2d14SJiri Pirko    sim, = simdev.nsims
12907fedbb7cSJakub Kicinski    sim.set_xdp(map_obj, "offload", JSON=False) # map fixup msg breaks JSON
1291e05b2d14SJiri Pirko    simdev.remove()
12927fedbb7cSJakub Kicinski    bpftool_map_list_wait(expected=0)
12937fedbb7cSJakub Kicinski
12947fedbb7cSJakub Kicinski    start_test("Test map creation fail path...")
1295e05b2d14SJiri Pirko    simdev = NetdevSimDev()
1296e05b2d14SJiri Pirko    sim, = simdev.nsims
12977fedbb7cSJakub Kicinski    sim.dfs["bpf_map_accept"] = "N"
12987fedbb7cSJakub Kicinski    ret, _ = sim.set_xdp(map_obj, "offload", JSON=False, fail=False)
12997fedbb7cSJakub Kicinski    fail(ret == 0,
13007fedbb7cSJakub Kicinski         "netdevsim didn't refuse to create a map with offload disabled")
1301752d7b45SJakub Kicinski
1302e05b2d14SJiri Pirko    simdev.remove()
13037736b6edSJakub Kicinski
13047736b6edSJakub Kicinski    start_test("Test multi-dev ASIC program reuse...")
1305e05b2d14SJiri Pirko    simdevA = NetdevSimDev()
1306e05b2d14SJiri Pirko    simA, = simdevA.nsims
1307e05b2d14SJiri Pirko    simdevB = NetdevSimDev(3)
1308e05b2d14SJiri Pirko    simB1, simB2, simB3 = simdevB.nsims
13097736b6edSJakub Kicinski    sims = (simA, simB1, simB2, simB3)
13107736b6edSJakub Kicinski    simB = (simB1, simB2, simB3)
13117736b6edSJakub Kicinski
1312afef88e6SDaniel Müller    bpftool_prog_load("sample_map_ret0.bpf.o", "/sys/fs/bpf/nsimA",
13137736b6edSJakub Kicinski                      dev=simA['ifname'])
13147736b6edSJakub Kicinski    progA = bpf_pinned("/sys/fs/bpf/nsimA")
1315afef88e6SDaniel Müller    bpftool_prog_load("sample_map_ret0.bpf.o", "/sys/fs/bpf/nsimB",
13167736b6edSJakub Kicinski                      dev=simB1['ifname'])
13177736b6edSJakub Kicinski    progB = bpf_pinned("/sys/fs/bpf/nsimB")
13187736b6edSJakub Kicinski
13197736b6edSJakub Kicinski    simA.set_xdp(progA, "offload", JSON=False)
1320e05b2d14SJiri Pirko    for d in simdevB.nsims:
13217736b6edSJakub Kicinski        d.set_xdp(progB, "offload", JSON=False)
13227736b6edSJakub Kicinski
13237736b6edSJakub Kicinski    start_test("Test multi-dev ASIC cross-dev replace...")
13247736b6edSJakub Kicinski    ret, _ = simA.set_xdp(progB, "offload", force=True, JSON=False, fail=False)
13257736b6edSJakub Kicinski    fail(ret == 0, "cross-ASIC program allowed")
1326e05b2d14SJiri Pirko    for d in simdevB.nsims:
13277736b6edSJakub Kicinski        ret, _ = d.set_xdp(progA, "offload", force=True, JSON=False, fail=False)
13287736b6edSJakub Kicinski        fail(ret == 0, "cross-ASIC program allowed")
13297736b6edSJakub Kicinski
13307736b6edSJakub Kicinski    start_test("Test multi-dev ASIC cross-dev install...")
13317736b6edSJakub Kicinski    for d in sims:
13327736b6edSJakub Kicinski        d.unset_xdp("offload")
13337736b6edSJakub Kicinski
13347736b6edSJakub Kicinski    ret, _, err = simA.set_xdp(progB, "offload", force=True, JSON=False,
13357736b6edSJakub Kicinski                               fail=False, include_stderr=True)
13367736b6edSJakub Kicinski    fail(ret == 0, "cross-ASIC program allowed")
1337*40535704SStanislav Fomichev    check_extack(err, "Program bound to different device.", args)
1338e05b2d14SJiri Pirko    for d in simdevB.nsims:
13397736b6edSJakub Kicinski        ret, _, err = d.set_xdp(progA, "offload", force=True, JSON=False,
13407736b6edSJakub Kicinski                                fail=False, include_stderr=True)
13417736b6edSJakub Kicinski        fail(ret == 0, "cross-ASIC program allowed")
1342*40535704SStanislav Fomichev        check_extack(err, "Program bound to different device.", args)
13437736b6edSJakub Kicinski
13447736b6edSJakub Kicinski    start_test("Test multi-dev ASIC cross-dev map reuse...")
13457736b6edSJakub Kicinski
13467736b6edSJakub Kicinski    mapA = bpftool("prog show %s" % (progA))[1]["map_ids"][0]
13477736b6edSJakub Kicinski    mapB = bpftool("prog show %s" % (progB))[1]["map_ids"][0]
13487736b6edSJakub Kicinski
1349afef88e6SDaniel Müller    ret, _ = bpftool_prog_load("sample_map_ret0.bpf.o", "/sys/fs/bpf/nsimB_",
13507736b6edSJakub Kicinski                               dev=simB3['ifname'],
13517736b6edSJakub Kicinski                               maps=["idx 0 id %d" % (mapB)],
13527736b6edSJakub Kicinski                               fail=False)
13537736b6edSJakub Kicinski    fail(ret != 0, "couldn't reuse a map on the same ASIC")
13547736b6edSJakub Kicinski    rm("/sys/fs/bpf/nsimB_")
13557736b6edSJakub Kicinski
1356afef88e6SDaniel Müller    ret, _, err = bpftool_prog_load("sample_map_ret0.bpf.o", "/sys/fs/bpf/nsimA_",
13577736b6edSJakub Kicinski                                    dev=simA['ifname'],
13587736b6edSJakub Kicinski                                    maps=["idx 0 id %d" % (mapB)],
13597736b6edSJakub Kicinski                                    fail=False, include_stderr=True)
13607736b6edSJakub Kicinski    fail(ret == 0, "could reuse a map on a different ASIC")
13617736b6edSJakub Kicinski    fail(err.count("offload device mismatch between prog and map") == 0,
13627736b6edSJakub Kicinski         "error message missing for cross-ASIC map")
13637736b6edSJakub Kicinski
1364afef88e6SDaniel Müller    ret, _, err = bpftool_prog_load("sample_map_ret0.bpf.o", "/sys/fs/bpf/nsimB_",
13657736b6edSJakub Kicinski                                    dev=simB1['ifname'],
13667736b6edSJakub Kicinski                                    maps=["idx 0 id %d" % (mapA)],
13677736b6edSJakub Kicinski                                    fail=False, include_stderr=True)
13687736b6edSJakub Kicinski    fail(ret == 0, "could reuse a map on a different ASIC")
13697736b6edSJakub Kicinski    fail(err.count("offload device mismatch between prog and map") == 0,
13707736b6edSJakub Kicinski         "error message missing for cross-ASIC map")
13717736b6edSJakub Kicinski
13727736b6edSJakub Kicinski    start_test("Test multi-dev ASIC cross-dev destruction...")
13737736b6edSJakub Kicinski    bpftool_prog_list_wait(expected=2)
13747736b6edSJakub Kicinski
1375e05b2d14SJiri Pirko    simdevA.remove()
13767736b6edSJakub Kicinski    bpftool_prog_list_wait(expected=1)
13777736b6edSJakub Kicinski
13787736b6edSJakub Kicinski    ifnameB = bpftool("prog show %s" % (progB))[1]["dev"]["ifname"]
13791c6d6e02SMasanari Iida    fail(ifnameB != simB1['ifname'], "program not bound to original device")
13807736b6edSJakub Kicinski    simB1.remove()
13817736b6edSJakub Kicinski    bpftool_prog_list_wait(expected=1)
13827736b6edSJakub Kicinski
13837736b6edSJakub Kicinski    start_test("Test multi-dev ASIC cross-dev destruction - move...")
13847736b6edSJakub Kicinski    ifnameB = bpftool("prog show %s" % (progB))[1]["dev"]["ifname"]
13857736b6edSJakub Kicinski    fail(ifnameB not in (simB2['ifname'], simB3['ifname']),
13867736b6edSJakub Kicinski         "program not bound to remaining devices")
13877736b6edSJakub Kicinski
13887736b6edSJakub Kicinski    simB2.remove()
13897736b6edSJakub Kicinski    ifnameB = bpftool("prog show %s" % (progB))[1]["dev"]["ifname"]
13907736b6edSJakub Kicinski    fail(ifnameB != simB3['ifname'], "program not bound to remaining device")
13917736b6edSJakub Kicinski
13927736b6edSJakub Kicinski    simB3.remove()
1393e05b2d14SJiri Pirko    simdevB.remove()
13947736b6edSJakub Kicinski    bpftool_prog_list_wait(expected=0)
13957736b6edSJakub Kicinski
13967736b6edSJakub Kicinski    start_test("Test multi-dev ASIC cross-dev destruction - orphaned...")
13977736b6edSJakub Kicinski    ret, out = bpftool("prog show %s" % (progB), fail=False)
13987736b6edSJakub Kicinski    fail(ret == 0, "got information about orphaned program")
13997736b6edSJakub Kicinski    fail("error" not in out, "no error reported for get info on orphaned")
14007736b6edSJakub Kicinski    fail(out["error"] != "can't get prog info: No such device",
14017736b6edSJakub Kicinski         "wrong error for get info on orphaned")
14027736b6edSJakub Kicinski
1403417ec264SJakub Kicinski    print("%s: OK" % (os.path.basename(__file__)))
1404417ec264SJakub Kicinski
1405417ec264SJakub Kicinskifinally:
1406417ec264SJakub Kicinski    log("Clean up...", "", level=1)
1407417ec264SJakub Kicinski    log_level_inc()
1408417ec264SJakub Kicinski    clean_up()
1409