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") 824caf95228SQuentin Monnet check_extack_nsim(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") 834caf95228SQuentin Monnet check_extack_nsim(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