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