blob: e3c750f17cb83b0452a22925fe6a0b1bc7d8cedc [file] [log] [blame]
Jakub Kicinski417ec262017-12-01 15:09:00 -08001#!/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
Jakub Kicinski752d7b42017-12-27 18:39:11 -080021import random
22import string
Jakub Kicinski417ec262017-12-01 15:09:00 -080023import 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
Jakub Kicinski752d7b42017-12-27 18:39:11 -080032netns = [] # net namespaces to be removed
Jakub Kicinski417ec262017-12-01 15:09:00 -080033
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
Jakub Kicinski752d7b42017-12-27 18:39:11 -0800134def tool(name, args, flags, JSON=True, ns="", fail=True):
Jakub Kicinski417ec262017-12-01 15:09:00 -0800135 params = ""
136 if JSON:
137 params += "%s " % (flags["json"])
138
Jakub Kicinski752d7b42017-12-27 18:39:11 -0800139 if ns != "":
140 ns = "ip netns exec %s " % (ns)
141
142 ret, out = cmd(ns + name + " " + params + args, fail=fail)
Jakub Kicinski417ec262017-12-01 15:09:00 -0800143 if JSON and len(out.strip()) != 0:
144 return ret, json.loads(out)
145 else:
146 return ret, out
147
Jakub Kicinski752d7b42017-12-27 18:39:11 -0800148def bpftool(args, JSON=True, ns="", fail=True):
149 return tool("bpftool", args, {"json":"-p"}, JSON=JSON, ns=ns, fail=fail)
Jakub Kicinski417ec262017-12-01 15:09:00 -0800150
Jakub Kicinski752d7b42017-12-27 18:39:11 -0800151def bpftool_prog_list(expected=None, ns=""):
152 _, progs = bpftool("prog show", JSON=True, ns=ns, fail=True)
Jakub Kicinski417ec262017-12-01 15:09:00 -0800153 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
Jakub Kicinski752d7b42017-12-27 18:39:11 -0800167def ip(args, force=False, JSON=True, ns="", fail=True):
Jakub Kicinski417ec262017-12-01 15:09:00 -0800168 if force:
169 args = "-force " + args
Jakub Kicinski752d7b42017-12-27 18:39:11 -0800170 return tool("ip", args, {"json":"-j"}, JSON=JSON, ns=ns, fail=fail)
Jakub Kicinski417ec262017-12-01 15:09:00 -0800171
Jakub Kicinski752d7b42017-12-27 18:39:11 -0800172def tc(args, JSON=True, ns="", fail=True):
173 return tool("tc", args, {"json":"-p"}, JSON=JSON, ns=ns, fail=fail)
Jakub Kicinski417ec262017-12-01 15:09:00 -0800174
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
Jakub Kicinski752d7b42017-12-27 18:39:11 -0800187def 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
Jakub Kicinski417ec262017-12-01 15:09:00 -0800196class 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
Jakub Kicinski752d7b42017-12-27 18:39:11 -0800255 self.ns = ""
256
Jakub Kicinski417ec262017-12-01 15:09:00 -0800257 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)
Jakub Kicinski752d7b42017-12-27 18:39:11 -0800277 ip("link del dev %s" % (self.dev["ifname"]), ns=self.ns)
Jakub Kicinski417ec262017-12-01 15:09:00 -0800278
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
Jakub Kicinski752d7b42017-12-27 18:39:11 -0800305 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
Jakub Kicinski417ec262017-12-01 15:09:00 -0800310 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))
Jakub Kicinski752d7b42017-12-27 18:39:11 -0800397 for ns in netns:
398 cmd("ip netns delete %s" % (ns))
Jakub Kicinski417ec262017-12-01 15:09:00 -0800399
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
Jakub Kicinski752d7b42017-12-27 18:39:11 -0800408def 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
Jakub Kicinski417ec262017-12-01 15:09:00 -0800437# 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
Jakub Kicinski752d7b42017-12-27 18:39:11 -0800473# 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
Jakub Kicinski417ec262017-12-01 15:09:00 -0800479try:
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")
Jakub Kicinski752d7b42017-12-27 18:39:11 -0800611 fail("dev" in progs[0].keys(),
612 "Device parameters reported for non-offloaded program")
Jakub Kicinski417ec262017-12-01 15:09:00 -0800613
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)
David S. Millerfba961a2017-12-22 11:16:31 -0500711 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")
Jakub Kicinski417ec262017-12-01 15:09:00 -0800713
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
Jakub Kicinski752d7b42017-12-27 18:39:11 -0800737 # 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
Jakub Kicinski417ec262017-12-01 15:09:00 -0800766 print("%s: OK" % (os.path.basename(__file__)))
767
768finally:
769 log("Clean up...", "", level=1)
770 log_level_inc()
771 clean_up()