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