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):
162 return tool("bpftool", args
, {"json":"-p"}, JSON
=JSON
, ns
=ns
, fail
=fail
)
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
))
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
))
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
:
186 raise Exception("Time out waiting for program counts to stabilize want %d, have %d" % (expected
, nprogs
))
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
:
194 raise Exception("Time out waiting for map counts to stabilize want %d, have %d" % (expected
, nmaps
))
196 def ip(args
, force
=False, JSON
=True, ns
="", fail
=True, include_stderr
=False):
198 args
= "-force " + args
199 return tool("ip", args
, {"json":"-j"}, JSON
=JSON
, ns
=ns
,
200 fail
=fail
, include_stderr
=include_stderr
)
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
)
206 def ethtool(dev
, opt
, args
, fail
=True):
207 return cmd("ethtool %s %s %s" % (opt
, dev
["ifname"], args
), fail
=fail
)
209 def bpf_obj(name
, sec
=".text", path
=bpf_test_dir
,):
210 return "obj %s sec %s" % (os
.path
.join(path
, name
), sec
)
212 def bpf_pinned(name
):
213 return "pinned %s" % (name
)
215 def bpf_bytecode(bytecode
):
216 return "bytecode \"%s\"" % (bytecode
)
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)
227 def int2str(fmt
, val
):
229 for b
in struct
.pack(fmt
, val
):
231 return " ".join(map(lambda x
: str(x
), ret
))
236 inttab
.append(int(i
, 16))
237 ba
= bytearray(inttab
)
240 elif len(strtab
) == 8:
243 raise Exception("String array of len %d can't be unpacked to an int" %
245 return struct
.unpack(fmt
, ba
)[0]
249 Class for accessing DebugFS directories as a dictionary.
252 def __init__(self
, path
):
254 self
._dict
= self
._debugfs
_dir
_read
(path
)
257 return len(self
._dict
.keys())
259 def __getitem__(self
, key
):
261 key
= list(self
._dict
.keys())[key
]
262 return self
._dict
[key
]
264 def __setitem__(self
, key
, value
):
265 log("DebugFS set %s = %s" % (key
, value
), "")
268 cmd("echo '%s' > %s/%s" % (value
, self
.path
, key
))
271 _
, out
= cmd('cat %s/%s' % (self
.path
, key
))
272 self
._dict
[key
] = out
.strip()
274 def _debugfs_dir_read(self
, path
):
277 log("DebugFS state for %s" % (path
), "")
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
))
286 elif os
.path
.isdir(p
):
287 dfs
[f
] = DebugfsDir(p
)
289 raise Exception("%s is neither file nor directory" % (p
))
292 log("DebugFS state", dfs
)
299 Class for netdevsim netdevice and its attributes.
303 self
.dev
= self
._netdevsim
_create
()
308 self
.dfs_dir
= '/sys/kernel/debug/netdevsim/%s' % (self
.dev
['ifname'])
311 def __getitem__(self
, key
):
314 def _netdevsim_create(self
):
315 _
, old
= ip("link show")
316 ip("link add sim%d type netdevsim")
317 _
, new
= ip("link show")
320 f
= filter(lambda x
: x
["ifname"] == dev
["ifname"], old
)
321 if len(list(f
)) == 0:
324 raise Exception("failed to create netdevsim device")
328 ip("link del dev %s" % (self
.dev
["ifname"]), ns
=self
.ns
)
330 def dfs_refresh(self
):
331 self
.dfs
= DebugfsDir(self
.dfs_dir
)
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())
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
))
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
:
354 raise Exception("Time out waiting for program counts to stabilize want %d/%d, have %d bound, %d loaded" % (bound
, total
, nbound
, nprogs
))
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
)
361 def set_mtu(self
, mtu
, fail
=True):
362 return ip("link set dev %s mtu %d" % (self
.dev
["ifname"], mtu
),
365 def set_xdp(self
, bpf
, mode
, force
=False, JSON
=True, verbose
=False,
366 fail
=True, include_stderr
=False):
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
)
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
)
379 def ip_link_show(self
, xdp
):
380 _
, link
= ip("link show dev %s" % (self
['ifname']))
382 raise Exception("Multiple objects on ip link show")
385 fail(xdp
!= "xdp" in link
,
386 "XDP program not reporting in iplink (reported %s, expected %s)" %
387 ("xdp" in link
, xdp
))
390 def tc_add_ingress(self
):
391 tc("qdisc add dev %s ingress" % (self
['ifname']))
393 def tc_del_ingress(self
):
394 tc("qdisc del dev %s ingress" % (self
['ifname']))
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
)
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"]
406 args
= "-s filter show dev %s ingress" % (self
['ifname'])
407 _
, out
= tc(args
, JSON
=False)
410 lines
= out
.split('\n')
413 if "handle" not in words
:
417 fltr
[flag
] = flag
in words
420 idx
= words
.index(name
)
421 fltr
[name
] = words
[idx
+ 1]
426 if expected
is not None:
427 fail(len(filters
) != expected
,
428 "%d ingress filters loaded, expected %d" %
429 (len(filters
), expected
))
432 def cls_filter_op(self
, op
, qdisc
="ingress", prio
=None, handle
=None,
433 chain
=None, cls
="", params
="",
434 fail
=True, include_stderr
=False):
437 spec
+= " prio %d" % (prio
)
439 spec
+= " handle %s" % (handle
)
440 if chain
is not None:
441 spec
+= " chain %d" % (chain
)
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
)
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):
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
)
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
)
472 ################################################################################
474 global files
, netns
, devs
479 cmd("rm -f %s" % (f
))
481 cmd("ip netns delete %s" % (ns
))
485 def pin_prog(file_name
, idx
=0):
486 progs
= bpftool_prog_list(expected
=(idx
+ 1))
488 bpftool("prog pin id %d %s" % (prog
["id"], file_name
))
489 files
.append(file_name
)
491 return file_name
, bpf_pinned(file_name
)
493 def pin_map(file_name
, idx
=0, expected
=1):
494 maps
= bpftool_map_list(expected
=expected
)
496 bpftool("map pin id %d %s" % (m
["id"], file_name
))
497 files
.append(file_name
)
499 return file_name
, bpf_pinned(file_name
)
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" %
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" %
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
)
520 fail("dev" not in prog
.keys(), "Device parameters not reported")
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")
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"]))
531 fail("ifname" in dev
.keys(), "Ifname is reported for other ns")
533 maps
= bpftool_map_list(expected
=2, ns
=ns
)
535 fail("dev" not in m
.keys(), "Device parameters not reported")
536 fail(dev
!= m
["dev"], "Map's device different than program's")
538 def check_extack(output
, reference
, args
):
541 lines
= output
.split("\n")
542 comp
= len(lines
) >= 2 and lines
[1] == reference
543 fail(not comp
, "Missing or incorrect netlink extack message")
545 def check_extack_nsim(output
, reference
, args
):
546 check_extack(output
, "Error: netdevsim: " + reference
, args
)
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
))
552 def check_verifier_log(output
, reference
):
553 lines
= output
.split("\n")
554 for l
in reversed(lines
):
557 fail(True, "Missing or incorrect message from netdevsim in verifier log")
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
,
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",
568 check_no_extack(res
, needle
)
572 parser
= argparse
.ArgumentParser()
573 parser
.add_argument("--log", help="output verbose log to given file")
574 args
= parser
.parse_args()
576 logfile
= open(args
.log
, 'w+')
577 logfile
.write("# -*-Org-*-")
579 log("Prepare...", "", level
=1)
583 skip(os
.getuid() != 0, "test must be run as root")
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")
592 ret
, out
= cmd("modprobe netdevsim", fail
=False)
593 skip(ret
!= 0, "netdevsim module could not be loaded")
596 _
, out
= cmd("mount")
597 if out
.find("/sys/kernel/debug type debugfs") == -1:
598 cmd("mount -t debugfs none /sys/kernel/debug")
600 # Check samples are compiled
601 samples
= ["sample_ret0.o", "sample_map_ret0.o"]
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" %
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?", "")
615 # Check if net namespaces seem to work
617 skip(ns
is None, "Could not create a net namespace")
618 cmd("ip netns delete %s" % (ns
))
622 obj
= bpf_obj("sample_ret0.o")
623 bytecode
= bpf_bytecode("1,6 0 0 4294967295,")
625 start_test("Test destruction of generic XDP...")
627 sim
.set_xdp(obj
, "generic")
629 bpftool_prog_list_wait(expected
=0)
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")
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)
643 sim
.tc_flush_filters()
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
)
652 sim
.set_ethtool_tc_offloads(True)
653 sim
.dfs
["bpf_tc_non_bound_accept"] = "Y"
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)
661 fail(not fltr
["in_hw"], "Filter not offloaded by default")
663 sim
.tc_flush_filters()
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)
671 fail(not fltr
["in_hw"], "Bytecode not offloaded by default")
673 sim
.tc_flush_filters()
674 sim
.dfs
["bpf_tc_non_bound_accept"] = "N"
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.",
684 start_test("Test non-0 chain offload...")
685 ret
, _
, err
= sim
.cls_bpf_add_filter(obj
, chain
=1, prio
=1, handle
=1,
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()
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")
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")
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")
705 start_test("Test TC replace bad flags...")
708 ret
, _
= sim
.cls_bpf_add_filter(obj
, op
="replace", prio
=1, handle
=1,
709 skip_sw
=(j
== 1), skip_hw
=(j
== 2),
711 fail(bool(ret
) != bool(j
),
712 "Software TC incorrect load in replace test, iteration %d" %
714 sim
.cls_filter_op(op
="delete", prio
=1, handle
=1, cls
="bpf")
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")
720 sim
.set_ethtool_tc_offloads(False)
722 test_spurios_extack(sim
, obj
, False, "TC offload is disabled")
723 test_spurios_extack(sim
, obj
, True, "TC offload is disabled")
725 sim
.set_ethtool_tc_offloads(True)
727 sim
.tc_flush_filters()
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!")
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)
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")
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")
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...")
758 start_test("Test qdisc removal frees things...")
759 sim
.tc_flush_filters()
760 sim
.tc_show_ingress(expected
=0)
762 start_test("Test disabling TC offloads is OK without filters...")
763 ret
, _
= sim
.set_ethtool_tc_offloads(False, fail
=False)
765 "Driver refused to disable TC offloads without filters installed...")
767 sim
.set_ethtool_tc_offloads(True)
769 start_test("Test destroying device gets rid of TC filters...")
770 sim
.cls_bpf_add_filter(obj
, skip_sw
=True)
772 bpftool_prog_list_wait(expected
=0)
775 sim
.set_ethtool_tc_offloads(True)
777 start_test("Test destroying device gets rid of XDP...")
778 sim
.set_xdp(obj
, "offload")
780 bpftool_prog_list_wait(expected
=0)
783 sim
.set_ethtool_tc_offloads(True)
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")
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)
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")
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
)
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
)
828 start_test("Test MTU restrictions...")
829 ret
, _
= sim
.set_mtu(9000, fail
=False)
831 "Driver should refuse to increase MTU to 9000 with XDP loaded...")
833 bpftool_prog_list_wait(expected
=0)
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
)
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)
847 fail(link_xdp
["id"] != prog
["id"], "Loaded program has wrong ID")
848 check_verifier_log(err
, "[netdevsim] Hello from netdevsim!")
850 start_test("Test XDP offload is device bound...")
851 dfs
= sim
.dfs_get_bound_progs(expected
=1)
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")
860 start_test("Test removing XDP program many times...")
861 sim
.unset_xdp("offload")
862 sim
.unset_xdp("offload")
867 bpftool_prog_list_wait(expected
=0)
869 start_test("Test attempt to use a program for a wrong device...")
871 sim2
.set_xdp(obj
, "offload")
872 pin_file
, pinned
= pin_prog("/sys/fs/bpf/tmp")
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
)
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
)
884 bpftool_prog_list_wait(expected
=0)
886 start_test("Test mixing of TC and XDP...")
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")
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
)
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)
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)
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")
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")
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
)
927 sim
.tc_flush_filters(bound
=2, total
=2)
929 start_test("Test if netdev removal waits for translation...")
931 sim
.dfs
["bpf_bind_verifier_delay"] = delay_msec
933 cmd_line
= "tc filter add dev %s ingress bpf %s da skip_sw" % \
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:
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
))
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
))
950 # Remove all pinned files and reinstantiate the netdev
952 bpftool_prog_list_wait(expected
=0)
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
959 start_test("Test bpftool bound info reporting (own ns)...")
960 check_dev_info(False, "")
962 start_test("Test bpftool bound info reporting (other ns)...")
965 check_dev_info(True, "")
967 start_test("Test bpftool bound info reporting (remote ns)...")
968 check_dev_info(False, ns
)
970 start_test("Test bpftool bound info reporting (back to own ns)...")
972 check_dev_info(False, "")
974 prog_file
, _
= pin_prog("/sys/fs/bpf/tmp_prog")
975 map_file
, _
= pin_map("/sys/fs/bpf/tmp_map", idx
=1, expected
=2)
978 start_test("Test bpftool bound info reporting (removed dev)...")
979 check_dev_info_removed(prog_file
=prog_file
, map_file
=map_file
)
981 # Remove all pinned files and reinstantiate the netdev
983 bpftool_prog_list_wait(expected
=0)
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]
994 bpftool("map update id %d key %s value %s" %
995 (m
["id"], int2str("I", i
), int2str("Q", i
* 3)))
998 ret
, _
= bpftool("map update id %d key %s value %s" %
999 (m
["id"], int2str("I", 3), int2str("Q", 3 * 3)),
1001 fail(ret
== 0, "added too many entries")
1003 start_test("Test map update (exists)...")
1006 bpftool("map update id %d key %s value %s exist" %
1007 (m
["id"], int2str("I", i
), int2str("Q", i
* 3)))
1010 ret
, err
= bpftool("map update id %d key %s value %s exist" %
1011 (m
["id"], int2str("I", 3), int2str("Q", 3 * 3)),
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"]))
1017 start_test("Test map update (noexist)...")
1020 ret
, err
= bpftool("map update id %d key %s value %s noexist" %
1021 (m
["id"], int2str("I", i
), int2str("Q", i
* 3)),
1023 fail(ret
== 0, "updated existing key")
1024 fail(err
["error"].find("File exists") == -1,
1025 "expected EEXIST, error is '%s'" % (err
["error"]))
1027 start_test("Test map dump...")
1029 _
, entries
= bpftool("map dump id %d" % (m
["id"]))
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))
1036 start_test("Test map getnext...")
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"]))
1051 start_test("Test map delete (htab)...")
1053 bpftool("map delete id %d key %s" % (htab
["id"], int2str("I", i
)))
1055 start_test("Test map delete (array)...")
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"]))
1063 start_test("Test map remove...")
1064 sim
.unset_xdp("offload")
1065 bpftool_map_list_wait(expected
=0)
1069 sim
.set_xdp(map_obj
, "offload", JSON
=False) # map fixup msg breaks JSON
1071 bpftool_map_list_wait(expected
=0)
1073 start_test("Test map creation fail path...")
1075 sim
.dfs
["bpf_map_accept"] = "N"
1076 ret
, _
= sim
.set_xdp(map_obj
, "offload", JSON
=False, fail
=False)
1078 "netdevsim didn't refuse to create a map with offload disabled")
1080 print("%s: OK" % (os
.path
.basename(__file__
)))
1083 log("Clean up...", "", level
=1)