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