xref: /openbmc/linux/tools/testing/selftests/bpf/test_offload.py (revision 9dae47aba0a055f761176d9297371d5bb24289ec)
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
16from datetime import datetime
17import argparse
18import json
19import os
20import pprint
21import random
22import string
23import subprocess
24import time
25
26logfile = None
27log_level = 1
28bpf_test_dir = os.path.dirname(os.path.realpath(__file__))
29pp = pprint.PrettyPrinter()
30devs = [] # devices we created for clean up
31files = [] # files to be removed
32netns = [] # net namespaces to be removed
33
34def log_get_sec(level=0):
35    return "*" * (log_level + level)
36
37def log_level_inc(add=1):
38    global log_level
39    log_level += add
40
41def log_level_dec(sub=1):
42    global log_level
43    log_level -= sub
44
45def log_level_set(level):
46    global log_level
47    log_level = level
48
49def log(header, data, level=None):
50    """
51    Output to an optional log.
52    """
53    if logfile is None:
54        return
55    if level is not None:
56        log_level_set(level)
57
58    if not isinstance(data, str):
59        data = pp.pformat(data)
60
61    if len(header):
62        logfile.write("\n" + log_get_sec() + " ")
63        logfile.write(header)
64    if len(header) and len(data.strip()):
65        logfile.write("\n")
66    logfile.write(data)
67
68def skip(cond, msg):
69    if not cond:
70        return
71    print("SKIP: " + msg)
72    log("SKIP: " + msg, "", level=1)
73    os.sys.exit(0)
74
75def fail(cond, msg):
76    if not cond:
77        return
78    print("FAIL: " + msg)
79    log("FAIL: " + msg, "", level=1)
80    os.sys.exit(1)
81
82def start_test(msg):
83    log(msg, "", level=1)
84    log_level_inc()
85    print(msg)
86
87def cmd(cmd, shell=True, include_stderr=False, background=False, fail=True):
88    """
89    Run a command in subprocess and return tuple of (retval, stdout);
90    optionally return stderr as well as third value.
91    """
92    proc = subprocess.Popen(cmd, shell=shell, stdout=subprocess.PIPE,
93                            stderr=subprocess.PIPE)
94    if background:
95        msg = "%s START: %s" % (log_get_sec(1),
96                                datetime.now().strftime("%H:%M:%S.%f"))
97        log("BKG " + proc.args, msg)
98        return proc
99
100    return cmd_result(proc, include_stderr=include_stderr, fail=fail)
101
102def cmd_result(proc, include_stderr=False, fail=False):
103    stdout, stderr = proc.communicate()
104    stdout = stdout.decode("utf-8")
105    stderr = stderr.decode("utf-8")
106    proc.stdout.close()
107    proc.stderr.close()
108
109    stderr = "\n" + stderr
110    if stderr[-1] == "\n":
111        stderr = stderr[:-1]
112
113    sec = log_get_sec(1)
114    log("CMD " + proc.args,
115        "RETCODE: %d\n%s STDOUT:\n%s%s STDERR:%s\n%s END: %s" %
116        (proc.returncode, sec, stdout, sec, stderr,
117         sec, datetime.now().strftime("%H:%M:%S.%f")))
118
119    if proc.returncode != 0 and fail:
120        if len(stderr) > 0 and stderr[-1] == "\n":
121            stderr = stderr[:-1]
122        raise Exception("Command failed: %s\n%s" % (proc.args, stderr))
123
124    if include_stderr:
125        return proc.returncode, stdout, stderr
126    else:
127        return proc.returncode, stdout
128
129def rm(f):
130    cmd("rm -f %s" % (f))
131    if f in files:
132        files.remove(f)
133
134def tool(name, args, flags, JSON=True, ns="", fail=True):
135    params = ""
136    if JSON:
137        params += "%s " % (flags["json"])
138
139    if ns != "":
140        ns = "ip netns exec %s " % (ns)
141
142    ret, out = cmd(ns + name + " " + params + args, fail=fail)
143    if JSON and len(out.strip()) != 0:
144        return ret, json.loads(out)
145    else:
146        return ret, out
147
148def bpftool(args, JSON=True, ns="", fail=True):
149    return tool("bpftool", args, {"json":"-p"}, JSON=JSON, ns=ns, fail=fail)
150
151def bpftool_prog_list(expected=None, ns=""):
152    _, progs = bpftool("prog show", JSON=True, ns=ns, fail=True)
153    if expected is not None:
154        if len(progs) != expected:
155            fail(True, "%d BPF programs loaded, expected %d" %
156                 (len(progs), expected))
157    return progs
158
159def bpftool_prog_list_wait(expected=0, n_retry=20):
160    for i in range(n_retry):
161        nprogs = len(bpftool_prog_list())
162        if nprogs == expected:
163            return
164        time.sleep(0.05)
165    raise Exception("Time out waiting for program counts to stabilize want %d, have %d" % (expected, nprogs))
166
167def ip(args, force=False, JSON=True, ns="", fail=True):
168    if force:
169        args = "-force " + args
170    return tool("ip", args, {"json":"-j"}, JSON=JSON, ns=ns, fail=fail)
171
172def tc(args, JSON=True, ns="", fail=True):
173    return tool("tc", args, {"json":"-p"}, JSON=JSON, ns=ns, fail=fail)
174
175def ethtool(dev, opt, args, fail=True):
176    return cmd("ethtool %s %s %s" % (opt, dev["ifname"], args), fail=fail)
177
178def bpf_obj(name, sec=".text", path=bpf_test_dir,):
179    return "obj %s sec %s" % (os.path.join(path, name), sec)
180
181def bpf_pinned(name):
182    return "pinned %s" % (name)
183
184def bpf_bytecode(bytecode):
185    return "bytecode \"%s\"" % (bytecode)
186
187def mknetns(n_retry=10):
188    for i in range(n_retry):
189        name = ''.join([random.choice(string.ascii_letters) for i in range(8)])
190        ret, _ = ip("netns add %s" % (name), fail=False)
191        if ret == 0:
192            netns.append(name)
193            return name
194    return None
195
196class DebugfsDir:
197    """
198    Class for accessing DebugFS directories as a dictionary.
199    """
200
201    def __init__(self, path):
202        self.path = path
203        self._dict = self._debugfs_dir_read(path)
204
205    def __len__(self):
206        return len(self._dict.keys())
207
208    def __getitem__(self, key):
209        if type(key) is int:
210            key = list(self._dict.keys())[key]
211        return self._dict[key]
212
213    def __setitem__(self, key, value):
214        log("DebugFS set %s = %s" % (key, value), "")
215        log_level_inc()
216
217        cmd("echo '%s' > %s/%s" % (value, self.path, key))
218        log_level_dec()
219
220        _, out = cmd('cat %s/%s' % (self.path, key))
221        self._dict[key] = out.strip()
222
223    def _debugfs_dir_read(self, path):
224        dfs = {}
225
226        log("DebugFS state for %s" % (path), "")
227        log_level_inc(add=2)
228
229        _, out = cmd('ls ' + path)
230        for f in out.split():
231            p = os.path.join(path, f)
232            if os.path.isfile(p):
233                _, out = cmd('cat %s/%s' % (path, f))
234                dfs[f] = out.strip()
235            elif os.path.isdir(p):
236                dfs[f] = DebugfsDir(p)
237            else:
238                raise Exception("%s is neither file nor directory" % (p))
239
240        log_level_dec()
241        log("DebugFS state", dfs)
242        log_level_dec()
243
244        return dfs
245
246class NetdevSim:
247    """
248    Class for netdevsim netdevice and its attributes.
249    """
250
251    def __init__(self):
252        self.dev = self._netdevsim_create()
253        devs.append(self)
254
255        self.ns = ""
256
257        self.dfs_dir = '/sys/kernel/debug/netdevsim/%s' % (self.dev['ifname'])
258        self.dfs_refresh()
259
260    def __getitem__(self, key):
261        return self.dev[key]
262
263    def _netdevsim_create(self):
264        _, old  = ip("link show")
265        ip("link add sim%d type netdevsim")
266        _, new  = ip("link show")
267
268        for dev in new:
269            f = filter(lambda x: x["ifname"] == dev["ifname"], old)
270            if len(list(f)) == 0:
271                return dev
272
273        raise Exception("failed to create netdevsim device")
274
275    def remove(self):
276        devs.remove(self)
277        ip("link del dev %s" % (self.dev["ifname"]), ns=self.ns)
278
279    def dfs_refresh(self):
280        self.dfs = DebugfsDir(self.dfs_dir)
281        return self.dfs
282
283    def dfs_num_bound_progs(self):
284        path = os.path.join(self.dfs_dir, "bpf_bound_progs")
285        _, progs = cmd('ls %s' % (path))
286        return len(progs.split())
287
288    def dfs_get_bound_progs(self, expected):
289        progs = DebugfsDir(os.path.join(self.dfs_dir, "bpf_bound_progs"))
290        if expected is not None:
291            if len(progs) != expected:
292                fail(True, "%d BPF programs bound, expected %d" %
293                     (len(progs), expected))
294        return progs
295
296    def wait_for_flush(self, bound=0, total=0, n_retry=20):
297        for i in range(n_retry):
298            nbound = self.dfs_num_bound_progs()
299            nprogs = len(bpftool_prog_list())
300            if nbound == bound and nprogs == total:
301                return
302            time.sleep(0.05)
303        raise Exception("Time out waiting for program counts to stabilize want %d/%d, have %d bound, %d loaded" % (bound, total, nbound, nprogs))
304
305    def set_ns(self, ns):
306        name = "1" if ns == "" else ns
307        ip("link set dev %s netns %s" % (self.dev["ifname"], name), ns=self.ns)
308        self.ns = ns
309
310    def set_mtu(self, mtu, fail=True):
311        return ip("link set dev %s mtu %d" % (self.dev["ifname"], mtu),
312                  fail=fail)
313
314    def set_xdp(self, bpf, mode, force=False, fail=True):
315        return ip("link set dev %s xdp%s %s" % (self.dev["ifname"], mode, bpf),
316                  force=force, fail=fail)
317
318    def unset_xdp(self, mode, force=False, fail=True):
319        return ip("link set dev %s xdp%s off" % (self.dev["ifname"], mode),
320                  force=force, fail=fail)
321
322    def ip_link_show(self, xdp):
323        _, link = ip("link show dev %s" % (self['ifname']))
324        if len(link) > 1:
325            raise Exception("Multiple objects on ip link show")
326        if len(link) < 1:
327            return {}
328        fail(xdp != "xdp" in link,
329             "XDP program not reporting in iplink (reported %s, expected %s)" %
330             ("xdp" in link, xdp))
331        return link[0]
332
333    def tc_add_ingress(self):
334        tc("qdisc add dev %s ingress" % (self['ifname']))
335
336    def tc_del_ingress(self):
337        tc("qdisc del dev %s ingress" % (self['ifname']))
338
339    def tc_flush_filters(self, bound=0, total=0):
340        self.tc_del_ingress()
341        self.tc_add_ingress()
342        self.wait_for_flush(bound=bound, total=total)
343
344    def tc_show_ingress(self, expected=None):
345        # No JSON support, oh well...
346        flags = ["skip_sw", "skip_hw", "in_hw"]
347        named = ["protocol", "pref", "chain", "handle", "id", "tag"]
348
349        args = "-s filter show dev %s ingress" % (self['ifname'])
350        _, out = tc(args, JSON=False)
351
352        filters = []
353        lines = out.split('\n')
354        for line in lines:
355            words = line.split()
356            if "handle" not in words:
357                continue
358            fltr = {}
359            for flag in flags:
360                fltr[flag] = flag in words
361            for name in named:
362                try:
363                    idx = words.index(name)
364                    fltr[name] = words[idx + 1]
365                except ValueError:
366                    pass
367            filters.append(fltr)
368
369        if expected is not None:
370            fail(len(filters) != expected,
371                 "%d ingress filters loaded, expected %d" %
372                 (len(filters), expected))
373        return filters
374
375    def cls_bpf_add_filter(self, bpf, da=False, skip_sw=False, skip_hw=False,
376                           fail=True):
377        params = ""
378        if da:
379            params += " da"
380        if skip_sw:
381            params += " skip_sw"
382        if skip_hw:
383            params += " skip_hw"
384        return tc("filter add dev %s ingress bpf %s %s" %
385                  (self['ifname'], bpf, params), fail=fail)
386
387    def set_ethtool_tc_offloads(self, enable, fail=True):
388        args = "hw-tc-offload %s" % ("on" if enable else "off")
389        return ethtool(self, "-K", args, fail=fail)
390
391################################################################################
392def clean_up():
393    for dev in devs:
394        dev.remove()
395    for f in files:
396        cmd("rm -f %s" % (f))
397    for ns in netns:
398        cmd("ip netns delete %s" % (ns))
399
400def pin_prog(file_name, idx=0):
401    progs = bpftool_prog_list(expected=(idx + 1))
402    prog = progs[idx]
403    bpftool("prog pin id %d %s" % (prog["id"], file_name))
404    files.append(file_name)
405
406    return file_name, bpf_pinned(file_name)
407
408def check_dev_info(other_ns, ns, pin_file=None, removed=False):
409    if removed:
410        bpftool_prog_list(expected=0)
411        ret, err = bpftool("prog show pin %s" % (pin_file), fail=False)
412        fail(ret == 0, "Showing prog with removed device did not fail")
413        fail(err["error"].find("No such device") == -1,
414             "Showing prog with removed device expected ENODEV, error is %s" %
415             (err["error"]))
416        return
417    progs = bpftool_prog_list(expected=int(not removed), ns=ns)
418    prog = progs[0]
419
420    fail("dev" not in prog.keys(), "Device parameters not reported")
421    dev = prog["dev"]
422    fail("ifindex" not in dev.keys(), "Device parameters not reported")
423    fail("ns_dev" not in dev.keys(), "Device parameters not reported")
424    fail("ns_inode" not in dev.keys(), "Device parameters not reported")
425
426    if not removed and not other_ns:
427        fail("ifname" not in dev.keys(), "Ifname not reported")
428        fail(dev["ifname"] != sim["ifname"],
429             "Ifname incorrect %s vs %s" % (dev["ifname"], sim["ifname"]))
430    else:
431        fail("ifname" in dev.keys(), "Ifname is reported for other ns")
432        if removed:
433            fail(dev["ifindex"] != 0, "Device perameters not zero on removed")
434            fail(dev["ns_dev"] != 0, "Device perameters not zero on removed")
435            fail(dev["ns_inode"] != 0, "Device perameters not zero on removed")
436
437# Parse command line
438parser = argparse.ArgumentParser()
439parser.add_argument("--log", help="output verbose log to given file")
440args = parser.parse_args()
441if args.log:
442    logfile = open(args.log, 'w+')
443    logfile.write("# -*-Org-*-")
444
445log("Prepare...", "", level=1)
446log_level_inc()
447
448# Check permissions
449skip(os.getuid() != 0, "test must be run as root")
450
451# Check tools
452ret, progs = bpftool("prog", fail=False)
453skip(ret != 0, "bpftool not installed")
454# Check no BPF programs are loaded
455skip(len(progs) != 0, "BPF programs already loaded on the system")
456
457# Check netdevsim
458ret, out = cmd("modprobe netdevsim", fail=False)
459skip(ret != 0, "netdevsim module could not be loaded")
460
461# Check debugfs
462_, out = cmd("mount")
463if out.find("/sys/kernel/debug type debugfs") == -1:
464    cmd("mount -t debugfs none /sys/kernel/debug")
465
466# Check samples are compiled
467samples = ["sample_ret0.o"]
468for s in samples:
469    ret, out = cmd("ls %s/%s" % (bpf_test_dir, s), fail=False)
470    skip(ret != 0, "sample %s/%s not found, please compile it" %
471         (bpf_test_dir, s))
472
473# Check if net namespaces seem to work
474ns = mknetns()
475skip(ns is None, "Could not create a net namespace")
476cmd("ip netns delete %s" % (ns))
477netns = []
478
479try:
480    obj = bpf_obj("sample_ret0.o")
481    bytecode = bpf_bytecode("1,6 0 0 4294967295,")
482
483    start_test("Test destruction of generic XDP...")
484    sim = NetdevSim()
485    sim.set_xdp(obj, "generic")
486    sim.remove()
487    bpftool_prog_list_wait(expected=0)
488
489    sim = NetdevSim()
490    sim.tc_add_ingress()
491
492    start_test("Test TC non-offloaded...")
493    ret, _ = sim.cls_bpf_add_filter(obj, skip_hw=True, fail=False)
494    fail(ret != 0, "Software TC filter did not load")
495
496    start_test("Test TC non-offloaded isn't getting bound...")
497    ret, _ = sim.cls_bpf_add_filter(obj, fail=False)
498    fail(ret != 0, "Software TC filter did not load")
499    sim.dfs_get_bound_progs(expected=0)
500
501    sim.tc_flush_filters()
502
503    start_test("Test TC offloads are off by default...")
504    ret, _ = sim.cls_bpf_add_filter(obj, skip_sw=True, fail=False)
505    fail(ret == 0, "TC filter loaded without enabling TC offloads")
506    sim.wait_for_flush()
507
508    sim.set_ethtool_tc_offloads(True)
509    sim.dfs["bpf_tc_non_bound_accept"] = "Y"
510
511    start_test("Test TC offload by default...")
512    ret, _ = sim.cls_bpf_add_filter(obj, fail=False)
513    fail(ret != 0, "Software TC filter did not load")
514    sim.dfs_get_bound_progs(expected=0)
515    ingress = sim.tc_show_ingress(expected=1)
516    fltr = ingress[0]
517    fail(not fltr["in_hw"], "Filter not offloaded by default")
518
519    sim.tc_flush_filters()
520
521    start_test("Test TC cBPF bytcode tries offload by default...")
522    ret, _ = sim.cls_bpf_add_filter(bytecode, fail=False)
523    fail(ret != 0, "Software TC filter did not load")
524    sim.dfs_get_bound_progs(expected=0)
525    ingress = sim.tc_show_ingress(expected=1)
526    fltr = ingress[0]
527    fail(not fltr["in_hw"], "Bytecode not offloaded by default")
528
529    sim.tc_flush_filters()
530    sim.dfs["bpf_tc_non_bound_accept"] = "N"
531
532    start_test("Test TC cBPF unbound bytecode doesn't offload...")
533    ret, _ = sim.cls_bpf_add_filter(bytecode, skip_sw=True, fail=False)
534    fail(ret == 0, "TC bytecode loaded for offload")
535    sim.wait_for_flush()
536
537    start_test("Test TC offloads work...")
538    ret, _ = sim.cls_bpf_add_filter(obj, skip_sw=True, fail=False)
539    fail(ret != 0, "TC filter did not load with TC offloads enabled")
540
541    start_test("Test TC offload basics...")
542    dfs = sim.dfs_get_bound_progs(expected=1)
543    progs = bpftool_prog_list(expected=1)
544    ingress = sim.tc_show_ingress(expected=1)
545
546    dprog = dfs[0]
547    prog = progs[0]
548    fltr = ingress[0]
549    fail(fltr["skip_hw"], "TC does reports 'skip_hw' on offloaded filter")
550    fail(not fltr["in_hw"], "TC does not report 'in_hw' for offloaded filter")
551    fail(not fltr["skip_sw"], "TC does not report 'skip_sw' back")
552
553    start_test("Test TC offload is device-bound...")
554    fail(str(prog["id"]) != fltr["id"], "Program IDs don't match")
555    fail(prog["tag"] != fltr["tag"], "Program tags don't match")
556    fail(fltr["id"] != dprog["id"], "Program IDs don't match")
557    fail(dprog["state"] != "xlated", "Offloaded program state not translated")
558    fail(dprog["loaded"] != "Y", "Offloaded program is not loaded")
559
560    start_test("Test disabling TC offloads is rejected while filters installed...")
561    ret, _ = sim.set_ethtool_tc_offloads(False, fail=False)
562    fail(ret == 0, "Driver should refuse to disable TC offloads with filters installed...")
563
564    start_test("Test qdisc removal frees things...")
565    sim.tc_flush_filters()
566    sim.tc_show_ingress(expected=0)
567
568    start_test("Test disabling TC offloads is OK without filters...")
569    ret, _ = sim.set_ethtool_tc_offloads(False, fail=False)
570    fail(ret != 0,
571         "Driver refused to disable TC offloads without filters installed...")
572
573    sim.set_ethtool_tc_offloads(True)
574
575    start_test("Test destroying device gets rid of TC filters...")
576    sim.cls_bpf_add_filter(obj, skip_sw=True)
577    sim.remove()
578    bpftool_prog_list_wait(expected=0)
579
580    sim = NetdevSim()
581    sim.set_ethtool_tc_offloads(True)
582
583    start_test("Test destroying device gets rid of XDP...")
584    sim.set_xdp(obj, "offload")
585    sim.remove()
586    bpftool_prog_list_wait(expected=0)
587
588    sim = NetdevSim()
589    sim.set_ethtool_tc_offloads(True)
590
591    start_test("Test XDP prog reporting...")
592    sim.set_xdp(obj, "drv")
593    ipl = sim.ip_link_show(xdp=True)
594    progs = bpftool_prog_list(expected=1)
595    fail(ipl["xdp"]["prog"]["id"] != progs[0]["id"],
596         "Loaded program has wrong ID")
597
598    start_test("Test XDP prog replace without force...")
599    ret, _ = sim.set_xdp(obj, "drv", fail=False)
600    fail(ret == 0, "Replaced XDP program without -force")
601    sim.wait_for_flush(total=1)
602
603    start_test("Test XDP prog replace with force...")
604    ret, _ = sim.set_xdp(obj, "drv", force=True, fail=False)
605    fail(ret != 0, "Could not replace XDP program with -force")
606    bpftool_prog_list_wait(expected=1)
607    ipl = sim.ip_link_show(xdp=True)
608    progs = bpftool_prog_list(expected=1)
609    fail(ipl["xdp"]["prog"]["id"] != progs[0]["id"],
610         "Loaded program has wrong ID")
611    fail("dev" in progs[0].keys(),
612         "Device parameters reported for non-offloaded program")
613
614    start_test("Test XDP prog replace with bad flags...")
615    ret, _ = sim.set_xdp(obj, "offload", force=True, fail=False)
616    fail(ret == 0, "Replaced XDP program with a program in different mode")
617    ret, _ = sim.set_xdp(obj, "", force=True, fail=False)
618    fail(ret == 0, "Replaced XDP program with a program in different mode")
619
620    start_test("Test XDP prog remove with bad flags...")
621    ret, _ = sim.unset_xdp("offload", force=True, fail=False)
622    fail(ret == 0, "Removed program with a bad mode mode")
623    ret, _ = sim.unset_xdp("", force=True, fail=False)
624    fail(ret == 0, "Removed program with a bad mode mode")
625
626    start_test("Test MTU restrictions...")
627    ret, _ = sim.set_mtu(9000, fail=False)
628    fail(ret == 0,
629         "Driver should refuse to increase MTU to 9000 with XDP loaded...")
630    sim.unset_xdp("drv")
631    bpftool_prog_list_wait(expected=0)
632    sim.set_mtu(9000)
633    ret, _ = sim.set_xdp(obj, "drv", fail=False)
634    fail(ret == 0, "Driver should refuse to load program with MTU of 9000...")
635    sim.set_mtu(1500)
636
637    sim.wait_for_flush()
638    start_test("Test XDP offload...")
639    sim.set_xdp(obj, "offload")
640    ipl = sim.ip_link_show(xdp=True)
641    link_xdp = ipl["xdp"]["prog"]
642    progs = bpftool_prog_list(expected=1)
643    prog = progs[0]
644    fail(link_xdp["id"] != prog["id"], "Loaded program has wrong ID")
645
646    start_test("Test XDP offload is device bound...")
647    dfs = sim.dfs_get_bound_progs(expected=1)
648    dprog = dfs[0]
649
650    fail(prog["id"] != link_xdp["id"], "Program IDs don't match")
651    fail(prog["tag"] != link_xdp["tag"], "Program tags don't match")
652    fail(str(link_xdp["id"]) != dprog["id"], "Program IDs don't match")
653    fail(dprog["state"] != "xlated", "Offloaded program state not translated")
654    fail(dprog["loaded"] != "Y", "Offloaded program is not loaded")
655
656    start_test("Test removing XDP program many times...")
657    sim.unset_xdp("offload")
658    sim.unset_xdp("offload")
659    sim.unset_xdp("drv")
660    sim.unset_xdp("drv")
661    sim.unset_xdp("")
662    sim.unset_xdp("")
663    bpftool_prog_list_wait(expected=0)
664
665    start_test("Test attempt to use a program for a wrong device...")
666    sim2 = NetdevSim()
667    sim2.set_xdp(obj, "offload")
668    pin_file, pinned = pin_prog("/sys/fs/bpf/tmp")
669
670    ret, _ = sim.set_xdp(pinned, "offload", fail=False)
671    fail(ret == 0, "Pinned program loaded for a different device accepted")
672    sim2.remove()
673    ret, _ = sim.set_xdp(pinned, "offload", fail=False)
674    fail(ret == 0, "Pinned program loaded for a removed device accepted")
675    rm(pin_file)
676    bpftool_prog_list_wait(expected=0)
677
678    start_test("Test mixing of TC and XDP...")
679    sim.tc_add_ingress()
680    sim.set_xdp(obj, "offload")
681    ret, _ = sim.cls_bpf_add_filter(obj, skip_sw=True, fail=False)
682    fail(ret == 0, "Loading TC when XDP active should fail")
683    sim.unset_xdp("offload")
684    sim.wait_for_flush()
685
686    sim.cls_bpf_add_filter(obj, skip_sw=True)
687    ret, _ = sim.set_xdp(obj, "offload", fail=False)
688    fail(ret == 0, "Loading XDP when TC active should fail")
689
690    start_test("Test binding TC from pinned...")
691    pin_file, pinned = pin_prog("/sys/fs/bpf/tmp")
692    sim.tc_flush_filters(bound=1, total=1)
693    sim.cls_bpf_add_filter(pinned, da=True, skip_sw=True)
694    sim.tc_flush_filters(bound=1, total=1)
695
696    start_test("Test binding XDP from pinned...")
697    sim.set_xdp(obj, "offload")
698    pin_file, pinned = pin_prog("/sys/fs/bpf/tmp2", idx=1)
699
700    sim.set_xdp(pinned, "offload", force=True)
701    sim.unset_xdp("offload")
702    sim.set_xdp(pinned, "offload", force=True)
703    sim.unset_xdp("offload")
704
705    start_test("Test offload of wrong type fails...")
706    ret, _ = sim.cls_bpf_add_filter(pinned, da=True, skip_sw=True, fail=False)
707    fail(ret == 0, "Managed to attach XDP program to TC")
708
709    start_test("Test asking for TC offload of two filters...")
710    sim.cls_bpf_add_filter(obj, da=True, skip_sw=True)
711    ret, _ = sim.cls_bpf_add_filter(obj, da=True, skip_sw=True, fail=False)
712    fail(ret == 0, "Managed to offload two TC filters at the same time")
713
714    sim.tc_flush_filters(bound=2, total=2)
715
716    start_test("Test if netdev removal waits for translation...")
717    delay_msec = 500
718    sim.dfs["bpf_bind_verifier_delay"] = delay_msec
719    start = time.time()
720    cmd_line = "tc filter add dev %s ingress bpf %s da skip_sw" % \
721               (sim['ifname'], obj)
722    tc_proc = cmd(cmd_line, background=True, fail=False)
723    # Wait for the verifier to start
724    while sim.dfs_num_bound_progs() <= 2:
725        pass
726    sim.remove()
727    end = time.time()
728    ret, _ = cmd_result(tc_proc, fail=False)
729    time_diff = end - start
730    log("Time", "start:\t%s\nend:\t%s\ndiff:\t%s" % (start, end, time_diff))
731
732    fail(ret == 0, "Managed to load TC filter on a unregistering device")
733    delay_sec = delay_msec * 0.001
734    fail(time_diff < delay_sec, "Removal process took %s, expected %s" %
735         (time_diff, delay_sec))
736
737    # Remove all pinned files and reinstantiate the netdev
738    clean_up()
739    bpftool_prog_list_wait(expected=0)
740
741    sim = NetdevSim()
742    sim.set_ethtool_tc_offloads(True)
743    sim.set_xdp(obj, "offload")
744
745    start_test("Test bpftool bound info reporting (own ns)...")
746    check_dev_info(False, "")
747
748    start_test("Test bpftool bound info reporting (other ns)...")
749    ns = mknetns()
750    sim.set_ns(ns)
751    check_dev_info(True, "")
752
753    start_test("Test bpftool bound info reporting (remote ns)...")
754    check_dev_info(False, ns)
755
756    start_test("Test bpftool bound info reporting (back to own ns)...")
757    sim.set_ns("")
758    check_dev_info(False, "")
759
760    pin_file, _ = pin_prog("/sys/fs/bpf/tmp")
761    sim.remove()
762
763    start_test("Test bpftool bound info reporting (removed dev)...")
764    check_dev_info(True, "", pin_file=pin_file, removed=True)
765
766    print("%s: OK" % (os.path.basename(__file__)))
767
768finally:
769    log("Clean up...", "", level=1)
770    log_level_inc()
771    clean_up()
772