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