1417ec264SJakub Kicinski#!/usr/bin/python3
2417ec264SJakub Kicinski
3417ec264SJakub Kicinski# Copyright (C) 2017 Netronome Systems, Inc.
4417ec264SJakub Kicinski#
5417ec264SJakub Kicinski# This software is licensed under the GNU General License Version 2,
6417ec264SJakub Kicinski# June 1991 as shown in the file COPYING in the top-level directory of this
7417ec264SJakub Kicinski# source tree.
8417ec264SJakub Kicinski#
9417ec264SJakub Kicinski# THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS"
10417ec264SJakub Kicinski# WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING,
11417ec264SJakub Kicinski# BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
12417ec264SJakub Kicinski# FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE
13417ec264SJakub Kicinski# OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME
14417ec264SJakub Kicinski# THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
15417ec264SJakub Kicinski
16417ec264SJakub Kicinskifrom datetime import datetime
17417ec264SJakub Kicinskiimport argparse
18417ec264SJakub Kicinskiimport json
19417ec264SJakub Kicinskiimport os
20417ec264SJakub Kicinskiimport pprint
21752d7b45SJakub Kicinskiimport random
22752d7b45SJakub Kicinskiimport string
237fedbb7cSJakub Kicinskiimport struct
24417ec264SJakub Kicinskiimport subprocess
25417ec264SJakub Kicinskiimport time
26417ec264SJakub Kicinski
27417ec264SJakub Kicinskilogfile = None
28417ec264SJakub Kicinskilog_level = 1
29caf95228SQuentin Monnetskip_extack = False
30417ec264SJakub Kicinskibpf_test_dir = os.path.dirname(os.path.realpath(__file__))
31417ec264SJakub Kicinskipp = pprint.PrettyPrinter()
32417ec264SJakub Kicinskidevs = [] # devices we created for clean up
33417ec264SJakub Kicinskifiles = [] # files to be removed
34752d7b45SJakub Kicinskinetns = [] # net namespaces to be removed
35417ec264SJakub Kicinski
36417ec264SJakub Kicinskidef log_get_sec(level=0):
37417ec264SJakub Kicinski    return "*" * (log_level + level)
38417ec264SJakub Kicinski
39417ec264SJakub Kicinskidef log_level_inc(add=1):
40417ec264SJakub Kicinski    global log_level
41417ec264SJakub Kicinski    log_level += add
42417ec264SJakub Kicinski
43417ec264SJakub Kicinskidef log_level_dec(sub=1):
44417ec264SJakub Kicinski    global log_level
45417ec264SJakub Kicinski    log_level -= sub
46417ec264SJakub Kicinski
47417ec264SJakub Kicinskidef log_level_set(level):
48417ec264SJakub Kicinski    global log_level
49417ec264SJakub Kicinski    log_level = level
50417ec264SJakub Kicinski
51417ec264SJakub Kicinskidef log(header, data, level=None):
52417ec264SJakub Kicinski    """
53417ec264SJakub Kicinski    Output to an optional log.
54417ec264SJakub Kicinski    """
55417ec264SJakub Kicinski    if logfile is None:
56417ec264SJakub Kicinski        return
57417ec264SJakub Kicinski    if level is not None:
58417ec264SJakub Kicinski        log_level_set(level)
59417ec264SJakub Kicinski
60417ec264SJakub Kicinski    if not isinstance(data, str):
61417ec264SJakub Kicinski        data = pp.pformat(data)
62417ec264SJakub Kicinski
63417ec264SJakub Kicinski    if len(header):
64417ec264SJakub Kicinski        logfile.write("\n" + log_get_sec() + " ")
65417ec264SJakub Kicinski        logfile.write(header)
66417ec264SJakub Kicinski    if len(header) and len(data.strip()):
67417ec264SJakub Kicinski        logfile.write("\n")
68417ec264SJakub Kicinski    logfile.write(data)
69417ec264SJakub Kicinski
70417ec264SJakub Kicinskidef skip(cond, msg):
71417ec264SJakub Kicinski    if not cond:
72417ec264SJakub Kicinski        return
73417ec264SJakub Kicinski    print("SKIP: " + msg)
74417ec264SJakub Kicinski    log("SKIP: " + msg, "", level=1)
75417ec264SJakub Kicinski    os.sys.exit(0)
76417ec264SJakub Kicinski
77417ec264SJakub Kicinskidef fail(cond, msg):
78417ec264SJakub Kicinski    if not cond:
79417ec264SJakub Kicinski        return
80417ec264SJakub Kicinski    print("FAIL: " + msg)
81417ec264SJakub Kicinski    log("FAIL: " + msg, "", level=1)
82417ec264SJakub Kicinski    os.sys.exit(1)
83417ec264SJakub Kicinski
84417ec264SJakub Kicinskidef start_test(msg):
85417ec264SJakub Kicinski    log(msg, "", level=1)
86417ec264SJakub Kicinski    log_level_inc()
87417ec264SJakub Kicinski    print(msg)
88417ec264SJakub Kicinski
89417ec264SJakub Kicinskidef cmd(cmd, shell=True, include_stderr=False, background=False, fail=True):
90417ec264SJakub Kicinski    """
91417ec264SJakub Kicinski    Run a command in subprocess and return tuple of (retval, stdout);
92417ec264SJakub Kicinski    optionally return stderr as well as third value.
93417ec264SJakub Kicinski    """
94417ec264SJakub Kicinski    proc = subprocess.Popen(cmd, shell=shell, stdout=subprocess.PIPE,
95417ec264SJakub Kicinski                            stderr=subprocess.PIPE)
96417ec264SJakub Kicinski    if background:
97417ec264SJakub Kicinski        msg = "%s START: %s" % (log_get_sec(1),
98417ec264SJakub Kicinski                                datetime.now().strftime("%H:%M:%S.%f"))
99417ec264SJakub Kicinski        log("BKG " + proc.args, msg)
100417ec264SJakub Kicinski        return proc
101417ec264SJakub Kicinski
102417ec264SJakub Kicinski    return cmd_result(proc, include_stderr=include_stderr, fail=fail)
103417ec264SJakub Kicinski
104417ec264SJakub Kicinskidef cmd_result(proc, include_stderr=False, fail=False):
105417ec264SJakub Kicinski    stdout, stderr = proc.communicate()
106417ec264SJakub Kicinski    stdout = stdout.decode("utf-8")
107417ec264SJakub Kicinski    stderr = stderr.decode("utf-8")
108417ec264SJakub Kicinski    proc.stdout.close()
109417ec264SJakub Kicinski    proc.stderr.close()
110417ec264SJakub Kicinski
111417ec264SJakub Kicinski    stderr = "\n" + stderr
112417ec264SJakub Kicinski    if stderr[-1] == "\n":
113417ec264SJakub Kicinski        stderr = stderr[:-1]
114417ec264SJakub Kicinski
115417ec264SJakub Kicinski    sec = log_get_sec(1)
116417ec264SJakub Kicinski    log("CMD " + proc.args,
117417ec264SJakub Kicinski        "RETCODE: %d\n%s STDOUT:\n%s%s STDERR:%s\n%s END: %s" %
118417ec264SJakub Kicinski        (proc.returncode, sec, stdout, sec, stderr,
119417ec264SJakub Kicinski         sec, datetime.now().strftime("%H:%M:%S.%f")))
120417ec264SJakub Kicinski
121417ec264SJakub Kicinski    if proc.returncode != 0 and fail:
122417ec264SJakub Kicinski        if len(stderr) > 0 and stderr[-1] == "\n":
123417ec264SJakub Kicinski            stderr = stderr[:-1]
124417ec264SJakub Kicinski        raise Exception("Command failed: %s\n%s" % (proc.args, stderr))
125417ec264SJakub Kicinski
126417ec264SJakub Kicinski    if include_stderr:
127417ec264SJakub Kicinski        return proc.returncode, stdout, stderr
128417ec264SJakub Kicinski    else:
129417ec264SJakub Kicinski        return proc.returncode, stdout
130417ec264SJakub Kicinski
131417ec264SJakub Kicinskidef rm(f):
132417ec264SJakub Kicinski    cmd("rm -f %s" % (f))
133417ec264SJakub Kicinski    if f in files:
134417ec264SJakub Kicinski        files.remove(f)
135417ec264SJakub Kicinski
136caf95228SQuentin Monnetdef tool(name, args, flags, JSON=True, ns="", fail=True, include_stderr=False):
137417ec264SJakub Kicinski    params = ""
138417ec264SJakub Kicinski    if JSON:
139417ec264SJakub Kicinski        params += "%s " % (flags["json"])
140417ec264SJakub Kicinski
141752d7b45SJakub Kicinski    if ns != "":
142752d7b45SJakub Kicinski        ns = "ip netns exec %s " % (ns)
143752d7b45SJakub Kicinski
144caf95228SQuentin Monnet    if include_stderr:
145caf95228SQuentin Monnet        ret, stdout, stderr = cmd(ns + name + " " + params + args,
146caf95228SQuentin Monnet                                  fail=fail, include_stderr=True)
147caf95228SQuentin Monnet    else:
148caf95228SQuentin Monnet        ret, stdout = cmd(ns + name + " " + params + args,
149caf95228SQuentin Monnet                          fail=fail, include_stderr=False)
150caf95228SQuentin Monnet
151caf95228SQuentin Monnet    if JSON and len(stdout.strip()) != 0:
152caf95228SQuentin Monnet        out = json.loads(stdout)
153caf95228SQuentin Monnet    else:
154caf95228SQuentin Monnet        out = stdout
155caf95228SQuentin Monnet
156caf95228SQuentin Monnet    if include_stderr:
157caf95228SQuentin Monnet        return ret, out, stderr
158417ec264SJakub Kicinski    else:
159417ec264SJakub Kicinski        return ret, out
160417ec264SJakub Kicinski
161752d7b45SJakub Kicinskidef bpftool(args, JSON=True, ns="", fail=True):
162752d7b45SJakub Kicinski    return tool("bpftool", args, {"json":"-p"}, JSON=JSON, ns=ns, fail=fail)
163417ec264SJakub Kicinski
164752d7b45SJakub Kicinskidef bpftool_prog_list(expected=None, ns=""):
165752d7b45SJakub Kicinski    _, progs = bpftool("prog show", JSON=True, ns=ns, fail=True)
16647cf52a2SJakub Kicinski    # Remove the base progs
16747cf52a2SJakub Kicinski    for p in base_progs:
16847cf52a2SJakub Kicinski        if p in progs:
16947cf52a2SJakub Kicinski            progs.remove(p)
170417ec264SJakub Kicinski    if expected is not None:
171417ec264SJakub Kicinski        if len(progs) != expected:
172417ec264SJakub Kicinski            fail(True, "%d BPF programs loaded, expected %d" %
173417ec264SJakub Kicinski                 (len(progs), expected))
174417ec264SJakub Kicinski    return progs
175417ec264SJakub Kicinski
1767fedbb7cSJakub Kicinskidef bpftool_map_list(expected=None, ns=""):
1777fedbb7cSJakub Kicinski    _, maps = bpftool("map show", JSON=True, ns=ns, fail=True)
17847cf52a2SJakub Kicinski    # Remove the base maps
17947cf52a2SJakub Kicinski    for m in base_maps:
18047cf52a2SJakub Kicinski        if m in maps:
18147cf52a2SJakub Kicinski            maps.remove(m)
1827fedbb7cSJakub Kicinski    if expected is not None:
1837fedbb7cSJakub Kicinski        if len(maps) != expected:
1847fedbb7cSJakub Kicinski            fail(True, "%d BPF maps loaded, expected %d" %
1857fedbb7cSJakub Kicinski                 (len(maps), expected))
1867fedbb7cSJakub Kicinski    return maps
1877fedbb7cSJakub Kicinski
188417ec264SJakub Kicinskidef bpftool_prog_list_wait(expected=0, n_retry=20):
189417ec264SJakub Kicinski    for i in range(n_retry):
190417ec264SJakub Kicinski        nprogs = len(bpftool_prog_list())
191417ec264SJakub Kicinski        if nprogs == expected:
192417ec264SJakub Kicinski            return
193417ec264SJakub Kicinski        time.sleep(0.05)
194417ec264SJakub Kicinski    raise Exception("Time out waiting for program counts to stabilize want %d, have %d" % (expected, nprogs))
195417ec264SJakub Kicinski
1967fedbb7cSJakub Kicinskidef bpftool_map_list_wait(expected=0, n_retry=20):
1977fedbb7cSJakub Kicinski    for i in range(n_retry):
1987fedbb7cSJakub Kicinski        nmaps = len(bpftool_map_list())
1997fedbb7cSJakub Kicinski        if nmaps == expected:
2007fedbb7cSJakub Kicinski            return
2017fedbb7cSJakub Kicinski        time.sleep(0.05)
2027fedbb7cSJakub Kicinski    raise Exception("Time out waiting for map counts to stabilize want %d, have %d" % (expected, nmaps))
2037fedbb7cSJakub Kicinski
204caf95228SQuentin Monnetdef ip(args, force=False, JSON=True, ns="", fail=True, include_stderr=False):
205417ec264SJakub Kicinski    if force:
206417ec264SJakub Kicinski        args = "-force " + args
207caf95228SQuentin Monnet    return tool("ip", args, {"json":"-j"}, JSON=JSON, ns=ns,
208caf95228SQuentin Monnet                fail=fail, include_stderr=include_stderr)
209417ec264SJakub Kicinski
210caf95228SQuentin Monnetdef tc(args, JSON=True, ns="", fail=True, include_stderr=False):
211caf95228SQuentin Monnet    return tool("tc", args, {"json":"-p"}, JSON=JSON, ns=ns,
212caf95228SQuentin Monnet                fail=fail, include_stderr=include_stderr)
213417ec264SJakub Kicinski
214417ec264SJakub Kicinskidef ethtool(dev, opt, args, fail=True):
215417ec264SJakub Kicinski    return cmd("ethtool %s %s %s" % (opt, dev["ifname"], args), fail=fail)
216417ec264SJakub Kicinski
217417ec264SJakub Kicinskidef bpf_obj(name, sec=".text", path=bpf_test_dir,):
218417ec264SJakub Kicinski    return "obj %s sec %s" % (os.path.join(path, name), sec)
219417ec264SJakub Kicinski
220417ec264SJakub Kicinskidef bpf_pinned(name):
221417ec264SJakub Kicinski    return "pinned %s" % (name)
222417ec264SJakub Kicinski
223417ec264SJakub Kicinskidef bpf_bytecode(bytecode):
224417ec264SJakub Kicinski    return "bytecode \"%s\"" % (bytecode)
225417ec264SJakub Kicinski
226752d7b45SJakub Kicinskidef mknetns(n_retry=10):
227752d7b45SJakub Kicinski    for i in range(n_retry):
228752d7b45SJakub Kicinski        name = ''.join([random.choice(string.ascii_letters) for i in range(8)])
229752d7b45SJakub Kicinski        ret, _ = ip("netns add %s" % (name), fail=False)
230752d7b45SJakub Kicinski        if ret == 0:
231752d7b45SJakub Kicinski            netns.append(name)
232752d7b45SJakub Kicinski            return name
233752d7b45SJakub Kicinski    return None
234752d7b45SJakub Kicinski
2357fedbb7cSJakub Kicinskidef int2str(fmt, val):
2367fedbb7cSJakub Kicinski    ret = []
2377fedbb7cSJakub Kicinski    for b in struct.pack(fmt, val):
2387fedbb7cSJakub Kicinski        ret.append(int(b))
2397fedbb7cSJakub Kicinski    return " ".join(map(lambda x: str(x), ret))
2407fedbb7cSJakub Kicinski
2417fedbb7cSJakub Kicinskidef str2int(strtab):
2427fedbb7cSJakub Kicinski    inttab = []
2437fedbb7cSJakub Kicinski    for i in strtab:
2447fedbb7cSJakub Kicinski        inttab.append(int(i, 16))
2457fedbb7cSJakub Kicinski    ba = bytearray(inttab)
2467fedbb7cSJakub Kicinski    if len(strtab) == 4:
2477fedbb7cSJakub Kicinski        fmt = "I"
2487fedbb7cSJakub Kicinski    elif len(strtab) == 8:
2497fedbb7cSJakub Kicinski        fmt = "Q"
2507fedbb7cSJakub Kicinski    else:
2517fedbb7cSJakub Kicinski        raise Exception("String array of len %d can't be unpacked to an int" %
2527fedbb7cSJakub Kicinski                        (len(strtab)))
2537fedbb7cSJakub Kicinski    return struct.unpack(fmt, ba)[0]
2547fedbb7cSJakub Kicinski
255417ec264SJakub Kicinskiclass DebugfsDir:
256417ec264SJakub Kicinski    """
257417ec264SJakub Kicinski    Class for accessing DebugFS directories as a dictionary.
258417ec264SJakub Kicinski    """
259417ec264SJakub Kicinski
260417ec264SJakub Kicinski    def __init__(self, path):
261417ec264SJakub Kicinski        self.path = path
262417ec264SJakub Kicinski        self._dict = self._debugfs_dir_read(path)
263417ec264SJakub Kicinski
264417ec264SJakub Kicinski    def __len__(self):
265417ec264SJakub Kicinski        return len(self._dict.keys())
266417ec264SJakub Kicinski
267417ec264SJakub Kicinski    def __getitem__(self, key):
268417ec264SJakub Kicinski        if type(key) is int:
269417ec264SJakub Kicinski            key = list(self._dict.keys())[key]
270417ec264SJakub Kicinski        return self._dict[key]
271417ec264SJakub Kicinski
272417ec264SJakub Kicinski    def __setitem__(self, key, value):
273417ec264SJakub Kicinski        log("DebugFS set %s = %s" % (key, value), "")
274417ec264SJakub Kicinski        log_level_inc()
275417ec264SJakub Kicinski
276417ec264SJakub Kicinski        cmd("echo '%s' > %s/%s" % (value, self.path, key))
277417ec264SJakub Kicinski        log_level_dec()
278417ec264SJakub Kicinski
279417ec264SJakub Kicinski        _, out = cmd('cat %s/%s' % (self.path, key))
280417ec264SJakub Kicinski        self._dict[key] = out.strip()
281417ec264SJakub Kicinski
282417ec264SJakub Kicinski    def _debugfs_dir_read(self, path):
283417ec264SJakub Kicinski        dfs = {}
284417ec264SJakub Kicinski
285417ec264SJakub Kicinski        log("DebugFS state for %s" % (path), "")
286417ec264SJakub Kicinski        log_level_inc(add=2)
287417ec264SJakub Kicinski
288417ec264SJakub Kicinski        _, out = cmd('ls ' + path)
289417ec264SJakub Kicinski        for f in out.split():
290417ec264SJakub Kicinski            p = os.path.join(path, f)
291417ec264SJakub Kicinski            if os.path.isfile(p):
292417ec264SJakub Kicinski                _, out = cmd('cat %s/%s' % (path, f))
293417ec264SJakub Kicinski                dfs[f] = out.strip()
294417ec264SJakub Kicinski            elif os.path.isdir(p):
295417ec264SJakub Kicinski                dfs[f] = DebugfsDir(p)
296417ec264SJakub Kicinski            else:
297417ec264SJakub Kicinski                raise Exception("%s is neither file nor directory" % (p))
298417ec264SJakub Kicinski
299417ec264SJakub Kicinski        log_level_dec()
300417ec264SJakub Kicinski        log("DebugFS state", dfs)
301417ec264SJakub Kicinski        log_level_dec()
302417ec264SJakub Kicinski
303417ec264SJakub Kicinski        return dfs
304417ec264SJakub Kicinski
305417ec264SJakub Kicinskiclass NetdevSim:
306417ec264SJakub Kicinski    """
307417ec264SJakub Kicinski    Class for netdevsim netdevice and its attributes.
308417ec264SJakub Kicinski    """
309417ec264SJakub Kicinski
310417ec264SJakub Kicinski    def __init__(self):
311417ec264SJakub Kicinski        self.dev = self._netdevsim_create()
312417ec264SJakub Kicinski        devs.append(self)
313417ec264SJakub Kicinski
314752d7b45SJakub Kicinski        self.ns = ""
315752d7b45SJakub Kicinski
316417ec264SJakub Kicinski        self.dfs_dir = '/sys/kernel/debug/netdevsim/%s' % (self.dev['ifname'])
317417ec264SJakub Kicinski        self.dfs_refresh()
318417ec264SJakub Kicinski
319417ec264SJakub Kicinski    def __getitem__(self, key):
320417ec264SJakub Kicinski        return self.dev[key]
321417ec264SJakub Kicinski
322417ec264SJakub Kicinski    def _netdevsim_create(self):
323417ec264SJakub Kicinski        _, old  = ip("link show")
324417ec264SJakub Kicinski        ip("link add sim%d type netdevsim")
325417ec264SJakub Kicinski        _, new  = ip("link show")
326417ec264SJakub Kicinski
327417ec264SJakub Kicinski        for dev in new:
328417ec264SJakub Kicinski            f = filter(lambda x: x["ifname"] == dev["ifname"], old)
329417ec264SJakub Kicinski            if len(list(f)) == 0:
330417ec264SJakub Kicinski                return dev
331417ec264SJakub Kicinski
332417ec264SJakub Kicinski        raise Exception("failed to create netdevsim device")
333417ec264SJakub Kicinski
334417ec264SJakub Kicinski    def remove(self):
335417ec264SJakub Kicinski        devs.remove(self)
336752d7b45SJakub Kicinski        ip("link del dev %s" % (self.dev["ifname"]), ns=self.ns)
337417ec264SJakub Kicinski
338417ec264SJakub Kicinski    def dfs_refresh(self):
339417ec264SJakub Kicinski        self.dfs = DebugfsDir(self.dfs_dir)
340417ec264SJakub Kicinski        return self.dfs
341417ec264SJakub Kicinski
342417ec264SJakub Kicinski    def dfs_num_bound_progs(self):
343417ec264SJakub Kicinski        path = os.path.join(self.dfs_dir, "bpf_bound_progs")
344417ec264SJakub Kicinski        _, progs = cmd('ls %s' % (path))
345417ec264SJakub Kicinski        return len(progs.split())
346417ec264SJakub Kicinski
347417ec264SJakub Kicinski    def dfs_get_bound_progs(self, expected):
348417ec264SJakub Kicinski        progs = DebugfsDir(os.path.join(self.dfs_dir, "bpf_bound_progs"))
349417ec264SJakub Kicinski        if expected is not None:
350417ec264SJakub Kicinski            if len(progs) != expected:
351417ec264SJakub Kicinski                fail(True, "%d BPF programs bound, expected %d" %
352417ec264SJakub Kicinski                     (len(progs), expected))
353417ec264SJakub Kicinski        return progs
354417ec264SJakub Kicinski
355417ec264SJakub Kicinski    def wait_for_flush(self, bound=0, total=0, n_retry=20):
356417ec264SJakub Kicinski        for i in range(n_retry):
357417ec264SJakub Kicinski            nbound = self.dfs_num_bound_progs()
358417ec264SJakub Kicinski            nprogs = len(bpftool_prog_list())
359417ec264SJakub Kicinski            if nbound == bound and nprogs == total:
360417ec264SJakub Kicinski                return
361417ec264SJakub Kicinski            time.sleep(0.05)
362417ec264SJakub Kicinski        raise Exception("Time out waiting for program counts to stabilize want %d/%d, have %d bound, %d loaded" % (bound, total, nbound, nprogs))
363417ec264SJakub Kicinski
364752d7b45SJakub Kicinski    def set_ns(self, ns):
365752d7b45SJakub Kicinski        name = "1" if ns == "" else ns
366752d7b45SJakub Kicinski        ip("link set dev %s netns %s" % (self.dev["ifname"], name), ns=self.ns)
367752d7b45SJakub Kicinski        self.ns = ns
368752d7b45SJakub Kicinski
369417ec264SJakub Kicinski    def set_mtu(self, mtu, fail=True):
370417ec264SJakub Kicinski        return ip("link set dev %s mtu %d" % (self.dev["ifname"], mtu),
371417ec264SJakub Kicinski                  fail=fail)
372417ec264SJakub Kicinski
3739045bdc8SQuentin Monnet    def set_xdp(self, bpf, mode, force=False, JSON=True, verbose=False,
374caf95228SQuentin Monnet                fail=True, include_stderr=False):
3759045bdc8SQuentin Monnet        if verbose:
3769045bdc8SQuentin Monnet            bpf += " verbose"
377417ec264SJakub Kicinski        return ip("link set dev %s xdp%s %s" % (self.dev["ifname"], mode, bpf),
378caf95228SQuentin Monnet                  force=force, JSON=JSON,
379caf95228SQuentin Monnet                  fail=fail, include_stderr=include_stderr)
380417ec264SJakub Kicinski
381caf95228SQuentin Monnet    def unset_xdp(self, mode, force=False, JSON=True,
382caf95228SQuentin Monnet                  fail=True, include_stderr=False):
383417ec264SJakub Kicinski        return ip("link set dev %s xdp%s off" % (self.dev["ifname"], mode),
384caf95228SQuentin Monnet                  force=force, JSON=JSON,
385caf95228SQuentin Monnet                  fail=fail, include_stderr=include_stderr)
386417ec264SJakub Kicinski
387417ec264SJakub Kicinski    def ip_link_show(self, xdp):
388417ec264SJakub Kicinski        _, link = ip("link show dev %s" % (self['ifname']))
389417ec264SJakub Kicinski        if len(link) > 1:
390417ec264SJakub Kicinski            raise Exception("Multiple objects on ip link show")
391417ec264SJakub Kicinski        if len(link) < 1:
392417ec264SJakub Kicinski            return {}
393417ec264SJakub Kicinski        fail(xdp != "xdp" in link,
394417ec264SJakub Kicinski             "XDP program not reporting in iplink (reported %s, expected %s)" %
395417ec264SJakub Kicinski             ("xdp" in link, xdp))
396417ec264SJakub Kicinski        return link[0]
397417ec264SJakub Kicinski
398417ec264SJakub Kicinski    def tc_add_ingress(self):
399417ec264SJakub Kicinski        tc("qdisc add dev %s ingress" % (self['ifname']))
400417ec264SJakub Kicinski
401417ec264SJakub Kicinski    def tc_del_ingress(self):
402417ec264SJakub Kicinski        tc("qdisc del dev %s ingress" % (self['ifname']))
403417ec264SJakub Kicinski
404417ec264SJakub Kicinski    def tc_flush_filters(self, bound=0, total=0):
405417ec264SJakub Kicinski        self.tc_del_ingress()
406417ec264SJakub Kicinski        self.tc_add_ingress()
407417ec264SJakub Kicinski        self.wait_for_flush(bound=bound, total=total)
408417ec264SJakub Kicinski
409417ec264SJakub Kicinski    def tc_show_ingress(self, expected=None):
410417ec264SJakub Kicinski        # No JSON support, oh well...
411417ec264SJakub Kicinski        flags = ["skip_sw", "skip_hw", "in_hw"]
412417ec264SJakub Kicinski        named = ["protocol", "pref", "chain", "handle", "id", "tag"]
413417ec264SJakub Kicinski
414417ec264SJakub Kicinski        args = "-s filter show dev %s ingress" % (self['ifname'])
415417ec264SJakub Kicinski        _, out = tc(args, JSON=False)
416417ec264SJakub Kicinski
417417ec264SJakub Kicinski        filters = []
418417ec264SJakub Kicinski        lines = out.split('\n')
419417ec264SJakub Kicinski        for line in lines:
420417ec264SJakub Kicinski            words = line.split()
421417ec264SJakub Kicinski            if "handle" not in words:
422417ec264SJakub Kicinski                continue
423417ec264SJakub Kicinski            fltr = {}
424417ec264SJakub Kicinski            for flag in flags:
425417ec264SJakub Kicinski                fltr[flag] = flag in words
426417ec264SJakub Kicinski            for name in named:
427417ec264SJakub Kicinski                try:
428417ec264SJakub Kicinski                    idx = words.index(name)
429417ec264SJakub Kicinski                    fltr[name] = words[idx + 1]
430417ec264SJakub Kicinski                except ValueError:
431417ec264SJakub Kicinski                    pass
432417ec264SJakub Kicinski            filters.append(fltr)
433417ec264SJakub Kicinski
434417ec264SJakub Kicinski        if expected is not None:
435417ec264SJakub Kicinski            fail(len(filters) != expected,
436417ec264SJakub Kicinski                 "%d ingress filters loaded, expected %d" %
437417ec264SJakub Kicinski                 (len(filters), expected))
438417ec264SJakub Kicinski        return filters
439417ec264SJakub Kicinski
4406d2d58f1SJakub Kicinski    def cls_filter_op(self, op, qdisc="ingress", prio=None, handle=None,
441baf6a07eSJakub Kicinski                      chain=None, cls="", params="",
4426d2d58f1SJakub Kicinski                      fail=True, include_stderr=False):
4436d2d58f1SJakub Kicinski        spec = ""
4446d2d58f1SJakub Kicinski        if prio is not None:
4456d2d58f1SJakub Kicinski            spec += " prio %d" % (prio)
4466d2d58f1SJakub Kicinski        if handle:
4476d2d58f1SJakub Kicinski            spec += " handle %s" % (handle)
448baf6a07eSJakub Kicinski        if chain is not None:
449baf6a07eSJakub Kicinski            spec += " chain %d" % (chain)
4506d2d58f1SJakub Kicinski
4516d2d58f1SJakub Kicinski        return tc("filter {op} dev {dev} {qdisc} {spec} {cls} {params}"\
4526d2d58f1SJakub Kicinski                  .format(op=op, dev=self['ifname'], qdisc=qdisc, spec=spec,
4536d2d58f1SJakub Kicinski                          cls=cls, params=params),
4546d2d58f1SJakub Kicinski                  fail=fail, include_stderr=include_stderr)
4556d2d58f1SJakub Kicinski
4566d2d58f1SJakub Kicinski    def cls_bpf_add_filter(self, bpf, op="add", prio=None, handle=None,
457baf6a07eSJakub Kicinski                           chain=None, da=False, verbose=False,
4586d2d58f1SJakub Kicinski                           skip_sw=False, skip_hw=False,
4596d2d58f1SJakub Kicinski                           fail=True, include_stderr=False):
4606d2d58f1SJakub Kicinski        cls = "bpf " + bpf
4616d2d58f1SJakub Kicinski
462417ec264SJakub Kicinski        params = ""
463417ec264SJakub Kicinski        if da:
464417ec264SJakub Kicinski            params += " da"
4659045bdc8SQuentin Monnet        if verbose:
4669045bdc8SQuentin Monnet            params += " verbose"
467417ec264SJakub Kicinski        if skip_sw:
468417ec264SJakub Kicinski            params += " skip_sw"
469417ec264SJakub Kicinski        if skip_hw:
470417ec264SJakub Kicinski            params += " skip_hw"
4716d2d58f1SJakub Kicinski
4726d2d58f1SJakub Kicinski        return self.cls_filter_op(op=op, prio=prio, handle=handle, cls=cls,
473baf6a07eSJakub Kicinski                                  chain=chain, params=params,
474caf95228SQuentin Monnet                                  fail=fail, include_stderr=include_stderr)
475417ec264SJakub Kicinski
476417ec264SJakub Kicinski    def set_ethtool_tc_offloads(self, enable, fail=True):
477417ec264SJakub Kicinski        args = "hw-tc-offload %s" % ("on" if enable else "off")
478417ec264SJakub Kicinski        return ethtool(self, "-K", args, fail=fail)
479417ec264SJakub Kicinski
480417ec264SJakub Kicinski################################################################################
481417ec264SJakub Kicinskidef clean_up():
4827fedbb7cSJakub Kicinski    global files, netns, devs
4837fedbb7cSJakub Kicinski
484417ec264SJakub Kicinski    for dev in devs:
485417ec264SJakub Kicinski        dev.remove()
486417ec264SJakub Kicinski    for f in files:
487417ec264SJakub Kicinski        cmd("rm -f %s" % (f))
488752d7b45SJakub Kicinski    for ns in netns:
489752d7b45SJakub Kicinski        cmd("ip netns delete %s" % (ns))
4907fedbb7cSJakub Kicinski    files = []
4917fedbb7cSJakub Kicinski    netns = []
492417ec264SJakub Kicinski
493417ec264SJakub Kicinskidef pin_prog(file_name, idx=0):
494417ec264SJakub Kicinski    progs = bpftool_prog_list(expected=(idx + 1))
495417ec264SJakub Kicinski    prog = progs[idx]
496417ec264SJakub Kicinski    bpftool("prog pin id %d %s" % (prog["id"], file_name))
497417ec264SJakub Kicinski    files.append(file_name)
498417ec264SJakub Kicinski
499417ec264SJakub Kicinski    return file_name, bpf_pinned(file_name)
500417ec264SJakub Kicinski
5017fedbb7cSJakub Kicinskidef pin_map(file_name, idx=0, expected=1):
5027fedbb7cSJakub Kicinski    maps = bpftool_map_list(expected=expected)
5037fedbb7cSJakub Kicinski    m = maps[idx]
5047fedbb7cSJakub Kicinski    bpftool("map pin id %d %s" % (m["id"], file_name))
5057fedbb7cSJakub Kicinski    files.append(file_name)
5067fedbb7cSJakub Kicinski
5077fedbb7cSJakub Kicinski    return file_name, bpf_pinned(file_name)
5087fedbb7cSJakub Kicinski
5097fedbb7cSJakub Kicinskidef check_dev_info_removed(prog_file=None, map_file=None):
510752d7b45SJakub Kicinski    bpftool_prog_list(expected=0)
5117fedbb7cSJakub Kicinski    ret, err = bpftool("prog show pin %s" % (prog_file), fail=False)
512752d7b45SJakub Kicinski    fail(ret == 0, "Showing prog with removed device did not fail")
513752d7b45SJakub Kicinski    fail(err["error"].find("No such device") == -1,
514752d7b45SJakub Kicinski         "Showing prog with removed device expected ENODEV, error is %s" %
515752d7b45SJakub Kicinski         (err["error"]))
5167fedbb7cSJakub Kicinski
5177fedbb7cSJakub Kicinski    bpftool_map_list(expected=0)
5187fedbb7cSJakub Kicinski    ret, err = bpftool("map show pin %s" % (map_file), fail=False)
5197fedbb7cSJakub Kicinski    fail(ret == 0, "Showing map with removed device did not fail")
5207fedbb7cSJakub Kicinski    fail(err["error"].find("No such device") == -1,
5217fedbb7cSJakub Kicinski         "Showing map with removed device expected ENODEV, error is %s" %
5227fedbb7cSJakub Kicinski         (err["error"]))
5237fedbb7cSJakub Kicinski
5247fedbb7cSJakub Kicinskidef check_dev_info(other_ns, ns, prog_file=None, map_file=None, removed=False):
5257fedbb7cSJakub Kicinski    progs = bpftool_prog_list(expected=1, ns=ns)
526752d7b45SJakub Kicinski    prog = progs[0]
527752d7b45SJakub Kicinski
528752d7b45SJakub Kicinski    fail("dev" not in prog.keys(), "Device parameters not reported")
529752d7b45SJakub Kicinski    dev = prog["dev"]
530752d7b45SJakub Kicinski    fail("ifindex" not in dev.keys(), "Device parameters not reported")
531752d7b45SJakub Kicinski    fail("ns_dev" not in dev.keys(), "Device parameters not reported")
532752d7b45SJakub Kicinski    fail("ns_inode" not in dev.keys(), "Device parameters not reported")
533752d7b45SJakub Kicinski
5347fedbb7cSJakub Kicinski    if not other_ns:
535752d7b45SJakub Kicinski        fail("ifname" not in dev.keys(), "Ifname not reported")
536752d7b45SJakub Kicinski        fail(dev["ifname"] != sim["ifname"],
537752d7b45SJakub Kicinski             "Ifname incorrect %s vs %s" % (dev["ifname"], sim["ifname"]))
538752d7b45SJakub Kicinski    else:
539752d7b45SJakub Kicinski        fail("ifname" in dev.keys(), "Ifname is reported for other ns")
5407fedbb7cSJakub Kicinski
5417fedbb7cSJakub Kicinski    maps = bpftool_map_list(expected=2, ns=ns)
5427fedbb7cSJakub Kicinski    for m in maps:
5437fedbb7cSJakub Kicinski        fail("dev" not in m.keys(), "Device parameters not reported")
5447fedbb7cSJakub Kicinski        fail(dev != m["dev"], "Map's device different than program's")
545752d7b45SJakub Kicinski
546caf95228SQuentin Monnetdef check_extack(output, reference, args):
547caf95228SQuentin Monnet    if skip_extack:
548caf95228SQuentin Monnet        return
549caf95228SQuentin Monnet    lines = output.split("\n")
550219f860dSJakub Kicinski    comp = len(lines) >= 2 and lines[1] == 'Error: ' + reference
551caf95228SQuentin Monnet    fail(not comp, "Missing or incorrect netlink extack message")
552caf95228SQuentin Monnet
553caf95228SQuentin Monnetdef check_extack_nsim(output, reference, args):
554219f860dSJakub Kicinski    check_extack(output, "netdevsim: " + reference, args)
555caf95228SQuentin Monnet
5562fb89a38SJakub Kicinskidef check_no_extack(res, needle):
5572fb89a38SJakub Kicinski    fail((res[1] + res[2]).count(needle) or (res[1] + res[2]).count("Warning:"),
5582fb89a38SJakub Kicinski         "Found '%s' in command output, leaky extack?" % (needle))
5592fb89a38SJakub Kicinski
5609045bdc8SQuentin Monnetdef check_verifier_log(output, reference):
5619045bdc8SQuentin Monnet    lines = output.split("\n")
5629045bdc8SQuentin Monnet    for l in reversed(lines):
5639045bdc8SQuentin Monnet        if l == reference:
5649045bdc8SQuentin Monnet            return
5659045bdc8SQuentin Monnet    fail(True, "Missing or incorrect message from netdevsim in verifier log")
5669045bdc8SQuentin Monnet
5672fb89a38SJakub Kicinskidef test_spurios_extack(sim, obj, skip_hw, needle):
5682fb89a38SJakub Kicinski    res = sim.cls_bpf_add_filter(obj, prio=1, handle=1, skip_hw=skip_hw,
5692fb89a38SJakub Kicinski                                 include_stderr=True)
5702fb89a38SJakub Kicinski    check_no_extack(res, needle)
5712fb89a38SJakub Kicinski    res = sim.cls_bpf_add_filter(obj, op="replace", prio=1, handle=1,
5722fb89a38SJakub Kicinski                                 skip_hw=skip_hw, include_stderr=True)
5732fb89a38SJakub Kicinski    check_no_extack(res, needle)
5742fb89a38SJakub Kicinski    res = sim.cls_filter_op(op="delete", prio=1, handle=1, cls="bpf",
5752fb89a38SJakub Kicinski                            include_stderr=True)
5762fb89a38SJakub Kicinski    check_no_extack(res, needle)
5772fb89a38SJakub Kicinski
5782fb89a38SJakub Kicinski
579417ec264SJakub Kicinski# Parse command line
580417ec264SJakub Kicinskiparser = argparse.ArgumentParser()
581417ec264SJakub Kicinskiparser.add_argument("--log", help="output verbose log to given file")
582417ec264SJakub Kicinskiargs = parser.parse_args()
583417ec264SJakub Kicinskiif args.log:
584417ec264SJakub Kicinski    logfile = open(args.log, 'w+')
585417ec264SJakub Kicinski    logfile.write("# -*-Org-*-")
586417ec264SJakub Kicinski
587417ec264SJakub Kicinskilog("Prepare...", "", level=1)
588417ec264SJakub Kicinskilog_level_inc()
589417ec264SJakub Kicinski
590417ec264SJakub Kicinski# Check permissions
591417ec264SJakub Kicinskiskip(os.getuid() != 0, "test must be run as root")
592417ec264SJakub Kicinski
593417ec264SJakub Kicinski# Check tools
594417ec264SJakub Kicinskiret, progs = bpftool("prog", fail=False)
595417ec264SJakub Kicinskiskip(ret != 0, "bpftool not installed")
59647cf52a2SJakub Kicinskibase_progs = progs
59747cf52a2SJakub Kicinski_, base_maps = bpftool("map")
598417ec264SJakub Kicinski
599417ec264SJakub Kicinski# Check netdevsim
600417ec264SJakub Kicinskiret, out = cmd("modprobe netdevsim", fail=False)
601417ec264SJakub Kicinskiskip(ret != 0, "netdevsim module could not be loaded")
602417ec264SJakub Kicinski
603417ec264SJakub Kicinski# Check debugfs
604417ec264SJakub Kicinski_, out = cmd("mount")
605417ec264SJakub Kicinskiif out.find("/sys/kernel/debug type debugfs") == -1:
606417ec264SJakub Kicinski    cmd("mount -t debugfs none /sys/kernel/debug")
607417ec264SJakub Kicinski
608417ec264SJakub Kicinski# Check samples are compiled
6097fedbb7cSJakub Kicinskisamples = ["sample_ret0.o", "sample_map_ret0.o"]
610417ec264SJakub Kicinskifor s in samples:
611417ec264SJakub Kicinski    ret, out = cmd("ls %s/%s" % (bpf_test_dir, s), fail=False)
612417ec264SJakub Kicinski    skip(ret != 0, "sample %s/%s not found, please compile it" %
613417ec264SJakub Kicinski         (bpf_test_dir, s))
614417ec264SJakub Kicinski
615caf95228SQuentin Monnet# Check if iproute2 is built with libmnl (needed by extack support)
616caf95228SQuentin Monnet_, _, err = cmd("tc qdisc delete dev lo handle 0",
617caf95228SQuentin Monnet                fail=False, include_stderr=True)
618caf95228SQuentin Monnetif err.find("Error: Failed to find qdisc with specified handle.") == -1:
619caf95228SQuentin Monnet    print("Warning: no extack message in iproute2 output, libmnl missing?")
620caf95228SQuentin Monnet    log("Warning: no extack message in iproute2 output, libmnl missing?", "")
621caf95228SQuentin Monnet    skip_extack = True
622caf95228SQuentin Monnet
623752d7b45SJakub Kicinski# Check if net namespaces seem to work
624752d7b45SJakub Kicinskins = mknetns()
625752d7b45SJakub Kicinskiskip(ns is None, "Could not create a net namespace")
626752d7b45SJakub Kicinskicmd("ip netns delete %s" % (ns))
627752d7b45SJakub Kicinskinetns = []
628752d7b45SJakub Kicinski
629417ec264SJakub Kicinskitry:
630417ec264SJakub Kicinski    obj = bpf_obj("sample_ret0.o")
631417ec264SJakub Kicinski    bytecode = bpf_bytecode("1,6 0 0 4294967295,")
632417ec264SJakub Kicinski
633417ec264SJakub Kicinski    start_test("Test destruction of generic XDP...")
634417ec264SJakub Kicinski    sim = NetdevSim()
635417ec264SJakub Kicinski    sim.set_xdp(obj, "generic")
636417ec264SJakub Kicinski    sim.remove()
637417ec264SJakub Kicinski    bpftool_prog_list_wait(expected=0)
638417ec264SJakub Kicinski
639417ec264SJakub Kicinski    sim = NetdevSim()
640417ec264SJakub Kicinski    sim.tc_add_ingress()
641417ec264SJakub Kicinski
642417ec264SJakub Kicinski    start_test("Test TC non-offloaded...")
643417ec264SJakub Kicinski    ret, _ = sim.cls_bpf_add_filter(obj, skip_hw=True, fail=False)
644417ec264SJakub Kicinski    fail(ret != 0, "Software TC filter did not load")
645417ec264SJakub Kicinski
646417ec264SJakub Kicinski    start_test("Test TC non-offloaded isn't getting bound...")
647417ec264SJakub Kicinski    ret, _ = sim.cls_bpf_add_filter(obj, fail=False)
648417ec264SJakub Kicinski    fail(ret != 0, "Software TC filter did not load")
649417ec264SJakub Kicinski    sim.dfs_get_bound_progs(expected=0)
650417ec264SJakub Kicinski
651417ec264SJakub Kicinski    sim.tc_flush_filters()
652417ec264SJakub Kicinski
653417ec264SJakub Kicinski    start_test("Test TC offloads are off by default...")
654caf95228SQuentin Monnet    ret, _, err = sim.cls_bpf_add_filter(obj, skip_sw=True,
655caf95228SQuentin Monnet                                         fail=False, include_stderr=True)
656417ec264SJakub Kicinski    fail(ret == 0, "TC filter loaded without enabling TC offloads")
657219f860dSJakub Kicinski    check_extack(err, "TC offload is disabled on net device.", args)
658417ec264SJakub Kicinski    sim.wait_for_flush()
659417ec264SJakub Kicinski
660417ec264SJakub Kicinski    sim.set_ethtool_tc_offloads(True)
661417ec264SJakub Kicinski    sim.dfs["bpf_tc_non_bound_accept"] = "Y"
662417ec264SJakub Kicinski
663417ec264SJakub Kicinski    start_test("Test TC offload by default...")
664417ec264SJakub Kicinski    ret, _ = sim.cls_bpf_add_filter(obj, fail=False)
665417ec264SJakub Kicinski    fail(ret != 0, "Software TC filter did not load")
666417ec264SJakub Kicinski    sim.dfs_get_bound_progs(expected=0)
667417ec264SJakub Kicinski    ingress = sim.tc_show_ingress(expected=1)
668417ec264SJakub Kicinski    fltr = ingress[0]
669417ec264SJakub Kicinski    fail(not fltr["in_hw"], "Filter not offloaded by default")
670417ec264SJakub Kicinski
671417ec264SJakub Kicinski    sim.tc_flush_filters()
672417ec264SJakub Kicinski
673417ec264SJakub Kicinski    start_test("Test TC cBPF bytcode tries offload by default...")
674417ec264SJakub Kicinski    ret, _ = sim.cls_bpf_add_filter(bytecode, fail=False)
675417ec264SJakub Kicinski    fail(ret != 0, "Software TC filter did not load")
676417ec264SJakub Kicinski    sim.dfs_get_bound_progs(expected=0)
677417ec264SJakub Kicinski    ingress = sim.tc_show_ingress(expected=1)
678417ec264SJakub Kicinski    fltr = ingress[0]
679417ec264SJakub Kicinski    fail(not fltr["in_hw"], "Bytecode not offloaded by default")
680417ec264SJakub Kicinski
681417ec264SJakub Kicinski    sim.tc_flush_filters()
682417ec264SJakub Kicinski    sim.dfs["bpf_tc_non_bound_accept"] = "N"
683417ec264SJakub Kicinski
684417ec264SJakub Kicinski    start_test("Test TC cBPF unbound bytecode doesn't offload...")
685caf95228SQuentin Monnet    ret, _, err = sim.cls_bpf_add_filter(bytecode, skip_sw=True,
686caf95228SQuentin Monnet                                         fail=False, include_stderr=True)
687417ec264SJakub Kicinski    fail(ret == 0, "TC bytecode loaded for offload")
688caf95228SQuentin Monnet    check_extack_nsim(err, "netdevsim configured to reject unbound programs.",
689caf95228SQuentin Monnet                      args)
690417ec264SJakub Kicinski    sim.wait_for_flush()
691417ec264SJakub Kicinski
692baf6a07eSJakub Kicinski    start_test("Test non-0 chain offload...")
693baf6a07eSJakub Kicinski    ret, _, err = sim.cls_bpf_add_filter(obj, chain=1, prio=1, handle=1,
694baf6a07eSJakub Kicinski                                         skip_sw=True,
695baf6a07eSJakub Kicinski                                         fail=False, include_stderr=True)
696baf6a07eSJakub Kicinski    fail(ret == 0, "Offloaded a filter to chain other than 0")
697219f860dSJakub Kicinski    check_extack(err, "Driver supports only offload of chain 0.", args)
698baf6a07eSJakub Kicinski    sim.tc_flush_filters()
699baf6a07eSJakub Kicinski
7006d2d58f1SJakub Kicinski    start_test("Test TC replace...")
7016d2d58f1SJakub Kicinski    sim.cls_bpf_add_filter(obj, prio=1, handle=1)
7026d2d58f1SJakub Kicinski    sim.cls_bpf_add_filter(obj, op="replace", prio=1, handle=1)
7036d2d58f1SJakub Kicinski    sim.cls_filter_op(op="delete", prio=1, handle=1, cls="bpf")
7046d2d58f1SJakub Kicinski
7056d2d58f1SJakub Kicinski    sim.cls_bpf_add_filter(obj, prio=1, handle=1, skip_sw=True)
7066d2d58f1SJakub Kicinski    sim.cls_bpf_add_filter(obj, op="replace", prio=1, handle=1, skip_sw=True)
7076d2d58f1SJakub Kicinski    sim.cls_filter_op(op="delete", prio=1, handle=1, cls="bpf")
7086d2d58f1SJakub Kicinski
7096d2d58f1SJakub Kicinski    sim.cls_bpf_add_filter(obj, prio=1, handle=1, skip_hw=True)
7106d2d58f1SJakub Kicinski    sim.cls_bpf_add_filter(obj, op="replace", prio=1, handle=1, skip_hw=True)
7116d2d58f1SJakub Kicinski    sim.cls_filter_op(op="delete", prio=1, handle=1, cls="bpf")
7126d2d58f1SJakub Kicinski
7136d2d58f1SJakub Kicinski    start_test("Test TC replace bad flags...")
7146d2d58f1SJakub Kicinski    for i in range(3):
7156d2d58f1SJakub Kicinski        for j in range(3):
7166d2d58f1SJakub Kicinski            ret, _ = sim.cls_bpf_add_filter(obj, op="replace", prio=1, handle=1,
7176d2d58f1SJakub Kicinski                                            skip_sw=(j == 1), skip_hw=(j == 2),
7186d2d58f1SJakub Kicinski                                            fail=False)
7196d2d58f1SJakub Kicinski            fail(bool(ret) != bool(j),
7206d2d58f1SJakub Kicinski                 "Software TC incorrect load in replace test, iteration %d" %
7216d2d58f1SJakub Kicinski                 (j))
7226d2d58f1SJakub Kicinski        sim.cls_filter_op(op="delete", prio=1, handle=1, cls="bpf")
7236d2d58f1SJakub Kicinski
7242fb89a38SJakub Kicinski    start_test("Test spurious extack from the driver...")
7252fb89a38SJakub Kicinski    test_spurios_extack(sim, obj, False, "netdevsim")
7262fb89a38SJakub Kicinski    test_spurios_extack(sim, obj, True, "netdevsim")
7272fb89a38SJakub Kicinski
7282fb89a38SJakub Kicinski    sim.set_ethtool_tc_offloads(False)
7292fb89a38SJakub Kicinski
7302fb89a38SJakub Kicinski    test_spurios_extack(sim, obj, False, "TC offload is disabled")
7312fb89a38SJakub Kicinski    test_spurios_extack(sim, obj, True, "TC offload is disabled")
7322fb89a38SJakub Kicinski
7332fb89a38SJakub Kicinski    sim.set_ethtool_tc_offloads(True)
7342fb89a38SJakub Kicinski
7356d2d58f1SJakub Kicinski    sim.tc_flush_filters()
7366d2d58f1SJakub Kicinski
737417ec264SJakub Kicinski    start_test("Test TC offloads work...")
7389045bdc8SQuentin Monnet    ret, _, err = sim.cls_bpf_add_filter(obj, verbose=True, skip_sw=True,
7399045bdc8SQuentin Monnet                                         fail=False, include_stderr=True)
740417ec264SJakub Kicinski    fail(ret != 0, "TC filter did not load with TC offloads enabled")
7419045bdc8SQuentin Monnet    check_verifier_log(err, "[netdevsim] Hello from netdevsim!")
742417ec264SJakub Kicinski
743417ec264SJakub Kicinski    start_test("Test TC offload basics...")
744417ec264SJakub Kicinski    dfs = sim.dfs_get_bound_progs(expected=1)
745417ec264SJakub Kicinski    progs = bpftool_prog_list(expected=1)
746417ec264SJakub Kicinski    ingress = sim.tc_show_ingress(expected=1)
747417ec264SJakub Kicinski
748417ec264SJakub Kicinski    dprog = dfs[0]
749417ec264SJakub Kicinski    prog = progs[0]
750417ec264SJakub Kicinski    fltr = ingress[0]
751417ec264SJakub Kicinski    fail(fltr["skip_hw"], "TC does reports 'skip_hw' on offloaded filter")
752417ec264SJakub Kicinski    fail(not fltr["in_hw"], "TC does not report 'in_hw' for offloaded filter")
753417ec264SJakub Kicinski    fail(not fltr["skip_sw"], "TC does not report 'skip_sw' back")
754417ec264SJakub Kicinski
755417ec264SJakub Kicinski    start_test("Test TC offload is device-bound...")
756417ec264SJakub Kicinski    fail(str(prog["id"]) != fltr["id"], "Program IDs don't match")
757417ec264SJakub Kicinski    fail(prog["tag"] != fltr["tag"], "Program tags don't match")
758417ec264SJakub Kicinski    fail(fltr["id"] != dprog["id"], "Program IDs don't match")
759417ec264SJakub Kicinski    fail(dprog["state"] != "xlated", "Offloaded program state not translated")
760417ec264SJakub Kicinski    fail(dprog["loaded"] != "Y", "Offloaded program is not loaded")
761417ec264SJakub Kicinski
762417ec264SJakub Kicinski    start_test("Test disabling TC offloads is rejected while filters installed...")
763417ec264SJakub Kicinski    ret, _ = sim.set_ethtool_tc_offloads(False, fail=False)
764417ec264SJakub Kicinski    fail(ret == 0, "Driver should refuse to disable TC offloads with filters installed...")
765417ec264SJakub Kicinski
766417ec264SJakub Kicinski    start_test("Test qdisc removal frees things...")
767417ec264SJakub Kicinski    sim.tc_flush_filters()
768417ec264SJakub Kicinski    sim.tc_show_ingress(expected=0)
769417ec264SJakub Kicinski
770417ec264SJakub Kicinski    start_test("Test disabling TC offloads is OK without filters...")
771417ec264SJakub Kicinski    ret, _ = sim.set_ethtool_tc_offloads(False, fail=False)
772417ec264SJakub Kicinski    fail(ret != 0,
773417ec264SJakub Kicinski         "Driver refused to disable TC offloads without filters installed...")
774417ec264SJakub Kicinski
775417ec264SJakub Kicinski    sim.set_ethtool_tc_offloads(True)
776417ec264SJakub Kicinski
777417ec264SJakub Kicinski    start_test("Test destroying device gets rid of TC filters...")
778417ec264SJakub Kicinski    sim.cls_bpf_add_filter(obj, skip_sw=True)
779417ec264SJakub Kicinski    sim.remove()
780417ec264SJakub Kicinski    bpftool_prog_list_wait(expected=0)
781417ec264SJakub Kicinski
782417ec264SJakub Kicinski    sim = NetdevSim()
783417ec264SJakub Kicinski    sim.set_ethtool_tc_offloads(True)
784417ec264SJakub Kicinski
785417ec264SJakub Kicinski    start_test("Test destroying device gets rid of XDP...")
786417ec264SJakub Kicinski    sim.set_xdp(obj, "offload")
787417ec264SJakub Kicinski    sim.remove()
788417ec264SJakub Kicinski    bpftool_prog_list_wait(expected=0)
789417ec264SJakub Kicinski
790417ec264SJakub Kicinski    sim = NetdevSim()
791417ec264SJakub Kicinski    sim.set_ethtool_tc_offloads(True)
792417ec264SJakub Kicinski
793417ec264SJakub Kicinski    start_test("Test XDP prog reporting...")
794417ec264SJakub Kicinski    sim.set_xdp(obj, "drv")
795417ec264SJakub Kicinski    ipl = sim.ip_link_show(xdp=True)
796417ec264SJakub Kicinski    progs = bpftool_prog_list(expected=1)
797417ec264SJakub Kicinski    fail(ipl["xdp"]["prog"]["id"] != progs[0]["id"],
798417ec264SJakub Kicinski         "Loaded program has wrong ID")
799417ec264SJakub Kicinski
800417ec264SJakub Kicinski    start_test("Test XDP prog replace without force...")
801417ec264SJakub Kicinski    ret, _ = sim.set_xdp(obj, "drv", fail=False)
802417ec264SJakub Kicinski    fail(ret == 0, "Replaced XDP program without -force")
803417ec264SJakub Kicinski    sim.wait_for_flush(total=1)
804417ec264SJakub Kicinski
805417ec264SJakub Kicinski    start_test("Test XDP prog replace with force...")
806417ec264SJakub Kicinski    ret, _ = sim.set_xdp(obj, "drv", force=True, fail=False)
807417ec264SJakub Kicinski    fail(ret != 0, "Could not replace XDP program with -force")
808417ec264SJakub Kicinski    bpftool_prog_list_wait(expected=1)
809417ec264SJakub Kicinski    ipl = sim.ip_link_show(xdp=True)
810417ec264SJakub Kicinski    progs = bpftool_prog_list(expected=1)
811417ec264SJakub Kicinski    fail(ipl["xdp"]["prog"]["id"] != progs[0]["id"],
812417ec264SJakub Kicinski         "Loaded program has wrong ID")
813752d7b45SJakub Kicinski    fail("dev" in progs[0].keys(),
814752d7b45SJakub Kicinski         "Device parameters reported for non-offloaded program")
815417ec264SJakub Kicinski
816417ec264SJakub Kicinski    start_test("Test XDP prog replace with bad flags...")
817caf95228SQuentin Monnet    ret, _, err = sim.set_xdp(obj, "offload", force=True,
818caf95228SQuentin Monnet                              fail=False, include_stderr=True)
819417ec264SJakub Kicinski    fail(ret == 0, "Replaced XDP program with a program in different mode")
820caf95228SQuentin Monnet    check_extack_nsim(err, "program loaded with different flags.", args)
821caf95228SQuentin Monnet    ret, _, err = sim.set_xdp(obj, "", force=True,
822caf95228SQuentin Monnet                              fail=False, include_stderr=True)
823417ec264SJakub Kicinski    fail(ret == 0, "Replaced XDP program with a program in different mode")
82405296620SJakub Kicinski    check_extack(err, "program loaded with different flags.", args)
825417ec264SJakub Kicinski
826417ec264SJakub Kicinski    start_test("Test XDP prog remove with bad flags...")
827caf95228SQuentin Monnet    ret, _, err = sim.unset_xdp("offload", force=True,
828caf95228SQuentin Monnet                                fail=False, include_stderr=True)
829417ec264SJakub Kicinski    fail(ret == 0, "Removed program with a bad mode mode")
830caf95228SQuentin Monnet    check_extack_nsim(err, "program loaded with different flags.", args)
831caf95228SQuentin Monnet    ret, _, err = sim.unset_xdp("", force=True,
832caf95228SQuentin Monnet                                fail=False, include_stderr=True)
8337479efc7SJakub Kicinski    fail(ret == 0, "Removed program with a bad mode")
83405296620SJakub Kicinski    check_extack(err, "program loaded with different flags.", args)
835417ec264SJakub Kicinski
836417ec264SJakub Kicinski    start_test("Test MTU restrictions...")
837417ec264SJakub Kicinski    ret, _ = sim.set_mtu(9000, fail=False)
838417ec264SJakub Kicinski    fail(ret == 0,
839417ec264SJakub Kicinski         "Driver should refuse to increase MTU to 9000 with XDP loaded...")
840417ec264SJakub Kicinski    sim.unset_xdp("drv")
841417ec264SJakub Kicinski    bpftool_prog_list_wait(expected=0)
842417ec264SJakub Kicinski    sim.set_mtu(9000)
843caf95228SQuentin Monnet    ret, _, err = sim.set_xdp(obj, "drv", fail=False, include_stderr=True)
844417ec264SJakub Kicinski    fail(ret == 0, "Driver should refuse to load program with MTU of 9000...")
845caf95228SQuentin Monnet    check_extack_nsim(err, "MTU too large w/ XDP enabled.", args)
846417ec264SJakub Kicinski    sim.set_mtu(1500)
847417ec264SJakub Kicinski
848417ec264SJakub Kicinski    sim.wait_for_flush()
849417ec264SJakub Kicinski    start_test("Test XDP offload...")
8509045bdc8SQuentin Monnet    _, _, err = sim.set_xdp(obj, "offload", verbose=True, include_stderr=True)
851417ec264SJakub Kicinski    ipl = sim.ip_link_show(xdp=True)
852417ec264SJakub Kicinski    link_xdp = ipl["xdp"]["prog"]
853417ec264SJakub Kicinski    progs = bpftool_prog_list(expected=1)
854417ec264SJakub Kicinski    prog = progs[0]
855417ec264SJakub Kicinski    fail(link_xdp["id"] != prog["id"], "Loaded program has wrong ID")
8569045bdc8SQuentin Monnet    check_verifier_log(err, "[netdevsim] Hello from netdevsim!")
857417ec264SJakub Kicinski
858417ec264SJakub Kicinski    start_test("Test XDP offload is device bound...")
859417ec264SJakub Kicinski    dfs = sim.dfs_get_bound_progs(expected=1)
860417ec264SJakub Kicinski    dprog = dfs[0]
861417ec264SJakub Kicinski
862417ec264SJakub Kicinski    fail(prog["id"] != link_xdp["id"], "Program IDs don't match")
863417ec264SJakub Kicinski    fail(prog["tag"] != link_xdp["tag"], "Program tags don't match")
864417ec264SJakub Kicinski    fail(str(link_xdp["id"]) != dprog["id"], "Program IDs don't match")
865417ec264SJakub Kicinski    fail(dprog["state"] != "xlated", "Offloaded program state not translated")
866417ec264SJakub Kicinski    fail(dprog["loaded"] != "Y", "Offloaded program is not loaded")
867417ec264SJakub Kicinski
868417ec264SJakub Kicinski    start_test("Test removing XDP program many times...")
869417ec264SJakub Kicinski    sim.unset_xdp("offload")
870417ec264SJakub Kicinski    sim.unset_xdp("offload")
871417ec264SJakub Kicinski    sim.unset_xdp("drv")
872417ec264SJakub Kicinski    sim.unset_xdp("drv")
873417ec264SJakub Kicinski    sim.unset_xdp("")
874417ec264SJakub Kicinski    sim.unset_xdp("")
875417ec264SJakub Kicinski    bpftool_prog_list_wait(expected=0)
876417ec264SJakub Kicinski
877417ec264SJakub Kicinski    start_test("Test attempt to use a program for a wrong device...")
878417ec264SJakub Kicinski    sim2 = NetdevSim()
879417ec264SJakub Kicinski    sim2.set_xdp(obj, "offload")
880417ec264SJakub Kicinski    pin_file, pinned = pin_prog("/sys/fs/bpf/tmp")
881417ec264SJakub Kicinski
882caf95228SQuentin Monnet    ret, _, err = sim.set_xdp(pinned, "offload",
883caf95228SQuentin Monnet                              fail=False, include_stderr=True)
884417ec264SJakub Kicinski    fail(ret == 0, "Pinned program loaded for a different device accepted")
885caf95228SQuentin Monnet    check_extack_nsim(err, "program bound to different dev.", args)
886417ec264SJakub Kicinski    sim2.remove()
887caf95228SQuentin Monnet    ret, _, err = sim.set_xdp(pinned, "offload",
888caf95228SQuentin Monnet                              fail=False, include_stderr=True)
889417ec264SJakub Kicinski    fail(ret == 0, "Pinned program loaded for a removed device accepted")
890caf95228SQuentin Monnet    check_extack_nsim(err, "xdpoffload of non-bound program.", args)
891417ec264SJakub Kicinski    rm(pin_file)
892417ec264SJakub Kicinski    bpftool_prog_list_wait(expected=0)
893417ec264SJakub Kicinski
894417ec264SJakub Kicinski    start_test("Test mixing of TC and XDP...")
895417ec264SJakub Kicinski    sim.tc_add_ingress()
896417ec264SJakub Kicinski    sim.set_xdp(obj, "offload")
897caf95228SQuentin Monnet    ret, _, err = sim.cls_bpf_add_filter(obj, skip_sw=True,
898caf95228SQuentin Monnet                                         fail=False, include_stderr=True)
899417ec264SJakub Kicinski    fail(ret == 0, "Loading TC when XDP active should fail")
900caf95228SQuentin Monnet    check_extack_nsim(err, "driver and netdev offload states mismatch.", args)
901417ec264SJakub Kicinski    sim.unset_xdp("offload")
902417ec264SJakub Kicinski    sim.wait_for_flush()
903417ec264SJakub Kicinski
904417ec264SJakub Kicinski    sim.cls_bpf_add_filter(obj, skip_sw=True)
905caf95228SQuentin Monnet    ret, _, err = sim.set_xdp(obj, "offload", fail=False, include_stderr=True)
906417ec264SJakub Kicinski    fail(ret == 0, "Loading XDP when TC active should fail")
907caf95228SQuentin Monnet    check_extack_nsim(err, "TC program is already loaded.", args)
908417ec264SJakub Kicinski
909417ec264SJakub Kicinski    start_test("Test binding TC from pinned...")
910417ec264SJakub Kicinski    pin_file, pinned = pin_prog("/sys/fs/bpf/tmp")
911417ec264SJakub Kicinski    sim.tc_flush_filters(bound=1, total=1)
912417ec264SJakub Kicinski    sim.cls_bpf_add_filter(pinned, da=True, skip_sw=True)
913417ec264SJakub Kicinski    sim.tc_flush_filters(bound=1, total=1)
914417ec264SJakub Kicinski
915417ec264SJakub Kicinski    start_test("Test binding XDP from pinned...")
916417ec264SJakub Kicinski    sim.set_xdp(obj, "offload")
917417ec264SJakub Kicinski    pin_file, pinned = pin_prog("/sys/fs/bpf/tmp2", idx=1)
918417ec264SJakub Kicinski
919417ec264SJakub Kicinski    sim.set_xdp(pinned, "offload", force=True)
920417ec264SJakub Kicinski    sim.unset_xdp("offload")
921417ec264SJakub Kicinski    sim.set_xdp(pinned, "offload", force=True)
922417ec264SJakub Kicinski    sim.unset_xdp("offload")
923417ec264SJakub Kicinski
924417ec264SJakub Kicinski    start_test("Test offload of wrong type fails...")
925417ec264SJakub Kicinski    ret, _ = sim.cls_bpf_add_filter(pinned, da=True, skip_sw=True, fail=False)
926417ec264SJakub Kicinski    fail(ret == 0, "Managed to attach XDP program to TC")
927417ec264SJakub Kicinski
928417ec264SJakub Kicinski    start_test("Test asking for TC offload of two filters...")
929417ec264SJakub Kicinski    sim.cls_bpf_add_filter(obj, da=True, skip_sw=True)
930caf95228SQuentin Monnet    ret, _, err = sim.cls_bpf_add_filter(obj, da=True, skip_sw=True,
931caf95228SQuentin Monnet                                         fail=False, include_stderr=True)
932fba961abSDavid S. Miller    fail(ret == 0, "Managed to offload two TC filters at the same time")
933caf95228SQuentin Monnet    check_extack_nsim(err, "driver and netdev offload states mismatch.", args)
934417ec264SJakub Kicinski
935417ec264SJakub Kicinski    sim.tc_flush_filters(bound=2, total=2)
936417ec264SJakub Kicinski
937417ec264SJakub Kicinski    start_test("Test if netdev removal waits for translation...")
938417ec264SJakub Kicinski    delay_msec = 500
939417ec264SJakub Kicinski    sim.dfs["bpf_bind_verifier_delay"] = delay_msec
940417ec264SJakub Kicinski    start = time.time()
941417ec264SJakub Kicinski    cmd_line = "tc filter add dev %s ingress bpf %s da skip_sw" % \
942417ec264SJakub Kicinski               (sim['ifname'], obj)
943417ec264SJakub Kicinski    tc_proc = cmd(cmd_line, background=True, fail=False)
944417ec264SJakub Kicinski    # Wait for the verifier to start
945417ec264SJakub Kicinski    while sim.dfs_num_bound_progs() <= 2:
946417ec264SJakub Kicinski        pass
947417ec264SJakub Kicinski    sim.remove()
948417ec264SJakub Kicinski    end = time.time()
949417ec264SJakub Kicinski    ret, _ = cmd_result(tc_proc, fail=False)
950417ec264SJakub Kicinski    time_diff = end - start
951417ec264SJakub Kicinski    log("Time", "start:\t%s\nend:\t%s\ndiff:\t%s" % (start, end, time_diff))
952417ec264SJakub Kicinski
953417ec264SJakub Kicinski    fail(ret == 0, "Managed to load TC filter on a unregistering device")
954417ec264SJakub Kicinski    delay_sec = delay_msec * 0.001
955417ec264SJakub Kicinski    fail(time_diff < delay_sec, "Removal process took %s, expected %s" %
956417ec264SJakub Kicinski         (time_diff, delay_sec))
957417ec264SJakub Kicinski
958752d7b45SJakub Kicinski    # Remove all pinned files and reinstantiate the netdev
959752d7b45SJakub Kicinski    clean_up()
960752d7b45SJakub Kicinski    bpftool_prog_list_wait(expected=0)
961752d7b45SJakub Kicinski
962752d7b45SJakub Kicinski    sim = NetdevSim()
9637fedbb7cSJakub Kicinski    map_obj = bpf_obj("sample_map_ret0.o")
9647fedbb7cSJakub Kicinski    start_test("Test loading program with maps...")
9657fedbb7cSJakub Kicinski    sim.set_xdp(map_obj, "offload", JSON=False) # map fixup msg breaks JSON
966752d7b45SJakub Kicinski
967752d7b45SJakub Kicinski    start_test("Test bpftool bound info reporting (own ns)...")
968752d7b45SJakub Kicinski    check_dev_info(False, "")
969752d7b45SJakub Kicinski
970752d7b45SJakub Kicinski    start_test("Test bpftool bound info reporting (other ns)...")
971752d7b45SJakub Kicinski    ns = mknetns()
972752d7b45SJakub Kicinski    sim.set_ns(ns)
973752d7b45SJakub Kicinski    check_dev_info(True, "")
974752d7b45SJakub Kicinski
975752d7b45SJakub Kicinski    start_test("Test bpftool bound info reporting (remote ns)...")
976752d7b45SJakub Kicinski    check_dev_info(False, ns)
977752d7b45SJakub Kicinski
978752d7b45SJakub Kicinski    start_test("Test bpftool bound info reporting (back to own ns)...")
979752d7b45SJakub Kicinski    sim.set_ns("")
980752d7b45SJakub Kicinski    check_dev_info(False, "")
981752d7b45SJakub Kicinski
9827fedbb7cSJakub Kicinski    prog_file, _ = pin_prog("/sys/fs/bpf/tmp_prog")
9837fedbb7cSJakub Kicinski    map_file, _ = pin_map("/sys/fs/bpf/tmp_map", idx=1, expected=2)
984752d7b45SJakub Kicinski    sim.remove()
985752d7b45SJakub Kicinski
986752d7b45SJakub Kicinski    start_test("Test bpftool bound info reporting (removed dev)...")
9877fedbb7cSJakub Kicinski    check_dev_info_removed(prog_file=prog_file, map_file=map_file)
9887fedbb7cSJakub Kicinski
9897fedbb7cSJakub Kicinski    # Remove all pinned files and reinstantiate the netdev
9907fedbb7cSJakub Kicinski    clean_up()
9917fedbb7cSJakub Kicinski    bpftool_prog_list_wait(expected=0)
9927fedbb7cSJakub Kicinski
9937fedbb7cSJakub Kicinski    sim = NetdevSim()
9947fedbb7cSJakub Kicinski
9957fedbb7cSJakub Kicinski    start_test("Test map update (no flags)...")
9967fedbb7cSJakub Kicinski    sim.set_xdp(map_obj, "offload", JSON=False) # map fixup msg breaks JSON
9977fedbb7cSJakub Kicinski    maps = bpftool_map_list(expected=2)
9987fedbb7cSJakub Kicinski    array = maps[0] if maps[0]["type"] == "array" else maps[1]
9997fedbb7cSJakub Kicinski    htab = maps[0] if maps[0]["type"] == "hash" else maps[1]
10007fedbb7cSJakub Kicinski    for m in maps:
10017fedbb7cSJakub Kicinski        for i in range(2):
10027fedbb7cSJakub Kicinski            bpftool("map update id %d key %s value %s" %
10037fedbb7cSJakub Kicinski                    (m["id"], int2str("I", i), int2str("Q", i * 3)))
10047fedbb7cSJakub Kicinski
10057fedbb7cSJakub Kicinski    for m in maps:
10067fedbb7cSJakub Kicinski        ret, _ = bpftool("map update id %d key %s value %s" %
10077fedbb7cSJakub Kicinski                         (m["id"], int2str("I", 3), int2str("Q", 3 * 3)),
10087fedbb7cSJakub Kicinski                         fail=False)
10097fedbb7cSJakub Kicinski        fail(ret == 0, "added too many entries")
10107fedbb7cSJakub Kicinski
10117fedbb7cSJakub Kicinski    start_test("Test map update (exists)...")
10127fedbb7cSJakub Kicinski    for m in maps:
10137fedbb7cSJakub Kicinski        for i in range(2):
10147fedbb7cSJakub Kicinski            bpftool("map update id %d key %s value %s exist" %
10157fedbb7cSJakub Kicinski                    (m["id"], int2str("I", i), int2str("Q", i * 3)))
10167fedbb7cSJakub Kicinski
10177fedbb7cSJakub Kicinski    for m in maps:
10187fedbb7cSJakub Kicinski        ret, err = bpftool("map update id %d key %s value %s exist" %
10197fedbb7cSJakub Kicinski                           (m["id"], int2str("I", 3), int2str("Q", 3 * 3)),
10207fedbb7cSJakub Kicinski                           fail=False)
10217fedbb7cSJakub Kicinski        fail(ret == 0, "updated non-existing key")
10227fedbb7cSJakub Kicinski        fail(err["error"].find("No such file or directory") == -1,
10237fedbb7cSJakub Kicinski             "expected ENOENT, error is '%s'" % (err["error"]))
10247fedbb7cSJakub Kicinski
10257fedbb7cSJakub Kicinski    start_test("Test map update (noexist)...")
10267fedbb7cSJakub Kicinski    for m in maps:
10277fedbb7cSJakub Kicinski        for i in range(2):
10287fedbb7cSJakub Kicinski            ret, err = bpftool("map update id %d key %s value %s noexist" %
10297fedbb7cSJakub Kicinski                               (m["id"], int2str("I", i), int2str("Q", i * 3)),
10307fedbb7cSJakub Kicinski                               fail=False)
10317fedbb7cSJakub Kicinski        fail(ret == 0, "updated existing key")
10327fedbb7cSJakub Kicinski        fail(err["error"].find("File exists") == -1,
10337fedbb7cSJakub Kicinski             "expected EEXIST, error is '%s'" % (err["error"]))
10347fedbb7cSJakub Kicinski
10357fedbb7cSJakub Kicinski    start_test("Test map dump...")
10367fedbb7cSJakub Kicinski    for m in maps:
10377fedbb7cSJakub Kicinski        _, entries = bpftool("map dump id %d" % (m["id"]))
10387fedbb7cSJakub Kicinski        for i in range(2):
10397fedbb7cSJakub Kicinski            key = str2int(entries[i]["key"])
10407fedbb7cSJakub Kicinski            fail(key != i, "expected key %d, got %d" % (key, i))
10417fedbb7cSJakub Kicinski            val = str2int(entries[i]["value"])
10427fedbb7cSJakub Kicinski            fail(val != i * 3, "expected value %d, got %d" % (val, i * 3))
10437fedbb7cSJakub Kicinski
10447fedbb7cSJakub Kicinski    start_test("Test map getnext...")
10457fedbb7cSJakub Kicinski    for m in maps:
10467fedbb7cSJakub Kicinski        _, entry = bpftool("map getnext id %d" % (m["id"]))
10477fedbb7cSJakub Kicinski        key = str2int(entry["next_key"])
10487fedbb7cSJakub Kicinski        fail(key != 0, "next key %d, expected %d" % (key, 0))
10497fedbb7cSJakub Kicinski        _, entry = bpftool("map getnext id %d key %s" %
10507fedbb7cSJakub Kicinski                           (m["id"], int2str("I", 0)))
10517fedbb7cSJakub Kicinski        key = str2int(entry["next_key"])
10527fedbb7cSJakub Kicinski        fail(key != 1, "next key %d, expected %d" % (key, 1))
10537fedbb7cSJakub Kicinski        ret, err = bpftool("map getnext id %d key %s" %
10547fedbb7cSJakub Kicinski                           (m["id"], int2str("I", 1)), fail=False)
10557fedbb7cSJakub Kicinski        fail(ret == 0, "got next key past the end of map")
10567fedbb7cSJakub Kicinski        fail(err["error"].find("No such file or directory") == -1,
10577fedbb7cSJakub Kicinski             "expected ENOENT, error is '%s'" % (err["error"]))
10587fedbb7cSJakub Kicinski
10597fedbb7cSJakub Kicinski    start_test("Test map delete (htab)...")
10607fedbb7cSJakub Kicinski    for i in range(2):
10617fedbb7cSJakub Kicinski        bpftool("map delete id %d key %s" % (htab["id"], int2str("I", i)))
10627fedbb7cSJakub Kicinski
10637fedbb7cSJakub Kicinski    start_test("Test map delete (array)...")
10647fedbb7cSJakub Kicinski    for i in range(2):
10657fedbb7cSJakub Kicinski        ret, err = bpftool("map delete id %d key %s" %
10667fedbb7cSJakub Kicinski                           (htab["id"], int2str("I", i)), fail=False)
10677fedbb7cSJakub Kicinski        fail(ret == 0, "removed entry from an array")
10687fedbb7cSJakub Kicinski        fail(err["error"].find("No such file or directory") == -1,
10697fedbb7cSJakub Kicinski             "expected ENOENT, error is '%s'" % (err["error"]))
10707fedbb7cSJakub Kicinski
10717fedbb7cSJakub Kicinski    start_test("Test map remove...")
10727fedbb7cSJakub Kicinski    sim.unset_xdp("offload")
10737fedbb7cSJakub Kicinski    bpftool_map_list_wait(expected=0)
10747fedbb7cSJakub Kicinski    sim.remove()
10757fedbb7cSJakub Kicinski
10767fedbb7cSJakub Kicinski    sim = NetdevSim()
10777fedbb7cSJakub Kicinski    sim.set_xdp(map_obj, "offload", JSON=False) # map fixup msg breaks JSON
10787fedbb7cSJakub Kicinski    sim.remove()
10797fedbb7cSJakub Kicinski    bpftool_map_list_wait(expected=0)
10807fedbb7cSJakub Kicinski
10817fedbb7cSJakub Kicinski    start_test("Test map creation fail path...")
10827fedbb7cSJakub Kicinski    sim = NetdevSim()
10837fedbb7cSJakub Kicinski    sim.dfs["bpf_map_accept"] = "N"
10847fedbb7cSJakub Kicinski    ret, _ = sim.set_xdp(map_obj, "offload", JSON=False, fail=False)
10857fedbb7cSJakub Kicinski    fail(ret == 0,
10867fedbb7cSJakub Kicinski         "netdevsim didn't refuse to create a map with offload disabled")
1087752d7b45SJakub Kicinski
1088417ec264SJakub Kicinski    print("%s: OK" % (os.path.basename(__file__)))
1089417ec264SJakub Kicinski
1090417ec264SJakub Kicinskifinally:
1091417ec264SJakub Kicinski    log("Clean up...", "", level=1)
1092417ec264SJakub Kicinski    log_level_inc()
1093417ec264SJakub Kicinski    clean_up()
1094