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