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) 166417ec264SJakub Kicinski if expected is not None: 167417ec264SJakub Kicinski if len(progs) != expected: 168417ec264SJakub Kicinski fail(True, "%d BPF programs loaded, expected %d" % 169417ec264SJakub Kicinski (len(progs), expected)) 170417ec264SJakub Kicinski return progs 171417ec264SJakub Kicinski 1727fedbb7cSJakub Kicinskidef bpftool_map_list(expected=None, ns=""): 1737fedbb7cSJakub Kicinski _, maps = bpftool("map show", JSON=True, ns=ns, fail=True) 1747fedbb7cSJakub Kicinski if expected is not None: 1757fedbb7cSJakub Kicinski if len(maps) != expected: 1767fedbb7cSJakub Kicinski fail(True, "%d BPF maps loaded, expected %d" % 1777fedbb7cSJakub Kicinski (len(maps), expected)) 1787fedbb7cSJakub Kicinski return maps 1797fedbb7cSJakub Kicinski 180417ec264SJakub Kicinskidef bpftool_prog_list_wait(expected=0, n_retry=20): 181417ec264SJakub Kicinski for i in range(n_retry): 182417ec264SJakub Kicinski nprogs = len(bpftool_prog_list()) 183417ec264SJakub Kicinski if nprogs == expected: 184417ec264SJakub Kicinski return 185417ec264SJakub Kicinski time.sleep(0.05) 186417ec264SJakub Kicinski raise Exception("Time out waiting for program counts to stabilize want %d, have %d" % (expected, nprogs)) 187417ec264SJakub Kicinski 1887fedbb7cSJakub Kicinskidef bpftool_map_list_wait(expected=0, n_retry=20): 1897fedbb7cSJakub Kicinski for i in range(n_retry): 1907fedbb7cSJakub Kicinski nmaps = len(bpftool_map_list()) 1917fedbb7cSJakub Kicinski if nmaps == expected: 1927fedbb7cSJakub Kicinski return 1937fedbb7cSJakub Kicinski time.sleep(0.05) 1947fedbb7cSJakub Kicinski raise Exception("Time out waiting for map counts to stabilize want %d, have %d" % (expected, nmaps)) 1957fedbb7cSJakub Kicinski 196caf95228SQuentin Monnetdef ip(args, force=False, JSON=True, ns="", fail=True, include_stderr=False): 197417ec264SJakub Kicinski if force: 198417ec264SJakub Kicinski args = "-force " + args 199caf95228SQuentin Monnet return tool("ip", args, {"json":"-j"}, JSON=JSON, ns=ns, 200caf95228SQuentin Monnet fail=fail, include_stderr=include_stderr) 201417ec264SJakub Kicinski 202caf95228SQuentin Monnetdef tc(args, JSON=True, ns="", fail=True, include_stderr=False): 203caf95228SQuentin Monnet return tool("tc", args, {"json":"-p"}, JSON=JSON, ns=ns, 204caf95228SQuentin Monnet fail=fail, include_stderr=include_stderr) 205417ec264SJakub Kicinski 206417ec264SJakub Kicinskidef ethtool(dev, opt, args, fail=True): 207417ec264SJakub Kicinski return cmd("ethtool %s %s %s" % (opt, dev["ifname"], args), fail=fail) 208417ec264SJakub Kicinski 209417ec264SJakub Kicinskidef bpf_obj(name, sec=".text", path=bpf_test_dir,): 210417ec264SJakub Kicinski return "obj %s sec %s" % (os.path.join(path, name), sec) 211417ec264SJakub Kicinski 212417ec264SJakub Kicinskidef bpf_pinned(name): 213417ec264SJakub Kicinski return "pinned %s" % (name) 214417ec264SJakub Kicinski 215417ec264SJakub Kicinskidef bpf_bytecode(bytecode): 216417ec264SJakub Kicinski return "bytecode \"%s\"" % (bytecode) 217417ec264SJakub Kicinski 218752d7b45SJakub Kicinskidef mknetns(n_retry=10): 219752d7b45SJakub Kicinski for i in range(n_retry): 220752d7b45SJakub Kicinski name = ''.join([random.choice(string.ascii_letters) for i in range(8)]) 221752d7b45SJakub Kicinski ret, _ = ip("netns add %s" % (name), fail=False) 222752d7b45SJakub Kicinski if ret == 0: 223752d7b45SJakub Kicinski netns.append(name) 224752d7b45SJakub Kicinski return name 225752d7b45SJakub Kicinski return None 226752d7b45SJakub Kicinski 2277fedbb7cSJakub Kicinskidef int2str(fmt, val): 2287fedbb7cSJakub Kicinski ret = [] 2297fedbb7cSJakub Kicinski for b in struct.pack(fmt, val): 2307fedbb7cSJakub Kicinski ret.append(int(b)) 2317fedbb7cSJakub Kicinski return " ".join(map(lambda x: str(x), ret)) 2327fedbb7cSJakub Kicinski 2337fedbb7cSJakub Kicinskidef str2int(strtab): 2347fedbb7cSJakub Kicinski inttab = [] 2357fedbb7cSJakub Kicinski for i in strtab: 2367fedbb7cSJakub Kicinski inttab.append(int(i, 16)) 2377fedbb7cSJakub Kicinski ba = bytearray(inttab) 2387fedbb7cSJakub Kicinski if len(strtab) == 4: 2397fedbb7cSJakub Kicinski fmt = "I" 2407fedbb7cSJakub Kicinski elif len(strtab) == 8: 2417fedbb7cSJakub Kicinski fmt = "Q" 2427fedbb7cSJakub Kicinski else: 2437fedbb7cSJakub Kicinski raise Exception("String array of len %d can't be unpacked to an int" % 2447fedbb7cSJakub Kicinski (len(strtab))) 2457fedbb7cSJakub Kicinski return struct.unpack(fmt, ba)[0] 2467fedbb7cSJakub Kicinski 247417ec264SJakub Kicinskiclass DebugfsDir: 248417ec264SJakub Kicinski """ 249417ec264SJakub Kicinski Class for accessing DebugFS directories as a dictionary. 250417ec264SJakub Kicinski """ 251417ec264SJakub Kicinski 252417ec264SJakub Kicinski def __init__(self, path): 253417ec264SJakub Kicinski self.path = path 254417ec264SJakub Kicinski self._dict = self._debugfs_dir_read(path) 255417ec264SJakub Kicinski 256417ec264SJakub Kicinski def __len__(self): 257417ec264SJakub Kicinski return len(self._dict.keys()) 258417ec264SJakub Kicinski 259417ec264SJakub Kicinski def __getitem__(self, key): 260417ec264SJakub Kicinski if type(key) is int: 261417ec264SJakub Kicinski key = list(self._dict.keys())[key] 262417ec264SJakub Kicinski return self._dict[key] 263417ec264SJakub Kicinski 264417ec264SJakub Kicinski def __setitem__(self, key, value): 265417ec264SJakub Kicinski log("DebugFS set %s = %s" % (key, value), "") 266417ec264SJakub Kicinski log_level_inc() 267417ec264SJakub Kicinski 268417ec264SJakub Kicinski cmd("echo '%s' > %s/%s" % (value, self.path, key)) 269417ec264SJakub Kicinski log_level_dec() 270417ec264SJakub Kicinski 271417ec264SJakub Kicinski _, out = cmd('cat %s/%s' % (self.path, key)) 272417ec264SJakub Kicinski self._dict[key] = out.strip() 273417ec264SJakub Kicinski 274417ec264SJakub Kicinski def _debugfs_dir_read(self, path): 275417ec264SJakub Kicinski dfs = {} 276417ec264SJakub Kicinski 277417ec264SJakub Kicinski log("DebugFS state for %s" % (path), "") 278417ec264SJakub Kicinski log_level_inc(add=2) 279417ec264SJakub Kicinski 280417ec264SJakub Kicinski _, out = cmd('ls ' + path) 281417ec264SJakub Kicinski for f in out.split(): 282417ec264SJakub Kicinski p = os.path.join(path, f) 283417ec264SJakub Kicinski if os.path.isfile(p): 284417ec264SJakub Kicinski _, out = cmd('cat %s/%s' % (path, f)) 285417ec264SJakub Kicinski dfs[f] = out.strip() 286417ec264SJakub Kicinski elif os.path.isdir(p): 287417ec264SJakub Kicinski dfs[f] = DebugfsDir(p) 288417ec264SJakub Kicinski else: 289417ec264SJakub Kicinski raise Exception("%s is neither file nor directory" % (p)) 290417ec264SJakub Kicinski 291417ec264SJakub Kicinski log_level_dec() 292417ec264SJakub Kicinski log("DebugFS state", dfs) 293417ec264SJakub Kicinski log_level_dec() 294417ec264SJakub Kicinski 295417ec264SJakub Kicinski return dfs 296417ec264SJakub Kicinski 297417ec264SJakub Kicinskiclass NetdevSim: 298417ec264SJakub Kicinski """ 299417ec264SJakub Kicinski Class for netdevsim netdevice and its attributes. 300417ec264SJakub Kicinski """ 301417ec264SJakub Kicinski 302417ec264SJakub Kicinski def __init__(self): 303417ec264SJakub Kicinski self.dev = self._netdevsim_create() 304417ec264SJakub Kicinski devs.append(self) 305417ec264SJakub Kicinski 306752d7b45SJakub Kicinski self.ns = "" 307752d7b45SJakub Kicinski 308417ec264SJakub Kicinski self.dfs_dir = '/sys/kernel/debug/netdevsim/%s' % (self.dev['ifname']) 309417ec264SJakub Kicinski self.dfs_refresh() 310417ec264SJakub Kicinski 311417ec264SJakub Kicinski def __getitem__(self, key): 312417ec264SJakub Kicinski return self.dev[key] 313417ec264SJakub Kicinski 314417ec264SJakub Kicinski def _netdevsim_create(self): 315417ec264SJakub Kicinski _, old = ip("link show") 316417ec264SJakub Kicinski ip("link add sim%d type netdevsim") 317417ec264SJakub Kicinski _, new = ip("link show") 318417ec264SJakub Kicinski 319417ec264SJakub Kicinski for dev in new: 320417ec264SJakub Kicinski f = filter(lambda x: x["ifname"] == dev["ifname"], old) 321417ec264SJakub Kicinski if len(list(f)) == 0: 322417ec264SJakub Kicinski return dev 323417ec264SJakub Kicinski 324417ec264SJakub Kicinski raise Exception("failed to create netdevsim device") 325417ec264SJakub Kicinski 326417ec264SJakub Kicinski def remove(self): 327417ec264SJakub Kicinski devs.remove(self) 328752d7b45SJakub Kicinski ip("link del dev %s" % (self.dev["ifname"]), ns=self.ns) 329417ec264SJakub Kicinski 330417ec264SJakub Kicinski def dfs_refresh(self): 331417ec264SJakub Kicinski self.dfs = DebugfsDir(self.dfs_dir) 332417ec264SJakub Kicinski return self.dfs 333417ec264SJakub Kicinski 334417ec264SJakub Kicinski def dfs_num_bound_progs(self): 335417ec264SJakub Kicinski path = os.path.join(self.dfs_dir, "bpf_bound_progs") 336417ec264SJakub Kicinski _, progs = cmd('ls %s' % (path)) 337417ec264SJakub Kicinski return len(progs.split()) 338417ec264SJakub Kicinski 339417ec264SJakub Kicinski def dfs_get_bound_progs(self, expected): 340417ec264SJakub Kicinski progs = DebugfsDir(os.path.join(self.dfs_dir, "bpf_bound_progs")) 341417ec264SJakub Kicinski if expected is not None: 342417ec264SJakub Kicinski if len(progs) != expected: 343417ec264SJakub Kicinski fail(True, "%d BPF programs bound, expected %d" % 344417ec264SJakub Kicinski (len(progs), expected)) 345417ec264SJakub Kicinski return progs 346417ec264SJakub Kicinski 347417ec264SJakub Kicinski def wait_for_flush(self, bound=0, total=0, n_retry=20): 348417ec264SJakub Kicinski for i in range(n_retry): 349417ec264SJakub Kicinski nbound = self.dfs_num_bound_progs() 350417ec264SJakub Kicinski nprogs = len(bpftool_prog_list()) 351417ec264SJakub Kicinski if nbound == bound and nprogs == total: 352417ec264SJakub Kicinski return 353417ec264SJakub Kicinski time.sleep(0.05) 354417ec264SJakub Kicinski raise Exception("Time out waiting for program counts to stabilize want %d/%d, have %d bound, %d loaded" % (bound, total, nbound, nprogs)) 355417ec264SJakub Kicinski 356752d7b45SJakub Kicinski def set_ns(self, ns): 357752d7b45SJakub Kicinski name = "1" if ns == "" else ns 358752d7b45SJakub Kicinski ip("link set dev %s netns %s" % (self.dev["ifname"], name), ns=self.ns) 359752d7b45SJakub Kicinski self.ns = ns 360752d7b45SJakub Kicinski 361417ec264SJakub Kicinski def set_mtu(self, mtu, fail=True): 362417ec264SJakub Kicinski return ip("link set dev %s mtu %d" % (self.dev["ifname"], mtu), 363417ec264SJakub Kicinski fail=fail) 364417ec264SJakub Kicinski 3659045bdc8SQuentin Monnet def set_xdp(self, bpf, mode, force=False, JSON=True, verbose=False, 366caf95228SQuentin Monnet fail=True, include_stderr=False): 3679045bdc8SQuentin Monnet if verbose: 3689045bdc8SQuentin Monnet bpf += " verbose" 369417ec264SJakub Kicinski return ip("link set dev %s xdp%s %s" % (self.dev["ifname"], mode, bpf), 370caf95228SQuentin Monnet force=force, JSON=JSON, 371caf95228SQuentin Monnet fail=fail, include_stderr=include_stderr) 372417ec264SJakub Kicinski 373caf95228SQuentin Monnet def unset_xdp(self, mode, force=False, JSON=True, 374caf95228SQuentin Monnet fail=True, include_stderr=False): 375417ec264SJakub Kicinski return ip("link set dev %s xdp%s off" % (self.dev["ifname"], mode), 376caf95228SQuentin Monnet force=force, JSON=JSON, 377caf95228SQuentin Monnet fail=fail, include_stderr=include_stderr) 378417ec264SJakub Kicinski 379417ec264SJakub Kicinski def ip_link_show(self, xdp): 380417ec264SJakub Kicinski _, link = ip("link show dev %s" % (self['ifname'])) 381417ec264SJakub Kicinski if len(link) > 1: 382417ec264SJakub Kicinski raise Exception("Multiple objects on ip link show") 383417ec264SJakub Kicinski if len(link) < 1: 384417ec264SJakub Kicinski return {} 385417ec264SJakub Kicinski fail(xdp != "xdp" in link, 386417ec264SJakub Kicinski "XDP program not reporting in iplink (reported %s, expected %s)" % 387417ec264SJakub Kicinski ("xdp" in link, xdp)) 388417ec264SJakub Kicinski return link[0] 389417ec264SJakub Kicinski 390417ec264SJakub Kicinski def tc_add_ingress(self): 391417ec264SJakub Kicinski tc("qdisc add dev %s ingress" % (self['ifname'])) 392417ec264SJakub Kicinski 393417ec264SJakub Kicinski def tc_del_ingress(self): 394417ec264SJakub Kicinski tc("qdisc del dev %s ingress" % (self['ifname'])) 395417ec264SJakub Kicinski 396417ec264SJakub Kicinski def tc_flush_filters(self, bound=0, total=0): 397417ec264SJakub Kicinski self.tc_del_ingress() 398417ec264SJakub Kicinski self.tc_add_ingress() 399417ec264SJakub Kicinski self.wait_for_flush(bound=bound, total=total) 400417ec264SJakub Kicinski 401417ec264SJakub Kicinski def tc_show_ingress(self, expected=None): 402417ec264SJakub Kicinski # No JSON support, oh well... 403417ec264SJakub Kicinski flags = ["skip_sw", "skip_hw", "in_hw"] 404417ec264SJakub Kicinski named = ["protocol", "pref", "chain", "handle", "id", "tag"] 405417ec264SJakub Kicinski 406417ec264SJakub Kicinski args = "-s filter show dev %s ingress" % (self['ifname']) 407417ec264SJakub Kicinski _, out = tc(args, JSON=False) 408417ec264SJakub Kicinski 409417ec264SJakub Kicinski filters = [] 410417ec264SJakub Kicinski lines = out.split('\n') 411417ec264SJakub Kicinski for line in lines: 412417ec264SJakub Kicinski words = line.split() 413417ec264SJakub Kicinski if "handle" not in words: 414417ec264SJakub Kicinski continue 415417ec264SJakub Kicinski fltr = {} 416417ec264SJakub Kicinski for flag in flags: 417417ec264SJakub Kicinski fltr[flag] = flag in words 418417ec264SJakub Kicinski for name in named: 419417ec264SJakub Kicinski try: 420417ec264SJakub Kicinski idx = words.index(name) 421417ec264SJakub Kicinski fltr[name] = words[idx + 1] 422417ec264SJakub Kicinski except ValueError: 423417ec264SJakub Kicinski pass 424417ec264SJakub Kicinski filters.append(fltr) 425417ec264SJakub Kicinski 426417ec264SJakub Kicinski if expected is not None: 427417ec264SJakub Kicinski fail(len(filters) != expected, 428417ec264SJakub Kicinski "%d ingress filters loaded, expected %d" % 429417ec264SJakub Kicinski (len(filters), expected)) 430417ec264SJakub Kicinski return filters 431417ec264SJakub Kicinski 4329045bdc8SQuentin Monnet def cls_bpf_add_filter(self, bpf, da=False, verbose=False, skip_sw=False, 4339045bdc8SQuentin Monnet skip_hw=False, fail=True, include_stderr=False): 434417ec264SJakub Kicinski params = "" 435417ec264SJakub Kicinski if da: 436417ec264SJakub Kicinski params += " da" 4379045bdc8SQuentin Monnet if verbose: 4389045bdc8SQuentin Monnet params += " verbose" 439417ec264SJakub Kicinski if skip_sw: 440417ec264SJakub Kicinski params += " skip_sw" 441417ec264SJakub Kicinski if skip_hw: 442417ec264SJakub Kicinski params += " skip_hw" 443417ec264SJakub Kicinski return tc("filter add dev %s ingress bpf %s %s" % 444caf95228SQuentin Monnet (self['ifname'], bpf, params), 445caf95228SQuentin Monnet fail=fail, include_stderr=include_stderr) 446417ec264SJakub Kicinski 447417ec264SJakub Kicinski def set_ethtool_tc_offloads(self, enable, fail=True): 448417ec264SJakub Kicinski args = "hw-tc-offload %s" % ("on" if enable else "off") 449417ec264SJakub Kicinski return ethtool(self, "-K", args, fail=fail) 450417ec264SJakub Kicinski 451417ec264SJakub Kicinski################################################################################ 452417ec264SJakub Kicinskidef clean_up(): 4537fedbb7cSJakub Kicinski global files, netns, devs 4547fedbb7cSJakub Kicinski 455417ec264SJakub Kicinski for dev in devs: 456417ec264SJakub Kicinski dev.remove() 457417ec264SJakub Kicinski for f in files: 458417ec264SJakub Kicinski cmd("rm -f %s" % (f)) 459752d7b45SJakub Kicinski for ns in netns: 460752d7b45SJakub Kicinski cmd("ip netns delete %s" % (ns)) 4617fedbb7cSJakub Kicinski files = [] 4627fedbb7cSJakub Kicinski netns = [] 463417ec264SJakub Kicinski 464417ec264SJakub Kicinskidef pin_prog(file_name, idx=0): 465417ec264SJakub Kicinski progs = bpftool_prog_list(expected=(idx + 1)) 466417ec264SJakub Kicinski prog = progs[idx] 467417ec264SJakub Kicinski bpftool("prog pin id %d %s" % (prog["id"], file_name)) 468417ec264SJakub Kicinski files.append(file_name) 469417ec264SJakub Kicinski 470417ec264SJakub Kicinski return file_name, bpf_pinned(file_name) 471417ec264SJakub Kicinski 4727fedbb7cSJakub Kicinskidef pin_map(file_name, idx=0, expected=1): 4737fedbb7cSJakub Kicinski maps = bpftool_map_list(expected=expected) 4747fedbb7cSJakub Kicinski m = maps[idx] 4757fedbb7cSJakub Kicinski bpftool("map pin id %d %s" % (m["id"], file_name)) 4767fedbb7cSJakub Kicinski files.append(file_name) 4777fedbb7cSJakub Kicinski 4787fedbb7cSJakub Kicinski return file_name, bpf_pinned(file_name) 4797fedbb7cSJakub Kicinski 4807fedbb7cSJakub Kicinskidef check_dev_info_removed(prog_file=None, map_file=None): 481752d7b45SJakub Kicinski bpftool_prog_list(expected=0) 4827fedbb7cSJakub Kicinski ret, err = bpftool("prog show pin %s" % (prog_file), fail=False) 483752d7b45SJakub Kicinski fail(ret == 0, "Showing prog with removed device did not fail") 484752d7b45SJakub Kicinski fail(err["error"].find("No such device") == -1, 485752d7b45SJakub Kicinski "Showing prog with removed device expected ENODEV, error is %s" % 486752d7b45SJakub Kicinski (err["error"])) 4877fedbb7cSJakub Kicinski 4887fedbb7cSJakub Kicinski bpftool_map_list(expected=0) 4897fedbb7cSJakub Kicinski ret, err = bpftool("map show pin %s" % (map_file), fail=False) 4907fedbb7cSJakub Kicinski fail(ret == 0, "Showing map with removed device did not fail") 4917fedbb7cSJakub Kicinski fail(err["error"].find("No such device") == -1, 4927fedbb7cSJakub Kicinski "Showing map with removed device expected ENODEV, error is %s" % 4937fedbb7cSJakub Kicinski (err["error"])) 4947fedbb7cSJakub Kicinski 4957fedbb7cSJakub Kicinskidef check_dev_info(other_ns, ns, prog_file=None, map_file=None, removed=False): 4967fedbb7cSJakub Kicinski progs = bpftool_prog_list(expected=1, ns=ns) 497752d7b45SJakub Kicinski prog = progs[0] 498752d7b45SJakub Kicinski 499752d7b45SJakub Kicinski fail("dev" not in prog.keys(), "Device parameters not reported") 500752d7b45SJakub Kicinski dev = prog["dev"] 501752d7b45SJakub Kicinski fail("ifindex" not in dev.keys(), "Device parameters not reported") 502752d7b45SJakub Kicinski fail("ns_dev" not in dev.keys(), "Device parameters not reported") 503752d7b45SJakub Kicinski fail("ns_inode" not in dev.keys(), "Device parameters not reported") 504752d7b45SJakub Kicinski 5057fedbb7cSJakub Kicinski if not other_ns: 506752d7b45SJakub Kicinski fail("ifname" not in dev.keys(), "Ifname not reported") 507752d7b45SJakub Kicinski fail(dev["ifname"] != sim["ifname"], 508752d7b45SJakub Kicinski "Ifname incorrect %s vs %s" % (dev["ifname"], sim["ifname"])) 509752d7b45SJakub Kicinski else: 510752d7b45SJakub Kicinski fail("ifname" in dev.keys(), "Ifname is reported for other ns") 5117fedbb7cSJakub Kicinski 5127fedbb7cSJakub Kicinski maps = bpftool_map_list(expected=2, ns=ns) 5137fedbb7cSJakub Kicinski for m in maps: 5147fedbb7cSJakub Kicinski fail("dev" not in m.keys(), "Device parameters not reported") 5157fedbb7cSJakub Kicinski fail(dev != m["dev"], "Map's device different than program's") 516752d7b45SJakub Kicinski 517caf95228SQuentin Monnetdef check_extack(output, reference, args): 518caf95228SQuentin Monnet if skip_extack: 519caf95228SQuentin Monnet return 520caf95228SQuentin Monnet lines = output.split("\n") 521caf95228SQuentin Monnet comp = len(lines) >= 2 and lines[1] == reference 522caf95228SQuentin Monnet fail(not comp, "Missing or incorrect netlink extack message") 523caf95228SQuentin Monnet 524caf95228SQuentin Monnetdef check_extack_nsim(output, reference, args): 525caf95228SQuentin Monnet check_extack(output, "Error: netdevsim: " + reference, args) 526caf95228SQuentin Monnet 5279045bdc8SQuentin Monnetdef check_verifier_log(output, reference): 5289045bdc8SQuentin Monnet lines = output.split("\n") 5299045bdc8SQuentin Monnet for l in reversed(lines): 5309045bdc8SQuentin Monnet if l == reference: 5319045bdc8SQuentin Monnet return 5329045bdc8SQuentin Monnet fail(True, "Missing or incorrect message from netdevsim in verifier log") 5339045bdc8SQuentin Monnet 534417ec264SJakub Kicinski# Parse command line 535417ec264SJakub Kicinskiparser = argparse.ArgumentParser() 536417ec264SJakub Kicinskiparser.add_argument("--log", help="output verbose log to given file") 537417ec264SJakub Kicinskiargs = parser.parse_args() 538417ec264SJakub Kicinskiif args.log: 539417ec264SJakub Kicinski logfile = open(args.log, 'w+') 540417ec264SJakub Kicinski logfile.write("# -*-Org-*-") 541417ec264SJakub Kicinski 542417ec264SJakub Kicinskilog("Prepare...", "", level=1) 543417ec264SJakub Kicinskilog_level_inc() 544417ec264SJakub Kicinski 545417ec264SJakub Kicinski# Check permissions 546417ec264SJakub Kicinskiskip(os.getuid() != 0, "test must be run as root") 547417ec264SJakub Kicinski 548417ec264SJakub Kicinski# Check tools 549417ec264SJakub Kicinskiret, progs = bpftool("prog", fail=False) 550417ec264SJakub Kicinskiskip(ret != 0, "bpftool not installed") 551417ec264SJakub Kicinski# Check no BPF programs are loaded 552417ec264SJakub Kicinskiskip(len(progs) != 0, "BPF programs already loaded on the system") 553417ec264SJakub Kicinski 554417ec264SJakub Kicinski# Check netdevsim 555417ec264SJakub Kicinskiret, out = cmd("modprobe netdevsim", fail=False) 556417ec264SJakub Kicinskiskip(ret != 0, "netdevsim module could not be loaded") 557417ec264SJakub Kicinski 558417ec264SJakub Kicinski# Check debugfs 559417ec264SJakub Kicinski_, out = cmd("mount") 560417ec264SJakub Kicinskiif out.find("/sys/kernel/debug type debugfs") == -1: 561417ec264SJakub Kicinski cmd("mount -t debugfs none /sys/kernel/debug") 562417ec264SJakub Kicinski 563417ec264SJakub Kicinski# Check samples are compiled 5647fedbb7cSJakub Kicinskisamples = ["sample_ret0.o", "sample_map_ret0.o"] 565417ec264SJakub Kicinskifor s in samples: 566417ec264SJakub Kicinski ret, out = cmd("ls %s/%s" % (bpf_test_dir, s), fail=False) 567417ec264SJakub Kicinski skip(ret != 0, "sample %s/%s not found, please compile it" % 568417ec264SJakub Kicinski (bpf_test_dir, s)) 569417ec264SJakub Kicinski 570caf95228SQuentin Monnet# Check if iproute2 is built with libmnl (needed by extack support) 571caf95228SQuentin Monnet_, _, err = cmd("tc qdisc delete dev lo handle 0", 572caf95228SQuentin Monnet fail=False, include_stderr=True) 573caf95228SQuentin Monnetif err.find("Error: Failed to find qdisc with specified handle.") == -1: 574caf95228SQuentin Monnet print("Warning: no extack message in iproute2 output, libmnl missing?") 575caf95228SQuentin Monnet log("Warning: no extack message in iproute2 output, libmnl missing?", "") 576caf95228SQuentin Monnet skip_extack = True 577caf95228SQuentin Monnet 578752d7b45SJakub Kicinski# Check if net namespaces seem to work 579752d7b45SJakub Kicinskins = mknetns() 580752d7b45SJakub Kicinskiskip(ns is None, "Could not create a net namespace") 581752d7b45SJakub Kicinskicmd("ip netns delete %s" % (ns)) 582752d7b45SJakub Kicinskinetns = [] 583752d7b45SJakub Kicinski 584417ec264SJakub Kicinskitry: 585417ec264SJakub Kicinski obj = bpf_obj("sample_ret0.o") 586417ec264SJakub Kicinski bytecode = bpf_bytecode("1,6 0 0 4294967295,") 587417ec264SJakub Kicinski 588417ec264SJakub Kicinski start_test("Test destruction of generic XDP...") 589417ec264SJakub Kicinski sim = NetdevSim() 590417ec264SJakub Kicinski sim.set_xdp(obj, "generic") 591417ec264SJakub Kicinski sim.remove() 592417ec264SJakub Kicinski bpftool_prog_list_wait(expected=0) 593417ec264SJakub Kicinski 594417ec264SJakub Kicinski sim = NetdevSim() 595417ec264SJakub Kicinski sim.tc_add_ingress() 596417ec264SJakub Kicinski 597417ec264SJakub Kicinski start_test("Test TC non-offloaded...") 598417ec264SJakub Kicinski ret, _ = sim.cls_bpf_add_filter(obj, skip_hw=True, fail=False) 599417ec264SJakub Kicinski fail(ret != 0, "Software TC filter did not load") 600417ec264SJakub Kicinski 601417ec264SJakub Kicinski start_test("Test TC non-offloaded isn't getting bound...") 602417ec264SJakub Kicinski ret, _ = sim.cls_bpf_add_filter(obj, fail=False) 603417ec264SJakub Kicinski fail(ret != 0, "Software TC filter did not load") 604417ec264SJakub Kicinski sim.dfs_get_bound_progs(expected=0) 605417ec264SJakub Kicinski 606417ec264SJakub Kicinski sim.tc_flush_filters() 607417ec264SJakub Kicinski 608417ec264SJakub Kicinski start_test("Test TC offloads are off by default...") 609caf95228SQuentin Monnet ret, _, err = sim.cls_bpf_add_filter(obj, skip_sw=True, 610caf95228SQuentin Monnet fail=False, include_stderr=True) 611417ec264SJakub Kicinski fail(ret == 0, "TC filter loaded without enabling TC offloads") 612caf95228SQuentin Monnet check_extack(err, "Error: TC offload is disabled on net device.", args) 613417ec264SJakub Kicinski sim.wait_for_flush() 614417ec264SJakub Kicinski 615417ec264SJakub Kicinski sim.set_ethtool_tc_offloads(True) 616417ec264SJakub Kicinski sim.dfs["bpf_tc_non_bound_accept"] = "Y" 617417ec264SJakub Kicinski 618417ec264SJakub Kicinski start_test("Test TC offload by default...") 619417ec264SJakub Kicinski ret, _ = sim.cls_bpf_add_filter(obj, fail=False) 620417ec264SJakub Kicinski fail(ret != 0, "Software TC filter did not load") 621417ec264SJakub Kicinski sim.dfs_get_bound_progs(expected=0) 622417ec264SJakub Kicinski ingress = sim.tc_show_ingress(expected=1) 623417ec264SJakub Kicinski fltr = ingress[0] 624417ec264SJakub Kicinski fail(not fltr["in_hw"], "Filter not offloaded by default") 625417ec264SJakub Kicinski 626417ec264SJakub Kicinski sim.tc_flush_filters() 627417ec264SJakub Kicinski 628417ec264SJakub Kicinski start_test("Test TC cBPF bytcode tries offload by default...") 629417ec264SJakub Kicinski ret, _ = sim.cls_bpf_add_filter(bytecode, fail=False) 630417ec264SJakub Kicinski fail(ret != 0, "Software TC filter did not load") 631417ec264SJakub Kicinski sim.dfs_get_bound_progs(expected=0) 632417ec264SJakub Kicinski ingress = sim.tc_show_ingress(expected=1) 633417ec264SJakub Kicinski fltr = ingress[0] 634417ec264SJakub Kicinski fail(not fltr["in_hw"], "Bytecode not offloaded by default") 635417ec264SJakub Kicinski 636417ec264SJakub Kicinski sim.tc_flush_filters() 637417ec264SJakub Kicinski sim.dfs["bpf_tc_non_bound_accept"] = "N" 638417ec264SJakub Kicinski 639417ec264SJakub Kicinski start_test("Test TC cBPF unbound bytecode doesn't offload...") 640caf95228SQuentin Monnet ret, _, err = sim.cls_bpf_add_filter(bytecode, skip_sw=True, 641caf95228SQuentin Monnet fail=False, include_stderr=True) 642417ec264SJakub Kicinski fail(ret == 0, "TC bytecode loaded for offload") 643caf95228SQuentin Monnet check_extack_nsim(err, "netdevsim configured to reject unbound programs.", 644caf95228SQuentin Monnet args) 645417ec264SJakub Kicinski sim.wait_for_flush() 646417ec264SJakub Kicinski 647417ec264SJakub Kicinski start_test("Test TC offloads work...") 6489045bdc8SQuentin Monnet ret, _, err = sim.cls_bpf_add_filter(obj, verbose=True, skip_sw=True, 6499045bdc8SQuentin Monnet fail=False, include_stderr=True) 650417ec264SJakub Kicinski fail(ret != 0, "TC filter did not load with TC offloads enabled") 6519045bdc8SQuentin Monnet check_verifier_log(err, "[netdevsim] Hello from netdevsim!") 652417ec264SJakub Kicinski 653417ec264SJakub Kicinski start_test("Test TC offload basics...") 654417ec264SJakub Kicinski dfs = sim.dfs_get_bound_progs(expected=1) 655417ec264SJakub Kicinski progs = bpftool_prog_list(expected=1) 656417ec264SJakub Kicinski ingress = sim.tc_show_ingress(expected=1) 657417ec264SJakub Kicinski 658417ec264SJakub Kicinski dprog = dfs[0] 659417ec264SJakub Kicinski prog = progs[0] 660417ec264SJakub Kicinski fltr = ingress[0] 661417ec264SJakub Kicinski fail(fltr["skip_hw"], "TC does reports 'skip_hw' on offloaded filter") 662417ec264SJakub Kicinski fail(not fltr["in_hw"], "TC does not report 'in_hw' for offloaded filter") 663417ec264SJakub Kicinski fail(not fltr["skip_sw"], "TC does not report 'skip_sw' back") 664417ec264SJakub Kicinski 665417ec264SJakub Kicinski start_test("Test TC offload is device-bound...") 666417ec264SJakub Kicinski fail(str(prog["id"]) != fltr["id"], "Program IDs don't match") 667417ec264SJakub Kicinski fail(prog["tag"] != fltr["tag"], "Program tags don't match") 668417ec264SJakub Kicinski fail(fltr["id"] != dprog["id"], "Program IDs don't match") 669417ec264SJakub Kicinski fail(dprog["state"] != "xlated", "Offloaded program state not translated") 670417ec264SJakub Kicinski fail(dprog["loaded"] != "Y", "Offloaded program is not loaded") 671417ec264SJakub Kicinski 672417ec264SJakub Kicinski start_test("Test disabling TC offloads is rejected while filters installed...") 673417ec264SJakub Kicinski ret, _ = sim.set_ethtool_tc_offloads(False, fail=False) 674417ec264SJakub Kicinski fail(ret == 0, "Driver should refuse to disable TC offloads with filters installed...") 675417ec264SJakub Kicinski 676417ec264SJakub Kicinski start_test("Test qdisc removal frees things...") 677417ec264SJakub Kicinski sim.tc_flush_filters() 678417ec264SJakub Kicinski sim.tc_show_ingress(expected=0) 679417ec264SJakub Kicinski 680417ec264SJakub Kicinski start_test("Test disabling TC offloads is OK without filters...") 681417ec264SJakub Kicinski ret, _ = sim.set_ethtool_tc_offloads(False, fail=False) 682417ec264SJakub Kicinski fail(ret != 0, 683417ec264SJakub Kicinski "Driver refused to disable TC offloads without filters installed...") 684417ec264SJakub Kicinski 685417ec264SJakub Kicinski sim.set_ethtool_tc_offloads(True) 686417ec264SJakub Kicinski 687417ec264SJakub Kicinski start_test("Test destroying device gets rid of TC filters...") 688417ec264SJakub Kicinski sim.cls_bpf_add_filter(obj, skip_sw=True) 689417ec264SJakub Kicinski sim.remove() 690417ec264SJakub Kicinski bpftool_prog_list_wait(expected=0) 691417ec264SJakub Kicinski 692417ec264SJakub Kicinski sim = NetdevSim() 693417ec264SJakub Kicinski sim.set_ethtool_tc_offloads(True) 694417ec264SJakub Kicinski 695417ec264SJakub Kicinski start_test("Test destroying device gets rid of XDP...") 696417ec264SJakub Kicinski sim.set_xdp(obj, "offload") 697417ec264SJakub Kicinski sim.remove() 698417ec264SJakub Kicinski bpftool_prog_list_wait(expected=0) 699417ec264SJakub Kicinski 700417ec264SJakub Kicinski sim = NetdevSim() 701417ec264SJakub Kicinski sim.set_ethtool_tc_offloads(True) 702417ec264SJakub Kicinski 703417ec264SJakub Kicinski start_test("Test XDP prog reporting...") 704417ec264SJakub Kicinski sim.set_xdp(obj, "drv") 705417ec264SJakub Kicinski ipl = sim.ip_link_show(xdp=True) 706417ec264SJakub Kicinski progs = bpftool_prog_list(expected=1) 707417ec264SJakub Kicinski fail(ipl["xdp"]["prog"]["id"] != progs[0]["id"], 708417ec264SJakub Kicinski "Loaded program has wrong ID") 709417ec264SJakub Kicinski 710417ec264SJakub Kicinski start_test("Test XDP prog replace without force...") 711417ec264SJakub Kicinski ret, _ = sim.set_xdp(obj, "drv", fail=False) 712417ec264SJakub Kicinski fail(ret == 0, "Replaced XDP program without -force") 713417ec264SJakub Kicinski sim.wait_for_flush(total=1) 714417ec264SJakub Kicinski 715417ec264SJakub Kicinski start_test("Test XDP prog replace with force...") 716417ec264SJakub Kicinski ret, _ = sim.set_xdp(obj, "drv", force=True, fail=False) 717417ec264SJakub Kicinski fail(ret != 0, "Could not replace XDP program with -force") 718417ec264SJakub Kicinski bpftool_prog_list_wait(expected=1) 719417ec264SJakub Kicinski ipl = sim.ip_link_show(xdp=True) 720417ec264SJakub Kicinski progs = bpftool_prog_list(expected=1) 721417ec264SJakub Kicinski fail(ipl["xdp"]["prog"]["id"] != progs[0]["id"], 722417ec264SJakub Kicinski "Loaded program has wrong ID") 723752d7b45SJakub Kicinski fail("dev" in progs[0].keys(), 724752d7b45SJakub Kicinski "Device parameters reported for non-offloaded program") 725417ec264SJakub Kicinski 726417ec264SJakub Kicinski start_test("Test XDP prog replace with bad flags...") 727caf95228SQuentin Monnet ret, _, err = sim.set_xdp(obj, "offload", force=True, 728caf95228SQuentin Monnet fail=False, include_stderr=True) 729417ec264SJakub Kicinski fail(ret == 0, "Replaced XDP program with a program in different mode") 730caf95228SQuentin Monnet check_extack_nsim(err, "program loaded with different flags.", args) 731caf95228SQuentin Monnet ret, _, err = sim.set_xdp(obj, "", force=True, 732caf95228SQuentin Monnet fail=False, include_stderr=True) 733417ec264SJakub Kicinski fail(ret == 0, "Replaced XDP program with a program in different mode") 734caf95228SQuentin Monnet check_extack_nsim(err, "program loaded with different flags.", args) 735417ec264SJakub Kicinski 736417ec264SJakub Kicinski start_test("Test XDP prog remove with bad flags...") 737caf95228SQuentin Monnet ret, _, err = sim.unset_xdp("offload", force=True, 738caf95228SQuentin Monnet fail=False, include_stderr=True) 739417ec264SJakub Kicinski fail(ret == 0, "Removed program with a bad mode mode") 740caf95228SQuentin Monnet check_extack_nsim(err, "program loaded with different flags.", args) 741caf95228SQuentin Monnet ret, _, err = sim.unset_xdp("", force=True, 742caf95228SQuentin Monnet fail=False, include_stderr=True) 743417ec264SJakub Kicinski fail(ret == 0, "Removed program with a bad mode mode") 744caf95228SQuentin Monnet check_extack_nsim(err, "program loaded with different flags.", args) 745417ec264SJakub Kicinski 746417ec264SJakub Kicinski start_test("Test MTU restrictions...") 747417ec264SJakub Kicinski ret, _ = sim.set_mtu(9000, fail=False) 748417ec264SJakub Kicinski fail(ret == 0, 749417ec264SJakub Kicinski "Driver should refuse to increase MTU to 9000 with XDP loaded...") 750417ec264SJakub Kicinski sim.unset_xdp("drv") 751417ec264SJakub Kicinski bpftool_prog_list_wait(expected=0) 752417ec264SJakub Kicinski sim.set_mtu(9000) 753caf95228SQuentin Monnet ret, _, err = sim.set_xdp(obj, "drv", fail=False, include_stderr=True) 754417ec264SJakub Kicinski fail(ret == 0, "Driver should refuse to load program with MTU of 9000...") 755caf95228SQuentin Monnet check_extack_nsim(err, "MTU too large w/ XDP enabled.", args) 756417ec264SJakub Kicinski sim.set_mtu(1500) 757417ec264SJakub Kicinski 758417ec264SJakub Kicinski sim.wait_for_flush() 759417ec264SJakub Kicinski start_test("Test XDP offload...") 7609045bdc8SQuentin Monnet _, _, err = sim.set_xdp(obj, "offload", verbose=True, include_stderr=True) 761417ec264SJakub Kicinski ipl = sim.ip_link_show(xdp=True) 762417ec264SJakub Kicinski link_xdp = ipl["xdp"]["prog"] 763417ec264SJakub Kicinski progs = bpftool_prog_list(expected=1) 764417ec264SJakub Kicinski prog = progs[0] 765417ec264SJakub Kicinski fail(link_xdp["id"] != prog["id"], "Loaded program has wrong ID") 7669045bdc8SQuentin Monnet check_verifier_log(err, "[netdevsim] Hello from netdevsim!") 767417ec264SJakub Kicinski 768417ec264SJakub Kicinski start_test("Test XDP offload is device bound...") 769417ec264SJakub Kicinski dfs = sim.dfs_get_bound_progs(expected=1) 770417ec264SJakub Kicinski dprog = dfs[0] 771417ec264SJakub Kicinski 772417ec264SJakub Kicinski fail(prog["id"] != link_xdp["id"], "Program IDs don't match") 773417ec264SJakub Kicinski fail(prog["tag"] != link_xdp["tag"], "Program tags don't match") 774417ec264SJakub Kicinski fail(str(link_xdp["id"]) != dprog["id"], "Program IDs don't match") 775417ec264SJakub Kicinski fail(dprog["state"] != "xlated", "Offloaded program state not translated") 776417ec264SJakub Kicinski fail(dprog["loaded"] != "Y", "Offloaded program is not loaded") 777417ec264SJakub Kicinski 778417ec264SJakub Kicinski start_test("Test removing XDP program many times...") 779417ec264SJakub Kicinski sim.unset_xdp("offload") 780417ec264SJakub Kicinski sim.unset_xdp("offload") 781417ec264SJakub Kicinski sim.unset_xdp("drv") 782417ec264SJakub Kicinski sim.unset_xdp("drv") 783417ec264SJakub Kicinski sim.unset_xdp("") 784417ec264SJakub Kicinski sim.unset_xdp("") 785417ec264SJakub Kicinski bpftool_prog_list_wait(expected=0) 786417ec264SJakub Kicinski 787417ec264SJakub Kicinski start_test("Test attempt to use a program for a wrong device...") 788417ec264SJakub Kicinski sim2 = NetdevSim() 789417ec264SJakub Kicinski sim2.set_xdp(obj, "offload") 790417ec264SJakub Kicinski pin_file, pinned = pin_prog("/sys/fs/bpf/tmp") 791417ec264SJakub Kicinski 792caf95228SQuentin Monnet ret, _, err = sim.set_xdp(pinned, "offload", 793caf95228SQuentin Monnet fail=False, include_stderr=True) 794417ec264SJakub Kicinski fail(ret == 0, "Pinned program loaded for a different device accepted") 795caf95228SQuentin Monnet check_extack_nsim(err, "program bound to different dev.", args) 796417ec264SJakub Kicinski sim2.remove() 797caf95228SQuentin Monnet ret, _, err = sim.set_xdp(pinned, "offload", 798caf95228SQuentin Monnet fail=False, include_stderr=True) 799417ec264SJakub Kicinski fail(ret == 0, "Pinned program loaded for a removed device accepted") 800caf95228SQuentin Monnet check_extack_nsim(err, "xdpoffload of non-bound program.", args) 801417ec264SJakub Kicinski rm(pin_file) 802417ec264SJakub Kicinski bpftool_prog_list_wait(expected=0) 803417ec264SJakub Kicinski 804417ec264SJakub Kicinski start_test("Test mixing of TC and XDP...") 805417ec264SJakub Kicinski sim.tc_add_ingress() 806417ec264SJakub Kicinski sim.set_xdp(obj, "offload") 807caf95228SQuentin Monnet ret, _, err = sim.cls_bpf_add_filter(obj, skip_sw=True, 808caf95228SQuentin Monnet fail=False, include_stderr=True) 809417ec264SJakub Kicinski fail(ret == 0, "Loading TC when XDP active should fail") 810caf95228SQuentin Monnet check_extack_nsim(err, "driver and netdev offload states mismatch.", args) 811417ec264SJakub Kicinski sim.unset_xdp("offload") 812417ec264SJakub Kicinski sim.wait_for_flush() 813417ec264SJakub Kicinski 814417ec264SJakub Kicinski sim.cls_bpf_add_filter(obj, skip_sw=True) 815caf95228SQuentin Monnet ret, _, err = sim.set_xdp(obj, "offload", fail=False, include_stderr=True) 816417ec264SJakub Kicinski fail(ret == 0, "Loading XDP when TC active should fail") 817caf95228SQuentin Monnet check_extack_nsim(err, "TC program is already loaded.", args) 818417ec264SJakub Kicinski 819417ec264SJakub Kicinski start_test("Test binding TC from pinned...") 820417ec264SJakub Kicinski pin_file, pinned = pin_prog("/sys/fs/bpf/tmp") 821417ec264SJakub Kicinski sim.tc_flush_filters(bound=1, total=1) 822417ec264SJakub Kicinski sim.cls_bpf_add_filter(pinned, da=True, skip_sw=True) 823417ec264SJakub Kicinski sim.tc_flush_filters(bound=1, total=1) 824417ec264SJakub Kicinski 825417ec264SJakub Kicinski start_test("Test binding XDP from pinned...") 826417ec264SJakub Kicinski sim.set_xdp(obj, "offload") 827417ec264SJakub Kicinski pin_file, pinned = pin_prog("/sys/fs/bpf/tmp2", idx=1) 828417ec264SJakub Kicinski 829417ec264SJakub Kicinski sim.set_xdp(pinned, "offload", force=True) 830417ec264SJakub Kicinski sim.unset_xdp("offload") 831417ec264SJakub Kicinski sim.set_xdp(pinned, "offload", force=True) 832417ec264SJakub Kicinski sim.unset_xdp("offload") 833417ec264SJakub Kicinski 834417ec264SJakub Kicinski start_test("Test offload of wrong type fails...") 835417ec264SJakub Kicinski ret, _ = sim.cls_bpf_add_filter(pinned, da=True, skip_sw=True, fail=False) 836417ec264SJakub Kicinski fail(ret == 0, "Managed to attach XDP program to TC") 837417ec264SJakub Kicinski 838417ec264SJakub Kicinski start_test("Test asking for TC offload of two filters...") 839417ec264SJakub Kicinski sim.cls_bpf_add_filter(obj, da=True, skip_sw=True) 840caf95228SQuentin Monnet ret, _, err = sim.cls_bpf_add_filter(obj, da=True, skip_sw=True, 841caf95228SQuentin Monnet fail=False, include_stderr=True) 842fba961abSDavid S. Miller fail(ret == 0, "Managed to offload two TC filters at the same time") 843caf95228SQuentin Monnet check_extack_nsim(err, "driver and netdev offload states mismatch.", args) 844417ec264SJakub Kicinski 845417ec264SJakub Kicinski sim.tc_flush_filters(bound=2, total=2) 846417ec264SJakub Kicinski 847417ec264SJakub Kicinski start_test("Test if netdev removal waits for translation...") 848417ec264SJakub Kicinski delay_msec = 500 849417ec264SJakub Kicinski sim.dfs["bpf_bind_verifier_delay"] = delay_msec 850417ec264SJakub Kicinski start = time.time() 851417ec264SJakub Kicinski cmd_line = "tc filter add dev %s ingress bpf %s da skip_sw" % \ 852417ec264SJakub Kicinski (sim['ifname'], obj) 853417ec264SJakub Kicinski tc_proc = cmd(cmd_line, background=True, fail=False) 854417ec264SJakub Kicinski # Wait for the verifier to start 855417ec264SJakub Kicinski while sim.dfs_num_bound_progs() <= 2: 856417ec264SJakub Kicinski pass 857417ec264SJakub Kicinski sim.remove() 858417ec264SJakub Kicinski end = time.time() 859417ec264SJakub Kicinski ret, _ = cmd_result(tc_proc, fail=False) 860417ec264SJakub Kicinski time_diff = end - start 861417ec264SJakub Kicinski log("Time", "start:\t%s\nend:\t%s\ndiff:\t%s" % (start, end, time_diff)) 862417ec264SJakub Kicinski 863417ec264SJakub Kicinski fail(ret == 0, "Managed to load TC filter on a unregistering device") 864417ec264SJakub Kicinski delay_sec = delay_msec * 0.001 865417ec264SJakub Kicinski fail(time_diff < delay_sec, "Removal process took %s, expected %s" % 866417ec264SJakub Kicinski (time_diff, delay_sec)) 867417ec264SJakub Kicinski 868752d7b45SJakub Kicinski # Remove all pinned files and reinstantiate the netdev 869752d7b45SJakub Kicinski clean_up() 870752d7b45SJakub Kicinski bpftool_prog_list_wait(expected=0) 871752d7b45SJakub Kicinski 872752d7b45SJakub Kicinski sim = NetdevSim() 8737fedbb7cSJakub Kicinski map_obj = bpf_obj("sample_map_ret0.o") 8747fedbb7cSJakub Kicinski start_test("Test loading program with maps...") 8757fedbb7cSJakub Kicinski sim.set_xdp(map_obj, "offload", JSON=False) # map fixup msg breaks JSON 876752d7b45SJakub Kicinski 877752d7b45SJakub Kicinski start_test("Test bpftool bound info reporting (own ns)...") 878752d7b45SJakub Kicinski check_dev_info(False, "") 879752d7b45SJakub Kicinski 880752d7b45SJakub Kicinski start_test("Test bpftool bound info reporting (other ns)...") 881752d7b45SJakub Kicinski ns = mknetns() 882752d7b45SJakub Kicinski sim.set_ns(ns) 883752d7b45SJakub Kicinski check_dev_info(True, "") 884752d7b45SJakub Kicinski 885752d7b45SJakub Kicinski start_test("Test bpftool bound info reporting (remote ns)...") 886752d7b45SJakub Kicinski check_dev_info(False, ns) 887752d7b45SJakub Kicinski 888752d7b45SJakub Kicinski start_test("Test bpftool bound info reporting (back to own ns)...") 889752d7b45SJakub Kicinski sim.set_ns("") 890752d7b45SJakub Kicinski check_dev_info(False, "") 891752d7b45SJakub Kicinski 8927fedbb7cSJakub Kicinski prog_file, _ = pin_prog("/sys/fs/bpf/tmp_prog") 8937fedbb7cSJakub Kicinski map_file, _ = pin_map("/sys/fs/bpf/tmp_map", idx=1, expected=2) 894752d7b45SJakub Kicinski sim.remove() 895752d7b45SJakub Kicinski 896752d7b45SJakub Kicinski start_test("Test bpftool bound info reporting (removed dev)...") 8977fedbb7cSJakub Kicinski check_dev_info_removed(prog_file=prog_file, map_file=map_file) 8987fedbb7cSJakub Kicinski 8997fedbb7cSJakub Kicinski # Remove all pinned files and reinstantiate the netdev 9007fedbb7cSJakub Kicinski clean_up() 9017fedbb7cSJakub Kicinski bpftool_prog_list_wait(expected=0) 9027fedbb7cSJakub Kicinski 9037fedbb7cSJakub Kicinski sim = NetdevSim() 9047fedbb7cSJakub Kicinski 9057fedbb7cSJakub Kicinski start_test("Test map update (no flags)...") 9067fedbb7cSJakub Kicinski sim.set_xdp(map_obj, "offload", JSON=False) # map fixup msg breaks JSON 9077fedbb7cSJakub Kicinski maps = bpftool_map_list(expected=2) 9087fedbb7cSJakub Kicinski array = maps[0] if maps[0]["type"] == "array" else maps[1] 9097fedbb7cSJakub Kicinski htab = maps[0] if maps[0]["type"] == "hash" else maps[1] 9107fedbb7cSJakub Kicinski for m in maps: 9117fedbb7cSJakub Kicinski for i in range(2): 9127fedbb7cSJakub Kicinski bpftool("map update id %d key %s value %s" % 9137fedbb7cSJakub Kicinski (m["id"], int2str("I", i), int2str("Q", i * 3))) 9147fedbb7cSJakub Kicinski 9157fedbb7cSJakub Kicinski for m in maps: 9167fedbb7cSJakub Kicinski ret, _ = bpftool("map update id %d key %s value %s" % 9177fedbb7cSJakub Kicinski (m["id"], int2str("I", 3), int2str("Q", 3 * 3)), 9187fedbb7cSJakub Kicinski fail=False) 9197fedbb7cSJakub Kicinski fail(ret == 0, "added too many entries") 9207fedbb7cSJakub Kicinski 9217fedbb7cSJakub Kicinski start_test("Test map update (exists)...") 9227fedbb7cSJakub Kicinski for m in maps: 9237fedbb7cSJakub Kicinski for i in range(2): 9247fedbb7cSJakub Kicinski bpftool("map update id %d key %s value %s exist" % 9257fedbb7cSJakub Kicinski (m["id"], int2str("I", i), int2str("Q", i * 3))) 9267fedbb7cSJakub Kicinski 9277fedbb7cSJakub Kicinski for m in maps: 9287fedbb7cSJakub Kicinski ret, err = bpftool("map update id %d key %s value %s exist" % 9297fedbb7cSJakub Kicinski (m["id"], int2str("I", 3), int2str("Q", 3 * 3)), 9307fedbb7cSJakub Kicinski fail=False) 9317fedbb7cSJakub Kicinski fail(ret == 0, "updated non-existing key") 9327fedbb7cSJakub Kicinski fail(err["error"].find("No such file or directory") == -1, 9337fedbb7cSJakub Kicinski "expected ENOENT, error is '%s'" % (err["error"])) 9347fedbb7cSJakub Kicinski 9357fedbb7cSJakub Kicinski start_test("Test map update (noexist)...") 9367fedbb7cSJakub Kicinski for m in maps: 9377fedbb7cSJakub Kicinski for i in range(2): 9387fedbb7cSJakub Kicinski ret, err = bpftool("map update id %d key %s value %s noexist" % 9397fedbb7cSJakub Kicinski (m["id"], int2str("I", i), int2str("Q", i * 3)), 9407fedbb7cSJakub Kicinski fail=False) 9417fedbb7cSJakub Kicinski fail(ret == 0, "updated existing key") 9427fedbb7cSJakub Kicinski fail(err["error"].find("File exists") == -1, 9437fedbb7cSJakub Kicinski "expected EEXIST, error is '%s'" % (err["error"])) 9447fedbb7cSJakub Kicinski 9457fedbb7cSJakub Kicinski start_test("Test map dump...") 9467fedbb7cSJakub Kicinski for m in maps: 9477fedbb7cSJakub Kicinski _, entries = bpftool("map dump id %d" % (m["id"])) 9487fedbb7cSJakub Kicinski for i in range(2): 9497fedbb7cSJakub Kicinski key = str2int(entries[i]["key"]) 9507fedbb7cSJakub Kicinski fail(key != i, "expected key %d, got %d" % (key, i)) 9517fedbb7cSJakub Kicinski val = str2int(entries[i]["value"]) 9527fedbb7cSJakub Kicinski fail(val != i * 3, "expected value %d, got %d" % (val, i * 3)) 9537fedbb7cSJakub Kicinski 9547fedbb7cSJakub Kicinski start_test("Test map getnext...") 9557fedbb7cSJakub Kicinski for m in maps: 9567fedbb7cSJakub Kicinski _, entry = bpftool("map getnext id %d" % (m["id"])) 9577fedbb7cSJakub Kicinski key = str2int(entry["next_key"]) 9587fedbb7cSJakub Kicinski fail(key != 0, "next key %d, expected %d" % (key, 0)) 9597fedbb7cSJakub Kicinski _, entry = bpftool("map getnext id %d key %s" % 9607fedbb7cSJakub Kicinski (m["id"], int2str("I", 0))) 9617fedbb7cSJakub Kicinski key = str2int(entry["next_key"]) 9627fedbb7cSJakub Kicinski fail(key != 1, "next key %d, expected %d" % (key, 1)) 9637fedbb7cSJakub Kicinski ret, err = bpftool("map getnext id %d key %s" % 9647fedbb7cSJakub Kicinski (m["id"], int2str("I", 1)), fail=False) 9657fedbb7cSJakub Kicinski fail(ret == 0, "got next key past the end of map") 9667fedbb7cSJakub Kicinski fail(err["error"].find("No such file or directory") == -1, 9677fedbb7cSJakub Kicinski "expected ENOENT, error is '%s'" % (err["error"])) 9687fedbb7cSJakub Kicinski 9697fedbb7cSJakub Kicinski start_test("Test map delete (htab)...") 9707fedbb7cSJakub Kicinski for i in range(2): 9717fedbb7cSJakub Kicinski bpftool("map delete id %d key %s" % (htab["id"], int2str("I", i))) 9727fedbb7cSJakub Kicinski 9737fedbb7cSJakub Kicinski start_test("Test map delete (array)...") 9747fedbb7cSJakub Kicinski for i in range(2): 9757fedbb7cSJakub Kicinski ret, err = bpftool("map delete id %d key %s" % 9767fedbb7cSJakub Kicinski (htab["id"], int2str("I", i)), fail=False) 9777fedbb7cSJakub Kicinski fail(ret == 0, "removed entry from an array") 9787fedbb7cSJakub Kicinski fail(err["error"].find("No such file or directory") == -1, 9797fedbb7cSJakub Kicinski "expected ENOENT, error is '%s'" % (err["error"])) 9807fedbb7cSJakub Kicinski 9817fedbb7cSJakub Kicinski start_test("Test map remove...") 9827fedbb7cSJakub Kicinski sim.unset_xdp("offload") 9837fedbb7cSJakub Kicinski bpftool_map_list_wait(expected=0) 9847fedbb7cSJakub Kicinski sim.remove() 9857fedbb7cSJakub Kicinski 9867fedbb7cSJakub Kicinski sim = NetdevSim() 9877fedbb7cSJakub Kicinski sim.set_xdp(map_obj, "offload", JSON=False) # map fixup msg breaks JSON 9887fedbb7cSJakub Kicinski sim.remove() 9897fedbb7cSJakub Kicinski bpftool_map_list_wait(expected=0) 9907fedbb7cSJakub Kicinski 9917fedbb7cSJakub Kicinski start_test("Test map creation fail path...") 9927fedbb7cSJakub Kicinski sim = NetdevSim() 9937fedbb7cSJakub Kicinski sim.dfs["bpf_map_accept"] = "N" 9947fedbb7cSJakub Kicinski ret, _ = sim.set_xdp(map_obj, "offload", JSON=False, fail=False) 9957fedbb7cSJakub Kicinski fail(ret == 0, 9967fedbb7cSJakub Kicinski "netdevsim didn't refuse to create a map with offload disabled") 997752d7b45SJakub Kicinski 998417ec264SJakub Kicinski print("%s: OK" % (os.path.basename(__file__))) 999417ec264SJakub Kicinski 1000417ec264SJakub Kicinskifinally: 1001417ec264SJakub Kicinski log("Clean up...", "", level=1) 1002417ec264SJakub Kicinski log_level_inc() 1003417ec264SJakub Kicinski clean_up() 1004