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