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