blob: 8dca4dc6c193f3051de9b403bdbc4428ac3c72a3 [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 Kicinski7fedbb72018-01-17 19:13:31 -080023import struct
Jakub Kicinski417ec262017-12-01 15:09:00 -080024import subprocess
25import time
26
27logfile = None
28log_level = 1
Quentin Monnetcaf95222018-01-23 11:22:53 -080029skip_extack = False
Jakub Kicinski417ec262017-12-01 15:09:00 -080030bpf_test_dir = os.path.dirname(os.path.realpath(__file__))
31pp = pprint.PrettyPrinter()
32devs = [] # devices we created for clean up
33files = [] # files to be removed
Jakub Kicinski752d7b42017-12-27 18:39:11 -080034netns = [] # net namespaces to be removed
Jakub Kicinski417ec262017-12-01 15:09:00 -080035
36def log_get_sec(level=0):
37 return "*" * (log_level + level)
38
39def log_level_inc(add=1):
40 global log_level
41 log_level += add
42
43def log_level_dec(sub=1):
44 global log_level
45 log_level -= sub
46
47def log_level_set(level):
48 global log_level
49 log_level = level
50
51def log(header, data, level=None):
52 """
53 Output to an optional log.
54 """
55 if logfile is None:
56 return
57 if level is not None:
58 log_level_set(level)
59
60 if not isinstance(data, str):
61 data = pp.pformat(data)
62
63 if len(header):
64 logfile.write("\n" + log_get_sec() + " ")
65 logfile.write(header)
66 if len(header) and len(data.strip()):
67 logfile.write("\n")
68 logfile.write(data)
69
70def skip(cond, msg):
71 if not cond:
72 return
73 print("SKIP: " + msg)
74 log("SKIP: " + msg, "", level=1)
75 os.sys.exit(0)
76
77def fail(cond, msg):
78 if not cond:
79 return
80 print("FAIL: " + msg)
81 log("FAIL: " + msg, "", level=1)
82 os.sys.exit(1)
83
84def start_test(msg):
85 log(msg, "", level=1)
86 log_level_inc()
87 print(msg)
88
89def cmd(cmd, shell=True, include_stderr=False, background=False, fail=True):
90 """
91 Run a command in subprocess and return tuple of (retval, stdout);
92 optionally return stderr as well as third value.
93 """
94 proc = subprocess.Popen(cmd, shell=shell, stdout=subprocess.PIPE,
95 stderr=subprocess.PIPE)
96 if background:
97 msg = "%s START: %s" % (log_get_sec(1),
98 datetime.now().strftime("%H:%M:%S.%f"))
99 log("BKG " + proc.args, msg)
100 return proc
101
102 return cmd_result(proc, include_stderr=include_stderr, fail=fail)
103
104def cmd_result(proc, include_stderr=False, fail=False):
105 stdout, stderr = proc.communicate()
106 stdout = stdout.decode("utf-8")
107 stderr = stderr.decode("utf-8")
108 proc.stdout.close()
109 proc.stderr.close()
110
111 stderr = "\n" + stderr
112 if stderr[-1] == "\n":
113 stderr = stderr[:-1]
114
115 sec = log_get_sec(1)
116 log("CMD " + proc.args,
117 "RETCODE: %d\n%s STDOUT:\n%s%s STDERR:%s\n%s END: %s" %
118 (proc.returncode, sec, stdout, sec, stderr,
119 sec, datetime.now().strftime("%H:%M:%S.%f")))
120
121 if proc.returncode != 0 and fail:
122 if len(stderr) > 0 and stderr[-1] == "\n":
123 stderr = stderr[:-1]
124 raise Exception("Command failed: %s\n%s" % (proc.args, stderr))
125
126 if include_stderr:
127 return proc.returncode, stdout, stderr
128 else:
129 return proc.returncode, stdout
130
131def rm(f):
132 cmd("rm -f %s" % (f))
133 if f in files:
134 files.remove(f)
135
Quentin Monnetcaf95222018-01-23 11:22:53 -0800136def tool(name, args, flags, JSON=True, ns="", fail=True, include_stderr=False):
Jakub Kicinski417ec262017-12-01 15:09:00 -0800137 params = ""
138 if JSON:
139 params += "%s " % (flags["json"])
140
Jakub Kicinski752d7b42017-12-27 18:39:11 -0800141 if ns != "":
142 ns = "ip netns exec %s " % (ns)
143
Quentin Monnetcaf95222018-01-23 11:22:53 -0800144 if include_stderr:
145 ret, stdout, stderr = cmd(ns + name + " " + params + args,
146 fail=fail, include_stderr=True)
147 else:
148 ret, stdout = cmd(ns + name + " " + params + args,
149 fail=fail, include_stderr=False)
150
151 if JSON and len(stdout.strip()) != 0:
152 out = json.loads(stdout)
153 else:
154 out = stdout
155
156 if include_stderr:
157 return ret, out, stderr
Jakub Kicinski417ec262017-12-01 15:09:00 -0800158 else:
159 return ret, out
160
Jakub Kicinski752d7b42017-12-27 18:39:11 -0800161def bpftool(args, JSON=True, ns="", fail=True):
162 return tool("bpftool", args, {"json":"-p"}, JSON=JSON, ns=ns, fail=fail)
Jakub Kicinski417ec262017-12-01 15:09:00 -0800163
Jakub Kicinski752d7b42017-12-27 18:39:11 -0800164def bpftool_prog_list(expected=None, ns=""):
165 _, progs = bpftool("prog show", JSON=True, ns=ns, fail=True)
Jakub Kicinski417ec262017-12-01 15:09:00 -0800166 if expected is not None:
167 if len(progs) != expected:
168 fail(True, "%d BPF programs loaded, expected %d" %
169 (len(progs), expected))
170 return progs
171
Jakub Kicinski7fedbb72018-01-17 19:13:31 -0800172def bpftool_map_list(expected=None, ns=""):
173 _, maps = bpftool("map show", JSON=True, ns=ns, fail=True)
174 if expected is not None:
175 if len(maps) != expected:
176 fail(True, "%d BPF maps loaded, expected %d" %
177 (len(maps), expected))
178 return maps
179
Jakub Kicinski417ec262017-12-01 15:09:00 -0800180def bpftool_prog_list_wait(expected=0, n_retry=20):
181 for i in range(n_retry):
182 nprogs = len(bpftool_prog_list())
183 if nprogs == expected:
184 return
185 time.sleep(0.05)
186 raise Exception("Time out waiting for program counts to stabilize want %d, have %d" % (expected, nprogs))
187
Jakub Kicinski7fedbb72018-01-17 19:13:31 -0800188def bpftool_map_list_wait(expected=0, n_retry=20):
189 for i in range(n_retry):
190 nmaps = len(bpftool_map_list())
191 if nmaps == expected:
192 return
193 time.sleep(0.05)
194 raise Exception("Time out waiting for map counts to stabilize want %d, have %d" % (expected, nmaps))
195
Quentin Monnetcaf95222018-01-23 11:22:53 -0800196def ip(args, force=False, JSON=True, ns="", fail=True, include_stderr=False):
Jakub Kicinski417ec262017-12-01 15:09:00 -0800197 if force:
198 args = "-force " + args
Quentin Monnetcaf95222018-01-23 11:22:53 -0800199 return tool("ip", args, {"json":"-j"}, JSON=JSON, ns=ns,
200 fail=fail, include_stderr=include_stderr)
Jakub Kicinski417ec262017-12-01 15:09:00 -0800201
Quentin Monnetcaf95222018-01-23 11:22:53 -0800202def 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)
Jakub Kicinski417ec262017-12-01 15:09:00 -0800205
206def ethtool(dev, opt, args, fail=True):
207 return cmd("ethtool %s %s %s" % (opt, dev["ifname"], args), fail=fail)
208
209def bpf_obj(name, sec=".text", path=bpf_test_dir,):
210 return "obj %s sec %s" % (os.path.join(path, name), sec)
211
212def bpf_pinned(name):
213 return "pinned %s" % (name)
214
215def bpf_bytecode(bytecode):
216 return "bytecode \"%s\"" % (bytecode)
217
Jakub Kicinski752d7b42017-12-27 18:39:11 -0800218def mknetns(n_retry=10):
219 for i in range(n_retry):
220 name = ''.join([random.choice(string.ascii_letters) for i in range(8)])
221 ret, _ = ip("netns add %s" % (name), fail=False)
222 if ret == 0:
223 netns.append(name)
224 return name
225 return None
226
Jakub Kicinski7fedbb72018-01-17 19:13:31 -0800227def int2str(fmt, val):
228 ret = []
229 for b in struct.pack(fmt, val):
230 ret.append(int(b))
231 return " ".join(map(lambda x: str(x), ret))
232
233def str2int(strtab):
234 inttab = []
235 for i in strtab:
236 inttab.append(int(i, 16))
237 ba = bytearray(inttab)
238 if len(strtab) == 4:
239 fmt = "I"
240 elif len(strtab) == 8:
241 fmt = "Q"
242 else:
243 raise Exception("String array of len %d can't be unpacked to an int" %
244 (len(strtab)))
245 return struct.unpack(fmt, ba)[0]
246
Jakub Kicinski417ec262017-12-01 15:09:00 -0800247class DebugfsDir:
248 """
249 Class for accessing DebugFS directories as a dictionary.
250 """
251
252 def __init__(self, path):
253 self.path = path
254 self._dict = self._debugfs_dir_read(path)
255
256 def __len__(self):
257 return len(self._dict.keys())
258
259 def __getitem__(self, key):
260 if type(key) is int:
261 key = list(self._dict.keys())[key]
262 return self._dict[key]
263
264 def __setitem__(self, key, value):
265 log("DebugFS set %s = %s" % (key, value), "")
266 log_level_inc()
267
268 cmd("echo '%s' > %s/%s" % (value, self.path, key))
269 log_level_dec()
270
271 _, out = cmd('cat %s/%s' % (self.path, key))
272 self._dict[key] = out.strip()
273
274 def _debugfs_dir_read(self, path):
275 dfs = {}
276
277 log("DebugFS state for %s" % (path), "")
278 log_level_inc(add=2)
279
280 _, out = cmd('ls ' + path)
281 for f in out.split():
282 p = os.path.join(path, f)
283 if os.path.isfile(p):
284 _, out = cmd('cat %s/%s' % (path, f))
285 dfs[f] = out.strip()
286 elif os.path.isdir(p):
287 dfs[f] = DebugfsDir(p)
288 else:
289 raise Exception("%s is neither file nor directory" % (p))
290
291 log_level_dec()
292 log("DebugFS state", dfs)
293 log_level_dec()
294
295 return dfs
296
297class NetdevSim:
298 """
299 Class for netdevsim netdevice and its attributes.
300 """
301
302 def __init__(self):
303 self.dev = self._netdevsim_create()
304 devs.append(self)
305
Jakub Kicinski752d7b42017-12-27 18:39:11 -0800306 self.ns = ""
307
Jakub Kicinski417ec262017-12-01 15:09:00 -0800308 self.dfs_dir = '/sys/kernel/debug/netdevsim/%s' % (self.dev['ifname'])
309 self.dfs_refresh()
310
311 def __getitem__(self, key):
312 return self.dev[key]
313
314 def _netdevsim_create(self):
315 _, old = ip("link show")
316 ip("link add sim%d type netdevsim")
317 _, new = ip("link show")
318
319 for dev in new:
320 f = filter(lambda x: x["ifname"] == dev["ifname"], old)
321 if len(list(f)) == 0:
322 return dev
323
324 raise Exception("failed to create netdevsim device")
325
326 def remove(self):
327 devs.remove(self)
Jakub Kicinski752d7b42017-12-27 18:39:11 -0800328 ip("link del dev %s" % (self.dev["ifname"]), ns=self.ns)
Jakub Kicinski417ec262017-12-01 15:09:00 -0800329
330 def dfs_refresh(self):
331 self.dfs = DebugfsDir(self.dfs_dir)
332 return self.dfs
333
334 def dfs_num_bound_progs(self):
335 path = os.path.join(self.dfs_dir, "bpf_bound_progs")
336 _, progs = cmd('ls %s' % (path))
337 return len(progs.split())
338
339 def dfs_get_bound_progs(self, expected):
340 progs = DebugfsDir(os.path.join(self.dfs_dir, "bpf_bound_progs"))
341 if expected is not None:
342 if len(progs) != expected:
343 fail(True, "%d BPF programs bound, expected %d" %
344 (len(progs), expected))
345 return progs
346
347 def wait_for_flush(self, bound=0, total=0, n_retry=20):
348 for i in range(n_retry):
349 nbound = self.dfs_num_bound_progs()
350 nprogs = len(bpftool_prog_list())
351 if nbound == bound and nprogs == total:
352 return
353 time.sleep(0.05)
354 raise Exception("Time out waiting for program counts to stabilize want %d/%d, have %d bound, %d loaded" % (bound, total, nbound, nprogs))
355
Jakub Kicinski752d7b42017-12-27 18:39:11 -0800356 def set_ns(self, ns):
357 name = "1" if ns == "" else ns
358 ip("link set dev %s netns %s" % (self.dev["ifname"], name), ns=self.ns)
359 self.ns = ns
360
Jakub Kicinski417ec262017-12-01 15:09:00 -0800361 def set_mtu(self, mtu, fail=True):
362 return ip("link set dev %s mtu %d" % (self.dev["ifname"], mtu),
363 fail=fail)
364
Quentin Monnetcaf95222018-01-23 11:22:53 -0800365 def set_xdp(self, bpf, mode, force=False, JSON=True,
366 fail=True, include_stderr=False):
Jakub Kicinski417ec262017-12-01 15:09:00 -0800367 return ip("link set dev %s xdp%s %s" % (self.dev["ifname"], mode, bpf),
Quentin Monnetcaf95222018-01-23 11:22:53 -0800368 force=force, JSON=JSON,
369 fail=fail, include_stderr=include_stderr)
Jakub Kicinski417ec262017-12-01 15:09:00 -0800370
Quentin Monnetcaf95222018-01-23 11:22:53 -0800371 def unset_xdp(self, mode, force=False, JSON=True,
372 fail=True, include_stderr=False):
Jakub Kicinski417ec262017-12-01 15:09:00 -0800373 return ip("link set dev %s xdp%s off" % (self.dev["ifname"], mode),
Quentin Monnetcaf95222018-01-23 11:22:53 -0800374 force=force, JSON=JSON,
375 fail=fail, include_stderr=include_stderr)
Jakub Kicinski417ec262017-12-01 15:09:00 -0800376
377 def ip_link_show(self, xdp):
378 _, link = ip("link show dev %s" % (self['ifname']))
379 if len(link) > 1:
380 raise Exception("Multiple objects on ip link show")
381 if len(link) < 1:
382 return {}
383 fail(xdp != "xdp" in link,
384 "XDP program not reporting in iplink (reported %s, expected %s)" %
385 ("xdp" in link, xdp))
386 return link[0]
387
388 def tc_add_ingress(self):
389 tc("qdisc add dev %s ingress" % (self['ifname']))
390
391 def tc_del_ingress(self):
392 tc("qdisc del dev %s ingress" % (self['ifname']))
393
394 def tc_flush_filters(self, bound=0, total=0):
395 self.tc_del_ingress()
396 self.tc_add_ingress()
397 self.wait_for_flush(bound=bound, total=total)
398
399 def tc_show_ingress(self, expected=None):
400 # No JSON support, oh well...
401 flags = ["skip_sw", "skip_hw", "in_hw"]
402 named = ["protocol", "pref", "chain", "handle", "id", "tag"]
403
404 args = "-s filter show dev %s ingress" % (self['ifname'])
405 _, out = tc(args, JSON=False)
406
407 filters = []
408 lines = out.split('\n')
409 for line in lines:
410 words = line.split()
411 if "handle" not in words:
412 continue
413 fltr = {}
414 for flag in flags:
415 fltr[flag] = flag in words
416 for name in named:
417 try:
418 idx = words.index(name)
419 fltr[name] = words[idx + 1]
420 except ValueError:
421 pass
422 filters.append(fltr)
423
424 if expected is not None:
425 fail(len(filters) != expected,
426 "%d ingress filters loaded, expected %d" %
427 (len(filters), expected))
428 return filters
429
430 def cls_bpf_add_filter(self, bpf, da=False, skip_sw=False, skip_hw=False,
Quentin Monnetcaf95222018-01-23 11:22:53 -0800431 fail=True, include_stderr=False):
Jakub Kicinski417ec262017-12-01 15:09:00 -0800432 params = ""
433 if da:
434 params += " da"
435 if skip_sw:
436 params += " skip_sw"
437 if skip_hw:
438 params += " skip_hw"
439 return tc("filter add dev %s ingress bpf %s %s" %
Quentin Monnetcaf95222018-01-23 11:22:53 -0800440 (self['ifname'], bpf, params),
441 fail=fail, include_stderr=include_stderr)
Jakub Kicinski417ec262017-12-01 15:09:00 -0800442
443 def set_ethtool_tc_offloads(self, enable, fail=True):
444 args = "hw-tc-offload %s" % ("on" if enable else "off")
445 return ethtool(self, "-K", args, fail=fail)
446
447################################################################################
448def clean_up():
Jakub Kicinski7fedbb72018-01-17 19:13:31 -0800449 global files, netns, devs
450
Jakub Kicinski417ec262017-12-01 15:09:00 -0800451 for dev in devs:
452 dev.remove()
453 for f in files:
454 cmd("rm -f %s" % (f))
Jakub Kicinski752d7b42017-12-27 18:39:11 -0800455 for ns in netns:
456 cmd("ip netns delete %s" % (ns))
Jakub Kicinski7fedbb72018-01-17 19:13:31 -0800457 files = []
458 netns = []
Jakub Kicinski417ec262017-12-01 15:09:00 -0800459
460def pin_prog(file_name, idx=0):
461 progs = bpftool_prog_list(expected=(idx + 1))
462 prog = progs[idx]
463 bpftool("prog pin id %d %s" % (prog["id"], file_name))
464 files.append(file_name)
465
466 return file_name, bpf_pinned(file_name)
467
Jakub Kicinski7fedbb72018-01-17 19:13:31 -0800468def pin_map(file_name, idx=0, expected=1):
469 maps = bpftool_map_list(expected=expected)
470 m = maps[idx]
471 bpftool("map pin id %d %s" % (m["id"], file_name))
472 files.append(file_name)
473
474 return file_name, bpf_pinned(file_name)
475
476def check_dev_info_removed(prog_file=None, map_file=None):
477 bpftool_prog_list(expected=0)
478 ret, err = bpftool("prog show pin %s" % (prog_file), fail=False)
479 fail(ret == 0, "Showing prog with removed device did not fail")
480 fail(err["error"].find("No such device") == -1,
481 "Showing prog with removed device expected ENODEV, error is %s" %
482 (err["error"]))
483
484 bpftool_map_list(expected=0)
485 ret, err = bpftool("map show pin %s" % (map_file), fail=False)
486 fail(ret == 0, "Showing map with removed device did not fail")
487 fail(err["error"].find("No such device") == -1,
488 "Showing map with removed device expected ENODEV, error is %s" %
489 (err["error"]))
490
491def check_dev_info(other_ns, ns, prog_file=None, map_file=None, removed=False):
492 progs = bpftool_prog_list(expected=1, ns=ns)
Jakub Kicinski752d7b42017-12-27 18:39:11 -0800493 prog = progs[0]
494
495 fail("dev" not in prog.keys(), "Device parameters not reported")
496 dev = prog["dev"]
497 fail("ifindex" not in dev.keys(), "Device parameters not reported")
498 fail("ns_dev" not in dev.keys(), "Device parameters not reported")
499 fail("ns_inode" not in dev.keys(), "Device parameters not reported")
500
Jakub Kicinski7fedbb72018-01-17 19:13:31 -0800501 if not other_ns:
Jakub Kicinski752d7b42017-12-27 18:39:11 -0800502 fail("ifname" not in dev.keys(), "Ifname not reported")
503 fail(dev["ifname"] != sim["ifname"],
504 "Ifname incorrect %s vs %s" % (dev["ifname"], sim["ifname"]))
505 else:
506 fail("ifname" in dev.keys(), "Ifname is reported for other ns")
Jakub Kicinski7fedbb72018-01-17 19:13:31 -0800507
508 maps = bpftool_map_list(expected=2, ns=ns)
509 for m in maps:
510 fail("dev" not in m.keys(), "Device parameters not reported")
511 fail(dev != m["dev"], "Map's device different than program's")
Jakub Kicinski752d7b42017-12-27 18:39:11 -0800512
Quentin Monnetcaf95222018-01-23 11:22:53 -0800513def check_extack(output, reference, args):
514 if skip_extack:
515 return
516 lines = output.split("\n")
517 comp = len(lines) >= 2 and lines[1] == reference
518 fail(not comp, "Missing or incorrect netlink extack message")
519
520def check_extack_nsim(output, reference, args):
521 check_extack(output, "Error: netdevsim: " + reference, args)
522
Jakub Kicinski417ec262017-12-01 15:09:00 -0800523# Parse command line
524parser = argparse.ArgumentParser()
525parser.add_argument("--log", help="output verbose log to given file")
526args = parser.parse_args()
527if args.log:
528 logfile = open(args.log, 'w+')
529 logfile.write("# -*-Org-*-")
530
531log("Prepare...", "", level=1)
532log_level_inc()
533
534# Check permissions
535skip(os.getuid() != 0, "test must be run as root")
536
537# Check tools
538ret, progs = bpftool("prog", fail=False)
539skip(ret != 0, "bpftool not installed")
540# Check no BPF programs are loaded
541skip(len(progs) != 0, "BPF programs already loaded on the system")
542
543# Check netdevsim
544ret, out = cmd("modprobe netdevsim", fail=False)
545skip(ret != 0, "netdevsim module could not be loaded")
546
547# Check debugfs
548_, out = cmd("mount")
549if out.find("/sys/kernel/debug type debugfs") == -1:
550 cmd("mount -t debugfs none /sys/kernel/debug")
551
552# Check samples are compiled
Jakub Kicinski7fedbb72018-01-17 19:13:31 -0800553samples = ["sample_ret0.o", "sample_map_ret0.o"]
Jakub Kicinski417ec262017-12-01 15:09:00 -0800554for s in samples:
555 ret, out = cmd("ls %s/%s" % (bpf_test_dir, s), fail=False)
556 skip(ret != 0, "sample %s/%s not found, please compile it" %
557 (bpf_test_dir, s))
558
Quentin Monnetcaf95222018-01-23 11:22:53 -0800559# Check if iproute2 is built with libmnl (needed by extack support)
560_, _, err = cmd("tc qdisc delete dev lo handle 0",
561 fail=False, include_stderr=True)
562if err.find("Error: Failed to find qdisc with specified handle.") == -1:
563 print("Warning: no extack message in iproute2 output, libmnl missing?")
564 log("Warning: no extack message in iproute2 output, libmnl missing?", "")
565 skip_extack = True
566
Jakub Kicinski752d7b42017-12-27 18:39:11 -0800567# Check if net namespaces seem to work
568ns = mknetns()
569skip(ns is None, "Could not create a net namespace")
570cmd("ip netns delete %s" % (ns))
571netns = []
572
Jakub Kicinski417ec262017-12-01 15:09:00 -0800573try:
574 obj = bpf_obj("sample_ret0.o")
575 bytecode = bpf_bytecode("1,6 0 0 4294967295,")
576
577 start_test("Test destruction of generic XDP...")
578 sim = NetdevSim()
579 sim.set_xdp(obj, "generic")
580 sim.remove()
581 bpftool_prog_list_wait(expected=0)
582
583 sim = NetdevSim()
584 sim.tc_add_ingress()
585
586 start_test("Test TC non-offloaded...")
587 ret, _ = sim.cls_bpf_add_filter(obj, skip_hw=True, fail=False)
588 fail(ret != 0, "Software TC filter did not load")
589
590 start_test("Test TC non-offloaded isn't getting bound...")
591 ret, _ = sim.cls_bpf_add_filter(obj, fail=False)
592 fail(ret != 0, "Software TC filter did not load")
593 sim.dfs_get_bound_progs(expected=0)
594
595 sim.tc_flush_filters()
596
597 start_test("Test TC offloads are off by default...")
Quentin Monnetcaf95222018-01-23 11:22:53 -0800598 ret, _, err = sim.cls_bpf_add_filter(obj, skip_sw=True,
599 fail=False, include_stderr=True)
Jakub Kicinski417ec262017-12-01 15:09:00 -0800600 fail(ret == 0, "TC filter loaded without enabling TC offloads")
Quentin Monnetcaf95222018-01-23 11:22:53 -0800601 check_extack(err, "Error: TC offload is disabled on net device.", args)
Jakub Kicinski417ec262017-12-01 15:09:00 -0800602 sim.wait_for_flush()
603
604 sim.set_ethtool_tc_offloads(True)
605 sim.dfs["bpf_tc_non_bound_accept"] = "Y"
606
607 start_test("Test TC offload by default...")
608 ret, _ = sim.cls_bpf_add_filter(obj, fail=False)
609 fail(ret != 0, "Software TC filter did not load")
610 sim.dfs_get_bound_progs(expected=0)
611 ingress = sim.tc_show_ingress(expected=1)
612 fltr = ingress[0]
613 fail(not fltr["in_hw"], "Filter not offloaded by default")
614
615 sim.tc_flush_filters()
616
617 start_test("Test TC cBPF bytcode tries offload by default...")
618 ret, _ = sim.cls_bpf_add_filter(bytecode, fail=False)
619 fail(ret != 0, "Software TC filter did not load")
620 sim.dfs_get_bound_progs(expected=0)
621 ingress = sim.tc_show_ingress(expected=1)
622 fltr = ingress[0]
623 fail(not fltr["in_hw"], "Bytecode not offloaded by default")
624
625 sim.tc_flush_filters()
626 sim.dfs["bpf_tc_non_bound_accept"] = "N"
627
628 start_test("Test TC cBPF unbound bytecode doesn't offload...")
Quentin Monnetcaf95222018-01-23 11:22:53 -0800629 ret, _, err = sim.cls_bpf_add_filter(bytecode, skip_sw=True,
630 fail=False, include_stderr=True)
Jakub Kicinski417ec262017-12-01 15:09:00 -0800631 fail(ret == 0, "TC bytecode loaded for offload")
Quentin Monnetcaf95222018-01-23 11:22:53 -0800632 check_extack_nsim(err, "netdevsim configured to reject unbound programs.",
633 args)
Jakub Kicinski417ec262017-12-01 15:09:00 -0800634 sim.wait_for_flush()
635
636 start_test("Test TC offloads work...")
637 ret, _ = sim.cls_bpf_add_filter(obj, skip_sw=True, fail=False)
638 fail(ret != 0, "TC filter did not load with TC offloads enabled")
639
640 start_test("Test TC offload basics...")
641 dfs = sim.dfs_get_bound_progs(expected=1)
642 progs = bpftool_prog_list(expected=1)
643 ingress = sim.tc_show_ingress(expected=1)
644
645 dprog = dfs[0]
646 prog = progs[0]
647 fltr = ingress[0]
648 fail(fltr["skip_hw"], "TC does reports 'skip_hw' on offloaded filter")
649 fail(not fltr["in_hw"], "TC does not report 'in_hw' for offloaded filter")
650 fail(not fltr["skip_sw"], "TC does not report 'skip_sw' back")
651
652 start_test("Test TC offload is device-bound...")
653 fail(str(prog["id"]) != fltr["id"], "Program IDs don't match")
654 fail(prog["tag"] != fltr["tag"], "Program tags don't match")
655 fail(fltr["id"] != dprog["id"], "Program IDs don't match")
656 fail(dprog["state"] != "xlated", "Offloaded program state not translated")
657 fail(dprog["loaded"] != "Y", "Offloaded program is not loaded")
658
659 start_test("Test disabling TC offloads is rejected while filters installed...")
660 ret, _ = sim.set_ethtool_tc_offloads(False, fail=False)
661 fail(ret == 0, "Driver should refuse to disable TC offloads with filters installed...")
662
663 start_test("Test qdisc removal frees things...")
664 sim.tc_flush_filters()
665 sim.tc_show_ingress(expected=0)
666
667 start_test("Test disabling TC offloads is OK without filters...")
668 ret, _ = sim.set_ethtool_tc_offloads(False, fail=False)
669 fail(ret != 0,
670 "Driver refused to disable TC offloads without filters installed...")
671
672 sim.set_ethtool_tc_offloads(True)
673
674 start_test("Test destroying device gets rid of TC filters...")
675 sim.cls_bpf_add_filter(obj, skip_sw=True)
676 sim.remove()
677 bpftool_prog_list_wait(expected=0)
678
679 sim = NetdevSim()
680 sim.set_ethtool_tc_offloads(True)
681
682 start_test("Test destroying device gets rid of XDP...")
683 sim.set_xdp(obj, "offload")
684 sim.remove()
685 bpftool_prog_list_wait(expected=0)
686
687 sim = NetdevSim()
688 sim.set_ethtool_tc_offloads(True)
689
690 start_test("Test XDP prog reporting...")
691 sim.set_xdp(obj, "drv")
692 ipl = sim.ip_link_show(xdp=True)
693 progs = bpftool_prog_list(expected=1)
694 fail(ipl["xdp"]["prog"]["id"] != progs[0]["id"],
695 "Loaded program has wrong ID")
696
697 start_test("Test XDP prog replace without force...")
698 ret, _ = sim.set_xdp(obj, "drv", fail=False)
699 fail(ret == 0, "Replaced XDP program without -force")
700 sim.wait_for_flush(total=1)
701
702 start_test("Test XDP prog replace with force...")
703 ret, _ = sim.set_xdp(obj, "drv", force=True, fail=False)
704 fail(ret != 0, "Could not replace XDP program with -force")
705 bpftool_prog_list_wait(expected=1)
706 ipl = sim.ip_link_show(xdp=True)
707 progs = bpftool_prog_list(expected=1)
708 fail(ipl["xdp"]["prog"]["id"] != progs[0]["id"],
709 "Loaded program has wrong ID")
Jakub Kicinski752d7b42017-12-27 18:39:11 -0800710 fail("dev" in progs[0].keys(),
711 "Device parameters reported for non-offloaded program")
Jakub Kicinski417ec262017-12-01 15:09:00 -0800712
713 start_test("Test XDP prog replace with bad flags...")
Quentin Monnetcaf95222018-01-23 11:22:53 -0800714 ret, _, err = sim.set_xdp(obj, "offload", force=True,
715 fail=False, include_stderr=True)
Jakub Kicinski417ec262017-12-01 15:09:00 -0800716 fail(ret == 0, "Replaced XDP program with a program in different mode")
Quentin Monnetcaf95222018-01-23 11:22:53 -0800717 check_extack_nsim(err, "program loaded with different flags.", args)
718 ret, _, err = sim.set_xdp(obj, "", force=True,
719 fail=False, include_stderr=True)
Jakub Kicinski417ec262017-12-01 15:09:00 -0800720 fail(ret == 0, "Replaced XDP program with a program in different mode")
Quentin Monnetcaf95222018-01-23 11:22:53 -0800721 check_extack_nsim(err, "program loaded with different flags.", args)
Jakub Kicinski417ec262017-12-01 15:09:00 -0800722
723 start_test("Test XDP prog remove with bad flags...")
Quentin Monnetcaf95222018-01-23 11:22:53 -0800724 ret, _, err = sim.unset_xdp("offload", force=True,
725 fail=False, include_stderr=True)
Jakub Kicinski417ec262017-12-01 15:09:00 -0800726 fail(ret == 0, "Removed program with a bad mode mode")
Quentin Monnetcaf95222018-01-23 11:22:53 -0800727 check_extack_nsim(err, "program loaded with different flags.", args)
728 ret, _, err = sim.unset_xdp("", force=True,
729 fail=False, include_stderr=True)
Jakub Kicinski417ec262017-12-01 15:09:00 -0800730 fail(ret == 0, "Removed program with a bad mode mode")
Quentin Monnetcaf95222018-01-23 11:22:53 -0800731 check_extack_nsim(err, "program loaded with different flags.", args)
Jakub Kicinski417ec262017-12-01 15:09:00 -0800732
733 start_test("Test MTU restrictions...")
734 ret, _ = sim.set_mtu(9000, fail=False)
735 fail(ret == 0,
736 "Driver should refuse to increase MTU to 9000 with XDP loaded...")
737 sim.unset_xdp("drv")
738 bpftool_prog_list_wait(expected=0)
739 sim.set_mtu(9000)
Quentin Monnetcaf95222018-01-23 11:22:53 -0800740 ret, _, err = sim.set_xdp(obj, "drv", fail=False, include_stderr=True)
Jakub Kicinski417ec262017-12-01 15:09:00 -0800741 fail(ret == 0, "Driver should refuse to load program with MTU of 9000...")
Quentin Monnetcaf95222018-01-23 11:22:53 -0800742 check_extack_nsim(err, "MTU too large w/ XDP enabled.", args)
Jakub Kicinski417ec262017-12-01 15:09:00 -0800743 sim.set_mtu(1500)
744
745 sim.wait_for_flush()
746 start_test("Test XDP offload...")
747 sim.set_xdp(obj, "offload")
748 ipl = sim.ip_link_show(xdp=True)
749 link_xdp = ipl["xdp"]["prog"]
750 progs = bpftool_prog_list(expected=1)
751 prog = progs[0]
752 fail(link_xdp["id"] != prog["id"], "Loaded program has wrong ID")
753
754 start_test("Test XDP offload is device bound...")
755 dfs = sim.dfs_get_bound_progs(expected=1)
756 dprog = dfs[0]
757
758 fail(prog["id"] != link_xdp["id"], "Program IDs don't match")
759 fail(prog["tag"] != link_xdp["tag"], "Program tags don't match")
760 fail(str(link_xdp["id"]) != dprog["id"], "Program IDs don't match")
761 fail(dprog["state"] != "xlated", "Offloaded program state not translated")
762 fail(dprog["loaded"] != "Y", "Offloaded program is not loaded")
763
764 start_test("Test removing XDP program many times...")
765 sim.unset_xdp("offload")
766 sim.unset_xdp("offload")
767 sim.unset_xdp("drv")
768 sim.unset_xdp("drv")
769 sim.unset_xdp("")
770 sim.unset_xdp("")
771 bpftool_prog_list_wait(expected=0)
772
773 start_test("Test attempt to use a program for a wrong device...")
774 sim2 = NetdevSim()
775 sim2.set_xdp(obj, "offload")
776 pin_file, pinned = pin_prog("/sys/fs/bpf/tmp")
777
Quentin Monnetcaf95222018-01-23 11:22:53 -0800778 ret, _, err = sim.set_xdp(pinned, "offload",
779 fail=False, include_stderr=True)
Jakub Kicinski417ec262017-12-01 15:09:00 -0800780 fail(ret == 0, "Pinned program loaded for a different device accepted")
Quentin Monnetcaf95222018-01-23 11:22:53 -0800781 check_extack_nsim(err, "program bound to different dev.", args)
Jakub Kicinski417ec262017-12-01 15:09:00 -0800782 sim2.remove()
Quentin Monnetcaf95222018-01-23 11:22:53 -0800783 ret, _, err = sim.set_xdp(pinned, "offload",
784 fail=False, include_stderr=True)
Jakub Kicinski417ec262017-12-01 15:09:00 -0800785 fail(ret == 0, "Pinned program loaded for a removed device accepted")
Quentin Monnetcaf95222018-01-23 11:22:53 -0800786 check_extack_nsim(err, "xdpoffload of non-bound program.", args)
Jakub Kicinski417ec262017-12-01 15:09:00 -0800787 rm(pin_file)
788 bpftool_prog_list_wait(expected=0)
789
790 start_test("Test mixing of TC and XDP...")
791 sim.tc_add_ingress()
792 sim.set_xdp(obj, "offload")
Quentin Monnetcaf95222018-01-23 11:22:53 -0800793 ret, _, err = sim.cls_bpf_add_filter(obj, skip_sw=True,
794 fail=False, include_stderr=True)
Jakub Kicinski417ec262017-12-01 15:09:00 -0800795 fail(ret == 0, "Loading TC when XDP active should fail")
Quentin Monnetcaf95222018-01-23 11:22:53 -0800796 check_extack_nsim(err, "driver and netdev offload states mismatch.", args)
Jakub Kicinski417ec262017-12-01 15:09:00 -0800797 sim.unset_xdp("offload")
798 sim.wait_for_flush()
799
800 sim.cls_bpf_add_filter(obj, skip_sw=True)
Quentin Monnetcaf95222018-01-23 11:22:53 -0800801 ret, _, err = sim.set_xdp(obj, "offload", fail=False, include_stderr=True)
Jakub Kicinski417ec262017-12-01 15:09:00 -0800802 fail(ret == 0, "Loading XDP when TC active should fail")
Quentin Monnetcaf95222018-01-23 11:22:53 -0800803 check_extack_nsim(err, "TC program is already loaded.", args)
Jakub Kicinski417ec262017-12-01 15:09:00 -0800804
805 start_test("Test binding TC from pinned...")
806 pin_file, pinned = pin_prog("/sys/fs/bpf/tmp")
807 sim.tc_flush_filters(bound=1, total=1)
808 sim.cls_bpf_add_filter(pinned, da=True, skip_sw=True)
809 sim.tc_flush_filters(bound=1, total=1)
810
811 start_test("Test binding XDP from pinned...")
812 sim.set_xdp(obj, "offload")
813 pin_file, pinned = pin_prog("/sys/fs/bpf/tmp2", idx=1)
814
815 sim.set_xdp(pinned, "offload", force=True)
816 sim.unset_xdp("offload")
817 sim.set_xdp(pinned, "offload", force=True)
818 sim.unset_xdp("offload")
819
820 start_test("Test offload of wrong type fails...")
821 ret, _ = sim.cls_bpf_add_filter(pinned, da=True, skip_sw=True, fail=False)
822 fail(ret == 0, "Managed to attach XDP program to TC")
823
824 start_test("Test asking for TC offload of two filters...")
825 sim.cls_bpf_add_filter(obj, da=True, skip_sw=True)
Quentin Monnetcaf95222018-01-23 11:22:53 -0800826 ret, _, err = sim.cls_bpf_add_filter(obj, da=True, skip_sw=True,
827 fail=False, include_stderr=True)
David S. Millerfba961a2017-12-22 11:16:31 -0500828 fail(ret == 0, "Managed to offload two TC filters at the same time")
Quentin Monnetcaf95222018-01-23 11:22:53 -0800829 check_extack_nsim(err, "driver and netdev offload states mismatch.", args)
Jakub Kicinski417ec262017-12-01 15:09:00 -0800830
831 sim.tc_flush_filters(bound=2, total=2)
832
833 start_test("Test if netdev removal waits for translation...")
834 delay_msec = 500
835 sim.dfs["bpf_bind_verifier_delay"] = delay_msec
836 start = time.time()
837 cmd_line = "tc filter add dev %s ingress bpf %s da skip_sw" % \
838 (sim['ifname'], obj)
839 tc_proc = cmd(cmd_line, background=True, fail=False)
840 # Wait for the verifier to start
841 while sim.dfs_num_bound_progs() <= 2:
842 pass
843 sim.remove()
844 end = time.time()
845 ret, _ = cmd_result(tc_proc, fail=False)
846 time_diff = end - start
847 log("Time", "start:\t%s\nend:\t%s\ndiff:\t%s" % (start, end, time_diff))
848
849 fail(ret == 0, "Managed to load TC filter on a unregistering device")
850 delay_sec = delay_msec * 0.001
851 fail(time_diff < delay_sec, "Removal process took %s, expected %s" %
852 (time_diff, delay_sec))
853
Jakub Kicinski752d7b42017-12-27 18:39:11 -0800854 # Remove all pinned files and reinstantiate the netdev
855 clean_up()
856 bpftool_prog_list_wait(expected=0)
857
858 sim = NetdevSim()
Jakub Kicinski7fedbb72018-01-17 19:13:31 -0800859 map_obj = bpf_obj("sample_map_ret0.o")
860 start_test("Test loading program with maps...")
861 sim.set_xdp(map_obj, "offload", JSON=False) # map fixup msg breaks JSON
Jakub Kicinski752d7b42017-12-27 18:39:11 -0800862
863 start_test("Test bpftool bound info reporting (own ns)...")
864 check_dev_info(False, "")
865
866 start_test("Test bpftool bound info reporting (other ns)...")
867 ns = mknetns()
868 sim.set_ns(ns)
869 check_dev_info(True, "")
870
871 start_test("Test bpftool bound info reporting (remote ns)...")
872 check_dev_info(False, ns)
873
874 start_test("Test bpftool bound info reporting (back to own ns)...")
875 sim.set_ns("")
876 check_dev_info(False, "")
877
Jakub Kicinski7fedbb72018-01-17 19:13:31 -0800878 prog_file, _ = pin_prog("/sys/fs/bpf/tmp_prog")
879 map_file, _ = pin_map("/sys/fs/bpf/tmp_map", idx=1, expected=2)
Jakub Kicinski752d7b42017-12-27 18:39:11 -0800880 sim.remove()
881
882 start_test("Test bpftool bound info reporting (removed dev)...")
Jakub Kicinski7fedbb72018-01-17 19:13:31 -0800883 check_dev_info_removed(prog_file=prog_file, map_file=map_file)
884
885 # Remove all pinned files and reinstantiate the netdev
886 clean_up()
887 bpftool_prog_list_wait(expected=0)
888
889 sim = NetdevSim()
890
891 start_test("Test map update (no flags)...")
892 sim.set_xdp(map_obj, "offload", JSON=False) # map fixup msg breaks JSON
893 maps = bpftool_map_list(expected=2)
894 array = maps[0] if maps[0]["type"] == "array" else maps[1]
895 htab = maps[0] if maps[0]["type"] == "hash" else maps[1]
896 for m in maps:
897 for i in range(2):
898 bpftool("map update id %d key %s value %s" %
899 (m["id"], int2str("I", i), int2str("Q", i * 3)))
900
901 for m in maps:
902 ret, _ = bpftool("map update id %d key %s value %s" %
903 (m["id"], int2str("I", 3), int2str("Q", 3 * 3)),
904 fail=False)
905 fail(ret == 0, "added too many entries")
906
907 start_test("Test map update (exists)...")
908 for m in maps:
909 for i in range(2):
910 bpftool("map update id %d key %s value %s exist" %
911 (m["id"], int2str("I", i), int2str("Q", i * 3)))
912
913 for m in maps:
914 ret, err = bpftool("map update id %d key %s value %s exist" %
915 (m["id"], int2str("I", 3), int2str("Q", 3 * 3)),
916 fail=False)
917 fail(ret == 0, "updated non-existing key")
918 fail(err["error"].find("No such file or directory") == -1,
919 "expected ENOENT, error is '%s'" % (err["error"]))
920
921 start_test("Test map update (noexist)...")
922 for m in maps:
923 for i in range(2):
924 ret, err = bpftool("map update id %d key %s value %s noexist" %
925 (m["id"], int2str("I", i), int2str("Q", i * 3)),
926 fail=False)
927 fail(ret == 0, "updated existing key")
928 fail(err["error"].find("File exists") == -1,
929 "expected EEXIST, error is '%s'" % (err["error"]))
930
931 start_test("Test map dump...")
932 for m in maps:
933 _, entries = bpftool("map dump id %d" % (m["id"]))
934 for i in range(2):
935 key = str2int(entries[i]["key"])
936 fail(key != i, "expected key %d, got %d" % (key, i))
937 val = str2int(entries[i]["value"])
938 fail(val != i * 3, "expected value %d, got %d" % (val, i * 3))
939
940 start_test("Test map getnext...")
941 for m in maps:
942 _, entry = bpftool("map getnext id %d" % (m["id"]))
943 key = str2int(entry["next_key"])
944 fail(key != 0, "next key %d, expected %d" % (key, 0))
945 _, entry = bpftool("map getnext id %d key %s" %
946 (m["id"], int2str("I", 0)))
947 key = str2int(entry["next_key"])
948 fail(key != 1, "next key %d, expected %d" % (key, 1))
949 ret, err = bpftool("map getnext id %d key %s" %
950 (m["id"], int2str("I", 1)), fail=False)
951 fail(ret == 0, "got next key past the end of map")
952 fail(err["error"].find("No such file or directory") == -1,
953 "expected ENOENT, error is '%s'" % (err["error"]))
954
955 start_test("Test map delete (htab)...")
956 for i in range(2):
957 bpftool("map delete id %d key %s" % (htab["id"], int2str("I", i)))
958
959 start_test("Test map delete (array)...")
960 for i in range(2):
961 ret, err = bpftool("map delete id %d key %s" %
962 (htab["id"], int2str("I", i)), fail=False)
963 fail(ret == 0, "removed entry from an array")
964 fail(err["error"].find("No such file or directory") == -1,
965 "expected ENOENT, error is '%s'" % (err["error"]))
966
967 start_test("Test map remove...")
968 sim.unset_xdp("offload")
969 bpftool_map_list_wait(expected=0)
970 sim.remove()
971
972 sim = NetdevSim()
973 sim.set_xdp(map_obj, "offload", JSON=False) # map fixup msg breaks JSON
974 sim.remove()
975 bpftool_map_list_wait(expected=0)
976
977 start_test("Test map creation fail path...")
978 sim = NetdevSim()
979 sim.dfs["bpf_map_accept"] = "N"
980 ret, _ = sim.set_xdp(map_obj, "offload", JSON=False, fail=False)
981 fail(ret == 0,
982 "netdevsim didn't refuse to create a map with offload disabled")
Jakub Kicinski752d7b42017-12-27 18:39:11 -0800983
Jakub Kicinski417ec262017-12-01 15:09:00 -0800984 print("%s: OK" % (os.path.basename(__file__)))
985
986finally:
987 log("Clean up...", "", level=1)
988 log_level_inc()
989 clean_up()