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