1#!/usr/bin/python3 2 3# Copyright (C) 2017 Netronome Systems, Inc. 4# 5# This software is licensed under the GNU General License Version 2, 6# June 1991 as shown in the file COPYING in the top-level directory of this 7# source tree. 8# 9# THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" 10# WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, 11# BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 12# FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE 13# OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME 14# THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 15 16from datetime import datetime 17import argparse 18import json 19import os 20import pprint 21import random 22import string 23import struct 24import subprocess 25import time 26 27logfile = None 28log_level = 1 29skip_extack = False 30bpf_test_dir = os.path.dirname(os.path.realpath(__file__)) 31pp = pprint.PrettyPrinter() 32devs = [] # devices we created for clean up 33files = [] # files to be removed 34netns = [] # net namespaces to be removed 35 36def log_get_sec(level=0): 37 return "*" * (log_level + level) 38 39def log_level_inc(add=1): 40 global log_level 41 log_level += add 42 43def log_level_dec(sub=1): 44 global log_level 45 log_level -= sub 46 47def log_level_set(level): 48 global log_level 49 log_level = level 50 51def log(header, data, level=None): 52 """ 53 Output to an optional log. 54 """ 55 if logfile is None: 56 return 57 if level is not None: 58 log_level_set(level) 59 60 if not isinstance(data, str): 61 data = pp.pformat(data) 62 63 if len(header): 64 logfile.write("\n" + log_get_sec() + " ") 65 logfile.write(header) 66 if len(header) and len(data.strip()): 67 logfile.write("\n") 68 logfile.write(data) 69 70def skip(cond, msg): 71 if not cond: 72 return 73 print("SKIP: " + msg) 74 log("SKIP: " + msg, "", level=1) 75 os.sys.exit(0) 76 77def fail(cond, msg): 78 if not cond: 79 return 80 print("FAIL: " + msg) 81 log("FAIL: " + msg, "", level=1) 82 os.sys.exit(1) 83 84def start_test(msg): 85 log(msg, "", level=1) 86 log_level_inc() 87 print(msg) 88 89def cmd(cmd, shell=True, include_stderr=False, background=False, fail=True): 90 """ 91 Run a command in subprocess and return tuple of (retval, stdout); 92 optionally return stderr as well as third value. 93 """ 94 proc = subprocess.Popen(cmd, shell=shell, stdout=subprocess.PIPE, 95 stderr=subprocess.PIPE) 96 if background: 97 msg = "%s START: %s" % (log_get_sec(1), 98 datetime.now().strftime("%H:%M:%S.%f")) 99 log("BKG " + proc.args, msg) 100 return proc 101 102 return cmd_result(proc, include_stderr=include_stderr, fail=fail) 103 104def cmd_result(proc, include_stderr=False, fail=False): 105 stdout, stderr = proc.communicate() 106 stdout = stdout.decode("utf-8") 107 stderr = stderr.decode("utf-8") 108 proc.stdout.close() 109 proc.stderr.close() 110 111 stderr = "\n" + stderr 112 if stderr[-1] == "\n": 113 stderr = stderr[:-1] 114 115 sec = log_get_sec(1) 116 log("CMD " + proc.args, 117 "RETCODE: %d\n%s STDOUT:\n%s%s STDERR:%s\n%s END: %s" % 118 (proc.returncode, sec, stdout, sec, stderr, 119 sec, datetime.now().strftime("%H:%M:%S.%f"))) 120 121 if proc.returncode != 0 and fail: 122 if len(stderr) > 0 and stderr[-1] == "\n": 123 stderr = stderr[:-1] 124 raise Exception("Command failed: %s\n%s" % (proc.args, stderr)) 125 126 if include_stderr: 127 return proc.returncode, stdout, stderr 128 else: 129 return proc.returncode, stdout 130 131def rm(f): 132 cmd("rm -f %s" % (f)) 133 if f in files: 134 files.remove(f) 135 136def tool(name, args, flags, JSON=True, ns="", fail=True, include_stderr=False): 137 params = "" 138 if JSON: 139 params += "%s " % (flags["json"]) 140 141 if ns != "": 142 ns = "ip netns exec %s " % (ns) 143 144 if include_stderr: 145 ret, stdout, stderr = cmd(ns + name + " " + params + args, 146 fail=fail, include_stderr=True) 147 else: 148 ret, stdout = cmd(ns + name + " " + params + args, 149 fail=fail, include_stderr=False) 150 151 if JSON and len(stdout.strip()) != 0: 152 out = json.loads(stdout) 153 else: 154 out = stdout 155 156 if include_stderr: 157 return ret, out, stderr 158 else: 159 return ret, out 160 161def bpftool(args, JSON=True, ns="", fail=True): 162 return tool("bpftool", args, {"json":"-p"}, JSON=JSON, ns=ns, fail=fail) 163 164def bpftool_prog_list(expected=None, ns=""): 165 _, progs = bpftool("prog show", JSON=True, ns=ns, fail=True) 166 if expected is not None: 167 if len(progs) != expected: 168 fail(True, "%d BPF programs loaded, expected %d" % 169 (len(progs), expected)) 170 return progs 171 172def bpftool_map_list(expected=None, ns=""): 173 _, maps = bpftool("map show", JSON=True, ns=ns, fail=True) 174 if expected is not None: 175 if len(maps) != expected: 176 fail(True, "%d BPF maps loaded, expected %d" % 177 (len(maps), expected)) 178 return maps 179 180def bpftool_prog_list_wait(expected=0, n_retry=20): 181 for i in range(n_retry): 182 nprogs = len(bpftool_prog_list()) 183 if nprogs == expected: 184 return 185 time.sleep(0.05) 186 raise Exception("Time out waiting for program counts to stabilize want %d, have %d" % (expected, nprogs)) 187 188def bpftool_map_list_wait(expected=0, n_retry=20): 189 for i in range(n_retry): 190 nmaps = len(bpftool_map_list()) 191 if nmaps == expected: 192 return 193 time.sleep(0.05) 194 raise Exception("Time out waiting for map counts to stabilize want %d, have %d" % (expected, nmaps)) 195 196def ip(args, force=False, JSON=True, ns="", fail=True, include_stderr=False): 197 if force: 198 args = "-force " + args 199 return tool("ip", args, {"json":"-j"}, JSON=JSON, ns=ns, 200 fail=fail, include_stderr=include_stderr) 201 202def tc(args, JSON=True, ns="", fail=True, include_stderr=False): 203 return tool("tc", args, {"json":"-p"}, JSON=JSON, ns=ns, 204 fail=fail, include_stderr=include_stderr) 205 206def ethtool(dev, opt, args, fail=True): 207 return cmd("ethtool %s %s %s" % (opt, dev["ifname"], args), fail=fail) 208 209def bpf_obj(name, sec=".text", path=bpf_test_dir,): 210 return "obj %s sec %s" % (os.path.join(path, name), sec) 211 212def bpf_pinned(name): 213 return "pinned %s" % (name) 214 215def bpf_bytecode(bytecode): 216 return "bytecode \"%s\"" % (bytecode) 217 218def mknetns(n_retry=10): 219 for i in range(n_retry): 220 name = ''.join([random.choice(string.ascii_letters) for i in range(8)]) 221 ret, _ = ip("netns add %s" % (name), fail=False) 222 if ret == 0: 223 netns.append(name) 224 return name 225 return None 226 227def int2str(fmt, val): 228 ret = [] 229 for b in struct.pack(fmt, val): 230 ret.append(int(b)) 231 return " ".join(map(lambda x: str(x), ret)) 232 233def str2int(strtab): 234 inttab = [] 235 for i in strtab: 236 inttab.append(int(i, 16)) 237 ba = bytearray(inttab) 238 if len(strtab) == 4: 239 fmt = "I" 240 elif len(strtab) == 8: 241 fmt = "Q" 242 else: 243 raise Exception("String array of len %d can't be unpacked to an int" % 244 (len(strtab))) 245 return struct.unpack(fmt, ba)[0] 246 247class DebugfsDir: 248 """ 249 Class for accessing DebugFS directories as a dictionary. 250 """ 251 252 def __init__(self, path): 253 self.path = path 254 self._dict = self._debugfs_dir_read(path) 255 256 def __len__(self): 257 return len(self._dict.keys()) 258 259 def __getitem__(self, key): 260 if type(key) is int: 261 key = list(self._dict.keys())[key] 262 return self._dict[key] 263 264 def __setitem__(self, key, value): 265 log("DebugFS set %s = %s" % (key, value), "") 266 log_level_inc() 267 268 cmd("echo '%s' > %s/%s" % (value, self.path, key)) 269 log_level_dec() 270 271 _, out = cmd('cat %s/%s' % (self.path, key)) 272 self._dict[key] = out.strip() 273 274 def _debugfs_dir_read(self, path): 275 dfs = {} 276 277 log("DebugFS state for %s" % (path), "") 278 log_level_inc(add=2) 279 280 _, out = cmd('ls ' + path) 281 for f in out.split(): 282 p = os.path.join(path, f) 283 if os.path.isfile(p): 284 _, out = cmd('cat %s/%s' % (path, f)) 285 dfs[f] = out.strip() 286 elif os.path.isdir(p): 287 dfs[f] = DebugfsDir(p) 288 else: 289 raise Exception("%s is neither file nor directory" % (p)) 290 291 log_level_dec() 292 log("DebugFS state", dfs) 293 log_level_dec() 294 295 return dfs 296 297class NetdevSim: 298 """ 299 Class for netdevsim netdevice and its attributes. 300 """ 301 302 def __init__(self): 303 self.dev = self._netdevsim_create() 304 devs.append(self) 305 306 self.ns = "" 307 308 self.dfs_dir = '/sys/kernel/debug/netdevsim/%s' % (self.dev['ifname']) 309 self.dfs_refresh() 310 311 def __getitem__(self, key): 312 return self.dev[key] 313 314 def _netdevsim_create(self): 315 _, old = ip("link show") 316 ip("link add sim%d type netdevsim") 317 _, new = ip("link show") 318 319 for dev in new: 320 f = filter(lambda x: x["ifname"] == dev["ifname"], old) 321 if len(list(f)) == 0: 322 return dev 323 324 raise Exception("failed to create netdevsim device") 325 326 def remove(self): 327 devs.remove(self) 328 ip("link del dev %s" % (self.dev["ifname"]), ns=self.ns) 329 330 def dfs_refresh(self): 331 self.dfs = DebugfsDir(self.dfs_dir) 332 return self.dfs 333 334 def dfs_num_bound_progs(self): 335 path = os.path.join(self.dfs_dir, "bpf_bound_progs") 336 _, progs = cmd('ls %s' % (path)) 337 return len(progs.split()) 338 339 def dfs_get_bound_progs(self, expected): 340 progs = DebugfsDir(os.path.join(self.dfs_dir, "bpf_bound_progs")) 341 if expected is not None: 342 if len(progs) != expected: 343 fail(True, "%d BPF programs bound, expected %d" % 344 (len(progs), expected)) 345 return progs 346 347 def wait_for_flush(self, bound=0, total=0, n_retry=20): 348 for i in range(n_retry): 349 nbound = self.dfs_num_bound_progs() 350 nprogs = len(bpftool_prog_list()) 351 if nbound == bound and nprogs == total: 352 return 353 time.sleep(0.05) 354 raise Exception("Time out waiting for program counts to stabilize want %d/%d, have %d bound, %d loaded" % (bound, total, nbound, nprogs)) 355 356 def set_ns(self, ns): 357 name = "1" if ns == "" else ns 358 ip("link set dev %s netns %s" % (self.dev["ifname"], name), ns=self.ns) 359 self.ns = ns 360 361 def set_mtu(self, mtu, fail=True): 362 return ip("link set dev %s mtu %d" % (self.dev["ifname"], mtu), 363 fail=fail) 364 365 def set_xdp(self, bpf, mode, force=False, JSON=True, verbose=False, 366 fail=True, include_stderr=False): 367 if verbose: 368 bpf += " verbose" 369 return ip("link set dev %s xdp%s %s" % (self.dev["ifname"], mode, bpf), 370 force=force, JSON=JSON, 371 fail=fail, include_stderr=include_stderr) 372 373 def unset_xdp(self, mode, force=False, JSON=True, 374 fail=True, include_stderr=False): 375 return ip("link set dev %s xdp%s off" % (self.dev["ifname"], mode), 376 force=force, JSON=JSON, 377 fail=fail, include_stderr=include_stderr) 378 379 def ip_link_show(self, xdp): 380 _, link = ip("link show dev %s" % (self['ifname'])) 381 if len(link) > 1: 382 raise Exception("Multiple objects on ip link show") 383 if len(link) < 1: 384 return {} 385 fail(xdp != "xdp" in link, 386 "XDP program not reporting in iplink (reported %s, expected %s)" % 387 ("xdp" in link, xdp)) 388 return link[0] 389 390 def tc_add_ingress(self): 391 tc("qdisc add dev %s ingress" % (self['ifname'])) 392 393 def tc_del_ingress(self): 394 tc("qdisc del dev %s ingress" % (self['ifname'])) 395 396 def tc_flush_filters(self, bound=0, total=0): 397 self.tc_del_ingress() 398 self.tc_add_ingress() 399 self.wait_for_flush(bound=bound, total=total) 400 401 def tc_show_ingress(self, expected=None): 402 # No JSON support, oh well... 403 flags = ["skip_sw", "skip_hw", "in_hw"] 404 named = ["protocol", "pref", "chain", "handle", "id", "tag"] 405 406 args = "-s filter show dev %s ingress" % (self['ifname']) 407 _, out = tc(args, JSON=False) 408 409 filters = [] 410 lines = out.split('\n') 411 for line in lines: 412 words = line.split() 413 if "handle" not in words: 414 continue 415 fltr = {} 416 for flag in flags: 417 fltr[flag] = flag in words 418 for name in named: 419 try: 420 idx = words.index(name) 421 fltr[name] = words[idx + 1] 422 except ValueError: 423 pass 424 filters.append(fltr) 425 426 if expected is not None: 427 fail(len(filters) != expected, 428 "%d ingress filters loaded, expected %d" % 429 (len(filters), expected)) 430 return filters 431 432 def cls_filter_op(self, op, qdisc="ingress", prio=None, handle=None, 433 chain=None, cls="", params="", 434 fail=True, include_stderr=False): 435 spec = "" 436 if prio is not None: 437 spec += " prio %d" % (prio) 438 if handle: 439 spec += " handle %s" % (handle) 440 if chain is not None: 441 spec += " chain %d" % (chain) 442 443 return tc("filter {op} dev {dev} {qdisc} {spec} {cls} {params}"\ 444 .format(op=op, dev=self['ifname'], qdisc=qdisc, spec=spec, 445 cls=cls, params=params), 446 fail=fail, include_stderr=include_stderr) 447 448 def cls_bpf_add_filter(self, bpf, op="add", prio=None, handle=None, 449 chain=None, da=False, verbose=False, 450 skip_sw=False, skip_hw=False, 451 fail=True, include_stderr=False): 452 cls = "bpf " + bpf 453 454 params = "" 455 if da: 456 params += " da" 457 if verbose: 458 params += " verbose" 459 if skip_sw: 460 params += " skip_sw" 461 if skip_hw: 462 params += " skip_hw" 463 464 return self.cls_filter_op(op=op, prio=prio, handle=handle, cls=cls, 465 chain=chain, params=params, 466 fail=fail, include_stderr=include_stderr) 467 468 def set_ethtool_tc_offloads(self, enable, fail=True): 469 args = "hw-tc-offload %s" % ("on" if enable else "off") 470 return ethtool(self, "-K", args, fail=fail) 471 472################################################################################ 473def clean_up(): 474 global files, netns, devs 475 476 for dev in devs: 477 dev.remove() 478 for f in files: 479 cmd("rm -f %s" % (f)) 480 for ns in netns: 481 cmd("ip netns delete %s" % (ns)) 482 files = [] 483 netns = [] 484 485def pin_prog(file_name, idx=0): 486 progs = bpftool_prog_list(expected=(idx + 1)) 487 prog = progs[idx] 488 bpftool("prog pin id %d %s" % (prog["id"], file_name)) 489 files.append(file_name) 490 491 return file_name, bpf_pinned(file_name) 492 493def pin_map(file_name, idx=0, expected=1): 494 maps = bpftool_map_list(expected=expected) 495 m = maps[idx] 496 bpftool("map pin id %d %s" % (m["id"], file_name)) 497 files.append(file_name) 498 499 return file_name, bpf_pinned(file_name) 500 501def check_dev_info_removed(prog_file=None, map_file=None): 502 bpftool_prog_list(expected=0) 503 ret, err = bpftool("prog show pin %s" % (prog_file), fail=False) 504 fail(ret == 0, "Showing prog with removed device did not fail") 505 fail(err["error"].find("No such device") == -1, 506 "Showing prog with removed device expected ENODEV, error is %s" % 507 (err["error"])) 508 509 bpftool_map_list(expected=0) 510 ret, err = bpftool("map show pin %s" % (map_file), fail=False) 511 fail(ret == 0, "Showing map with removed device did not fail") 512 fail(err["error"].find("No such device") == -1, 513 "Showing map with removed device expected ENODEV, error is %s" % 514 (err["error"])) 515 516def check_dev_info(other_ns, ns, prog_file=None, map_file=None, removed=False): 517 progs = bpftool_prog_list(expected=1, ns=ns) 518 prog = progs[0] 519 520 fail("dev" not in prog.keys(), "Device parameters not reported") 521 dev = prog["dev"] 522 fail("ifindex" not in dev.keys(), "Device parameters not reported") 523 fail("ns_dev" not in dev.keys(), "Device parameters not reported") 524 fail("ns_inode" not in dev.keys(), "Device parameters not reported") 525 526 if not other_ns: 527 fail("ifname" not in dev.keys(), "Ifname not reported") 528 fail(dev["ifname"] != sim["ifname"], 529 "Ifname incorrect %s vs %s" % (dev["ifname"], sim["ifname"])) 530 else: 531 fail("ifname" in dev.keys(), "Ifname is reported for other ns") 532 533 maps = bpftool_map_list(expected=2, ns=ns) 534 for m in maps: 535 fail("dev" not in m.keys(), "Device parameters not reported") 536 fail(dev != m["dev"], "Map's device different than program's") 537 538def check_extack(output, reference, args): 539 if skip_extack: 540 return 541 lines = output.split("\n") 542 comp = len(lines) >= 2 and lines[1] == reference 543 fail(not comp, "Missing or incorrect netlink extack message") 544 545def check_extack_nsim(output, reference, args): 546 check_extack(output, "Error: netdevsim: " + reference, args) 547 548def check_no_extack(res, needle): 549 fail((res[1] + res[2]).count(needle) or (res[1] + res[2]).count("Warning:"), 550 "Found '%s' in command output, leaky extack?" % (needle)) 551 552def check_verifier_log(output, reference): 553 lines = output.split("\n") 554 for l in reversed(lines): 555 if l == reference: 556 return 557 fail(True, "Missing or incorrect message from netdevsim in verifier log") 558 559def test_spurios_extack(sim, obj, skip_hw, needle): 560 res = sim.cls_bpf_add_filter(obj, prio=1, handle=1, skip_hw=skip_hw, 561 include_stderr=True) 562 check_no_extack(res, needle) 563 res = sim.cls_bpf_add_filter(obj, op="replace", prio=1, handle=1, 564 skip_hw=skip_hw, include_stderr=True) 565 check_no_extack(res, needle) 566 res = sim.cls_filter_op(op="delete", prio=1, handle=1, cls="bpf", 567 include_stderr=True) 568 check_no_extack(res, needle) 569 570 571# Parse command line 572parser = argparse.ArgumentParser() 573parser.add_argument("--log", help="output verbose log to given file") 574args = parser.parse_args() 575if args.log: 576 logfile = open(args.log, 'w+') 577 logfile.write("# -*-Org-*-") 578 579log("Prepare...", "", level=1) 580log_level_inc() 581 582# Check permissions 583skip(os.getuid() != 0, "test must be run as root") 584 585# Check tools 586ret, progs = bpftool("prog", fail=False) 587skip(ret != 0, "bpftool not installed") 588# Check no BPF programs are loaded 589skip(len(progs) != 0, "BPF programs already loaded on the system") 590 591# Check netdevsim 592ret, out = cmd("modprobe netdevsim", fail=False) 593skip(ret != 0, "netdevsim module could not be loaded") 594 595# Check debugfs 596_, out = cmd("mount") 597if out.find("/sys/kernel/debug type debugfs") == -1: 598 cmd("mount -t debugfs none /sys/kernel/debug") 599 600# Check samples are compiled 601samples = ["sample_ret0.o", "sample_map_ret0.o"] 602for s in samples: 603 ret, out = cmd("ls %s/%s" % (bpf_test_dir, s), fail=False) 604 skip(ret != 0, "sample %s/%s not found, please compile it" % 605 (bpf_test_dir, s)) 606 607# Check if iproute2 is built with libmnl (needed by extack support) 608_, _, err = cmd("tc qdisc delete dev lo handle 0", 609 fail=False, include_stderr=True) 610if err.find("Error: Failed to find qdisc with specified handle.") == -1: 611 print("Warning: no extack message in iproute2 output, libmnl missing?") 612 log("Warning: no extack message in iproute2 output, libmnl missing?", "") 613 skip_extack = True 614 615# Check if net namespaces seem to work 616ns = mknetns() 617skip(ns is None, "Could not create a net namespace") 618cmd("ip netns delete %s" % (ns)) 619netns = [] 620 621try: 622 obj = bpf_obj("sample_ret0.o") 623 bytecode = bpf_bytecode("1,6 0 0 4294967295,") 624 625 start_test("Test destruction of generic XDP...") 626 sim = NetdevSim() 627 sim.set_xdp(obj, "generic") 628 sim.remove() 629 bpftool_prog_list_wait(expected=0) 630 631 sim = NetdevSim() 632 sim.tc_add_ingress() 633 634 start_test("Test TC non-offloaded...") 635 ret, _ = sim.cls_bpf_add_filter(obj, skip_hw=True, fail=False) 636 fail(ret != 0, "Software TC filter did not load") 637 638 start_test("Test TC non-offloaded isn't getting bound...") 639 ret, _ = sim.cls_bpf_add_filter(obj, fail=False) 640 fail(ret != 0, "Software TC filter did not load") 641 sim.dfs_get_bound_progs(expected=0) 642 643 sim.tc_flush_filters() 644 645 start_test("Test TC offloads are off by default...") 646 ret, _, err = sim.cls_bpf_add_filter(obj, skip_sw=True, 647 fail=False, include_stderr=True) 648 fail(ret == 0, "TC filter loaded without enabling TC offloads") 649 check_extack(err, "Error: TC offload is disabled on net device.", args) 650 sim.wait_for_flush() 651 652 sim.set_ethtool_tc_offloads(True) 653 sim.dfs["bpf_tc_non_bound_accept"] = "Y" 654 655 start_test("Test TC offload by default...") 656 ret, _ = sim.cls_bpf_add_filter(obj, fail=False) 657 fail(ret != 0, "Software TC filter did not load") 658 sim.dfs_get_bound_progs(expected=0) 659 ingress = sim.tc_show_ingress(expected=1) 660 fltr = ingress[0] 661 fail(not fltr["in_hw"], "Filter not offloaded by default") 662 663 sim.tc_flush_filters() 664 665 start_test("Test TC cBPF bytcode tries offload by default...") 666 ret, _ = sim.cls_bpf_add_filter(bytecode, fail=False) 667 fail(ret != 0, "Software TC filter did not load") 668 sim.dfs_get_bound_progs(expected=0) 669 ingress = sim.tc_show_ingress(expected=1) 670 fltr = ingress[0] 671 fail(not fltr["in_hw"], "Bytecode not offloaded by default") 672 673 sim.tc_flush_filters() 674 sim.dfs["bpf_tc_non_bound_accept"] = "N" 675 676 start_test("Test TC cBPF unbound bytecode doesn't offload...") 677 ret, _, err = sim.cls_bpf_add_filter(bytecode, skip_sw=True, 678 fail=False, include_stderr=True) 679 fail(ret == 0, "TC bytecode loaded for offload") 680 check_extack_nsim(err, "netdevsim configured to reject unbound programs.", 681 args) 682 sim.wait_for_flush() 683 684 start_test("Test non-0 chain offload...") 685 ret, _, err = sim.cls_bpf_add_filter(obj, chain=1, prio=1, handle=1, 686 skip_sw=True, 687 fail=False, include_stderr=True) 688 fail(ret == 0, "Offloaded a filter to chain other than 0") 689 check_extack(err, "Error: Driver supports only offload of chain 0.", args) 690 sim.tc_flush_filters() 691 692 start_test("Test TC replace...") 693 sim.cls_bpf_add_filter(obj, prio=1, handle=1) 694 sim.cls_bpf_add_filter(obj, op="replace", prio=1, handle=1) 695 sim.cls_filter_op(op="delete", prio=1, handle=1, cls="bpf") 696 697 sim.cls_bpf_add_filter(obj, prio=1, handle=1, skip_sw=True) 698 sim.cls_bpf_add_filter(obj, op="replace", prio=1, handle=1, skip_sw=True) 699 sim.cls_filter_op(op="delete", prio=1, handle=1, cls="bpf") 700 701 sim.cls_bpf_add_filter(obj, prio=1, handle=1, skip_hw=True) 702 sim.cls_bpf_add_filter(obj, op="replace", prio=1, handle=1, skip_hw=True) 703 sim.cls_filter_op(op="delete", prio=1, handle=1, cls="bpf") 704 705 start_test("Test TC replace bad flags...") 706 for i in range(3): 707 for j in range(3): 708 ret, _ = sim.cls_bpf_add_filter(obj, op="replace", prio=1, handle=1, 709 skip_sw=(j == 1), skip_hw=(j == 2), 710 fail=False) 711 fail(bool(ret) != bool(j), 712 "Software TC incorrect load in replace test, iteration %d" % 713 (j)) 714 sim.cls_filter_op(op="delete", prio=1, handle=1, cls="bpf") 715 716 start_test("Test spurious extack from the driver...") 717 test_spurios_extack(sim, obj, False, "netdevsim") 718 test_spurios_extack(sim, obj, True, "netdevsim") 719 720 sim.set_ethtool_tc_offloads(False) 721 722 test_spurios_extack(sim, obj, False, "TC offload is disabled") 723 test_spurios_extack(sim, obj, True, "TC offload is disabled") 724 725 sim.set_ethtool_tc_offloads(True) 726 727 sim.tc_flush_filters() 728 729 start_test("Test TC offloads work...") 730 ret, _, err = sim.cls_bpf_add_filter(obj, verbose=True, skip_sw=True, 731 fail=False, include_stderr=True) 732 fail(ret != 0, "TC filter did not load with TC offloads enabled") 733 check_verifier_log(err, "[netdevsim] Hello from netdevsim!") 734 735 start_test("Test TC offload basics...") 736 dfs = sim.dfs_get_bound_progs(expected=1) 737 progs = bpftool_prog_list(expected=1) 738 ingress = sim.tc_show_ingress(expected=1) 739 740 dprog = dfs[0] 741 prog = progs[0] 742 fltr = ingress[0] 743 fail(fltr["skip_hw"], "TC does reports 'skip_hw' on offloaded filter") 744 fail(not fltr["in_hw"], "TC does not report 'in_hw' for offloaded filter") 745 fail(not fltr["skip_sw"], "TC does not report 'skip_sw' back") 746 747 start_test("Test TC offload is device-bound...") 748 fail(str(prog["id"]) != fltr["id"], "Program IDs don't match") 749 fail(prog["tag"] != fltr["tag"], "Program tags don't match") 750 fail(fltr["id"] != dprog["id"], "Program IDs don't match") 751 fail(dprog["state"] != "xlated", "Offloaded program state not translated") 752 fail(dprog["loaded"] != "Y", "Offloaded program is not loaded") 753 754 start_test("Test disabling TC offloads is rejected while filters installed...") 755 ret, _ = sim.set_ethtool_tc_offloads(False, fail=False) 756 fail(ret == 0, "Driver should refuse to disable TC offloads with filters installed...") 757 758 start_test("Test qdisc removal frees things...") 759 sim.tc_flush_filters() 760 sim.tc_show_ingress(expected=0) 761 762 start_test("Test disabling TC offloads is OK without filters...") 763 ret, _ = sim.set_ethtool_tc_offloads(False, fail=False) 764 fail(ret != 0, 765 "Driver refused to disable TC offloads without filters installed...") 766 767 sim.set_ethtool_tc_offloads(True) 768 769 start_test("Test destroying device gets rid of TC filters...") 770 sim.cls_bpf_add_filter(obj, skip_sw=True) 771 sim.remove() 772 bpftool_prog_list_wait(expected=0) 773 774 sim = NetdevSim() 775 sim.set_ethtool_tc_offloads(True) 776 777 start_test("Test destroying device gets rid of XDP...") 778 sim.set_xdp(obj, "offload") 779 sim.remove() 780 bpftool_prog_list_wait(expected=0) 781 782 sim = NetdevSim() 783 sim.set_ethtool_tc_offloads(True) 784 785 start_test("Test XDP prog reporting...") 786 sim.set_xdp(obj, "drv") 787 ipl = sim.ip_link_show(xdp=True) 788 progs = bpftool_prog_list(expected=1) 789 fail(ipl["xdp"]["prog"]["id"] != progs[0]["id"], 790 "Loaded program has wrong ID") 791 792 start_test("Test XDP prog replace without force...") 793 ret, _ = sim.set_xdp(obj, "drv", fail=False) 794 fail(ret == 0, "Replaced XDP program without -force") 795 sim.wait_for_flush(total=1) 796 797 start_test("Test XDP prog replace with force...") 798 ret, _ = sim.set_xdp(obj, "drv", force=True, fail=False) 799 fail(ret != 0, "Could not replace XDP program with -force") 800 bpftool_prog_list_wait(expected=1) 801 ipl = sim.ip_link_show(xdp=True) 802 progs = bpftool_prog_list(expected=1) 803 fail(ipl["xdp"]["prog"]["id"] != progs[0]["id"], 804 "Loaded program has wrong ID") 805 fail("dev" in progs[0].keys(), 806 "Device parameters reported for non-offloaded program") 807 808 start_test("Test XDP prog replace with bad flags...") 809 ret, _, err = sim.set_xdp(obj, "offload", force=True, 810 fail=False, include_stderr=True) 811 fail(ret == 0, "Replaced XDP program with a program in different mode") 812 check_extack_nsim(err, "program loaded with different flags.", args) 813 ret, _, err = sim.set_xdp(obj, "", force=True, 814 fail=False, include_stderr=True) 815 fail(ret == 0, "Replaced XDP program with a program in different mode") 816 check_extack_nsim(err, "program loaded with different flags.", args) 817 818 start_test("Test XDP prog remove with bad flags...") 819 ret, _, err = sim.unset_xdp("offload", force=True, 820 fail=False, include_stderr=True) 821 fail(ret == 0, "Removed program with a bad mode mode") 822 check_extack_nsim(err, "program loaded with different flags.", args) 823 ret, _, err = sim.unset_xdp("", force=True, 824 fail=False, include_stderr=True) 825 fail(ret == 0, "Removed program with a bad mode mode") 826 check_extack_nsim(err, "program loaded with different flags.", args) 827 828 start_test("Test MTU restrictions...") 829 ret, _ = sim.set_mtu(9000, fail=False) 830 fail(ret == 0, 831 "Driver should refuse to increase MTU to 9000 with XDP loaded...") 832 sim.unset_xdp("drv") 833 bpftool_prog_list_wait(expected=0) 834 sim.set_mtu(9000) 835 ret, _, err = sim.set_xdp(obj, "drv", fail=False, include_stderr=True) 836 fail(ret == 0, "Driver should refuse to load program with MTU of 9000...") 837 check_extack_nsim(err, "MTU too large w/ XDP enabled.", args) 838 sim.set_mtu(1500) 839 840 sim.wait_for_flush() 841 start_test("Test XDP offload...") 842 _, _, err = sim.set_xdp(obj, "offload", verbose=True, include_stderr=True) 843 ipl = sim.ip_link_show(xdp=True) 844 link_xdp = ipl["xdp"]["prog"] 845 progs = bpftool_prog_list(expected=1) 846 prog = progs[0] 847 fail(link_xdp["id"] != prog["id"], "Loaded program has wrong ID") 848 check_verifier_log(err, "[netdevsim] Hello from netdevsim!") 849 850 start_test("Test XDP offload is device bound...") 851 dfs = sim.dfs_get_bound_progs(expected=1) 852 dprog = dfs[0] 853 854 fail(prog["id"] != link_xdp["id"], "Program IDs don't match") 855 fail(prog["tag"] != link_xdp["tag"], "Program tags don't match") 856 fail(str(link_xdp["id"]) != dprog["id"], "Program IDs don't match") 857 fail(dprog["state"] != "xlated", "Offloaded program state not translated") 858 fail(dprog["loaded"] != "Y", "Offloaded program is not loaded") 859 860 start_test("Test removing XDP program many times...") 861 sim.unset_xdp("offload") 862 sim.unset_xdp("offload") 863 sim.unset_xdp("drv") 864 sim.unset_xdp("drv") 865 sim.unset_xdp("") 866 sim.unset_xdp("") 867 bpftool_prog_list_wait(expected=0) 868 869 start_test("Test attempt to use a program for a wrong device...") 870 sim2 = NetdevSim() 871 sim2.set_xdp(obj, "offload") 872 pin_file, pinned = pin_prog("/sys/fs/bpf/tmp") 873 874 ret, _, err = sim.set_xdp(pinned, "offload", 875 fail=False, include_stderr=True) 876 fail(ret == 0, "Pinned program loaded for a different device accepted") 877 check_extack_nsim(err, "program bound to different dev.", args) 878 sim2.remove() 879 ret, _, err = sim.set_xdp(pinned, "offload", 880 fail=False, include_stderr=True) 881 fail(ret == 0, "Pinned program loaded for a removed device accepted") 882 check_extack_nsim(err, "xdpoffload of non-bound program.", args) 883 rm(pin_file) 884 bpftool_prog_list_wait(expected=0) 885 886 start_test("Test mixing of TC and XDP...") 887 sim.tc_add_ingress() 888 sim.set_xdp(obj, "offload") 889 ret, _, err = sim.cls_bpf_add_filter(obj, skip_sw=True, 890 fail=False, include_stderr=True) 891 fail(ret == 0, "Loading TC when XDP active should fail") 892 check_extack_nsim(err, "driver and netdev offload states mismatch.", args) 893 sim.unset_xdp("offload") 894 sim.wait_for_flush() 895 896 sim.cls_bpf_add_filter(obj, skip_sw=True) 897 ret, _, err = sim.set_xdp(obj, "offload", fail=False, include_stderr=True) 898 fail(ret == 0, "Loading XDP when TC active should fail") 899 check_extack_nsim(err, "TC program is already loaded.", args) 900 901 start_test("Test binding TC from pinned...") 902 pin_file, pinned = pin_prog("/sys/fs/bpf/tmp") 903 sim.tc_flush_filters(bound=1, total=1) 904 sim.cls_bpf_add_filter(pinned, da=True, skip_sw=True) 905 sim.tc_flush_filters(bound=1, total=1) 906 907 start_test("Test binding XDP from pinned...") 908 sim.set_xdp(obj, "offload") 909 pin_file, pinned = pin_prog("/sys/fs/bpf/tmp2", idx=1) 910 911 sim.set_xdp(pinned, "offload", force=True) 912 sim.unset_xdp("offload") 913 sim.set_xdp(pinned, "offload", force=True) 914 sim.unset_xdp("offload") 915 916 start_test("Test offload of wrong type fails...") 917 ret, _ = sim.cls_bpf_add_filter(pinned, da=True, skip_sw=True, fail=False) 918 fail(ret == 0, "Managed to attach XDP program to TC") 919 920 start_test("Test asking for TC offload of two filters...") 921 sim.cls_bpf_add_filter(obj, da=True, skip_sw=True) 922 ret, _, err = sim.cls_bpf_add_filter(obj, da=True, skip_sw=True, 923 fail=False, include_stderr=True) 924 fail(ret == 0, "Managed to offload two TC filters at the same time") 925 check_extack_nsim(err, "driver and netdev offload states mismatch.", args) 926 927 sim.tc_flush_filters(bound=2, total=2) 928 929 start_test("Test if netdev removal waits for translation...") 930 delay_msec = 500 931 sim.dfs["bpf_bind_verifier_delay"] = delay_msec 932 start = time.time() 933 cmd_line = "tc filter add dev %s ingress bpf %s da skip_sw" % \ 934 (sim['ifname'], obj) 935 tc_proc = cmd(cmd_line, background=True, fail=False) 936 # Wait for the verifier to start 937 while sim.dfs_num_bound_progs() <= 2: 938 pass 939 sim.remove() 940 end = time.time() 941 ret, _ = cmd_result(tc_proc, fail=False) 942 time_diff = end - start 943 log("Time", "start:\t%s\nend:\t%s\ndiff:\t%s" % (start, end, time_diff)) 944 945 fail(ret == 0, "Managed to load TC filter on a unregistering device") 946 delay_sec = delay_msec * 0.001 947 fail(time_diff < delay_sec, "Removal process took %s, expected %s" % 948 (time_diff, delay_sec)) 949 950 # Remove all pinned files and reinstantiate the netdev 951 clean_up() 952 bpftool_prog_list_wait(expected=0) 953 954 sim = NetdevSim() 955 map_obj = bpf_obj("sample_map_ret0.o") 956 start_test("Test loading program with maps...") 957 sim.set_xdp(map_obj, "offload", JSON=False) # map fixup msg breaks JSON 958 959 start_test("Test bpftool bound info reporting (own ns)...") 960 check_dev_info(False, "") 961 962 start_test("Test bpftool bound info reporting (other ns)...") 963 ns = mknetns() 964 sim.set_ns(ns) 965 check_dev_info(True, "") 966 967 start_test("Test bpftool bound info reporting (remote ns)...") 968 check_dev_info(False, ns) 969 970 start_test("Test bpftool bound info reporting (back to own ns)...") 971 sim.set_ns("") 972 check_dev_info(False, "") 973 974 prog_file, _ = pin_prog("/sys/fs/bpf/tmp_prog") 975 map_file, _ = pin_map("/sys/fs/bpf/tmp_map", idx=1, expected=2) 976 sim.remove() 977 978 start_test("Test bpftool bound info reporting (removed dev)...") 979 check_dev_info_removed(prog_file=prog_file, map_file=map_file) 980 981 # Remove all pinned files and reinstantiate the netdev 982 clean_up() 983 bpftool_prog_list_wait(expected=0) 984 985 sim = NetdevSim() 986 987 start_test("Test map update (no flags)...") 988 sim.set_xdp(map_obj, "offload", JSON=False) # map fixup msg breaks JSON 989 maps = bpftool_map_list(expected=2) 990 array = maps[0] if maps[0]["type"] == "array" else maps[1] 991 htab = maps[0] if maps[0]["type"] == "hash" else maps[1] 992 for m in maps: 993 for i in range(2): 994 bpftool("map update id %d key %s value %s" % 995 (m["id"], int2str("I", i), int2str("Q", i * 3))) 996 997 for m in maps: 998 ret, _ = bpftool("map update id %d key %s value %s" % 999 (m["id"], int2str("I", 3), int2str("Q", 3 * 3)), 1000 fail=False) 1001 fail(ret == 0, "added too many entries") 1002 1003 start_test("Test map update (exists)...") 1004 for m in maps: 1005 for i in range(2): 1006 bpftool("map update id %d key %s value %s exist" % 1007 (m["id"], int2str("I", i), int2str("Q", i * 3))) 1008 1009 for m in maps: 1010 ret, err = bpftool("map update id %d key %s value %s exist" % 1011 (m["id"], int2str("I", 3), int2str("Q", 3 * 3)), 1012 fail=False) 1013 fail(ret == 0, "updated non-existing key") 1014 fail(err["error"].find("No such file or directory") == -1, 1015 "expected ENOENT, error is '%s'" % (err["error"])) 1016 1017 start_test("Test map update (noexist)...") 1018 for m in maps: 1019 for i in range(2): 1020 ret, err = bpftool("map update id %d key %s value %s noexist" % 1021 (m["id"], int2str("I", i), int2str("Q", i * 3)), 1022 fail=False) 1023 fail(ret == 0, "updated existing key") 1024 fail(err["error"].find("File exists") == -1, 1025 "expected EEXIST, error is '%s'" % (err["error"])) 1026 1027 start_test("Test map dump...") 1028 for m in maps: 1029 _, entries = bpftool("map dump id %d" % (m["id"])) 1030 for i in range(2): 1031 key = str2int(entries[i]["key"]) 1032 fail(key != i, "expected key %d, got %d" % (key, i)) 1033 val = str2int(entries[i]["value"]) 1034 fail(val != i * 3, "expected value %d, got %d" % (val, i * 3)) 1035 1036 start_test("Test map getnext...") 1037 for m in maps: 1038 _, entry = bpftool("map getnext id %d" % (m["id"])) 1039 key = str2int(entry["next_key"]) 1040 fail(key != 0, "next key %d, expected %d" % (key, 0)) 1041 _, entry = bpftool("map getnext id %d key %s" % 1042 (m["id"], int2str("I", 0))) 1043 key = str2int(entry["next_key"]) 1044 fail(key != 1, "next key %d, expected %d" % (key, 1)) 1045 ret, err = bpftool("map getnext id %d key %s" % 1046 (m["id"], int2str("I", 1)), fail=False) 1047 fail(ret == 0, "got next key past the end of map") 1048 fail(err["error"].find("No such file or directory") == -1, 1049 "expected ENOENT, error is '%s'" % (err["error"])) 1050 1051 start_test("Test map delete (htab)...") 1052 for i in range(2): 1053 bpftool("map delete id %d key %s" % (htab["id"], int2str("I", i))) 1054 1055 start_test("Test map delete (array)...") 1056 for i in range(2): 1057 ret, err = bpftool("map delete id %d key %s" % 1058 (htab["id"], int2str("I", i)), fail=False) 1059 fail(ret == 0, "removed entry from an array") 1060 fail(err["error"].find("No such file or directory") == -1, 1061 "expected ENOENT, error is '%s'" % (err["error"])) 1062 1063 start_test("Test map remove...") 1064 sim.unset_xdp("offload") 1065 bpftool_map_list_wait(expected=0) 1066 sim.remove() 1067 1068 sim = NetdevSim() 1069 sim.set_xdp(map_obj, "offload", JSON=False) # map fixup msg breaks JSON 1070 sim.remove() 1071 bpftool_map_list_wait(expected=0) 1072 1073 start_test("Test map creation fail path...") 1074 sim = NetdevSim() 1075 sim.dfs["bpf_map_accept"] = "N" 1076 ret, _ = sim.set_xdp(map_obj, "offload", JSON=False, fail=False) 1077 fail(ret == 0, 1078 "netdevsim didn't refuse to create a map with offload disabled") 1079 1080 print("%s: OK" % (os.path.basename(__file__))) 1081 1082finally: 1083 log("Clean up...", "", level=1) 1084 log_level_inc() 1085 clean_up() 1086