1#!/usr/bin/env python3 2 3# Copyright (C) 2017 Netronome Systems, Inc. 4# Copyright (c) 2019 Mellanox Technologies. All rights reserved 5# 6# This software is licensed under the GNU General License Version 2, 7# June 1991 as shown in the file COPYING in the top-level directory of this 8# source tree. 9# 10# THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" 11# WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, 12# BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 13# FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE 14# OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME 15# THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 16 17from datetime import datetime 18import argparse 19import errno 20import json 21import os 22import pprint 23import random 24import re 25import stat 26import string 27import struct 28import subprocess 29import time 30import traceback 31 32from lib.py import NetdevSim, NetdevSimDev 33 34 35logfile = None 36log_level = 1 37skip_extack = False 38bpf_test_dir = os.path.dirname(os.path.realpath(__file__)) 39pp = pprint.PrettyPrinter() 40devs = [] # devices we created for clean up 41files = [] # files to be removed 42netns = [] # net namespaces to be removed 43 44def log_get_sec(level=0): 45 return "*" * (log_level + level) 46 47def log_level_inc(add=1): 48 global log_level 49 log_level += add 50 51def log_level_dec(sub=1): 52 global log_level 53 log_level -= sub 54 55def log_level_set(level): 56 global log_level 57 log_level = level 58 59def log(header, data, level=None): 60 """ 61 Output to an optional log. 62 """ 63 if logfile is None: 64 return 65 if level is not None: 66 log_level_set(level) 67 68 if not isinstance(data, str): 69 data = pp.pformat(data) 70 71 if len(header): 72 logfile.write("\n" + log_get_sec() + " ") 73 logfile.write(header) 74 if len(header) and len(data.strip()): 75 logfile.write("\n") 76 logfile.write(data) 77 78def skip(cond, msg): 79 if not cond: 80 return 81 print("SKIP: " + msg) 82 log("SKIP: " + msg, "", level=1) 83 os.sys.exit(0) 84 85def fail(cond, msg): 86 if not cond: 87 return 88 print("FAIL: " + msg) 89 tb = "".join(traceback.extract_stack().format()) 90 print(tb) 91 log("FAIL: " + msg, tb, level=1) 92 os.sys.exit(1) 93 94def start_test(msg): 95 log(msg, "", level=1) 96 log_level_inc() 97 print(msg) 98 99def cmd(cmd, shell=True, include_stderr=False, background=False, fail=True): 100 """ 101 Run a command in subprocess and return tuple of (retval, stdout); 102 optionally return stderr as well as third value. 103 """ 104 proc = subprocess.Popen(cmd, shell=shell, stdout=subprocess.PIPE, 105 stderr=subprocess.PIPE) 106 if background: 107 msg = "%s START: %s" % (log_get_sec(1), 108 datetime.now().strftime("%H:%M:%S.%f")) 109 log("BKG " + proc.args, msg) 110 return proc 111 112 return cmd_result(proc, include_stderr=include_stderr, fail=fail) 113 114def cmd_result(proc, include_stderr=False, fail=False): 115 stdout, stderr = proc.communicate() 116 stdout = stdout.decode("utf-8") 117 stderr = stderr.decode("utf-8") 118 proc.stdout.close() 119 proc.stderr.close() 120 121 stderr = "\n" + stderr 122 if stderr[-1] == "\n": 123 stderr = stderr[:-1] 124 125 sec = log_get_sec(1) 126 log("CMD " + proc.args, 127 "RETCODE: %d\n%s STDOUT:\n%s%s STDERR:%s\n%s END: %s" % 128 (proc.returncode, sec, stdout, sec, stderr, 129 sec, datetime.now().strftime("%H:%M:%S.%f"))) 130 131 if proc.returncode != 0 and fail: 132 if len(stderr) > 0 and stderr[-1] == "\n": 133 stderr = stderr[:-1] 134 raise Exception("Command failed: %s\n%s" % (proc.args, stderr)) 135 136 if include_stderr: 137 return proc.returncode, stdout, stderr 138 else: 139 return proc.returncode, stdout 140 141def rm(f): 142 cmd("rm -f %s" % (f)) 143 if f in files: 144 files.remove(f) 145 146def tool(name, args, flags, JSON=True, ns="", fail=True, include_stderr=False): 147 params = "" 148 if JSON: 149 params += "%s " % (flags["json"]) 150 151 if ns: 152 ns = "ip netns exec %s " % (ns) 153 elif ns is None: 154 ns = "" 155 156 if include_stderr: 157 ret, stdout, stderr = cmd(ns + name + " " + params + args, 158 fail=fail, include_stderr=True) 159 else: 160 ret, stdout = cmd(ns + name + " " + params + args, 161 fail=fail, include_stderr=False) 162 163 if JSON and len(stdout.strip()) != 0: 164 out = json.loads(stdout) 165 else: 166 out = stdout 167 168 if include_stderr: 169 return ret, out, stderr 170 else: 171 return ret, out 172 173def bpftool(args, JSON=True, ns="", fail=True, include_stderr=False): 174 return tool("bpftool", args, {"json":"-p"}, JSON=JSON, ns=ns, 175 fail=fail, include_stderr=include_stderr) 176 177def bpftool_prog_list(expected=None, ns="", exclude_orphaned=True): 178 _, progs = bpftool("prog show", JSON=True, ns=ns, fail=True) 179 # Remove the base progs 180 for p in base_progs: 181 if p in progs: 182 progs.remove(p) 183 if exclude_orphaned: 184 progs = [ p for p in progs if not p['orphaned'] ] 185 if expected is not None: 186 if len(progs) != expected: 187 fail(True, "%d BPF programs loaded, expected %d\nLoaded Progs:\n%s" % 188 (len(progs), expected, pp.pformat(progs))) 189 return progs 190 191def bpftool_map_list(expected=None, ns=""): 192 _, maps = bpftool("map show", JSON=True, ns=ns, fail=True) 193 # Remove the base maps 194 maps = [m for m in maps if m not in base_maps and m.get('name') and m.get('name') not in base_map_names] 195 if expected is not None: 196 if len(maps) != expected: 197 fail(True, "%d BPF maps loaded, expected %d" % 198 (len(maps), expected)) 199 return maps 200 201def bpftool_prog_list_wait(expected=0, n_retry=20): 202 for i in range(n_retry): 203 nprogs = len(bpftool_prog_list()) 204 if nprogs == expected: 205 return 206 time.sleep(0.05) 207 raise Exception("Time out waiting for program counts to stabilize want %d, have %d" % (expected, nprogs)) 208 209def bpftool_map_list_wait(expected=0, n_retry=20, ns=""): 210 nmaps = None 211 for i in range(n_retry): 212 maps = bpftool_map_list(ns=ns) 213 nmaps = len(maps) 214 if nmaps == expected: 215 return maps 216 time.sleep(0.05) 217 raise Exception("Time out waiting for map counts to stabilize want %d, have %d" % (expected, nmaps)) 218 219def bpftool_prog_load(sample, file_name, maps=[], prog_type="xdp", dev=None, 220 fail=True, include_stderr=False, dev_bind=None): 221 args = "prog load %s %s" % (os.path.join(bpf_test_dir, sample), file_name) 222 if prog_type is not None: 223 args += " type " + prog_type 224 if dev is not None: 225 args += " dev " + dev 226 elif dev_bind is not None: 227 args += " xdpmeta_dev " + dev_bind 228 if len(maps): 229 args += " map " + " map ".join(maps) 230 231 res = bpftool(args, fail=fail, include_stderr=include_stderr) 232 if res[0] == 0: 233 files.append(file_name) 234 return res 235 236def ip(args, force=False, JSON=True, ns="", fail=True, include_stderr=False): 237 if force: 238 args = "-force " + args 239 return tool("ip", args, {"json":"-j"}, JSON=JSON, ns=ns, 240 fail=fail, include_stderr=include_stderr) 241 242def tc(args, JSON=True, ns="", fail=True, include_stderr=False): 243 return tool("tc", args, {"json":"-p"}, JSON=JSON, ns=ns, 244 fail=fail, include_stderr=include_stderr) 245 246def ethtool(dev, opt, args, fail=True): 247 return cmd("ethtool %s %s %s" % (opt, dev["ifname"], args), fail=fail) 248 249def bpf_obj(name, sec="xdp", path=bpf_test_dir,): 250 return "obj %s sec %s" % (os.path.join(path, name), sec) 251 252def bpf_pinned(name): 253 return "pinned %s" % (name) 254 255def bpf_bytecode(bytecode): 256 return "bytecode \"%s\"" % (bytecode) 257 258def mknetns(n_retry=10): 259 for i in range(n_retry): 260 name = ''.join([random.choice(string.ascii_letters) for i in range(8)]) 261 ret, _ = ip("netns add %s" % (name), fail=False) 262 if ret == 0: 263 netns.append(name) 264 return name 265 return None 266 267def int2str(fmt, val): 268 ret = [] 269 for b in struct.pack(fmt, val): 270 ret.append(int(b)) 271 return " ".join(map(lambda x: str(x), ret)) 272 273def str2int(strtab): 274 inttab = [] 275 for i in strtab: 276 inttab.append(int(i, 16)) 277 ba = bytearray(inttab) 278 if len(strtab) == 4: 279 fmt = "I" 280 elif len(strtab) == 8: 281 fmt = "Q" 282 else: 283 raise Exception("String array of len %d can't be unpacked to an int" % 284 (len(strtab))) 285 return struct.unpack(fmt, ba)[0] 286 287class DebugfsDir: 288 """ 289 Class for accessing DebugFS directories as a dictionary. 290 """ 291 292 def __init__(self, path): 293 self.path = path 294 self._dict = self._debugfs_dir_read(path) 295 296 def __len__(self): 297 return len(self._dict.keys()) 298 299 def __getitem__(self, key): 300 if type(key) is int: 301 key = list(self._dict.keys())[key] 302 return self._dict[key] 303 304 def __setitem__(self, key, value): 305 log("DebugFS set %s = %s" % (key, value), "") 306 log_level_inc() 307 308 cmd("echo '%s' > %s/%s" % (value, self.path, key)) 309 log_level_dec() 310 311 _, out = cmd('cat %s/%s' % (self.path, key)) 312 self._dict[key] = out.strip() 313 314 def _debugfs_dir_read(self, path): 315 dfs = {} 316 317 log("DebugFS state for %s" % (path), "") 318 log_level_inc(add=2) 319 320 _, out = cmd('ls ' + path) 321 for f in out.split(): 322 if f == "ports": 323 continue 324 325 p = os.path.join(path, f) 326 if not os.stat(p).st_mode & stat.S_IRUSR: 327 continue 328 329 if os.path.isfile(p): 330 # We need to init trap_flow_action_cookie before read it 331 if f == "trap_flow_action_cookie": 332 cmd('echo deadbeef > %s/%s' % (path, f)) 333 _, out = cmd('cat %s/%s' % (path, f)) 334 dfs[f] = out.strip() 335 elif os.path.isdir(p): 336 dfs[f] = DebugfsDir(p) 337 else: 338 raise Exception("%s is neither file nor directory" % (p)) 339 340 log_level_dec() 341 log("DebugFS state", dfs) 342 log_level_dec() 343 344 return dfs 345 346class BpfNetdevSimDev(NetdevSimDev): 347 """ 348 Class for netdevsim bus device and its attributes. 349 """ 350 def __init__(self, port_count=1, ns=None): 351 super().__init__(port_count, ns=ns) 352 devs.append(self) 353 354 def _make_port(self, port_index, ifname): 355 return BpfNetdevSim(self, port_index, ifname, self.ns) 356 357 def dfs_num_bound_progs(self): 358 path = os.path.join(self.dfs_dir, "bpf_bound_progs") 359 _, progs = cmd('ls %s' % (path)) 360 return len(progs.split()) 361 362 def dfs_get_bound_progs(self, expected): 363 progs = DebugfsDir(os.path.join(self.dfs_dir, "bpf_bound_progs")) 364 if expected is not None: 365 if len(progs) != expected: 366 fail(True, "%d BPF programs bound, expected %d" % 367 (len(progs), expected)) 368 return progs 369 370 def remove(self): 371 super().remove() 372 devs.remove(self) 373 374 375class BpfNetdevSim(NetdevSim): 376 """ 377 Class for netdevsim netdevice and its attributes. 378 """ 379 380 def __init__(self, nsimdev, port_index, ifname, ns=None): 381 super().__init__(nsimdev, port_index, ifname, ns=ns) 382 383 self.dfs_dir = "%s/ports/%u/" % (nsimdev.dfs_dir, port_index) 384 self.dfs_refresh() 385 386 def __getitem__(self, key): 387 return self.dev[key] 388 389 def remove(self): 390 self.nsimdev.remove_nsim(self) 391 392 def dfs_refresh(self): 393 self.dfs = DebugfsDir(self.dfs_dir) 394 return self.dfs 395 396 def dfs_read(self, f): 397 path = os.path.join(self.dfs_dir, f) 398 _, data = cmd('cat %s' % (path)) 399 return data.strip() 400 401 def wait_for_flush(self, bound=0, total=0, n_retry=20): 402 for i in range(n_retry): 403 nbound = self.nsimdev.dfs_num_bound_progs() 404 nprogs = len(bpftool_prog_list()) 405 if nbound == bound and nprogs == total: 406 return 407 time.sleep(0.05) 408 raise Exception("Time out waiting for program counts to stabilize want %d/%d, have %d bound, %d loaded" % (bound, total, nbound, nprogs)) 409 410 def set_ns(self, ns): 411 name = ns if ns else "1" 412 ip("link set dev %s netns %s" % (self.dev["ifname"], name), ns=self.ns) 413 self.ns = ns 414 415 def set_mtu(self, mtu, fail=True): 416 return ip("link set dev %s mtu %d" % (self.dev["ifname"], mtu), 417 fail=fail) 418 419 def set_xdp(self, bpf, mode, force=False, JSON=True, verbose=False, 420 fail=True, include_stderr=False): 421 if verbose: 422 bpf += " verbose" 423 return ip("link set dev %s xdp%s %s" % (self.dev["ifname"], mode, bpf), 424 force=force, JSON=JSON, 425 fail=fail, include_stderr=include_stderr) 426 427 def unset_xdp(self, mode, force=False, JSON=True, 428 fail=True, include_stderr=False): 429 return ip("link set dev %s xdp%s off" % (self.dev["ifname"], mode), 430 force=force, JSON=JSON, 431 fail=fail, include_stderr=include_stderr) 432 433 def ip_link_show(self, xdp): 434 _, link = ip("link show dev %s" % (self['ifname'])) 435 if len(link) > 1: 436 raise Exception("Multiple objects on ip link show") 437 if len(link) < 1: 438 return {} 439 fail(xdp != "xdp" in link, 440 "XDP program not reporting in iplink (reported %s, expected %s)" % 441 ("xdp" in link, xdp)) 442 return link[0] 443 444 def tc_add_ingress(self): 445 tc("qdisc add dev %s ingress" % (self['ifname'])) 446 447 def tc_del_ingress(self): 448 tc("qdisc del dev %s ingress" % (self['ifname'])) 449 450 def tc_flush_filters(self, bound=0, total=0): 451 self.tc_del_ingress() 452 self.tc_add_ingress() 453 self.wait_for_flush(bound=bound, total=total) 454 455 def tc_add_block(self, block, fail=True, include_stderr=False): 456 return tc("qdisc add dev %s ingress_block %d clsact" % 457 (self['ifname'], block), fail=fail, 458 include_stderr=include_stderr) 459 460 def tc_del_block(self, block): 461 tc("qdisc del dev %s clsact" % (self['ifname']), fail=False) 462 463 def tc_add_block_filter(self, block, bpf, op="add", prio=None, handle=None, 464 da=False, verbose=False, skip_sw=False, 465 skip_hw=False, fail=True, include_stderr=False): 466 spec = "" 467 if prio is not None: 468 spec += " prio %d" % (prio) 469 if handle: 470 spec += " handle %s" % (handle) 471 472 params = "" 473 if da: 474 params += " da" 475 if verbose: 476 params += " verbose" 477 if skip_sw: 478 params += " skip_sw" 479 if skip_hw: 480 params += " skip_hw" 481 482 return tc("filter {op} block {block} {spec} bpf {bpf} {params}" 483 .format(op=op, block=block, spec=spec, bpf=bpf, 484 params=params), 485 fail=fail, include_stderr=include_stderr) 486 487 def tc_show_ingress(self, expected=None): 488 # No JSON support, oh well... 489 flags = ["skip_sw", "skip_hw", "in_hw"] 490 named = ["protocol", "pref", "chain", "handle", "id", "tag"] 491 492 args = "-s filter show dev %s ingress" % (self['ifname']) 493 _, out = tc(args, JSON=False) 494 495 filters = [] 496 lines = out.split('\n') 497 for line in lines: 498 words = line.split() 499 if "handle" not in words: 500 continue 501 fltr = {} 502 for flag in flags: 503 fltr[flag] = flag in words 504 for name in named: 505 try: 506 idx = words.index(name) 507 fltr[name] = words[idx + 1] 508 except ValueError: 509 pass 510 filters.append(fltr) 511 512 if expected is not None: 513 fail(len(filters) != expected, 514 "%d ingress filters loaded, expected %d" % 515 (len(filters), expected)) 516 return filters 517 518 def cls_filter_op(self, op, qdisc="ingress", prio=None, handle=None, 519 chain=None, cls="", params="", 520 fail=True, include_stderr=False): 521 spec = "" 522 if prio is not None: 523 spec += " prio %d" % (prio) 524 if handle: 525 spec += " handle %s" % (handle) 526 if chain is not None: 527 spec += " chain %d" % (chain) 528 529 return tc("filter {op} dev {dev} {qdisc} {spec} {cls} {params}"\ 530 .format(op=op, dev=self['ifname'], qdisc=qdisc, spec=spec, 531 cls=cls, params=params), 532 fail=fail, include_stderr=include_stderr) 533 534 def cls_bpf_add_filter(self, bpf, op="add", prio=None, handle=None, 535 chain=None, da=False, verbose=False, 536 skip_sw=False, skip_hw=False, 537 fail=True, include_stderr=False): 538 cls = "bpf " + bpf 539 540 params = "" 541 if da: 542 params += " da" 543 if verbose: 544 params += " verbose" 545 if skip_sw: 546 params += " skip_sw" 547 if skip_hw: 548 params += " skip_hw" 549 550 return self.cls_filter_op(op=op, prio=prio, handle=handle, cls=cls, 551 chain=chain, params=params, 552 fail=fail, include_stderr=include_stderr) 553 554 def set_ethtool_tc_offloads(self, enable, fail=True): 555 args = "hw-tc-offload %s" % ("on" if enable else "off") 556 return ethtool(self, "-K", args, fail=fail) 557 558################################################################################ 559def clean_up(): 560 global files, netns, devs 561 562 for dev in devs: 563 dev.remove() 564 for f in files: 565 cmd("rm -f %s" % (f)) 566 for ns in netns: 567 cmd("ip netns delete %s" % (ns)) 568 files = [] 569 netns = [] 570 571def pin_prog(file_name, idx=0): 572 progs = bpftool_prog_list(expected=(idx + 1)) 573 prog = progs[idx] 574 bpftool("prog pin id %d %s" % (prog["id"], file_name)) 575 files.append(file_name) 576 577 return file_name, bpf_pinned(file_name) 578 579def pin_map(file_name, idx=0, expected=1): 580 maps = bpftool_map_list_wait(expected=expected) 581 m = maps[idx] 582 bpftool("map pin id %d %s" % (m["id"], file_name)) 583 files.append(file_name) 584 585 return file_name, bpf_pinned(file_name) 586 587def check_dev_info_removed(prog_file=None, map_file=None): 588 bpftool_prog_list(expected=0) 589 bpftool_prog_list(expected=1, exclude_orphaned=False) 590 ret, err = bpftool("prog show pin %s" % (prog_file), fail=False) 591 fail(ret != 0, "failed to show prog with removed device") 592 593 bpftool_map_list_wait(expected=0) 594 ret, err = bpftool("map show pin %s" % (map_file), fail=False) 595 fail(ret == 0, "Showing map with removed device did not fail") 596 fail(err["error"].find("No such device") == -1, 597 "Showing map with removed device expected ENODEV, error is %s" % 598 (err["error"])) 599 600def check_dev_info(other_ns, ns, prog_file=None, map_file=None, removed=False): 601 progs = bpftool_prog_list(expected=1, ns=ns) 602 prog = progs[0] 603 604 fail("dev" not in prog.keys(), "Device parameters not reported") 605 dev = prog["dev"] 606 fail("ifindex" not in dev.keys(), "Device parameters not reported") 607 fail("ns_dev" not in dev.keys(), "Device parameters not reported") 608 fail("ns_inode" not in dev.keys(), "Device parameters not reported") 609 610 if not other_ns: 611 fail("ifname" not in dev.keys(), "Ifname not reported") 612 fail(dev["ifname"] != sim["ifname"], 613 "Ifname incorrect %s vs %s" % (dev["ifname"], sim["ifname"])) 614 else: 615 fail("ifname" in dev.keys(), "Ifname is reported for other ns") 616 617 maps = bpftool_map_list_wait(expected=2, ns=ns) 618 for m in maps: 619 fail("dev" not in m.keys(), "Device parameters not reported") 620 fail(dev != m["dev"], "Map's device different than program's") 621 622def check_extack(output, reference, args): 623 if skip_extack: 624 return 625 lines = output.split("\n") 626 comp = len(lines) >= 2 and lines[1] == 'Error: ' + reference 627 fail(not comp, "Missing or incorrect netlink extack message") 628 629def check_extack_nsim(output, reference, args): 630 check_extack(output, "netdevsim: " + reference, args) 631 632def check_no_extack(res, needle): 633 haystack = (res[1] + res[2]).strip() 634 fail(haystack.count(needle) or haystack.count("Warning:"), 635 "Unexpected command output, leaky extack? ('%s', '%s')" % (needle, haystack)) 636 637def check_verifier_log(output, reference): 638 lines = output.split("\n") 639 for l in reversed(lines): 640 if l == reference: 641 return 642 fail(True, "Missing or incorrect message from netdevsim in verifier log") 643 644def check_multi_basic(two_xdps): 645 fail(two_xdps["mode"] != 4, "Bad mode reported with multiple programs") 646 fail("prog" in two_xdps, "Base program reported in multi program mode") 647 fail(len(two_xdps["attached"]) != 2, 648 "Wrong attached program count with two programs") 649 fail(two_xdps["attached"][0]["prog"]["id"] == 650 two_xdps["attached"][1]["prog"]["id"], 651 "Offloaded and other programs have the same id") 652 653def test_spurios_extack(sim, obj, skip_hw, needle): 654 res = sim.cls_bpf_add_filter(obj, prio=1, handle=1, skip_hw=skip_hw, 655 include_stderr=True) 656 check_no_extack(res, needle) 657 res = sim.cls_bpf_add_filter(obj, op="replace", prio=1, handle=1, 658 skip_hw=skip_hw, include_stderr=True) 659 check_no_extack(res, needle) 660 res = sim.cls_filter_op(op="delete", prio=1, handle=1, cls="bpf", 661 include_stderr=True) 662 check_no_extack(res, needle) 663 664def test_tc_shared_block(): 665 """ 666 Device-bound cls_bpf program attached through a shared block. 667 """ 668 # A shared block has no qdisc, so the target device is derived from the 669 # block's bound netdevs and the program must match every member, since 670 # the offload path broadcasts it to every callback on the block. A 671 # netdev joining a populated block later is replayed the stored filter 672 # before it is inserted into block->ports, so the driver callback must 673 # enforce the device match as well. 674 simdev = BpfNetdevSimDev() 675 sim, = simdev.nsims 676 sim.set_ethtool_tc_offloads(True) 677 678 start_test("Test TC shared-block load dev-bound program...") 679 bpftool_prog_load("sample_ret0.bpf.o", "/sys/fs/bpf/tc_shared", 680 prog_type="classifier", dev=sim['ifname']) 681 682 start_test("Test TC shared-block dev-bound attach...") 683 # The pinned program is dev-bound to this device; attaching it through 684 # a shared block must succeed even though block->q is NULL. 685 sim.tc_add_block(22) 686 # err is kept (include_stderr) for post-mortem value on a failure. 687 ret, _, err = sim.tc_add_block_filter(22, bpf_pinned("/sys/fs/bpf/tc_shared"), 688 da=True, skip_sw=True, 689 fail=False, include_stderr=True) 690 fail(ret != 0, "dev-bound program rejected on a matching shared block") 691 fail(sim.dfs_read("bpf_offloaded_id") == "0", 692 "dev-bound program not offloaded on a matching shared block") 693 sim.tc_del_block(22) 694 695 start_test("Test TC shared-block broadcast to a foreign member...") 696 # Add an independently backed device to the SAME block before the 697 # attach. The program is bound to sim's device only, and the offload 698 # path fans it out to every member, so the attach must be rejected. 699 simdev2 = BpfNetdevSimDev() 700 sim2, = simdev2.nsims 701 sim2.set_ethtool_tc_offloads(True) 702 sim.tc_add_block(40) 703 sim2.tc_add_block(40) 704 ret, _, err = sim.tc_add_block_filter(40, bpf_pinned("/sys/fs/bpf/tc_shared"), 705 da=True, skip_sw=True, 706 fail=False, include_stderr=True) 707 fail(ret == 0, "dev-bound program broadcast to a foreign shared-block member") 708 check_extack(err, "Program is not bound to every device in the block.", args) 709 sim2.tc_del_block(40) 710 sim.tc_del_block(40) 711 simdev2.remove() 712 713 start_test("Test TC shared-block late join replay...") 714 # Build a block owned by sim's device with the matching program 715 # installed, then let an independently backed device join it. The 716 # join replays the stored filter to the joining device's callback 717 # before the device is recorded in block->ports; the driver must 718 # reject the replay so the foreign device is never loaded. 719 # sim2 is created here (not inherited) so the TC-offload state it 720 # depends on is explicit. 721 simdev2 = BpfNetdevSimDev() 722 sim2, = simdev2.nsims 723 sim2.set_ethtool_tc_offloads(True) 724 sim.tc_add_block(30) 725 ret, _, err = sim.tc_add_block_filter(30, bpf_pinned("/sys/fs/bpf/tc_shared"), 726 da=True, skip_sw=True, 727 fail=False, include_stderr=True) 728 fail(ret != 0, "dev-bound program rejected on a matching shared block") 729 ret, _, err = sim2.tc_add_block(30, fail=False, include_stderr=True) 730 fail(ret == 0, "foreign device joined a populated dev-bound shared block") 731 check_extack_nsim(err, "program bound to a different device.", args) 732 fail(sim2.dfs_read("bpf_offloaded_id") != "0", 733 "replayed program loaded onto a foreign device") 734 # The add above was expected to fail, so the qdisc may not exist; the 735 # delete is harmless and only cleans up when it does. 736 sim2.tc_del_block(30) 737 sim.tc_del_block(30) 738 simdev2.remove() 739 740 start_test("Test TC shared-block same-offload-device member...") 741 # A second port of the same netdevsim device shares the offload device, 742 # so a program bound to one port may legitimately be offloaded to both. 743 simdev3 = BpfNetdevSimDev(2) 744 sim3, sim3b = simdev3.nsims 745 sim3.set_ethtool_tc_offloads(True) 746 sim3b.set_ethtool_tc_offloads(True) 747 bpftool_prog_load("sample_ret0.bpf.o", "/sys/fs/bpf/tc_shared_multi", 748 prog_type="classifier", dev=sim3['ifname']) 749 sim3.tc_add_block(50) 750 sim3b.tc_add_block(50) 751 # err kept for post-mortem value on a failure. 752 ret, _, err = sim3.tc_add_block_filter(50, bpf_pinned("/sys/fs/bpf/tc_shared_multi"), 753 da=True, skip_sw=True, 754 fail=False, include_stderr=True) 755 fail(ret != 0, "same-offload-device member rejected on a shared block") 756 fail(sim3.dfs_read("bpf_offloaded_id") == "0", 757 "program not offloaded to the bound port") 758 fail(sim3b.dfs_read("bpf_offloaded_id") == "0", 759 "program not offloaded to the same-offload-device port") 760 sim3b.tc_del_block(50) 761 sim3.tc_del_block(50) 762 763 simdev3.remove() 764 simdev.remove() 765 rm("/sys/fs/bpf/tc_shared_multi") 766 rm("/sys/fs/bpf/tc_shared") 767 bpftool_prog_list_wait(expected=0) 768 769def test_multi_prog(simdev, sim, obj, modename, modeid): 770 start_test("Test multi-attachment XDP - %s + offload..." % 771 (modename or "default", )) 772 sim.set_xdp(obj, "offload") 773 xdp = sim.ip_link_show(xdp=True)["xdp"] 774 offloaded = sim.dfs_read("bpf_offloaded_id") 775 fail("prog" not in xdp, "Base program not reported in single program mode") 776 fail(len(xdp["attached"]) != 1, 777 "Wrong attached program count with one program") 778 779 sim.set_xdp(obj, modename) 780 two_xdps = sim.ip_link_show(xdp=True)["xdp"] 781 782 fail(xdp["attached"][0] not in two_xdps["attached"], 783 "Offload program not reported after other activated") 784 check_multi_basic(two_xdps) 785 786 offloaded2 = sim.dfs_read("bpf_offloaded_id") 787 fail(offloaded != offloaded2, 788 "Offload ID changed after loading other program") 789 790 start_test("Test multi-attachment XDP - replace...") 791 ret, _, err = sim.set_xdp(obj, "offload", fail=False, include_stderr=True) 792 fail(ret == 0, "Replaced one of programs without -force") 793 check_extack(err, "XDP program already attached.", args) 794 795 start_test("Test multi-attachment XDP - remove without mode...") 796 ret, _, err = sim.unset_xdp("", force=True, 797 fail=False, include_stderr=True) 798 fail(ret == 0, "Removed program without a mode flag") 799 check_extack(err, "More than one program loaded, unset mode is ambiguous.", args) 800 801 sim.unset_xdp("offload") 802 xdp = sim.ip_link_show(xdp=True)["xdp"] 803 offloaded = sim.dfs_read("bpf_offloaded_id") 804 805 fail(xdp["mode"] != modeid, "Bad mode reported after multiple programs") 806 fail("prog" not in xdp, 807 "Base program not reported after multi program mode") 808 fail(xdp["attached"][0] not in two_xdps["attached"], 809 "Offload program not reported after other activated") 810 fail(len(xdp["attached"]) != 1, 811 "Wrong attached program count with remaining programs") 812 fail(offloaded != "0", "Offload ID reported with only other program left") 813 814 start_test("Test multi-attachment XDP - reattach...") 815 sim.set_xdp(obj, "offload") 816 two_xdps = sim.ip_link_show(xdp=True)["xdp"] 817 818 fail(xdp["attached"][0] not in two_xdps["attached"], 819 "Other program not reported after offload activated") 820 check_multi_basic(two_xdps) 821 822 start_test("Test multi-attachment XDP - device remove...") 823 simdev.remove() 824 825 simdev = BpfNetdevSimDev() 826 sim, = simdev.nsims 827 sim.set_ethtool_tc_offloads(True) 828 return [simdev, sim] 829 830# Parse command line 831parser = argparse.ArgumentParser() 832parser.add_argument("--log", help="output verbose log to given file") 833args = parser.parse_args() 834if args.log: 835 logfile = open(args.log, 'w+') 836 logfile.write("# -*-Org-*-") 837 838log("Prepare...", "", level=1) 839log_level_inc() 840 841# Check permissions 842skip(os.getuid() != 0, "test must be run as root") 843 844# Check tools 845ret, progs = bpftool("prog", fail=False) 846skip(ret != 0, "bpftool not installed") 847base_progs = progs 848_, base_maps = bpftool("map") 849base_map_names = [ 850 'pid_iter.rodata', # created on each bpftool invocation 851 'libbpf_det_bind', # created on each bpftool invocation 852 'libbpf_global', 853] 854 855# Check netdevsim 856if not os.path.isdir("/sys/bus/netdevsim/"): 857 ret, out = cmd("modprobe netdevsim", fail=False) 858 skip(ret != 0, "netdevsim module could not be loaded") 859 860# Check debugfs 861_, out = cmd("mount") 862if out.find("/sys/kernel/debug type debugfs") == -1: 863 cmd("mount -t debugfs none /sys/kernel/debug") 864 865# Check samples are compiled 866samples = ["sample_ret0.bpf.o", "sample_map_ret0.bpf.o"] 867for s in samples: 868 ret, out = cmd("ls %s/%s" % (bpf_test_dir, s), fail=False) 869 skip(ret != 0, "sample %s/%s not found, please compile it" % 870 (bpf_test_dir, s)) 871 872# Check if iproute2 is built with libmnl (needed by extack support) 873_, _, err = cmd("tc qdisc delete dev lo handle 0", 874 fail=False, include_stderr=True) 875if err.find("Error: Failed to find qdisc with specified handle.") == -1: 876 print("Warning: no extack message in iproute2 output, libmnl missing?") 877 log("Warning: no extack message in iproute2 output, libmnl missing?", "") 878 skip_extack = True 879 880# Check if net namespaces seem to work 881ns = mknetns() 882skip(ns is None, "Could not create a net namespace") 883cmd("ip netns delete %s" % (ns)) 884netns = [] 885 886try: 887 obj = bpf_obj("sample_ret0.bpf.o") 888 bytecode = bpf_bytecode("1,6 0 0 4294967295,") 889 890 start_test("Test destruction of generic XDP...") 891 simdev = BpfNetdevSimDev() 892 sim, = simdev.nsims 893 sim.set_xdp(obj, "generic") 894 simdev.remove() 895 bpftool_prog_list_wait(expected=0) 896 897 simdev = BpfNetdevSimDev() 898 sim, = simdev.nsims 899 sim.tc_add_ingress() 900 901 start_test("Test TC non-offloaded...") 902 ret, _ = sim.cls_bpf_add_filter(obj, skip_hw=True, fail=False) 903 fail(ret != 0, "Software TC filter did not load") 904 905 start_test("Test TC non-offloaded isn't getting bound...") 906 ret, _ = sim.cls_bpf_add_filter(obj, fail=False) 907 fail(ret != 0, "Software TC filter did not load") 908 simdev.dfs_get_bound_progs(expected=0) 909 910 sim.tc_flush_filters() 911 912 start_test("Test TC offloads are off by default...") 913 ret, _, err = sim.cls_bpf_add_filter(obj, skip_sw=True, 914 fail=False, include_stderr=True) 915 fail(ret == 0, "TC filter loaded without enabling TC offloads") 916 check_extack(err, "TC offload is disabled on net device.", args) 917 sim.wait_for_flush() 918 919 sim.set_ethtool_tc_offloads(True) 920 sim.dfs["bpf_tc_non_bound_accept"] = "Y" 921 922 start_test("Test TC offload by default...") 923 ret, _ = sim.cls_bpf_add_filter(obj, fail=False) 924 fail(ret != 0, "Software TC filter did not load") 925 simdev.dfs_get_bound_progs(expected=0) 926 ingress = sim.tc_show_ingress(expected=1) 927 fltr = ingress[0] 928 fail(not fltr["in_hw"], "Filter not offloaded by default") 929 930 sim.tc_flush_filters() 931 932 start_test("Test TC cBPF bytcode tries offload by default...") 933 ret, _ = sim.cls_bpf_add_filter(bytecode, fail=False) 934 fail(ret != 0, "Software TC filter did not load") 935 simdev.dfs_get_bound_progs(expected=0) 936 ingress = sim.tc_show_ingress(expected=1) 937 fltr = ingress[0] 938 fail(not fltr["in_hw"], "Bytecode not offloaded by default") 939 940 sim.tc_flush_filters() 941 sim.dfs["bpf_tc_non_bound_accept"] = "N" 942 943 start_test("Test TC cBPF unbound bytecode doesn't offload...") 944 ret, _, err = sim.cls_bpf_add_filter(bytecode, skip_sw=True, 945 fail=False, include_stderr=True) 946 fail(ret == 0, "TC bytecode loaded for offload") 947 check_extack_nsim(err, "netdevsim configured to reject unbound programs.", 948 args) 949 sim.wait_for_flush() 950 951 start_test("Test non-0 chain offload...") 952 ret, _, err = sim.cls_bpf_add_filter(obj, chain=1, prio=1, handle=1, 953 skip_sw=True, 954 fail=False, include_stderr=True) 955 fail(ret == 0, "Offloaded a filter to chain other than 0") 956 check_extack(err, "Driver supports only offload of chain 0.", args) 957 sim.tc_flush_filters() 958 959 start_test("Test TC replace...") 960 sim.cls_bpf_add_filter(obj, prio=1, handle=1) 961 sim.cls_bpf_add_filter(obj, op="replace", prio=1, handle=1) 962 sim.cls_filter_op(op="delete", prio=1, handle=1, cls="bpf") 963 964 sim.cls_bpf_add_filter(obj, prio=1, handle=1, skip_sw=True) 965 sim.cls_bpf_add_filter(obj, op="replace", prio=1, handle=1, skip_sw=True) 966 sim.cls_filter_op(op="delete", prio=1, handle=1, cls="bpf") 967 968 sim.cls_bpf_add_filter(obj, prio=1, handle=1, skip_hw=True) 969 sim.cls_bpf_add_filter(obj, op="replace", prio=1, handle=1, skip_hw=True) 970 sim.cls_filter_op(op="delete", prio=1, handle=1, cls="bpf") 971 972 start_test("Test TC replace bad flags...") 973 for i in range(3): 974 for j in range(3): 975 ret, _ = sim.cls_bpf_add_filter(obj, op="replace", prio=1, handle=1, 976 skip_sw=(j == 1), skip_hw=(j == 2), 977 fail=False) 978 fail(bool(ret) != bool(j), 979 "Software TC incorrect load in replace test, iteration %d" % 980 (j)) 981 sim.cls_filter_op(op="delete", prio=1, handle=1, cls="bpf") 982 983 start_test("Test spurious extack from the driver...") 984 test_spurios_extack(sim, obj, False, "netdevsim") 985 test_spurios_extack(sim, obj, True, "netdevsim") 986 987 sim.set_ethtool_tc_offloads(False) 988 989 test_spurios_extack(sim, obj, False, "TC offload is disabled") 990 test_spurios_extack(sim, obj, True, "TC offload is disabled") 991 992 sim.set_ethtool_tc_offloads(True) 993 994 sim.tc_flush_filters() 995 996 start_test("Test TC offloads failure...") 997 sim.dfs["dev/bpf_bind_verifier_accept"] = 0 998 ret, _, err = sim.cls_bpf_add_filter(obj, verbose=True, skip_sw=True, 999 fail=False, include_stderr=True) 1000 fail(ret == 0, "TC filter did not reject with TC offloads enabled") 1001 check_verifier_log(err, "[netdevsim] Hello from netdevsim!") 1002 sim.dfs["dev/bpf_bind_verifier_accept"] = 1 1003 1004 start_test("Test TC offloads work...") 1005 ret, _, err = sim.cls_bpf_add_filter(obj, verbose=True, skip_sw=True, 1006 fail=False, include_stderr=True) 1007 fail(ret != 0, "TC filter did not load with TC offloads enabled") 1008 1009 start_test("Test TC offload basics...") 1010 dfs = simdev.dfs_get_bound_progs(expected=1) 1011 progs = bpftool_prog_list(expected=1) 1012 ingress = sim.tc_show_ingress(expected=1) 1013 1014 dprog = dfs[0] 1015 prog = progs[0] 1016 fltr = ingress[0] 1017 fail(fltr["skip_hw"], "TC does reports 'skip_hw' on offloaded filter") 1018 fail(not fltr["in_hw"], "TC does not report 'in_hw' for offloaded filter") 1019 fail(not fltr["skip_sw"], "TC does not report 'skip_sw' back") 1020 1021 start_test("Test TC offload is device-bound...") 1022 fail(str(prog["id"]) != fltr["id"], "Program IDs don't match") 1023 fail(prog["tag"] != fltr["tag"], "Program tags don't match") 1024 fail(fltr["id"] != dprog["id"], "Program IDs don't match") 1025 fail(dprog["state"] != "xlated", "Offloaded program state not translated") 1026 fail(dprog["loaded"] != "Y", "Offloaded program is not loaded") 1027 1028 start_test("Test disabling TC offloads is rejected while filters installed...") 1029 ret, _ = sim.set_ethtool_tc_offloads(False, fail=False) 1030 fail(ret == 0, "Driver should refuse to disable TC offloads with filters installed...") 1031 sim.set_ethtool_tc_offloads(True) 1032 1033 start_test("Test qdisc removal frees things...") 1034 sim.tc_flush_filters() 1035 sim.tc_show_ingress(expected=0) 1036 1037 start_test("Test disabling TC offloads is OK without filters...") 1038 ret, _ = sim.set_ethtool_tc_offloads(False, fail=False) 1039 fail(ret != 0, 1040 "Driver refused to disable TC offloads without filters installed...") 1041 1042 sim.set_ethtool_tc_offloads(True) 1043 1044 start_test("Test destroying device gets rid of TC filters...") 1045 sim.cls_bpf_add_filter(obj, skip_sw=True) 1046 simdev.remove() 1047 bpftool_prog_list_wait(expected=0) 1048 1049 simdev = BpfNetdevSimDev() 1050 sim, = simdev.nsims 1051 sim.set_ethtool_tc_offloads(True) 1052 1053 start_test("Test destroying device gets rid of XDP...") 1054 sim.set_xdp(obj, "offload") 1055 simdev.remove() 1056 bpftool_prog_list_wait(expected=0) 1057 1058 simdev = BpfNetdevSimDev() 1059 sim, = simdev.nsims 1060 sim.set_ethtool_tc_offloads(True) 1061 1062 start_test("Test XDP prog reporting...") 1063 sim.set_xdp(obj, "drv") 1064 ipl = sim.ip_link_show(xdp=True) 1065 progs = bpftool_prog_list(expected=1) 1066 fail(ipl["xdp"]["prog"]["id"] != progs[0]["id"], 1067 "Loaded program has wrong ID") 1068 1069 start_test("Test XDP prog replace without force...") 1070 ret, _ = sim.set_xdp(obj, "drv", fail=False) 1071 fail(ret == 0, "Replaced XDP program without -force") 1072 sim.wait_for_flush(total=1) 1073 1074 start_test("Test XDP prog replace with force...") 1075 ret, _ = sim.set_xdp(obj, "drv", force=True, fail=False) 1076 fail(ret != 0, "Could not replace XDP program with -force") 1077 bpftool_prog_list_wait(expected=1) 1078 ipl = sim.ip_link_show(xdp=True) 1079 progs = bpftool_prog_list(expected=1) 1080 fail(ipl["xdp"]["prog"]["id"] != progs[0]["id"], 1081 "Loaded program has wrong ID") 1082 fail("dev" in progs[0].keys(), 1083 "Device parameters reported for non-offloaded program") 1084 1085 start_test("Test XDP prog replace with bad flags...") 1086 ret, _, err = sim.set_xdp(obj, "generic", force=True, 1087 fail=False, include_stderr=True) 1088 fail(ret == 0, "Replaced XDP program with a program in different mode") 1089 check_extack(err, 1090 "Native and generic XDP can't be active at the same time.", 1091 args) 1092 1093 start_test("Test MTU restrictions...") 1094 ret, _ = sim.set_mtu(9000, fail=False) 1095 fail(ret == 0, 1096 "Driver should refuse to increase MTU to 9000 with XDP loaded...") 1097 sim.unset_xdp("drv") 1098 bpftool_prog_list_wait(expected=0) 1099 sim.set_mtu(9000) 1100 ret, _, err = sim.set_xdp(obj, "drv", fail=False, include_stderr=True) 1101 fail(ret == 0, "Driver should refuse to load program with MTU of 9000...") 1102 check_extack_nsim(err, "MTU too large w/ XDP enabled.", args) 1103 sim.set_mtu(1500) 1104 1105 sim.wait_for_flush() 1106 start_test("Test non-offload XDP attaching to HW...") 1107 bpftool_prog_load("sample_ret0.bpf.o", "/sys/fs/bpf/nooffload") 1108 nooffload = bpf_pinned("/sys/fs/bpf/nooffload") 1109 ret, _, err = sim.set_xdp(nooffload, "offload", 1110 fail=False, include_stderr=True) 1111 fail(ret == 0, "attached non-offloaded XDP program to HW") 1112 check_extack_nsim(err, "xdpoffload of non-bound program.", args) 1113 rm("/sys/fs/bpf/nooffload") 1114 1115 start_test("Test offload XDP attaching to drv...") 1116 bpftool_prog_load("sample_ret0.bpf.o", "/sys/fs/bpf/offload", 1117 dev=sim['ifname']) 1118 offload = bpf_pinned("/sys/fs/bpf/offload") 1119 ret, _, err = sim.set_xdp(offload, "drv", fail=False, include_stderr=True) 1120 fail(ret == 0, "attached offloaded XDP program to drv") 1121 check_extack(err, "Using offloaded program without HW_MODE flag is not supported.", args) 1122 rm("/sys/fs/bpf/offload") 1123 sim.wait_for_flush() 1124 1125 bpftool_prog_load("sample_ret0.bpf.o", "/sys/fs/bpf/devbound", 1126 dev_bind=sim['ifname']) 1127 devbound = bpf_pinned("/sys/fs/bpf/devbound") 1128 start_test("Test dev-bound program in generic mode...") 1129 ret, _, err = sim.set_xdp(devbound, "generic", fail=False, include_stderr=True) 1130 fail(ret == 0, "devbound program in generic mode allowed") 1131 check_extack(err, "Can't attach device-bound programs in generic mode.", args) 1132 rm("/sys/fs/bpf/devbound") 1133 sim.wait_for_flush() 1134 1135 start_test("Test XDP load failure...") 1136 sim.dfs["dev/bpf_bind_verifier_accept"] = 0 1137 ret, _, err = bpftool_prog_load("sample_ret0.bpf.o", "/sys/fs/bpf/offload", 1138 dev=sim['ifname'], fail=False, include_stderr=True) 1139 fail(ret == 0, "verifier should fail on load") 1140 check_verifier_log(err, "[netdevsim] Hello from netdevsim!") 1141 sim.dfs["dev/bpf_bind_verifier_accept"] = 1 1142 sim.wait_for_flush() 1143 1144 start_test("Test XDP offload...") 1145 _, _, err = sim.set_xdp(obj, "offload", verbose=True, include_stderr=True) 1146 ipl = sim.ip_link_show(xdp=True) 1147 link_xdp = ipl["xdp"]["prog"] 1148 progs = bpftool_prog_list(expected=1) 1149 prog = progs[0] 1150 fail(link_xdp["id"] != prog["id"], "Loaded program has wrong ID") 1151 1152 start_test("Test XDP offload is device bound...") 1153 dfs = simdev.dfs_get_bound_progs(expected=1) 1154 dprog = dfs[0] 1155 1156 fail(prog["id"] != link_xdp["id"], "Program IDs don't match") 1157 fail(prog["tag"] != link_xdp["tag"], "Program tags don't match") 1158 fail(str(link_xdp["id"]) != dprog["id"], "Program IDs don't match") 1159 fail(dprog["state"] != "xlated", "Offloaded program state not translated") 1160 fail(dprog["loaded"] != "Y", "Offloaded program is not loaded") 1161 1162 start_test("Test removing XDP program many times...") 1163 sim.unset_xdp("offload") 1164 sim.unset_xdp("offload") 1165 sim.unset_xdp("drv") 1166 sim.unset_xdp("drv") 1167 sim.unset_xdp("") 1168 sim.unset_xdp("") 1169 bpftool_prog_list_wait(expected=0) 1170 1171 start_test("Test attempt to use a program for a wrong device...") 1172 simdev2 = BpfNetdevSimDev() 1173 sim2, = simdev2.nsims 1174 sim2.set_xdp(obj, "offload") 1175 pin_file, pinned = pin_prog("/sys/fs/bpf/tmp") 1176 1177 ret, _, err = sim.set_xdp(pinned, "offload", 1178 fail=False, include_stderr=True) 1179 fail(ret == 0, "Pinned program loaded for a different device accepted") 1180 check_extack(err, "Program bound to different device.", args) 1181 simdev2.remove() 1182 ret, _, err = sim.set_xdp(pinned, "offload", 1183 fail=False, include_stderr=True) 1184 fail(ret == 0, "Pinned program loaded for a removed device accepted") 1185 check_extack(err, "Program bound to different device.", args) 1186 rm(pin_file) 1187 bpftool_prog_list_wait(expected=0) 1188 1189 simdev, sim = test_multi_prog(simdev, sim, obj, "", 1) 1190 simdev, sim = test_multi_prog(simdev, sim, obj, "drv", 1) 1191 simdev, sim = test_multi_prog(simdev, sim, obj, "generic", 2) 1192 1193 start_test("Test mixing of TC and XDP...") 1194 sim.tc_add_ingress() 1195 sim.set_xdp(obj, "offload") 1196 ret, _, err = sim.cls_bpf_add_filter(obj, skip_sw=True, 1197 fail=False, include_stderr=True) 1198 fail(ret == 0, "Loading TC when XDP active should fail") 1199 check_extack_nsim(err, "driver and netdev offload states mismatch.", args) 1200 sim.unset_xdp("offload") 1201 sim.wait_for_flush() 1202 1203 sim.cls_bpf_add_filter(obj, skip_sw=True) 1204 ret, _, err = sim.set_xdp(obj, "offload", fail=False, include_stderr=True) 1205 fail(ret == 0, "Loading XDP when TC active should fail") 1206 check_extack_nsim(err, "TC program is already loaded.", args) 1207 1208 start_test("Test binding TC from pinned...") 1209 pin_file, pinned = pin_prog("/sys/fs/bpf/tmp") 1210 sim.tc_flush_filters(bound=1, total=1) 1211 sim.cls_bpf_add_filter(pinned, da=True, skip_sw=True) 1212 sim.tc_flush_filters(bound=1, total=1) 1213 1214 start_test("Test binding XDP from pinned...") 1215 sim.set_xdp(obj, "offload") 1216 pin_file, pinned = pin_prog("/sys/fs/bpf/tmp2", idx=1) 1217 1218 sim.set_xdp(pinned, "offload", force=True) 1219 sim.unset_xdp("offload") 1220 sim.set_xdp(pinned, "offload", force=True) 1221 sim.unset_xdp("offload") 1222 1223 start_test("Test offload of wrong type fails...") 1224 ret, _ = sim.cls_bpf_add_filter(pinned, da=True, skip_sw=True, fail=False) 1225 fail(ret == 0, "Managed to attach XDP program to TC") 1226 1227 start_test("Test asking for TC offload of two filters...") 1228 sim.cls_bpf_add_filter(obj, da=True, skip_sw=True) 1229 ret, _, err = sim.cls_bpf_add_filter(obj, da=True, skip_sw=True, 1230 fail=False, include_stderr=True) 1231 fail(ret == 0, "Managed to offload two TC filters at the same time") 1232 check_extack_nsim(err, "driver and netdev offload states mismatch.", args) 1233 1234 sim.tc_flush_filters(bound=2, total=2) 1235 1236 start_test("Test if netdev removal waits for translation...") 1237 delay_msec = 500 1238 sim.dfs["dev/bpf_bind_verifier_delay"] = delay_msec 1239 start = time.time() 1240 cmd_line = "tc filter add dev %s ingress bpf %s da skip_sw" % \ 1241 (sim['ifname'], obj) 1242 tc_proc = cmd(cmd_line, background=True, fail=False) 1243 # Wait for the verifier to start 1244 while simdev.dfs_num_bound_progs() <= 2: 1245 pass 1246 simdev.remove() 1247 end = time.time() 1248 ret, _ = cmd_result(tc_proc, fail=False) 1249 time_diff = end - start 1250 log("Time", "start:\t%s\nend:\t%s\ndiff:\t%s" % (start, end, time_diff)) 1251 1252 fail(ret == 0, "Managed to load TC filter on a unregistering device") 1253 delay_sec = delay_msec * 0.001 1254 fail(time_diff < delay_sec, "Removal process took %s, expected %s" % 1255 (time_diff, delay_sec)) 1256 1257 # Remove all pinned files and reinstantiate the netdev 1258 clean_up() 1259 bpftool_prog_list_wait(expected=0) 1260 1261 simdev = BpfNetdevSimDev() 1262 sim, = simdev.nsims 1263 map_obj = bpf_obj("sample_map_ret0.bpf.o") 1264 start_test("Test loading program with maps...") 1265 sim.set_xdp(map_obj, "offload", JSON=False) # map fixup msg breaks JSON 1266 1267 start_test("Test bpftool bound info reporting (own ns)...") 1268 check_dev_info(False, "") 1269 1270 start_test("Test bpftool bound info reporting (other ns)...") 1271 ns = mknetns() 1272 sim.set_ns(ns) 1273 check_dev_info(True, "") 1274 1275 start_test("Test bpftool bound info reporting (remote ns)...") 1276 check_dev_info(False, ns) 1277 1278 start_test("Test bpftool bound info reporting (back to own ns)...") 1279 sim.set_ns("") 1280 check_dev_info(False, "") 1281 1282 prog_file, _ = pin_prog("/sys/fs/bpf/tmp_prog") 1283 map_file, _ = pin_map("/sys/fs/bpf/tmp_map", idx=1, expected=2) 1284 simdev.remove() 1285 1286 start_test("Test bpftool bound info reporting (removed dev)...") 1287 check_dev_info_removed(prog_file=prog_file, map_file=map_file) 1288 1289 # Remove all pinned files and reinstantiate the netdev 1290 clean_up() 1291 bpftool_prog_list_wait(expected=0) 1292 1293 simdev = BpfNetdevSimDev() 1294 sim, = simdev.nsims 1295 1296 start_test("Test map update (no flags)...") 1297 sim.set_xdp(map_obj, "offload", JSON=False) # map fixup msg breaks JSON 1298 maps = bpftool_map_list_wait(expected=2) 1299 array = maps[0] if maps[0]["type"] == "array" else maps[1] 1300 htab = maps[0] if maps[0]["type"] == "hash" else maps[1] 1301 for m in maps: 1302 for i in range(2): 1303 bpftool("map update id %d key %s value %s" % 1304 (m["id"], int2str("I", i), int2str("Q", i * 3))) 1305 1306 for m in maps: 1307 ret, _ = bpftool("map update id %d key %s value %s" % 1308 (m["id"], int2str("I", 3), int2str("Q", 3 * 3)), 1309 fail=False) 1310 fail(ret == 0, "added too many entries") 1311 1312 start_test("Test map update (exists)...") 1313 for m in maps: 1314 for i in range(2): 1315 bpftool("map update id %d key %s value %s exist" % 1316 (m["id"], int2str("I", i), int2str("Q", i * 3))) 1317 1318 for m in maps: 1319 ret, err = bpftool("map update id %d key %s value %s exist" % 1320 (m["id"], int2str("I", 3), int2str("Q", 3 * 3)), 1321 fail=False) 1322 fail(ret == 0, "updated non-existing key") 1323 fail(err["error"].find("No such file or directory") == -1, 1324 "expected ENOENT, error is '%s'" % (err["error"])) 1325 1326 start_test("Test map update (noexist)...") 1327 for m in maps: 1328 for i in range(2): 1329 ret, err = bpftool("map update id %d key %s value %s noexist" % 1330 (m["id"], int2str("I", i), int2str("Q", i * 3)), 1331 fail=False) 1332 fail(ret == 0, "updated existing key") 1333 fail(err["error"].find("File exists") == -1, 1334 "expected EEXIST, error is '%s'" % (err["error"])) 1335 1336 start_test("Test map dump...") 1337 for m in maps: 1338 _, entries = bpftool("map dump id %d" % (m["id"])) 1339 for i in range(2): 1340 key = str2int(entries[i]["key"]) 1341 fail(key != i, "expected key %d, got %d" % (key, i)) 1342 val = str2int(entries[i]["value"]) 1343 fail(val != i * 3, "expected value %d, got %d" % (val, i * 3)) 1344 1345 start_test("Test map getnext...") 1346 for m in maps: 1347 _, entry = bpftool("map getnext id %d" % (m["id"])) 1348 key = str2int(entry["next_key"]) 1349 fail(key != 0, "next key %d, expected %d" % (key, 0)) 1350 _, entry = bpftool("map getnext id %d key %s" % 1351 (m["id"], int2str("I", 0))) 1352 key = str2int(entry["next_key"]) 1353 fail(key != 1, "next key %d, expected %d" % (key, 1)) 1354 ret, err = bpftool("map getnext id %d key %s" % 1355 (m["id"], int2str("I", 1)), fail=False) 1356 fail(ret == 0, "got next key past the end of map") 1357 fail(err["error"].find("No such file or directory") == -1, 1358 "expected ENOENT, error is '%s'" % (err["error"])) 1359 1360 start_test("Test map delete (htab)...") 1361 for i in range(2): 1362 bpftool("map delete id %d key %s" % (htab["id"], int2str("I", i))) 1363 1364 start_test("Test map delete (array)...") 1365 for i in range(2): 1366 ret, err = bpftool("map delete id %d key %s" % 1367 (htab["id"], int2str("I", i)), fail=False) 1368 fail(ret == 0, "removed entry from an array") 1369 fail(err["error"].find("No such file or directory") == -1, 1370 "expected ENOENT, error is '%s'" % (err["error"])) 1371 1372 start_test("Test map remove...") 1373 sim.unset_xdp("offload") 1374 bpftool_map_list_wait(expected=0) 1375 simdev.remove() 1376 1377 simdev = BpfNetdevSimDev() 1378 sim, = simdev.nsims 1379 sim.set_xdp(map_obj, "offload", JSON=False) # map fixup msg breaks JSON 1380 simdev.remove() 1381 bpftool_map_list_wait(expected=0) 1382 1383 start_test("Test map creation fail path...") 1384 simdev = BpfNetdevSimDev() 1385 sim, = simdev.nsims 1386 sim.dfs["bpf_map_accept"] = "N" 1387 ret, _ = sim.set_xdp(map_obj, "offload", JSON=False, fail=False) 1388 fail(ret == 0, 1389 "netdevsim didn't refuse to create a map with offload disabled") 1390 1391 simdev.remove() 1392 1393 start_test("Test multi-dev ASIC program reuse...") 1394 simdevA = BpfNetdevSimDev() 1395 simA, = simdevA.nsims 1396 simdevB = BpfNetdevSimDev(3) 1397 simB1, simB2, simB3 = simdevB.nsims 1398 sims = (simA, simB1, simB2, simB3) 1399 simB = (simB1, simB2, simB3) 1400 1401 bpftool_prog_load("sample_map_ret0.bpf.o", "/sys/fs/bpf/nsimA", 1402 dev=simA['ifname']) 1403 progA = bpf_pinned("/sys/fs/bpf/nsimA") 1404 bpftool_prog_load("sample_map_ret0.bpf.o", "/sys/fs/bpf/nsimB", 1405 dev=simB1['ifname']) 1406 progB = bpf_pinned("/sys/fs/bpf/nsimB") 1407 1408 simA.set_xdp(progA, "offload", JSON=False) 1409 for d in simdevB.nsims: 1410 d.set_xdp(progB, "offload", JSON=False) 1411 1412 start_test("Test multi-dev ASIC cross-dev replace...") 1413 ret, _ = simA.set_xdp(progB, "offload", force=True, JSON=False, fail=False) 1414 fail(ret == 0, "cross-ASIC program allowed") 1415 for d in simdevB.nsims: 1416 ret, _ = d.set_xdp(progA, "offload", force=True, JSON=False, fail=False) 1417 fail(ret == 0, "cross-ASIC program allowed") 1418 1419 start_test("Test multi-dev ASIC cross-dev install...") 1420 for d in sims: 1421 d.unset_xdp("offload") 1422 1423 ret, _, err = simA.set_xdp(progB, "offload", force=True, JSON=False, 1424 fail=False, include_stderr=True) 1425 fail(ret == 0, "cross-ASIC program allowed") 1426 check_extack(err, "Program bound to different device.", args) 1427 for d in simdevB.nsims: 1428 ret, _, err = d.set_xdp(progA, "offload", force=True, JSON=False, 1429 fail=False, include_stderr=True) 1430 fail(ret == 0, "cross-ASIC program allowed") 1431 check_extack(err, "Program bound to different device.", args) 1432 1433 start_test("Test multi-dev ASIC cross-dev map reuse...") 1434 1435 mapA = bpftool("prog show %s" % (progA))[1]["map_ids"][0] 1436 mapB = bpftool("prog show %s" % (progB))[1]["map_ids"][0] 1437 1438 ret, _ = bpftool_prog_load("sample_map_ret0.bpf.o", "/sys/fs/bpf/nsimB_", 1439 dev=simB3['ifname'], 1440 maps=["idx 0 id %d" % (mapB)], 1441 fail=False) 1442 fail(ret != 0, "couldn't reuse a map on the same ASIC") 1443 rm("/sys/fs/bpf/nsimB_") 1444 1445 ret, _, err = bpftool_prog_load("sample_map_ret0.bpf.o", "/sys/fs/bpf/nsimA_", 1446 dev=simA['ifname'], 1447 maps=["idx 0 id %d" % (mapB)], 1448 fail=False, include_stderr=True) 1449 fail(ret == 0, "could reuse a map on a different ASIC") 1450 fail(err.count("offload device mismatch between prog and map") == 0, 1451 "error message missing for cross-ASIC map") 1452 1453 ret, _, err = bpftool_prog_load("sample_map_ret0.bpf.o", "/sys/fs/bpf/nsimB_", 1454 dev=simB1['ifname'], 1455 maps=["idx 0 id %d" % (mapA)], 1456 fail=False, include_stderr=True) 1457 fail(ret == 0, "could reuse a map on a different ASIC") 1458 fail(err.count("offload device mismatch between prog and map") == 0, 1459 "error message missing for cross-ASIC map") 1460 1461 start_test("Test multi-dev ASIC cross-dev destruction...") 1462 bpftool_prog_list_wait(expected=2) 1463 1464 simdevA.remove() 1465 bpftool_prog_list_wait(expected=1) 1466 1467 ifnameB = bpftool("prog show %s" % (progB))[1]["dev"]["ifname"] 1468 fail(ifnameB != simB1['ifname'], "program not bound to original device") 1469 simB1.remove() 1470 bpftool_prog_list_wait(expected=1) 1471 1472 start_test("Test multi-dev ASIC cross-dev destruction - move...") 1473 ifnameB = bpftool("prog show %s" % (progB))[1]["dev"]["ifname"] 1474 fail(ifnameB not in (simB2['ifname'], simB3['ifname']), 1475 "program not bound to remaining devices") 1476 1477 simB2.remove() 1478 ifnameB = bpftool("prog show %s" % (progB))[1]["dev"]["ifname"] 1479 fail(ifnameB != simB3['ifname'], "program not bound to remaining device") 1480 1481 simB3.remove() 1482 simdevB.remove() 1483 bpftool_prog_list_wait(expected=0) 1484 1485 start_test("Test multi-dev ASIC cross-dev destruction - orphaned...") 1486 ret, out = bpftool("prog show %s" % (progB), fail=False) 1487 fail(ret != 0, "couldn't get information about orphaned program") 1488 1489 test_tc_shared_block() 1490 1491 print("%s: OK" % (os.path.basename(__file__))) 1492 1493finally: 1494 log("Clean up...", "", level=1) 1495 log_level_inc() 1496 clean_up() 1497