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