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