3 # Copyright (C) 2017 Netronome Systems, Inc.
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
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.
16 from datetime
import datetime
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
36 def log_get_sec(level
=0):
37 return "*" * (log_level
+ level
)
39 def log_level_inc(add
=1):
43 def log_level_dec(sub
=1):
47 def log_level_set(level
):
51 def log(header
, data
, level
=None):
53 Output to an optional log.
60 if not isinstance(data
, str):
61 data
= pp
.pformat(data
)
64 logfile
.write("\n" + log_get_sec() + " ")
66 if len(header
) and len(data
.strip()):
74 log("SKIP: " + msg
, "", level
=1)
81 log("FAIL: " + msg
, "", level
=1)
89 def cmd(cmd
, shell
=True, include_stderr
=False, background
=False, fail
=True):
91 Run a command in subprocess and return tuple of (retval, stdout);
92 optionally return stderr as well as third value.
94 proc
= subprocess
.Popen(cmd
, shell
=shell
, stdout
=subprocess
.PIPE
,
95 stderr
=subprocess
.PIPE
)
97 msg
= "%s START: %s" % (log_get_sec(1),
98 datetime
.now().strftime("%H:%M:%S.%f"))
99 log("BKG " + proc
.args
, msg
)
102 return cmd_result(proc
, include_stderr
=include_stderr
, fail
=fail
)
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")
111 stderr
= "\n" + stderr
112 if stderr
[-1] == "\n":
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")))
121 if proc
.returncode
!= 0 and fail
:
122 if len(stderr
) > 0 and stderr
[-1] == "\n":
124 raise Exception("Command failed: %s\n%s" % (proc
.args
, stderr
))
127 return proc
.returncode
, stdout
, stderr
129 return proc
.returncode
, stdout
132 cmd("rm -f %s" % (f
))
136 def tool(name
, args
, flags
, JSON
=True, ns
="", fail
=True, include_stderr
=False):
139 params
+= "%s " % (flags
["json"])
142 ns
= "ip netns exec %s " % (ns
)
145 ret
, stdout
, stderr
= cmd(ns
+ name
+ " " + params
+ args
,
146 fail
=fail
, include_stderr
=True)
148 ret
, stdout
= cmd(ns
+ name
+ " " + params
+ args
,
149 fail
=fail
, include_stderr
=False)
151 if JSON
and len(stdout
.strip()) != 0:
152 out
= json
.loads(stdout
)
157 return ret
, out
, stderr
161 def bpftool(args
, JSON
=True, ns
="", fail
=True, include_stderr
=False):
162 return tool("bpftool", args
, {"json":"-p"}, JSON
=JSON
, ns
=ns
,
163 fail
=fail
, include_stderr
=include_stderr
)
165 def bpftool_prog_list(expected
=None, ns
=""):
166 _
, progs
= bpftool("prog show", JSON
=True, ns
=ns
, fail
=True)
167 # Remove the base progs
171 if expected
is not None:
172 if len(progs
) != expected
:
173 fail(True, "%d BPF programs loaded, expected %d" %
174 (len(progs
), expected
))
177 def bpftool_map_list(expected
=None, ns
=""):
178 _
, maps
= bpftool("map show", JSON
=True, ns
=ns
, fail
=True)
179 # Remove the base maps
183 if expected
is not None:
184 if len(maps
) != expected
:
185 fail(True, "%d BPF maps loaded, expected %d" %
186 (len(maps
), expected
))
189 def bpftool_prog_list_wait(expected
=0, n_retry
=20):
190 for i
in range(n_retry
):
191 nprogs
= len(bpftool_prog_list())
192 if nprogs
== expected
:
195 raise Exception("Time out waiting for program counts to stabilize want %d, have %d" % (expected
, nprogs
))
197 def bpftool_map_list_wait(expected
=0, n_retry
=20):
198 for i
in range(n_retry
):
199 nmaps
= len(bpftool_map_list())
200 if nmaps
== expected
:
203 raise Exception("Time out waiting for map counts to stabilize want %d, have %d" % (expected
, nmaps
))
205 def bpftool_prog_load(sample
, file_name
, maps
=[], prog_type
="xdp", dev
=None,
206 fail
=True, include_stderr
=False):
207 args
= "prog load %s %s" % (os
.path
.join(bpf_test_dir
, sample
), file_name
)
208 if prog_type
is not None:
209 args
+= " type " + prog_type
211 args
+= " dev " + dev
213 args
+= " map " + " map ".join(maps
)
215 res
= bpftool(args
, fail
=fail
, include_stderr
=include_stderr
)
217 files
.append(file_name
)
220 def ip(args
, force
=False, JSON
=True, ns
="", fail
=True, include_stderr
=False):
222 args
= "-force " + args
223 return tool("ip", args
, {"json":"-j"}, JSON
=JSON
, ns
=ns
,
224 fail
=fail
, include_stderr
=include_stderr
)
226 def tc(args
, JSON
=True, ns
="", fail
=True, include_stderr
=False):
227 return tool("tc", args
, {"json":"-p"}, JSON
=JSON
, ns
=ns
,
228 fail
=fail
, include_stderr
=include_stderr
)
230 def ethtool(dev
, opt
, args
, fail
=True):
231 return cmd("ethtool %s %s %s" % (opt
, dev
["ifname"], args
), fail
=fail
)
233 def bpf_obj(name
, sec
=".text", path
=bpf_test_dir
,):
234 return "obj %s sec %s" % (os
.path
.join(path
, name
), sec
)
236 def bpf_pinned(name
):
237 return "pinned %s" % (name
)
239 def bpf_bytecode(bytecode
):
240 return "bytecode \"%s\"" % (bytecode
)
242 def mknetns(n_retry
=10):
243 for i
in range(n_retry
):
244 name
= ''.join([random
.choice(string
.ascii_letters
) for i
in range(8)])
245 ret
, _
= ip("netns add %s" % (name
), fail
=False)
251 def int2str(fmt
, val
):
253 for b
in struct
.pack(fmt
, val
):
255 return " ".join(map(lambda x
: str(x
), ret
))
260 inttab
.append(int(i
, 16))
261 ba
= bytearray(inttab
)
264 elif len(strtab
) == 8:
267 raise Exception("String array of len %d can't be unpacked to an int" %
269 return struct
.unpack(fmt
, ba
)[0]
273 Class for accessing DebugFS directories as a dictionary.
276 def __init__(self
, path
):
278 self
._dict
= self
._debugfs
_dir
_read
(path
)
281 return len(self
._dict
.keys())
283 def __getitem__(self
, key
):
285 key
= list(self
._dict
.keys())[key
]
286 return self
._dict
[key
]
288 def __setitem__(self
, key
, value
):
289 log("DebugFS set %s = %s" % (key
, value
), "")
292 cmd("echo '%s' > %s/%s" % (value
, self
.path
, key
))
295 _
, out
= cmd('cat %s/%s' % (self
.path
, key
))
296 self
._dict
[key
] = out
.strip()
298 def _debugfs_dir_read(self
, path
):
301 log("DebugFS state for %s" % (path
), "")
304 _
, out
= cmd('ls ' + path
)
305 for f
in out
.split():
306 p
= os
.path
.join(path
, f
)
307 if os
.path
.isfile(p
):
308 _
, out
= cmd('cat %s/%s' % (path
, f
))
310 elif os
.path
.isdir(p
):
311 dfs
[f
] = DebugfsDir(p
)
313 raise Exception("%s is neither file nor directory" % (p
))
316 log("DebugFS state", dfs
)
323 Class for netdevsim netdevice and its attributes.
326 def __init__(self
, link
=None):
329 self
.dev
= self
._netdevsim
_create
()
334 self
.dfs_dir
= '/sys/kernel/debug/netdevsim/%s' % (self
.dev
['ifname'])
335 self
.sdev_dir
= self
.dfs_dir
+ '/sdev/'
338 def __getitem__(self
, key
):
341 def _netdevsim_create(self
):
342 link
= "" if self
.link
is None else "link " + self
.link
.dev
['ifname']
343 _
, old
= ip("link show")
344 ip("link add sim%d {link} type netdevsim".format(link
=link
))
345 _
, new
= ip("link show")
348 f
= filter(lambda x
: x
["ifname"] == dev
["ifname"], old
)
349 if len(list(f
)) == 0:
352 raise Exception("failed to create netdevsim device")
356 ip("link del dev %s" % (self
.dev
["ifname"]), ns
=self
.ns
)
358 def dfs_refresh(self
):
359 self
.dfs
= DebugfsDir(self
.dfs_dir
)
362 def dfs_read(self
, f
):
363 path
= os
.path
.join(self
.dfs_dir
, f
)
364 _
, data
= cmd('cat %s' % (path
))
367 def dfs_num_bound_progs(self
):
368 path
= os
.path
.join(self
.sdev_dir
, "bpf_bound_progs")
369 _
, progs
= cmd('ls %s' % (path
))
370 return len(progs
.split())
372 def dfs_get_bound_progs(self
, expected
):
373 progs
= DebugfsDir(os
.path
.join(self
.sdev_dir
, "bpf_bound_progs"))
374 if expected
is not None:
375 if len(progs
) != expected
:
376 fail(True, "%d BPF programs bound, expected %d" %
377 (len(progs
), expected
))
380 def wait_for_flush(self
, bound
=0, total
=0, n_retry
=20):
381 for i
in range(n_retry
):
382 nbound
= self
.dfs_num_bound_progs()
383 nprogs
= len(bpftool_prog_list())
384 if nbound
== bound
and nprogs
== total
:
387 raise Exception("Time out waiting for program counts to stabilize want %d/%d, have %d bound, %d loaded" % (bound
, total
, nbound
, nprogs
))
389 def set_ns(self
, ns
):
390 name
= "1" if ns
== "" else ns
391 ip("link set dev %s netns %s" % (self
.dev
["ifname"], name
), ns
=self
.ns
)
394 def set_mtu(self
, mtu
, fail
=True):
395 return ip("link set dev %s mtu %d" % (self
.dev
["ifname"], mtu
),
398 def set_xdp(self
, bpf
, mode
, force
=False, JSON
=True, verbose
=False,
399 fail
=True, include_stderr
=False):
402 return ip("link set dev %s xdp%s %s" % (self
.dev
["ifname"], mode
, bpf
),
403 force
=force
, JSON
=JSON
,
404 fail
=fail
, include_stderr
=include_stderr
)
406 def unset_xdp(self
, mode
, force
=False, JSON
=True,
407 fail
=True, include_stderr
=False):
408 return ip("link set dev %s xdp%s off" % (self
.dev
["ifname"], mode
),
409 force
=force
, JSON
=JSON
,
410 fail
=fail
, include_stderr
=include_stderr
)
412 def ip_link_show(self
, xdp
):
413 _
, link
= ip("link show dev %s" % (self
['ifname']))
415 raise Exception("Multiple objects on ip link show")
418 fail(xdp
!= "xdp" in link
,
419 "XDP program not reporting in iplink (reported %s, expected %s)" %
420 ("xdp" in link
, xdp
))
423 def tc_add_ingress(self
):
424 tc("qdisc add dev %s ingress" % (self
['ifname']))
426 def tc_del_ingress(self
):
427 tc("qdisc del dev %s ingress" % (self
['ifname']))
429 def tc_flush_filters(self
, bound
=0, total
=0):
430 self
.tc_del_ingress()
431 self
.tc_add_ingress()
432 self
.wait_for_flush(bound
=bound
, total
=total
)
434 def tc_show_ingress(self
, expected
=None):
435 # No JSON support, oh well...
436 flags
= ["skip_sw", "skip_hw", "in_hw"]
437 named
= ["protocol", "pref", "chain", "handle", "id", "tag"]
439 args
= "-s filter show dev %s ingress" % (self
['ifname'])
440 _
, out
= tc(args
, JSON
=False)
443 lines
= out
.split('\n')
446 if "handle" not in words
:
450 fltr
[flag
] = flag
in words
453 idx
= words
.index(name
)
454 fltr
[name
] = words
[idx
+ 1]
459 if expected
is not None:
460 fail(len(filters
) != expected
,
461 "%d ingress filters loaded, expected %d" %
462 (len(filters
), expected
))
465 def cls_filter_op(self
, op
, qdisc
="ingress", prio
=None, handle
=None,
466 chain
=None, cls
="", params
="",
467 fail
=True, include_stderr
=False):
470 spec
+= " prio %d" % (prio
)
472 spec
+= " handle %s" % (handle
)
473 if chain
is not None:
474 spec
+= " chain %d" % (chain
)
476 return tc("filter {op} dev {dev} {qdisc} {spec} {cls} {params}"\
477 .format(op
=op
, dev
=self
['ifname'], qdisc
=qdisc
, spec
=spec
,
478 cls
=cls
, params
=params
),
479 fail
=fail
, include_stderr
=include_stderr
)
481 def cls_bpf_add_filter(self
, bpf
, op
="add", prio
=None, handle
=None,
482 chain
=None, da
=False, verbose
=False,
483 skip_sw
=False, skip_hw
=False,
484 fail
=True, include_stderr
=False):
497 return self
.cls_filter_op(op
=op
, prio
=prio
, handle
=handle
, cls
=cls
,
498 chain
=chain
, params
=params
,
499 fail
=fail
, include_stderr
=include_stderr
)
501 def set_ethtool_tc_offloads(self
, enable
, fail
=True):
502 args
= "hw-tc-offload %s" % ("on" if enable
else "off")
503 return ethtool(self
, "-K", args
, fail
=fail
)
505 ################################################################################
507 global files
, netns
, devs
512 cmd("rm -f %s" % (f
))
514 cmd("ip netns delete %s" % (ns
))
518 def pin_prog(file_name
, idx
=0):
519 progs
= bpftool_prog_list(expected
=(idx
+ 1))
521 bpftool("prog pin id %d %s" % (prog
["id"], file_name
))
522 files
.append(file_name
)
524 return file_name
, bpf_pinned(file_name
)
526 def pin_map(file_name
, idx
=0, expected
=1):
527 maps
= bpftool_map_list(expected
=expected
)
529 bpftool("map pin id %d %s" % (m
["id"], file_name
))
530 files
.append(file_name
)
532 return file_name
, bpf_pinned(file_name
)
534 def check_dev_info_removed(prog_file
=None, map_file
=None):
535 bpftool_prog_list(expected
=0)
536 ret
, err
= bpftool("prog show pin %s" % (prog_file
), fail
=False)
537 fail(ret
== 0, "Showing prog with removed device did not fail")
538 fail(err
["error"].find("No such device") == -1,
539 "Showing prog with removed device expected ENODEV, error is %s" %
542 bpftool_map_list(expected
=0)
543 ret
, err
= bpftool("map show pin %s" % (map_file
), fail
=False)
544 fail(ret
== 0, "Showing map with removed device did not fail")
545 fail(err
["error"].find("No such device") == -1,
546 "Showing map with removed device expected ENODEV, error is %s" %
549 def check_dev_info(other_ns
, ns
, prog_file
=None, map_file
=None, removed
=False):
550 progs
= bpftool_prog_list(expected
=1, ns
=ns
)
553 fail("dev" not in prog
.keys(), "Device parameters not reported")
555 fail("ifindex" not in dev
.keys(), "Device parameters not reported")
556 fail("ns_dev" not in dev
.keys(), "Device parameters not reported")
557 fail("ns_inode" not in dev
.keys(), "Device parameters not reported")
560 fail("ifname" not in dev
.keys(), "Ifname not reported")
561 fail(dev
["ifname"] != sim
["ifname"],
562 "Ifname incorrect %s vs %s" % (dev
["ifname"], sim
["ifname"]))
564 fail("ifname" in dev
.keys(), "Ifname is reported for other ns")
566 maps
= bpftool_map_list(expected
=2, ns
=ns
)
568 fail("dev" not in m
.keys(), "Device parameters not reported")
569 fail(dev
!= m
["dev"], "Map's device different than program's")
571 def check_extack(output
, reference
, args
):
574 lines
= output
.split("\n")
575 comp
= len(lines
) >= 2 and lines
[1] == 'Error: ' + reference
576 fail(not comp
, "Missing or incorrect netlink extack message")
578 def check_extack_nsim(output
, reference
, args
):
579 check_extack(output
, "netdevsim: " + reference
, args
)
581 def check_no_extack(res
, needle
):
582 fail((res
[1] + res
[2]).count(needle
) or (res
[1] + res
[2]).count("Warning:"),
583 "Found '%s' in command output, leaky extack?" % (needle
))
585 def check_verifier_log(output
, reference
):
586 lines
= output
.split("\n")
587 for l
in reversed(lines
):
590 fail(True, "Missing or incorrect message from netdevsim in verifier log")
592 def test_spurios_extack(sim
, obj
, skip_hw
, needle
):
593 res
= sim
.cls_bpf_add_filter(obj
, prio
=1, handle
=1, skip_hw
=skip_hw
,
595 check_no_extack(res
, needle
)
596 res
= sim
.cls_bpf_add_filter(obj
, op
="replace", prio
=1, handle
=1,
597 skip_hw
=skip_hw
, include_stderr
=True)
598 check_no_extack(res
, needle
)
599 res
= sim
.cls_filter_op(op
="delete", prio
=1, handle
=1, cls
="bpf",
601 check_no_extack(res
, needle
)
605 parser
= argparse
.ArgumentParser()
606 parser
.add_argument("--log", help="output verbose log to given file")
607 args
= parser
.parse_args()
609 logfile
= open(args
.log
, 'w+')
610 logfile
.write("# -*-Org-*-")
612 log("Prepare...", "", level
=1)
616 skip(os
.getuid() != 0, "test must be run as root")
619 ret
, progs
= bpftool("prog", fail
=False)
620 skip(ret
!= 0, "bpftool not installed")
622 _
, base_maps
= bpftool("map")
625 ret
, out
= cmd("modprobe netdevsim", fail
=False)
626 skip(ret
!= 0, "netdevsim module could not be loaded")
629 _
, out
= cmd("mount")
630 if out
.find("/sys/kernel/debug type debugfs") == -1:
631 cmd("mount -t debugfs none /sys/kernel/debug")
633 # Check samples are compiled
634 samples
= ["sample_ret0.o", "sample_map_ret0.o"]
636 ret
, out
= cmd("ls %s/%s" % (bpf_test_dir
, s
), fail
=False)
637 skip(ret
!= 0, "sample %s/%s not found, please compile it" %
640 # Check if iproute2 is built with libmnl (needed by extack support)
641 _
, _
, err
= cmd("tc qdisc delete dev lo handle 0",
642 fail
=False, include_stderr
=True)
643 if err
.find("Error: Failed to find qdisc with specified handle.") == -1:
644 print("Warning: no extack message in iproute2 output, libmnl missing?")
645 log("Warning: no extack message in iproute2 output, libmnl missing?", "")
648 # Check if net namespaces seem to work
650 skip(ns
is None, "Could not create a net namespace")
651 cmd("ip netns delete %s" % (ns
))
655 obj
= bpf_obj("sample_ret0.o")
656 bytecode
= bpf_bytecode("1,6 0 0 4294967295,")
658 start_test("Test destruction of generic XDP...")
660 sim
.set_xdp(obj
, "generic")
662 bpftool_prog_list_wait(expected
=0)
667 start_test("Test TC non-offloaded...")
668 ret
, _
= sim
.cls_bpf_add_filter(obj
, skip_hw
=True, fail
=False)
669 fail(ret
!= 0, "Software TC filter did not load")
671 start_test("Test TC non-offloaded isn't getting bound...")
672 ret
, _
= sim
.cls_bpf_add_filter(obj
, fail
=False)
673 fail(ret
!= 0, "Software TC filter did not load")
674 sim
.dfs_get_bound_progs(expected
=0)
676 sim
.tc_flush_filters()
678 start_test("Test TC offloads are off by default...")
679 ret
, _
, err
= sim
.cls_bpf_add_filter(obj
, skip_sw
=True,
680 fail
=False, include_stderr
=True)
681 fail(ret
== 0, "TC filter loaded without enabling TC offloads")
682 check_extack(err
, "TC offload is disabled on net device.", args
)
685 sim
.set_ethtool_tc_offloads(True)
686 sim
.dfs
["bpf_tc_non_bound_accept"] = "Y"
688 start_test("Test TC offload by default...")
689 ret
, _
= sim
.cls_bpf_add_filter(obj
, fail
=False)
690 fail(ret
!= 0, "Software TC filter did not load")
691 sim
.dfs_get_bound_progs(expected
=0)
692 ingress
= sim
.tc_show_ingress(expected
=1)
694 fail(not fltr
["in_hw"], "Filter not offloaded by default")
696 sim
.tc_flush_filters()
698 start_test("Test TC cBPF bytcode tries offload by default...")
699 ret
, _
= sim
.cls_bpf_add_filter(bytecode
, fail
=False)
700 fail(ret
!= 0, "Software TC filter did not load")
701 sim
.dfs_get_bound_progs(expected
=0)
702 ingress
= sim
.tc_show_ingress(expected
=1)
704 fail(not fltr
["in_hw"], "Bytecode not offloaded by default")
706 sim
.tc_flush_filters()
707 sim
.dfs
["bpf_tc_non_bound_accept"] = "N"
709 start_test("Test TC cBPF unbound bytecode doesn't offload...")
710 ret
, _
, err
= sim
.cls_bpf_add_filter(bytecode
, skip_sw
=True,
711 fail
=False, include_stderr
=True)
712 fail(ret
== 0, "TC bytecode loaded for offload")
713 check_extack_nsim(err
, "netdevsim configured to reject unbound programs.",
717 start_test("Test non-0 chain offload...")
718 ret
, _
, err
= sim
.cls_bpf_add_filter(obj
, chain
=1, prio
=1, handle
=1,
720 fail
=False, include_stderr
=True)
721 fail(ret
== 0, "Offloaded a filter to chain other than 0")
722 check_extack(err
, "Driver supports only offload of chain 0.", args
)
723 sim
.tc_flush_filters()
725 start_test("Test TC replace...")
726 sim
.cls_bpf_add_filter(obj
, prio
=1, handle
=1)
727 sim
.cls_bpf_add_filter(obj
, op
="replace", prio
=1, handle
=1)
728 sim
.cls_filter_op(op
="delete", prio
=1, handle
=1, cls
="bpf")
730 sim
.cls_bpf_add_filter(obj
, prio
=1, handle
=1, skip_sw
=True)
731 sim
.cls_bpf_add_filter(obj
, op
="replace", prio
=1, handle
=1, skip_sw
=True)
732 sim
.cls_filter_op(op
="delete", prio
=1, handle
=1, cls
="bpf")
734 sim
.cls_bpf_add_filter(obj
, prio
=1, handle
=1, skip_hw
=True)
735 sim
.cls_bpf_add_filter(obj
, op
="replace", prio
=1, handle
=1, skip_hw
=True)
736 sim
.cls_filter_op(op
="delete", prio
=1, handle
=1, cls
="bpf")
738 start_test("Test TC replace bad flags...")
741 ret
, _
= sim
.cls_bpf_add_filter(obj
, op
="replace", prio
=1, handle
=1,
742 skip_sw
=(j
== 1), skip_hw
=(j
== 2),
744 fail(bool(ret
) != bool(j
),
745 "Software TC incorrect load in replace test, iteration %d" %
747 sim
.cls_filter_op(op
="delete", prio
=1, handle
=1, cls
="bpf")
749 start_test("Test spurious extack from the driver...")
750 test_spurios_extack(sim
, obj
, False, "netdevsim")
751 test_spurios_extack(sim
, obj
, True, "netdevsim")
753 sim
.set_ethtool_tc_offloads(False)
755 test_spurios_extack(sim
, obj
, False, "TC offload is disabled")
756 test_spurios_extack(sim
, obj
, True, "TC offload is disabled")
758 sim
.set_ethtool_tc_offloads(True)
760 sim
.tc_flush_filters()
762 start_test("Test TC offloads work...")
763 ret
, _
, err
= sim
.cls_bpf_add_filter(obj
, verbose
=True, skip_sw
=True,
764 fail
=False, include_stderr
=True)
765 fail(ret
!= 0, "TC filter did not load with TC offloads enabled")
766 check_verifier_log(err
, "[netdevsim] Hello from netdevsim!")
768 start_test("Test TC offload basics...")
769 dfs
= sim
.dfs_get_bound_progs(expected
=1)
770 progs
= bpftool_prog_list(expected
=1)
771 ingress
= sim
.tc_show_ingress(expected
=1)
776 fail(fltr
["skip_hw"], "TC does reports 'skip_hw' on offloaded filter")
777 fail(not fltr
["in_hw"], "TC does not report 'in_hw' for offloaded filter")
778 fail(not fltr
["skip_sw"], "TC does not report 'skip_sw' back")
780 start_test("Test TC offload is device-bound...")
781 fail(str(prog
["id"]) != fltr
["id"], "Program IDs don't match")
782 fail(prog
["tag"] != fltr
["tag"], "Program tags don't match")
783 fail(fltr
["id"] != dprog
["id"], "Program IDs don't match")
784 fail(dprog
["state"] != "xlated", "Offloaded program state not translated")
785 fail(dprog
["loaded"] != "Y", "Offloaded program is not loaded")
787 start_test("Test disabling TC offloads is rejected while filters installed...")
788 ret
, _
= sim
.set_ethtool_tc_offloads(False, fail
=False)
789 fail(ret
== 0, "Driver should refuse to disable TC offloads with filters installed...")
791 start_test("Test qdisc removal frees things...")
792 sim
.tc_flush_filters()
793 sim
.tc_show_ingress(expected
=0)
795 start_test("Test disabling TC offloads is OK without filters...")
796 ret
, _
= sim
.set_ethtool_tc_offloads(False, fail
=False)
798 "Driver refused to disable TC offloads without filters installed...")
800 sim
.set_ethtool_tc_offloads(True)
802 start_test("Test destroying device gets rid of TC filters...")
803 sim
.cls_bpf_add_filter(obj
, skip_sw
=True)
805 bpftool_prog_list_wait(expected
=0)
808 sim
.set_ethtool_tc_offloads(True)
810 start_test("Test destroying device gets rid of XDP...")
811 sim
.set_xdp(obj
, "offload")
813 bpftool_prog_list_wait(expected
=0)
816 sim
.set_ethtool_tc_offloads(True)
818 start_test("Test XDP prog reporting...")
819 sim
.set_xdp(obj
, "drv")
820 ipl
= sim
.ip_link_show(xdp
=True)
821 progs
= bpftool_prog_list(expected
=1)
822 fail(ipl
["xdp"]["prog"]["id"] != progs
[0]["id"],
823 "Loaded program has wrong ID")
825 start_test("Test XDP prog replace without force...")
826 ret
, _
= sim
.set_xdp(obj
, "drv", fail
=False)
827 fail(ret
== 0, "Replaced XDP program without -force")
828 sim
.wait_for_flush(total
=1)
830 start_test("Test XDP prog replace with force...")
831 ret
, _
= sim
.set_xdp(obj
, "drv", force
=True, fail
=False)
832 fail(ret
!= 0, "Could not replace XDP program with -force")
833 bpftool_prog_list_wait(expected
=1)
834 ipl
= sim
.ip_link_show(xdp
=True)
835 progs
= bpftool_prog_list(expected
=1)
836 fail(ipl
["xdp"]["prog"]["id"] != progs
[0]["id"],
837 "Loaded program has wrong ID")
838 fail("dev" in progs
[0].keys(),
839 "Device parameters reported for non-offloaded program")
841 start_test("Test XDP prog replace with bad flags...")
842 ret
, _
, err
= sim
.set_xdp(obj
, "generic", force
=True,
843 fail
=False, include_stderr
=True)
844 fail(ret
== 0, "Replaced XDP program with a program in different mode")
845 fail(err
.count("File exists") != 1, "Replaced driver XDP with generic")
846 ret
, _
, err
= sim
.set_xdp(obj
, "", force
=True,
847 fail
=False, include_stderr
=True)
848 fail(ret
== 0, "Replaced XDP program with a program in different mode")
849 check_extack(err
, "program loaded with different flags.", args
)
851 start_test("Test XDP prog remove with bad flags...")
852 ret
, _
, err
= sim
.unset_xdp("", force
=True,
853 fail
=False, include_stderr
=True)
854 fail(ret
== 0, "Removed program with a bad mode")
855 check_extack(err
, "program loaded with different flags.", args
)
857 start_test("Test MTU restrictions...")
858 ret
, _
= sim
.set_mtu(9000, fail
=False)
860 "Driver should refuse to increase MTU to 9000 with XDP loaded...")
862 bpftool_prog_list_wait(expected
=0)
864 ret
, _
, err
= sim
.set_xdp(obj
, "drv", fail
=False, include_stderr
=True)
865 fail(ret
== 0, "Driver should refuse to load program with MTU of 9000...")
866 check_extack_nsim(err
, "MTU too large w/ XDP enabled.", args
)
870 start_test("Test non-offload XDP attaching to HW...")
871 bpftool_prog_load("sample_ret0.o", "/sys/fs/bpf/nooffload")
872 nooffload
= bpf_pinned("/sys/fs/bpf/nooffload")
873 ret
, _
, err
= sim
.set_xdp(nooffload
, "offload",
874 fail
=False, include_stderr
=True)
875 fail(ret
== 0, "attached non-offloaded XDP program to HW")
876 check_extack_nsim(err
, "xdpoffload of non-bound program.", args
)
877 rm("/sys/fs/bpf/nooffload")
879 start_test("Test offload XDP attaching to drv...")
880 bpftool_prog_load("sample_ret0.o", "/sys/fs/bpf/offload",
882 offload
= bpf_pinned("/sys/fs/bpf/offload")
883 ret
, _
, err
= sim
.set_xdp(offload
, "drv", fail
=False, include_stderr
=True)
884 fail(ret
== 0, "attached offloaded XDP program to drv")
885 check_extack(err
, "using device-bound program without HW_MODE flag is not supported.", args
)
886 rm("/sys/fs/bpf/offload")
889 start_test("Test XDP offload...")
890 _
, _
, err
= sim
.set_xdp(obj
, "offload", verbose
=True, include_stderr
=True)
891 ipl
= sim
.ip_link_show(xdp
=True)
892 link_xdp
= ipl
["xdp"]["prog"]
893 progs
= bpftool_prog_list(expected
=1)
895 fail(link_xdp
["id"] != prog
["id"], "Loaded program has wrong ID")
896 check_verifier_log(err
, "[netdevsim] Hello from netdevsim!")
898 start_test("Test XDP offload is device bound...")
899 dfs
= sim
.dfs_get_bound_progs(expected
=1)
902 fail(prog
["id"] != link_xdp
["id"], "Program IDs don't match")
903 fail(prog
["tag"] != link_xdp
["tag"], "Program tags don't match")
904 fail(str(link_xdp
["id"]) != dprog
["id"], "Program IDs don't match")
905 fail(dprog
["state"] != "xlated", "Offloaded program state not translated")
906 fail(dprog
["loaded"] != "Y", "Offloaded program is not loaded")
908 start_test("Test removing XDP program many times...")
909 sim
.unset_xdp("offload")
910 sim
.unset_xdp("offload")
915 bpftool_prog_list_wait(expected
=0)
917 start_test("Test attempt to use a program for a wrong device...")
919 sim2
.set_xdp(obj
, "offload")
920 pin_file
, pinned
= pin_prog("/sys/fs/bpf/tmp")
922 ret
, _
, err
= sim
.set_xdp(pinned
, "offload",
923 fail
=False, include_stderr
=True)
924 fail(ret
== 0, "Pinned program loaded for a different device accepted")
925 check_extack_nsim(err
, "program bound to different dev.", args
)
927 ret
, _
, err
= sim
.set_xdp(pinned
, "offload",
928 fail
=False, include_stderr
=True)
929 fail(ret
== 0, "Pinned program loaded for a removed device accepted")
930 check_extack_nsim(err
, "xdpoffload of non-bound program.", args
)
932 bpftool_prog_list_wait(expected
=0)
934 start_test("Test multi-attachment XDP - attach...")
935 sim
.set_xdp(obj
, "offload")
936 xdp
= sim
.ip_link_show(xdp
=True)["xdp"]
937 offloaded
= sim
.dfs_read("bpf_offloaded_id")
938 fail("prog" not in xdp
, "Base program not reported in single program mode")
939 fail(len(ipl
["xdp"]["attached"]) != 1,
940 "Wrong attached program count with one program")
943 two_xdps
= sim
.ip_link_show(xdp
=True)["xdp"]
944 offloaded2
= sim
.dfs_read("bpf_offloaded_id")
946 fail(two_xdps
["mode"] != 4, "Bad mode reported with multiple programs")
947 fail("prog" in two_xdps
, "Base program reported in multi program mode")
948 fail(xdp
["attached"][0] not in two_xdps
["attached"],
949 "Offload program not reported after driver activated")
950 fail(len(two_xdps
["attached"]) != 2,
951 "Wrong attached program count with two programs")
952 fail(two_xdps
["attached"][0]["prog"]["id"] ==
953 two_xdps
["attached"][1]["prog"]["id"],
954 "offloaded and drv programs have the same id")
955 fail(offloaded
!= offloaded2
,
956 "offload ID changed after loading driver program")
958 start_test("Test multi-attachment XDP - replace...")
959 ret
, _
, err
= sim
.set_xdp(obj
, "offload", fail
=False, include_stderr
=True)
960 fail(err
.count("busy") != 1, "Replaced one of programs without -force")
962 start_test("Test multi-attachment XDP - detach...")
963 ret
, _
, err
= sim
.unset_xdp("drv", force
=True,
964 fail
=False, include_stderr
=True)
965 fail(ret
== 0, "Removed program with a bad mode")
966 check_extack(err
, "program loaded with different flags.", args
)
968 sim
.unset_xdp("offload")
969 xdp
= sim
.ip_link_show(xdp
=True)["xdp"]
970 offloaded
= sim
.dfs_read("bpf_offloaded_id")
972 fail(xdp
["mode"] != 1, "Bad mode reported after multiple programs")
973 fail("prog" not in xdp
,
974 "Base program not reported after multi program mode")
975 fail(xdp
["attached"][0] not in two_xdps
["attached"],
976 "Offload program not reported after driver activated")
977 fail(len(ipl
["xdp"]["attached"]) != 1,
978 "Wrong attached program count with remaining programs")
979 fail(offloaded
!= "0", "offload ID reported with only driver program left")
981 start_test("Test multi-attachment XDP - device remove...")
982 sim
.set_xdp(obj
, "offload")
986 sim
.set_ethtool_tc_offloads(True)
988 start_test("Test mixing of TC and XDP...")
990 sim
.set_xdp(obj
, "offload")
991 ret
, _
, err
= sim
.cls_bpf_add_filter(obj
, skip_sw
=True,
992 fail
=False, include_stderr
=True)
993 fail(ret
== 0, "Loading TC when XDP active should fail")
994 check_extack_nsim(err
, "driver and netdev offload states mismatch.", args
)
995 sim
.unset_xdp("offload")
998 sim
.cls_bpf_add_filter(obj
, skip_sw
=True)
999 ret
, _
, err
= sim
.set_xdp(obj
, "offload", fail
=False, include_stderr
=True)
1000 fail(ret
== 0, "Loading XDP when TC active should fail")
1001 check_extack_nsim(err
, "TC program is already loaded.", args
)
1003 start_test("Test binding TC from pinned...")
1004 pin_file
, pinned
= pin_prog("/sys/fs/bpf/tmp")
1005 sim
.tc_flush_filters(bound
=1, total
=1)
1006 sim
.cls_bpf_add_filter(pinned
, da
=True, skip_sw
=True)
1007 sim
.tc_flush_filters(bound
=1, total
=1)
1009 start_test("Test binding XDP from pinned...")
1010 sim
.set_xdp(obj
, "offload")
1011 pin_file
, pinned
= pin_prog("/sys/fs/bpf/tmp2", idx
=1)
1013 sim
.set_xdp(pinned
, "offload", force
=True)
1014 sim
.unset_xdp("offload")
1015 sim
.set_xdp(pinned
, "offload", force
=True)
1016 sim
.unset_xdp("offload")
1018 start_test("Test offload of wrong type fails...")
1019 ret
, _
= sim
.cls_bpf_add_filter(pinned
, da
=True, skip_sw
=True, fail
=False)
1020 fail(ret
== 0, "Managed to attach XDP program to TC")
1022 start_test("Test asking for TC offload of two filters...")
1023 sim
.cls_bpf_add_filter(obj
, da
=True, skip_sw
=True)
1024 ret
, _
, err
= sim
.cls_bpf_add_filter(obj
, da
=True, skip_sw
=True,
1025 fail
=False, include_stderr
=True)
1026 fail(ret
== 0, "Managed to offload two TC filters at the same time")
1027 check_extack_nsim(err
, "driver and netdev offload states mismatch.", args
)
1029 sim
.tc_flush_filters(bound
=2, total
=2)
1031 start_test("Test if netdev removal waits for translation...")
1033 sim
.dfs
["bpf_bind_verifier_delay"] = delay_msec
1035 cmd_line
= "tc filter add dev %s ingress bpf %s da skip_sw" % \
1036 (sim
['ifname'], obj
)
1037 tc_proc
= cmd(cmd_line
, background
=True, fail
=False)
1038 # Wait for the verifier to start
1039 while sim
.dfs_num_bound_progs() <= 2:
1043 ret
, _
= cmd_result(tc_proc
, fail
=False)
1044 time_diff
= end
- start
1045 log("Time", "start:\t%s\nend:\t%s\ndiff:\t%s" % (start
, end
, time_diff
))
1047 fail(ret
== 0, "Managed to load TC filter on a unregistering device")
1048 delay_sec
= delay_msec
* 0.001
1049 fail(time_diff
< delay_sec
, "Removal process took %s, expected %s" %
1050 (time_diff
, delay_sec
))
1052 # Remove all pinned files and reinstantiate the netdev
1054 bpftool_prog_list_wait(expected
=0)
1057 map_obj
= bpf_obj("sample_map_ret0.o")
1058 start_test("Test loading program with maps...")
1059 sim
.set_xdp(map_obj
, "offload", JSON
=False) # map fixup msg breaks JSON
1061 start_test("Test bpftool bound info reporting (own ns)...")
1062 check_dev_info(False, "")
1064 start_test("Test bpftool bound info reporting (other ns)...")
1067 check_dev_info(True, "")
1069 start_test("Test bpftool bound info reporting (remote ns)...")
1070 check_dev_info(False, ns
)
1072 start_test("Test bpftool bound info reporting (back to own ns)...")
1074 check_dev_info(False, "")
1076 prog_file
, _
= pin_prog("/sys/fs/bpf/tmp_prog")
1077 map_file
, _
= pin_map("/sys/fs/bpf/tmp_map", idx
=1, expected
=2)
1080 start_test("Test bpftool bound info reporting (removed dev)...")
1081 check_dev_info_removed(prog_file
=prog_file
, map_file
=map_file
)
1083 # Remove all pinned files and reinstantiate the netdev
1085 bpftool_prog_list_wait(expected
=0)
1089 start_test("Test map update (no flags)...")
1090 sim
.set_xdp(map_obj
, "offload", JSON
=False) # map fixup msg breaks JSON
1091 maps
= bpftool_map_list(expected
=2)
1092 array
= maps
[0] if maps
[0]["type"] == "array" else maps
[1]
1093 htab
= maps
[0] if maps
[0]["type"] == "hash" else maps
[1]
1096 bpftool("map update id %d key %s value %s" %
1097 (m
["id"], int2str("I", i
), int2str("Q", i
* 3)))
1100 ret
, _
= bpftool("map update id %d key %s value %s" %
1101 (m
["id"], int2str("I", 3), int2str("Q", 3 * 3)),
1103 fail(ret
== 0, "added too many entries")
1105 start_test("Test map update (exists)...")
1108 bpftool("map update id %d key %s value %s exist" %
1109 (m
["id"], int2str("I", i
), int2str("Q", i
* 3)))
1112 ret
, err
= bpftool("map update id %d key %s value %s exist" %
1113 (m
["id"], int2str("I", 3), int2str("Q", 3 * 3)),
1115 fail(ret
== 0, "updated non-existing key")
1116 fail(err
["error"].find("No such file or directory") == -1,
1117 "expected ENOENT, error is '%s'" % (err
["error"]))
1119 start_test("Test map update (noexist)...")
1122 ret
, err
= bpftool("map update id %d key %s value %s noexist" %
1123 (m
["id"], int2str("I", i
), int2str("Q", i
* 3)),
1125 fail(ret
== 0, "updated existing key")
1126 fail(err
["error"].find("File exists") == -1,
1127 "expected EEXIST, error is '%s'" % (err
["error"]))
1129 start_test("Test map dump...")
1131 _
, entries
= bpftool("map dump id %d" % (m
["id"]))
1133 key
= str2int(entries
[i
]["key"])
1134 fail(key
!= i
, "expected key %d, got %d" % (key
, i
))
1135 val
= str2int(entries
[i
]["value"])
1136 fail(val
!= i
* 3, "expected value %d, got %d" % (val
, i
* 3))
1138 start_test("Test map getnext...")
1140 _
, entry
= bpftool("map getnext id %d" % (m
["id"]))
1141 key
= str2int(entry
["next_key"])
1142 fail(key
!= 0, "next key %d, expected %d" % (key
, 0))
1143 _
, entry
= bpftool("map getnext id %d key %s" %
1144 (m
["id"], int2str("I", 0)))
1145 key
= str2int(entry
["next_key"])
1146 fail(key
!= 1, "next key %d, expected %d" % (key
, 1))
1147 ret
, err
= bpftool("map getnext id %d key %s" %
1148 (m
["id"], int2str("I", 1)), fail
=False)
1149 fail(ret
== 0, "got next key past the end of map")
1150 fail(err
["error"].find("No such file or directory") == -1,
1151 "expected ENOENT, error is '%s'" % (err
["error"]))
1153 start_test("Test map delete (htab)...")
1155 bpftool("map delete id %d key %s" % (htab
["id"], int2str("I", i
)))
1157 start_test("Test map delete (array)...")
1159 ret
, err
= bpftool("map delete id %d key %s" %
1160 (htab
["id"], int2str("I", i
)), fail
=False)
1161 fail(ret
== 0, "removed entry from an array")
1162 fail(err
["error"].find("No such file or directory") == -1,
1163 "expected ENOENT, error is '%s'" % (err
["error"]))
1165 start_test("Test map remove...")
1166 sim
.unset_xdp("offload")
1167 bpftool_map_list_wait(expected
=0)
1171 sim
.set_xdp(map_obj
, "offload", JSON
=False) # map fixup msg breaks JSON
1173 bpftool_map_list_wait(expected
=0)
1175 start_test("Test map creation fail path...")
1177 sim
.dfs
["bpf_map_accept"] = "N"
1178 ret
, _
= sim
.set_xdp(map_obj
, "offload", JSON
=False, fail
=False)
1180 "netdevsim didn't refuse to create a map with offload disabled")
1184 start_test("Test multi-dev ASIC program reuse...")
1187 simB2
= NetdevSim(link
=simB1
)
1188 simB3
= NetdevSim(link
=simB1
)
1189 sims
= (simA
, simB1
, simB2
, simB3
)
1190 simB
= (simB1
, simB2
, simB3
)
1192 bpftool_prog_load("sample_map_ret0.o", "/sys/fs/bpf/nsimA",
1194 progA
= bpf_pinned("/sys/fs/bpf/nsimA")
1195 bpftool_prog_load("sample_map_ret0.o", "/sys/fs/bpf/nsimB",
1196 dev
=simB1
['ifname'])
1197 progB
= bpf_pinned("/sys/fs/bpf/nsimB")
1199 simA
.set_xdp(progA
, "offload", JSON
=False)
1201 d
.set_xdp(progB
, "offload", JSON
=False)
1203 start_test("Test multi-dev ASIC cross-dev replace...")
1204 ret
, _
= simA
.set_xdp(progB
, "offload", force
=True, JSON
=False, fail
=False)
1205 fail(ret
== 0, "cross-ASIC program allowed")
1207 ret
, _
= d
.set_xdp(progA
, "offload", force
=True, JSON
=False, fail
=False)
1208 fail(ret
== 0, "cross-ASIC program allowed")
1210 start_test("Test multi-dev ASIC cross-dev install...")
1212 d
.unset_xdp("offload")
1214 ret
, _
, err
= simA
.set_xdp(progB
, "offload", force
=True, JSON
=False,
1215 fail
=False, include_stderr
=True)
1216 fail(ret
== 0, "cross-ASIC program allowed")
1217 check_extack_nsim(err
, "program bound to different dev.", args
)
1219 ret
, _
, err
= d
.set_xdp(progA
, "offload", force
=True, JSON
=False,
1220 fail
=False, include_stderr
=True)
1221 fail(ret
== 0, "cross-ASIC program allowed")
1222 check_extack_nsim(err
, "program bound to different dev.", args
)
1224 start_test("Test multi-dev ASIC cross-dev map reuse...")
1226 mapA
= bpftool("prog show %s" % (progA
))[1]["map_ids"][0]
1227 mapB
= bpftool("prog show %s" % (progB
))[1]["map_ids"][0]
1229 ret
, _
= bpftool_prog_load("sample_map_ret0.o", "/sys/fs/bpf/nsimB_",
1230 dev
=simB3
['ifname'],
1231 maps
=["idx 0 id %d" % (mapB
)],
1233 fail(ret
!= 0, "couldn't reuse a map on the same ASIC")
1234 rm("/sys/fs/bpf/nsimB_")
1236 ret
, _
, err
= bpftool_prog_load("sample_map_ret0.o", "/sys/fs/bpf/nsimA_",
1238 maps
=["idx 0 id %d" % (mapB
)],
1239 fail
=False, include_stderr
=True)
1240 fail(ret
== 0, "could reuse a map on a different ASIC")
1241 fail(err
.count("offload device mismatch between prog and map") == 0,
1242 "error message missing for cross-ASIC map")
1244 ret
, _
, err
= bpftool_prog_load("sample_map_ret0.o", "/sys/fs/bpf/nsimB_",
1245 dev
=simB1
['ifname'],
1246 maps
=["idx 0 id %d" % (mapA
)],
1247 fail
=False, include_stderr
=True)
1248 fail(ret
== 0, "could reuse a map on a different ASIC")
1249 fail(err
.count("offload device mismatch between prog and map") == 0,
1250 "error message missing for cross-ASIC map")
1252 start_test("Test multi-dev ASIC cross-dev destruction...")
1253 bpftool_prog_list_wait(expected
=2)
1256 bpftool_prog_list_wait(expected
=1)
1258 ifnameB
= bpftool("prog show %s" % (progB
))[1]["dev"]["ifname"]
1259 fail(ifnameB
!= simB1
['ifname'], "program not bound to originial device")
1261 bpftool_prog_list_wait(expected
=1)
1263 start_test("Test multi-dev ASIC cross-dev destruction - move...")
1264 ifnameB
= bpftool("prog show %s" % (progB
))[1]["dev"]["ifname"]
1265 fail(ifnameB
not in (simB2
['ifname'], simB3
['ifname']),
1266 "program not bound to remaining devices")
1269 ifnameB
= bpftool("prog show %s" % (progB
))[1]["dev"]["ifname"]
1270 fail(ifnameB
!= simB3
['ifname'], "program not bound to remaining device")
1273 bpftool_prog_list_wait(expected
=0)
1275 start_test("Test multi-dev ASIC cross-dev destruction - orphaned...")
1276 ret
, out
= bpftool("prog show %s" % (progB
), fail
=False)
1277 fail(ret
== 0, "got information about orphaned program")
1278 fail("error" not in out
, "no error reported for get info on orphaned")
1279 fail(out
["error"] != "can't get prog info: No such device",
1280 "wrong error for get info on orphaned")
1282 print("%s: OK" % (os
.path
.basename(__file__
)))
1285 log("Clean up...", "", level
=1)