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