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