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" % 188 (len(progs), expected)) 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 for i in range(n_retry): 211 maps = bpftool_map_list(ns=ns) 212 if len(maps) == expected: 213 return maps 214 time.sleep(0.05) 215 raise Exception("Time out waiting for map counts to stabilize want %d, have %d" % (expected, nmaps)) 216 217def bpftool_prog_load(sample, file_name, maps=[], prog_type="xdp", dev=None, 218 fail=True, include_stderr=False): 219 args = "prog load %s %s" % (os.path.join(bpf_test_dir, sample), file_name) 220 if prog_type is not None: 221 args += " type " + prog_type 222 if dev is not None: 223 args += " dev " + dev 224 if len(maps): 225 args += " map " + " map ".join(maps) 226 227 res = bpftool(args, fail=fail, include_stderr=include_stderr) 228 if res[0] == 0: 229 files.append(file_name) 230 return res 231 232def ip(args, force=False, JSON=True, ns="", fail=True, include_stderr=False): 233 if force: 234 args = "-force " + args 235 return tool("ip", args, {"json":"-j"}, JSON=JSON, ns=ns, 236 fail=fail, include_stderr=include_stderr) 237 238def tc(args, JSON=True, ns="", fail=True, include_stderr=False): 239 return tool("tc", args, {"json":"-p"}, JSON=JSON, ns=ns, 240 fail=fail, include_stderr=include_stderr) 241 242def ethtool(dev, opt, args, fail=True): 243 return cmd("ethtool %s %s %s" % (opt, dev["ifname"], args), fail=fail) 244 245def bpf_obj(name, sec="xdp", path=bpf_test_dir,): 246 return "obj %s sec %s" % (os.path.join(path, name), sec) 247 248def bpf_pinned(name): 249 return "pinned %s" % (name) 250 251def bpf_bytecode(bytecode): 252 return "bytecode \"%s\"" % (bytecode) 253 254def mknetns(n_retry=10): 255 for i in range(n_retry): 256 name = ''.join([random.choice(string.ascii_letters) for i in range(8)]) 257 ret, _ = ip("netns add %s" % (name), fail=False) 258 if ret == 0: 259 netns.append(name) 260 return name 261 return None 262 263def int2str(fmt, val): 264 ret = [] 265 for b in struct.pack(fmt, val): 266 ret.append(int(b)) 267 return " ".join(map(lambda x: str(x), ret)) 268 269def str2int(strtab): 270 inttab = [] 271 for i in strtab: 272 inttab.append(int(i, 16)) 273 ba = bytearray(inttab) 274 if len(strtab) == 4: 275 fmt = "I" 276 elif len(strtab) == 8: 277 fmt = "Q" 278 else: 279 raise Exception("String array of len %d can't be unpacked to an int" % 280 (len(strtab))) 281 return struct.unpack(fmt, ba)[0] 282 283class DebugfsDir: 284 """ 285 Class for accessing DebugFS directories as a dictionary. 286 """ 287 288 def __init__(self, path): 289 self.path = path 290 self._dict = self._debugfs_dir_read(path) 291 292 def __len__(self): 293 return len(self._dict.keys()) 294 295 def __getitem__(self, key): 296 if type(key) is int: 297 key = list(self._dict.keys())[key] 298 return self._dict[key] 299 300 def __setitem__(self, key, value): 301 log("DebugFS set %s = %s" % (key, value), "") 302 log_level_inc() 303 304 cmd("echo '%s' > %s/%s" % (value, self.path, key)) 305 log_level_dec() 306 307 _, out = cmd('cat %s/%s' % (self.path, key)) 308 self._dict[key] = out.strip() 309 310 def _debugfs_dir_read(self, path): 311 dfs = {} 312 313 log("DebugFS state for %s" % (path), "") 314 log_level_inc(add=2) 315 316 _, out = cmd('ls ' + path) 317 for f in out.split(): 318 if f == "ports": 319 continue 320 321 p = os.path.join(path, f) 322 if not os.stat(p).st_mode & stat.S_IRUSR: 323 continue 324 325 if os.path.isfile(p): 326 # We need to init trap_flow_action_cookie before read it 327 if f == "trap_flow_action_cookie": 328 cmd('echo deadbeef > %s/%s' % (path, f)) 329 _, out = cmd('cat %s/%s' % (path, f)) 330 dfs[f] = out.strip() 331 elif os.path.isdir(p): 332 dfs[f] = DebugfsDir(p) 333 else: 334 raise Exception("%s is neither file nor directory" % (p)) 335 336 log_level_dec() 337 log("DebugFS state", dfs) 338 log_level_dec() 339 340 return dfs 341 342class BpfNetdevSimDev(NetdevSimDev): 343 """ 344 Class for netdevsim bus device and its attributes. 345 """ 346 def __init__(self, port_count=1, ns=None): 347 super().__init__(port_count, ns=ns) 348 devs.append(self) 349 350 def _make_port(self, port_index, ifname): 351 return BpfNetdevSim(self, port_index, ifname, self.ns) 352 353 def dfs_num_bound_progs(self): 354 path = os.path.join(self.dfs_dir, "bpf_bound_progs") 355 _, progs = cmd('ls %s' % (path)) 356 return len(progs.split()) 357 358 def dfs_get_bound_progs(self, expected): 359 progs = DebugfsDir(os.path.join(self.dfs_dir, "bpf_bound_progs")) 360 if expected is not None: 361 if len(progs) != expected: 362 fail(True, "%d BPF programs bound, expected %d" % 363 (len(progs), expected)) 364 return progs 365 366 def remove(self): 367 super().remove() 368 devs.remove(self) 369 370 371class BpfNetdevSim(NetdevSim): 372 """ 373 Class for netdevsim netdevice and its attributes. 374 """ 375 376 def __init__(self, nsimdev, port_index, ifname, ns=None): 377 super().__init__(nsimdev, port_index, ifname, ns=ns) 378 379 self.dfs_dir = "%s/ports/%u/" % (nsimdev.dfs_dir, port_index) 380 self.dfs_refresh() 381 382 def __getitem__(self, key): 383 return self.dev[key] 384 385 def remove(self): 386 self.nsimdev.remove_nsim(self) 387 388 def dfs_refresh(self): 389 self.dfs = DebugfsDir(self.dfs_dir) 390 return self.dfs 391 392 def dfs_read(self, f): 393 path = os.path.join(self.dfs_dir, f) 394 _, data = cmd('cat %s' % (path)) 395 return data.strip() 396 397 def wait_for_flush(self, bound=0, total=0, n_retry=20): 398 for i in range(n_retry): 399 nbound = self.nsimdev.dfs_num_bound_progs() 400 nprogs = len(bpftool_prog_list()) 401 if nbound == bound and nprogs == total: 402 return 403 time.sleep(0.05) 404 raise Exception("Time out waiting for program counts to stabilize want %d/%d, have %d bound, %d loaded" % (bound, total, nbound, nprogs)) 405 406 def set_ns(self, ns): 407 name = ns if ns else "1" 408 ip("link set dev %s netns %s" % (self.dev["ifname"], name), ns=self.ns) 409 self.ns = ns 410 411 def set_mtu(self, mtu, fail=True): 412 return ip("link set dev %s mtu %d" % (self.dev["ifname"], mtu), 413 fail=fail) 414 415 def set_xdp(self, bpf, mode, force=False, JSON=True, verbose=False, 416 fail=True, include_stderr=False): 417 if verbose: 418 bpf += " verbose" 419 return ip("link set dev %s xdp%s %s" % (self.dev["ifname"], mode, bpf), 420 force=force, JSON=JSON, 421 fail=fail, include_stderr=include_stderr) 422 423 def unset_xdp(self, mode, force=False, JSON=True, 424 fail=True, include_stderr=False): 425 return ip("link set dev %s xdp%s off" % (self.dev["ifname"], mode), 426 force=force, JSON=JSON, 427 fail=fail, include_stderr=include_stderr) 428 429 def ip_link_show(self, xdp): 430 _, link = ip("link show dev %s" % (self['ifname'])) 431 if len(link) > 1: 432 raise Exception("Multiple objects on ip link show") 433 if len(link) < 1: 434 return {} 435 fail(xdp != "xdp" in link, 436 "XDP program not reporting in iplink (reported %s, expected %s)" % 437 ("xdp" in link, xdp)) 438 return link[0] 439 440 def tc_add_ingress(self): 441 tc("qdisc add dev %s ingress" % (self['ifname'])) 442 443 def tc_del_ingress(self): 444 tc("qdisc del dev %s ingress" % (self['ifname'])) 445 446 def tc_flush_filters(self, bound=0, total=0): 447 self.tc_del_ingress() 448 self.tc_add_ingress() 449 self.wait_for_flush(bound=bound, total=total) 450 451 def tc_show_ingress(self, expected=None): 452 # No JSON support, oh well... 453 flags = ["skip_sw", "skip_hw", "in_hw"] 454 named = ["protocol", "pref", "chain", "handle", "id", "tag"] 455 456 args = "-s filter show dev %s ingress" % (self['ifname']) 457 _, out = tc(args, JSON=False) 458 459 filters = [] 460 lines = out.split('\n') 461 for line in lines: 462 words = line.split() 463 if "handle" not in words: 464 continue 465 fltr = {} 466 for flag in flags: 467 fltr[flag] = flag in words 468 for name in named: 469 try: 470 idx = words.index(name) 471 fltr[name] = words[idx + 1] 472 except ValueError: 473 pass 474 filters.append(fltr) 475 476 if expected is not None: 477 fail(len(filters) != expected, 478 "%d ingress filters loaded, expected %d" % 479 (len(filters), expected)) 480 return filters 481 482 def cls_filter_op(self, op, qdisc="ingress", prio=None, handle=None, 483 chain=None, cls="", params="", 484 fail=True, include_stderr=False): 485 spec = "" 486 if prio is not None: 487 spec += " prio %d" % (prio) 488 if handle: 489 spec += " handle %s" % (handle) 490 if chain is not None: 491 spec += " chain %d" % (chain) 492 493 return tc("filter {op} dev {dev} {qdisc} {spec} {cls} {params}"\ 494 .format(op=op, dev=self['ifname'], qdisc=qdisc, spec=spec, 495 cls=cls, params=params), 496 fail=fail, include_stderr=include_stderr) 497 498 def cls_bpf_add_filter(self, bpf, op="add", prio=None, handle=None, 499 chain=None, da=False, verbose=False, 500 skip_sw=False, skip_hw=False, 501 fail=True, include_stderr=False): 502 cls = "bpf " + bpf 503 504 params = "" 505 if da: 506 params += " da" 507 if verbose: 508 params += " verbose" 509 if skip_sw: 510 params += " skip_sw" 511 if skip_hw: 512 params += " skip_hw" 513 514 return self.cls_filter_op(op=op, prio=prio, handle=handle, cls=cls, 515 chain=chain, params=params, 516 fail=fail, include_stderr=include_stderr) 517 518 def set_ethtool_tc_offloads(self, enable, fail=True): 519 args = "hw-tc-offload %s" % ("on" if enable else "off") 520 return ethtool(self, "-K", args, fail=fail) 521 522################################################################################ 523def clean_up(): 524 global files, netns, devs 525 526 for dev in devs: 527 dev.remove() 528 for f in files: 529 cmd("rm -f %s" % (f)) 530 for ns in netns: 531 cmd("ip netns delete %s" % (ns)) 532 files = [] 533 netns = [] 534 535def pin_prog(file_name, idx=0): 536 progs = bpftool_prog_list(expected=(idx + 1)) 537 prog = progs[idx] 538 bpftool("prog pin id %d %s" % (prog["id"], file_name)) 539 files.append(file_name) 540 541 return file_name, bpf_pinned(file_name) 542 543def pin_map(file_name, idx=0, expected=1): 544 maps = bpftool_map_list_wait(expected=expected) 545 m = maps[idx] 546 bpftool("map pin id %d %s" % (m["id"], file_name)) 547 files.append(file_name) 548 549 return file_name, bpf_pinned(file_name) 550 551def check_dev_info_removed(prog_file=None, map_file=None): 552 bpftool_prog_list(expected=0) 553 bpftool_prog_list(expected=1, exclude_orphaned=False) 554 ret, err = bpftool("prog show pin %s" % (prog_file), fail=False) 555 fail(ret != 0, "failed to show prog with removed device") 556 557 bpftool_map_list_wait(expected=0) 558 ret, err = bpftool("map show pin %s" % (map_file), fail=False) 559 fail(ret == 0, "Showing map with removed device did not fail") 560 fail(err["error"].find("No such device") == -1, 561 "Showing map with removed device expected ENODEV, error is %s" % 562 (err["error"])) 563 564def check_dev_info(other_ns, ns, prog_file=None, map_file=None, removed=False): 565 progs = bpftool_prog_list(expected=1, ns=ns) 566 prog = progs[0] 567 568 fail("dev" not in prog.keys(), "Device parameters not reported") 569 dev = prog["dev"] 570 fail("ifindex" not in dev.keys(), "Device parameters not reported") 571 fail("ns_dev" not in dev.keys(), "Device parameters not reported") 572 fail("ns_inode" not in dev.keys(), "Device parameters not reported") 573 574 if not other_ns: 575 fail("ifname" not in dev.keys(), "Ifname not reported") 576 fail(dev["ifname"] != sim["ifname"], 577 "Ifname incorrect %s vs %s" % (dev["ifname"], sim["ifname"])) 578 else: 579 fail("ifname" in dev.keys(), "Ifname is reported for other ns") 580 581 maps = bpftool_map_list_wait(expected=2, ns=ns) 582 for m in maps: 583 fail("dev" not in m.keys(), "Device parameters not reported") 584 fail(dev != m["dev"], "Map's device different than program's") 585 586def check_extack(output, reference, args): 587 if skip_extack: 588 return 589 lines = output.split("\n") 590 comp = len(lines) >= 2 and lines[1] == 'Error: ' + reference 591 fail(not comp, "Missing or incorrect netlink extack message") 592 593def check_extack_nsim(output, reference, args): 594 check_extack(output, "netdevsim: " + reference, args) 595 596def check_no_extack(res, needle): 597 haystack = (res[1] + res[2]).strip() 598 fail(haystack.count(needle) or haystack.count("Warning:"), 599 "Unexpected command output, leaky extack? ('%s', '%s')" % (needle, haystack)) 600 601def check_verifier_log(output, reference): 602 lines = output.split("\n") 603 for l in reversed(lines): 604 if l == reference: 605 return 606 fail(True, "Missing or incorrect message from netdevsim in verifier log") 607 608def check_multi_basic(two_xdps): 609 fail(two_xdps["mode"] != 4, "Bad mode reported with multiple programs") 610 fail("prog" in two_xdps, "Base program reported in multi program mode") 611 fail(len(two_xdps["attached"]) != 2, 612 "Wrong attached program count with two programs") 613 fail(two_xdps["attached"][0]["prog"]["id"] == 614 two_xdps["attached"][1]["prog"]["id"], 615 "Offloaded and other programs have the same id") 616 617def test_spurios_extack(sim, obj, skip_hw, needle): 618 res = sim.cls_bpf_add_filter(obj, prio=1, handle=1, skip_hw=skip_hw, 619 include_stderr=True) 620 check_no_extack(res, needle) 621 res = sim.cls_bpf_add_filter(obj, op="replace", prio=1, handle=1, 622 skip_hw=skip_hw, include_stderr=True) 623 check_no_extack(res, needle) 624 res = sim.cls_filter_op(op="delete", prio=1, handle=1, cls="bpf", 625 include_stderr=True) 626 check_no_extack(res, needle) 627 628def test_multi_prog(simdev, sim, obj, modename, modeid): 629 start_test("Test multi-attachment XDP - %s + offload..." % 630 (modename or "default", )) 631 sim.set_xdp(obj, "offload") 632 xdp = sim.ip_link_show(xdp=True)["xdp"] 633 offloaded = sim.dfs_read("bpf_offloaded_id") 634 fail("prog" not in xdp, "Base program not reported in single program mode") 635 fail(len(xdp["attached"]) != 1, 636 "Wrong attached program count with one program") 637 638 sim.set_xdp(obj, modename) 639 two_xdps = sim.ip_link_show(xdp=True)["xdp"] 640 641 fail(xdp["attached"][0] not in two_xdps["attached"], 642 "Offload program not reported after other activated") 643 check_multi_basic(two_xdps) 644 645 offloaded2 = sim.dfs_read("bpf_offloaded_id") 646 fail(offloaded != offloaded2, 647 "Offload ID changed after loading other program") 648 649 start_test("Test multi-attachment XDP - replace...") 650 ret, _, err = sim.set_xdp(obj, "offload", fail=False, include_stderr=True) 651 fail(ret == 0, "Replaced one of programs without -force") 652 check_extack(err, "XDP program already attached.", args) 653 654 start_test("Test multi-attachment XDP - remove without mode...") 655 ret, _, err = sim.unset_xdp("", force=True, 656 fail=False, include_stderr=True) 657 fail(ret == 0, "Removed program without a mode flag") 658 check_extack(err, "More than one program loaded, unset mode is ambiguous.", args) 659 660 sim.unset_xdp("offload") 661 xdp = sim.ip_link_show(xdp=True)["xdp"] 662 offloaded = sim.dfs_read("bpf_offloaded_id") 663 664 fail(xdp["mode"] != modeid, "Bad mode reported after multiple programs") 665 fail("prog" not in xdp, 666 "Base program not reported after multi program mode") 667 fail(xdp["attached"][0] not in two_xdps["attached"], 668 "Offload program not reported after other activated") 669 fail(len(xdp["attached"]) != 1, 670 "Wrong attached program count with remaining programs") 671 fail(offloaded != "0", "Offload ID reported with only other program left") 672 673 start_test("Test multi-attachment XDP - reattach...") 674 sim.set_xdp(obj, "offload") 675 two_xdps = sim.ip_link_show(xdp=True)["xdp"] 676 677 fail(xdp["attached"][0] not in two_xdps["attached"], 678 "Other program not reported after offload activated") 679 check_multi_basic(two_xdps) 680 681 start_test("Test multi-attachment XDP - device remove...") 682 simdev.remove() 683 684 simdev = BpfNetdevSimDev() 685 sim, = simdev.nsims 686 sim.set_ethtool_tc_offloads(True) 687 return [simdev, sim] 688 689# Parse command line 690parser = argparse.ArgumentParser() 691parser.add_argument("--log", help="output verbose log to given file") 692args = parser.parse_args() 693if args.log: 694 logfile = open(args.log, 'w+') 695 logfile.write("# -*-Org-*-") 696 697log("Prepare...", "", level=1) 698log_level_inc() 699 700# Check permissions 701skip(os.getuid() != 0, "test must be run as root") 702 703# Check tools 704ret, progs = bpftool("prog", fail=False) 705skip(ret != 0, "bpftool not installed") 706base_progs = progs 707_, base_maps = bpftool("map") 708base_map_names = [ 709 'pid_iter.rodata', # created on each bpftool invocation 710 'libbpf_det_bind', # created on each bpftool invocation 711] 712 713# Check netdevsim 714if not os.path.isdir("/sys/bus/netdevsim/"): 715 ret, out = cmd("modprobe netdevsim", fail=False) 716 skip(ret != 0, "netdevsim module could not be loaded") 717 718# Check debugfs 719_, out = cmd("mount") 720if out.find("/sys/kernel/debug type debugfs") == -1: 721 cmd("mount -t debugfs none /sys/kernel/debug") 722 723# Check samples are compiled 724samples = ["sample_ret0.bpf.o", "sample_map_ret0.bpf.o"] 725for s in samples: 726 ret, out = cmd("ls %s/%s" % (bpf_test_dir, s), fail=False) 727 skip(ret != 0, "sample %s/%s not found, please compile it" % 728 (bpf_test_dir, s)) 729 730# Check if iproute2 is built with libmnl (needed by extack support) 731_, _, err = cmd("tc qdisc delete dev lo handle 0", 732 fail=False, include_stderr=True) 733if err.find("Error: Failed to find qdisc with specified handle.") == -1: 734 print("Warning: no extack message in iproute2 output, libmnl missing?") 735 log("Warning: no extack message in iproute2 output, libmnl missing?", "") 736 skip_extack = True 737 738# Check if net namespaces seem to work 739ns = mknetns() 740skip(ns is None, "Could not create a net namespace") 741cmd("ip netns delete %s" % (ns)) 742netns = [] 743 744try: 745 obj = bpf_obj("sample_ret0.bpf.o") 746 bytecode = bpf_bytecode("1,6 0 0 4294967295,") 747 748 start_test("Test destruction of generic XDP...") 749 simdev = BpfNetdevSimDev() 750 sim, = simdev.nsims 751 sim.set_xdp(obj, "generic") 752 simdev.remove() 753 bpftool_prog_list_wait(expected=0) 754 755 simdev = BpfNetdevSimDev() 756 sim, = simdev.nsims 757 sim.tc_add_ingress() 758 759 start_test("Test TC non-offloaded...") 760 ret, _ = sim.cls_bpf_add_filter(obj, skip_hw=True, fail=False) 761 fail(ret != 0, "Software TC filter did not load") 762 763 start_test("Test TC non-offloaded isn't getting bound...") 764 ret, _ = sim.cls_bpf_add_filter(obj, fail=False) 765 fail(ret != 0, "Software TC filter did not load") 766 simdev.dfs_get_bound_progs(expected=0) 767 768 sim.tc_flush_filters() 769 770 start_test("Test TC offloads are off by default...") 771 ret, _, err = sim.cls_bpf_add_filter(obj, skip_sw=True, 772 fail=False, include_stderr=True) 773 fail(ret == 0, "TC filter loaded without enabling TC offloads") 774 check_extack(err, "TC offload is disabled on net device.", args) 775 sim.wait_for_flush() 776 777 sim.set_ethtool_tc_offloads(True) 778 sim.dfs["bpf_tc_non_bound_accept"] = "Y" 779 780 start_test("Test TC offload by default...") 781 ret, _ = sim.cls_bpf_add_filter(obj, fail=False) 782 fail(ret != 0, "Software TC filter did not load") 783 simdev.dfs_get_bound_progs(expected=0) 784 ingress = sim.tc_show_ingress(expected=1) 785 fltr = ingress[0] 786 fail(not fltr["in_hw"], "Filter not offloaded by default") 787 788 sim.tc_flush_filters() 789 790 start_test("Test TC cBPF bytcode tries offload by default...") 791 ret, _ = sim.cls_bpf_add_filter(bytecode, fail=False) 792 fail(ret != 0, "Software TC filter did not load") 793 simdev.dfs_get_bound_progs(expected=0) 794 ingress = sim.tc_show_ingress(expected=1) 795 fltr = ingress[0] 796 fail(not fltr["in_hw"], "Bytecode not offloaded by default") 797 798 sim.tc_flush_filters() 799 sim.dfs["bpf_tc_non_bound_accept"] = "N" 800 801 start_test("Test TC cBPF unbound bytecode doesn't offload...") 802 ret, _, err = sim.cls_bpf_add_filter(bytecode, skip_sw=True, 803 fail=False, include_stderr=True) 804 fail(ret == 0, "TC bytecode loaded for offload") 805 check_extack_nsim(err, "netdevsim configured to reject unbound programs.", 806 args) 807 sim.wait_for_flush() 808 809 start_test("Test non-0 chain offload...") 810 ret, _, err = sim.cls_bpf_add_filter(obj, chain=1, prio=1, handle=1, 811 skip_sw=True, 812 fail=False, include_stderr=True) 813 fail(ret == 0, "Offloaded a filter to chain other than 0") 814 check_extack(err, "Driver supports only offload of chain 0.", args) 815 sim.tc_flush_filters() 816 817 start_test("Test TC replace...") 818 sim.cls_bpf_add_filter(obj, prio=1, handle=1) 819 sim.cls_bpf_add_filter(obj, op="replace", prio=1, handle=1) 820 sim.cls_filter_op(op="delete", prio=1, handle=1, cls="bpf") 821 822 sim.cls_bpf_add_filter(obj, prio=1, handle=1, skip_sw=True) 823 sim.cls_bpf_add_filter(obj, op="replace", prio=1, handle=1, skip_sw=True) 824 sim.cls_filter_op(op="delete", prio=1, handle=1, cls="bpf") 825 826 sim.cls_bpf_add_filter(obj, prio=1, handle=1, skip_hw=True) 827 sim.cls_bpf_add_filter(obj, op="replace", prio=1, handle=1, skip_hw=True) 828 sim.cls_filter_op(op="delete", prio=1, handle=1, cls="bpf") 829 830 start_test("Test TC replace bad flags...") 831 for i in range(3): 832 for j in range(3): 833 ret, _ = sim.cls_bpf_add_filter(obj, op="replace", prio=1, handle=1, 834 skip_sw=(j == 1), skip_hw=(j == 2), 835 fail=False) 836 fail(bool(ret) != bool(j), 837 "Software TC incorrect load in replace test, iteration %d" % 838 (j)) 839 sim.cls_filter_op(op="delete", prio=1, handle=1, cls="bpf") 840 841 start_test("Test spurious extack from the driver...") 842 test_spurios_extack(sim, obj, False, "netdevsim") 843 test_spurios_extack(sim, obj, True, "netdevsim") 844 845 sim.set_ethtool_tc_offloads(False) 846 847 test_spurios_extack(sim, obj, False, "TC offload is disabled") 848 test_spurios_extack(sim, obj, True, "TC offload is disabled") 849 850 sim.set_ethtool_tc_offloads(True) 851 852 sim.tc_flush_filters() 853 854 start_test("Test TC offloads failure...") 855 sim.dfs["dev/bpf_bind_verifier_accept"] = 0 856 ret, _, err = sim.cls_bpf_add_filter(obj, verbose=True, skip_sw=True, 857 fail=False, include_stderr=True) 858 fail(ret == 0, "TC filter did not reject with TC offloads enabled") 859 check_verifier_log(err, "[netdevsim] Hello from netdevsim!") 860 sim.dfs["dev/bpf_bind_verifier_accept"] = 1 861 862 start_test("Test TC offloads work...") 863 ret, _, err = sim.cls_bpf_add_filter(obj, verbose=True, skip_sw=True, 864 fail=False, include_stderr=True) 865 fail(ret != 0, "TC filter did not load with TC offloads enabled") 866 867 start_test("Test TC offload basics...") 868 dfs = simdev.dfs_get_bound_progs(expected=1) 869 progs = bpftool_prog_list(expected=1) 870 ingress = sim.tc_show_ingress(expected=1) 871 872 dprog = dfs[0] 873 prog = progs[0] 874 fltr = ingress[0] 875 fail(fltr["skip_hw"], "TC does reports 'skip_hw' on offloaded filter") 876 fail(not fltr["in_hw"], "TC does not report 'in_hw' for offloaded filter") 877 fail(not fltr["skip_sw"], "TC does not report 'skip_sw' back") 878 879 start_test("Test TC offload is device-bound...") 880 fail(str(prog["id"]) != fltr["id"], "Program IDs don't match") 881 fail(prog["tag"] != fltr["tag"], "Program tags don't match") 882 fail(fltr["id"] != dprog["id"], "Program IDs don't match") 883 fail(dprog["state"] != "xlated", "Offloaded program state not translated") 884 fail(dprog["loaded"] != "Y", "Offloaded program is not loaded") 885 886 start_test("Test disabling TC offloads is rejected while filters installed...") 887 ret, _ = sim.set_ethtool_tc_offloads(False, fail=False) 888 fail(ret == 0, "Driver should refuse to disable TC offloads with filters installed...") 889 sim.set_ethtool_tc_offloads(True) 890 891 start_test("Test qdisc removal frees things...") 892 sim.tc_flush_filters() 893 sim.tc_show_ingress(expected=0) 894 895 start_test("Test disabling TC offloads is OK without filters...") 896 ret, _ = sim.set_ethtool_tc_offloads(False, fail=False) 897 fail(ret != 0, 898 "Driver refused to disable TC offloads without filters installed...") 899 900 sim.set_ethtool_tc_offloads(True) 901 902 start_test("Test destroying device gets rid of TC filters...") 903 sim.cls_bpf_add_filter(obj, skip_sw=True) 904 simdev.remove() 905 bpftool_prog_list_wait(expected=0) 906 907 simdev = BpfNetdevSimDev() 908 sim, = simdev.nsims 909 sim.set_ethtool_tc_offloads(True) 910 911 start_test("Test destroying device gets rid of XDP...") 912 sim.set_xdp(obj, "offload") 913 simdev.remove() 914 bpftool_prog_list_wait(expected=0) 915 916 simdev = BpfNetdevSimDev() 917 sim, = simdev.nsims 918 sim.set_ethtool_tc_offloads(True) 919 920 start_test("Test XDP prog reporting...") 921 sim.set_xdp(obj, "drv") 922 ipl = sim.ip_link_show(xdp=True) 923 progs = bpftool_prog_list(expected=1) 924 fail(ipl["xdp"]["prog"]["id"] != progs[0]["id"], 925 "Loaded program has wrong ID") 926 927 start_test("Test XDP prog replace without force...") 928 ret, _ = sim.set_xdp(obj, "drv", fail=False) 929 fail(ret == 0, "Replaced XDP program without -force") 930 sim.wait_for_flush(total=1) 931 932 start_test("Test XDP prog replace with force...") 933 ret, _ = sim.set_xdp(obj, "drv", force=True, fail=False) 934 fail(ret != 0, "Could not replace XDP program with -force") 935 bpftool_prog_list_wait(expected=1) 936 ipl = sim.ip_link_show(xdp=True) 937 progs = bpftool_prog_list(expected=1) 938 fail(ipl["xdp"]["prog"]["id"] != progs[0]["id"], 939 "Loaded program has wrong ID") 940 fail("dev" in progs[0].keys(), 941 "Device parameters reported for non-offloaded program") 942 943 start_test("Test XDP prog replace with bad flags...") 944 ret, _, err = sim.set_xdp(obj, "generic", force=True, 945 fail=False, include_stderr=True) 946 fail(ret == 0, "Replaced XDP program with a program in different mode") 947 check_extack(err, 948 "Native and generic XDP can't be active at the same time.", 949 args) 950 951 start_test("Test MTU restrictions...") 952 ret, _ = sim.set_mtu(9000, fail=False) 953 fail(ret == 0, 954 "Driver should refuse to increase MTU to 9000 with XDP loaded...") 955 sim.unset_xdp("drv") 956 bpftool_prog_list_wait(expected=0) 957 sim.set_mtu(9000) 958 ret, _, err = sim.set_xdp(obj, "drv", fail=False, include_stderr=True) 959 fail(ret == 0, "Driver should refuse to load program with MTU of 9000...") 960 check_extack_nsim(err, "MTU too large w/ XDP enabled.", args) 961 sim.set_mtu(1500) 962 963 sim.wait_for_flush() 964 start_test("Test non-offload XDP attaching to HW...") 965 bpftool_prog_load("sample_ret0.bpf.o", "/sys/fs/bpf/nooffload") 966 nooffload = bpf_pinned("/sys/fs/bpf/nooffload") 967 ret, _, err = sim.set_xdp(nooffload, "offload", 968 fail=False, include_stderr=True) 969 fail(ret == 0, "attached non-offloaded XDP program to HW") 970 check_extack_nsim(err, "xdpoffload of non-bound program.", args) 971 rm("/sys/fs/bpf/nooffload") 972 973 start_test("Test offload XDP attaching to drv...") 974 bpftool_prog_load("sample_ret0.bpf.o", "/sys/fs/bpf/offload", 975 dev=sim['ifname']) 976 offload = bpf_pinned("/sys/fs/bpf/offload") 977 ret, _, err = sim.set_xdp(offload, "drv", fail=False, include_stderr=True) 978 fail(ret == 0, "attached offloaded XDP program to drv") 979 check_extack(err, "Using offloaded program without HW_MODE flag is not supported.", args) 980 rm("/sys/fs/bpf/offload") 981 sim.wait_for_flush() 982 983 start_test("Test XDP load failure...") 984 sim.dfs["dev/bpf_bind_verifier_accept"] = 0 985 ret, _, err = bpftool_prog_load("sample_ret0.bpf.o", "/sys/fs/bpf/offload", 986 dev=sim['ifname'], fail=False, include_stderr=True) 987 fail(ret == 0, "verifier should fail on load") 988 check_verifier_log(err, "[netdevsim] Hello from netdevsim!") 989 sim.dfs["dev/bpf_bind_verifier_accept"] = 1 990 sim.wait_for_flush() 991 992 start_test("Test XDP offload...") 993 _, _, err = sim.set_xdp(obj, "offload", verbose=True, include_stderr=True) 994 ipl = sim.ip_link_show(xdp=True) 995 link_xdp = ipl["xdp"]["prog"] 996 progs = bpftool_prog_list(expected=1) 997 prog = progs[0] 998 fail(link_xdp["id"] != prog["id"], "Loaded program has wrong ID") 999 1000 start_test("Test XDP offload is device bound...") 1001 dfs = simdev.dfs_get_bound_progs(expected=1) 1002 dprog = dfs[0] 1003 1004 fail(prog["id"] != link_xdp["id"], "Program IDs don't match") 1005 fail(prog["tag"] != link_xdp["tag"], "Program tags don't match") 1006 fail(str(link_xdp["id"]) != dprog["id"], "Program IDs don't match") 1007 fail(dprog["state"] != "xlated", "Offloaded program state not translated") 1008 fail(dprog["loaded"] != "Y", "Offloaded program is not loaded") 1009 1010 start_test("Test removing XDP program many times...") 1011 sim.unset_xdp("offload") 1012 sim.unset_xdp("offload") 1013 sim.unset_xdp("drv") 1014 sim.unset_xdp("drv") 1015 sim.unset_xdp("") 1016 sim.unset_xdp("") 1017 bpftool_prog_list_wait(expected=0) 1018 1019 start_test("Test attempt to use a program for a wrong device...") 1020 simdev2 = BpfNetdevSimDev() 1021 sim2, = simdev2.nsims 1022 sim2.set_xdp(obj, "offload") 1023 pin_file, pinned = pin_prog("/sys/fs/bpf/tmp") 1024 1025 ret, _, err = sim.set_xdp(pinned, "offload", 1026 fail=False, include_stderr=True) 1027 fail(ret == 0, "Pinned program loaded for a different device accepted") 1028 check_extack(err, "Program bound to different device.", args) 1029 simdev2.remove() 1030 ret, _, err = sim.set_xdp(pinned, "offload", 1031 fail=False, include_stderr=True) 1032 fail(ret == 0, "Pinned program loaded for a removed device accepted") 1033 check_extack(err, "Program bound to different device.", args) 1034 rm(pin_file) 1035 bpftool_prog_list_wait(expected=0) 1036 1037 simdev, sim = test_multi_prog(simdev, sim, obj, "", 1) 1038 simdev, sim = test_multi_prog(simdev, sim, obj, "drv", 1) 1039 simdev, sim = test_multi_prog(simdev, sim, obj, "generic", 2) 1040 1041 start_test("Test mixing of TC and XDP...") 1042 sim.tc_add_ingress() 1043 sim.set_xdp(obj, "offload") 1044 ret, _, err = sim.cls_bpf_add_filter(obj, skip_sw=True, 1045 fail=False, include_stderr=True) 1046 fail(ret == 0, "Loading TC when XDP active should fail") 1047 check_extack_nsim(err, "driver and netdev offload states mismatch.", args) 1048 sim.unset_xdp("offload") 1049 sim.wait_for_flush() 1050 1051 sim.cls_bpf_add_filter(obj, skip_sw=True) 1052 ret, _, err = sim.set_xdp(obj, "offload", fail=False, include_stderr=True) 1053 fail(ret == 0, "Loading XDP when TC active should fail") 1054 check_extack_nsim(err, "TC program is already loaded.", args) 1055 1056 start_test("Test binding TC from pinned...") 1057 pin_file, pinned = pin_prog("/sys/fs/bpf/tmp") 1058 sim.tc_flush_filters(bound=1, total=1) 1059 sim.cls_bpf_add_filter(pinned, da=True, skip_sw=True) 1060 sim.tc_flush_filters(bound=1, total=1) 1061 1062 start_test("Test binding XDP from pinned...") 1063 sim.set_xdp(obj, "offload") 1064 pin_file, pinned = pin_prog("/sys/fs/bpf/tmp2", idx=1) 1065 1066 sim.set_xdp(pinned, "offload", force=True) 1067 sim.unset_xdp("offload") 1068 sim.set_xdp(pinned, "offload", force=True) 1069 sim.unset_xdp("offload") 1070 1071 start_test("Test offload of wrong type fails...") 1072 ret, _ = sim.cls_bpf_add_filter(pinned, da=True, skip_sw=True, fail=False) 1073 fail(ret == 0, "Managed to attach XDP program to TC") 1074 1075 start_test("Test asking for TC offload of two filters...") 1076 sim.cls_bpf_add_filter(obj, da=True, skip_sw=True) 1077 ret, _, err = sim.cls_bpf_add_filter(obj, da=True, skip_sw=True, 1078 fail=False, include_stderr=True) 1079 fail(ret == 0, "Managed to offload two TC filters at the same time") 1080 check_extack_nsim(err, "driver and netdev offload states mismatch.", args) 1081 1082 sim.tc_flush_filters(bound=2, total=2) 1083 1084 start_test("Test if netdev removal waits for translation...") 1085 delay_msec = 500 1086 sim.dfs["dev/bpf_bind_verifier_delay"] = delay_msec 1087 start = time.time() 1088 cmd_line = "tc filter add dev %s ingress bpf %s da skip_sw" % \ 1089 (sim['ifname'], obj) 1090 tc_proc = cmd(cmd_line, background=True, fail=False) 1091 # Wait for the verifier to start 1092 while simdev.dfs_num_bound_progs() <= 2: 1093 pass 1094 simdev.remove() 1095 end = time.time() 1096 ret, _ = cmd_result(tc_proc, fail=False) 1097 time_diff = end - start 1098 log("Time", "start:\t%s\nend:\t%s\ndiff:\t%s" % (start, end, time_diff)) 1099 1100 fail(ret == 0, "Managed to load TC filter on a unregistering device") 1101 delay_sec = delay_msec * 0.001 1102 fail(time_diff < delay_sec, "Removal process took %s, expected %s" % 1103 (time_diff, delay_sec)) 1104 1105 # Remove all pinned files and reinstantiate the netdev 1106 clean_up() 1107 bpftool_prog_list_wait(expected=0) 1108 1109 simdev = BpfNetdevSimDev() 1110 sim, = simdev.nsims 1111 map_obj = bpf_obj("sample_map_ret0.bpf.o") 1112 start_test("Test loading program with maps...") 1113 sim.set_xdp(map_obj, "offload", JSON=False) # map fixup msg breaks JSON 1114 1115 start_test("Test bpftool bound info reporting (own ns)...") 1116 check_dev_info(False, "") 1117 1118 start_test("Test bpftool bound info reporting (other ns)...") 1119 ns = mknetns() 1120 sim.set_ns(ns) 1121 check_dev_info(True, "") 1122 1123 start_test("Test bpftool bound info reporting (remote ns)...") 1124 check_dev_info(False, ns) 1125 1126 start_test("Test bpftool bound info reporting (back to own ns)...") 1127 sim.set_ns("") 1128 check_dev_info(False, "") 1129 1130 prog_file, _ = pin_prog("/sys/fs/bpf/tmp_prog") 1131 map_file, _ = pin_map("/sys/fs/bpf/tmp_map", idx=1, expected=2) 1132 simdev.remove() 1133 1134 start_test("Test bpftool bound info reporting (removed dev)...") 1135 check_dev_info_removed(prog_file=prog_file, map_file=map_file) 1136 1137 # Remove all pinned files and reinstantiate the netdev 1138 clean_up() 1139 bpftool_prog_list_wait(expected=0) 1140 1141 simdev = BpfNetdevSimDev() 1142 sim, = simdev.nsims 1143 1144 start_test("Test map update (no flags)...") 1145 sim.set_xdp(map_obj, "offload", JSON=False) # map fixup msg breaks JSON 1146 maps = bpftool_map_list_wait(expected=2) 1147 array = maps[0] if maps[0]["type"] == "array" else maps[1] 1148 htab = maps[0] if maps[0]["type"] == "hash" else maps[1] 1149 for m in maps: 1150 for i in range(2): 1151 bpftool("map update id %d key %s value %s" % 1152 (m["id"], int2str("I", i), int2str("Q", i * 3))) 1153 1154 for m in maps: 1155 ret, _ = bpftool("map update id %d key %s value %s" % 1156 (m["id"], int2str("I", 3), int2str("Q", 3 * 3)), 1157 fail=False) 1158 fail(ret == 0, "added too many entries") 1159 1160 start_test("Test map update (exists)...") 1161 for m in maps: 1162 for i in range(2): 1163 bpftool("map update id %d key %s value %s exist" % 1164 (m["id"], int2str("I", i), int2str("Q", i * 3))) 1165 1166 for m in maps: 1167 ret, err = bpftool("map update id %d key %s value %s exist" % 1168 (m["id"], int2str("I", 3), int2str("Q", 3 * 3)), 1169 fail=False) 1170 fail(ret == 0, "updated non-existing key") 1171 fail(err["error"].find("No such file or directory") == -1, 1172 "expected ENOENT, error is '%s'" % (err["error"])) 1173 1174 start_test("Test map update (noexist)...") 1175 for m in maps: 1176 for i in range(2): 1177 ret, err = bpftool("map update id %d key %s value %s noexist" % 1178 (m["id"], int2str("I", i), int2str("Q", i * 3)), 1179 fail=False) 1180 fail(ret == 0, "updated existing key") 1181 fail(err["error"].find("File exists") == -1, 1182 "expected EEXIST, error is '%s'" % (err["error"])) 1183 1184 start_test("Test map dump...") 1185 for m in maps: 1186 _, entries = bpftool("map dump id %d" % (m["id"])) 1187 for i in range(2): 1188 key = str2int(entries[i]["key"]) 1189 fail(key != i, "expected key %d, got %d" % (key, i)) 1190 val = str2int(entries[i]["value"]) 1191 fail(val != i * 3, "expected value %d, got %d" % (val, i * 3)) 1192 1193 start_test("Test map getnext...") 1194 for m in maps: 1195 _, entry = bpftool("map getnext id %d" % (m["id"])) 1196 key = str2int(entry["next_key"]) 1197 fail(key != 0, "next key %d, expected %d" % (key, 0)) 1198 _, entry = bpftool("map getnext id %d key %s" % 1199 (m["id"], int2str("I", 0))) 1200 key = str2int(entry["next_key"]) 1201 fail(key != 1, "next key %d, expected %d" % (key, 1)) 1202 ret, err = bpftool("map getnext id %d key %s" % 1203 (m["id"], int2str("I", 1)), fail=False) 1204 fail(ret == 0, "got next key past the end of map") 1205 fail(err["error"].find("No such file or directory") == -1, 1206 "expected ENOENT, error is '%s'" % (err["error"])) 1207 1208 start_test("Test map delete (htab)...") 1209 for i in range(2): 1210 bpftool("map delete id %d key %s" % (htab["id"], int2str("I", i))) 1211 1212 start_test("Test map delete (array)...") 1213 for i in range(2): 1214 ret, err = bpftool("map delete id %d key %s" % 1215 (htab["id"], int2str("I", i)), fail=False) 1216 fail(ret == 0, "removed entry from an array") 1217 fail(err["error"].find("No such file or directory") == -1, 1218 "expected ENOENT, error is '%s'" % (err["error"])) 1219 1220 start_test("Test map remove...") 1221 sim.unset_xdp("offload") 1222 bpftool_map_list_wait(expected=0) 1223 simdev.remove() 1224 1225 simdev = BpfNetdevSimDev() 1226 sim, = simdev.nsims 1227 sim.set_xdp(map_obj, "offload", JSON=False) # map fixup msg breaks JSON 1228 simdev.remove() 1229 bpftool_map_list_wait(expected=0) 1230 1231 start_test("Test map creation fail path...") 1232 simdev = BpfNetdevSimDev() 1233 sim, = simdev.nsims 1234 sim.dfs["bpf_map_accept"] = "N" 1235 ret, _ = sim.set_xdp(map_obj, "offload", JSON=False, fail=False) 1236 fail(ret == 0, 1237 "netdevsim didn't refuse to create a map with offload disabled") 1238 1239 simdev.remove() 1240 1241 start_test("Test multi-dev ASIC program reuse...") 1242 simdevA = BpfNetdevSimDev() 1243 simA, = simdevA.nsims 1244 simdevB = BpfNetdevSimDev(3) 1245 simB1, simB2, simB3 = simdevB.nsims 1246 sims = (simA, simB1, simB2, simB3) 1247 simB = (simB1, simB2, simB3) 1248 1249 bpftool_prog_load("sample_map_ret0.bpf.o", "/sys/fs/bpf/nsimA", 1250 dev=simA['ifname']) 1251 progA = bpf_pinned("/sys/fs/bpf/nsimA") 1252 bpftool_prog_load("sample_map_ret0.bpf.o", "/sys/fs/bpf/nsimB", 1253 dev=simB1['ifname']) 1254 progB = bpf_pinned("/sys/fs/bpf/nsimB") 1255 1256 simA.set_xdp(progA, "offload", JSON=False) 1257 for d in simdevB.nsims: 1258 d.set_xdp(progB, "offload", JSON=False) 1259 1260 start_test("Test multi-dev ASIC cross-dev replace...") 1261 ret, _ = simA.set_xdp(progB, "offload", force=True, JSON=False, fail=False) 1262 fail(ret == 0, "cross-ASIC program allowed") 1263 for d in simdevB.nsims: 1264 ret, _ = d.set_xdp(progA, "offload", force=True, JSON=False, fail=False) 1265 fail(ret == 0, "cross-ASIC program allowed") 1266 1267 start_test("Test multi-dev ASIC cross-dev install...") 1268 for d in sims: 1269 d.unset_xdp("offload") 1270 1271 ret, _, err = simA.set_xdp(progB, "offload", force=True, JSON=False, 1272 fail=False, include_stderr=True) 1273 fail(ret == 0, "cross-ASIC program allowed") 1274 check_extack(err, "Program bound to different device.", args) 1275 for d in simdevB.nsims: 1276 ret, _, err = d.set_xdp(progA, "offload", force=True, JSON=False, 1277 fail=False, include_stderr=True) 1278 fail(ret == 0, "cross-ASIC program allowed") 1279 check_extack(err, "Program bound to different device.", args) 1280 1281 start_test("Test multi-dev ASIC cross-dev map reuse...") 1282 1283 mapA = bpftool("prog show %s" % (progA))[1]["map_ids"][0] 1284 mapB = bpftool("prog show %s" % (progB))[1]["map_ids"][0] 1285 1286 ret, _ = bpftool_prog_load("sample_map_ret0.bpf.o", "/sys/fs/bpf/nsimB_", 1287 dev=simB3['ifname'], 1288 maps=["idx 0 id %d" % (mapB)], 1289 fail=False) 1290 fail(ret != 0, "couldn't reuse a map on the same ASIC") 1291 rm("/sys/fs/bpf/nsimB_") 1292 1293 ret, _, err = bpftool_prog_load("sample_map_ret0.bpf.o", "/sys/fs/bpf/nsimA_", 1294 dev=simA['ifname'], 1295 maps=["idx 0 id %d" % (mapB)], 1296 fail=False, include_stderr=True) 1297 fail(ret == 0, "could reuse a map on a different ASIC") 1298 fail(err.count("offload device mismatch between prog and map") == 0, 1299 "error message missing for cross-ASIC map") 1300 1301 ret, _, err = bpftool_prog_load("sample_map_ret0.bpf.o", "/sys/fs/bpf/nsimB_", 1302 dev=simB1['ifname'], 1303 maps=["idx 0 id %d" % (mapA)], 1304 fail=False, include_stderr=True) 1305 fail(ret == 0, "could reuse a map on a different ASIC") 1306 fail(err.count("offload device mismatch between prog and map") == 0, 1307 "error message missing for cross-ASIC map") 1308 1309 start_test("Test multi-dev ASIC cross-dev destruction...") 1310 bpftool_prog_list_wait(expected=2) 1311 1312 simdevA.remove() 1313 bpftool_prog_list_wait(expected=1) 1314 1315 ifnameB = bpftool("prog show %s" % (progB))[1]["dev"]["ifname"] 1316 fail(ifnameB != simB1['ifname'], "program not bound to original device") 1317 simB1.remove() 1318 bpftool_prog_list_wait(expected=1) 1319 1320 start_test("Test multi-dev ASIC cross-dev destruction - move...") 1321 ifnameB = bpftool("prog show %s" % (progB))[1]["dev"]["ifname"] 1322 fail(ifnameB not in (simB2['ifname'], simB3['ifname']), 1323 "program not bound to remaining devices") 1324 1325 simB2.remove() 1326 ifnameB = bpftool("prog show %s" % (progB))[1]["dev"]["ifname"] 1327 fail(ifnameB != simB3['ifname'], "program not bound to remaining device") 1328 1329 simB3.remove() 1330 simdevB.remove() 1331 bpftool_prog_list_wait(expected=0) 1332 1333 start_test("Test multi-dev ASIC cross-dev destruction - orphaned...") 1334 ret, out = bpftool("prog show %s" % (progB), fail=False) 1335 fail(ret != 0, "couldn't get information about orphaned program") 1336 1337 print("%s: OK" % (os.path.basename(__file__))) 1338 1339finally: 1340 log("Clean up...", "", level=1) 1341 log_level_inc() 1342 clean_up() 1343