aboutsummaryrefslogtreecommitdiffstats
path: root/tools
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2018-04-03 17:04:18 -0400
committerLinus Torvalds <torvalds@linux-foundation.org>2018-04-03 17:04:18 -0400
commit5bb053bef82523a8fd78d650bca81c9f114fa276 (patch)
tree58c2fe47f60bb69230bb05d57a6c9e3f47f7b1fe /tools
parentbb2407a7219760926760f0448fddf00d625e5aec (diff)
parent159f02977b2feb18a4bece5e586c838a6d26d44b (diff)
Merge git://git.kernel.org/pub/scm/linux/kernel/git/davem/net-next
Pull networking updates from David Miller: 1) Support offloading wireless authentication to userspace via NL80211_CMD_EXTERNAL_AUTH, from Srinivas Dasari. 2) A lot of work on network namespace setup/teardown from Kirill Tkhai. Setup and cleanup of namespaces now all run asynchronously and thus performance is significantly increased. 3) Add rx/tx timestamping support to mv88e6xxx driver, from Brandon Streiff. 4) Support zerocopy on RDS sockets, from Sowmini Varadhan. 5) Use denser instruction encoding in x86 eBPF JIT, from Daniel Borkmann. 6) Support hw offload of vlan filtering in mvpp2 dreiver, from Maxime Chevallier. 7) Support grafting of child qdiscs in mlxsw driver, from Nogah Frankel. 8) Add packet forwarding tests to selftests, from Ido Schimmel. 9) Deal with sub-optimal GSO packets better in BBR congestion control, from Eric Dumazet. 10) Support 5-tuple hashing in ipv6 multipath routing, from David Ahern. 11) Add path MTU tests to selftests, from Stefano Brivio. 12) Various bits of IPSEC offloading support for mlx5, from Aviad Yehezkel, Yossi Kuperman, and Saeed Mahameed. 13) Support RSS spreading on ntuple filters in SFC driver, from Edward Cree. 14) Lots of sockmap work from John Fastabend. Applications can use eBPF to filter sendmsg and sendpage operations. 15) In-kernel receive TLS support, from Dave Watson. 16) Add XDP support to ixgbevf, this is significant because it should allow optimized XDP usage in various cloud environments. From Tony Nguyen. 17) Add new Intel E800 series "ice" ethernet driver, from Anirudh Venkataramanan et al. 18) IP fragmentation match offload support in nfp driver, from Pieter Jansen van Vuuren. 19) Support XDP redirect in i40e driver, from Björn Töpel. 20) Add BPF_RAW_TRACEPOINT program type for accessing the arguments of tracepoints in their raw form, from Alexei Starovoitov. 21) Lots of striding RQ improvements to mlx5 driver with many performance improvements, from Tariq Toukan. 22) Use rhashtable for inet frag reassembly, from Eric Dumazet. * git://git.kernel.org/pub/scm/linux/kernel/git/davem/net-next: (1678 commits) net: mvneta: improve suspend/resume net: mvneta: split rxq/txq init and txq deinit into SW and HW parts ipv6: frags: fix /proc/sys/net/ipv6/ip6frag_low_thresh net: bgmac: Fix endian access in bgmac_dma_tx_ring_free() net: bgmac: Correctly annotate register space route: check sysctl_fib_multipath_use_neigh earlier than hash fix typo in command value in drivers/net/phy/mdio-bitbang. sky2: Increase D3 delay to sky2 stops working after suspend net/mlx5e: Set EQE based as default TX interrupt moderation mode ibmvnic: Disable irqs before exiting reset from closed state net: sched: do not emit messages while holding spinlock vlan: also check phy_driver ts_info for vlan's real device Bluetooth: Mark expected switch fall-throughs Bluetooth: Set HCI_QUIRK_SIMULTANEOUS_DISCOVERY for BTUSB_QCA_ROME Bluetooth: btrsi: remove unused including <linux/version.h> Bluetooth: hci_bcm: Remove DMI quirk for the MINIX Z83-4 sh_eth: kill useless check in __sh_eth_get_regs() sh_eth: add sh_eth_cpu_data::no_xdfar flag ipv6: factorize sk_wmem_alloc updates done by __ip6_append_data() ipv4: factorize sk_wmem_alloc updates done by __ip_append_data() ...
Diffstat (limited to 'tools')
-rw-r--r--tools/bpf/Makefile78
-rw-r--r--tools/bpf/bpftool/Documentation/bpftool-prog.rst18
-rw-r--r--tools/bpf/bpftool/Makefile6
-rw-r--r--tools/bpf/bpftool/bash-completion/bpftool13
-rw-r--r--tools/bpf/bpftool/cfg.c514
-rw-r--r--tools/bpf/bpftool/cfg.h43
-rw-r--r--tools/bpf/bpftool/main.c104
-rw-r--r--tools/bpf/bpftool/prog.c305
-rw-r--r--tools/bpf/bpftool/xlated_dumper.c338
-rw-r--r--tools/bpf/bpftool/xlated_dumper.h64
-rw-r--r--tools/include/uapi/linux/bpf.h107
-rw-r--r--tools/lib/bpf/bpf.c55
-rw-r--r--tools/lib/bpf/bpf.h18
-rw-r--r--tools/lib/bpf/libbpf.c114
-rw-r--r--tools/lib/bpf/libbpf.h8
-rw-r--r--tools/testing/selftests/bpf/Makefile25
-rw-r--r--tools/testing/selftests/bpf/bpf_helpers.h12
-rw-r--r--tools/testing/selftests/bpf/bpf_rlimit.h28
-rw-r--r--tools/testing/selftests/bpf/connect4_prog.c45
-rw-r--r--tools/testing/selftests/bpf/connect6_prog.c61
-rw-r--r--tools/testing/selftests/bpf/sockmap_parse_prog.c15
-rw-r--r--tools/testing/selftests/bpf/sockmap_tcp_msg_prog.c33
-rw-r--r--tools/testing/selftests/bpf/sockmap_verdict_prog.c7
-rw-r--r--tools/testing/selftests/bpf/test_align.c6
-rw-r--r--tools/testing/selftests/bpf/test_dev_cgroup.c6
-rw-r--r--tools/testing/selftests/bpf/test_lpm_map.c14
-rw-r--r--tools/testing/selftests/bpf/test_lru_map.c6
-rw-r--r--tools/testing/selftests/bpf/test_maps.c62
-rw-r--r--tools/testing/selftests/bpf/test_progs.c230
-rw-r--r--tools/testing/selftests/bpf/test_sock.c479
-rw-r--r--tools/testing/selftests/bpf/test_sock_addr.c588
-rwxr-xr-xtools/testing/selftests/bpf/test_sock_addr.sh57
-rw-r--r--tools/testing/selftests/bpf/test_stacktrace_build_id.c60
-rw-r--r--tools/testing/selftests/bpf/test_tag.c4
-rw-r--r--tools/testing/selftests/bpf/test_tcpbpf_user.c2
-rw-r--r--tools/testing/selftests/bpf/test_verifier.c304
-rw-r--r--tools/testing/selftests/bpf/test_verifier_log.c8
-rw-r--r--tools/testing/selftests/bpf/urandom_read.c22
-rw-r--r--tools/testing/selftests/net/Makefile2
-rw-r--r--tools/testing/selftests/net/config5
-rwxr-xr-xtools/testing/selftests/net/fib-onlink-tests.sh467
-rwxr-xr-xtools/testing/selftests/net/fib_tests.sh664
-rw-r--r--tools/testing/selftests/net/forwarding/.gitignore1
-rw-r--r--tools/testing/selftests/net/forwarding/README56
-rwxr-xr-xtools/testing/selftests/net/forwarding/bridge_vlan_aware.sh88
-rwxr-xr-xtools/testing/selftests/net/forwarding/bridge_vlan_unaware.sh86
-rw-r--r--tools/testing/selftests/net/forwarding/config12
-rw-r--r--tools/testing/selftests/net/forwarding/forwarding.config.sample35
-rw-r--r--tools/testing/selftests/net/forwarding/lib.sh577
-rwxr-xr-xtools/testing/selftests/net/forwarding/router.sh125
-rwxr-xr-xtools/testing/selftests/net/forwarding/router_multipath.sh376
-rwxr-xr-xtools/testing/selftests/net/forwarding/tc_actions.sh202
-rwxr-xr-xtools/testing/selftests/net/forwarding/tc_chains.sh122
-rw-r--r--tools/testing/selftests/net/forwarding/tc_common.sh25
-rwxr-xr-xtools/testing/selftests/net/forwarding/tc_flower.sh196
-rwxr-xr-xtools/testing/selftests/net/forwarding/tc_shblocks.sh122
-rwxr-xr-xtools/testing/selftests/net/in_netns.sh23
-rw-r--r--tools/testing/selftests/net/msg_zerocopy.c131
-rwxr-xr-xtools/testing/selftests/net/pmtu.sh471
-rw-r--r--tools/testing/selftests/net/psock_fanout.c35
-rwxr-xr-xtools/testing/selftests/net/rtnetlink.sh6
-rwxr-xr-xtools/testing/selftests/net/run_afpackettests4
-rw-r--r--tools/testing/selftests/networking/timestamping/txtimestamp.c21
-rw-r--r--tools/testing/selftests/tc-testing/README173
-rw-r--r--tools/testing/selftests/tc-testing/TODO.txt25
-rw-r--r--tools/testing/selftests/tc-testing/TdcPlugin.py74
-rw-r--r--tools/testing/selftests/tc-testing/creating-plugins/AddingPlugins.txt104
-rw-r--r--tools/testing/selftests/tc-testing/creating-testcases/AddingTestCases.txt35
-rw-r--r--tools/testing/selftests/tc-testing/plugin-lib/README-PLUGINS27
-rw-r--r--tools/testing/selftests/tc-testing/plugin-lib/nsPlugin.py141
-rw-r--r--tools/testing/selftests/tc-testing/plugin-lib/rootPlugin.py19
-rw-r--r--tools/testing/selftests/tc-testing/plugin-lib/valgrindPlugin.py142
-rw-r--r--tools/testing/selftests/tc-testing/plugins/__init__.py0
-rw-r--r--tools/testing/selftests/tc-testing/tc-tests/actions/bpf.json289
-rw-r--r--tools/testing/selftests/tc-testing/tc-tests/actions/connmark.json291
-rw-r--r--tools/testing/selftests/tc-testing/tc-tests/actions/csum.json410
-rw-r--r--tools/testing/selftests/tc-testing/tc-tests/actions/gact.json71
-rw-r--r--tools/testing/selftests/tc-testing/tc-tests/actions/mirred.json192
-rw-r--r--tools/testing/selftests/tc-testing/tc-tests/actions/police.json144
-rw-r--r--tools/testing/selftests/tc-testing/tc-tests/actions/skbedit.json168
-rw-r--r--tools/testing/selftests/tc-testing/tc-tests/actions/skbmod.json24
-rw-r--r--tools/testing/selftests/tc-testing/tc-tests/actions/vlan.json410
-rwxr-xr-xtools/testing/selftests/tc-testing/tdc.py576
-rwxr-xr-xtools/testing/selftests/tc-testing/tdc_batch.py8
-rw-r--r--tools/testing/selftests/tc-testing/tdc_helper.py15
85 files changed, 10107 insertions, 942 deletions
diff --git a/tools/bpf/Makefile b/tools/bpf/Makefile
index c8ec0ae16bf0..1ea545965ee3 100644
--- a/tools/bpf/Makefile
+++ b/tools/bpf/Makefile
@@ -1,19 +1,28 @@
1# SPDX-License-Identifier: GPL-2.0 1# SPDX-License-Identifier: GPL-2.0
2prefix = /usr 2include ../scripts/Makefile.include
3
4prefix ?= /usr/local
3 5
4CC = gcc 6CC = gcc
5LEX = flex 7LEX = flex
6YACC = bison 8YACC = bison
7MAKE = make 9MAKE = make
10INSTALL ?= install
8 11
9CFLAGS += -Wall -O2 12CFLAGS += -Wall -O2
10CFLAGS += -D__EXPORTED_HEADERS__ -I../../include/uapi -I../../include 13CFLAGS += -D__EXPORTED_HEADERS__ -I$(srctree)/include/uapi -I$(srctree)/include
11 14
12ifeq ($(srctree),) 15ifeq ($(srctree),)
13srctree := $(patsubst %/,%,$(dir $(CURDIR))) 16srctree := $(patsubst %/,%,$(dir $(CURDIR)))
14srctree := $(patsubst %/,%,$(dir $(srctree))) 17srctree := $(patsubst %/,%,$(dir $(srctree)))
15endif 18endif
16 19
20ifeq ($(V),1)
21 Q =
22else
23 Q = @
24endif
25
17FEATURE_USER = .bpf 26FEATURE_USER = .bpf
18FEATURE_TESTS = libbfd disassembler-four-args 27FEATURE_TESTS = libbfd disassembler-four-args
19FEATURE_DISPLAY = libbfd disassembler-four-args 28FEATURE_DISPLAY = libbfd disassembler-four-args
@@ -38,40 +47,59 @@ ifeq ($(feature-disassembler-four-args), 1)
38CFLAGS += -DDISASM_FOUR_ARGS_SIGNATURE 47CFLAGS += -DDISASM_FOUR_ARGS_SIGNATURE
39endif 48endif
40 49
41%.yacc.c: %.y 50$(OUTPUT)%.yacc.c: $(srctree)/tools/bpf/%.y
42 $(YACC) -o $@ -d $< 51 $(QUIET_BISON)$(YACC) -o $@ -d $<
43 52
44%.lex.c: %.l 53$(OUTPUT)%.lex.c: $(srctree)/tools/bpf/%.l
45 $(LEX) -o $@ $< 54 $(QUIET_FLEX)$(LEX) -o $@ $<
46 55
47all: bpf_jit_disasm bpf_dbg bpf_asm bpftool 56$(OUTPUT)%.o: $(srctree)/tools/bpf/%.c
57 $(QUIET_CC)$(COMPILE.c) -o $@ $<
48 58
49bpf_jit_disasm : CFLAGS += -DPACKAGE='bpf_jit_disasm' 59$(OUTPUT)%.yacc.o: $(OUTPUT)%.yacc.c
50bpf_jit_disasm : LDLIBS = -lopcodes -lbfd -ldl 60 $(QUIET_CC)$(COMPILE.c) -o $@ $<
51bpf_jit_disasm : bpf_jit_disasm.o 61$(OUTPUT)%.lex.o: $(OUTPUT)%.lex.c
62 $(QUIET_CC)$(COMPILE.c) -o $@ $<
52 63
53bpf_dbg : LDLIBS = -lreadline 64PROGS = $(OUTPUT)bpf_jit_disasm $(OUTPUT)bpf_dbg $(OUTPUT)bpf_asm
54bpf_dbg : bpf_dbg.o
55 65
56bpf_asm : LDLIBS = 66all: $(PROGS) bpftool
57bpf_asm : bpf_asm.o bpf_exp.yacc.o bpf_exp.lex.o
58bpf_exp.lex.o : bpf_exp.yacc.c
59 67
60clean: bpftool_clean 68$(OUTPUT)bpf_jit_disasm: CFLAGS += -DPACKAGE='bpf_jit_disasm'
61 rm -rf *.o bpf_jit_disasm bpf_dbg bpf_asm bpf_exp.yacc.* bpf_exp.lex.* 69$(OUTPUT)bpf_jit_disasm: $(OUTPUT)bpf_jit_disasm.o
70 $(QUIET_LINK)$(CC) $(CFLAGS) -o $@ $^ -lopcodes -lbfd -ldl
62 71
63install: bpftool_install 72$(OUTPUT)bpf_dbg: $(OUTPUT)bpf_dbg.o
64 install bpf_jit_disasm $(prefix)/bin/bpf_jit_disasm 73 $(QUIET_LINK)$(CC) $(CFLAGS) -o $@ $^ -lreadline
65 install bpf_dbg $(prefix)/bin/bpf_dbg 74
66 install bpf_asm $(prefix)/bin/bpf_asm 75$(OUTPUT)bpf_asm: $(OUTPUT)bpf_asm.o $(OUTPUT)bpf_exp.yacc.o $(OUTPUT)bpf_exp.lex.o
76 $(QUIET_LINK)$(CC) $(CFLAGS) -o $@ $^
77
78$(OUTPUT)bpf_exp.lex.c: $(OUTPUT)bpf_exp.yacc.c
79
80clean: bpftool_clean
81 $(call QUIET_CLEAN, bpf-progs)
82 $(Q)rm -rf $(OUTPUT)*.o $(OUTPUT)bpf_jit_disasm $(OUTPUT)bpf_dbg \
83 $(OUTPUT)bpf_asm $(OUTPUT)bpf_exp.yacc.* $(OUTPUT)bpf_exp.lex.*
84 $(call QUIET_CLEAN, core-gen)
85 $(Q)rm -f $(OUTPUT)FEATURE-DUMP.bpf
86
87install: $(PROGS) bpftool_install
88 $(call QUIET_INSTALL, bpf_jit_disasm)
89 $(Q)$(INSTALL) -m 0755 -d $(DESTDIR)$(prefix)/bin
90 $(Q)$(INSTALL) $(OUTPUT)bpf_jit_disasm $(DESTDIR)$(prefix)/bin/bpf_jit_disasm
91 $(call QUIET_INSTALL, bpf_dbg)
92 $(Q)$(INSTALL) $(OUTPUT)bpf_dbg $(DESTDIR)$(prefix)/bin/bpf_dbg
93 $(call QUIET_INSTALL, bpf_asm)
94 $(Q)$(INSTALL) $(OUTPUT)bpf_asm $(DESTDIR)$(prefix)/bin/bpf_asm
67 95
68bpftool: 96bpftool:
69 $(MAKE) -C bpftool 97 $(call descend,bpftool)
70 98
71bpftool_install: 99bpftool_install:
72 $(MAKE) -C bpftool install 100 $(call descend,bpftool,install)
73 101
74bpftool_clean: 102bpftool_clean:
75 $(MAKE) -C bpftool clean 103 $(call descend,bpftool,clean)
76 104
77.PHONY: bpftool FORCE 105.PHONY: all install clean bpftool bpftool_install bpftool_clean
diff --git a/tools/bpf/bpftool/Documentation/bpftool-prog.rst b/tools/bpf/bpftool/Documentation/bpftool-prog.rst
index e4ceee7f2dff..67ca6c69376c 100644
--- a/tools/bpf/bpftool/Documentation/bpftool-prog.rst
+++ b/tools/bpf/bpftool/Documentation/bpftool-prog.rst
@@ -21,7 +21,7 @@ MAP COMMANDS
21============= 21=============
22 22
23| **bpftool** **prog { show | list }** [*PROG*] 23| **bpftool** **prog { show | list }** [*PROG*]
24| **bpftool** **prog dump xlated** *PROG* [{**file** *FILE* | **opcodes**}] 24| **bpftool** **prog dump xlated** *PROG* [{**file** *FILE* | **opcodes** | **visual**}]
25| **bpftool** **prog dump jited** *PROG* [{**file** *FILE* | **opcodes**}] 25| **bpftool** **prog dump jited** *PROG* [{**file** *FILE* | **opcodes**}]
26| **bpftool** **prog pin** *PROG* *FILE* 26| **bpftool** **prog pin** *PROG* *FILE*
27| **bpftool** **prog load** *OBJ* *FILE* 27| **bpftool** **prog load** *OBJ* *FILE*
@@ -39,12 +39,18 @@ DESCRIPTION
39 Output will start with program ID followed by program type and 39 Output will start with program ID followed by program type and
40 zero or more named attributes (depending on kernel version). 40 zero or more named attributes (depending on kernel version).
41 41
42 **bpftool prog dump xlated** *PROG* [{ **file** *FILE* | **opcodes** }] 42 **bpftool prog dump xlated** *PROG* [{ **file** *FILE* | **opcodes** | **visual** }]
43 Dump eBPF instructions of the program from the kernel. 43 Dump eBPF instructions of the program from the kernel. By
44 If *FILE* is specified image will be written to a file, 44 default, eBPF will be disassembled and printed to standard
45 otherwise it will be disassembled and printed to stdout. 45 output in human-readable format. In this case, **opcodes**
46 controls if raw opcodes should be printed as well.
46 47
47 **opcodes** controls if raw opcodes will be printed. 48 If **file** is specified, the binary image will instead be
49 written to *FILE*.
50
51 If **visual** is specified, control flow graph (CFG) will be
52 built instead, and eBPF instructions will be presented with
53 CFG in DOT format, on standard output.
48 54
49 **bpftool prog dump jited** *PROG* [{ **file** *FILE* | **opcodes** }] 55 **bpftool prog dump jited** *PROG* [{ **file** *FILE* | **opcodes** }]
50 Dump jited image (host machine code) of the program. 56 Dump jited image (host machine code) of the program.
diff --git a/tools/bpf/bpftool/Makefile b/tools/bpf/bpftool/Makefile
index 26901ec87361..4e69782c4a79 100644
--- a/tools/bpf/bpftool/Makefile
+++ b/tools/bpf/bpftool/Makefile
@@ -38,7 +38,7 @@ bash_compdir ?= /usr/share/bash-completion/completions
38CC = gcc 38CC = gcc
39 39
40CFLAGS += -O2 40CFLAGS += -O2
41CFLAGS += -W -Wall -Wextra -Wno-unused-parameter -Wshadow 41CFLAGS += -W -Wall -Wextra -Wno-unused-parameter -Wshadow -Wno-missing-field-initializers
42CFLAGS += -DPACKAGE='"bpftool"' -D__EXPORTED_HEADERS__ -I$(srctree)/tools/include/uapi -I$(srctree)/tools/include -I$(srctree)/tools/lib/bpf -I$(srctree)/kernel/bpf/ 42CFLAGS += -DPACKAGE='"bpftool"' -D__EXPORTED_HEADERS__ -I$(srctree)/tools/include/uapi -I$(srctree)/tools/include -I$(srctree)/tools/lib/bpf -I$(srctree)/kernel/bpf/
43CFLAGS += -DBPFTOOL_VERSION='"$(BPFTOOL_VERSION)"' 43CFLAGS += -DBPFTOOL_VERSION='"$(BPFTOOL_VERSION)"'
44LIBS = -lelf -lbfd -lopcodes $(LIBBPF) 44LIBS = -lelf -lbfd -lopcodes $(LIBBPF)
@@ -70,7 +70,7 @@ ifeq ($(feature-disassembler-four-args), 1)
70CFLAGS += -DDISASM_FOUR_ARGS_SIGNATURE 70CFLAGS += -DDISASM_FOUR_ARGS_SIGNATURE
71endif 71endif
72 72
73include $(wildcard *.d) 73include $(wildcard $(OUTPUT)*.d)
74 74
75all: $(OUTPUT)bpftool 75all: $(OUTPUT)bpftool
76 76
@@ -89,6 +89,8 @@ $(OUTPUT)%.o: %.c
89clean: $(LIBBPF)-clean 89clean: $(LIBBPF)-clean
90 $(call QUIET_CLEAN, bpftool) 90 $(call QUIET_CLEAN, bpftool)
91 $(Q)$(RM) $(OUTPUT)bpftool $(OUTPUT)*.o $(OUTPUT)*.d 91 $(Q)$(RM) $(OUTPUT)bpftool $(OUTPUT)*.o $(OUTPUT)*.d
92 $(call QUIET_CLEAN, core-gen)
93 $(Q)$(RM) $(OUTPUT)FEATURE-DUMP.bpftool
92 94
93install: $(OUTPUT)bpftool 95install: $(OUTPUT)bpftool
94 $(call QUIET_INSTALL, bpftool) 96 $(call QUIET_INSTALL, bpftool)
diff --git a/tools/bpf/bpftool/bash-completion/bpftool b/tools/bpf/bpftool/bash-completion/bpftool
index 08719c54a614..490811b45fa7 100644
--- a/tools/bpf/bpftool/bash-completion/bpftool
+++ b/tools/bpf/bpftool/bash-completion/bpftool
@@ -147,7 +147,7 @@ _bpftool()
147 147
148 # Deal with simplest keywords 148 # Deal with simplest keywords
149 case $prev in 149 case $prev in
150 help|key|opcodes) 150 help|key|opcodes|visual)
151 return 0 151 return 0
152 ;; 152 ;;
153 tag) 153 tag)
@@ -223,11 +223,16 @@ _bpftool()
223 return 0 223 return 0
224 ;; 224 ;;
225 *) 225 *)
226 _bpftool_once_attr 'file' 226 _bpftool_once_attr 'file'
227 if _bpftool_search_list 'xlated'; then
228 COMPREPLY+=( $( compgen -W 'opcodes visual' -- \
229 "$cur" ) )
230 else
227 COMPREPLY+=( $( compgen -W 'opcodes' -- \ 231 COMPREPLY+=( $( compgen -W 'opcodes' -- \
228 "$cur" ) ) 232 "$cur" ) )
229 return 0 233 fi
230 ;; 234 return 0
235 ;;
231 esac 236 esac
232 ;; 237 ;;
233 pin) 238 pin)
diff --git a/tools/bpf/bpftool/cfg.c b/tools/bpf/bpftool/cfg.c
new file mode 100644
index 000000000000..f30b3a4a840b
--- /dev/null
+++ b/tools/bpf/bpftool/cfg.c
@@ -0,0 +1,514 @@
1// SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
2/*
3 * Copyright (C) 2018 Netronome Systems, Inc.
4 *
5 * This software is dual 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 or the BSD 2-Clause License provided below. You have the
8 * option to license this software under the complete terms of either license.
9 *
10 * The BSD 2-Clause License:
11 *
12 * Redistribution and use in source and binary forms, with or
13 * without modification, are permitted provided that the following
14 * conditions are met:
15 *
16 * 1. Redistributions of source code must retain the above
17 * copyright notice, this list of conditions and the following
18 * disclaimer.
19 *
20 * 2. Redistributions in binary form must reproduce the above
21 * copyright notice, this list of conditions and the following
22 * disclaimer in the documentation and/or other materials
23 * provided with the distribution.
24 *
25 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
26 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
27 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
28 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
29 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
30 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
31 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
32 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
33 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
34 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
35 * POSSIBILITY OF SUCH DAMAGE.
36 */
37
38#include <linux/list.h>
39#include <stdlib.h>
40#include <string.h>
41
42#include "cfg.h"
43#include "main.h"
44#include "xlated_dumper.h"
45
46struct cfg {
47 struct list_head funcs;
48 int func_num;
49};
50
51struct func_node {
52 struct list_head l;
53 struct list_head bbs;
54 struct bpf_insn *start;
55 struct bpf_insn *end;
56 int idx;
57 int bb_num;
58};
59
60struct bb_node {
61 struct list_head l;
62 struct list_head e_prevs;
63 struct list_head e_succs;
64 struct bpf_insn *head;
65 struct bpf_insn *tail;
66 int idx;
67};
68
69#define EDGE_FLAG_EMPTY 0x0
70#define EDGE_FLAG_FALLTHROUGH 0x1
71#define EDGE_FLAG_JUMP 0x2
72struct edge_node {
73 struct list_head l;
74 struct bb_node *src;
75 struct bb_node *dst;
76 int flags;
77};
78
79#define ENTRY_BLOCK_INDEX 0
80#define EXIT_BLOCK_INDEX 1
81#define NUM_FIXED_BLOCKS 2
82#define func_prev(func) list_prev_entry(func, l)
83#define func_next(func) list_next_entry(func, l)
84#define bb_prev(bb) list_prev_entry(bb, l)
85#define bb_next(bb) list_next_entry(bb, l)
86#define entry_bb(func) func_first_bb(func)
87#define exit_bb(func) func_last_bb(func)
88#define cfg_first_func(cfg) \
89 list_first_entry(&cfg->funcs, struct func_node, l)
90#define cfg_last_func(cfg) \
91 list_last_entry(&cfg->funcs, struct func_node, l)
92#define func_first_bb(func) \
93 list_first_entry(&func->bbs, struct bb_node, l)
94#define func_last_bb(func) \
95 list_last_entry(&func->bbs, struct bb_node, l)
96
97static struct func_node *cfg_append_func(struct cfg *cfg, struct bpf_insn *insn)
98{
99 struct func_node *new_func, *func;
100
101 list_for_each_entry(func, &cfg->funcs, l) {
102 if (func->start == insn)
103 return func;
104 else if (func->start > insn)
105 break;
106 }
107
108 func = func_prev(func);
109 new_func = calloc(1, sizeof(*new_func));
110 if (!new_func) {
111 p_err("OOM when allocating FUNC node");
112 return NULL;
113 }
114 new_func->start = insn;
115 new_func->idx = cfg->func_num;
116 list_add(&new_func->l, &func->l);
117 cfg->func_num++;
118
119 return new_func;
120}
121
122static struct bb_node *func_append_bb(struct func_node *func,
123 struct bpf_insn *insn)
124{
125 struct bb_node *new_bb, *bb;
126
127 list_for_each_entry(bb, &func->bbs, l) {
128 if (bb->head == insn)
129 return bb;
130 else if (bb->head > insn)
131 break;
132 }
133
134 bb = bb_prev(bb);
135 new_bb = calloc(1, sizeof(*new_bb));
136 if (!new_bb) {
137 p_err("OOM when allocating BB node");
138 return NULL;
139 }
140 new_bb->head = insn;
141 INIT_LIST_HEAD(&new_bb->e_prevs);
142 INIT_LIST_HEAD(&new_bb->e_succs);
143 list_add(&new_bb->l, &bb->l);
144
145 return new_bb;
146}
147
148static struct bb_node *func_insert_dummy_bb(struct list_head *after)
149{
150 struct bb_node *bb;
151
152 bb = calloc(1, sizeof(*bb));
153 if (!bb) {
154 p_err("OOM when allocating BB node");
155 return NULL;
156 }
157
158 INIT_LIST_HEAD(&bb->e_prevs);
159 INIT_LIST_HEAD(&bb->e_succs);
160 list_add(&bb->l, after);
161
162 return bb;
163}
164
165static bool cfg_partition_funcs(struct cfg *cfg, struct bpf_insn *cur,
166 struct bpf_insn *end)
167{
168 struct func_node *func, *last_func;
169
170 func = cfg_append_func(cfg, cur);
171 if (!func)
172 return true;
173
174 for (; cur < end; cur++) {
175 if (cur->code != (BPF_JMP | BPF_CALL))
176 continue;
177 if (cur->src_reg != BPF_PSEUDO_CALL)
178 continue;
179 func = cfg_append_func(cfg, cur + cur->off + 1);
180 if (!func)
181 return true;
182 }
183
184 last_func = cfg_last_func(cfg);
185 last_func->end = end - 1;
186 func = cfg_first_func(cfg);
187 list_for_each_entry_from(func, &last_func->l, l) {
188 func->end = func_next(func)->start - 1;
189 }
190
191 return false;
192}
193
194static bool func_partition_bb_head(struct func_node *func)
195{
196 struct bpf_insn *cur, *end;
197 struct bb_node *bb;
198
199 cur = func->start;
200 end = func->end;
201 INIT_LIST_HEAD(&func->bbs);
202 bb = func_append_bb(func, cur);
203 if (!bb)
204 return true;
205
206 for (; cur <= end; cur++) {
207 if (BPF_CLASS(cur->code) == BPF_JMP) {
208 u8 opcode = BPF_OP(cur->code);
209
210 if (opcode == BPF_EXIT || opcode == BPF_CALL)
211 continue;
212
213 bb = func_append_bb(func, cur + cur->off + 1);
214 if (!bb)
215 return true;
216
217 if (opcode != BPF_JA) {
218 bb = func_append_bb(func, cur + 1);
219 if (!bb)
220 return true;
221 }
222 }
223 }
224
225 return false;
226}
227
228static void func_partition_bb_tail(struct func_node *func)
229{
230 unsigned int bb_idx = NUM_FIXED_BLOCKS;
231 struct bb_node *bb, *last;
232
233 last = func_last_bb(func);
234 last->tail = func->end;
235 bb = func_first_bb(func);
236 list_for_each_entry_from(bb, &last->l, l) {
237 bb->tail = bb_next(bb)->head - 1;
238 bb->idx = bb_idx++;
239 }
240
241 last->idx = bb_idx++;
242 func->bb_num = bb_idx;
243}
244
245static bool func_add_special_bb(struct func_node *func)
246{
247 struct bb_node *bb;
248
249 bb = func_insert_dummy_bb(&func->bbs);
250 if (!bb)
251 return true;
252 bb->idx = ENTRY_BLOCK_INDEX;
253
254 bb = func_insert_dummy_bb(&func_last_bb(func)->l);
255 if (!bb)
256 return true;
257 bb->idx = EXIT_BLOCK_INDEX;
258
259 return false;
260}
261
262static bool func_partition_bb(struct func_node *func)
263{
264 if (func_partition_bb_head(func))
265 return true;
266
267 func_partition_bb_tail(func);
268
269 return false;
270}
271
272static struct bb_node *func_search_bb_with_head(struct func_node *func,
273 struct bpf_insn *insn)
274{
275 struct bb_node *bb;
276
277 list_for_each_entry(bb, &func->bbs, l) {
278 if (bb->head == insn)
279 return bb;
280 }
281
282 return NULL;
283}
284
285static struct edge_node *new_edge(struct bb_node *src, struct bb_node *dst,
286 int flags)
287{
288 struct edge_node *e;
289
290 e = calloc(1, sizeof(*e));
291 if (!e) {
292 p_err("OOM when allocating edge node");
293 return NULL;
294 }
295
296 if (src)
297 e->src = src;
298 if (dst)
299 e->dst = dst;
300
301 e->flags |= flags;
302
303 return e;
304}
305
306static bool func_add_bb_edges(struct func_node *func)
307{
308 struct bpf_insn *insn;
309 struct edge_node *e;
310 struct bb_node *bb;
311
312 bb = entry_bb(func);
313 e = new_edge(bb, bb_next(bb), EDGE_FLAG_FALLTHROUGH);
314 if (!e)
315 return true;
316 list_add_tail(&e->l, &bb->e_succs);
317
318 bb = exit_bb(func);
319 e = new_edge(bb_prev(bb), bb, EDGE_FLAG_FALLTHROUGH);
320 if (!e)
321 return true;
322 list_add_tail(&e->l, &bb->e_prevs);
323
324 bb = entry_bb(func);
325 bb = bb_next(bb);
326 list_for_each_entry_from(bb, &exit_bb(func)->l, l) {
327 e = new_edge(bb, NULL, EDGE_FLAG_EMPTY);
328 if (!e)
329 return true;
330 e->src = bb;
331
332 insn = bb->tail;
333 if (BPF_CLASS(insn->code) != BPF_JMP ||
334 BPF_OP(insn->code) == BPF_EXIT) {
335 e->dst = bb_next(bb);
336 e->flags |= EDGE_FLAG_FALLTHROUGH;
337 list_add_tail(&e->l, &bb->e_succs);
338 continue;
339 } else if (BPF_OP(insn->code) == BPF_JA) {
340 e->dst = func_search_bb_with_head(func,
341 insn + insn->off + 1);
342 e->flags |= EDGE_FLAG_JUMP;
343 list_add_tail(&e->l, &bb->e_succs);
344 continue;
345 }
346
347 e->dst = bb_next(bb);
348 e->flags |= EDGE_FLAG_FALLTHROUGH;
349 list_add_tail(&e->l, &bb->e_succs);
350
351 e = new_edge(bb, NULL, EDGE_FLAG_JUMP);
352 if (!e)
353 return true;
354 e->src = bb;
355 e->dst = func_search_bb_with_head(func, insn + insn->off + 1);
356 list_add_tail(&e->l, &bb->e_succs);
357 }
358
359 return false;
360}
361
362static bool cfg_build(struct cfg *cfg, struct bpf_insn *insn, unsigned int len)
363{
364 int cnt = len / sizeof(*insn);
365 struct func_node *func;
366
367 INIT_LIST_HEAD(&cfg->funcs);
368
369 if (cfg_partition_funcs(cfg, insn, insn + cnt))
370 return true;
371
372 list_for_each_entry(func, &cfg->funcs, l) {
373 if (func_partition_bb(func) || func_add_special_bb(func))
374 return true;
375
376 if (func_add_bb_edges(func))
377 return true;
378 }
379
380 return false;
381}
382
383static void cfg_destroy(struct cfg *cfg)
384{
385 struct func_node *func, *func2;
386
387 list_for_each_entry_safe(func, func2, &cfg->funcs, l) {
388 struct bb_node *bb, *bb2;
389
390 list_for_each_entry_safe(bb, bb2, &func->bbs, l) {
391 struct edge_node *e, *e2;
392
393 list_for_each_entry_safe(e, e2, &bb->e_prevs, l) {
394 list_del(&e->l);
395 free(e);
396 }
397
398 list_for_each_entry_safe(e, e2, &bb->e_succs, l) {
399 list_del(&e->l);
400 free(e);
401 }
402
403 list_del(&bb->l);
404 free(bb);
405 }
406
407 list_del(&func->l);
408 free(func);
409 }
410}
411
412static void draw_bb_node(struct func_node *func, struct bb_node *bb)
413{
414 const char *shape;
415
416 if (bb->idx == ENTRY_BLOCK_INDEX || bb->idx == EXIT_BLOCK_INDEX)
417 shape = "Mdiamond";
418 else
419 shape = "record";
420
421 printf("\tfn_%d_bb_%d [shape=%s,style=filled,label=\"",
422 func->idx, bb->idx, shape);
423
424 if (bb->idx == ENTRY_BLOCK_INDEX) {
425 printf("ENTRY");
426 } else if (bb->idx == EXIT_BLOCK_INDEX) {
427 printf("EXIT");
428 } else {
429 unsigned int start_idx;
430 struct dump_data dd = {};
431
432 printf("{");
433 kernel_syms_load(&dd);
434 start_idx = bb->head - func->start;
435 dump_xlated_for_graph(&dd, bb->head, bb->tail, start_idx);
436 kernel_syms_destroy(&dd);
437 printf("}");
438 }
439
440 printf("\"];\n\n");
441}
442
443static void draw_bb_succ_edges(struct func_node *func, struct bb_node *bb)
444{
445 const char *style = "\"solid,bold\"";
446 const char *color = "black";
447 int func_idx = func->idx;
448 struct edge_node *e;
449 int weight = 10;
450
451 if (list_empty(&bb->e_succs))
452 return;
453
454 list_for_each_entry(e, &bb->e_succs, l) {
455 printf("\tfn_%d_bb_%d:s -> fn_%d_bb_%d:n [style=%s, color=%s, weight=%d, constraint=true",
456 func_idx, e->src->idx, func_idx, e->dst->idx,
457 style, color, weight);
458 printf("];\n");
459 }
460}
461
462static void func_output_bb_def(struct func_node *func)
463{
464 struct bb_node *bb;
465
466 list_for_each_entry(bb, &func->bbs, l) {
467 draw_bb_node(func, bb);
468 }
469}
470
471static void func_output_edges(struct func_node *func)
472{
473 int func_idx = func->idx;
474 struct bb_node *bb;
475
476 list_for_each_entry(bb, &func->bbs, l) {
477 draw_bb_succ_edges(func, bb);
478 }
479
480 /* Add an invisible edge from ENTRY to EXIT, this is to
481 * improve the graph layout.
482 */
483 printf("\tfn_%d_bb_%d:s -> fn_%d_bb_%d:n [style=\"invis\", constraint=true];\n",
484 func_idx, ENTRY_BLOCK_INDEX, func_idx, EXIT_BLOCK_INDEX);
485}
486
487static void cfg_dump(struct cfg *cfg)
488{
489 struct func_node *func;
490
491 printf("digraph \"DOT graph for eBPF program\" {\n");
492 list_for_each_entry(func, &cfg->funcs, l) {
493 printf("subgraph \"cluster_%d\" {\n\tstyle=\"dashed\";\n\tcolor=\"black\";\n\tlabel=\"func_%d ()\";\n",
494 func->idx, func->idx);
495 func_output_bb_def(func);
496 func_output_edges(func);
497 printf("}\n");
498 }
499 printf("}\n");
500}
501
502void dump_xlated_cfg(void *buf, unsigned int len)
503{
504 struct bpf_insn *insn = buf;
505 struct cfg cfg;
506
507 memset(&cfg, 0, sizeof(cfg));
508 if (cfg_build(&cfg, insn, len))
509 return;
510
511 cfg_dump(&cfg);
512
513 cfg_destroy(&cfg);
514}
diff --git a/tools/bpf/bpftool/cfg.h b/tools/bpf/bpftool/cfg.h
new file mode 100644
index 000000000000..2cc9bd990b13
--- /dev/null
+++ b/tools/bpf/bpftool/cfg.h
@@ -0,0 +1,43 @@
1// SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
2/*
3 * Copyright (C) 2018 Netronome Systems, Inc.
4 *
5 * This software is dual 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 or the BSD 2-Clause License provided below. You have the
8 * option to license this software under the complete terms of either license.
9 *
10 * The BSD 2-Clause License:
11 *
12 * Redistribution and use in source and binary forms, with or
13 * without modification, are permitted provided that the following
14 * conditions are met:
15 *
16 * 1. Redistributions of source code must retain the above
17 * copyright notice, this list of conditions and the following
18 * disclaimer.
19 *
20 * 2. Redistributions in binary form must reproduce the above
21 * copyright notice, this list of conditions and the following
22 * disclaimer in the documentation and/or other materials
23 * provided with the distribution.
24 *
25 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
26 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
27 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
28 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
29 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
30 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
31 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
32 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
33 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
34 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
35 * POSSIBILITY OF SUCH DAMAGE.
36 */
37
38#ifndef __BPF_TOOL_CFG_H
39#define __BPF_TOOL_CFG_H
40
41void dump_xlated_cfg(void *buf, unsigned int len);
42
43#endif /* __BPF_TOOL_CFG_H */
diff --git a/tools/bpf/bpftool/main.c b/tools/bpf/bpftool/main.c
index 185acfa229b5..1ec852d21d44 100644
--- a/tools/bpf/bpftool/main.c
+++ b/tools/bpf/bpftool/main.c
@@ -46,6 +46,9 @@
46 46
47#include "main.h" 47#include "main.h"
48 48
49#define BATCH_LINE_LEN_MAX 65536
50#define BATCH_ARG_NB_MAX 4096
51
49const char *bin_name; 52const char *bin_name;
50static int last_argc; 53static int last_argc;
51static char **last_argv; 54static char **last_argv;
@@ -157,6 +160,54 @@ void fprint_hex(FILE *f, void *arg, unsigned int n, const char *sep)
157 } 160 }
158} 161}
159 162
163/* Split command line into argument vector. */
164static int make_args(char *line, char *n_argv[], int maxargs, int cmd_nb)
165{
166 static const char ws[] = " \t\r\n";
167 char *cp = line;
168 int n_argc = 0;
169
170 while (*cp) {
171 /* Skip leading whitespace. */
172 cp += strspn(cp, ws);
173
174 if (*cp == '\0')
175 break;
176
177 if (n_argc >= (maxargs - 1)) {
178 p_err("too many arguments to command %d", cmd_nb);
179 return -1;
180 }
181
182 /* Word begins with quote. */
183 if (*cp == '\'' || *cp == '"') {
184 char quote = *cp++;
185
186 n_argv[n_argc++] = cp;
187 /* Find ending quote. */
188 cp = strchr(cp, quote);
189 if (!cp) {
190 p_err("unterminated quoted string in command %d",
191 cmd_nb);
192 return -1;
193 }
194 } else {
195 n_argv[n_argc++] = cp;
196
197 /* Find end of word. */
198 cp += strcspn(cp, ws);
199 if (*cp == '\0')
200 break;
201 }
202
203 /* Separate words. */
204 *cp++ = 0;
205 }
206 n_argv[n_argc] = NULL;
207
208 return n_argc;
209}
210
160static int do_batch(int argc, char **argv); 211static int do_batch(int argc, char **argv);
161 212
162static const struct cmd cmds[] = { 213static const struct cmd cmds[] = {
@@ -171,11 +222,12 @@ static const struct cmd cmds[] = {
171 222
172static int do_batch(int argc, char **argv) 223static int do_batch(int argc, char **argv)
173{ 224{
225 char buf[BATCH_LINE_LEN_MAX], contline[BATCH_LINE_LEN_MAX];
226 char *n_argv[BATCH_ARG_NB_MAX];
174 unsigned int lines = 0; 227 unsigned int lines = 0;
175 char *n_argv[4096];
176 char buf[65536];
177 int n_argc; 228 int n_argc;
178 FILE *fp; 229 FILE *fp;
230 char *cp;
179 int err; 231 int err;
180 int i; 232 int i;
181 233
@@ -191,7 +243,10 @@ static int do_batch(int argc, char **argv)
191 } 243 }
192 NEXT_ARG(); 244 NEXT_ARG();
193 245
194 fp = fopen(*argv, "r"); 246 if (!strcmp(*argv, "-"))
247 fp = stdin;
248 else
249 fp = fopen(*argv, "r");
195 if (!fp) { 250 if (!fp) {
196 p_err("Can't open file (%s): %s", *argv, strerror(errno)); 251 p_err("Can't open file (%s): %s", *argv, strerror(errno));
197 return -1; 252 return -1;
@@ -200,27 +255,45 @@ static int do_batch(int argc, char **argv)
200 if (json_output) 255 if (json_output)
201 jsonw_start_array(json_wtr); 256 jsonw_start_array(json_wtr);
202 while (fgets(buf, sizeof(buf), fp)) { 257 while (fgets(buf, sizeof(buf), fp)) {
258 cp = strchr(buf, '#');
259 if (cp)
260 *cp = '\0';
261
203 if (strlen(buf) == sizeof(buf) - 1) { 262 if (strlen(buf) == sizeof(buf) - 1) {
204 errno = E2BIG; 263 errno = E2BIG;
205 break; 264 break;
206 } 265 }
207 266
208 n_argc = 0; 267 /* Append continuation lines if any (coming after a line ending
209 n_argv[n_argc] = strtok(buf, " \t\n"); 268 * with '\' in the batch file).
210 269 */
211 while (n_argv[n_argc]) { 270 while ((cp = strstr(buf, "\\\n")) != NULL) {
212 n_argc++; 271 if (!fgets(contline, sizeof(contline), fp) ||
213 if (n_argc == ARRAY_SIZE(n_argv)) { 272 strlen(contline) == 0) {
214 p_err("line %d has too many arguments, skip", 273 p_err("missing continuation line on command %d",
215 lines); 274 lines);
216 n_argc = 0; 275 err = -1;
217 break; 276 goto err_close;
277 }
278
279 cp = strchr(contline, '#');
280 if (cp)
281 *cp = '\0';
282
283 if (strlen(buf) + strlen(contline) + 1 > sizeof(buf)) {
284 p_err("command %d is too long", lines);
285 err = -1;
286 goto err_close;
218 } 287 }
219 n_argv[n_argc] = strtok(NULL, " \t\n"); 288 buf[strlen(buf) - 2] = '\0';
289 strcat(buf, contline);
220 } 290 }
221 291
292 n_argc = make_args(buf, n_argv, BATCH_ARG_NB_MAX, lines);
222 if (!n_argc) 293 if (!n_argc)
223 continue; 294 continue;
295 if (n_argc < 0)
296 goto err_close;
224 297
225 if (json_output) { 298 if (json_output) {
226 jsonw_start_object(json_wtr); 299 jsonw_start_object(json_wtr);
@@ -247,11 +320,12 @@ static int do_batch(int argc, char **argv)
247 p_err("reading batch file failed: %s", strerror(errno)); 320 p_err("reading batch file failed: %s", strerror(errno));
248 err = -1; 321 err = -1;
249 } else { 322 } else {
250 p_info("processed %d lines", lines); 323 p_info("processed %d commands", lines);
251 err = 0; 324 err = 0;
252 } 325 }
253err_close: 326err_close:
254 fclose(fp); 327 if (fp != stdin)
328 fclose(fp);
255 329
256 if (json_output) 330 if (json_output)
257 jsonw_end_array(json_wtr); 331 jsonw_end_array(json_wtr);
diff --git a/tools/bpf/bpftool/prog.c b/tools/bpf/bpftool/prog.c
index e549e329be82..f7a810897eac 100644
--- a/tools/bpf/bpftool/prog.c
+++ b/tools/bpf/bpftool/prog.c
@@ -47,8 +47,9 @@
47#include <bpf.h> 47#include <bpf.h>
48#include <libbpf.h> 48#include <libbpf.h>
49 49
50#include "cfg.h"
50#include "main.h" 51#include "main.h"
51#include "disasm.h" 52#include "xlated_dumper.h"
52 53
53static const char * const prog_type_name[] = { 54static const char * const prog_type_name[] = {
54 [BPF_PROG_TYPE_UNSPEC] = "unspec", 55 [BPF_PROG_TYPE_UNSPEC] = "unspec",
@@ -407,259 +408,6 @@ static int do_show(int argc, char **argv)
407 return err; 408 return err;
408} 409}
409 410
410#define SYM_MAX_NAME 256
411
412struct kernel_sym {
413 unsigned long address;
414 char name[SYM_MAX_NAME];
415};
416
417struct dump_data {
418 unsigned long address_call_base;
419 struct kernel_sym *sym_mapping;
420 __u32 sym_count;
421 char scratch_buff[SYM_MAX_NAME];
422};
423
424static int kernel_syms_cmp(const void *sym_a, const void *sym_b)
425{
426 return ((struct kernel_sym *)sym_a)->address -
427 ((struct kernel_sym *)sym_b)->address;
428}
429
430static void kernel_syms_load(struct dump_data *dd)
431{
432 struct kernel_sym *sym;
433 char buff[256];
434 void *tmp, *address;
435 FILE *fp;
436
437 fp = fopen("/proc/kallsyms", "r");
438 if (!fp)
439 return;
440
441 while (!feof(fp)) {
442 if (!fgets(buff, sizeof(buff), fp))
443 break;
444 tmp = realloc(dd->sym_mapping,
445 (dd->sym_count + 1) *
446 sizeof(*dd->sym_mapping));
447 if (!tmp) {
448out:
449 free(dd->sym_mapping);
450 dd->sym_mapping = NULL;
451 fclose(fp);
452 return;
453 }
454 dd->sym_mapping = tmp;
455 sym = &dd->sym_mapping[dd->sym_count];
456 if (sscanf(buff, "%p %*c %s", &address, sym->name) != 2)
457 continue;
458 sym->address = (unsigned long)address;
459 if (!strcmp(sym->name, "__bpf_call_base")) {
460 dd->address_call_base = sym->address;
461 /* sysctl kernel.kptr_restrict was set */
462 if (!sym->address)
463 goto out;
464 }
465 if (sym->address)
466 dd->sym_count++;
467 }
468
469 fclose(fp);
470
471 qsort(dd->sym_mapping, dd->sym_count,
472 sizeof(*dd->sym_mapping), kernel_syms_cmp);
473}
474
475static void kernel_syms_destroy(struct dump_data *dd)
476{
477 free(dd->sym_mapping);
478}
479
480static struct kernel_sym *kernel_syms_search(struct dump_data *dd,
481 unsigned long key)
482{
483 struct kernel_sym sym = {
484 .address = key,
485 };
486
487 return dd->sym_mapping ?
488 bsearch(&sym, dd->sym_mapping, dd->sym_count,
489 sizeof(*dd->sym_mapping), kernel_syms_cmp) : NULL;
490}
491
492static void print_insn(struct bpf_verifier_env *env, const char *fmt, ...)
493{
494 va_list args;
495
496 va_start(args, fmt);
497 vprintf(fmt, args);
498 va_end(args);
499}
500
501static const char *print_call_pcrel(struct dump_data *dd,
502 struct kernel_sym *sym,
503 unsigned long address,
504 const struct bpf_insn *insn)
505{
506 if (sym)
507 snprintf(dd->scratch_buff, sizeof(dd->scratch_buff),
508 "%+d#%s", insn->off, sym->name);
509 else
510 snprintf(dd->scratch_buff, sizeof(dd->scratch_buff),
511 "%+d#0x%lx", insn->off, address);
512 return dd->scratch_buff;
513}
514
515static const char *print_call_helper(struct dump_data *dd,
516 struct kernel_sym *sym,
517 unsigned long address)
518{
519 if (sym)
520 snprintf(dd->scratch_buff, sizeof(dd->scratch_buff),
521 "%s", sym->name);
522 else
523 snprintf(dd->scratch_buff, sizeof(dd->scratch_buff),
524 "0x%lx", address);
525 return dd->scratch_buff;
526}
527
528static const char *print_call(void *private_data,
529 const struct bpf_insn *insn)
530{
531 struct dump_data *dd = private_data;
532 unsigned long address = dd->address_call_base + insn->imm;
533 struct kernel_sym *sym;
534
535 sym = kernel_syms_search(dd, address);
536 if (insn->src_reg == BPF_PSEUDO_CALL)
537 return print_call_pcrel(dd, sym, address, insn);
538 else
539 return print_call_helper(dd, sym, address);
540}
541
542static const char *print_imm(void *private_data,
543 const struct bpf_insn *insn,
544 __u64 full_imm)
545{
546 struct dump_data *dd = private_data;
547
548 if (insn->src_reg == BPF_PSEUDO_MAP_FD)
549 snprintf(dd->scratch_buff, sizeof(dd->scratch_buff),
550 "map[id:%u]", insn->imm);
551 else
552 snprintf(dd->scratch_buff, sizeof(dd->scratch_buff),
553 "0x%llx", (unsigned long long)full_imm);
554 return dd->scratch_buff;
555}
556
557static void dump_xlated_plain(struct dump_data *dd, void *buf,
558 unsigned int len, bool opcodes)
559{
560 const struct bpf_insn_cbs cbs = {
561 .cb_print = print_insn,
562 .cb_call = print_call,
563 .cb_imm = print_imm,
564 .private_data = dd,
565 };
566 struct bpf_insn *insn = buf;
567 bool double_insn = false;
568 unsigned int i;
569
570 for (i = 0; i < len / sizeof(*insn); i++) {
571 if (double_insn) {
572 double_insn = false;
573 continue;
574 }
575
576 double_insn = insn[i].code == (BPF_LD | BPF_IMM | BPF_DW);
577
578 printf("% 4d: ", i);
579 print_bpf_insn(&cbs, NULL, insn + i, true);
580
581 if (opcodes) {
582 printf(" ");
583 fprint_hex(stdout, insn + i, 8, " ");
584 if (double_insn && i < len - 1) {
585 printf(" ");
586 fprint_hex(stdout, insn + i + 1, 8, " ");
587 }
588 printf("\n");
589 }
590 }
591}
592
593static void print_insn_json(struct bpf_verifier_env *env, const char *fmt, ...)
594{
595 unsigned int l = strlen(fmt);
596 char chomped_fmt[l];
597 va_list args;
598
599 va_start(args, fmt);
600 if (l > 0) {
601 strncpy(chomped_fmt, fmt, l - 1);
602 chomped_fmt[l - 1] = '\0';
603 }
604 jsonw_vprintf_enquote(json_wtr, chomped_fmt, args);
605 va_end(args);
606}
607
608static void dump_xlated_json(struct dump_data *dd, void *buf,
609 unsigned int len, bool opcodes)
610{
611 const struct bpf_insn_cbs cbs = {
612 .cb_print = print_insn_json,
613 .cb_call = print_call,
614 .cb_imm = print_imm,
615 .private_data = dd,
616 };
617 struct bpf_insn *insn = buf;
618 bool double_insn = false;
619 unsigned int i;
620
621 jsonw_start_array(json_wtr);
622 for (i = 0; i < len / sizeof(*insn); i++) {
623 if (double_insn) {
624 double_insn = false;
625 continue;
626 }
627 double_insn = insn[i].code == (BPF_LD | BPF_IMM | BPF_DW);
628
629 jsonw_start_object(json_wtr);
630 jsonw_name(json_wtr, "disasm");
631 print_bpf_insn(&cbs, NULL, insn + i, true);
632
633 if (opcodes) {
634 jsonw_name(json_wtr, "opcodes");
635 jsonw_start_object(json_wtr);
636
637 jsonw_name(json_wtr, "code");
638 jsonw_printf(json_wtr, "\"0x%02hhx\"", insn[i].code);
639
640 jsonw_name(json_wtr, "src_reg");
641 jsonw_printf(json_wtr, "\"0x%hhx\"", insn[i].src_reg);
642
643 jsonw_name(json_wtr, "dst_reg");
644 jsonw_printf(json_wtr, "\"0x%hhx\"", insn[i].dst_reg);
645
646 jsonw_name(json_wtr, "off");
647 print_hex_data_json((uint8_t *)(&insn[i].off), 2);
648
649 jsonw_name(json_wtr, "imm");
650 if (double_insn && i < len - 1)
651 print_hex_data_json((uint8_t *)(&insn[i].imm),
652 12);
653 else
654 print_hex_data_json((uint8_t *)(&insn[i].imm),
655 4);
656 jsonw_end_object(json_wtr);
657 }
658 jsonw_end_object(json_wtr);
659 }
660 jsonw_end_array(json_wtr);
661}
662
663static int do_dump(int argc, char **argv) 411static int do_dump(int argc, char **argv)
664{ 412{
665 struct bpf_prog_info info = {}; 413 struct bpf_prog_info info = {};
@@ -668,6 +416,7 @@ static int do_dump(int argc, char **argv)
668 unsigned int buf_size; 416 unsigned int buf_size;
669 char *filepath = NULL; 417 char *filepath = NULL;
670 bool opcodes = false; 418 bool opcodes = false;
419 bool visual = false;
671 unsigned char *buf; 420 unsigned char *buf;
672 __u32 *member_len; 421 __u32 *member_len;
673 __u64 *member_ptr; 422 __u64 *member_ptr;
@@ -706,6 +455,9 @@ static int do_dump(int argc, char **argv)
706 } else if (is_prefix(*argv, "opcodes")) { 455 } else if (is_prefix(*argv, "opcodes")) {
707 opcodes = true; 456 opcodes = true;
708 NEXT_ARG(); 457 NEXT_ARG();
458 } else if (is_prefix(*argv, "visual")) {
459 visual = true;
460 NEXT_ARG();
709 } 461 }
710 462
711 if (argc) { 463 if (argc) {
@@ -777,27 +529,30 @@ static int do_dump(int argc, char **argv)
777 529
778 if (json_output) 530 if (json_output)
779 jsonw_null(json_wtr); 531 jsonw_null(json_wtr);
780 } else { 532 } else if (member_len == &info.jited_prog_len) {
781 if (member_len == &info.jited_prog_len) { 533 const char *name = NULL;
782 const char *name = NULL; 534
783 535 if (info.ifindex) {
784 if (info.ifindex) { 536 name = ifindex_to_bfd_name_ns(info.ifindex,
785 name = ifindex_to_bfd_name_ns(info.ifindex, 537 info.netns_dev,
786 info.netns_dev, 538 info.netns_ino);
787 info.netns_ino); 539 if (!name)
788 if (!name) 540 goto err_free;
789 goto err_free;
790 }
791
792 disasm_print_insn(buf, *member_len, opcodes, name);
793 } else {
794 kernel_syms_load(&dd);
795 if (json_output)
796 dump_xlated_json(&dd, buf, *member_len, opcodes);
797 else
798 dump_xlated_plain(&dd, buf, *member_len, opcodes);
799 kernel_syms_destroy(&dd);
800 } 541 }
542
543 disasm_print_insn(buf, *member_len, opcodes, name);
544 } else if (visual) {
545 if (json_output)
546 jsonw_null(json_wtr);
547 else
548 dump_xlated_cfg(buf, *member_len);
549 } else {
550 kernel_syms_load(&dd);
551 if (json_output)
552 dump_xlated_json(&dd, buf, *member_len, opcodes);
553 else
554 dump_xlated_plain(&dd, buf, *member_len, opcodes);
555 kernel_syms_destroy(&dd);
801 } 556 }
802 557
803 free(buf); 558 free(buf);
@@ -851,7 +606,7 @@ static int do_help(int argc, char **argv)
851 606
852 fprintf(stderr, 607 fprintf(stderr,
853 "Usage: %s %s { show | list } [PROG]\n" 608 "Usage: %s %s { show | list } [PROG]\n"
854 " %s %s dump xlated PROG [{ file FILE | opcodes }]\n" 609 " %s %s dump xlated PROG [{ file FILE | opcodes | visual }]\n"
855 " %s %s dump jited PROG [{ file FILE | opcodes }]\n" 610 " %s %s dump jited PROG [{ file FILE | opcodes }]\n"
856 " %s %s pin PROG FILE\n" 611 " %s %s pin PROG FILE\n"
857 " %s %s load OBJ FILE\n" 612 " %s %s load OBJ FILE\n"
diff --git a/tools/bpf/bpftool/xlated_dumper.c b/tools/bpf/bpftool/xlated_dumper.c
new file mode 100644
index 000000000000..7a3173b76c16
--- /dev/null
+++ b/tools/bpf/bpftool/xlated_dumper.c
@@ -0,0 +1,338 @@
1// SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
2/*
3 * Copyright (C) 2018 Netronome Systems, Inc.
4 *
5 * This software is dual 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 or the BSD 2-Clause License provided below. You have the
8 * option to license this software under the complete terms of either license.
9 *
10 * The BSD 2-Clause License:
11 *
12 * Redistribution and use in source and binary forms, with or
13 * without modification, are permitted provided that the following
14 * conditions are met:
15 *
16 * 1. Redistributions of source code must retain the above
17 * copyright notice, this list of conditions and the following
18 * disclaimer.
19 *
20 * 2. Redistributions in binary form must reproduce the above
21 * copyright notice, this list of conditions and the following
22 * disclaimer in the documentation and/or other materials
23 * provided with the distribution.
24 *
25 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
26 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
27 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
28 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
29 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
30 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
31 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
32 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
33 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
34 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
35 * POSSIBILITY OF SUCH DAMAGE.
36 */
37
38#include <stdarg.h>
39#include <stdio.h>
40#include <stdlib.h>
41#include <string.h>
42#include <sys/types.h>
43
44#include "disasm.h"
45#include "json_writer.h"
46#include "main.h"
47#include "xlated_dumper.h"
48
49static int kernel_syms_cmp(const void *sym_a, const void *sym_b)
50{
51 return ((struct kernel_sym *)sym_a)->address -
52 ((struct kernel_sym *)sym_b)->address;
53}
54
55void kernel_syms_load(struct dump_data *dd)
56{
57 struct kernel_sym *sym;
58 char buff[256];
59 void *tmp, *address;
60 FILE *fp;
61
62 fp = fopen("/proc/kallsyms", "r");
63 if (!fp)
64 return;
65
66 while (!feof(fp)) {
67 if (!fgets(buff, sizeof(buff), fp))
68 break;
69 tmp = realloc(dd->sym_mapping,
70 (dd->sym_count + 1) *
71 sizeof(*dd->sym_mapping));
72 if (!tmp) {
73out:
74 free(dd->sym_mapping);
75 dd->sym_mapping = NULL;
76 fclose(fp);
77 return;
78 }
79 dd->sym_mapping = tmp;
80 sym = &dd->sym_mapping[dd->sym_count];
81 if (sscanf(buff, "%p %*c %s", &address, sym->name) != 2)
82 continue;
83 sym->address = (unsigned long)address;
84 if (!strcmp(sym->name, "__bpf_call_base")) {
85 dd->address_call_base = sym->address;
86 /* sysctl kernel.kptr_restrict was set */
87 if (!sym->address)
88 goto out;
89 }
90 if (sym->address)
91 dd->sym_count++;
92 }
93
94 fclose(fp);
95
96 qsort(dd->sym_mapping, dd->sym_count,
97 sizeof(*dd->sym_mapping), kernel_syms_cmp);
98}
99
100void kernel_syms_destroy(struct dump_data *dd)
101{
102 free(dd->sym_mapping);
103}
104
105static struct kernel_sym *kernel_syms_search(struct dump_data *dd,
106 unsigned long key)
107{
108 struct kernel_sym sym = {
109 .address = key,
110 };
111
112 return dd->sym_mapping ?
113 bsearch(&sym, dd->sym_mapping, dd->sym_count,
114 sizeof(*dd->sym_mapping), kernel_syms_cmp) : NULL;
115}
116
117static void print_insn(void *private_data, const char *fmt, ...)
118{
119 va_list args;
120
121 va_start(args, fmt);
122 vprintf(fmt, args);
123 va_end(args);
124}
125
126static void
127print_insn_for_graph(void *private_data, const char *fmt, ...)
128{
129 char buf[64], *p;
130 va_list args;
131
132 va_start(args, fmt);
133 vsnprintf(buf, sizeof(buf), fmt, args);
134 va_end(args);
135
136 p = buf;
137 while (*p != '\0') {
138 if (*p == '\n') {
139 memmove(p + 3, p, strlen(buf) + 1 - (p - buf));
140 /* Align each instruction dump row left. */
141 *p++ = '\\';
142 *p++ = 'l';
143 /* Output multiline concatenation. */
144 *p++ = '\\';
145 } else if (*p == '<' || *p == '>' || *p == '|' || *p == '&') {
146 memmove(p + 1, p, strlen(buf) + 1 - (p - buf));
147 /* Escape special character. */
148 *p++ = '\\';
149 }
150
151 p++;
152 }
153
154 printf("%s", buf);
155}
156
157static void print_insn_json(void *private_data, const char *fmt, ...)
158{
159 unsigned int l = strlen(fmt);
160 char chomped_fmt[l];
161 va_list args;
162
163 va_start(args, fmt);
164 if (l > 0) {
165 strncpy(chomped_fmt, fmt, l - 1);
166 chomped_fmt[l - 1] = '\0';
167 }
168 jsonw_vprintf_enquote(json_wtr, chomped_fmt, args);
169 va_end(args);
170}
171
172static const char *print_call_pcrel(struct dump_data *dd,
173 struct kernel_sym *sym,
174 unsigned long address,
175 const struct bpf_insn *insn)
176{
177 if (sym)
178 snprintf(dd->scratch_buff, sizeof(dd->scratch_buff),
179 "%+d#%s", insn->off, sym->name);
180 else
181 snprintf(dd->scratch_buff, sizeof(dd->scratch_buff),
182 "%+d#0x%lx", insn->off, address);
183 return dd->scratch_buff;
184}
185
186static const char *print_call_helper(struct dump_data *dd,
187 struct kernel_sym *sym,
188 unsigned long address)
189{
190 if (sym)
191 snprintf(dd->scratch_buff, sizeof(dd->scratch_buff),
192 "%s", sym->name);
193 else
194 snprintf(dd->scratch_buff, sizeof(dd->scratch_buff),
195 "0x%lx", address);
196 return dd->scratch_buff;
197}
198
199static const char *print_call(void *private_data,
200 const struct bpf_insn *insn)
201{
202 struct dump_data *dd = private_data;
203 unsigned long address = dd->address_call_base + insn->imm;
204 struct kernel_sym *sym;
205
206 sym = kernel_syms_search(dd, address);
207 if (insn->src_reg == BPF_PSEUDO_CALL)
208 return print_call_pcrel(dd, sym, address, insn);
209 else
210 return print_call_helper(dd, sym, address);
211}
212
213static const char *print_imm(void *private_data,
214 const struct bpf_insn *insn,
215 __u64 full_imm)
216{
217 struct dump_data *dd = private_data;
218
219 if (insn->src_reg == BPF_PSEUDO_MAP_FD)
220 snprintf(dd->scratch_buff, sizeof(dd->scratch_buff),
221 "map[id:%u]", insn->imm);
222 else
223 snprintf(dd->scratch_buff, sizeof(dd->scratch_buff),
224 "0x%llx", (unsigned long long)full_imm);
225 return dd->scratch_buff;
226}
227
228void dump_xlated_json(struct dump_data *dd, void *buf, unsigned int len,
229 bool opcodes)
230{
231 const struct bpf_insn_cbs cbs = {
232 .cb_print = print_insn_json,
233 .cb_call = print_call,
234 .cb_imm = print_imm,
235 .private_data = dd,
236 };
237 struct bpf_insn *insn = buf;
238 bool double_insn = false;
239 unsigned int i;
240
241 jsonw_start_array(json_wtr);
242 for (i = 0; i < len / sizeof(*insn); i++) {
243 if (double_insn) {
244 double_insn = false;
245 continue;
246 }
247 double_insn = insn[i].code == (BPF_LD | BPF_IMM | BPF_DW);
248
249 jsonw_start_object(json_wtr);
250 jsonw_name(json_wtr, "disasm");
251 print_bpf_insn(&cbs, insn + i, true);
252
253 if (opcodes) {
254 jsonw_name(json_wtr, "opcodes");
255 jsonw_start_object(json_wtr);
256
257 jsonw_name(json_wtr, "code");
258 jsonw_printf(json_wtr, "\"0x%02hhx\"", insn[i].code);
259
260 jsonw_name(json_wtr, "src_reg");
261 jsonw_printf(json_wtr, "\"0x%hhx\"", insn[i].src_reg);
262
263 jsonw_name(json_wtr, "dst_reg");
264 jsonw_printf(json_wtr, "\"0x%hhx\"", insn[i].dst_reg);
265
266 jsonw_name(json_wtr, "off");
267 print_hex_data_json((uint8_t *)(&insn[i].off), 2);
268
269 jsonw_name(json_wtr, "imm");
270 if (double_insn && i < len - 1)
271 print_hex_data_json((uint8_t *)(&insn[i].imm),
272 12);
273 else
274 print_hex_data_json((uint8_t *)(&insn[i].imm),
275 4);
276 jsonw_end_object(json_wtr);
277 }
278 jsonw_end_object(json_wtr);
279 }
280 jsonw_end_array(json_wtr);
281}
282
283void dump_xlated_plain(struct dump_data *dd, void *buf, unsigned int len,
284 bool opcodes)
285{
286 const struct bpf_insn_cbs cbs = {
287 .cb_print = print_insn,
288 .cb_call = print_call,
289 .cb_imm = print_imm,
290 .private_data = dd,
291 };
292 struct bpf_insn *insn = buf;
293 bool double_insn = false;
294 unsigned int i;
295
296 for (i = 0; i < len / sizeof(*insn); i++) {
297 if (double_insn) {
298 double_insn = false;
299 continue;
300 }
301
302 double_insn = insn[i].code == (BPF_LD | BPF_IMM | BPF_DW);
303
304 printf("% 4d: ", i);
305 print_bpf_insn(&cbs, insn + i, true);
306
307 if (opcodes) {
308 printf(" ");
309 fprint_hex(stdout, insn + i, 8, " ");
310 if (double_insn && i < len - 1) {
311 printf(" ");
312 fprint_hex(stdout, insn + i + 1, 8, " ");
313 }
314 printf("\n");
315 }
316 }
317}
318
319void dump_xlated_for_graph(struct dump_data *dd, void *buf_start, void *buf_end,
320 unsigned int start_idx)
321{
322 const struct bpf_insn_cbs cbs = {
323 .cb_print = print_insn_for_graph,
324 .cb_call = print_call,
325 .cb_imm = print_imm,
326 .private_data = dd,
327 };
328 struct bpf_insn *insn_start = buf_start;
329 struct bpf_insn *insn_end = buf_end;
330 struct bpf_insn *cur = insn_start;
331
332 for (; cur <= insn_end; cur++) {
333 printf("% 4d: ", (int)(cur - insn_start + start_idx));
334 print_bpf_insn(&cbs, cur, true);
335 if (cur != insn_end)
336 printf(" | ");
337 }
338}
diff --git a/tools/bpf/bpftool/xlated_dumper.h b/tools/bpf/bpftool/xlated_dumper.h
new file mode 100644
index 000000000000..b34affa7ef2d
--- /dev/null
+++ b/tools/bpf/bpftool/xlated_dumper.h
@@ -0,0 +1,64 @@
1// SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
2/*
3 * Copyright (C) 2018 Netronome Systems, Inc.
4 *
5 * This software is dual 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 or the BSD 2-Clause License provided below. You have the
8 * option to license this software under the complete terms of either license.
9 *
10 * The BSD 2-Clause License:
11 *
12 * Redistribution and use in source and binary forms, with or
13 * without modification, are permitted provided that the following
14 * conditions are met:
15 *
16 * 1. Redistributions of source code must retain the above
17 * copyright notice, this list of conditions and the following
18 * disclaimer.
19 *
20 * 2. Redistributions in binary form must reproduce the above
21 * copyright notice, this list of conditions and the following
22 * disclaimer in the documentation and/or other materials
23 * provided with the distribution.
24 *
25 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
26 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
27 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
28 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
29 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
30 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
31 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
32 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
33 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
34 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
35 * POSSIBILITY OF SUCH DAMAGE.
36 */
37
38#ifndef __BPF_TOOL_XLATED_DUMPER_H
39#define __BPF_TOOL_XLATED_DUMPER_H
40
41#define SYM_MAX_NAME 256
42
43struct kernel_sym {
44 unsigned long address;
45 char name[SYM_MAX_NAME];
46};
47
48struct dump_data {
49 unsigned long address_call_base;
50 struct kernel_sym *sym_mapping;
51 __u32 sym_count;
52 char scratch_buff[SYM_MAX_NAME + 8];
53};
54
55void kernel_syms_load(struct dump_data *dd);
56void kernel_syms_destroy(struct dump_data *dd);
57void dump_xlated_json(struct dump_data *dd, void *buf, unsigned int len,
58 bool opcodes);
59void dump_xlated_plain(struct dump_data *dd, void *buf, unsigned int len,
60 bool opcodes);
61void dump_xlated_for_graph(struct dump_data *dd, void *buf, void *buf_end,
62 unsigned int start_index);
63
64#endif
diff --git a/tools/include/uapi/linux/bpf.h b/tools/include/uapi/linux/bpf.h
index db6bdc375126..9d07465023a2 100644
--- a/tools/include/uapi/linux/bpf.h
+++ b/tools/include/uapi/linux/bpf.h
@@ -94,6 +94,7 @@ enum bpf_cmd {
94 BPF_MAP_GET_FD_BY_ID, 94 BPF_MAP_GET_FD_BY_ID,
95 BPF_OBJ_GET_INFO_BY_FD, 95 BPF_OBJ_GET_INFO_BY_FD,
96 BPF_PROG_QUERY, 96 BPF_PROG_QUERY,
97 BPF_RAW_TRACEPOINT_OPEN,
97}; 98};
98 99
99enum bpf_map_type { 100enum bpf_map_type {
@@ -133,6 +134,9 @@ enum bpf_prog_type {
133 BPF_PROG_TYPE_SOCK_OPS, 134 BPF_PROG_TYPE_SOCK_OPS,
134 BPF_PROG_TYPE_SK_SKB, 135 BPF_PROG_TYPE_SK_SKB,
135 BPF_PROG_TYPE_CGROUP_DEVICE, 136 BPF_PROG_TYPE_CGROUP_DEVICE,
137 BPF_PROG_TYPE_SK_MSG,
138 BPF_PROG_TYPE_RAW_TRACEPOINT,
139 BPF_PROG_TYPE_CGROUP_SOCK_ADDR,
136}; 140};
137 141
138enum bpf_attach_type { 142enum bpf_attach_type {
@@ -143,6 +147,13 @@ enum bpf_attach_type {
143 BPF_SK_SKB_STREAM_PARSER, 147 BPF_SK_SKB_STREAM_PARSER,
144 BPF_SK_SKB_STREAM_VERDICT, 148 BPF_SK_SKB_STREAM_VERDICT,
145 BPF_CGROUP_DEVICE, 149 BPF_CGROUP_DEVICE,
150 BPF_SK_MSG_VERDICT,
151 BPF_CGROUP_INET4_BIND,
152 BPF_CGROUP_INET6_BIND,
153 BPF_CGROUP_INET4_CONNECT,
154 BPF_CGROUP_INET6_CONNECT,
155 BPF_CGROUP_INET4_POST_BIND,
156 BPF_CGROUP_INET6_POST_BIND,
146 __MAX_BPF_ATTACH_TYPE 157 __MAX_BPF_ATTACH_TYPE
147}; 158};
148 159
@@ -231,6 +242,28 @@ enum bpf_attach_type {
231#define BPF_F_RDONLY (1U << 3) 242#define BPF_F_RDONLY (1U << 3)
232#define BPF_F_WRONLY (1U << 4) 243#define BPF_F_WRONLY (1U << 4)
233 244
245/* Flag for stack_map, store build_id+offset instead of pointer */
246#define BPF_F_STACK_BUILD_ID (1U << 5)
247
248enum bpf_stack_build_id_status {
249 /* user space need an empty entry to identify end of a trace */
250 BPF_STACK_BUILD_ID_EMPTY = 0,
251 /* with valid build_id and offset */
252 BPF_STACK_BUILD_ID_VALID = 1,
253 /* couldn't get build_id, fallback to ip */
254 BPF_STACK_BUILD_ID_IP = 2,
255};
256
257#define BPF_BUILD_ID_SIZE 20
258struct bpf_stack_build_id {
259 __s32 status;
260 unsigned char build_id[BPF_BUILD_ID_SIZE];
261 union {
262 __u64 offset;
263 __u64 ip;
264 };
265};
266
234union bpf_attr { 267union bpf_attr {
235 struct { /* anonymous struct used by BPF_MAP_CREATE command */ 268 struct { /* anonymous struct used by BPF_MAP_CREATE command */
236 __u32 map_type; /* one of enum bpf_map_type */ 269 __u32 map_type; /* one of enum bpf_map_type */
@@ -270,6 +303,11 @@ union bpf_attr {
270 __u32 prog_flags; 303 __u32 prog_flags;
271 char prog_name[BPF_OBJ_NAME_LEN]; 304 char prog_name[BPF_OBJ_NAME_LEN];
272 __u32 prog_ifindex; /* ifindex of netdev to prep for */ 305 __u32 prog_ifindex; /* ifindex of netdev to prep for */
306 /* For some prog types expected attach type must be known at
307 * load time to verify attach type specific parts of prog
308 * (context accesses, allowed helpers, etc).
309 */
310 __u32 expected_attach_type;
273 }; 311 };
274 312
275 struct { /* anonymous struct used by BPF_OBJ_* commands */ 313 struct { /* anonymous struct used by BPF_OBJ_* commands */
@@ -320,6 +358,11 @@ union bpf_attr {
320 __aligned_u64 prog_ids; 358 __aligned_u64 prog_ids;
321 __u32 prog_cnt; 359 __u32 prog_cnt;
322 } query; 360 } query;
361
362 struct {
363 __u64 name;
364 __u32 prog_fd;
365 } raw_tracepoint;
323} __attribute__((aligned(8))); 366} __attribute__((aligned(8)));
324 367
325/* BPF helper function descriptions: 368/* BPF helper function descriptions:
@@ -696,6 +739,22 @@ union bpf_attr {
696 * int bpf_override_return(pt_regs, rc) 739 * int bpf_override_return(pt_regs, rc)
697 * @pt_regs: pointer to struct pt_regs 740 * @pt_regs: pointer to struct pt_regs
698 * @rc: the return value to set 741 * @rc: the return value to set
742 *
743 * int bpf_msg_redirect_map(map, key, flags)
744 * Redirect msg to a sock in map using key as a lookup key for the
745 * sock in map.
746 * @map: pointer to sockmap
747 * @key: key to lookup sock in map
748 * @flags: reserved for future use
749 * Return: SK_PASS
750 *
751 * int bpf_bind(ctx, addr, addr_len)
752 * Bind socket to address. Only binding to IP is supported, no port can be
753 * set in addr.
754 * @ctx: pointer to context of type bpf_sock_addr
755 * @addr: pointer to struct sockaddr to bind socket to
756 * @addr_len: length of sockaddr structure
757 * Return: 0 on success or negative error code
699 */ 758 */
700#define __BPF_FUNC_MAPPER(FN) \ 759#define __BPF_FUNC_MAPPER(FN) \
701 FN(unspec), \ 760 FN(unspec), \
@@ -757,7 +816,12 @@ union bpf_attr {
757 FN(perf_prog_read_value), \ 816 FN(perf_prog_read_value), \
758 FN(getsockopt), \ 817 FN(getsockopt), \
759 FN(override_return), \ 818 FN(override_return), \
760 FN(sock_ops_cb_flags_set), 819 FN(sock_ops_cb_flags_set), \
820 FN(msg_redirect_map), \
821 FN(msg_apply_bytes), \
822 FN(msg_cork_bytes), \
823 FN(msg_pull_data), \
824 FN(bind),
761 825
762/* integer value in 'imm' field of BPF_CALL instruction selects which helper 826/* integer value in 'imm' field of BPF_CALL instruction selects which helper
763 * function eBPF program intends to call 827 * function eBPF program intends to call
@@ -885,6 +949,15 @@ struct bpf_sock {
885 __u32 protocol; 949 __u32 protocol;
886 __u32 mark; 950 __u32 mark;
887 __u32 priority; 951 __u32 priority;
952 __u32 src_ip4; /* Allows 1,2,4-byte read.
953 * Stored in network byte order.
954 */
955 __u32 src_ip6[4]; /* Allows 1,2,4-byte read.
956 * Stored in network byte order.
957 */
958 __u32 src_port; /* Allows 4-byte read.
959 * Stored in host byte order
960 */
888}; 961};
889 962
890#define XDP_PACKET_HEADROOM 256 963#define XDP_PACKET_HEADROOM 256
@@ -919,6 +992,14 @@ enum sk_action {
919 SK_PASS, 992 SK_PASS,
920}; 993};
921 994
995/* user accessible metadata for SK_MSG packet hook, new fields must
996 * be added to the end of this structure
997 */
998struct sk_msg_md {
999 void *data;
1000 void *data_end;
1001};
1002
922#define BPF_TAG_SIZE 8 1003#define BPF_TAG_SIZE 8
923 1004
924struct bpf_prog_info { 1005struct bpf_prog_info {
@@ -952,6 +1033,26 @@ struct bpf_map_info {
952 __u64 netns_ino; 1033 __u64 netns_ino;
953} __attribute__((aligned(8))); 1034} __attribute__((aligned(8)));
954 1035
1036/* User bpf_sock_addr struct to access socket fields and sockaddr struct passed
1037 * by user and intended to be used by socket (e.g. to bind to, depends on
1038 * attach attach type).
1039 */
1040struct bpf_sock_addr {
1041 __u32 user_family; /* Allows 4-byte read, but no write. */
1042 __u32 user_ip4; /* Allows 1,2,4-byte read and 4-byte write.
1043 * Stored in network byte order.
1044 */
1045 __u32 user_ip6[4]; /* Allows 1,2,4-byte read an 4-byte write.
1046 * Stored in network byte order.
1047 */
1048 __u32 user_port; /* Allows 4-byte read and write.
1049 * Stored in network byte order
1050 */
1051 __u32 family; /* Allows 4-byte read, but no write */
1052 __u32 type; /* Allows 4-byte read, but no write */
1053 __u32 protocol; /* Allows 4-byte read, but no write */
1054};
1055
955/* User bpf_sock_ops struct to access socket values and specify request ops 1056/* User bpf_sock_ops struct to access socket values and specify request ops
956 * and their replies. 1057 * and their replies.
957 * Some of this fields are in network (bigendian) byte order and may need 1058 * Some of this fields are in network (bigendian) byte order and may need
@@ -1106,4 +1207,8 @@ struct bpf_cgroup_dev_ctx {
1106 __u32 minor; 1207 __u32 minor;
1107}; 1208};
1108 1209
1210struct bpf_raw_tracepoint_args {
1211 __u64 args[0];
1212};
1213
1109#endif /* _UAPI__LINUX_BPF_H__ */ 1214#endif /* _UAPI__LINUX_BPF_H__ */
diff --git a/tools/lib/bpf/bpf.c b/tools/lib/bpf/bpf.c
index 592a58a2b681..acbb3f8b3bec 100644
--- a/tools/lib/bpf/bpf.c
+++ b/tools/lib/bpf/bpf.c
@@ -146,26 +146,30 @@ int bpf_create_map_in_map(enum bpf_map_type map_type, const char *name,
146 -1); 146 -1);
147} 147}
148 148
149int bpf_load_program_name(enum bpf_prog_type type, const char *name, 149int bpf_load_program_xattr(const struct bpf_load_program_attr *load_attr,
150 const struct bpf_insn *insns, 150 char *log_buf, size_t log_buf_sz)
151 size_t insns_cnt, const char *license,
152 __u32 kern_version, char *log_buf,
153 size_t log_buf_sz)
154{ 151{
155 int fd;
156 union bpf_attr attr; 152 union bpf_attr attr;
157 __u32 name_len = name ? strlen(name) : 0; 153 __u32 name_len;
154 int fd;
155
156 if (!load_attr)
157 return -EINVAL;
158
159 name_len = load_attr->name ? strlen(load_attr->name) : 0;
158 160
159 bzero(&attr, sizeof(attr)); 161 bzero(&attr, sizeof(attr));
160 attr.prog_type = type; 162 attr.prog_type = load_attr->prog_type;
161 attr.insn_cnt = (__u32)insns_cnt; 163 attr.expected_attach_type = load_attr->expected_attach_type;
162 attr.insns = ptr_to_u64(insns); 164 attr.insn_cnt = (__u32)load_attr->insns_cnt;
163 attr.license = ptr_to_u64(license); 165 attr.insns = ptr_to_u64(load_attr->insns);
166 attr.license = ptr_to_u64(load_attr->license);
164 attr.log_buf = ptr_to_u64(NULL); 167 attr.log_buf = ptr_to_u64(NULL);
165 attr.log_size = 0; 168 attr.log_size = 0;
166 attr.log_level = 0; 169 attr.log_level = 0;
167 attr.kern_version = kern_version; 170 attr.kern_version = load_attr->kern_version;
168 memcpy(attr.prog_name, name, min(name_len, BPF_OBJ_NAME_LEN - 1)); 171 memcpy(attr.prog_name, load_attr->name,
172 min(name_len, BPF_OBJ_NAME_LEN - 1));
169 173
170 fd = sys_bpf(BPF_PROG_LOAD, &attr, sizeof(attr)); 174 fd = sys_bpf(BPF_PROG_LOAD, &attr, sizeof(attr));
171 if (fd >= 0 || !log_buf || !log_buf_sz) 175 if (fd >= 0 || !log_buf || !log_buf_sz)
@@ -184,8 +188,18 @@ int bpf_load_program(enum bpf_prog_type type, const struct bpf_insn *insns,
184 __u32 kern_version, char *log_buf, 188 __u32 kern_version, char *log_buf,
185 size_t log_buf_sz) 189 size_t log_buf_sz)
186{ 190{
187 return bpf_load_program_name(type, NULL, insns, insns_cnt, license, 191 struct bpf_load_program_attr load_attr;
188 kern_version, log_buf, log_buf_sz); 192
193 memset(&load_attr, 0, sizeof(struct bpf_load_program_attr));
194 load_attr.prog_type = type;
195 load_attr.expected_attach_type = 0;
196 load_attr.name = NULL;
197 load_attr.insns = insns;
198 load_attr.insns_cnt = insns_cnt;
199 load_attr.license = license;
200 load_attr.kern_version = kern_version;
201
202 return bpf_load_program_xattr(&load_attr, log_buf, log_buf_sz);
189} 203}
190 204
191int bpf_verify_program(enum bpf_prog_type type, const struct bpf_insn *insns, 205int bpf_verify_program(enum bpf_prog_type type, const struct bpf_insn *insns,
@@ -428,6 +442,17 @@ int bpf_obj_get_info_by_fd(int prog_fd, void *info, __u32 *info_len)
428 return err; 442 return err;
429} 443}
430 444
445int bpf_raw_tracepoint_open(const char *name, int prog_fd)
446{
447 union bpf_attr attr;
448
449 bzero(&attr, sizeof(attr));
450 attr.raw_tracepoint.name = ptr_to_u64(name);
451 attr.raw_tracepoint.prog_fd = prog_fd;
452
453 return sys_bpf(BPF_RAW_TRACEPOINT_OPEN, &attr, sizeof(attr));
454}
455
431int bpf_set_link_xdp_fd(int ifindex, int fd, __u32 flags) 456int bpf_set_link_xdp_fd(int ifindex, int fd, __u32 flags)
432{ 457{
433 struct sockaddr_nl sa; 458 struct sockaddr_nl sa;
diff --git a/tools/lib/bpf/bpf.h b/tools/lib/bpf/bpf.h
index 8d18fb73d7fb..39f6a0d64a3b 100644
--- a/tools/lib/bpf/bpf.h
+++ b/tools/lib/bpf/bpf.h
@@ -41,13 +41,20 @@ int bpf_create_map_in_map(enum bpf_map_type map_type, const char *name,
41 int key_size, int inner_map_fd, int max_entries, 41 int key_size, int inner_map_fd, int max_entries,
42 __u32 map_flags); 42 __u32 map_flags);
43 43
44struct bpf_load_program_attr {
45 enum bpf_prog_type prog_type;
46 enum bpf_attach_type expected_attach_type;
47 const char *name;
48 const struct bpf_insn *insns;
49 size_t insns_cnt;
50 const char *license;
51 __u32 kern_version;
52};
53
44/* Recommend log buffer size */ 54/* Recommend log buffer size */
45#define BPF_LOG_BUF_SIZE (256 * 1024) 55#define BPF_LOG_BUF_SIZE (256 * 1024)
46int bpf_load_program_name(enum bpf_prog_type type, const char *name, 56int bpf_load_program_xattr(const struct bpf_load_program_attr *load_attr,
47 const struct bpf_insn *insns, 57 char *log_buf, size_t log_buf_sz);
48 size_t insns_cnt, const char *license,
49 __u32 kern_version, char *log_buf,
50 size_t log_buf_sz);
51int bpf_load_program(enum bpf_prog_type type, const struct bpf_insn *insns, 58int bpf_load_program(enum bpf_prog_type type, const struct bpf_insn *insns,
52 size_t insns_cnt, const char *license, 59 size_t insns_cnt, const char *license,
53 __u32 kern_version, char *log_buf, 60 __u32 kern_version, char *log_buf,
@@ -79,4 +86,5 @@ int bpf_map_get_fd_by_id(__u32 id);
79int bpf_obj_get_info_by_fd(int prog_fd, void *info, __u32 *info_len); 86int bpf_obj_get_info_by_fd(int prog_fd, void *info, __u32 *info_len);
80int bpf_prog_query(int target_fd, enum bpf_attach_type type, __u32 query_flags, 87int bpf_prog_query(int target_fd, enum bpf_attach_type type, __u32 query_flags,
81 __u32 *attach_flags, __u32 *prog_ids, __u32 *prog_cnt); 88 __u32 *attach_flags, __u32 *prog_ids, __u32 *prog_cnt);
89int bpf_raw_tracepoint_open(const char *name, int prog_fd);
82#endif 90#endif
diff --git a/tools/lib/bpf/libbpf.c b/tools/lib/bpf/libbpf.c
index 5bbbf285af74..5922443063f0 100644
--- a/tools/lib/bpf/libbpf.c
+++ b/tools/lib/bpf/libbpf.c
@@ -203,6 +203,8 @@ struct bpf_program {
203 struct bpf_object *obj; 203 struct bpf_object *obj;
204 void *priv; 204 void *priv;
205 bpf_program_clear_priv_t clear_priv; 205 bpf_program_clear_priv_t clear_priv;
206
207 enum bpf_attach_type expected_attach_type;
206}; 208};
207 209
208struct bpf_map { 210struct bpf_map {
@@ -1162,21 +1164,31 @@ static int bpf_object__collect_reloc(struct bpf_object *obj)
1162} 1164}
1163 1165
1164static int 1166static int
1165load_program(enum bpf_prog_type type, const char *name, struct bpf_insn *insns, 1167load_program(enum bpf_prog_type type, enum bpf_attach_type expected_attach_type,
1166 int insns_cnt, char *license, u32 kern_version, int *pfd) 1168 const char *name, struct bpf_insn *insns, int insns_cnt,
1169 char *license, u32 kern_version, int *pfd)
1167{ 1170{
1168 int ret; 1171 struct bpf_load_program_attr load_attr;
1169 char *log_buf; 1172 char *log_buf;
1173 int ret;
1170 1174
1171 if (!insns || !insns_cnt) 1175 memset(&load_attr, 0, sizeof(struct bpf_load_program_attr));
1176 load_attr.prog_type = type;
1177 load_attr.expected_attach_type = expected_attach_type;
1178 load_attr.name = name;
1179 load_attr.insns = insns;
1180 load_attr.insns_cnt = insns_cnt;
1181 load_attr.license = license;
1182 load_attr.kern_version = kern_version;
1183
1184 if (!load_attr.insns || !load_attr.insns_cnt)
1172 return -EINVAL; 1185 return -EINVAL;
1173 1186
1174 log_buf = malloc(BPF_LOG_BUF_SIZE); 1187 log_buf = malloc(BPF_LOG_BUF_SIZE);
1175 if (!log_buf) 1188 if (!log_buf)
1176 pr_warning("Alloc log buffer for bpf loader error, continue without log\n"); 1189 pr_warning("Alloc log buffer for bpf loader error, continue without log\n");
1177 1190
1178 ret = bpf_load_program_name(type, name, insns, insns_cnt, license, 1191 ret = bpf_load_program_xattr(&load_attr, log_buf, BPF_LOG_BUF_SIZE);
1179 kern_version, log_buf, BPF_LOG_BUF_SIZE);
1180 1192
1181 if (ret >= 0) { 1193 if (ret >= 0) {
1182 *pfd = ret; 1194 *pfd = ret;
@@ -1192,18 +1204,18 @@ load_program(enum bpf_prog_type type, const char *name, struct bpf_insn *insns,
1192 pr_warning("-- BEGIN DUMP LOG ---\n"); 1204 pr_warning("-- BEGIN DUMP LOG ---\n");
1193 pr_warning("\n%s\n", log_buf); 1205 pr_warning("\n%s\n", log_buf);
1194 pr_warning("-- END LOG --\n"); 1206 pr_warning("-- END LOG --\n");
1195 } else if (insns_cnt >= BPF_MAXINSNS) { 1207 } else if (load_attr.insns_cnt >= BPF_MAXINSNS) {
1196 pr_warning("Program too large (%d insns), at most %d insns\n", 1208 pr_warning("Program too large (%zu insns), at most %d insns\n",
1197 insns_cnt, BPF_MAXINSNS); 1209 load_attr.insns_cnt, BPF_MAXINSNS);
1198 ret = -LIBBPF_ERRNO__PROG2BIG; 1210 ret = -LIBBPF_ERRNO__PROG2BIG;
1199 } else { 1211 } else {
1200 /* Wrong program type? */ 1212 /* Wrong program type? */
1201 if (type != BPF_PROG_TYPE_KPROBE) { 1213 if (load_attr.prog_type != BPF_PROG_TYPE_KPROBE) {
1202 int fd; 1214 int fd;
1203 1215
1204 fd = bpf_load_program_name(BPF_PROG_TYPE_KPROBE, name, 1216 load_attr.prog_type = BPF_PROG_TYPE_KPROBE;
1205 insns, insns_cnt, license, 1217 load_attr.expected_attach_type = 0;
1206 kern_version, NULL, 0); 1218 fd = bpf_load_program_xattr(&load_attr, NULL, 0);
1207 if (fd >= 0) { 1219 if (fd >= 0) {
1208 close(fd); 1220 close(fd);
1209 ret = -LIBBPF_ERRNO__PROGTYPE; 1221 ret = -LIBBPF_ERRNO__PROGTYPE;
@@ -1247,8 +1259,9 @@ bpf_program__load(struct bpf_program *prog,
1247 pr_warning("Program '%s' is inconsistent: nr(%d) != 1\n", 1259 pr_warning("Program '%s' is inconsistent: nr(%d) != 1\n",
1248 prog->section_name, prog->instances.nr); 1260 prog->section_name, prog->instances.nr);
1249 } 1261 }
1250 err = load_program(prog->type, prog->name, prog->insns, 1262 err = load_program(prog->type, prog->expected_attach_type,
1251 prog->insns_cnt, license, kern_version, &fd); 1263 prog->name, prog->insns, prog->insns_cnt,
1264 license, kern_version, &fd);
1252 if (!err) 1265 if (!err)
1253 prog->instances.fds[0] = fd; 1266 prog->instances.fds[0] = fd;
1254 goto out; 1267 goto out;
@@ -1276,8 +1289,8 @@ bpf_program__load(struct bpf_program *prog,
1276 continue; 1289 continue;
1277 } 1290 }
1278 1291
1279 err = load_program(prog->type, prog->name, 1292 err = load_program(prog->type, prog->expected_attach_type,
1280 result.new_insn_ptr, 1293 prog->name, result.new_insn_ptr,
1281 result.new_insn_cnt, 1294 result.new_insn_cnt,
1282 license, kern_version, &fd); 1295 license, kern_version, &fd);
1283 1296
@@ -1835,11 +1848,25 @@ BPF_PROG_TYPE_FNS(tracepoint, BPF_PROG_TYPE_TRACEPOINT);
1835BPF_PROG_TYPE_FNS(xdp, BPF_PROG_TYPE_XDP); 1848BPF_PROG_TYPE_FNS(xdp, BPF_PROG_TYPE_XDP);
1836BPF_PROG_TYPE_FNS(perf_event, BPF_PROG_TYPE_PERF_EVENT); 1849BPF_PROG_TYPE_FNS(perf_event, BPF_PROG_TYPE_PERF_EVENT);
1837 1850
1838#define BPF_PROG_SEC(string, type) { string, sizeof(string) - 1, type } 1851static void bpf_program__set_expected_attach_type(struct bpf_program *prog,
1852 enum bpf_attach_type type)
1853{
1854 prog->expected_attach_type = type;
1855}
1856
1857#define BPF_PROG_SEC_FULL(string, ptype, atype) \
1858 { string, sizeof(string) - 1, ptype, atype }
1859
1860#define BPF_PROG_SEC(string, ptype) BPF_PROG_SEC_FULL(string, ptype, 0)
1861
1862#define BPF_SA_PROG_SEC(string, ptype) \
1863 BPF_PROG_SEC_FULL(string, BPF_PROG_TYPE_CGROUP_SOCK_ADDR, ptype)
1864
1839static const struct { 1865static const struct {
1840 const char *sec; 1866 const char *sec;
1841 size_t len; 1867 size_t len;
1842 enum bpf_prog_type prog_type; 1868 enum bpf_prog_type prog_type;
1869 enum bpf_attach_type expected_attach_type;
1843} section_names[] = { 1870} section_names[] = {
1844 BPF_PROG_SEC("socket", BPF_PROG_TYPE_SOCKET_FILTER), 1871 BPF_PROG_SEC("socket", BPF_PROG_TYPE_SOCKET_FILTER),
1845 BPF_PROG_SEC("kprobe/", BPF_PROG_TYPE_KPROBE), 1872 BPF_PROG_SEC("kprobe/", BPF_PROG_TYPE_KPROBE),
@@ -1857,10 +1884,18 @@ static const struct {
1857 BPF_PROG_SEC("lwt_xmit", BPF_PROG_TYPE_LWT_XMIT), 1884 BPF_PROG_SEC("lwt_xmit", BPF_PROG_TYPE_LWT_XMIT),
1858 BPF_PROG_SEC("sockops", BPF_PROG_TYPE_SOCK_OPS), 1885 BPF_PROG_SEC("sockops", BPF_PROG_TYPE_SOCK_OPS),
1859 BPF_PROG_SEC("sk_skb", BPF_PROG_TYPE_SK_SKB), 1886 BPF_PROG_SEC("sk_skb", BPF_PROG_TYPE_SK_SKB),
1887 BPF_PROG_SEC("sk_msg", BPF_PROG_TYPE_SK_MSG),
1888 BPF_SA_PROG_SEC("cgroup/bind4", BPF_CGROUP_INET4_BIND),
1889 BPF_SA_PROG_SEC("cgroup/bind6", BPF_CGROUP_INET6_BIND),
1890 BPF_SA_PROG_SEC("cgroup/connect4", BPF_CGROUP_INET4_CONNECT),
1891 BPF_SA_PROG_SEC("cgroup/connect6", BPF_CGROUP_INET6_CONNECT),
1860}; 1892};
1893
1861#undef BPF_PROG_SEC 1894#undef BPF_PROG_SEC
1895#undef BPF_PROG_SEC_FULL
1896#undef BPF_SA_PROG_SEC
1862 1897
1863static enum bpf_prog_type bpf_program__guess_type(struct bpf_program *prog) 1898static int bpf_program__identify_section(struct bpf_program *prog)
1864{ 1899{
1865 int i; 1900 int i;
1866 1901
@@ -1870,13 +1905,13 @@ static enum bpf_prog_type bpf_program__guess_type(struct bpf_program *prog)
1870 for (i = 0; i < ARRAY_SIZE(section_names); i++) 1905 for (i = 0; i < ARRAY_SIZE(section_names); i++)
1871 if (strncmp(prog->section_name, section_names[i].sec, 1906 if (strncmp(prog->section_name, section_names[i].sec,
1872 section_names[i].len) == 0) 1907 section_names[i].len) == 0)
1873 return section_names[i].prog_type; 1908 return i;
1874 1909
1875err: 1910err:
1876 pr_warning("failed to guess program type based on section name %s\n", 1911 pr_warning("failed to guess program type based on section name %s\n",
1877 prog->section_name); 1912 prog->section_name);
1878 1913
1879 return BPF_PROG_TYPE_UNSPEC; 1914 return -1;
1880} 1915}
1881 1916
1882int bpf_map__fd(struct bpf_map *map) 1917int bpf_map__fd(struct bpf_map *map)
@@ -1976,11 +2011,30 @@ long libbpf_get_error(const void *ptr)
1976int bpf_prog_load(const char *file, enum bpf_prog_type type, 2011int bpf_prog_load(const char *file, enum bpf_prog_type type,
1977 struct bpf_object **pobj, int *prog_fd) 2012 struct bpf_object **pobj, int *prog_fd)
1978{ 2013{
2014 struct bpf_prog_load_attr attr;
2015
2016 memset(&attr, 0, sizeof(struct bpf_prog_load_attr));
2017 attr.file = file;
2018 attr.prog_type = type;
2019 attr.expected_attach_type = 0;
2020
2021 return bpf_prog_load_xattr(&attr, pobj, prog_fd);
2022}
2023
2024int bpf_prog_load_xattr(const struct bpf_prog_load_attr *attr,
2025 struct bpf_object **pobj, int *prog_fd)
2026{
1979 struct bpf_program *prog, *first_prog = NULL; 2027 struct bpf_program *prog, *first_prog = NULL;
2028 enum bpf_attach_type expected_attach_type;
2029 enum bpf_prog_type prog_type;
1980 struct bpf_object *obj; 2030 struct bpf_object *obj;
2031 int section_idx;
1981 int err; 2032 int err;
1982 2033
1983 obj = bpf_object__open(file); 2034 if (!attr)
2035 return -EINVAL;
2036
2037 obj = bpf_object__open(attr->file);
1984 if (IS_ERR(obj)) 2038 if (IS_ERR(obj))
1985 return -ENOENT; 2039 return -ENOENT;
1986 2040
@@ -1989,15 +2043,23 @@ int bpf_prog_load(const char *file, enum bpf_prog_type type,
1989 * If type is not specified, try to guess it based on 2043 * If type is not specified, try to guess it based on
1990 * section name. 2044 * section name.
1991 */ 2045 */
1992 if (type == BPF_PROG_TYPE_UNSPEC) { 2046 prog_type = attr->prog_type;
1993 type = bpf_program__guess_type(prog); 2047 expected_attach_type = attr->expected_attach_type;
1994 if (type == BPF_PROG_TYPE_UNSPEC) { 2048 if (prog_type == BPF_PROG_TYPE_UNSPEC) {
2049 section_idx = bpf_program__identify_section(prog);
2050 if (section_idx < 0) {
1995 bpf_object__close(obj); 2051 bpf_object__close(obj);
1996 return -EINVAL; 2052 return -EINVAL;
1997 } 2053 }
2054 prog_type = section_names[section_idx].prog_type;
2055 expected_attach_type =
2056 section_names[section_idx].expected_attach_type;
1998 } 2057 }
1999 2058
2000 bpf_program__set_type(prog, type); 2059 bpf_program__set_type(prog, prog_type);
2060 bpf_program__set_expected_attach_type(prog,
2061 expected_attach_type);
2062
2001 if (prog->idx != obj->efile.text_shndx && !first_prog) 2063 if (prog->idx != obj->efile.text_shndx && !first_prog)
2002 first_prog = prog; 2064 first_prog = prog;
2003 } 2065 }
diff --git a/tools/lib/bpf/libbpf.h b/tools/lib/bpf/libbpf.h
index f85906533cdd..a3a62a583f27 100644
--- a/tools/lib/bpf/libbpf.h
+++ b/tools/lib/bpf/libbpf.h
@@ -248,6 +248,14 @@ int bpf_map__pin(struct bpf_map *map, const char *path);
248 248
249long libbpf_get_error(const void *ptr); 249long libbpf_get_error(const void *ptr);
250 250
251struct bpf_prog_load_attr {
252 const char *file;
253 enum bpf_prog_type prog_type;
254 enum bpf_attach_type expected_attach_type;
255};
256
257int bpf_prog_load_xattr(const struct bpf_prog_load_attr *attr,
258 struct bpf_object **pobj, int *prog_fd);
251int bpf_prog_load(const char *file, enum bpf_prog_type type, 259int bpf_prog_load(const char *file, enum bpf_prog_type type,
252 struct bpf_object **pobj, int *prog_fd); 260 struct bpf_object **pobj, int *prog_fd);
253 261
diff --git a/tools/testing/selftests/bpf/Makefile b/tools/testing/selftests/bpf/Makefile
index 5c43c187f27c..0a315ddabbf4 100644
--- a/tools/testing/selftests/bpf/Makefile
+++ b/tools/testing/selftests/bpf/Makefile
@@ -13,34 +13,49 @@ endif
13CFLAGS += -Wall -O2 -I$(APIDIR) -I$(LIBDIR) -I$(GENDIR) $(GENFLAGS) -I../../../include 13CFLAGS += -Wall -O2 -I$(APIDIR) -I$(LIBDIR) -I$(GENDIR) $(GENFLAGS) -I../../../include
14LDLIBS += -lcap -lelf -lrt -lpthread 14LDLIBS += -lcap -lelf -lrt -lpthread
15 15
16TEST_CUSTOM_PROGS = $(OUTPUT)/urandom_read
17all: $(TEST_CUSTOM_PROGS)
18
19$(TEST_CUSTOM_PROGS): urandom_read
20
21urandom_read: urandom_read.c
22 $(CC) -o $(TEST_CUSTOM_PROGS) -static $<
23
16# Order correspond to 'make run_tests' order 24# Order correspond to 'make run_tests' order
17TEST_GEN_PROGS = test_verifier test_tag test_maps test_lru_map test_lpm_map test_progs \ 25TEST_GEN_PROGS = test_verifier test_tag test_maps test_lru_map test_lpm_map test_progs \
18 test_align test_verifier_log test_dev_cgroup test_tcpbpf_user 26 test_align test_verifier_log test_dev_cgroup test_tcpbpf_user \
27 test_sock test_sock_addr
19 28
20TEST_GEN_FILES = test_pkt_access.o test_xdp.o test_l4lb.o test_tcp_estats.o test_obj_id.o \ 29TEST_GEN_FILES = test_pkt_access.o test_xdp.o test_l4lb.o test_tcp_estats.o test_obj_id.o \
21 test_pkt_md_access.o test_xdp_redirect.o test_xdp_meta.o sockmap_parse_prog.o \ 30 test_pkt_md_access.o test_xdp_redirect.o test_xdp_meta.o sockmap_parse_prog.o \
22 sockmap_verdict_prog.o dev_cgroup.o sample_ret0.o test_tracepoint.o \ 31 sockmap_verdict_prog.o dev_cgroup.o sample_ret0.o test_tracepoint.o \
23 test_l4lb_noinline.o test_xdp_noinline.o test_stacktrace_map.o \ 32 test_l4lb_noinline.o test_xdp_noinline.o test_stacktrace_map.o \
24 sample_map_ret0.o test_tcpbpf_kern.o 33 sample_map_ret0.o test_tcpbpf_kern.o test_stacktrace_build_id.o \
34 sockmap_tcp_msg_prog.o connect4_prog.o connect6_prog.o
25 35
26# Order correspond to 'make run_tests' order 36# Order correspond to 'make run_tests' order
27TEST_PROGS := test_kmod.sh \ 37TEST_PROGS := test_kmod.sh \
28 test_libbpf.sh \ 38 test_libbpf.sh \
29 test_xdp_redirect.sh \ 39 test_xdp_redirect.sh \
30 test_xdp_meta.sh \ 40 test_xdp_meta.sh \
31 test_offload.py 41 test_offload.py \
42 test_sock_addr.sh
32 43
33# Compile but not part of 'make run_tests' 44# Compile but not part of 'make run_tests'
34TEST_GEN_PROGS_EXTENDED = test_libbpf_open 45TEST_GEN_PROGS_EXTENDED = test_libbpf_open
35 46
36include ../lib.mk 47include ../lib.mk
37 48
38BPFOBJ := $(OUTPUT)/libbpf.a cgroup_helpers.c 49BPFOBJ := $(OUTPUT)/libbpf.a
39 50
40$(TEST_GEN_PROGS): $(BPFOBJ) 51$(TEST_GEN_PROGS): $(BPFOBJ)
41 52
42$(TEST_GEN_PROGS_EXTENDED): $(OUTPUT)/libbpf.a 53$(TEST_GEN_PROGS_EXTENDED): $(OUTPUT)/libbpf.a
43 54
55$(OUTPUT)/test_dev_cgroup: cgroup_helpers.c
56$(OUTPUT)/test_sock: cgroup_helpers.c
57$(OUTPUT)/test_sock_addr: cgroup_helpers.c
58
44.PHONY: force 59.PHONY: force
45 60
46# force a rebuild of BPFOBJ when its dependencies are updated 61# force a rebuild of BPFOBJ when its dependencies are updated
@@ -72,3 +87,5 @@ $(OUTPUT)/%.o: %.c
72 $(CLANG) $(CLANG_FLAGS) \ 87 $(CLANG) $(CLANG_FLAGS) \
73 -O2 -target bpf -emit-llvm -c $< -o - | \ 88 -O2 -target bpf -emit-llvm -c $< -o - | \
74 $(LLC) -march=bpf -mcpu=$(CPU) -filetype=obj -o $@ 89 $(LLC) -march=bpf -mcpu=$(CPU) -filetype=obj -o $@
90
91EXTRA_CLEAN := $(TEST_CUSTOM_PROGS)
diff --git a/tools/testing/selftests/bpf/bpf_helpers.h b/tools/testing/selftests/bpf/bpf_helpers.h
index dde2c11d7771..d8223d99f96d 100644
--- a/tools/testing/selftests/bpf/bpf_helpers.h
+++ b/tools/testing/selftests/bpf/bpf_helpers.h
@@ -86,6 +86,16 @@ static int (*bpf_perf_prog_read_value)(void *ctx, void *buf,
86 (void *) BPF_FUNC_perf_prog_read_value; 86 (void *) BPF_FUNC_perf_prog_read_value;
87static int (*bpf_override_return)(void *ctx, unsigned long rc) = 87static int (*bpf_override_return)(void *ctx, unsigned long rc) =
88 (void *) BPF_FUNC_override_return; 88 (void *) BPF_FUNC_override_return;
89static int (*bpf_msg_redirect_map)(void *ctx, void *map, int key, int flags) =
90 (void *) BPF_FUNC_msg_redirect_map;
91static int (*bpf_msg_apply_bytes)(void *ctx, int len) =
92 (void *) BPF_FUNC_msg_apply_bytes;
93static int (*bpf_msg_cork_bytes)(void *ctx, int len) =
94 (void *) BPF_FUNC_msg_cork_bytes;
95static int (*bpf_msg_pull_data)(void *ctx, int start, int end, int flags) =
96 (void *) BPF_FUNC_msg_pull_data;
97static int (*bpf_bind)(void *ctx, void *addr, int addr_len) =
98 (void *) BPF_FUNC_bind;
89 99
90/* llvm builtin functions that eBPF C program may use to 100/* llvm builtin functions that eBPF C program may use to
91 * emit BPF_LD_ABS and BPF_LD_IND instructions 101 * emit BPF_LD_ABS and BPF_LD_IND instructions
@@ -123,6 +133,8 @@ static int (*bpf_skb_under_cgroup)(void *ctx, void *map, int index) =
123 (void *) BPF_FUNC_skb_under_cgroup; 133 (void *) BPF_FUNC_skb_under_cgroup;
124static int (*bpf_skb_change_head)(void *, int len, int flags) = 134static int (*bpf_skb_change_head)(void *, int len, int flags) =
125 (void *) BPF_FUNC_skb_change_head; 135 (void *) BPF_FUNC_skb_change_head;
136static int (*bpf_skb_pull_data)(void *, int len) =
137 (void *) BPF_FUNC_skb_pull_data;
126 138
127/* Scan the ARCH passed in from ARCH env variable (see Makefile) */ 139/* Scan the ARCH passed in from ARCH env variable (see Makefile) */
128#if defined(__TARGET_ARCH_x86) 140#if defined(__TARGET_ARCH_x86)
diff --git a/tools/testing/selftests/bpf/bpf_rlimit.h b/tools/testing/selftests/bpf/bpf_rlimit.h
new file mode 100644
index 000000000000..9dac9b30f8ef
--- /dev/null
+++ b/tools/testing/selftests/bpf/bpf_rlimit.h
@@ -0,0 +1,28 @@
1#include <sys/resource.h>
2#include <stdio.h>
3
4static __attribute__((constructor)) void bpf_rlimit_ctor(void)
5{
6 struct rlimit rlim_old, rlim_new = {
7 .rlim_cur = RLIM_INFINITY,
8 .rlim_max = RLIM_INFINITY,
9 };
10
11 getrlimit(RLIMIT_MEMLOCK, &rlim_old);
12 /* For the sake of running the test cases, we temporarily
13 * set rlimit to infinity in order for kernel to focus on
14 * errors from actual test cases and not getting noise
15 * from hitting memlock limits. The limit is on per-process
16 * basis and not a global one, hence destructor not really
17 * needed here.
18 */
19 if (setrlimit(RLIMIT_MEMLOCK, &rlim_new) < 0) {
20 perror("Unable to lift memlock rlimit");
21 /* Trying out lower limit, but expect potential test
22 * case failures from this!
23 */
24 rlim_new.rlim_cur = rlim_old.rlim_cur + (1UL << 20);
25 rlim_new.rlim_max = rlim_old.rlim_max + (1UL << 20);
26 setrlimit(RLIMIT_MEMLOCK, &rlim_new);
27 }
28}
diff --git a/tools/testing/selftests/bpf/connect4_prog.c b/tools/testing/selftests/bpf/connect4_prog.c
new file mode 100644
index 000000000000..5a88a681d2ab
--- /dev/null
+++ b/tools/testing/selftests/bpf/connect4_prog.c
@@ -0,0 +1,45 @@
1// SPDX-License-Identifier: GPL-2.0
2// Copyright (c) 2018 Facebook
3
4#include <string.h>
5
6#include <linux/stddef.h>
7#include <linux/bpf.h>
8#include <linux/in.h>
9#include <linux/in6.h>
10#include <sys/socket.h>
11
12#include "bpf_helpers.h"
13#include "bpf_endian.h"
14
15#define SRC_REWRITE_IP4 0x7f000004U
16#define DST_REWRITE_IP4 0x7f000001U
17#define DST_REWRITE_PORT4 4444
18
19int _version SEC("version") = 1;
20
21SEC("cgroup/connect4")
22int connect_v4_prog(struct bpf_sock_addr *ctx)
23{
24 struct sockaddr_in sa;
25
26 /* Rewrite destination. */
27 ctx->user_ip4 = bpf_htonl(DST_REWRITE_IP4);
28 ctx->user_port = bpf_htons(DST_REWRITE_PORT4);
29
30 if (ctx->type == SOCK_DGRAM || ctx->type == SOCK_STREAM) {
31 ///* Rewrite source. */
32 memset(&sa, 0, sizeof(sa));
33
34 sa.sin_family = AF_INET;
35 sa.sin_port = bpf_htons(0);
36 sa.sin_addr.s_addr = bpf_htonl(SRC_REWRITE_IP4);
37
38 if (bpf_bind(ctx, (struct sockaddr *)&sa, sizeof(sa)) != 0)
39 return 0;
40 }
41
42 return 1;
43}
44
45char _license[] SEC("license") = "GPL";
diff --git a/tools/testing/selftests/bpf/connect6_prog.c b/tools/testing/selftests/bpf/connect6_prog.c
new file mode 100644
index 000000000000..8ea3f7d12dee
--- /dev/null
+++ b/tools/testing/selftests/bpf/connect6_prog.c
@@ -0,0 +1,61 @@
1// SPDX-License-Identifier: GPL-2.0
2// Copyright (c) 2018 Facebook
3
4#include <string.h>
5
6#include <linux/stddef.h>
7#include <linux/bpf.h>
8#include <linux/in.h>
9#include <linux/in6.h>
10#include <sys/socket.h>
11
12#include "bpf_helpers.h"
13#include "bpf_endian.h"
14
15#define SRC_REWRITE_IP6_0 0
16#define SRC_REWRITE_IP6_1 0
17#define SRC_REWRITE_IP6_2 0
18#define SRC_REWRITE_IP6_3 6
19
20#define DST_REWRITE_IP6_0 0
21#define DST_REWRITE_IP6_1 0
22#define DST_REWRITE_IP6_2 0
23#define DST_REWRITE_IP6_3 1
24
25#define DST_REWRITE_PORT6 6666
26
27int _version SEC("version") = 1;
28
29SEC("cgroup/connect6")
30int connect_v6_prog(struct bpf_sock_addr *ctx)
31{
32 struct sockaddr_in6 sa;
33
34 /* Rewrite destination. */
35 ctx->user_ip6[0] = bpf_htonl(DST_REWRITE_IP6_0);
36 ctx->user_ip6[1] = bpf_htonl(DST_REWRITE_IP6_1);
37 ctx->user_ip6[2] = bpf_htonl(DST_REWRITE_IP6_2);
38 ctx->user_ip6[3] = bpf_htonl(DST_REWRITE_IP6_3);
39
40 ctx->user_port = bpf_htons(DST_REWRITE_PORT6);
41
42 if (ctx->type == SOCK_DGRAM || ctx->type == SOCK_STREAM) {
43 /* Rewrite source. */
44 memset(&sa, 0, sizeof(sa));
45
46 sa.sin6_family = AF_INET6;
47 sa.sin6_port = bpf_htons(0);
48
49 sa.sin6_addr.s6_addr32[0] = bpf_htonl(SRC_REWRITE_IP6_0);
50 sa.sin6_addr.s6_addr32[1] = bpf_htonl(SRC_REWRITE_IP6_1);
51 sa.sin6_addr.s6_addr32[2] = bpf_htonl(SRC_REWRITE_IP6_2);
52 sa.sin6_addr.s6_addr32[3] = bpf_htonl(SRC_REWRITE_IP6_3);
53
54 if (bpf_bind(ctx, (struct sockaddr *)&sa, sizeof(sa)) != 0)
55 return 0;
56 }
57
58 return 1;
59}
60
61char _license[] SEC("license") = "GPL";
diff --git a/tools/testing/selftests/bpf/sockmap_parse_prog.c b/tools/testing/selftests/bpf/sockmap_parse_prog.c
index a1dec2b6d9c5..0f92858f6226 100644
--- a/tools/testing/selftests/bpf/sockmap_parse_prog.c
+++ b/tools/testing/selftests/bpf/sockmap_parse_prog.c
@@ -20,14 +20,25 @@ int bpf_prog1(struct __sk_buff *skb)
20 __u32 lport = skb->local_port; 20 __u32 lport = skb->local_port;
21 __u32 rport = skb->remote_port; 21 __u32 rport = skb->remote_port;
22 __u8 *d = data; 22 __u8 *d = data;
23 __u32 len = (__u32) data_end - (__u32) data;
24 int err;
23 25
24 if (data + 10 > data_end) 26 if (data + 10 > data_end) {
25 return skb->len; 27 err = bpf_skb_pull_data(skb, 10);
28 if (err)
29 return SK_DROP;
30
31 data_end = (void *)(long)skb->data_end;
32 data = (void *)(long)skb->data;
33 if (data + 10 > data_end)
34 return SK_DROP;
35 }
26 36
27 /* This write/read is a bit pointless but tests the verifier and 37 /* This write/read is a bit pointless but tests the verifier and
28 * strparser handler for read/write pkt data and access into sk 38 * strparser handler for read/write pkt data and access into sk
29 * fields. 39 * fields.
30 */ 40 */
41 d = data;
31 d[7] = 1; 42 d[7] = 1;
32 return skb->len; 43 return skb->len;
33} 44}
diff --git a/tools/testing/selftests/bpf/sockmap_tcp_msg_prog.c b/tools/testing/selftests/bpf/sockmap_tcp_msg_prog.c
new file mode 100644
index 000000000000..12a7b5c82ed6
--- /dev/null
+++ b/tools/testing/selftests/bpf/sockmap_tcp_msg_prog.c
@@ -0,0 +1,33 @@
1#include <linux/bpf.h>
2#include "bpf_helpers.h"
3#include "bpf_util.h"
4#include "bpf_endian.h"
5
6int _version SEC("version") = 1;
7
8#define bpf_printk(fmt, ...) \
9({ \
10 char ____fmt[] = fmt; \
11 bpf_trace_printk(____fmt, sizeof(____fmt), \
12 ##__VA_ARGS__); \
13})
14
15SEC("sk_msg1")
16int bpf_prog1(struct sk_msg_md *msg)
17{
18 void *data_end = (void *)(long) msg->data_end;
19 void *data = (void *)(long) msg->data;
20
21 char *d;
22
23 if (data + 8 > data_end)
24 return SK_DROP;
25
26 bpf_printk("data length %i\n", (__u64)msg->data_end - (__u64)msg->data);
27 d = (char *)data;
28 bpf_printk("hello sendmsg hook %i %i\n", d[0], d[1]);
29
30 return SK_PASS;
31}
32
33char _license[] SEC("license") = "GPL";
diff --git a/tools/testing/selftests/bpf/sockmap_verdict_prog.c b/tools/testing/selftests/bpf/sockmap_verdict_prog.c
index d7bea972cb21..2ce7634a4012 100644
--- a/tools/testing/selftests/bpf/sockmap_verdict_prog.c
+++ b/tools/testing/selftests/bpf/sockmap_verdict_prog.c
@@ -26,6 +26,13 @@ struct bpf_map_def SEC("maps") sock_map_tx = {
26 .max_entries = 20, 26 .max_entries = 20,
27}; 27};
28 28
29struct bpf_map_def SEC("maps") sock_map_msg = {
30 .type = BPF_MAP_TYPE_SOCKMAP,
31 .key_size = sizeof(int),
32 .value_size = sizeof(int),
33 .max_entries = 20,
34};
35
29struct bpf_map_def SEC("maps") sock_map_break = { 36struct bpf_map_def SEC("maps") sock_map_break = {
30 .type = BPF_MAP_TYPE_ARRAY, 37 .type = BPF_MAP_TYPE_ARRAY,
31 .key_size = sizeof(int), 38 .key_size = sizeof(int),
diff --git a/tools/testing/selftests/bpf/test_align.c b/tools/testing/selftests/bpf/test_align.c
index ff8bd7e3e50c..6b1b302310fe 100644
--- a/tools/testing/selftests/bpf/test_align.c
+++ b/tools/testing/selftests/bpf/test_align.c
@@ -9,8 +9,6 @@
9#include <stddef.h> 9#include <stddef.h>
10#include <stdbool.h> 10#include <stdbool.h>
11 11
12#include <sys/resource.h>
13
14#include <linux/unistd.h> 12#include <linux/unistd.h>
15#include <linux/filter.h> 13#include <linux/filter.h>
16#include <linux/bpf_perf_event.h> 14#include <linux/bpf_perf_event.h>
@@ -19,6 +17,7 @@
19#include <bpf/bpf.h> 17#include <bpf/bpf.h>
20 18
21#include "../../../include/linux/filter.h" 19#include "../../../include/linux/filter.h"
20#include "bpf_rlimit.h"
22 21
23#ifndef ARRAY_SIZE 22#ifndef ARRAY_SIZE
24# define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0])) 23# define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
@@ -702,9 +701,6 @@ static int do_test(unsigned int from, unsigned int to)
702int main(int argc, char **argv) 701int main(int argc, char **argv)
703{ 702{
704 unsigned int from = 0, to = ARRAY_SIZE(tests); 703 unsigned int from = 0, to = ARRAY_SIZE(tests);
705 struct rlimit rinf = { RLIM_INFINITY, RLIM_INFINITY };
706
707 setrlimit(RLIMIT_MEMLOCK, &rinf);
708 704
709 if (argc == 3) { 705 if (argc == 3) {
710 unsigned int l = atoi(argv[argc - 2]); 706 unsigned int l = atoi(argv[argc - 2]);
diff --git a/tools/testing/selftests/bpf/test_dev_cgroup.c b/tools/testing/selftests/bpf/test_dev_cgroup.c
index 3489cc283433..9c8b50bac7e0 100644
--- a/tools/testing/selftests/bpf/test_dev_cgroup.c
+++ b/tools/testing/selftests/bpf/test_dev_cgroup.c
@@ -11,13 +11,13 @@
11#include <errno.h> 11#include <errno.h>
12#include <assert.h> 12#include <assert.h>
13#include <sys/time.h> 13#include <sys/time.h>
14#include <sys/resource.h>
15 14
16#include <linux/bpf.h> 15#include <linux/bpf.h>
17#include <bpf/bpf.h> 16#include <bpf/bpf.h>
18#include <bpf/libbpf.h> 17#include <bpf/libbpf.h>
19 18
20#include "cgroup_helpers.h" 19#include "cgroup_helpers.h"
20#include "bpf_rlimit.h"
21 21
22#define DEV_CGROUP_PROG "./dev_cgroup.o" 22#define DEV_CGROUP_PROG "./dev_cgroup.o"
23 23
@@ -25,15 +25,11 @@
25 25
26int main(int argc, char **argv) 26int main(int argc, char **argv)
27{ 27{
28 struct rlimit limit = { RLIM_INFINITY, RLIM_INFINITY };
29 struct bpf_object *obj; 28 struct bpf_object *obj;
30 int error = EXIT_FAILURE; 29 int error = EXIT_FAILURE;
31 int prog_fd, cgroup_fd; 30 int prog_fd, cgroup_fd;
32 __u32 prog_cnt; 31 __u32 prog_cnt;
33 32
34 if (setrlimit(RLIMIT_MEMLOCK, &limit) < 0)
35 perror("Unable to lift memlock rlimit");
36
37 if (bpf_prog_load(DEV_CGROUP_PROG, BPF_PROG_TYPE_CGROUP_DEVICE, 33 if (bpf_prog_load(DEV_CGROUP_PROG, BPF_PROG_TYPE_CGROUP_DEVICE,
38 &obj, &prog_fd)) { 34 &obj, &prog_fd)) {
39 printf("Failed to load DEV_CGROUP program\n"); 35 printf("Failed to load DEV_CGROUP program\n");
diff --git a/tools/testing/selftests/bpf/test_lpm_map.c b/tools/testing/selftests/bpf/test_lpm_map.c
index 2be87e9ee28d..147e34cfceb7 100644
--- a/tools/testing/selftests/bpf/test_lpm_map.c
+++ b/tools/testing/selftests/bpf/test_lpm_map.c
@@ -22,10 +22,11 @@
22#include <unistd.h> 22#include <unistd.h>
23#include <arpa/inet.h> 23#include <arpa/inet.h>
24#include <sys/time.h> 24#include <sys/time.h>
25#include <sys/resource.h>
26 25
27#include <bpf/bpf.h> 26#include <bpf/bpf.h>
27
28#include "bpf_util.h" 28#include "bpf_util.h"
29#include "bpf_rlimit.h"
29 30
30struct tlpm_node { 31struct tlpm_node {
31 struct tlpm_node *next; 32 struct tlpm_node *next;
@@ -736,17 +737,11 @@ static void test_lpm_multi_thread(void)
736 737
737int main(void) 738int main(void)
738{ 739{
739 struct rlimit limit = { RLIM_INFINITY, RLIM_INFINITY }; 740 int i;
740 int i, ret;
741 741
742 /* we want predictable, pseudo random tests */ 742 /* we want predictable, pseudo random tests */
743 srand(0xf00ba1); 743 srand(0xf00ba1);
744 744
745 /* allow unlimited locked memory */
746 ret = setrlimit(RLIMIT_MEMLOCK, &limit);
747 if (ret < 0)
748 perror("Unable to lift memlock rlimit");
749
750 test_lpm_basic(); 745 test_lpm_basic();
751 test_lpm_order(); 746 test_lpm_order();
752 747
@@ -755,11 +750,8 @@ int main(void)
755 test_lpm_map(i); 750 test_lpm_map(i);
756 751
757 test_lpm_ipaddr(); 752 test_lpm_ipaddr();
758
759 test_lpm_delete(); 753 test_lpm_delete();
760
761 test_lpm_get_next_key(); 754 test_lpm_get_next_key();
762
763 test_lpm_multi_thread(); 755 test_lpm_multi_thread();
764 756
765 printf("test_lpm: OK\n"); 757 printf("test_lpm: OK\n");
diff --git a/tools/testing/selftests/bpf/test_lru_map.c b/tools/testing/selftests/bpf/test_lru_map.c
index 8c10c9180c1a..781c7de343be 100644
--- a/tools/testing/selftests/bpf/test_lru_map.c
+++ b/tools/testing/selftests/bpf/test_lru_map.c
@@ -16,10 +16,11 @@
16#include <time.h> 16#include <time.h>
17 17
18#include <sys/wait.h> 18#include <sys/wait.h>
19#include <sys/resource.h>
20 19
21#include <bpf/bpf.h> 20#include <bpf/bpf.h>
21
22#include "bpf_util.h" 22#include "bpf_util.h"
23#include "bpf_rlimit.h"
23 24
24#define LOCAL_FREE_TARGET (128) 25#define LOCAL_FREE_TARGET (128)
25#define PERCPU_FREE_TARGET (4) 26#define PERCPU_FREE_TARGET (4)
@@ -613,7 +614,6 @@ static void test_lru_sanity6(int map_type, int map_flags, int tgt_free)
613 614
614int main(int argc, char **argv) 615int main(int argc, char **argv)
615{ 616{
616 struct rlimit r = {RLIM_INFINITY, RLIM_INFINITY};
617 int map_types[] = {BPF_MAP_TYPE_LRU_HASH, 617 int map_types[] = {BPF_MAP_TYPE_LRU_HASH,
618 BPF_MAP_TYPE_LRU_PERCPU_HASH}; 618 BPF_MAP_TYPE_LRU_PERCPU_HASH};
619 int map_flags[] = {0, BPF_F_NO_COMMON_LRU}; 619 int map_flags[] = {0, BPF_F_NO_COMMON_LRU};
@@ -621,8 +621,6 @@ int main(int argc, char **argv)
621 621
622 setbuf(stdout, NULL); 622 setbuf(stdout, NULL);
623 623
624 assert(!setrlimit(RLIMIT_MEMLOCK, &r));
625
626 nr_cpus = bpf_num_possible_cpus(); 624 nr_cpus = bpf_num_possible_cpus();
627 assert(nr_cpus != -1); 625 assert(nr_cpus != -1);
628 printf("nr_cpus:%d\n\n", nr_cpus); 626 printf("nr_cpus:%d\n\n", nr_cpus);
diff --git a/tools/testing/selftests/bpf/test_maps.c b/tools/testing/selftests/bpf/test_maps.c
index 9e03a4c356a4..6c253343a6f9 100644
--- a/tools/testing/selftests/bpf/test_maps.c
+++ b/tools/testing/selftests/bpf/test_maps.c
@@ -17,13 +17,14 @@
17#include <stdlib.h> 17#include <stdlib.h>
18 18
19#include <sys/wait.h> 19#include <sys/wait.h>
20#include <sys/resource.h>
21 20
22#include <linux/bpf.h> 21#include <linux/bpf.h>
23 22
24#include <bpf/bpf.h> 23#include <bpf/bpf.h>
25#include <bpf/libbpf.h> 24#include <bpf/libbpf.h>
25
26#include "bpf_util.h" 26#include "bpf_util.h"
27#include "bpf_rlimit.h"
27 28
28static int map_flags; 29static int map_flags;
29 30
@@ -463,15 +464,17 @@ static void test_devmap(int task, void *data)
463#include <linux/err.h> 464#include <linux/err.h>
464#define SOCKMAP_PARSE_PROG "./sockmap_parse_prog.o" 465#define SOCKMAP_PARSE_PROG "./sockmap_parse_prog.o"
465#define SOCKMAP_VERDICT_PROG "./sockmap_verdict_prog.o" 466#define SOCKMAP_VERDICT_PROG "./sockmap_verdict_prog.o"
467#define SOCKMAP_TCP_MSG_PROG "./sockmap_tcp_msg_prog.o"
466static void test_sockmap(int tasks, void *data) 468static void test_sockmap(int tasks, void *data)
467{ 469{
468 int one = 1, map_fd_rx = 0, map_fd_tx = 0, map_fd_break, s, sc, rc; 470 struct bpf_map *bpf_map_rx, *bpf_map_tx, *bpf_map_msg, *bpf_map_break;
469 struct bpf_map *bpf_map_rx, *bpf_map_tx, *bpf_map_break; 471 int map_fd_msg = 0, map_fd_rx = 0, map_fd_tx = 0, map_fd_break;
470 int ports[] = {50200, 50201, 50202, 50204}; 472 int ports[] = {50200, 50201, 50202, 50204};
471 int err, i, fd, udp, sfd[6] = {0xdeadbeef}; 473 int err, i, fd, udp, sfd[6] = {0xdeadbeef};
472 u8 buf[20] = {0x0, 0x5, 0x3, 0x2, 0x1, 0x0}; 474 u8 buf[20] = {0x0, 0x5, 0x3, 0x2, 0x1, 0x0};
473 int parse_prog, verdict_prog; 475 int parse_prog, verdict_prog, msg_prog;
474 struct sockaddr_in addr; 476 struct sockaddr_in addr;
477 int one = 1, s, sc, rc;
475 struct bpf_object *obj; 478 struct bpf_object *obj;
476 struct timeval to; 479 struct timeval to;
477 __u32 key, value; 480 __u32 key, value;
@@ -583,6 +586,12 @@ static void test_sockmap(int tasks, void *data)
583 goto out_sockmap; 586 goto out_sockmap;
584 } 587 }
585 588
589 err = bpf_prog_attach(-1, fd, BPF_SK_MSG_VERDICT, 0);
590 if (!err) {
591 printf("Failed invalid msg verdict prog attach\n");
592 goto out_sockmap;
593 }
594
586 err = bpf_prog_attach(-1, fd, __MAX_BPF_ATTACH_TYPE, 0); 595 err = bpf_prog_attach(-1, fd, __MAX_BPF_ATTACH_TYPE, 0);
587 if (!err) { 596 if (!err) {
588 printf("Failed unknown prog attach\n"); 597 printf("Failed unknown prog attach\n");
@@ -601,6 +610,12 @@ static void test_sockmap(int tasks, void *data)
601 goto out_sockmap; 610 goto out_sockmap;
602 } 611 }
603 612
613 err = bpf_prog_detach(fd, BPF_SK_MSG_VERDICT);
614 if (err) {
615 printf("Failed empty msg verdict prog detach\n");
616 goto out_sockmap;
617 }
618
604 err = bpf_prog_detach(fd, __MAX_BPF_ATTACH_TYPE); 619 err = bpf_prog_detach(fd, __MAX_BPF_ATTACH_TYPE);
605 if (!err) { 620 if (!err) {
606 printf("Detach invalid prog successful\n"); 621 printf("Detach invalid prog successful\n");
@@ -615,6 +630,13 @@ static void test_sockmap(int tasks, void *data)
615 goto out_sockmap; 630 goto out_sockmap;
616 } 631 }
617 632
633 err = bpf_prog_load(SOCKMAP_TCP_MSG_PROG,
634 BPF_PROG_TYPE_SK_MSG, &obj, &msg_prog);
635 if (err) {
636 printf("Failed to load SK_SKB msg prog\n");
637 goto out_sockmap;
638 }
639
618 err = bpf_prog_load(SOCKMAP_VERDICT_PROG, 640 err = bpf_prog_load(SOCKMAP_VERDICT_PROG,
619 BPF_PROG_TYPE_SK_SKB, &obj, &verdict_prog); 641 BPF_PROG_TYPE_SK_SKB, &obj, &verdict_prog);
620 if (err) { 642 if (err) {
@@ -630,7 +652,7 @@ static void test_sockmap(int tasks, void *data)
630 652
631 map_fd_rx = bpf_map__fd(bpf_map_rx); 653 map_fd_rx = bpf_map__fd(bpf_map_rx);
632 if (map_fd_rx < 0) { 654 if (map_fd_rx < 0) {
633 printf("Failed to get map fd\n"); 655 printf("Failed to get map rx fd\n");
634 goto out_sockmap; 656 goto out_sockmap;
635 } 657 }
636 658
@@ -646,6 +668,18 @@ static void test_sockmap(int tasks, void *data)
646 goto out_sockmap; 668 goto out_sockmap;
647 } 669 }
648 670
671 bpf_map_msg = bpf_object__find_map_by_name(obj, "sock_map_msg");
672 if (IS_ERR(bpf_map_msg)) {
673 printf("Failed to load map msg from msg_verdict prog\n");
674 goto out_sockmap;
675 }
676
677 map_fd_msg = bpf_map__fd(bpf_map_msg);
678 if (map_fd_msg < 0) {
679 printf("Failed to get map msg fd\n");
680 goto out_sockmap;
681 }
682
649 bpf_map_break = bpf_object__find_map_by_name(obj, "sock_map_break"); 683 bpf_map_break = bpf_object__find_map_by_name(obj, "sock_map_break");
650 if (IS_ERR(bpf_map_break)) { 684 if (IS_ERR(bpf_map_break)) {
651 printf("Failed to load map tx from verdict prog\n"); 685 printf("Failed to load map tx from verdict prog\n");
@@ -679,6 +713,12 @@ static void test_sockmap(int tasks, void *data)
679 goto out_sockmap; 713 goto out_sockmap;
680 } 714 }
681 715
716 err = bpf_prog_attach(msg_prog, map_fd_msg, BPF_SK_MSG_VERDICT, 0);
717 if (err) {
718 printf("Failed msg verdict bpf prog attach\n");
719 goto out_sockmap;
720 }
721
682 err = bpf_prog_attach(verdict_prog, map_fd_rx, 722 err = bpf_prog_attach(verdict_prog, map_fd_rx,
683 __MAX_BPF_ATTACH_TYPE, 0); 723 __MAX_BPF_ATTACH_TYPE, 0);
684 if (!err) { 724 if (!err) {
@@ -718,6 +758,14 @@ static void test_sockmap(int tasks, void *data)
718 } 758 }
719 } 759 }
720 760
761 /* Put sfd[2] (sending fd below) into msg map to test sendmsg bpf */
762 i = 0;
763 err = bpf_map_update_elem(map_fd_msg, &i, &sfd[2], BPF_ANY);
764 if (err) {
765 printf("Failed map_fd_msg update sockmap %i\n", err);
766 goto out_sockmap;
767 }
768
721 /* Test map send/recv */ 769 /* Test map send/recv */
722 for (i = 0; i < 2; i++) { 770 for (i = 0; i < 2; i++) {
723 buf[0] = i; 771 buf[0] = i;
@@ -1126,10 +1174,6 @@ static void run_all_tests(void)
1126 1174
1127int main(void) 1175int main(void)
1128{ 1176{
1129 struct rlimit rinf = { RLIM_INFINITY, RLIM_INFINITY };
1130
1131 setrlimit(RLIMIT_MEMLOCK, &rinf);
1132
1133 map_flags = 0; 1177 map_flags = 0;
1134 run_all_tests(); 1178 run_all_tests();
1135 1179
diff --git a/tools/testing/selftests/bpf/test_progs.c b/tools/testing/selftests/bpf/test_progs.c
index b549308abd19..faadbe233966 100644
--- a/tools/testing/selftests/bpf/test_progs.c
+++ b/tools/testing/selftests/bpf/test_progs.c
@@ -26,7 +26,6 @@ typedef __u16 __sum16;
26 26
27#include <sys/ioctl.h> 27#include <sys/ioctl.h>
28#include <sys/wait.h> 28#include <sys/wait.h>
29#include <sys/resource.h>
30#include <sys/types.h> 29#include <sys/types.h>
31#include <fcntl.h> 30#include <fcntl.h>
32 31
@@ -34,9 +33,11 @@ typedef __u16 __sum16;
34#include <linux/err.h> 33#include <linux/err.h>
35#include <bpf/bpf.h> 34#include <bpf/bpf.h>
36#include <bpf/libbpf.h> 35#include <bpf/libbpf.h>
36
37#include "test_iptunnel_common.h" 37#include "test_iptunnel_common.h"
38#include "bpf_util.h" 38#include "bpf_util.h"
39#include "bpf_endian.h" 39#include "bpf_endian.h"
40#include "bpf_rlimit.h"
40 41
41static int error_cnt, pass_cnt; 42static int error_cnt, pass_cnt;
42 43
@@ -840,7 +841,8 @@ static void test_tp_attach_query(void)
840static int compare_map_keys(int map1_fd, int map2_fd) 841static int compare_map_keys(int map1_fd, int map2_fd)
841{ 842{
842 __u32 key, next_key; 843 __u32 key, next_key;
843 char val_buf[PERF_MAX_STACK_DEPTH * sizeof(__u64)]; 844 char val_buf[PERF_MAX_STACK_DEPTH *
845 sizeof(struct bpf_stack_build_id)];
844 int err; 846 int err;
845 847
846 err = bpf_map_get_next_key(map1_fd, NULL, &key); 848 err = bpf_map_get_next_key(map1_fd, NULL, &key);
@@ -875,7 +877,7 @@ static void test_stacktrace_map()
875 877
876 err = bpf_prog_load(file, BPF_PROG_TYPE_TRACEPOINT, &obj, &prog_fd); 878 err = bpf_prog_load(file, BPF_PROG_TYPE_TRACEPOINT, &obj, &prog_fd);
877 if (CHECK(err, "prog_load", "err %d errno %d\n", err, errno)) 879 if (CHECK(err, "prog_load", "err %d errno %d\n", err, errno))
878 goto out; 880 return;
879 881
880 /* Get the ID for the sched/sched_switch tracepoint */ 882 /* Get the ID for the sched/sched_switch tracepoint */
881 snprintf(buf, sizeof(buf), 883 snprintf(buf, sizeof(buf),
@@ -886,6 +888,181 @@ static void test_stacktrace_map()
886 888
887 bytes = read(efd, buf, sizeof(buf)); 889 bytes = read(efd, buf, sizeof(buf));
888 close(efd); 890 close(efd);
891 if (bytes <= 0 || bytes >= sizeof(buf))
892 goto close_prog;
893
894 /* Open the perf event and attach bpf progrram */
895 attr.config = strtol(buf, NULL, 0);
896 attr.type = PERF_TYPE_TRACEPOINT;
897 attr.sample_type = PERF_SAMPLE_RAW | PERF_SAMPLE_CALLCHAIN;
898 attr.sample_period = 1;
899 attr.wakeup_events = 1;
900 pmu_fd = syscall(__NR_perf_event_open, &attr, -1 /* pid */,
901 0 /* cpu 0 */, -1 /* group id */,
902 0 /* flags */);
903 if (CHECK(pmu_fd < 0, "perf_event_open", "err %d errno %d\n",
904 pmu_fd, errno))
905 goto close_prog;
906
907 err = ioctl(pmu_fd, PERF_EVENT_IOC_ENABLE, 0);
908 if (err)
909 goto disable_pmu;
910
911 err = ioctl(pmu_fd, PERF_EVENT_IOC_SET_BPF, prog_fd);
912 if (err)
913 goto disable_pmu;
914
915 /* find map fds */
916 control_map_fd = bpf_find_map(__func__, obj, "control_map");
917 if (control_map_fd < 0)
918 goto disable_pmu;
919
920 stackid_hmap_fd = bpf_find_map(__func__, obj, "stackid_hmap");
921 if (stackid_hmap_fd < 0)
922 goto disable_pmu;
923
924 stackmap_fd = bpf_find_map(__func__, obj, "stackmap");
925 if (stackmap_fd < 0)
926 goto disable_pmu;
927
928 /* give some time for bpf program run */
929 sleep(1);
930
931 /* disable stack trace collection */
932 key = 0;
933 val = 1;
934 bpf_map_update_elem(control_map_fd, &key, &val, 0);
935
936 /* for every element in stackid_hmap, we can find a corresponding one
937 * in stackmap, and vise versa.
938 */
939 err = compare_map_keys(stackid_hmap_fd, stackmap_fd);
940 if (CHECK(err, "compare_map_keys stackid_hmap vs. stackmap",
941 "err %d errno %d\n", err, errno))
942 goto disable_pmu_noerr;
943
944 err = compare_map_keys(stackmap_fd, stackid_hmap_fd);
945 if (CHECK(err, "compare_map_keys stackmap vs. stackid_hmap",
946 "err %d errno %d\n", err, errno))
947 goto disable_pmu_noerr;
948
949 goto disable_pmu_noerr;
950disable_pmu:
951 error_cnt++;
952disable_pmu_noerr:
953 ioctl(pmu_fd, PERF_EVENT_IOC_DISABLE);
954 close(pmu_fd);
955close_prog:
956 bpf_object__close(obj);
957}
958
959static void test_stacktrace_map_raw_tp()
960{
961 int control_map_fd, stackid_hmap_fd, stackmap_fd;
962 const char *file = "./test_stacktrace_map.o";
963 int efd, err, prog_fd;
964 __u32 key, val, duration = 0;
965 struct bpf_object *obj;
966
967 err = bpf_prog_load(file, BPF_PROG_TYPE_RAW_TRACEPOINT, &obj, &prog_fd);
968 if (CHECK(err, "prog_load raw tp", "err %d errno %d\n", err, errno))
969 return;
970
971 efd = bpf_raw_tracepoint_open("sched_switch", prog_fd);
972 if (CHECK(efd < 0, "raw_tp_open", "err %d errno %d\n", efd, errno))
973 goto close_prog;
974
975 /* find map fds */
976 control_map_fd = bpf_find_map(__func__, obj, "control_map");
977 if (control_map_fd < 0)
978 goto close_prog;
979
980 stackid_hmap_fd = bpf_find_map(__func__, obj, "stackid_hmap");
981 if (stackid_hmap_fd < 0)
982 goto close_prog;
983
984 stackmap_fd = bpf_find_map(__func__, obj, "stackmap");
985 if (stackmap_fd < 0)
986 goto close_prog;
987
988 /* give some time for bpf program run */
989 sleep(1);
990
991 /* disable stack trace collection */
992 key = 0;
993 val = 1;
994 bpf_map_update_elem(control_map_fd, &key, &val, 0);
995
996 /* for every element in stackid_hmap, we can find a corresponding one
997 * in stackmap, and vise versa.
998 */
999 err = compare_map_keys(stackid_hmap_fd, stackmap_fd);
1000 if (CHECK(err, "compare_map_keys stackid_hmap vs. stackmap",
1001 "err %d errno %d\n", err, errno))
1002 goto close_prog;
1003
1004 err = compare_map_keys(stackmap_fd, stackid_hmap_fd);
1005 if (CHECK(err, "compare_map_keys stackmap vs. stackid_hmap",
1006 "err %d errno %d\n", err, errno))
1007 goto close_prog;
1008
1009 goto close_prog_noerr;
1010close_prog:
1011 error_cnt++;
1012close_prog_noerr:
1013 bpf_object__close(obj);
1014}
1015
1016static int extract_build_id(char *build_id, size_t size)
1017{
1018 FILE *fp;
1019 char *line = NULL;
1020 size_t len = 0;
1021
1022 fp = popen("readelf -n ./urandom_read | grep 'Build ID'", "r");
1023 if (fp == NULL)
1024 return -1;
1025
1026 if (getline(&line, &len, fp) == -1)
1027 goto err;
1028 fclose(fp);
1029
1030 if (len > size)
1031 len = size;
1032 memcpy(build_id, line, len);
1033 build_id[len] = '\0';
1034 return 0;
1035err:
1036 fclose(fp);
1037 return -1;
1038}
1039
1040static void test_stacktrace_build_id(void)
1041{
1042 int control_map_fd, stackid_hmap_fd, stackmap_fd;
1043 const char *file = "./test_stacktrace_build_id.o";
1044 int bytes, efd, err, pmu_fd, prog_fd;
1045 struct perf_event_attr attr = {};
1046 __u32 key, previous_key, val, duration = 0;
1047 struct bpf_object *obj;
1048 char buf[256];
1049 int i, j;
1050 struct bpf_stack_build_id id_offs[PERF_MAX_STACK_DEPTH];
1051 int build_id_matches = 0;
1052
1053 err = bpf_prog_load(file, BPF_PROG_TYPE_TRACEPOINT, &obj, &prog_fd);
1054 if (CHECK(err, "prog_load", "err %d errno %d\n", err, errno))
1055 goto out;
1056
1057 /* Get the ID for the sched/sched_switch tracepoint */
1058 snprintf(buf, sizeof(buf),
1059 "/sys/kernel/debug/tracing/events/random/urandom_read/id");
1060 efd = open(buf, O_RDONLY, 0);
1061 if (CHECK(efd < 0, "open", "err %d errno %d\n", efd, errno))
1062 goto close_prog;
1063
1064 bytes = read(efd, buf, sizeof(buf));
1065 close(efd);
889 if (CHECK(bytes <= 0 || bytes >= sizeof(buf), 1066 if (CHECK(bytes <= 0 || bytes >= sizeof(buf),
890 "read", "bytes %d errno %d\n", bytes, errno)) 1067 "read", "bytes %d errno %d\n", bytes, errno))
891 goto close_prog; 1068 goto close_prog;
@@ -929,9 +1106,9 @@ static void test_stacktrace_map()
929 err, errno)) 1106 err, errno))
930 goto disable_pmu; 1107 goto disable_pmu;
931 1108
932 /* give some time for bpf program run */ 1109 assert(system("dd if=/dev/urandom of=/dev/zero count=4 2> /dev/null")
933 sleep(1); 1110 == 0);
934 1111 assert(system("./urandom_read if=/dev/urandom of=/dev/zero count=4 2> /dev/null") == 0);
935 /* disable stack trace collection */ 1112 /* disable stack trace collection */
936 key = 0; 1113 key = 0;
937 val = 1; 1114 val = 1;
@@ -948,7 +1125,40 @@ static void test_stacktrace_map()
948 err = compare_map_keys(stackmap_fd, stackid_hmap_fd); 1125 err = compare_map_keys(stackmap_fd, stackid_hmap_fd);
949 if (CHECK(err, "compare_map_keys stackmap vs. stackid_hmap", 1126 if (CHECK(err, "compare_map_keys stackmap vs. stackid_hmap",
950 "err %d errno %d\n", err, errno)) 1127 "err %d errno %d\n", err, errno))
951 ; /* fall through */ 1128 goto disable_pmu;
1129
1130 err = extract_build_id(buf, 256);
1131
1132 if (CHECK(err, "get build_id with readelf",
1133 "err %d errno %d\n", err, errno))
1134 goto disable_pmu;
1135
1136 err = bpf_map_get_next_key(stackmap_fd, NULL, &key);
1137 if (CHECK(err, "get_next_key from stackmap",
1138 "err %d, errno %d\n", err, errno))
1139 goto disable_pmu;
1140
1141 do {
1142 char build_id[64];
1143
1144 err = bpf_map_lookup_elem(stackmap_fd, &key, id_offs);
1145 if (CHECK(err, "lookup_elem from stackmap",
1146 "err %d, errno %d\n", err, errno))
1147 goto disable_pmu;
1148 for (i = 0; i < PERF_MAX_STACK_DEPTH; ++i)
1149 if (id_offs[i].status == BPF_STACK_BUILD_ID_VALID &&
1150 id_offs[i].offset != 0) {
1151 for (j = 0; j < 20; ++j)
1152 sprintf(build_id + 2 * j, "%02x",
1153 id_offs[i].build_id[j] & 0xff);
1154 if (strstr(buf, build_id) != NULL)
1155 build_id_matches = 1;
1156 }
1157 previous_key = key;
1158 } while (bpf_map_get_next_key(stackmap_fd, &previous_key, &key) == 0);
1159
1160 CHECK(build_id_matches < 1, "build id match",
1161 "Didn't find expected build ID from the map");
952 1162
953disable_pmu: 1163disable_pmu:
954 ioctl(pmu_fd, PERF_EVENT_IOC_DISABLE); 1164 ioctl(pmu_fd, PERF_EVENT_IOC_DISABLE);
@@ -965,10 +1175,6 @@ out:
965 1175
966int main(void) 1176int main(void)
967{ 1177{
968 struct rlimit rinf = { RLIM_INFINITY, RLIM_INFINITY };
969
970 setrlimit(RLIMIT_MEMLOCK, &rinf);
971
972 test_pkt_access(); 1178 test_pkt_access();
973 test_xdp(); 1179 test_xdp();
974 test_l4lb_all(); 1180 test_l4lb_all();
@@ -979,6 +1185,8 @@ int main(void)
979 test_obj_name(); 1185 test_obj_name();
980 test_tp_attach_query(); 1186 test_tp_attach_query();
981 test_stacktrace_map(); 1187 test_stacktrace_map();
1188 test_stacktrace_build_id();
1189 test_stacktrace_map_raw_tp();
982 1190
983 printf("Summary: %d PASSED, %d FAILED\n", pass_cnt, error_cnt); 1191 printf("Summary: %d PASSED, %d FAILED\n", pass_cnt, error_cnt);
984 return error_cnt ? EXIT_FAILURE : EXIT_SUCCESS; 1192 return error_cnt ? EXIT_FAILURE : EXIT_SUCCESS;
diff --git a/tools/testing/selftests/bpf/test_sock.c b/tools/testing/selftests/bpf/test_sock.c
new file mode 100644
index 000000000000..73bb20cfb9b7
--- /dev/null
+++ b/tools/testing/selftests/bpf/test_sock.c
@@ -0,0 +1,479 @@
1// SPDX-License-Identifier: GPL-2.0
2// Copyright (c) 2018 Facebook
3
4#include <stdio.h>
5#include <unistd.h>
6
7#include <arpa/inet.h>
8#include <sys/types.h>
9#include <sys/socket.h>
10
11#include <linux/filter.h>
12
13#include <bpf/bpf.h>
14
15#include "cgroup_helpers.h"
16
17#ifndef ARRAY_SIZE
18# define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
19#endif
20
21#define CG_PATH "/foo"
22#define MAX_INSNS 512
23
24char bpf_log_buf[BPF_LOG_BUF_SIZE];
25
26struct sock_test {
27 const char *descr;
28 /* BPF prog properties */
29 struct bpf_insn insns[MAX_INSNS];
30 enum bpf_attach_type expected_attach_type;
31 enum bpf_attach_type attach_type;
32 /* Socket properties */
33 int domain;
34 int type;
35 /* Endpoint to bind() to */
36 const char *ip;
37 unsigned short port;
38 /* Expected test result */
39 enum {
40 LOAD_REJECT,
41 ATTACH_REJECT,
42 BIND_REJECT,
43 SUCCESS,
44 } result;
45};
46
47static struct sock_test tests[] = {
48 {
49 "bind4 load with invalid access: src_ip6",
50 .insns = {
51 BPF_MOV64_REG(BPF_REG_6, BPF_REG_1),
52 BPF_LDX_MEM(BPF_W, BPF_REG_7, BPF_REG_6,
53 offsetof(struct bpf_sock, src_ip6[0])),
54 BPF_MOV64_IMM(BPF_REG_0, 1),
55 BPF_EXIT_INSN(),
56 },
57 BPF_CGROUP_INET4_POST_BIND,
58 BPF_CGROUP_INET4_POST_BIND,
59 0,
60 0,
61 NULL,
62 0,
63 LOAD_REJECT,
64 },
65 {
66 "bind4 load with invalid access: mark",
67 .insns = {
68 BPF_MOV64_REG(BPF_REG_6, BPF_REG_1),
69 BPF_LDX_MEM(BPF_W, BPF_REG_7, BPF_REG_6,
70 offsetof(struct bpf_sock, mark)),
71 BPF_MOV64_IMM(BPF_REG_0, 1),
72 BPF_EXIT_INSN(),
73 },
74 BPF_CGROUP_INET4_POST_BIND,
75 BPF_CGROUP_INET4_POST_BIND,
76 0,
77 0,
78 NULL,
79 0,
80 LOAD_REJECT,
81 },
82 {
83 "bind6 load with invalid access: src_ip4",
84 .insns = {
85 BPF_MOV64_REG(BPF_REG_6, BPF_REG_1),
86 BPF_LDX_MEM(BPF_W, BPF_REG_7, BPF_REG_6,
87 offsetof(struct bpf_sock, src_ip4)),
88 BPF_MOV64_IMM(BPF_REG_0, 1),
89 BPF_EXIT_INSN(),
90 },
91 BPF_CGROUP_INET6_POST_BIND,
92 BPF_CGROUP_INET6_POST_BIND,
93 0,
94 0,
95 NULL,
96 0,
97 LOAD_REJECT,
98 },
99 {
100 "sock_create load with invalid access: src_port",
101 .insns = {
102 BPF_MOV64_REG(BPF_REG_6, BPF_REG_1),
103 BPF_LDX_MEM(BPF_W, BPF_REG_7, BPF_REG_6,
104 offsetof(struct bpf_sock, src_port)),
105 BPF_MOV64_IMM(BPF_REG_0, 1),
106 BPF_EXIT_INSN(),
107 },
108 BPF_CGROUP_INET_SOCK_CREATE,
109 BPF_CGROUP_INET_SOCK_CREATE,
110 0,
111 0,
112 NULL,
113 0,
114 LOAD_REJECT,
115 },
116 {
117 "sock_create load w/o expected_attach_type (compat mode)",
118 .insns = {
119 BPF_MOV64_IMM(BPF_REG_0, 1),
120 BPF_EXIT_INSN(),
121 },
122 0,
123 BPF_CGROUP_INET_SOCK_CREATE,
124 AF_INET,
125 SOCK_STREAM,
126 "127.0.0.1",
127 8097,
128 SUCCESS,
129 },
130 {
131 "sock_create load w/ expected_attach_type",
132 .insns = {
133 BPF_MOV64_IMM(BPF_REG_0, 1),
134 BPF_EXIT_INSN(),
135 },
136 BPF_CGROUP_INET_SOCK_CREATE,
137 BPF_CGROUP_INET_SOCK_CREATE,
138 AF_INET,
139 SOCK_STREAM,
140 "127.0.0.1",
141 8097,
142 SUCCESS,
143 },
144 {
145 "attach type mismatch bind4 vs bind6",
146 .insns = {
147 BPF_MOV64_IMM(BPF_REG_0, 1),
148 BPF_EXIT_INSN(),
149 },
150 BPF_CGROUP_INET4_POST_BIND,
151 BPF_CGROUP_INET6_POST_BIND,
152 0,
153 0,
154 NULL,
155 0,
156 ATTACH_REJECT,
157 },
158 {
159 "attach type mismatch bind6 vs bind4",
160 .insns = {
161 BPF_MOV64_IMM(BPF_REG_0, 1),
162 BPF_EXIT_INSN(),
163 },
164 BPF_CGROUP_INET6_POST_BIND,
165 BPF_CGROUP_INET4_POST_BIND,
166 0,
167 0,
168 NULL,
169 0,
170 ATTACH_REJECT,
171 },
172 {
173 "attach type mismatch default vs bind4",
174 .insns = {
175 BPF_MOV64_IMM(BPF_REG_0, 1),
176 BPF_EXIT_INSN(),
177 },
178 0,
179 BPF_CGROUP_INET4_POST_BIND,
180 0,
181 0,
182 NULL,
183 0,
184 ATTACH_REJECT,
185 },
186 {
187 "attach type mismatch bind6 vs sock_create",
188 .insns = {
189 BPF_MOV64_IMM(BPF_REG_0, 1),
190 BPF_EXIT_INSN(),
191 },
192 BPF_CGROUP_INET6_POST_BIND,
193 BPF_CGROUP_INET_SOCK_CREATE,
194 0,
195 0,
196 NULL,
197 0,
198 ATTACH_REJECT,
199 },
200 {
201 "bind4 reject all",
202 .insns = {
203 BPF_MOV64_IMM(BPF_REG_0, 0),
204 BPF_EXIT_INSN(),
205 },
206 BPF_CGROUP_INET4_POST_BIND,
207 BPF_CGROUP_INET4_POST_BIND,
208 AF_INET,
209 SOCK_STREAM,
210 "0.0.0.0",
211 0,
212 BIND_REJECT,
213 },
214 {
215 "bind6 reject all",
216 .insns = {
217 BPF_MOV64_IMM(BPF_REG_0, 0),
218 BPF_EXIT_INSN(),
219 },
220 BPF_CGROUP_INET6_POST_BIND,
221 BPF_CGROUP_INET6_POST_BIND,
222 AF_INET6,
223 SOCK_STREAM,
224 "::",
225 0,
226 BIND_REJECT,
227 },
228 {
229 "bind6 deny specific IP & port",
230 .insns = {
231 BPF_MOV64_REG(BPF_REG_6, BPF_REG_1),
232
233 /* if (ip == expected && port == expected) */
234 BPF_LDX_MEM(BPF_W, BPF_REG_7, BPF_REG_6,
235 offsetof(struct bpf_sock, src_ip6[3])),
236 BPF_JMP_IMM(BPF_JNE, BPF_REG_7, 0x01000000, 4),
237 BPF_LDX_MEM(BPF_W, BPF_REG_7, BPF_REG_6,
238 offsetof(struct bpf_sock, src_port)),
239 BPF_JMP_IMM(BPF_JNE, BPF_REG_7, 0x2001, 2),
240
241 /* return DENY; */
242 BPF_MOV64_IMM(BPF_REG_0, 0),
243 BPF_JMP_A(1),
244
245 /* else return ALLOW; */
246 BPF_MOV64_IMM(BPF_REG_0, 1),
247 BPF_EXIT_INSN(),
248 },
249 BPF_CGROUP_INET6_POST_BIND,
250 BPF_CGROUP_INET6_POST_BIND,
251 AF_INET6,
252 SOCK_STREAM,
253 "::1",
254 8193,
255 BIND_REJECT,
256 },
257 {
258 "bind4 allow specific IP & port",
259 .insns = {
260 BPF_MOV64_REG(BPF_REG_6, BPF_REG_1),
261
262 /* if (ip == expected && port == expected) */
263 BPF_LDX_MEM(BPF_W, BPF_REG_7, BPF_REG_6,
264 offsetof(struct bpf_sock, src_ip4)),
265 BPF_JMP_IMM(BPF_JNE, BPF_REG_7, 0x0100007F, 4),
266 BPF_LDX_MEM(BPF_W, BPF_REG_7, BPF_REG_6,
267 offsetof(struct bpf_sock, src_port)),
268 BPF_JMP_IMM(BPF_JNE, BPF_REG_7, 0x1002, 2),
269
270 /* return ALLOW; */
271 BPF_MOV64_IMM(BPF_REG_0, 1),
272 BPF_JMP_A(1),
273
274 /* else return DENY; */
275 BPF_MOV64_IMM(BPF_REG_0, 0),
276 BPF_EXIT_INSN(),
277 },
278 BPF_CGROUP_INET4_POST_BIND,
279 BPF_CGROUP_INET4_POST_BIND,
280 AF_INET,
281 SOCK_STREAM,
282 "127.0.0.1",
283 4098,
284 SUCCESS,
285 },
286 {
287 "bind4 allow all",
288 .insns = {
289 BPF_MOV64_IMM(BPF_REG_0, 1),
290 BPF_EXIT_INSN(),
291 },
292 BPF_CGROUP_INET4_POST_BIND,
293 BPF_CGROUP_INET4_POST_BIND,
294 AF_INET,
295 SOCK_STREAM,
296 "0.0.0.0",
297 0,
298 SUCCESS,
299 },
300 {
301 "bind6 allow all",
302 .insns = {
303 BPF_MOV64_IMM(BPF_REG_0, 1),
304 BPF_EXIT_INSN(),
305 },
306 BPF_CGROUP_INET6_POST_BIND,
307 BPF_CGROUP_INET6_POST_BIND,
308 AF_INET6,
309 SOCK_STREAM,
310 "::",
311 0,
312 SUCCESS,
313 },
314};
315
316static size_t probe_prog_length(const struct bpf_insn *fp)
317{
318 size_t len;
319
320 for (len = MAX_INSNS - 1; len > 0; --len)
321 if (fp[len].code != 0 || fp[len].imm != 0)
322 break;
323 return len + 1;
324}
325
326static int load_sock_prog(const struct bpf_insn *prog,
327 enum bpf_attach_type attach_type)
328{
329 struct bpf_load_program_attr attr;
330
331 memset(&attr, 0, sizeof(struct bpf_load_program_attr));
332 attr.prog_type = BPF_PROG_TYPE_CGROUP_SOCK;
333 attr.expected_attach_type = attach_type;
334 attr.insns = prog;
335 attr.insns_cnt = probe_prog_length(attr.insns);
336 attr.license = "GPL";
337
338 return bpf_load_program_xattr(&attr, bpf_log_buf, BPF_LOG_BUF_SIZE);
339}
340
341static int attach_sock_prog(int cgfd, int progfd,
342 enum bpf_attach_type attach_type)
343{
344 return bpf_prog_attach(progfd, cgfd, attach_type, BPF_F_ALLOW_OVERRIDE);
345}
346
347static int bind_sock(int domain, int type, const char *ip, unsigned short port)
348{
349 struct sockaddr_storage addr;
350 struct sockaddr_in6 *addr6;
351 struct sockaddr_in *addr4;
352 int sockfd = -1;
353 socklen_t len;
354 int err = 0;
355
356 sockfd = socket(domain, type, 0);
357 if (sockfd < 0)
358 goto err;
359
360 memset(&addr, 0, sizeof(addr));
361
362 if (domain == AF_INET) {
363 len = sizeof(struct sockaddr_in);
364 addr4 = (struct sockaddr_in *)&addr;
365 addr4->sin_family = domain;
366 addr4->sin_port = htons(port);
367 if (inet_pton(domain, ip, (void *)&addr4->sin_addr) != 1)
368 goto err;
369 } else if (domain == AF_INET6) {
370 len = sizeof(struct sockaddr_in6);
371 addr6 = (struct sockaddr_in6 *)&addr;
372 addr6->sin6_family = domain;
373 addr6->sin6_port = htons(port);
374 if (inet_pton(domain, ip, (void *)&addr6->sin6_addr) != 1)
375 goto err;
376 } else {
377 goto err;
378 }
379
380 if (bind(sockfd, (const struct sockaddr *)&addr, len) == -1)
381 goto err;
382
383 goto out;
384err:
385 err = -1;
386out:
387 close(sockfd);
388 return err;
389}
390
391static int run_test_case(int cgfd, const struct sock_test *test)
392{
393 int progfd = -1;
394 int err = 0;
395
396 printf("Test case: %s .. ", test->descr);
397 progfd = load_sock_prog(test->insns, test->expected_attach_type);
398 if (progfd < 0) {
399 if (test->result == LOAD_REJECT)
400 goto out;
401 else
402 goto err;
403 }
404
405 if (attach_sock_prog(cgfd, progfd, test->attach_type) == -1) {
406 if (test->result == ATTACH_REJECT)
407 goto out;
408 else
409 goto err;
410 }
411
412 if (bind_sock(test->domain, test->type, test->ip, test->port) == -1) {
413 /* sys_bind() may fail for different reasons, errno has to be
414 * checked to confirm that BPF program rejected it.
415 */
416 if (test->result == BIND_REJECT && errno == EPERM)
417 goto out;
418 else
419 goto err;
420 }
421
422
423 if (test->result != SUCCESS)
424 goto err;
425
426 goto out;
427err:
428 err = -1;
429out:
430 /* Detaching w/o checking return code: best effort attempt. */
431 if (progfd != -1)
432 bpf_prog_detach(cgfd, test->attach_type);
433 close(progfd);
434 printf("[%s]\n", err ? "FAIL" : "PASS");
435 return err;
436}
437
438static int run_tests(int cgfd)
439{
440 int passes = 0;
441 int fails = 0;
442 int i;
443
444 for (i = 0; i < ARRAY_SIZE(tests); ++i) {
445 if (run_test_case(cgfd, &tests[i]))
446 ++fails;
447 else
448 ++passes;
449 }
450 printf("Summary: %d PASSED, %d FAILED\n", passes, fails);
451 return fails ? -1 : 0;
452}
453
454int main(int argc, char **argv)
455{
456 int cgfd = -1;
457 int err = 0;
458
459 if (setup_cgroup_environment())
460 goto err;
461
462 cgfd = create_and_get_cgroup(CG_PATH);
463 if (!cgfd)
464 goto err;
465
466 if (join_cgroup(CG_PATH))
467 goto err;
468
469 if (run_tests(cgfd))
470 goto err;
471
472 goto out;
473err:
474 err = -1;
475out:
476 close(cgfd);
477 cleanup_cgroup_environment();
478 return err;
479}
diff --git a/tools/testing/selftests/bpf/test_sock_addr.c b/tools/testing/selftests/bpf/test_sock_addr.c
new file mode 100644
index 000000000000..d488f20926e8
--- /dev/null
+++ b/tools/testing/selftests/bpf/test_sock_addr.c
@@ -0,0 +1,588 @@
1// SPDX-License-Identifier: GPL-2.0
2// Copyright (c) 2018 Facebook
3
4#include <stdio.h>
5#include <stdlib.h>
6#include <unistd.h>
7
8#include <arpa/inet.h>
9#include <sys/types.h>
10#include <sys/socket.h>
11
12#include <linux/filter.h>
13
14#include <bpf/bpf.h>
15#include <bpf/libbpf.h>
16
17#include "cgroup_helpers.h"
18
19#define CG_PATH "/foo"
20#define CONNECT4_PROG_PATH "./connect4_prog.o"
21#define CONNECT6_PROG_PATH "./connect6_prog.o"
22
23#define SERV4_IP "192.168.1.254"
24#define SERV4_REWRITE_IP "127.0.0.1"
25#define SERV4_PORT 4040
26#define SERV4_REWRITE_PORT 4444
27
28#define SERV6_IP "face:b00c:1234:5678::abcd"
29#define SERV6_REWRITE_IP "::1"
30#define SERV6_PORT 6060
31#define SERV6_REWRITE_PORT 6666
32
33#define INET_NTOP_BUF 40
34
35typedef int (*load_fn)(enum bpf_attach_type, const char *comment);
36typedef int (*info_fn)(int, struct sockaddr *, socklen_t *);
37
38struct program {
39 enum bpf_attach_type type;
40 load_fn loadfn;
41 int fd;
42 const char *name;
43 enum bpf_attach_type invalid_type;
44};
45
46char bpf_log_buf[BPF_LOG_BUF_SIZE];
47
48static int mk_sockaddr(int domain, const char *ip, unsigned short port,
49 struct sockaddr *addr, socklen_t addr_len)
50{
51 struct sockaddr_in6 *addr6;
52 struct sockaddr_in *addr4;
53
54 if (domain != AF_INET && domain != AF_INET6) {
55 log_err("Unsupported address family");
56 return -1;
57 }
58
59 memset(addr, 0, addr_len);
60
61 if (domain == AF_INET) {
62 if (addr_len < sizeof(struct sockaddr_in))
63 return -1;
64 addr4 = (struct sockaddr_in *)addr;
65 addr4->sin_family = domain;
66 addr4->sin_port = htons(port);
67 if (inet_pton(domain, ip, (void *)&addr4->sin_addr) != 1) {
68 log_err("Invalid IPv4: %s", ip);
69 return -1;
70 }
71 } else if (domain == AF_INET6) {
72 if (addr_len < sizeof(struct sockaddr_in6))
73 return -1;
74 addr6 = (struct sockaddr_in6 *)addr;
75 addr6->sin6_family = domain;
76 addr6->sin6_port = htons(port);
77 if (inet_pton(domain, ip, (void *)&addr6->sin6_addr) != 1) {
78 log_err("Invalid IPv6: %s", ip);
79 return -1;
80 }
81 }
82
83 return 0;
84}
85
86static int load_insns(enum bpf_attach_type attach_type,
87 const struct bpf_insn *insns, size_t insns_cnt,
88 const char *comment)
89{
90 struct bpf_load_program_attr load_attr;
91 int ret;
92
93 memset(&load_attr, 0, sizeof(struct bpf_load_program_attr));
94 load_attr.prog_type = BPF_PROG_TYPE_CGROUP_SOCK_ADDR;
95 load_attr.expected_attach_type = attach_type;
96 load_attr.insns = insns;
97 load_attr.insns_cnt = insns_cnt;
98 load_attr.license = "GPL";
99
100 ret = bpf_load_program_xattr(&load_attr, bpf_log_buf, BPF_LOG_BUF_SIZE);
101 if (ret < 0 && comment) {
102 log_err(">>> Loading %s program error.\n"
103 ">>> Output from verifier:\n%s\n-------\n",
104 comment, bpf_log_buf);
105 }
106
107 return ret;
108}
109
110/* [1] These testing programs try to read different context fields, including
111 * narrow loads of different sizes from user_ip4 and user_ip6, and write to
112 * those allowed to be overridden.
113 *
114 * [2] BPF_LD_IMM64 & BPF_JMP_REG are used below whenever there is a need to
115 * compare a register with unsigned 32bit integer. BPF_JMP_IMM can't be used
116 * in such cases since it accepts only _signed_ 32bit integer as IMM
117 * argument. Also note that BPF_LD_IMM64 contains 2 instructions what matters
118 * to count jumps properly.
119 */
120
121static int bind4_prog_load(enum bpf_attach_type attach_type,
122 const char *comment)
123{
124 union {
125 uint8_t u4_addr8[4];
126 uint16_t u4_addr16[2];
127 uint32_t u4_addr32;
128 } ip4;
129 struct sockaddr_in addr4_rw;
130
131 if (inet_pton(AF_INET, SERV4_IP, (void *)&ip4) != 1) {
132 log_err("Invalid IPv4: %s", SERV4_IP);
133 return -1;
134 }
135
136 if (mk_sockaddr(AF_INET, SERV4_REWRITE_IP, SERV4_REWRITE_PORT,
137 (struct sockaddr *)&addr4_rw, sizeof(addr4_rw)) == -1)
138 return -1;
139
140 /* See [1]. */
141 struct bpf_insn insns[] = {
142 BPF_MOV64_REG(BPF_REG_6, BPF_REG_1),
143
144 /* if (sk.family == AF_INET && */
145 BPF_LDX_MEM(BPF_W, BPF_REG_7, BPF_REG_6,
146 offsetof(struct bpf_sock_addr, family)),
147 BPF_JMP_IMM(BPF_JNE, BPF_REG_7, AF_INET, 16),
148
149 /* (sk.type == SOCK_DGRAM || sk.type == SOCK_STREAM) && */
150 BPF_LDX_MEM(BPF_W, BPF_REG_7, BPF_REG_6,
151 offsetof(struct bpf_sock_addr, type)),
152 BPF_JMP_IMM(BPF_JNE, BPF_REG_7, SOCK_DGRAM, 1),
153 BPF_JMP_A(1),
154 BPF_JMP_IMM(BPF_JNE, BPF_REG_7, SOCK_STREAM, 12),
155
156 /* 1st_byte_of_user_ip4 == expected && */
157 BPF_LDX_MEM(BPF_B, BPF_REG_7, BPF_REG_6,
158 offsetof(struct bpf_sock_addr, user_ip4)),
159 BPF_JMP_IMM(BPF_JNE, BPF_REG_7, ip4.u4_addr8[0], 10),
160
161 /* 1st_half_of_user_ip4 == expected && */
162 BPF_LDX_MEM(BPF_H, BPF_REG_7, BPF_REG_6,
163 offsetof(struct bpf_sock_addr, user_ip4)),
164 BPF_JMP_IMM(BPF_JNE, BPF_REG_7, ip4.u4_addr16[0], 8),
165
166 /* whole_user_ip4 == expected) { */
167 BPF_LDX_MEM(BPF_W, BPF_REG_7, BPF_REG_6,
168 offsetof(struct bpf_sock_addr, user_ip4)),
169 BPF_LD_IMM64(BPF_REG_8, ip4.u4_addr32), /* See [2]. */
170 BPF_JMP_REG(BPF_JNE, BPF_REG_7, BPF_REG_8, 4),
171
172 /* user_ip4 = addr4_rw.sin_addr */
173 BPF_MOV32_IMM(BPF_REG_7, addr4_rw.sin_addr.s_addr),
174 BPF_STX_MEM(BPF_W, BPF_REG_6, BPF_REG_7,
175 offsetof(struct bpf_sock_addr, user_ip4)),
176
177 /* user_port = addr4_rw.sin_port */
178 BPF_MOV32_IMM(BPF_REG_7, addr4_rw.sin_port),
179 BPF_STX_MEM(BPF_W, BPF_REG_6, BPF_REG_7,
180 offsetof(struct bpf_sock_addr, user_port)),
181 /* } */
182
183 /* return 1 */
184 BPF_MOV64_IMM(BPF_REG_0, 1),
185 BPF_EXIT_INSN(),
186 };
187
188 return load_insns(attach_type, insns,
189 sizeof(insns) / sizeof(struct bpf_insn), comment);
190}
191
192static int bind6_prog_load(enum bpf_attach_type attach_type,
193 const char *comment)
194{
195 struct sockaddr_in6 addr6_rw;
196 struct in6_addr ip6;
197
198 if (inet_pton(AF_INET6, SERV6_IP, (void *)&ip6) != 1) {
199 log_err("Invalid IPv6: %s", SERV6_IP);
200 return -1;
201 }
202
203 if (mk_sockaddr(AF_INET6, SERV6_REWRITE_IP, SERV6_REWRITE_PORT,
204 (struct sockaddr *)&addr6_rw, sizeof(addr6_rw)) == -1)
205 return -1;
206
207 /* See [1]. */
208 struct bpf_insn insns[] = {
209 BPF_MOV64_REG(BPF_REG_6, BPF_REG_1),
210
211 /* if (sk.family == AF_INET6 && */
212 BPF_LDX_MEM(BPF_W, BPF_REG_7, BPF_REG_6,
213 offsetof(struct bpf_sock_addr, family)),
214 BPF_JMP_IMM(BPF_JNE, BPF_REG_7, AF_INET6, 18),
215
216 /* 5th_byte_of_user_ip6 == expected && */
217 BPF_LDX_MEM(BPF_B, BPF_REG_7, BPF_REG_6,
218 offsetof(struct bpf_sock_addr, user_ip6[1])),
219 BPF_JMP_IMM(BPF_JNE, BPF_REG_7, ip6.s6_addr[4], 16),
220
221 /* 3rd_half_of_user_ip6 == expected && */
222 BPF_LDX_MEM(BPF_H, BPF_REG_7, BPF_REG_6,
223 offsetof(struct bpf_sock_addr, user_ip6[1])),
224 BPF_JMP_IMM(BPF_JNE, BPF_REG_7, ip6.s6_addr16[2], 14),
225
226 /* last_word_of_user_ip6 == expected) { */
227 BPF_LDX_MEM(BPF_W, BPF_REG_7, BPF_REG_6,
228 offsetof(struct bpf_sock_addr, user_ip6[3])),
229 BPF_LD_IMM64(BPF_REG_8, ip6.s6_addr32[3]), /* See [2]. */
230 BPF_JMP_REG(BPF_JNE, BPF_REG_7, BPF_REG_8, 10),
231
232
233#define STORE_IPV6_WORD(N) \
234 BPF_MOV32_IMM(BPF_REG_7, addr6_rw.sin6_addr.s6_addr32[N]), \
235 BPF_STX_MEM(BPF_W, BPF_REG_6, BPF_REG_7, \
236 offsetof(struct bpf_sock_addr, user_ip6[N]))
237
238 /* user_ip6 = addr6_rw.sin6_addr */
239 STORE_IPV6_WORD(0),
240 STORE_IPV6_WORD(1),
241 STORE_IPV6_WORD(2),
242 STORE_IPV6_WORD(3),
243
244 /* user_port = addr6_rw.sin6_port */
245 BPF_MOV32_IMM(BPF_REG_7, addr6_rw.sin6_port),
246 BPF_STX_MEM(BPF_W, BPF_REG_6, BPF_REG_7,
247 offsetof(struct bpf_sock_addr, user_port)),
248
249 /* } */
250
251 /* return 1 */
252 BPF_MOV64_IMM(BPF_REG_0, 1),
253 BPF_EXIT_INSN(),
254 };
255
256 return load_insns(attach_type, insns,
257 sizeof(insns) / sizeof(struct bpf_insn), comment);
258}
259
260static int connect_prog_load_path(const char *path,
261 enum bpf_attach_type attach_type,
262 const char *comment)
263{
264 struct bpf_prog_load_attr attr;
265 struct bpf_object *obj;
266 int prog_fd;
267
268 memset(&attr, 0, sizeof(struct bpf_prog_load_attr));
269 attr.file = path;
270 attr.prog_type = BPF_PROG_TYPE_CGROUP_SOCK_ADDR;
271 attr.expected_attach_type = attach_type;
272
273 if (bpf_prog_load_xattr(&attr, &obj, &prog_fd)) {
274 if (comment)
275 log_err(">>> Loading %s program at %s error.\n",
276 comment, path);
277 return -1;
278 }
279
280 return prog_fd;
281}
282
283static int connect4_prog_load(enum bpf_attach_type attach_type,
284 const char *comment)
285{
286 return connect_prog_load_path(CONNECT4_PROG_PATH, attach_type, comment);
287}
288
289static int connect6_prog_load(enum bpf_attach_type attach_type,
290 const char *comment)
291{
292 return connect_prog_load_path(CONNECT6_PROG_PATH, attach_type, comment);
293}
294
295static void print_ip_port(int sockfd, info_fn fn, const char *fmt)
296{
297 char addr_buf[INET_NTOP_BUF];
298 struct sockaddr_storage addr;
299 struct sockaddr_in6 *addr6;
300 struct sockaddr_in *addr4;
301 socklen_t addr_len;
302 unsigned short port;
303 void *nip;
304
305 addr_len = sizeof(struct sockaddr_storage);
306 memset(&addr, 0, addr_len);
307
308 if (fn(sockfd, (struct sockaddr *)&addr, (socklen_t *)&addr_len) == 0) {
309 if (addr.ss_family == AF_INET) {
310 addr4 = (struct sockaddr_in *)&addr;
311 nip = (void *)&addr4->sin_addr;
312 port = ntohs(addr4->sin_port);
313 } else if (addr.ss_family == AF_INET6) {
314 addr6 = (struct sockaddr_in6 *)&addr;
315 nip = (void *)&addr6->sin6_addr;
316 port = ntohs(addr6->sin6_port);
317 } else {
318 return;
319 }
320 const char *addr_str =
321 inet_ntop(addr.ss_family, nip, addr_buf, INET_NTOP_BUF);
322 printf(fmt, addr_str ? addr_str : "??", port);
323 }
324}
325
326static void print_local_ip_port(int sockfd, const char *fmt)
327{
328 print_ip_port(sockfd, getsockname, fmt);
329}
330
331static void print_remote_ip_port(int sockfd, const char *fmt)
332{
333 print_ip_port(sockfd, getpeername, fmt);
334}
335
336static int start_server(int type, const struct sockaddr_storage *addr,
337 socklen_t addr_len)
338{
339
340 int fd;
341
342 fd = socket(addr->ss_family, type, 0);
343 if (fd == -1) {
344 log_err("Failed to create server socket");
345 goto out;
346 }
347
348 if (bind(fd, (const struct sockaddr *)addr, addr_len) == -1) {
349 log_err("Failed to bind server socket");
350 goto close_out;
351 }
352
353 if (type == SOCK_STREAM) {
354 if (listen(fd, 128) == -1) {
355 log_err("Failed to listen on server socket");
356 goto close_out;
357 }
358 }
359
360 print_local_ip_port(fd, "\t Actual: bind(%s, %d)\n");
361
362 goto out;
363close_out:
364 close(fd);
365 fd = -1;
366out:
367 return fd;
368}
369
370static int connect_to_server(int type, const struct sockaddr_storage *addr,
371 socklen_t addr_len)
372{
373 int domain;
374 int fd;
375
376 domain = addr->ss_family;
377
378 if (domain != AF_INET && domain != AF_INET6) {
379 log_err("Unsupported address family");
380 return -1;
381 }
382
383 fd = socket(domain, type, 0);
384 if (fd == -1) {
385 log_err("Failed to creating client socket");
386 return -1;
387 }
388
389 if (connect(fd, (const struct sockaddr *)addr, addr_len) == -1) {
390 log_err("Fail to connect to server");
391 goto err;
392 }
393
394 print_remote_ip_port(fd, "\t Actual: connect(%s, %d)");
395 print_local_ip_port(fd, " from (%s, %d)\n");
396
397 return 0;
398err:
399 close(fd);
400 return -1;
401}
402
403static void print_test_case_num(int domain, int type)
404{
405 static int test_num;
406
407 printf("Test case #%d (%s/%s):\n", ++test_num,
408 (domain == AF_INET ? "IPv4" :
409 domain == AF_INET6 ? "IPv6" :
410 "unknown_domain"),
411 (type == SOCK_STREAM ? "TCP" :
412 type == SOCK_DGRAM ? "UDP" :
413 "unknown_type"));
414}
415
416static int run_test_case(int domain, int type, const char *ip,
417 unsigned short port)
418{
419 struct sockaddr_storage addr;
420 socklen_t addr_len = sizeof(addr);
421 int servfd = -1;
422 int err = 0;
423
424 print_test_case_num(domain, type);
425
426 if (mk_sockaddr(domain, ip, port, (struct sockaddr *)&addr,
427 addr_len) == -1)
428 return -1;
429
430 printf("\tRequested: bind(%s, %d) ..\n", ip, port);
431 servfd = start_server(type, &addr, addr_len);
432 if (servfd == -1)
433 goto err;
434
435 printf("\tRequested: connect(%s, %d) from (*, *) ..\n", ip, port);
436 if (connect_to_server(type, &addr, addr_len))
437 goto err;
438
439 goto out;
440err:
441 err = -1;
442out:
443 close(servfd);
444 return err;
445}
446
447static void close_progs_fds(struct program *progs, size_t prog_cnt)
448{
449 size_t i;
450
451 for (i = 0; i < prog_cnt; ++i) {
452 close(progs[i].fd);
453 progs[i].fd = -1;
454 }
455}
456
457static int load_and_attach_progs(int cgfd, struct program *progs,
458 size_t prog_cnt)
459{
460 size_t i;
461
462 for (i = 0; i < prog_cnt; ++i) {
463 printf("Load %s with invalid type (can pollute stderr) ",
464 progs[i].name);
465 fflush(stdout);
466 progs[i].fd = progs[i].loadfn(progs[i].invalid_type, NULL);
467 if (progs[i].fd != -1) {
468 log_err("Load with invalid type accepted for %s",
469 progs[i].name);
470 goto err;
471 }
472 printf("... REJECTED\n");
473
474 printf("Load %s with valid type", progs[i].name);
475 progs[i].fd = progs[i].loadfn(progs[i].type, progs[i].name);
476 if (progs[i].fd == -1) {
477 log_err("Failed to load program %s", progs[i].name);
478 goto err;
479 }
480 printf(" ... OK\n");
481
482 printf("Attach %s with invalid type", progs[i].name);
483 if (bpf_prog_attach(progs[i].fd, cgfd, progs[i].invalid_type,
484 BPF_F_ALLOW_OVERRIDE) != -1) {
485 log_err("Attach with invalid type accepted for %s",
486 progs[i].name);
487 goto err;
488 }
489 printf(" ... REJECTED\n");
490
491 printf("Attach %s with valid type", progs[i].name);
492 if (bpf_prog_attach(progs[i].fd, cgfd, progs[i].type,
493 BPF_F_ALLOW_OVERRIDE) == -1) {
494 log_err("Failed to attach program %s", progs[i].name);
495 goto err;
496 }
497 printf(" ... OK\n");
498 }
499
500 return 0;
501err:
502 close_progs_fds(progs, prog_cnt);
503 return -1;
504}
505
506static int run_domain_test(int domain, int cgfd, struct program *progs,
507 size_t prog_cnt, const char *ip, unsigned short port)
508{
509 int err = 0;
510
511 if (load_and_attach_progs(cgfd, progs, prog_cnt) == -1)
512 goto err;
513
514 if (run_test_case(domain, SOCK_STREAM, ip, port) == -1)
515 goto err;
516
517 if (run_test_case(domain, SOCK_DGRAM, ip, port) == -1)
518 goto err;
519
520 goto out;
521err:
522 err = -1;
523out:
524 close_progs_fds(progs, prog_cnt);
525 return err;
526}
527
528static int run_test(void)
529{
530 size_t inet6_prog_cnt;
531 size_t inet_prog_cnt;
532 int cgfd = -1;
533 int err = 0;
534
535 struct program inet6_progs[] = {
536 {BPF_CGROUP_INET6_BIND, bind6_prog_load, -1, "bind6",
537 BPF_CGROUP_INET4_BIND},
538 {BPF_CGROUP_INET6_CONNECT, connect6_prog_load, -1, "connect6",
539 BPF_CGROUP_INET4_CONNECT},
540 };
541 inet6_prog_cnt = sizeof(inet6_progs) / sizeof(struct program);
542
543 struct program inet_progs[] = {
544 {BPF_CGROUP_INET4_BIND, bind4_prog_load, -1, "bind4",
545 BPF_CGROUP_INET6_BIND},
546 {BPF_CGROUP_INET4_CONNECT, connect4_prog_load, -1, "connect4",
547 BPF_CGROUP_INET6_CONNECT},
548 };
549 inet_prog_cnt = sizeof(inet_progs) / sizeof(struct program);
550
551 if (setup_cgroup_environment())
552 goto err;
553
554 cgfd = create_and_get_cgroup(CG_PATH);
555 if (!cgfd)
556 goto err;
557
558 if (join_cgroup(CG_PATH))
559 goto err;
560
561 if (run_domain_test(AF_INET, cgfd, inet_progs, inet_prog_cnt, SERV4_IP,
562 SERV4_PORT) == -1)
563 goto err;
564
565 if (run_domain_test(AF_INET6, cgfd, inet6_progs, inet6_prog_cnt,
566 SERV6_IP, SERV6_PORT) == -1)
567 goto err;
568
569 goto out;
570err:
571 err = -1;
572out:
573 close(cgfd);
574 cleanup_cgroup_environment();
575 printf(err ? "### FAIL\n" : "### SUCCESS\n");
576 return err;
577}
578
579int main(int argc, char **argv)
580{
581 if (argc < 2) {
582 fprintf(stderr,
583 "%s has to be run via %s.sh. Skip direct run.\n",
584 argv[0], argv[0]);
585 exit(0);
586 }
587 return run_test();
588}
diff --git a/tools/testing/selftests/bpf/test_sock_addr.sh b/tools/testing/selftests/bpf/test_sock_addr.sh
new file mode 100755
index 000000000000..c6e1dcf992c4
--- /dev/null
+++ b/tools/testing/selftests/bpf/test_sock_addr.sh
@@ -0,0 +1,57 @@
1#!/bin/sh
2
3set -eu
4
5ping_once()
6{
7 ping -q -c 1 -W 1 ${1%%/*} >/dev/null 2>&1
8}
9
10wait_for_ip()
11{
12 local _i
13 echo -n "Wait for testing IPv4/IPv6 to become available "
14 for _i in $(seq ${MAX_PING_TRIES}); do
15 echo -n "."
16 if ping_once ${TEST_IPv4} && ping_once ${TEST_IPv6}; then
17 echo " OK"
18 return
19 fi
20 done
21 echo 1>&2 "ERROR: Timeout waiting for test IP to become available."
22 exit 1
23}
24
25setup()
26{
27 # Create testing interfaces not to interfere with current environment.
28 ip link add dev ${TEST_IF} type veth peer name ${TEST_IF_PEER}
29 ip link set ${TEST_IF} up
30 ip link set ${TEST_IF_PEER} up
31
32 ip -4 addr add ${TEST_IPv4} dev ${TEST_IF}
33 ip -6 addr add ${TEST_IPv6} dev ${TEST_IF}
34 wait_for_ip
35}
36
37cleanup()
38{
39 ip link del ${TEST_IF} 2>/dev/null || :
40 ip link del ${TEST_IF_PEER} 2>/dev/null || :
41}
42
43main()
44{
45 trap cleanup EXIT 2 3 6 15
46 setup
47 ./test_sock_addr setup_done
48}
49
50BASENAME=$(basename $0 .sh)
51TEST_IF="${BASENAME}1"
52TEST_IF_PEER="${BASENAME}2"
53TEST_IPv4="127.0.0.4/8"
54TEST_IPv6="::6/128"
55MAX_PING_TRIES=5
56
57main
diff --git a/tools/testing/selftests/bpf/test_stacktrace_build_id.c b/tools/testing/selftests/bpf/test_stacktrace_build_id.c
new file mode 100644
index 000000000000..b755bd783ce5
--- /dev/null
+++ b/tools/testing/selftests/bpf/test_stacktrace_build_id.c
@@ -0,0 +1,60 @@
1// SPDX-License-Identifier: GPL-2.0
2// Copyright (c) 2018 Facebook
3
4#include <linux/bpf.h>
5#include "bpf_helpers.h"
6
7#ifndef PERF_MAX_STACK_DEPTH
8#define PERF_MAX_STACK_DEPTH 127
9#endif
10
11struct bpf_map_def SEC("maps") control_map = {
12 .type = BPF_MAP_TYPE_ARRAY,
13 .key_size = sizeof(__u32),
14 .value_size = sizeof(__u32),
15 .max_entries = 1,
16};
17
18struct bpf_map_def SEC("maps") stackid_hmap = {
19 .type = BPF_MAP_TYPE_HASH,
20 .key_size = sizeof(__u32),
21 .value_size = sizeof(__u32),
22 .max_entries = 10000,
23};
24
25struct bpf_map_def SEC("maps") stackmap = {
26 .type = BPF_MAP_TYPE_STACK_TRACE,
27 .key_size = sizeof(__u32),
28 .value_size = sizeof(struct bpf_stack_build_id)
29 * PERF_MAX_STACK_DEPTH,
30 .max_entries = 128,
31 .map_flags = BPF_F_STACK_BUILD_ID,
32};
33
34/* taken from /sys/kernel/debug/tracing/events/random/urandom_read/format */
35struct random_urandom_args {
36 unsigned long long pad;
37 int got_bits;
38 int pool_left;
39 int input_left;
40};
41
42SEC("tracepoint/random/urandom_read")
43int oncpu(struct random_urandom_args *args)
44{
45 __u32 key = 0, val = 0, *value_p;
46
47 value_p = bpf_map_lookup_elem(&control_map, &key);
48 if (value_p && *value_p)
49 return 0; /* skip if non-zero *value_p */
50
51 /* The size of stackmap and stackid_hmap should be the same */
52 key = bpf_get_stackid(args, &stackmap, BPF_F_USER_STACK);
53 if ((int)key >= 0)
54 bpf_map_update_elem(&stackid_hmap, &key, &val, 0);
55
56 return 0;
57}
58
59char _license[] SEC("license") = "GPL";
60__u32 _version SEC("version") = 1; /* ignored by tracepoints, required by libbpf.a */
diff --git a/tools/testing/selftests/bpf/test_tag.c b/tools/testing/selftests/bpf/test_tag.c
index 8b201895c569..6272c784ca2a 100644
--- a/tools/testing/selftests/bpf/test_tag.c
+++ b/tools/testing/selftests/bpf/test_tag.c
@@ -12,7 +12,6 @@
12#include <assert.h> 12#include <assert.h>
13 13
14#include <sys/socket.h> 14#include <sys/socket.h>
15#include <sys/resource.h>
16 15
17#include <linux/filter.h> 16#include <linux/filter.h>
18#include <linux/bpf.h> 17#include <linux/bpf.h>
@@ -21,6 +20,7 @@
21#include <bpf/bpf.h> 20#include <bpf/bpf.h>
22 21
23#include "../../../include/linux/filter.h" 22#include "../../../include/linux/filter.h"
23#include "bpf_rlimit.h"
24 24
25static struct bpf_insn prog[BPF_MAXINSNS]; 25static struct bpf_insn prog[BPF_MAXINSNS];
26 26
@@ -184,11 +184,9 @@ static void do_test(uint32_t *tests, int start_insns, int fd_map,
184 184
185int main(void) 185int main(void)
186{ 186{
187 struct rlimit rinf = { RLIM_INFINITY, RLIM_INFINITY };
188 uint32_t tests = 0; 187 uint32_t tests = 0;
189 int i, fd_map; 188 int i, fd_map;
190 189
191 setrlimit(RLIMIT_MEMLOCK, &rinf);
192 fd_map = bpf_create_map(BPF_MAP_TYPE_HASH, sizeof(int), 190 fd_map = bpf_create_map(BPF_MAP_TYPE_HASH, sizeof(int),
193 sizeof(int), 1, BPF_F_NO_PREALLOC); 191 sizeof(int), 1, BPF_F_NO_PREALLOC);
194 assert(fd_map > 0); 192 assert(fd_map > 0);
diff --git a/tools/testing/selftests/bpf/test_tcpbpf_user.c b/tools/testing/selftests/bpf/test_tcpbpf_user.c
index 95a370f3d378..84ab5163c828 100644
--- a/tools/testing/selftests/bpf/test_tcpbpf_user.c
+++ b/tools/testing/selftests/bpf/test_tcpbpf_user.c
@@ -11,12 +11,14 @@
11#include <linux/ptrace.h> 11#include <linux/ptrace.h>
12#include <linux/bpf.h> 12#include <linux/bpf.h>
13#include <sys/ioctl.h> 13#include <sys/ioctl.h>
14#include <sys/time.h>
14#include <sys/types.h> 15#include <sys/types.h>
15#include <sys/stat.h> 16#include <sys/stat.h>
16#include <fcntl.h> 17#include <fcntl.h>
17#include <bpf/bpf.h> 18#include <bpf/bpf.h>
18#include <bpf/libbpf.h> 19#include <bpf/libbpf.h>
19#include "bpf_util.h" 20#include "bpf_util.h"
21#include "bpf_rlimit.h"
20#include <linux/perf_event.h> 22#include <linux/perf_event.h>
21#include "test_tcpbpf.h" 23#include "test_tcpbpf.h"
22 24
diff --git a/tools/testing/selftests/bpf/test_verifier.c b/tools/testing/selftests/bpf/test_verifier.c
index 437c0b1c9d21..3e7718b1a9ae 100644
--- a/tools/testing/selftests/bpf/test_verifier.c
+++ b/tools/testing/selftests/bpf/test_verifier.c
@@ -24,7 +24,6 @@
24#include <limits.h> 24#include <limits.h>
25 25
26#include <sys/capability.h> 26#include <sys/capability.h>
27#include <sys/resource.h>
28 27
29#include <linux/unistd.h> 28#include <linux/unistd.h>
30#include <linux/filter.h> 29#include <linux/filter.h>
@@ -41,7 +40,7 @@
41# define CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS 1 40# define CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS 1
42# endif 41# endif
43#endif 42#endif
44 43#include "bpf_rlimit.h"
45#include "../../../include/linux/filter.h" 44#include "../../../include/linux/filter.h"
46 45
47#ifndef ARRAY_SIZE 46#ifndef ARRAY_SIZE
@@ -57,6 +56,9 @@
57#define F_NEEDS_EFFICIENT_UNALIGNED_ACCESS (1 << 0) 56#define F_NEEDS_EFFICIENT_UNALIGNED_ACCESS (1 << 0)
58#define F_LOAD_WITH_STRICT_ALIGNMENT (1 << 1) 57#define F_LOAD_WITH_STRICT_ALIGNMENT (1 << 1)
59 58
59#define UNPRIV_SYSCTL "kernel/unprivileged_bpf_disabled"
60static bool unpriv_disabled = false;
61
60struct bpf_test { 62struct bpf_test {
61 const char *descr; 63 const char *descr;
62 struct bpf_insn insns[MAX_INSNS]; 64 struct bpf_insn insns[MAX_INSNS];
@@ -1595,6 +1597,60 @@ static struct bpf_test tests[] = {
1595 .prog_type = BPF_PROG_TYPE_SK_SKB, 1597 .prog_type = BPF_PROG_TYPE_SK_SKB,
1596 }, 1598 },
1597 { 1599 {
1600 "direct packet read for SK_MSG",
1601 .insns = {
1602 BPF_LDX_MEM(BPF_DW, BPF_REG_2, BPF_REG_1,
1603 offsetof(struct sk_msg_md, data)),
1604 BPF_LDX_MEM(BPF_DW, BPF_REG_3, BPF_REG_1,
1605 offsetof(struct sk_msg_md, data_end)),
1606 BPF_MOV64_REG(BPF_REG_0, BPF_REG_2),
1607 BPF_ALU64_IMM(BPF_ADD, BPF_REG_0, 8),
1608 BPF_JMP_REG(BPF_JGT, BPF_REG_0, BPF_REG_3, 1),
1609 BPF_LDX_MEM(BPF_B, BPF_REG_0, BPF_REG_2, 0),
1610 BPF_MOV64_IMM(BPF_REG_0, 0),
1611 BPF_EXIT_INSN(),
1612 },
1613 .result = ACCEPT,
1614 .prog_type = BPF_PROG_TYPE_SK_MSG,
1615 },
1616 {
1617 "direct packet write for SK_MSG",
1618 .insns = {
1619 BPF_LDX_MEM(BPF_DW, BPF_REG_2, BPF_REG_1,
1620 offsetof(struct sk_msg_md, data)),
1621 BPF_LDX_MEM(BPF_DW, BPF_REG_3, BPF_REG_1,
1622 offsetof(struct sk_msg_md, data_end)),
1623 BPF_MOV64_REG(BPF_REG_0, BPF_REG_2),
1624 BPF_ALU64_IMM(BPF_ADD, BPF_REG_0, 8),
1625 BPF_JMP_REG(BPF_JGT, BPF_REG_0, BPF_REG_3, 1),
1626 BPF_STX_MEM(BPF_B, BPF_REG_2, BPF_REG_2, 0),
1627 BPF_MOV64_IMM(BPF_REG_0, 0),
1628 BPF_EXIT_INSN(),
1629 },
1630 .result = ACCEPT,
1631 .prog_type = BPF_PROG_TYPE_SK_MSG,
1632 },
1633 {
1634 "overlapping checks for direct packet access SK_MSG",
1635 .insns = {
1636 BPF_LDX_MEM(BPF_DW, BPF_REG_2, BPF_REG_1,
1637 offsetof(struct sk_msg_md, data)),
1638 BPF_LDX_MEM(BPF_DW, BPF_REG_3, BPF_REG_1,
1639 offsetof(struct sk_msg_md, data_end)),
1640 BPF_MOV64_REG(BPF_REG_0, BPF_REG_2),
1641 BPF_ALU64_IMM(BPF_ADD, BPF_REG_0, 8),
1642 BPF_JMP_REG(BPF_JGT, BPF_REG_0, BPF_REG_3, 4),
1643 BPF_MOV64_REG(BPF_REG_1, BPF_REG_2),
1644 BPF_ALU64_IMM(BPF_ADD, BPF_REG_1, 6),
1645 BPF_JMP_REG(BPF_JGT, BPF_REG_1, BPF_REG_3, 1),
1646 BPF_LDX_MEM(BPF_H, BPF_REG_0, BPF_REG_2, 6),
1647 BPF_MOV64_IMM(BPF_REG_0, 0),
1648 BPF_EXIT_INSN(),
1649 },
1650 .result = ACCEPT,
1651 .prog_type = BPF_PROG_TYPE_SK_MSG,
1652 },
1653 {
1598 "check skb->mark is not writeable by sockets", 1654 "check skb->mark is not writeable by sockets",
1599 .insns = { 1655 .insns = {
1600 BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_1, 1656 BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_1,
@@ -2587,17 +2643,74 @@ static struct bpf_test tests[] = {
2587 .result = ACCEPT, 2643 .result = ACCEPT,
2588 }, 2644 },
2589 { 2645 {
2646 "runtime/jit: tail_call within bounds, prog once",
2647 .insns = {
2648 BPF_MOV64_IMM(BPF_REG_3, 0),
2649 BPF_LD_MAP_FD(BPF_REG_2, 0),
2650 BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0,
2651 BPF_FUNC_tail_call),
2652 BPF_MOV64_IMM(BPF_REG_0, 1),
2653 BPF_EXIT_INSN(),
2654 },
2655 .fixup_prog = { 1 },
2656 .result = ACCEPT,
2657 .retval = 42,
2658 },
2659 {
2660 "runtime/jit: tail_call within bounds, prog loop",
2661 .insns = {
2662 BPF_MOV64_IMM(BPF_REG_3, 1),
2663 BPF_LD_MAP_FD(BPF_REG_2, 0),
2664 BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0,
2665 BPF_FUNC_tail_call),
2666 BPF_MOV64_IMM(BPF_REG_0, 1),
2667 BPF_EXIT_INSN(),
2668 },
2669 .fixup_prog = { 1 },
2670 .result = ACCEPT,
2671 .retval = 41,
2672 },
2673 {
2674 "runtime/jit: tail_call within bounds, no prog",
2675 .insns = {
2676 BPF_MOV64_IMM(BPF_REG_3, 2),
2677 BPF_LD_MAP_FD(BPF_REG_2, 0),
2678 BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0,
2679 BPF_FUNC_tail_call),
2680 BPF_MOV64_IMM(BPF_REG_0, 1),
2681 BPF_EXIT_INSN(),
2682 },
2683 .fixup_prog = { 1 },
2684 .result = ACCEPT,
2685 .retval = 1,
2686 },
2687 {
2688 "runtime/jit: tail_call out of bounds",
2689 .insns = {
2690 BPF_MOV64_IMM(BPF_REG_3, 256),
2691 BPF_LD_MAP_FD(BPF_REG_2, 0),
2692 BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0,
2693 BPF_FUNC_tail_call),
2694 BPF_MOV64_IMM(BPF_REG_0, 2),
2695 BPF_EXIT_INSN(),
2696 },
2697 .fixup_prog = { 1 },
2698 .result = ACCEPT,
2699 .retval = 2,
2700 },
2701 {
2590 "runtime/jit: pass negative index to tail_call", 2702 "runtime/jit: pass negative index to tail_call",
2591 .insns = { 2703 .insns = {
2592 BPF_MOV64_IMM(BPF_REG_3, -1), 2704 BPF_MOV64_IMM(BPF_REG_3, -1),
2593 BPF_LD_MAP_FD(BPF_REG_2, 0), 2705 BPF_LD_MAP_FD(BPF_REG_2, 0),
2594 BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, 2706 BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0,
2595 BPF_FUNC_tail_call), 2707 BPF_FUNC_tail_call),
2596 BPF_MOV64_IMM(BPF_REG_0, 0), 2708 BPF_MOV64_IMM(BPF_REG_0, 2),
2597 BPF_EXIT_INSN(), 2709 BPF_EXIT_INSN(),
2598 }, 2710 },
2599 .fixup_prog = { 1 }, 2711 .fixup_prog = { 1 },
2600 .result = ACCEPT, 2712 .result = ACCEPT,
2713 .retval = 2,
2601 }, 2714 },
2602 { 2715 {
2603 "runtime/jit: pass > 32bit index to tail_call", 2716 "runtime/jit: pass > 32bit index to tail_call",
@@ -2606,11 +2719,12 @@ static struct bpf_test tests[] = {
2606 BPF_LD_MAP_FD(BPF_REG_2, 0), 2719 BPF_LD_MAP_FD(BPF_REG_2, 0),
2607 BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, 2720 BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0,
2608 BPF_FUNC_tail_call), 2721 BPF_FUNC_tail_call),
2609 BPF_MOV64_IMM(BPF_REG_0, 0), 2722 BPF_MOV64_IMM(BPF_REG_0, 2),
2610 BPF_EXIT_INSN(), 2723 BPF_EXIT_INSN(),
2611 }, 2724 },
2612 .fixup_prog = { 2 }, 2725 .fixup_prog = { 2 },
2613 .result = ACCEPT, 2726 .result = ACCEPT,
2727 .retval = 42,
2614 }, 2728 },
2615 { 2729 {
2616 "stack pointer arithmetic", 2730 "stack pointer arithmetic",
@@ -11164,6 +11278,94 @@ static struct bpf_test tests[] = {
11164 .prog_type = BPF_PROG_TYPE_TRACEPOINT, 11278 .prog_type = BPF_PROG_TYPE_TRACEPOINT,
11165 }, 11279 },
11166 { 11280 {
11281 "jit: lsh, rsh, arsh by 1",
11282 .insns = {
11283 BPF_MOV64_IMM(BPF_REG_0, 1),
11284 BPF_MOV64_IMM(BPF_REG_1, 0xff),
11285 BPF_ALU64_IMM(BPF_LSH, BPF_REG_1, 1),
11286 BPF_ALU32_IMM(BPF_LSH, BPF_REG_1, 1),
11287 BPF_JMP_IMM(BPF_JEQ, BPF_REG_1, 0x3fc, 1),
11288 BPF_EXIT_INSN(),
11289 BPF_ALU64_IMM(BPF_RSH, BPF_REG_1, 1),
11290 BPF_ALU32_IMM(BPF_RSH, BPF_REG_1, 1),
11291 BPF_JMP_IMM(BPF_JEQ, BPF_REG_1, 0xff, 1),
11292 BPF_EXIT_INSN(),
11293 BPF_ALU64_IMM(BPF_ARSH, BPF_REG_1, 1),
11294 BPF_JMP_IMM(BPF_JEQ, BPF_REG_1, 0x7f, 1),
11295 BPF_EXIT_INSN(),
11296 BPF_MOV64_IMM(BPF_REG_0, 2),
11297 BPF_EXIT_INSN(),
11298 },
11299 .result = ACCEPT,
11300 .retval = 2,
11301 },
11302 {
11303 "jit: mov32 for ldimm64, 1",
11304 .insns = {
11305 BPF_MOV64_IMM(BPF_REG_0, 2),
11306 BPF_LD_IMM64(BPF_REG_1, 0xfeffffffffffffffULL),
11307 BPF_ALU64_IMM(BPF_RSH, BPF_REG_1, 32),
11308 BPF_LD_IMM64(BPF_REG_2, 0xfeffffffULL),
11309 BPF_JMP_REG(BPF_JEQ, BPF_REG_1, BPF_REG_2, 1),
11310 BPF_MOV64_IMM(BPF_REG_0, 1),
11311 BPF_EXIT_INSN(),
11312 },
11313 .result = ACCEPT,
11314 .retval = 2,
11315 },
11316 {
11317 "jit: mov32 for ldimm64, 2",
11318 .insns = {
11319 BPF_MOV64_IMM(BPF_REG_0, 1),
11320 BPF_LD_IMM64(BPF_REG_1, 0x1ffffffffULL),
11321 BPF_LD_IMM64(BPF_REG_2, 0xffffffffULL),
11322 BPF_JMP_REG(BPF_JEQ, BPF_REG_1, BPF_REG_2, 1),
11323 BPF_MOV64_IMM(BPF_REG_0, 2),
11324 BPF_EXIT_INSN(),
11325 },
11326 .result = ACCEPT,
11327 .retval = 2,
11328 },
11329 {
11330 "jit: various mul tests",
11331 .insns = {
11332 BPF_LD_IMM64(BPF_REG_2, 0xeeff0d413122ULL),
11333 BPF_LD_IMM64(BPF_REG_0, 0xfefefeULL),
11334 BPF_LD_IMM64(BPF_REG_1, 0xefefefULL),
11335 BPF_ALU64_REG(BPF_MUL, BPF_REG_0, BPF_REG_1),
11336 BPF_JMP_REG(BPF_JEQ, BPF_REG_0, BPF_REG_2, 2),
11337 BPF_MOV64_IMM(BPF_REG_0, 1),
11338 BPF_EXIT_INSN(),
11339 BPF_LD_IMM64(BPF_REG_3, 0xfefefeULL),
11340 BPF_ALU64_REG(BPF_MUL, BPF_REG_3, BPF_REG_1),
11341 BPF_JMP_REG(BPF_JEQ, BPF_REG_3, BPF_REG_2, 2),
11342 BPF_MOV64_IMM(BPF_REG_0, 1),
11343 BPF_EXIT_INSN(),
11344 BPF_MOV32_REG(BPF_REG_2, BPF_REG_2),
11345 BPF_LD_IMM64(BPF_REG_0, 0xfefefeULL),
11346 BPF_ALU32_REG(BPF_MUL, BPF_REG_0, BPF_REG_1),
11347 BPF_JMP_REG(BPF_JEQ, BPF_REG_0, BPF_REG_2, 2),
11348 BPF_MOV64_IMM(BPF_REG_0, 1),
11349 BPF_EXIT_INSN(),
11350 BPF_LD_IMM64(BPF_REG_3, 0xfefefeULL),
11351 BPF_ALU32_REG(BPF_MUL, BPF_REG_3, BPF_REG_1),
11352 BPF_JMP_REG(BPF_JEQ, BPF_REG_3, BPF_REG_2, 2),
11353 BPF_MOV64_IMM(BPF_REG_0, 1),
11354 BPF_EXIT_INSN(),
11355 BPF_LD_IMM64(BPF_REG_0, 0x952a7bbcULL),
11356 BPF_LD_IMM64(BPF_REG_1, 0xfefefeULL),
11357 BPF_LD_IMM64(BPF_REG_2, 0xeeff0d413122ULL),
11358 BPF_ALU32_REG(BPF_MUL, BPF_REG_2, BPF_REG_1),
11359 BPF_JMP_REG(BPF_JEQ, BPF_REG_2, BPF_REG_0, 2),
11360 BPF_MOV64_IMM(BPF_REG_0, 1),
11361 BPF_EXIT_INSN(),
11362 BPF_MOV64_IMM(BPF_REG_0, 2),
11363 BPF_EXIT_INSN(),
11364 },
11365 .result = ACCEPT,
11366 .retval = 2,
11367 },
11368 {
11167 "xadd/w check unaligned stack", 11369 "xadd/w check unaligned stack",
11168 .insns = { 11370 .insns = {
11169 BPF_MOV64_IMM(BPF_REG_0, 1), 11371 BPF_MOV64_IMM(BPF_REG_0, 1),
@@ -11245,16 +11447,61 @@ static int create_map(uint32_t size_value, uint32_t max_elem)
11245 return fd; 11447 return fd;
11246} 11448}
11247 11449
11450static int create_prog_dummy1(void)
11451{
11452 struct bpf_insn prog[] = {
11453 BPF_MOV64_IMM(BPF_REG_0, 42),
11454 BPF_EXIT_INSN(),
11455 };
11456
11457 return bpf_load_program(BPF_PROG_TYPE_SOCKET_FILTER, prog,
11458 ARRAY_SIZE(prog), "GPL", 0, NULL, 0);
11459}
11460
11461static int create_prog_dummy2(int mfd, int idx)
11462{
11463 struct bpf_insn prog[] = {
11464 BPF_MOV64_IMM(BPF_REG_3, idx),
11465 BPF_LD_MAP_FD(BPF_REG_2, mfd),
11466 BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0,
11467 BPF_FUNC_tail_call),
11468 BPF_MOV64_IMM(BPF_REG_0, 41),
11469 BPF_EXIT_INSN(),
11470 };
11471
11472 return bpf_load_program(BPF_PROG_TYPE_SOCKET_FILTER, prog,
11473 ARRAY_SIZE(prog), "GPL", 0, NULL, 0);
11474}
11475
11248static int create_prog_array(void) 11476static int create_prog_array(void)
11249{ 11477{
11250 int fd; 11478 int p1key = 0, p2key = 1;
11479 int mfd, p1fd, p2fd;
11251 11480
11252 fd = bpf_create_map(BPF_MAP_TYPE_PROG_ARRAY, sizeof(int), 11481 mfd = bpf_create_map(BPF_MAP_TYPE_PROG_ARRAY, sizeof(int),
11253 sizeof(int), 4, 0); 11482 sizeof(int), 4, 0);
11254 if (fd < 0) 11483 if (mfd < 0) {
11255 printf("Failed to create prog array '%s'!\n", strerror(errno)); 11484 printf("Failed to create prog array '%s'!\n", strerror(errno));
11485 return -1;
11486 }
11256 11487
11257 return fd; 11488 p1fd = create_prog_dummy1();
11489 p2fd = create_prog_dummy2(mfd, p2key);
11490 if (p1fd < 0 || p2fd < 0)
11491 goto out;
11492 if (bpf_map_update_elem(mfd, &p1key, &p1fd, BPF_ANY) < 0)
11493 goto out;
11494 if (bpf_map_update_elem(mfd, &p2key, &p2fd, BPF_ANY) < 0)
11495 goto out;
11496 close(p2fd);
11497 close(p1fd);
11498
11499 return mfd;
11500out:
11501 close(p2fd);
11502 close(p1fd);
11503 close(mfd);
11504 return -1;
11258} 11505}
11259 11506
11260static int create_map_in_map(void) 11507static int create_map_in_map(void)
@@ -11375,7 +11622,8 @@ static void do_test_single(struct bpf_test *test, bool unpriv,
11375 goto fail_log; 11622 goto fail_log;
11376 } 11623 }
11377 if (!strstr(bpf_vlog, expected_err) && !reject_from_alignment) { 11624 if (!strstr(bpf_vlog, expected_err) && !reject_from_alignment) {
11378 printf("FAIL\nUnexpected error message!\n"); 11625 printf("FAIL\nUnexpected error message!\n\tEXP: %s\n\tRES: %s\n",
11626 expected_err, bpf_vlog);
11379 goto fail_log; 11627 goto fail_log;
11380 } 11628 }
11381 } 11629 }
@@ -11459,9 +11707,20 @@ out:
11459 return ret; 11707 return ret;
11460} 11708}
11461 11709
11710static void get_unpriv_disabled()
11711{
11712 char buf[2];
11713 FILE *fd;
11714
11715 fd = fopen("/proc/sys/"UNPRIV_SYSCTL, "r");
11716 if (fgets(buf, 2, fd) == buf && atoi(buf))
11717 unpriv_disabled = true;
11718 fclose(fd);
11719}
11720
11462static int do_test(bool unpriv, unsigned int from, unsigned int to) 11721static int do_test(bool unpriv, unsigned int from, unsigned int to)
11463{ 11722{
11464 int i, passes = 0, errors = 0; 11723 int i, passes = 0, errors = 0, skips = 0;
11465 11724
11466 for (i = from; i < to; i++) { 11725 for (i = from; i < to; i++) {
11467 struct bpf_test *test = &tests[i]; 11726 struct bpf_test *test = &tests[i];
@@ -11469,7 +11728,10 @@ static int do_test(bool unpriv, unsigned int from, unsigned int to)
11469 /* Program types that are not supported by non-root we 11728 /* Program types that are not supported by non-root we
11470 * skip right away. 11729 * skip right away.
11471 */ 11730 */
11472 if (!test->prog_type) { 11731 if (!test->prog_type && unpriv_disabled) {
11732 printf("#%d/u %s SKIP\n", i, test->descr);
11733 skips++;
11734 } else if (!test->prog_type) {
11473 if (!unpriv) 11735 if (!unpriv)
11474 set_admin(false); 11736 set_admin(false);
11475 printf("#%d/u %s ", i, test->descr); 11737 printf("#%d/u %s ", i, test->descr);
@@ -11478,20 +11740,22 @@ static int do_test(bool unpriv, unsigned int from, unsigned int to)
11478 set_admin(true); 11740 set_admin(true);
11479 } 11741 }
11480 11742
11481 if (!unpriv) { 11743 if (unpriv) {
11744 printf("#%d/p %s SKIP\n", i, test->descr);
11745 skips++;
11746 } else {
11482 printf("#%d/p %s ", i, test->descr); 11747 printf("#%d/p %s ", i, test->descr);
11483 do_test_single(test, false, &passes, &errors); 11748 do_test_single(test, false, &passes, &errors);
11484 } 11749 }
11485 } 11750 }
11486 11751
11487 printf("Summary: %d PASSED, %d FAILED\n", passes, errors); 11752 printf("Summary: %d PASSED, %d SKIPPED, %d FAILED\n", passes,
11753 skips, errors);
11488 return errors ? EXIT_FAILURE : EXIT_SUCCESS; 11754 return errors ? EXIT_FAILURE : EXIT_SUCCESS;
11489} 11755}
11490 11756
11491int main(int argc, char **argv) 11757int main(int argc, char **argv)
11492{ 11758{
11493 struct rlimit rinf = { RLIM_INFINITY, RLIM_INFINITY };
11494 struct rlimit rlim = { 1 << 20, 1 << 20 };
11495 unsigned int from = 0, to = ARRAY_SIZE(tests); 11759 unsigned int from = 0, to = ARRAY_SIZE(tests);
11496 bool unpriv = !is_admin(); 11760 bool unpriv = !is_admin();
11497 11761
@@ -11512,6 +11776,12 @@ int main(int argc, char **argv)
11512 } 11776 }
11513 } 11777 }
11514 11778
11515 setrlimit(RLIMIT_MEMLOCK, unpriv ? &rlim : &rinf); 11779 get_unpriv_disabled();
11780 if (unpriv && unpriv_disabled) {
11781 printf("Cannot run as unprivileged user with sysctl %s.\n",
11782 UNPRIV_SYSCTL);
11783 return EXIT_FAILURE;
11784 }
11785
11516 return do_test(unpriv, from, to); 11786 return do_test(unpriv, from, to);
11517} 11787}
diff --git a/tools/testing/selftests/bpf/test_verifier_log.c b/tools/testing/selftests/bpf/test_verifier_log.c
index e9626cf5607a..8d6918c3b4a2 100644
--- a/tools/testing/selftests/bpf/test_verifier_log.c
+++ b/tools/testing/selftests/bpf/test_verifier_log.c
@@ -4,7 +4,6 @@
4#include <string.h> 4#include <string.h>
5#include <unistd.h> 5#include <unistd.h>
6#include <sys/time.h> 6#include <sys/time.h>
7#include <sys/resource.h>
8 7
9#include <linux/bpf.h> 8#include <linux/bpf.h>
10#include <linux/filter.h> 9#include <linux/filter.h>
@@ -12,6 +11,8 @@
12 11
13#include <bpf/bpf.h> 12#include <bpf/bpf.h>
14 13
14#include "bpf_rlimit.h"
15
15#define LOG_SIZE (1 << 20) 16#define LOG_SIZE (1 << 20)
16 17
17#define err(str...) printf("ERROR: " str) 18#define err(str...) printf("ERROR: " str)
@@ -133,16 +134,11 @@ static void test_log_bad(char *log, size_t log_len, int log_level)
133 134
134int main(int argc, char **argv) 135int main(int argc, char **argv)
135{ 136{
136 struct rlimit limit = { RLIM_INFINITY, RLIM_INFINITY };
137 char full_log[LOG_SIZE]; 137 char full_log[LOG_SIZE];
138 char log[LOG_SIZE]; 138 char log[LOG_SIZE];
139 size_t want_len; 139 size_t want_len;
140 int i; 140 int i;
141 141
142 /* allow unlimited locked memory to have more consistent error code */
143 if (setrlimit(RLIMIT_MEMLOCK, &limit) < 0)
144 perror("Unable to lift memlock rlimit");
145
146 memset(log, 1, LOG_SIZE); 142 memset(log, 1, LOG_SIZE);
147 143
148 /* Test incorrect attr */ 144 /* Test incorrect attr */
diff --git a/tools/testing/selftests/bpf/urandom_read.c b/tools/testing/selftests/bpf/urandom_read.c
new file mode 100644
index 000000000000..4acfdebf36fa
--- /dev/null
+++ b/tools/testing/selftests/bpf/urandom_read.c
@@ -0,0 +1,22 @@
1#include <stdio.h>
2#include <unistd.h>
3#include <sys/types.h>
4#include <sys/stat.h>
5#include <fcntl.h>
6#include <stdlib.h>
7
8#define BUF_SIZE 256
9int main(void)
10{
11 int fd = open("/dev/urandom", O_RDONLY);
12 int i;
13 char buf[BUF_SIZE];
14
15 if (fd < 0)
16 return 1;
17 for (i = 0; i < 4; ++i)
18 read(fd, buf, BUF_SIZE);
19
20 close(fd);
21 return 0;
22}
diff --git a/tools/testing/selftests/net/Makefile b/tools/testing/selftests/net/Makefile
index d7c30d366935..785fc18a16b4 100644
--- a/tools/testing/selftests/net/Makefile
+++ b/tools/testing/selftests/net/Makefile
@@ -5,7 +5,7 @@ CFLAGS = -Wall -Wl,--no-as-needed -O2 -g
5CFLAGS += -I../../../../usr/include/ 5CFLAGS += -I../../../../usr/include/
6 6
7TEST_PROGS := run_netsocktests run_afpackettests test_bpf.sh netdevice.sh rtnetlink.sh 7TEST_PROGS := run_netsocktests run_afpackettests test_bpf.sh netdevice.sh rtnetlink.sh
8TEST_PROGS += fib_tests.sh 8TEST_PROGS += fib_tests.sh fib-onlink-tests.sh pmtu.sh
9TEST_GEN_FILES = socket 9TEST_GEN_FILES = socket
10TEST_GEN_FILES += psock_fanout psock_tpacket msg_zerocopy 10TEST_GEN_FILES += psock_fanout psock_tpacket msg_zerocopy
11TEST_GEN_PROGS = reuseport_bpf reuseport_bpf_cpu reuseport_bpf_numa 11TEST_GEN_PROGS = reuseport_bpf reuseport_bpf_cpu reuseport_bpf_numa
diff --git a/tools/testing/selftests/net/config b/tools/testing/selftests/net/config
index 7177bea1fdfa..6a75a3ea44ad 100644
--- a/tools/testing/selftests/net/config
+++ b/tools/testing/selftests/net/config
@@ -2,3 +2,8 @@ CONFIG_USER_NS=y
2CONFIG_BPF_SYSCALL=y 2CONFIG_BPF_SYSCALL=y
3CONFIG_TEST_BPF=m 3CONFIG_TEST_BPF=m
4CONFIG_NUMA=y 4CONFIG_NUMA=y
5CONFIG_NET_VRF=y
6CONFIG_NET_L3_MASTER_DEV=y
7CONFIG_IPV6=y
8CONFIG_IPV6_MULTIPLE_TABLES=y
9CONFIG_VETH=y
diff --git a/tools/testing/selftests/net/fib-onlink-tests.sh b/tools/testing/selftests/net/fib-onlink-tests.sh
new file mode 100755
index 000000000000..3991ad1a368d
--- /dev/null
+++ b/tools/testing/selftests/net/fib-onlink-tests.sh
@@ -0,0 +1,467 @@
1#!/bin/bash
2# SPDX-License-Identifier: GPL-2.0
3
4# IPv4 and IPv6 onlink tests
5
6PAUSE_ON_FAIL=${PAUSE_ON_FAIL:=no}
7
8# Network interfaces
9# - odd in current namespace; even in peer ns
10declare -A NETIFS
11# default VRF
12NETIFS[p1]=veth1
13NETIFS[p2]=veth2
14NETIFS[p3]=veth3
15NETIFS[p4]=veth4
16# VRF
17NETIFS[p5]=veth5
18NETIFS[p6]=veth6
19NETIFS[p7]=veth7
20NETIFS[p8]=veth8
21
22# /24 network
23declare -A V4ADDRS
24V4ADDRS[p1]=169.254.1.1
25V4ADDRS[p2]=169.254.1.2
26V4ADDRS[p3]=169.254.3.1
27V4ADDRS[p4]=169.254.3.2
28V4ADDRS[p5]=169.254.5.1
29V4ADDRS[p6]=169.254.5.2
30V4ADDRS[p7]=169.254.7.1
31V4ADDRS[p8]=169.254.7.2
32
33# /64 network
34declare -A V6ADDRS
35V6ADDRS[p1]=2001:db8:101::1
36V6ADDRS[p2]=2001:db8:101::2
37V6ADDRS[p3]=2001:db8:301::1
38V6ADDRS[p4]=2001:db8:301::2
39V6ADDRS[p5]=2001:db8:501::1
40V6ADDRS[p6]=2001:db8:501::2
41V6ADDRS[p7]=2001:db8:701::1
42V6ADDRS[p8]=2001:db8:701::2
43
44# Test networks:
45# [1] = default table
46# [2] = VRF
47#
48# /32 host routes
49declare -A TEST_NET4
50TEST_NET4[1]=169.254.101
51TEST_NET4[2]=169.254.102
52# /128 host routes
53declare -A TEST_NET6
54TEST_NET6[1]=2001:db8:101
55TEST_NET6[2]=2001:db8:102
56
57# connected gateway
58CONGW[1]=169.254.1.254
59CONGW[2]=169.254.3.254
60CONGW[3]=169.254.5.254
61
62# recursive gateway
63RECGW4[1]=169.254.11.254
64RECGW4[2]=169.254.12.254
65RECGW6[1]=2001:db8:11::64
66RECGW6[2]=2001:db8:12::64
67
68# for v4 mapped to v6
69declare -A TEST_NET4IN6IN6
70TEST_NET4IN6[1]=10.1.1.254
71TEST_NET4IN6[2]=10.2.1.254
72
73# mcast address
74MCAST6=ff02::1
75
76
77PEER_NS=bart
78PEER_CMD="ip netns exec ${PEER_NS}"
79VRF=lisa
80VRF_TABLE=1101
81PBR_TABLE=101
82
83################################################################################
84# utilities
85
86log_test()
87{
88 local rc=$1
89 local expected=$2
90 local msg="$3"
91
92 if [ ${rc} -eq ${expected} ]; then
93 nsuccess=$((nsuccess+1))
94 printf "\n TEST: %-50s [ OK ]\n" "${msg}"
95 else
96 nfail=$((nfail+1))
97 printf "\n TEST: %-50s [FAIL]\n" "${msg}"
98 if [ "${PAUSE_ON_FAIL}" = "yes" ]; then
99 echo
100 echo "hit enter to continue, 'q' to quit"
101 read a
102 [ "$a" = "q" ] && exit 1
103 fi
104 fi
105}
106
107log_section()
108{
109 echo
110 echo "######################################################################"
111 echo "TEST SECTION: $*"
112 echo "######################################################################"
113}
114
115log_subsection()
116{
117 echo
118 echo "#########################################"
119 echo "TEST SUBSECTION: $*"
120}
121
122run_cmd()
123{
124 echo
125 echo "COMMAND: $*"
126 eval $*
127}
128
129get_linklocal()
130{
131 local dev=$1
132 local pfx
133 local addr
134
135 addr=$(${pfx} ip -6 -br addr show dev ${dev} | \
136 awk '{
137 for (i = 3; i <= NF; ++i) {
138 if ($i ~ /^fe80/)
139 print $i
140 }
141 }'
142 )
143 addr=${addr/\/*}
144
145 [ -z "$addr" ] && return 1
146
147 echo $addr
148
149 return 0
150}
151
152################################################################################
153#
154
155setup()
156{
157 echo
158 echo "########################################"
159 echo "Configuring interfaces"
160
161 set -e
162
163 # create namespace
164 ip netns add ${PEER_NS}
165 ip -netns ${PEER_NS} li set lo up
166
167 # add vrf table
168 ip li add ${VRF} type vrf table ${VRF_TABLE}
169 ip li set ${VRF} up
170 ip ro add table ${VRF_TABLE} unreachable default
171 ip -6 ro add table ${VRF_TABLE} unreachable default
172
173 # create test interfaces
174 ip li add ${NETIFS[p1]} type veth peer name ${NETIFS[p2]}
175 ip li add ${NETIFS[p3]} type veth peer name ${NETIFS[p4]}
176 ip li add ${NETIFS[p5]} type veth peer name ${NETIFS[p6]}
177 ip li add ${NETIFS[p7]} type veth peer name ${NETIFS[p8]}
178
179 # enslave vrf interfaces
180 for n in 5 7; do
181 ip li set ${NETIFS[p${n}]} vrf ${VRF}
182 done
183
184 # add addresses
185 for n in 1 3 5 7; do
186 ip li set ${NETIFS[p${n}]} up
187 ip addr add ${V4ADDRS[p${n}]}/24 dev ${NETIFS[p${n}]}
188 ip addr add ${V6ADDRS[p${n}]}/64 dev ${NETIFS[p${n}]}
189 done
190
191 # move peer interfaces to namespace and add addresses
192 for n in 2 4 6 8; do
193 ip li set ${NETIFS[p${n}]} netns ${PEER_NS} up
194 ip -netns ${PEER_NS} addr add ${V4ADDRS[p${n}]}/24 dev ${NETIFS[p${n}]}
195 ip -netns ${PEER_NS} addr add ${V6ADDRS[p${n}]}/64 dev ${NETIFS[p${n}]}
196 done
197
198 set +e
199
200 # let DAD complete - assume default of 1 probe
201 sleep 1
202}
203
204cleanup()
205{
206 # make sure we start from a clean slate
207 ip netns del ${PEER_NS} 2>/dev/null
208 for n in 1 3 5 7; do
209 ip link del ${NETIFS[p${n}]} 2>/dev/null
210 done
211 ip link del ${VRF} 2>/dev/null
212 ip ro flush table ${VRF_TABLE}
213 ip -6 ro flush table ${VRF_TABLE}
214}
215
216################################################################################
217# IPv4 tests
218#
219
220run_ip()
221{
222 local table="$1"
223 local prefix="$2"
224 local gw="$3"
225 local dev="$4"
226 local exp_rc="$5"
227 local desc="$6"
228
229 # dev arg may be empty
230 [ -n "${dev}" ] && dev="dev ${dev}"
231
232 run_cmd ip ro add table "${table}" "${prefix}"/32 via "${gw}" "${dev}" onlink
233 log_test $? ${exp_rc} "${desc}"
234}
235
236run_ip_mpath()
237{
238 local table="$1"
239 local prefix="$2"
240 local nh1="$3"
241 local nh2="$4"
242 local exp_rc="$5"
243 local desc="$6"
244
245 # dev arg may be empty
246 [ -n "${dev}" ] && dev="dev ${dev}"
247
248 run_cmd ip ro add table "${table}" "${prefix}"/32 \
249 nexthop via ${nh1} nexthop via ${nh2}
250 log_test $? ${exp_rc} "${desc}"
251}
252
253valid_onlink_ipv4()
254{
255 # - unicast connected, unicast recursive
256 #
257 log_subsection "default VRF - main table"
258
259 run_ip 254 ${TEST_NET4[1]}.1 ${CONGW[1]} ${NETIFS[p1]} 0 "unicast connected"
260 run_ip 254 ${TEST_NET4[1]}.2 ${RECGW4[1]} ${NETIFS[p1]} 0 "unicast recursive"
261
262 log_subsection "VRF ${VRF}"
263
264 run_ip ${VRF_TABLE} ${TEST_NET4[2]}.1 ${CONGW[3]} ${NETIFS[p5]} 0 "unicast connected"
265 run_ip ${VRF_TABLE} ${TEST_NET4[2]}.2 ${RECGW4[2]} ${NETIFS[p5]} 0 "unicast recursive"
266
267 log_subsection "VRF device, PBR table"
268
269 run_ip ${PBR_TABLE} ${TEST_NET4[2]}.3 ${CONGW[3]} ${NETIFS[p5]} 0 "unicast connected"
270 run_ip ${PBR_TABLE} ${TEST_NET4[2]}.4 ${RECGW4[2]} ${NETIFS[p5]} 0 "unicast recursive"
271
272 # multipath version
273 #
274 log_subsection "default VRF - main table - multipath"
275
276 run_ip_mpath 254 ${TEST_NET4[1]}.5 \
277 "${CONGW[1]} dev ${NETIFS[p1]} onlink" \
278 "${CONGW[2]} dev ${NETIFS[p3]} onlink" \
279 0 "unicast connected - multipath"
280
281 run_ip_mpath 254 ${TEST_NET4[1]}.6 \
282 "${RECGW4[1]} dev ${NETIFS[p1]} onlink" \
283 "${RECGW4[2]} dev ${NETIFS[p3]} onlink" \
284 0 "unicast recursive - multipath"
285
286 run_ip_mpath 254 ${TEST_NET4[1]}.7 \
287 "${CONGW[1]} dev ${NETIFS[p1]}" \
288 "${CONGW[2]} dev ${NETIFS[p3]} onlink" \
289 0 "unicast connected - multipath onlink first only"
290
291 run_ip_mpath 254 ${TEST_NET4[1]}.8 \
292 "${CONGW[1]} dev ${NETIFS[p1]} onlink" \
293 "${CONGW[2]} dev ${NETIFS[p3]}" \
294 0 "unicast connected - multipath onlink second only"
295}
296
297invalid_onlink_ipv4()
298{
299 run_ip 254 ${TEST_NET4[1]}.11 ${V4ADDRS[p1]} ${NETIFS[p1]} 2 \
300 "Invalid gw - local unicast address"
301
302 run_ip ${VRF_TABLE} ${TEST_NET4[2]}.11 ${V4ADDRS[p5]} ${NETIFS[p5]} 2 \
303 "Invalid gw - local unicast address, VRF"
304
305 run_ip 254 ${TEST_NET4[1]}.101 ${V4ADDRS[p1]} "" 2 "No nexthop device given"
306
307 run_ip 254 ${TEST_NET4[1]}.102 ${V4ADDRS[p3]} ${NETIFS[p1]} 2 \
308 "Gateway resolves to wrong nexthop device"
309
310 run_ip ${VRF_TABLE} ${TEST_NET4[2]}.103 ${V4ADDRS[p7]} ${NETIFS[p5]} 2 \
311 "Gateway resolves to wrong nexthop device - VRF"
312}
313
314################################################################################
315# IPv6 tests
316#
317
318run_ip6()
319{
320 local table="$1"
321 local prefix="$2"
322 local gw="$3"
323 local dev="$4"
324 local exp_rc="$5"
325 local desc="$6"
326
327 # dev arg may be empty
328 [ -n "${dev}" ] && dev="dev ${dev}"
329
330 run_cmd ip -6 ro add table "${table}" "${prefix}"/128 via "${gw}" "${dev}" onlink
331 log_test $? ${exp_rc} "${desc}"
332}
333
334run_ip6_mpath()
335{
336 local table="$1"
337 local prefix="$2"
338 local opts="$3"
339 local nh1="$4"
340 local nh2="$5"
341 local exp_rc="$6"
342 local desc="$7"
343
344 run_cmd ip -6 ro add table "${table}" "${prefix}"/128 "${opts}" \
345 nexthop via ${nh1} nexthop via ${nh2}
346 log_test $? ${exp_rc} "${desc}"
347}
348
349valid_onlink_ipv6()
350{
351 # - unicast connected, unicast recursive, v4-mapped
352 #
353 log_subsection "default VRF - main table"
354
355 run_ip6 254 ${TEST_NET6[1]}::1 ${V6ADDRS[p1]/::*}::64 ${NETIFS[p1]} 0 "unicast connected"
356 run_ip6 254 ${TEST_NET6[1]}::2 ${RECGW6[1]} ${NETIFS[p1]} 0 "unicast recursive"
357 run_ip6 254 ${TEST_NET6[1]}::3 ::ffff:${TEST_NET4IN6[1]} ${NETIFS[p1]} 0 "v4-mapped"
358
359 log_subsection "VRF ${VRF}"
360
361 run_ip6 ${VRF_TABLE} ${TEST_NET6[2]}::1 ${V6ADDRS[p5]/::*}::64 ${NETIFS[p5]} 0 "unicast connected"
362 run_ip6 ${VRF_TABLE} ${TEST_NET6[2]}::2 ${RECGW6[2]} ${NETIFS[p5]} 0 "unicast recursive"
363 run_ip6 ${VRF_TABLE} ${TEST_NET6[2]}::3 ::ffff:${TEST_NET4IN6[2]} ${NETIFS[p5]} 0 "v4-mapped"
364
365 log_subsection "VRF device, PBR table"
366
367 run_ip6 ${PBR_TABLE} ${TEST_NET6[2]}::4 ${V6ADDRS[p5]/::*}::64 ${NETIFS[p5]} 0 "unicast connected"
368 run_ip6 ${PBR_TABLE} ${TEST_NET6[2]}::5 ${RECGW6[2]} ${NETIFS[p5]} 0 "unicast recursive"
369 run_ip6 ${PBR_TABLE} ${TEST_NET6[2]}::6 ::ffff:${TEST_NET4IN6[2]} ${NETIFS[p5]} 0 "v4-mapped"
370
371 # multipath version
372 #
373 log_subsection "default VRF - main table - multipath"
374
375 run_ip6_mpath 254 ${TEST_NET6[1]}::4 "onlink" \
376 "${V6ADDRS[p1]/::*}::64 dev ${NETIFS[p1]}" \
377 "${V6ADDRS[p3]/::*}::64 dev ${NETIFS[p3]}" \
378 0 "unicast connected - multipath onlink"
379
380 run_ip6_mpath 254 ${TEST_NET6[1]}::5 "onlink" \
381 "${RECGW6[1]} dev ${NETIFS[p1]}" \
382 "${RECGW6[2]} dev ${NETIFS[p3]}" \
383 0 "unicast recursive - multipath onlink"
384
385 run_ip6_mpath 254 ${TEST_NET6[1]}::6 "onlink" \
386 "::ffff:${TEST_NET4IN6[1]} dev ${NETIFS[p1]}" \
387 "::ffff:${TEST_NET4IN6[2]} dev ${NETIFS[p3]}" \
388 0 "v4-mapped - multipath onlink"
389
390 run_ip6_mpath 254 ${TEST_NET6[1]}::7 "" \
391 "${V6ADDRS[p1]/::*}::64 dev ${NETIFS[p1]} onlink" \
392 "${V6ADDRS[p3]/::*}::64 dev ${NETIFS[p3]} onlink" \
393 0 "unicast connected - multipath onlink both nexthops"
394
395 run_ip6_mpath 254 ${TEST_NET6[1]}::8 "" \
396 "${V6ADDRS[p1]/::*}::64 dev ${NETIFS[p1]} onlink" \
397 "${V6ADDRS[p3]/::*}::64 dev ${NETIFS[p3]}" \
398 0 "unicast connected - multipath onlink first only"
399
400 run_ip6_mpath 254 ${TEST_NET6[1]}::9 "" \
401 "${V6ADDRS[p1]/::*}::64 dev ${NETIFS[p1]}" \
402 "${V6ADDRS[p3]/::*}::64 dev ${NETIFS[p3]} onlink" \
403 0 "unicast connected - multipath onlink second only"
404}
405
406invalid_onlink_ipv6()
407{
408 local lladdr
409
410 lladdr=$(get_linklocal ${NETIFS[p1]}) || return 1
411
412 run_ip6 254 ${TEST_NET6[1]}::11 ${V6ADDRS[p1]} ${NETIFS[p1]} 2 \
413 "Invalid gw - local unicast address"
414 run_ip6 254 ${TEST_NET6[1]}::12 ${lladdr} ${NETIFS[p1]} 2 \
415 "Invalid gw - local linklocal address"
416 run_ip6 254 ${TEST_NET6[1]}::12 ${MCAST6} ${NETIFS[p1]} 2 \
417 "Invalid gw - multicast address"
418
419 lladdr=$(get_linklocal ${NETIFS[p5]}) || return 1
420 run_ip6 ${VRF_TABLE} ${TEST_NET6[2]}::11 ${V6ADDRS[p5]} ${NETIFS[p5]} 2 \
421 "Invalid gw - local unicast address, VRF"
422 run_ip6 ${VRF_TABLE} ${TEST_NET6[2]}::12 ${lladdr} ${NETIFS[p5]} 2 \
423 "Invalid gw - local linklocal address, VRF"
424 run_ip6 ${VRF_TABLE} ${TEST_NET6[2]}::12 ${MCAST6} ${NETIFS[p5]} 2 \
425 "Invalid gw - multicast address, VRF"
426
427 run_ip6 254 ${TEST_NET6[1]}::101 ${V6ADDRS[p1]} "" 2 \
428 "No nexthop device given"
429
430 # default VRF validation is done against LOCAL table
431 # run_ip6 254 ${TEST_NET6[1]}::102 ${V6ADDRS[p3]/::[0-9]/::64} ${NETIFS[p1]} 2 \
432 # "Gateway resolves to wrong nexthop device"
433
434 run_ip6 ${VRF_TABLE} ${TEST_NET6[2]}::103 ${V6ADDRS[p7]/::[0-9]/::64} ${NETIFS[p5]} 2 \
435 "Gateway resolves to wrong nexthop device - VRF"
436}
437
438run_onlink_tests()
439{
440 log_section "IPv4 onlink"
441 log_subsection "Valid onlink commands"
442 valid_onlink_ipv4
443 log_subsection "Invalid onlink commands"
444 invalid_onlink_ipv4
445
446 log_section "IPv6 onlink"
447 log_subsection "Valid onlink commands"
448 valid_onlink_ipv6
449 log_subsection "Invalid onlink commands"
450 invalid_onlink_ipv6
451}
452
453################################################################################
454# main
455
456nsuccess=0
457nfail=0
458
459cleanup
460setup
461run_onlink_tests
462cleanup
463
464if [ "$TESTS" != "none" ]; then
465 printf "\nTests passed: %3d\n" ${nsuccess}
466 printf "Tests failed: %3d\n" ${nfail}
467fi
diff --git a/tools/testing/selftests/net/fib_tests.sh b/tools/testing/selftests/net/fib_tests.sh
index a9154eefb2e2..9164e60d4b66 100755
--- a/tools/testing/selftests/net/fib_tests.sh
+++ b/tools/testing/selftests/net/fib_tests.sh
@@ -6,154 +6,179 @@
6 6
7ret=0 7ret=0
8 8
9check_err() 9VERBOSE=${VERBOSE:=0}
10{ 10PAUSE_ON_FAIL=${PAUSE_ON_FAIL:=no}
11 if [ $ret -eq 0 ]; then 11IP="ip -netns testns"
12 ret=$1
13 fi
14}
15 12
16check_fail() 13log_test()
17{ 14{
18 if [ $1 -eq 0 ]; then 15 local rc=$1
16 local expected=$2
17 local msg="$3"
18
19 if [ ${rc} -eq ${expected} ]; then
20 printf " TEST: %-60s [ OK ]\n" "${msg}"
21 else
19 ret=1 22 ret=1
23 printf " TEST: %-60s [FAIL]\n" "${msg}"
24 if [ "${PAUSE_ON_FAIL}" = "yes" ]; then
25 echo
26 echo "hit enter to continue, 'q' to quit"
27 read a
28 [ "$a" = "q" ] && exit 1
29 fi
20 fi 30 fi
21} 31}
22 32
23netns_create() 33setup()
24{ 34{
25 local testns=$1 35 set -e
36 ip netns add testns
37 $IP link set dev lo up
38
39 $IP link add dummy0 type dummy
40 $IP link set dev dummy0 up
41 $IP address add 198.51.100.1/24 dev dummy0
42 $IP -6 address add 2001:db8:1::1/64 dev dummy0
43 set +e
26 44
27 ip netns add $testns
28 ip netns exec $testns ip link set dev lo up
29} 45}
30 46
31fib_unreg_unicast_test() 47cleanup()
32{ 48{
33 ret=0 49 $IP link del dev dummy0 &> /dev/null
50 ip netns del testns
51}
34 52
35 netns_create "testns" 53get_linklocal()
54{
55 local dev=$1
56 local addr
36 57
37 ip netns exec testns ip link add dummy0 type dummy 58 addr=$($IP -6 -br addr show dev ${dev} | \
38 ip netns exec testns ip link set dev dummy0 up 59 awk '{
60 for (i = 3; i <= NF; ++i) {
61 if ($i ~ /^fe80/)
62 print $i
63 }
64 }'
65 )
66 addr=${addr/\/*}
39 67
40 ip netns exec testns ip address add 198.51.100.1/24 dev dummy0 68 [ -z "$addr" ] && return 1
41 ip netns exec testns ip -6 address add 2001:db8:1::1/64 dev dummy0
42 69
43 ip netns exec testns ip route get fibmatch 198.51.100.2 &> /dev/null 70 echo $addr
44 check_err $?
45 ip netns exec testns ip -6 route get fibmatch 2001:db8:1::2 &> /dev/null
46 check_err $?
47 71
48 ip netns exec testns ip link del dev dummy0 72 return 0
49 check_err $? 73}
50 74
51 ip netns exec testns ip route get fibmatch 198.51.100.2 &> /dev/null 75fib_unreg_unicast_test()
52 check_fail $? 76{
53 ip netns exec testns ip -6 route get fibmatch 2001:db8:1::2 &> /dev/null 77 echo
54 check_fail $? 78 echo "Single path route test"
55 79
56 ip netns del testns 80 setup
57 81
58 if [ $ret -ne 0 ]; then 82 echo " Start point"
59 echo "FAIL: unicast route test" 83 $IP route get fibmatch 198.51.100.2 &> /dev/null
60 return 1 84 log_test $? 0 "IPv4 fibmatch"
61 fi 85 $IP -6 route get fibmatch 2001:db8:1::2 &> /dev/null
62 echo "PASS: unicast route test" 86 log_test $? 0 "IPv6 fibmatch"
87
88 set -e
89 $IP link del dev dummy0
90 set +e
91
92 echo " Nexthop device deleted"
93 $IP route get fibmatch 198.51.100.2 &> /dev/null
94 log_test $? 2 "IPv4 fibmatch - no route"
95 $IP -6 route get fibmatch 2001:db8:1::2 &> /dev/null
96 log_test $? 2 "IPv6 fibmatch - no route"
97
98 cleanup
63} 99}
64 100
65fib_unreg_multipath_test() 101fib_unreg_multipath_test()
66{ 102{
67 ret=0
68
69 netns_create "testns"
70 103
71 ip netns exec testns ip link add dummy0 type dummy 104 echo
72 ip netns exec testns ip link set dev dummy0 up 105 echo "Multipath route test"
73 106
74 ip netns exec testns ip link add dummy1 type dummy 107 setup
75 ip netns exec testns ip link set dev dummy1 up
76 108
77 ip netns exec testns ip address add 198.51.100.1/24 dev dummy0 109 set -e
78 ip netns exec testns ip -6 address add 2001:db8:1::1/64 dev dummy0 110 $IP link add dummy1 type dummy
111 $IP link set dev dummy1 up
112 $IP address add 192.0.2.1/24 dev dummy1
113 $IP -6 address add 2001:db8:2::1/64 dev dummy1
79 114
80 ip netns exec testns ip address add 192.0.2.1/24 dev dummy1 115 $IP route add 203.0.113.0/24 \
81 ip netns exec testns ip -6 address add 2001:db8:2::1/64 dev dummy1
82
83 ip netns exec testns ip route add 203.0.113.0/24 \
84 nexthop via 198.51.100.2 dev dummy0 \ 116 nexthop via 198.51.100.2 dev dummy0 \
85 nexthop via 192.0.2.2 dev dummy1 117 nexthop via 192.0.2.2 dev dummy1
86 ip netns exec testns ip -6 route add 2001:db8:3::/64 \ 118 $IP -6 route add 2001:db8:3::/64 \
87 nexthop via 2001:db8:1::2 dev dummy0 \ 119 nexthop via 2001:db8:1::2 dev dummy0 \
88 nexthop via 2001:db8:2::2 dev dummy1 120 nexthop via 2001:db8:2::2 dev dummy1
121 set +e
122
123 echo " Start point"
124 $IP route get fibmatch 203.0.113.1 &> /dev/null
125 log_test $? 0 "IPv4 fibmatch"
126 $IP -6 route get fibmatch 2001:db8:3::1 &> /dev/null
127 log_test $? 0 "IPv6 fibmatch"
89 128
90 ip netns exec testns ip route get fibmatch 203.0.113.1 &> /dev/null 129 set -e
91 check_err $? 130 $IP link del dev dummy0
92 ip netns exec testns ip -6 route get fibmatch 2001:db8:3::1 &> /dev/null 131 set +e
93 check_err $?
94 132
95 ip netns exec testns ip link del dev dummy0 133 echo " One nexthop device deleted"
96 check_err $? 134 $IP route get fibmatch 203.0.113.1 &> /dev/null
135 log_test $? 2 "IPv4 - multipath route removed on delete"
97 136
98 ip netns exec testns ip route get fibmatch 203.0.113.1 &> /dev/null 137 $IP -6 route get fibmatch 2001:db8:3::1 &> /dev/null
99 check_fail $?
100 ip netns exec testns ip -6 route get fibmatch 2001:db8:3::1 &> /dev/null
101 # In IPv6 we do not flush the entire multipath route. 138 # In IPv6 we do not flush the entire multipath route.
102 check_err $? 139 log_test $? 0 "IPv6 - multipath down to single path"
103 140
104 ip netns exec testns ip link del dev dummy1 141 set -e
142 $IP link del dev dummy1
143 set +e
105 144
106 ip netns del testns 145 echo " Second nexthop device deleted"
146 $IP -6 route get fibmatch 2001:db8:3::1 &> /dev/null
147 log_test $? 2 "IPv6 - no route"
107 148
108 if [ $ret -ne 0 ]; then 149 cleanup
109 echo "FAIL: multipath route test"
110 return 1
111 fi
112 echo "PASS: multipath route test"
113} 150}
114 151
115fib_unreg_test() 152fib_unreg_test()
116{ 153{
117 echo "Running netdev unregister tests"
118
119 fib_unreg_unicast_test 154 fib_unreg_unicast_test
120 fib_unreg_multipath_test 155 fib_unreg_multipath_test
121} 156}
122 157
123fib_down_unicast_test() 158fib_down_unicast_test()
124{ 159{
125 ret=0 160 echo
126 161 echo "Single path, admin down"
127 netns_create "testns"
128
129 ip netns exec testns ip link add dummy0 type dummy
130 ip netns exec testns ip link set dev dummy0 up
131 162
132 ip netns exec testns ip address add 198.51.100.1/24 dev dummy0 163 setup
133 ip netns exec testns ip -6 address add 2001:db8:1::1/64 dev dummy0
134 164
135 ip netns exec testns ip route get fibmatch 198.51.100.2 &> /dev/null 165 echo " Start point"
136 check_err $? 166 $IP route get fibmatch 198.51.100.2 &> /dev/null
137 ip netns exec testns ip -6 route get fibmatch 2001:db8:1::2 &> /dev/null 167 log_test $? 0 "IPv4 fibmatch"
138 check_err $? 168 $IP -6 route get fibmatch 2001:db8:1::2 &> /dev/null
169 log_test $? 0 "IPv6 fibmatch"
139 170
140 ip netns exec testns ip link set dev dummy0 down 171 set -e
141 check_err $? 172 $IP link set dev dummy0 down
173 set +e
142 174
143 ip netns exec testns ip route get fibmatch 198.51.100.2 &> /dev/null 175 echo " Route deleted on down"
144 check_fail $? 176 $IP route get fibmatch 198.51.100.2 &> /dev/null
145 ip netns exec testns ip -6 route get fibmatch 2001:db8:1::2 &> /dev/null 177 log_test $? 2 "IPv4 fibmatch"
146 check_fail $? 178 $IP -6 route get fibmatch 2001:db8:1::2 &> /dev/null
179 log_test $? 2 "IPv6 fibmatch"
147 180
148 ip netns exec testns ip link del dev dummy0 181 cleanup
149
150 ip netns del testns
151
152 if [ $ret -ne 0 ]; then
153 echo "FAIL: unicast route test"
154 return 1
155 fi
156 echo "PASS: unicast route test"
157} 182}
158 183
159fib_down_multipath_test_do() 184fib_down_multipath_test_do()
@@ -161,251 +186,395 @@ fib_down_multipath_test_do()
161 local down_dev=$1 186 local down_dev=$1
162 local up_dev=$2 187 local up_dev=$2
163 188
164 ip netns exec testns ip route get fibmatch 203.0.113.1 \ 189 $IP route get fibmatch 203.0.113.1 \
165 oif $down_dev &> /dev/null 190 oif $down_dev &> /dev/null
166 check_fail $? 191 log_test $? 2 "IPv4 fibmatch on down device"
167 ip netns exec testns ip -6 route get fibmatch 2001:db8:3::1 \ 192 $IP -6 route get fibmatch 2001:db8:3::1 \
168 oif $down_dev &> /dev/null 193 oif $down_dev &> /dev/null
169 check_fail $? 194 log_test $? 2 "IPv6 fibmatch on down device"
170 195
171 ip netns exec testns ip route get fibmatch 203.0.113.1 \ 196 $IP route get fibmatch 203.0.113.1 \
172 oif $up_dev &> /dev/null 197 oif $up_dev &> /dev/null
173 check_err $? 198 log_test $? 0 "IPv4 fibmatch on up device"
174 ip netns exec testns ip -6 route get fibmatch 2001:db8:3::1 \ 199 $IP -6 route get fibmatch 2001:db8:3::1 \
175 oif $up_dev &> /dev/null 200 oif $up_dev &> /dev/null
176 check_err $? 201 log_test $? 0 "IPv6 fibmatch on up device"
177 202
178 ip netns exec testns ip route get fibmatch 203.0.113.1 | \ 203 $IP route get fibmatch 203.0.113.1 | \
179 grep $down_dev | grep -q "dead linkdown" 204 grep $down_dev | grep -q "dead linkdown"
180 check_err $? 205 log_test $? 0 "IPv4 flags on down device"
181 ip netns exec testns ip -6 route get fibmatch 2001:db8:3::1 | \ 206 $IP -6 route get fibmatch 2001:db8:3::1 | \
182 grep $down_dev | grep -q "dead linkdown" 207 grep $down_dev | grep -q "dead linkdown"
183 check_err $? 208 log_test $? 0 "IPv6 flags on down device"
184 209
185 ip netns exec testns ip route get fibmatch 203.0.113.1 | \ 210 $IP route get fibmatch 203.0.113.1 | \
186 grep $up_dev | grep -q "dead linkdown" 211 grep $up_dev | grep -q "dead linkdown"
187 check_fail $? 212 log_test $? 1 "IPv4 flags on up device"
188 ip netns exec testns ip -6 route get fibmatch 2001:db8:3::1 | \ 213 $IP -6 route get fibmatch 2001:db8:3::1 | \
189 grep $up_dev | grep -q "dead linkdown" 214 grep $up_dev | grep -q "dead linkdown"
190 check_fail $? 215 log_test $? 1 "IPv6 flags on up device"
191} 216}
192 217
193fib_down_multipath_test() 218fib_down_multipath_test()
194{ 219{
195 ret=0 220 echo
196 221 echo "Admin down multipath"
197 netns_create "testns"
198 222
199 ip netns exec testns ip link add dummy0 type dummy 223 setup
200 ip netns exec testns ip link set dev dummy0 up
201 224
202 ip netns exec testns ip link add dummy1 type dummy 225 set -e
203 ip netns exec testns ip link set dev dummy1 up 226 $IP link add dummy1 type dummy
227 $IP link set dev dummy1 up
204 228
205 ip netns exec testns ip address add 198.51.100.1/24 dev dummy0 229 $IP address add 192.0.2.1/24 dev dummy1
206 ip netns exec testns ip -6 address add 2001:db8:1::1/64 dev dummy0 230 $IP -6 address add 2001:db8:2::1/64 dev dummy1
207 231
208 ip netns exec testns ip address add 192.0.2.1/24 dev dummy1 232 $IP route add 203.0.113.0/24 \
209 ip netns exec testns ip -6 address add 2001:db8:2::1/64 dev dummy1
210
211 ip netns exec testns ip route add 203.0.113.0/24 \
212 nexthop via 198.51.100.2 dev dummy0 \ 233 nexthop via 198.51.100.2 dev dummy0 \
213 nexthop via 192.0.2.2 dev dummy1 234 nexthop via 192.0.2.2 dev dummy1
214 ip netns exec testns ip -6 route add 2001:db8:3::/64 \ 235 $IP -6 route add 2001:db8:3::/64 \
215 nexthop via 2001:db8:1::2 dev dummy0 \ 236 nexthop via 2001:db8:1::2 dev dummy0 \
216 nexthop via 2001:db8:2::2 dev dummy1 237 nexthop via 2001:db8:2::2 dev dummy1
238 set +e
239
240 echo " Verify start point"
241 $IP route get fibmatch 203.0.113.1 &> /dev/null
242 log_test $? 0 "IPv4 fibmatch"
217 243
218 ip netns exec testns ip route get fibmatch 203.0.113.1 &> /dev/null 244 $IP -6 route get fibmatch 2001:db8:3::1 &> /dev/null
219 check_err $? 245 log_test $? 0 "IPv6 fibmatch"
220 ip netns exec testns ip -6 route get fibmatch 2001:db8:3::1 &> /dev/null
221 check_err $?
222 246
223 ip netns exec testns ip link set dev dummy0 down 247 set -e
224 check_err $? 248 $IP link set dev dummy0 down
249 set +e
225 250
251 echo " One device down, one up"
226 fib_down_multipath_test_do "dummy0" "dummy1" 252 fib_down_multipath_test_do "dummy0" "dummy1"
227 253
228 ip netns exec testns ip link set dev dummy0 up 254 set -e
229 check_err $? 255 $IP link set dev dummy0 up
230 ip netns exec testns ip link set dev dummy1 down 256 $IP link set dev dummy1 down
231 check_err $? 257 set +e
232 258
259 echo " Other device down and up"
233 fib_down_multipath_test_do "dummy1" "dummy0" 260 fib_down_multipath_test_do "dummy1" "dummy0"
234 261
235 ip netns exec testns ip link set dev dummy0 down 262 set -e
236 check_err $? 263 $IP link set dev dummy0 down
237 264 set +e
238 ip netns exec testns ip route get fibmatch 203.0.113.1 &> /dev/null
239 check_fail $?
240 ip netns exec testns ip -6 route get fibmatch 2001:db8:3::1 &> /dev/null
241 check_fail $?
242 265
243 ip netns exec testns ip link del dev dummy1 266 echo " Both devices down"
244 ip netns exec testns ip link del dev dummy0 267 $IP route get fibmatch 203.0.113.1 &> /dev/null
245 268 log_test $? 2 "IPv4 fibmatch"
246 ip netns del testns 269 $IP -6 route get fibmatch 2001:db8:3::1 &> /dev/null
270 log_test $? 2 "IPv6 fibmatch"
247 271
248 if [ $ret -ne 0 ]; then 272 $IP link del dev dummy1
249 echo "FAIL: multipath route test" 273 cleanup
250 return 1
251 fi
252 echo "PASS: multipath route test"
253} 274}
254 275
255fib_down_test() 276fib_down_test()
256{ 277{
257 echo "Running netdev down tests"
258
259 fib_down_unicast_test 278 fib_down_unicast_test
260 fib_down_multipath_test 279 fib_down_multipath_test
261} 280}
262 281
282# Local routes should not be affected when carrier changes.
263fib_carrier_local_test() 283fib_carrier_local_test()
264{ 284{
265 ret=0 285 echo
266 286 echo "Local carrier tests - single path"
267 # Local routes should not be affected when carrier changes.
268 netns_create "testns"
269
270 ip netns exec testns ip link add dummy0 type dummy
271 ip netns exec testns ip link set dev dummy0 up
272 287
273 ip netns exec testns ip link set dev dummy0 carrier on 288 setup
274 289
275 ip netns exec testns ip address add 198.51.100.1/24 dev dummy0 290 set -e
276 ip netns exec testns ip -6 address add 2001:db8:1::1/64 dev dummy0 291 $IP link set dev dummy0 carrier on
292 set +e
277 293
278 ip netns exec testns ip route get fibmatch 198.51.100.1 &> /dev/null 294 echo " Start point"
279 check_err $? 295 $IP route get fibmatch 198.51.100.1 &> /dev/null
280 ip netns exec testns ip -6 route get fibmatch 2001:db8:1::1 &> /dev/null 296 log_test $? 0 "IPv4 fibmatch"
281 check_err $? 297 $IP -6 route get fibmatch 2001:db8:1::1 &> /dev/null
298 log_test $? 0 "IPv6 fibmatch"
282 299
283 ip netns exec testns ip route get fibmatch 198.51.100.1 | \ 300 $IP route get fibmatch 198.51.100.1 | \
284 grep -q "linkdown" 301 grep -q "linkdown"
285 check_fail $? 302 log_test $? 1 "IPv4 - no linkdown flag"
286 ip netns exec testns ip -6 route get fibmatch 2001:db8:1::1 | \ 303 $IP -6 route get fibmatch 2001:db8:1::1 | \
287 grep -q "linkdown" 304 grep -q "linkdown"
288 check_fail $? 305 log_test $? 1 "IPv6 - no linkdown flag"
289 306
290 ip netns exec testns ip link set dev dummy0 carrier off 307 set -e
308 $IP link set dev dummy0 carrier off
309 sleep 1
310 set +e
291 311
292 ip netns exec testns ip route get fibmatch 198.51.100.1 &> /dev/null 312 echo " Carrier off on nexthop"
293 check_err $? 313 $IP route get fibmatch 198.51.100.1 &> /dev/null
294 ip netns exec testns ip -6 route get fibmatch 2001:db8:1::1 &> /dev/null 314 log_test $? 0 "IPv4 fibmatch"
295 check_err $? 315 $IP -6 route get fibmatch 2001:db8:1::1 &> /dev/null
316 log_test $? 0 "IPv6 fibmatch"
296 317
297 ip netns exec testns ip route get fibmatch 198.51.100.1 | \ 318 $IP route get fibmatch 198.51.100.1 | \
298 grep -q "linkdown" 319 grep -q "linkdown"
299 check_fail $? 320 log_test $? 1 "IPv4 - linkdown flag set"
300 ip netns exec testns ip -6 route get fibmatch 2001:db8:1::1 | \ 321 $IP -6 route get fibmatch 2001:db8:1::1 | \
301 grep -q "linkdown" 322 grep -q "linkdown"
302 check_fail $? 323 log_test $? 1 "IPv6 - linkdown flag set"
303 324
304 ip netns exec testns ip address add 192.0.2.1/24 dev dummy0 325 set -e
305 ip netns exec testns ip -6 address add 2001:db8:2::1/64 dev dummy0 326 $IP address add 192.0.2.1/24 dev dummy0
327 $IP -6 address add 2001:db8:2::1/64 dev dummy0
328 set +e
306 329
307 ip netns exec testns ip route get fibmatch 192.0.2.1 &> /dev/null 330 echo " Route to local address with carrier down"
308 check_err $? 331 $IP route get fibmatch 192.0.2.1 &> /dev/null
309 ip netns exec testns ip -6 route get fibmatch 2001:db8:2::1 &> /dev/null 332 log_test $? 0 "IPv4 fibmatch"
310 check_err $? 333 $IP -6 route get fibmatch 2001:db8:2::1 &> /dev/null
334 log_test $? 0 "IPv6 fibmatch"
311 335
312 ip netns exec testns ip route get fibmatch 192.0.2.1 | \ 336 $IP route get fibmatch 192.0.2.1 | \
313 grep -q "linkdown" 337 grep -q "linkdown"
314 check_fail $? 338 log_test $? 1 "IPv4 linkdown flag set"
315 ip netns exec testns ip -6 route get fibmatch 2001:db8:2::1 | \ 339 $IP -6 route get fibmatch 2001:db8:2::1 | \
316 grep -q "linkdown" 340 grep -q "linkdown"
317 check_fail $? 341 log_test $? 1 "IPv6 linkdown flag set"
318 342
319 ip netns exec testns ip link del dev dummy0 343 cleanup
320
321 ip netns del testns
322
323 if [ $ret -ne 0 ]; then
324 echo "FAIL: local route carrier test"
325 return 1
326 fi
327 echo "PASS: local route carrier test"
328} 344}
329 345
330fib_carrier_unicast_test() 346fib_carrier_unicast_test()
331{ 347{
332 ret=0 348 ret=0
333 349
334 netns_create "testns" 350 echo
351 echo "Single path route carrier test"
335 352
336 ip netns exec testns ip link add dummy0 type dummy 353 setup
337 ip netns exec testns ip link set dev dummy0 up
338 354
339 ip netns exec testns ip link set dev dummy0 carrier on 355 set -e
356 $IP link set dev dummy0 carrier on
357 set +e
340 358
341 ip netns exec testns ip address add 198.51.100.1/24 dev dummy0 359 echo " Start point"
342 ip netns exec testns ip -6 address add 2001:db8:1::1/64 dev dummy0 360 $IP route get fibmatch 198.51.100.2 &> /dev/null
361 log_test $? 0 "IPv4 fibmatch"
362 $IP -6 route get fibmatch 2001:db8:1::2 &> /dev/null
363 log_test $? 0 "IPv6 fibmatch"
343 364
344 ip netns exec testns ip route get fibmatch 198.51.100.2 &> /dev/null 365 $IP route get fibmatch 198.51.100.2 | \
345 check_err $?
346 ip netns exec testns ip -6 route get fibmatch 2001:db8:1::2 &> /dev/null
347 check_err $?
348
349 ip netns exec testns ip route get fibmatch 198.51.100.2 | \
350 grep -q "linkdown" 366 grep -q "linkdown"
351 check_fail $? 367 log_test $? 1 "IPv4 no linkdown flag"
352 ip netns exec testns ip -6 route get fibmatch 2001:db8:1::2 | \ 368 $IP -6 route get fibmatch 2001:db8:1::2 | \
353 grep -q "linkdown" 369 grep -q "linkdown"
354 check_fail $? 370 log_test $? 1 "IPv6 no linkdown flag"
355 371
356 ip netns exec testns ip link set dev dummy0 carrier off 372 set -e
373 $IP link set dev dummy0 carrier off
374 set +e
357 375
358 ip netns exec testns ip route get fibmatch 198.51.100.2 &> /dev/null 376 echo " Carrier down"
359 check_err $? 377 $IP route get fibmatch 198.51.100.2 &> /dev/null
360 ip netns exec testns ip -6 route get fibmatch 2001:db8:1::2 &> /dev/null 378 log_test $? 0 "IPv4 fibmatch"
361 check_err $? 379 $IP -6 route get fibmatch 2001:db8:1::2 &> /dev/null
380 log_test $? 0 "IPv6 fibmatch"
362 381
363 ip netns exec testns ip route get fibmatch 198.51.100.2 | \ 382 $IP route get fibmatch 198.51.100.2 | \
364 grep -q "linkdown" 383 grep -q "linkdown"
365 check_err $? 384 log_test $? 0 "IPv4 linkdown flag set"
366 ip netns exec testns ip -6 route get fibmatch 2001:db8:1::2 | \ 385 $IP -6 route get fibmatch 2001:db8:1::2 | \
367 grep -q "linkdown" 386 grep -q "linkdown"
368 check_err $? 387 log_test $? 0 "IPv6 linkdown flag set"
369 388
370 ip netns exec testns ip address add 192.0.2.1/24 dev dummy0 389 set -e
371 ip netns exec testns ip -6 address add 2001:db8:2::1/64 dev dummy0 390 $IP address add 192.0.2.1/24 dev dummy0
391 $IP -6 address add 2001:db8:2::1/64 dev dummy0
392 set +e
372 393
373 ip netns exec testns ip route get fibmatch 192.0.2.2 &> /dev/null 394 echo " Second address added with carrier down"
374 check_err $? 395 $IP route get fibmatch 192.0.2.2 &> /dev/null
375 ip netns exec testns ip -6 route get fibmatch 2001:db8:2::2 &> /dev/null 396 log_test $? 0 "IPv4 fibmatch"
376 check_err $? 397 $IP -6 route get fibmatch 2001:db8:2::2 &> /dev/null
398 log_test $? 0 "IPv6 fibmatch"
377 399
378 ip netns exec testns ip route get fibmatch 192.0.2.2 | \ 400 $IP route get fibmatch 192.0.2.2 | \
379 grep -q "linkdown" 401 grep -q "linkdown"
380 check_err $? 402 log_test $? 0 "IPv4 linkdown flag set"
381 ip netns exec testns ip -6 route get fibmatch 2001:db8:2::2 | \ 403 $IP -6 route get fibmatch 2001:db8:2::2 | \
382 grep -q "linkdown" 404 grep -q "linkdown"
383 check_err $? 405 log_test $? 0 "IPv6 linkdown flag set"
384 406
385 ip netns exec testns ip link del dev dummy0 407 cleanup
408}
386 409
387 ip netns del testns 410fib_carrier_test()
411{
412 fib_carrier_local_test
413 fib_carrier_unicast_test
414}
388 415
389 if [ $ret -ne 0 ]; then 416################################################################################
390 echo "FAIL: unicast route carrier test" 417# Tests on nexthop spec
391 return 1 418
419# run 'ip route add' with given spec
420add_rt()
421{
422 local desc="$1"
423 local erc=$2
424 local vrf=$3
425 local pfx=$4
426 local gw=$5
427 local dev=$6
428 local cmd out rc
429
430 [ "$vrf" = "-" ] && vrf="default"
431 [ -n "$gw" ] && gw="via $gw"
432 [ -n "$dev" ] && dev="dev $dev"
433
434 cmd="$IP route add vrf $vrf $pfx $gw $dev"
435 if [ "$VERBOSE" = "1" ]; then
436 printf "\n COMMAND: $cmd\n"
437 fi
438
439 out=$(eval $cmd 2>&1)
440 rc=$?
441 if [ "$VERBOSE" = "1" -a -n "$out" ]; then
442 echo " $out"
392 fi 443 fi
393 echo "PASS: unicast route carrier test" 444 log_test $rc $erc "$desc"
394} 445}
395 446
396fib_carrier_test() 447fib4_nexthop()
397{ 448{
398 echo "Running netdev carrier change tests" 449 echo
450 echo "IPv4 nexthop tests"
399 451
400 fib_carrier_local_test 452 echo "<<< write me >>>"
401 fib_carrier_unicast_test
402} 453}
403 454
455fib6_nexthop()
456{
457 local lldummy=$(get_linklocal dummy0)
458 local llv1=$(get_linklocal dummy0)
459
460 if [ -z "$lldummy" ]; then
461 echo "Failed to get linklocal address for dummy0"
462 return 1
463 fi
464 if [ -z "$llv1" ]; then
465 echo "Failed to get linklocal address for veth1"
466 return 1
467 fi
468
469 echo
470 echo "IPv6 nexthop tests"
471
472 add_rt "Directly connected nexthop, unicast address" 0 \
473 - 2001:db8:101::/64 2001:db8:1::2
474 add_rt "Directly connected nexthop, unicast address with device" 0 \
475 - 2001:db8:102::/64 2001:db8:1::2 "dummy0"
476 add_rt "Gateway is linklocal address" 0 \
477 - 2001:db8:103::1/64 $llv1 "veth0"
478
479 # fails because LL address requires a device
480 add_rt "Gateway is linklocal address, no device" 2 \
481 - 2001:db8:104::1/64 $llv1
482
483 # local address can not be a gateway
484 add_rt "Gateway can not be local unicast address" 2 \
485 - 2001:db8:105::/64 2001:db8:1::1
486 add_rt "Gateway can not be local unicast address, with device" 2 \
487 - 2001:db8:106::/64 2001:db8:1::1 "dummy0"
488 add_rt "Gateway can not be a local linklocal address" 2 \
489 - 2001:db8:107::1/64 $lldummy "dummy0"
490
491 # VRF tests
492 add_rt "Gateway can be local address in a VRF" 0 \
493 - 2001:db8:108::/64 2001:db8:51::2
494 add_rt "Gateway can be local address in a VRF, with device" 0 \
495 - 2001:db8:109::/64 2001:db8:51::2 "veth0"
496 add_rt "Gateway can be local linklocal address in a VRF" 0 \
497 - 2001:db8:110::1/64 $llv1 "veth0"
498
499 add_rt "Redirect to VRF lookup" 0 \
500 - 2001:db8:111::/64 "" "red"
501
502 add_rt "VRF route, gateway can be local address in default VRF" 0 \
503 red 2001:db8:112::/64 2001:db8:51::1
504
505 # local address in same VRF fails
506 add_rt "VRF route, gateway can not be a local address" 2 \
507 red 2001:db8:113::1/64 2001:db8:2::1
508 add_rt "VRF route, gateway can not be a local addr with device" 2 \
509 red 2001:db8:114::1/64 2001:db8:2::1 "dummy1"
510}
511
512# Default VRF:
513# dummy0 - 198.51.100.1/24 2001:db8:1::1/64
514# veth0 - 192.0.2.1/24 2001:db8:51::1/64
515#
516# VRF red:
517# dummy1 - 192.168.2.1/24 2001:db8:2::1/64
518# veth1 - 192.0.2.2/24 2001:db8:51::2/64
519#
520# [ dummy0 veth0 ]--[ veth1 dummy1 ]
521
522fib_nexthop_test()
523{
524 setup
525
526 set -e
527
528 $IP -4 rule add pref 32765 table local
529 $IP -4 rule del pref 0
530 $IP -6 rule add pref 32765 table local
531 $IP -6 rule del pref 0
532
533 $IP link add red type vrf table 1
534 $IP link set red up
535 $IP -4 route add vrf red unreachable default metric 4278198272
536 $IP -6 route add vrf red unreachable default metric 4278198272
537
538 $IP link add veth0 type veth peer name veth1
539 $IP link set dev veth0 up
540 $IP address add 192.0.2.1/24 dev veth0
541 $IP -6 address add 2001:db8:51::1/64 dev veth0
542
543 $IP link set dev veth1 vrf red up
544 $IP address add 192.0.2.2/24 dev veth1
545 $IP -6 address add 2001:db8:51::2/64 dev veth1
546
547 $IP link add dummy1 type dummy
548 $IP link set dev dummy1 vrf red up
549 $IP address add 192.168.2.1/24 dev dummy1
550 $IP -6 address add 2001:db8:2::1/64 dev dummy1
551 set +e
552
553 sleep 1
554 fib4_nexthop
555 fib6_nexthop
556
557 (
558 $IP link del dev dummy1
559 $IP link del veth0
560 $IP link del red
561 ) 2>/dev/null
562 cleanup
563}
564
565################################################################################
566#
567
404fib_test() 568fib_test()
405{ 569{
406 fib_unreg_test 570 if [ -n "$TEST" ]; then
407 fib_down_test 571 eval $TEST
408 fib_carrier_test 572 else
573 fib_unreg_test
574 fib_down_test
575 fib_carrier_test
576 fib_nexthop_test
577 fi
409} 578}
410 579
411if [ "$(id -u)" -ne 0 ];then 580if [ "$(id -u)" -ne 0 ];then
@@ -424,6 +593,9 @@ if [ $? -ne 0 ]; then
424 exit 0 593 exit 0
425fi 594fi
426 595
596# start clean
597cleanup &> /dev/null
598
427fib_test 599fib_test
428 600
429exit $ret 601exit $ret
diff --git a/tools/testing/selftests/net/forwarding/.gitignore b/tools/testing/selftests/net/forwarding/.gitignore
new file mode 100644
index 000000000000..a793eef5b876
--- /dev/null
+++ b/tools/testing/selftests/net/forwarding/.gitignore
@@ -0,0 +1 @@
forwarding.config
diff --git a/tools/testing/selftests/net/forwarding/README b/tools/testing/selftests/net/forwarding/README
new file mode 100644
index 000000000000..4a0964c42860
--- /dev/null
+++ b/tools/testing/selftests/net/forwarding/README
@@ -0,0 +1,56 @@
1Motivation
2==========
3
4One of the nice things about network namespaces is that they allow one
5to easily create and test complex environments.
6
7Unfortunately, these namespaces can not be used with actual switching
8ASICs, as their ports can not be migrated to other network namespaces
9(NETIF_F_NETNS_LOCAL) and most of them probably do not support the
10L1-separation provided by namespaces.
11
12However, a similar kind of flexibility can be achieved by using VRFs and
13by looping the switch ports together. For example:
14
15 br0
16 +
17 vrf-h1 | vrf-h2
18 + +---+----+ +
19 | | | |
20 192.0.2.1/24 + + + + 192.0.2.2/24
21 swp1 swp2 swp3 swp4
22 + + + +
23 | | | |
24 +--------+ +--------+
25
26The VRFs act as lightweight namespaces representing hosts connected to
27the switch.
28
29This approach for testing switch ASICs has several advantages over the
30traditional method that requires multiple physical machines, to name a
31few:
32
331. Only the device under test (DUT) is being tested without noise from
34other system.
35
362. Ability to easily provision complex topologies. Testing bridging
37between 4-ports LAGs or 8-way ECMP requires many physical links that are
38not always available. With the VRF-based approach one merely needs to
39loopback more ports.
40
41These tests are written with switch ASICs in mind, but they can be run
42on any Linux box using veth pairs to emulate physical loopbacks.
43
44Guidelines for Writing Tests
45============================
46
47o Where possible, reuse an existing topology for different tests instead
48 of recreating the same topology.
49o Where possible, IPv6 and IPv4 addresses shall conform to RFC 3849 and
50 RFC 5737, respectively.
51o Where possible, tests shall be written so that they can be reused by
52 multiple topologies and added to lib.sh.
53o Checks shall be added to lib.sh for any external dependencies.
54o Code shall be checked using ShellCheck [1] prior to submission.
55
561. https://www.shellcheck.net/
diff --git a/tools/testing/selftests/net/forwarding/bridge_vlan_aware.sh b/tools/testing/selftests/net/forwarding/bridge_vlan_aware.sh
new file mode 100755
index 000000000000..75d922438bc9
--- /dev/null
+++ b/tools/testing/selftests/net/forwarding/bridge_vlan_aware.sh
@@ -0,0 +1,88 @@
1#!/bin/bash
2# SPDX-License-Identifier: GPL-2.0
3
4NUM_NETIFS=4
5CHECK_TC="yes"
6source lib.sh
7
8h1_create()
9{
10 simple_if_init $h1 192.0.2.1/24 2001:db8:1::1/64
11}
12
13h1_destroy()
14{
15 simple_if_fini $h1 192.0.2.1/24 2001:db8:1::1/64
16}
17
18h2_create()
19{
20 simple_if_init $h2 192.0.2.2/24 2001:db8:1::2/64
21}
22
23h2_destroy()
24{
25 simple_if_fini $h2 192.0.2.2/24 2001:db8:1::2/64
26}
27
28switch_create()
29{
30 # 10 Seconds ageing time.
31 ip link add dev br0 type bridge vlan_filtering 1 ageing_time 1000 \
32 mcast_snooping 0
33
34 ip link set dev $swp1 master br0
35 ip link set dev $swp2 master br0
36
37 ip link set dev br0 up
38 ip link set dev $swp1 up
39 ip link set dev $swp2 up
40}
41
42switch_destroy()
43{
44 ip link set dev $swp2 down
45 ip link set dev $swp1 down
46
47 ip link del dev br0
48}
49
50setup_prepare()
51{
52 h1=${NETIFS[p1]}
53 swp1=${NETIFS[p2]}
54
55 swp2=${NETIFS[p3]}
56 h2=${NETIFS[p4]}
57
58 vrf_prepare
59
60 h1_create
61 h2_create
62
63 switch_create
64}
65
66cleanup()
67{
68 pre_cleanup
69
70 switch_destroy
71
72 h2_destroy
73 h1_destroy
74
75 vrf_cleanup
76}
77
78trap cleanup EXIT
79
80setup_prepare
81setup_wait
82
83ping_test $h1 192.0.2.2
84ping6_test $h1 2001:db8:1::2
85learning_test "br0" $swp1 $h1 $h2
86flood_test $swp2 $h1 $h2
87
88exit $EXIT_STATUS
diff --git a/tools/testing/selftests/net/forwarding/bridge_vlan_unaware.sh b/tools/testing/selftests/net/forwarding/bridge_vlan_unaware.sh
new file mode 100755
index 000000000000..1cddf06f691d
--- /dev/null
+++ b/tools/testing/selftests/net/forwarding/bridge_vlan_unaware.sh
@@ -0,0 +1,86 @@
1#!/bin/bash
2# SPDX-License-Identifier: GPL-2.0
3
4NUM_NETIFS=4
5source lib.sh
6
7h1_create()
8{
9 simple_if_init $h1 192.0.2.1/24 2001:db8:1::1/64
10}
11
12h1_destroy()
13{
14 simple_if_fini $h1 192.0.2.1/24 2001:db8:1::1/64
15}
16
17h2_create()
18{
19 simple_if_init $h2 192.0.2.2/24 2001:db8:1::2/64
20}
21
22h2_destroy()
23{
24 simple_if_fini $h2 192.0.2.2/24 2001:db8:1::2/64
25}
26
27switch_create()
28{
29 # 10 Seconds ageing time.
30 ip link add dev br0 type bridge ageing_time 1000 mcast_snooping 0
31
32 ip link set dev $swp1 master br0
33 ip link set dev $swp2 master br0
34
35 ip link set dev br0 up
36 ip link set dev $swp1 up
37 ip link set dev $swp2 up
38}
39
40switch_destroy()
41{
42 ip link set dev $swp2 down
43 ip link set dev $swp1 down
44
45 ip link del dev br0
46}
47
48setup_prepare()
49{
50 h1=${NETIFS[p1]}
51 swp1=${NETIFS[p2]}
52
53 swp2=${NETIFS[p3]}
54 h2=${NETIFS[p4]}
55
56 vrf_prepare
57
58 h1_create
59 h2_create
60
61 switch_create
62}
63
64cleanup()
65{
66 pre_cleanup
67
68 switch_destroy
69
70 h2_destroy
71 h1_destroy
72
73 vrf_cleanup
74}
75
76trap cleanup EXIT
77
78setup_prepare
79setup_wait
80
81ping_test $h1 192.0.2.2
82ping6_test $h1 2001:db8:1::2
83learning_test "br0" $swp1 $h1 $h2
84flood_test $swp2 $h1 $h2
85
86exit $EXIT_STATUS
diff --git a/tools/testing/selftests/net/forwarding/config b/tools/testing/selftests/net/forwarding/config
new file mode 100644
index 000000000000..5cd2aed97958
--- /dev/null
+++ b/tools/testing/selftests/net/forwarding/config
@@ -0,0 +1,12 @@
1CONFIG_BRIDGE=m
2CONFIG_VLAN_8021Q=m
3CONFIG_BRIDGE_VLAN_FILTERING=y
4CONFIG_NET_L3_MASTER_DEV=y
5CONFIG_IPV6_MULTIPLE_TABLES=y
6CONFIG_NET_VRF=m
7CONFIG_BPF_SYSCALL=y
8CONFIG_CGROUP_BPF=y
9CONFIG_NET_CLS_FLOWER=m
10CONFIG_NET_SCH_INGRESS=m
11CONFIG_NET_ACT_GACT=m
12CONFIG_VETH=m
diff --git a/tools/testing/selftests/net/forwarding/forwarding.config.sample b/tools/testing/selftests/net/forwarding/forwarding.config.sample
new file mode 100644
index 000000000000..e819d049d9ce
--- /dev/null
+++ b/tools/testing/selftests/net/forwarding/forwarding.config.sample
@@ -0,0 +1,35 @@
1#!/bin/bash
2# SPDX-License-Identifier: GPL-2.0
3
4##############################################################################
5# Topology description. p1 looped back to p2, p3 to p4 and so on.
6declare -A NETIFS
7
8NETIFS[p1]=veth0
9NETIFS[p2]=veth1
10NETIFS[p3]=veth2
11NETIFS[p4]=veth3
12NETIFS[p5]=veth4
13NETIFS[p6]=veth5
14NETIFS[p7]=veth6
15NETIFS[p8]=veth7
16
17##############################################################################
18# Defines
19
20# IPv4 ping utility name
21PING=ping
22# IPv6 ping utility name. Some distributions use 'ping' for IPv6.
23PING6=ping6
24# Packet generator. Some distributions use 'mz'.
25MZ=mausezahn
26# Time to wait after interfaces participating in the test are all UP
27WAIT_TIME=5
28# Whether to pause on failure or not.
29PAUSE_ON_FAIL=no
30# Whether to pause on cleanup or not.
31PAUSE_ON_CLEANUP=no
32# Type of network interface to create
33NETIF_TYPE=veth
34# Whether to create virtual interfaces (veth) or not
35NETIF_CREATE=yes
diff --git a/tools/testing/selftests/net/forwarding/lib.sh b/tools/testing/selftests/net/forwarding/lib.sh
new file mode 100644
index 000000000000..1ac6c62271f3
--- /dev/null
+++ b/tools/testing/selftests/net/forwarding/lib.sh
@@ -0,0 +1,577 @@
1#!/bin/bash
2# SPDX-License-Identifier: GPL-2.0
3
4##############################################################################
5# Defines
6
7# Can be overridden by the configuration file.
8PING=${PING:=ping}
9PING6=${PING6:=ping6}
10MZ=${MZ:=mausezahn}
11WAIT_TIME=${WAIT_TIME:=5}
12PAUSE_ON_FAIL=${PAUSE_ON_FAIL:=no}
13PAUSE_ON_CLEANUP=${PAUSE_ON_CLEANUP:=no}
14NETIF_TYPE=${NETIF_TYPE:=veth}
15NETIF_CREATE=${NETIF_CREATE:=yes}
16
17if [[ -f forwarding.config ]]; then
18 source forwarding.config
19fi
20
21##############################################################################
22# Sanity checks
23
24check_tc_version()
25{
26 tc -j &> /dev/null
27 if [[ $? -ne 0 ]]; then
28 echo "SKIP: iproute2 too old; tc is missing JSON support"
29 exit 1
30 fi
31
32 tc filter help 2>&1 | grep block &> /dev/null
33 if [[ $? -ne 0 ]]; then
34 echo "SKIP: iproute2 too old; tc is missing shared block support"
35 exit 1
36 fi
37}
38
39if [[ "$(id -u)" -ne 0 ]]; then
40 echo "SKIP: need root privileges"
41 exit 0
42fi
43
44if [[ "$CHECK_TC" = "yes" ]]; then
45 check_tc_version
46fi
47
48if [[ ! -x "$(command -v jq)" ]]; then
49 echo "SKIP: jq not installed"
50 exit 1
51fi
52
53if [[ ! -x "$(command -v $MZ)" ]]; then
54 echo "SKIP: $MZ not installed"
55 exit 1
56fi
57
58if [[ ! -v NUM_NETIFS ]]; then
59 echo "SKIP: importer does not define \"NUM_NETIFS\""
60 exit 1
61fi
62
63##############################################################################
64# Command line options handling
65
66count=0
67
68while [[ $# -gt 0 ]]; do
69 if [[ "$count" -eq "0" ]]; then
70 unset NETIFS
71 declare -A NETIFS
72 fi
73 count=$((count + 1))
74 NETIFS[p$count]="$1"
75 shift
76done
77
78##############################################################################
79# Network interfaces configuration
80
81create_netif_veth()
82{
83 local i
84
85 for i in $(eval echo {1..$NUM_NETIFS}); do
86 local j=$((i+1))
87
88 ip link show dev ${NETIFS[p$i]} &> /dev/null
89 if [[ $? -ne 0 ]]; then
90 ip link add ${NETIFS[p$i]} type veth \
91 peer name ${NETIFS[p$j]}
92 if [[ $? -ne 0 ]]; then
93 echo "Failed to create netif"
94 exit 1
95 fi
96 fi
97 i=$j
98 done
99}
100
101create_netif()
102{
103 case "$NETIF_TYPE" in
104 veth) create_netif_veth
105 ;;
106 *) echo "Can not create interfaces of type \'$NETIF_TYPE\'"
107 exit 1
108 ;;
109 esac
110}
111
112if [[ "$NETIF_CREATE" = "yes" ]]; then
113 create_netif
114fi
115
116for i in $(eval echo {1..$NUM_NETIFS}); do
117 ip link show dev ${NETIFS[p$i]} &> /dev/null
118 if [[ $? -ne 0 ]]; then
119 echo "SKIP: could not find all required interfaces"
120 exit 1
121 fi
122done
123
124##############################################################################
125# Helpers
126
127# Exit status to return at the end. Set in case one of the tests fails.
128EXIT_STATUS=0
129# Per-test return value. Clear at the beginning of each test.
130RET=0
131
132check_err()
133{
134 local err=$1
135 local msg=$2
136
137 if [[ $RET -eq 0 && $err -ne 0 ]]; then
138 RET=$err
139 retmsg=$msg
140 fi
141}
142
143check_fail()
144{
145 local err=$1
146 local msg=$2
147
148 if [[ $RET -eq 0 && $err -eq 0 ]]; then
149 RET=1
150 retmsg=$msg
151 fi
152}
153
154log_test()
155{
156 local test_name=$1
157 local opt_str=$2
158
159 if [[ $# -eq 2 ]]; then
160 opt_str="($opt_str)"
161 fi
162
163 if [[ $RET -ne 0 ]]; then
164 EXIT_STATUS=1
165 printf "TEST: %-60s [FAIL]\n" "$test_name $opt_str"
166 if [[ ! -z "$retmsg" ]]; then
167 printf "\t%s\n" "$retmsg"
168 fi
169 if [ "${PAUSE_ON_FAIL}" = "yes" ]; then
170 echo "Hit enter to continue, 'q' to quit"
171 read a
172 [ "$a" = "q" ] && exit 1
173 fi
174 return 1
175 fi
176
177 printf "TEST: %-60s [PASS]\n" "$test_name $opt_str"
178 return 0
179}
180
181log_info()
182{
183 local msg=$1
184
185 echo "INFO: $msg"
186}
187
188setup_wait()
189{
190 for i in $(eval echo {1..$NUM_NETIFS}); do
191 while true; do
192 ip link show dev ${NETIFS[p$i]} up \
193 | grep 'state UP' &> /dev/null
194 if [[ $? -ne 0 ]]; then
195 sleep 1
196 else
197 break
198 fi
199 done
200 done
201
202 # Make sure links are ready.
203 sleep $WAIT_TIME
204}
205
206pre_cleanup()
207{
208 if [ "${PAUSE_ON_CLEANUP}" = "yes" ]; then
209 echo "Pausing before cleanup, hit any key to continue"
210 read
211 fi
212}
213
214vrf_prepare()
215{
216 ip -4 rule add pref 32765 table local
217 ip -4 rule del pref 0
218 ip -6 rule add pref 32765 table local
219 ip -6 rule del pref 0
220}
221
222vrf_cleanup()
223{
224 ip -6 rule add pref 0 table local
225 ip -6 rule del pref 32765
226 ip -4 rule add pref 0 table local
227 ip -4 rule del pref 32765
228}
229
230__last_tb_id=0
231declare -A __TB_IDS
232
233__vrf_td_id_assign()
234{
235 local vrf_name=$1
236
237 __last_tb_id=$((__last_tb_id + 1))
238 __TB_IDS[$vrf_name]=$__last_tb_id
239 return $__last_tb_id
240}
241
242__vrf_td_id_lookup()
243{
244 local vrf_name=$1
245
246 return ${__TB_IDS[$vrf_name]}
247}
248
249vrf_create()
250{
251 local vrf_name=$1
252 local tb_id
253
254 __vrf_td_id_assign $vrf_name
255 tb_id=$?
256
257 ip link add dev $vrf_name type vrf table $tb_id
258 ip -4 route add table $tb_id unreachable default metric 4278198272
259 ip -6 route add table $tb_id unreachable default metric 4278198272
260}
261
262vrf_destroy()
263{
264 local vrf_name=$1
265 local tb_id
266
267 __vrf_td_id_lookup $vrf_name
268 tb_id=$?
269
270 ip -6 route del table $tb_id unreachable default metric 4278198272
271 ip -4 route del table $tb_id unreachable default metric 4278198272
272 ip link del dev $vrf_name
273}
274
275__addr_add_del()
276{
277 local if_name=$1
278 local add_del=$2
279 local array
280
281 shift
282 shift
283 array=("${@}")
284
285 for addrstr in "${array[@]}"; do
286 ip address $add_del $addrstr dev $if_name
287 done
288}
289
290simple_if_init()
291{
292 local if_name=$1
293 local vrf_name
294 local array
295
296 shift
297 vrf_name=v$if_name
298 array=("${@}")
299
300 vrf_create $vrf_name
301 ip link set dev $if_name master $vrf_name
302 ip link set dev $vrf_name up
303 ip link set dev $if_name up
304
305 __addr_add_del $if_name add "${array[@]}"
306}
307
308simple_if_fini()
309{
310 local if_name=$1
311 local vrf_name
312 local array
313
314 shift
315 vrf_name=v$if_name
316 array=("${@}")
317
318 __addr_add_del $if_name del "${array[@]}"
319
320 ip link set dev $if_name down
321 vrf_destroy $vrf_name
322}
323
324master_name_get()
325{
326 local if_name=$1
327
328 ip -j link show dev $if_name | jq -r '.[]["master"]'
329}
330
331link_stats_tx_packets_get()
332{
333 local if_name=$1
334
335 ip -j -s link show dev $if_name | jq '.[]["stats64"]["tx"]["packets"]'
336}
337
338mac_get()
339{
340 local if_name=$1
341
342 ip -j link show dev $if_name | jq -r '.[]["address"]'
343}
344
345bridge_ageing_time_get()
346{
347 local bridge=$1
348 local ageing_time
349
350 # Need to divide by 100 to convert to seconds.
351 ageing_time=$(ip -j -d link show dev $bridge \
352 | jq '.[]["linkinfo"]["info_data"]["ageing_time"]')
353 echo $((ageing_time / 100))
354}
355
356forwarding_enable()
357{
358 ipv4_fwd=$(sysctl -n net.ipv4.conf.all.forwarding)
359 ipv6_fwd=$(sysctl -n net.ipv6.conf.all.forwarding)
360
361 sysctl -q -w net.ipv4.conf.all.forwarding=1
362 sysctl -q -w net.ipv6.conf.all.forwarding=1
363}
364
365forwarding_restore()
366{
367 sysctl -q -w net.ipv6.conf.all.forwarding=$ipv6_fwd
368 sysctl -q -w net.ipv4.conf.all.forwarding=$ipv4_fwd
369}
370
371tc_offload_check()
372{
373 for i in $(eval echo {1..$NUM_NETIFS}); do
374 ethtool -k ${NETIFS[p$i]} \
375 | grep "hw-tc-offload: on" &> /dev/null
376 if [[ $? -ne 0 ]]; then
377 return 1
378 fi
379 done
380
381 return 0
382}
383
384##############################################################################
385# Tests
386
387ping_test()
388{
389 local if_name=$1
390 local dip=$2
391 local vrf_name
392
393 RET=0
394
395 vrf_name=$(master_name_get $if_name)
396 ip vrf exec $vrf_name $PING $dip -c 10 -i 0.1 -w 2 &> /dev/null
397 check_err $?
398 log_test "ping"
399}
400
401ping6_test()
402{
403 local if_name=$1
404 local dip=$2
405 local vrf_name
406
407 RET=0
408
409 vrf_name=$(master_name_get $if_name)
410 ip vrf exec $vrf_name $PING6 $dip -c 10 -i 0.1 -w 2 &> /dev/null
411 check_err $?
412 log_test "ping6"
413}
414
415learning_test()
416{
417 local bridge=$1
418 local br_port1=$2 # Connected to `host1_if`.
419 local host1_if=$3
420 local host2_if=$4
421 local mac=de:ad:be:ef:13:37
422 local ageing_time
423
424 RET=0
425
426 bridge -j fdb show br $bridge brport $br_port1 \
427 | jq -e ".[] | select(.mac == \"$mac\")" &> /dev/null
428 check_fail $? "Found FDB record when should not"
429
430 # Disable unknown unicast flooding on `br_port1` to make sure
431 # packets are only forwarded through the port after a matching
432 # FDB entry was installed.
433 bridge link set dev $br_port1 flood off
434
435 tc qdisc add dev $host1_if ingress
436 tc filter add dev $host1_if ingress protocol ip pref 1 handle 101 \
437 flower dst_mac $mac action drop
438
439 $MZ $host2_if -c 1 -p 64 -b $mac -t ip -q
440 sleep 1
441
442 tc -j -s filter show dev $host1_if ingress \
443 | jq -e ".[] | select(.options.handle == 101) \
444 | select(.options.actions[0].stats.packets == 1)" &> /dev/null
445 check_fail $? "Packet reached second host when should not"
446
447 $MZ $host1_if -c 1 -p 64 -a $mac -t ip -q
448 sleep 1
449
450 bridge -j fdb show br $bridge brport $br_port1 \
451 | jq -e ".[] | select(.mac == \"$mac\")" &> /dev/null
452 check_err $? "Did not find FDB record when should"
453
454 $MZ $host2_if -c 1 -p 64 -b $mac -t ip -q
455 sleep 1
456
457 tc -j -s filter show dev $host1_if ingress \
458 | jq -e ".[] | select(.options.handle == 101) \
459 | select(.options.actions[0].stats.packets == 1)" &> /dev/null
460 check_err $? "Packet did not reach second host when should"
461
462 # Wait for 10 seconds after the ageing time to make sure FDB
463 # record was aged-out.
464 ageing_time=$(bridge_ageing_time_get $bridge)
465 sleep $((ageing_time + 10))
466
467 bridge -j fdb show br $bridge brport $br_port1 \
468 | jq -e ".[] | select(.mac == \"$mac\")" &> /dev/null
469 check_fail $? "Found FDB record when should not"
470
471 bridge link set dev $br_port1 learning off
472
473 $MZ $host1_if -c 1 -p 64 -a $mac -t ip -q
474 sleep 1
475
476 bridge -j fdb show br $bridge brport $br_port1 \
477 | jq -e ".[] | select(.mac == \"$mac\")" &> /dev/null
478 check_fail $? "Found FDB record when should not"
479
480 bridge link set dev $br_port1 learning on
481
482 tc filter del dev $host1_if ingress protocol ip pref 1 handle 101 flower
483 tc qdisc del dev $host1_if ingress
484
485 bridge link set dev $br_port1 flood on
486
487 log_test "FDB learning"
488}
489
490flood_test_do()
491{
492 local should_flood=$1
493 local mac=$2
494 local ip=$3
495 local host1_if=$4
496 local host2_if=$5
497 local err=0
498
499 # Add an ACL on `host2_if` which will tell us whether the packet
500 # was flooded to it or not.
501 tc qdisc add dev $host2_if ingress
502 tc filter add dev $host2_if ingress protocol ip pref 1 handle 101 \
503 flower dst_mac $mac action drop
504
505 $MZ $host1_if -c 1 -p 64 -b $mac -B $ip -t ip -q
506 sleep 1
507
508 tc -j -s filter show dev $host2_if ingress \
509 | jq -e ".[] | select(.options.handle == 101) \
510 | select(.options.actions[0].stats.packets == 1)" &> /dev/null
511 if [[ $? -ne 0 && $should_flood == "true" || \
512 $? -eq 0 && $should_flood == "false" ]]; then
513 err=1
514 fi
515
516 tc filter del dev $host2_if ingress protocol ip pref 1 handle 101 flower
517 tc qdisc del dev $host2_if ingress
518
519 return $err
520}
521
522flood_unicast_test()
523{
524 local br_port=$1
525 local host1_if=$2
526 local host2_if=$3
527 local mac=de:ad:be:ef:13:37
528 local ip=192.0.2.100
529
530 RET=0
531
532 bridge link set dev $br_port flood off
533
534 flood_test_do false $mac $ip $host1_if $host2_if
535 check_err $? "Packet flooded when should not"
536
537 bridge link set dev $br_port flood on
538
539 flood_test_do true $mac $ip $host1_if $host2_if
540 check_err $? "Packet was not flooded when should"
541
542 log_test "Unknown unicast flood"
543}
544
545flood_multicast_test()
546{
547 local br_port=$1
548 local host1_if=$2
549 local host2_if=$3
550 local mac=01:00:5e:00:00:01
551 local ip=239.0.0.1
552
553 RET=0
554
555 bridge link set dev $br_port mcast_flood off
556
557 flood_test_do false $mac $ip $host1_if $host2_if
558 check_err $? "Packet flooded when should not"
559
560 bridge link set dev $br_port mcast_flood on
561
562 flood_test_do true $mac $ip $host1_if $host2_if
563 check_err $? "Packet was not flooded when should"
564
565 log_test "Unregistered multicast flood"
566}
567
568flood_test()
569{
570 # `br_port` is connected to `host2_if`
571 local br_port=$1
572 local host1_if=$2
573 local host2_if=$3
574
575 flood_unicast_test $br_port $host1_if $host2_if
576 flood_multicast_test $br_port $host1_if $host2_if
577}
diff --git a/tools/testing/selftests/net/forwarding/router.sh b/tools/testing/selftests/net/forwarding/router.sh
new file mode 100755
index 000000000000..cc6a14abfa87
--- /dev/null
+++ b/tools/testing/selftests/net/forwarding/router.sh
@@ -0,0 +1,125 @@
1#!/bin/bash
2# SPDX-License-Identifier: GPL-2.0
3
4NUM_NETIFS=4
5source lib.sh
6
7h1_create()
8{
9 vrf_create "vrf-h1"
10 ip link set dev $h1 master vrf-h1
11
12 ip link set dev vrf-h1 up
13 ip link set dev $h1 up
14
15 ip address add 192.0.2.2/24 dev $h1
16 ip address add 2001:db8:1::2/64 dev $h1
17
18 ip route add 198.51.100.0/24 vrf vrf-h1 nexthop via 192.0.2.1
19 ip route add 2001:db8:2::/64 vrf vrf-h1 nexthop via 2001:db8:1::1
20}
21
22h1_destroy()
23{
24 ip route del 2001:db8:2::/64 vrf vrf-h1
25 ip route del 198.51.100.0/24 vrf vrf-h1
26
27 ip address del 2001:db8:1::2/64 dev $h1
28 ip address del 192.0.2.2/24 dev $h1
29
30 ip link set dev $h1 down
31 vrf_destroy "vrf-h1"
32}
33
34h2_create()
35{
36 vrf_create "vrf-h2"
37 ip link set dev $h2 master vrf-h2
38
39 ip link set dev vrf-h2 up
40 ip link set dev $h2 up
41
42 ip address add 198.51.100.2/24 dev $h2
43 ip address add 2001:db8:2::2/64 dev $h2
44
45 ip route add 192.0.2.0/24 vrf vrf-h2 nexthop via 198.51.100.1
46 ip route add 2001:db8:1::/64 vrf vrf-h2 nexthop via 2001:db8:2::1
47}
48
49h2_destroy()
50{
51 ip route del 2001:db8:1::/64 vrf vrf-h2
52 ip route del 192.0.2.0/24 vrf vrf-h2
53
54 ip address del 2001:db8:2::2/64 dev $h2
55 ip address del 198.51.100.2/24 dev $h2
56
57 ip link set dev $h2 down
58 vrf_destroy "vrf-h2"
59}
60
61router_create()
62{
63 ip link set dev $rp1 up
64 ip link set dev $rp2 up
65
66 ip address add 192.0.2.1/24 dev $rp1
67 ip address add 2001:db8:1::1/64 dev $rp1
68
69 ip address add 198.51.100.1/24 dev $rp2
70 ip address add 2001:db8:2::1/64 dev $rp2
71}
72
73router_destroy()
74{
75 ip address del 2001:db8:2::1/64 dev $rp2
76 ip address del 198.51.100.1/24 dev $rp2
77
78 ip address del 2001:db8:1::1/64 dev $rp1
79 ip address del 192.0.2.1/24 dev $rp1
80
81 ip link set dev $rp2 down
82 ip link set dev $rp1 down
83}
84
85setup_prepare()
86{
87 h1=${NETIFS[p1]}
88 rp1=${NETIFS[p2]}
89
90 rp2=${NETIFS[p3]}
91 h2=${NETIFS[p4]}
92
93 vrf_prepare
94
95 h1_create
96 h2_create
97
98 router_create
99
100 forwarding_enable
101}
102
103cleanup()
104{
105 pre_cleanup
106
107 forwarding_restore
108
109 router_destroy
110
111 h2_destroy
112 h1_destroy
113
114 vrf_cleanup
115}
116
117trap cleanup EXIT
118
119setup_prepare
120setup_wait
121
122ping_test $h1 198.51.100.2
123ping6_test $h1 2001:db8:2::2
124
125exit $EXIT_STATUS
diff --git a/tools/testing/selftests/net/forwarding/router_multipath.sh b/tools/testing/selftests/net/forwarding/router_multipath.sh
new file mode 100755
index 000000000000..3bc351008db6
--- /dev/null
+++ b/tools/testing/selftests/net/forwarding/router_multipath.sh
@@ -0,0 +1,376 @@
1#!/bin/bash
2# SPDX-License-Identifier: GPL-2.0
3
4NUM_NETIFS=8
5source lib.sh
6
7h1_create()
8{
9 vrf_create "vrf-h1"
10 ip link set dev $h1 master vrf-h1
11
12 ip link set dev vrf-h1 up
13 ip link set dev $h1 up
14
15 ip address add 192.0.2.2/24 dev $h1
16 ip address add 2001:db8:1::2/64 dev $h1
17
18 ip route add 198.51.100.0/24 vrf vrf-h1 nexthop via 192.0.2.1
19 ip route add 2001:db8:2::/64 vrf vrf-h1 nexthop via 2001:db8:1::1
20}
21
22h1_destroy()
23{
24 ip route del 2001:db8:2::/64 vrf vrf-h1
25 ip route del 198.51.100.0/24 vrf vrf-h1
26
27 ip address del 2001:db8:1::2/64 dev $h1
28 ip address del 192.0.2.2/24 dev $h1
29
30 ip link set dev $h1 down
31 vrf_destroy "vrf-h1"
32}
33
34h2_create()
35{
36 vrf_create "vrf-h2"
37 ip link set dev $h2 master vrf-h2
38
39 ip link set dev vrf-h2 up
40 ip link set dev $h2 up
41
42 ip address add 198.51.100.2/24 dev $h2
43 ip address add 2001:db8:2::2/64 dev $h2
44
45 ip route add 192.0.2.0/24 vrf vrf-h2 nexthop via 198.51.100.1
46 ip route add 2001:db8:1::/64 vrf vrf-h2 nexthop via 2001:db8:2::1
47}
48
49h2_destroy()
50{
51 ip route del 2001:db8:1::/64 vrf vrf-h2
52 ip route del 192.0.2.0/24 vrf vrf-h2
53
54 ip address del 2001:db8:2::2/64 dev $h2
55 ip address del 198.51.100.2/24 dev $h2
56
57 ip link set dev $h2 down
58 vrf_destroy "vrf-h2"
59}
60
61router1_create()
62{
63 vrf_create "vrf-r1"
64 ip link set dev $rp11 master vrf-r1
65 ip link set dev $rp12 master vrf-r1
66 ip link set dev $rp13 master vrf-r1
67
68 ip link set dev vrf-r1 up
69 ip link set dev $rp11 up
70 ip link set dev $rp12 up
71 ip link set dev $rp13 up
72
73 ip address add 192.0.2.1/24 dev $rp11
74 ip address add 2001:db8:1::1/64 dev $rp11
75
76 ip address add 169.254.2.12/24 dev $rp12
77 ip address add fe80:2::12/64 dev $rp12
78
79 ip address add 169.254.3.13/24 dev $rp13
80 ip address add fe80:3::13/64 dev $rp13
81
82 ip route add 198.51.100.0/24 vrf vrf-r1 \
83 nexthop via 169.254.2.22 dev $rp12 \
84 nexthop via 169.254.3.23 dev $rp13
85 ip route add 2001:db8:2::/64 vrf vrf-r1 \
86 nexthop via fe80:2::22 dev $rp12 \
87 nexthop via fe80:3::23 dev $rp13
88}
89
90router1_destroy()
91{
92 ip route del 2001:db8:2::/64 vrf vrf-r1
93 ip route del 198.51.100.0/24 vrf vrf-r1
94
95 ip address del fe80:3::13/64 dev $rp13
96 ip address del 169.254.3.13/24 dev $rp13
97
98 ip address del fe80:2::12/64 dev $rp12
99 ip address del 169.254.2.12/24 dev $rp12
100
101 ip address del 2001:db8:1::1/64 dev $rp11
102 ip address del 192.0.2.1/24 dev $rp11
103
104 ip link set dev $rp13 down
105 ip link set dev $rp12 down
106 ip link set dev $rp11 down
107
108 vrf_destroy "vrf-r1"
109}
110
111router2_create()
112{
113 vrf_create "vrf-r2"
114 ip link set dev $rp21 master vrf-r2
115 ip link set dev $rp22 master vrf-r2
116 ip link set dev $rp23 master vrf-r2
117
118 ip link set dev vrf-r2 up
119 ip link set dev $rp21 up
120 ip link set dev $rp22 up
121 ip link set dev $rp23 up
122
123 ip address add 198.51.100.1/24 dev $rp21
124 ip address add 2001:db8:2::1/64 dev $rp21
125
126 ip address add 169.254.2.22/24 dev $rp22
127 ip address add fe80:2::22/64 dev $rp22
128
129 ip address add 169.254.3.23/24 dev $rp23
130 ip address add fe80:3::23/64 dev $rp23
131
132 ip route add 192.0.2.0/24 vrf vrf-r2 \
133 nexthop via 169.254.2.12 dev $rp22 \
134 nexthop via 169.254.3.13 dev $rp23
135 ip route add 2001:db8:1::/64 vrf vrf-r2 \
136 nexthop via fe80:2::12 dev $rp22 \
137 nexthop via fe80:3::13 dev $rp23
138}
139
140router2_destroy()
141{
142 ip route del 2001:db8:1::/64 vrf vrf-r2
143 ip route del 192.0.2.0/24 vrf vrf-r2
144
145 ip address del fe80:3::23/64 dev $rp23
146 ip address del 169.254.3.23/24 dev $rp23
147
148 ip address del fe80:2::22/64 dev $rp22
149 ip address del 169.254.2.22/24 dev $rp22
150
151 ip address del 2001:db8:2::1/64 dev $rp21
152 ip address del 198.51.100.1/24 dev $rp21
153
154 ip link set dev $rp23 down
155 ip link set dev $rp22 down
156 ip link set dev $rp21 down
157
158 vrf_destroy "vrf-r2"
159}
160
161multipath_eval()
162{
163 local desc="$1"
164 local weight_rp12=$2
165 local weight_rp13=$3
166 local packets_rp12=$4
167 local packets_rp13=$5
168 local weights_ratio packets_ratio diff
169
170 RET=0
171
172 if [[ "$packets_rp12" -eq "0" || "$packets_rp13" -eq "0" ]]; then
173 check_err 1 "Packet difference is 0"
174 log_test "Multipath"
175 log_info "Expected ratio $weights_ratio"
176 return
177 fi
178
179 if [[ "$weight_rp12" -gt "$weight_rp13" ]]; then
180 weights_ratio=$(echo "scale=2; $weight_rp12 / $weight_rp13" \
181 | bc -l)
182 packets_ratio=$(echo "scale=2; $packets_rp12 / $packets_rp13" \
183 | bc -l)
184 else
185 weights_ratio=$(echo "scale=2; $weight_rp13 / $weight_rp12" | \
186 bc -l)
187 packets_ratio=$(echo "scale=2; $packets_rp13 / $packets_rp12" | \
188 bc -l)
189 fi
190
191 diff=$(echo $weights_ratio - $packets_ratio | bc -l)
192 diff=${diff#-}
193
194 test "$(echo "$diff / $weights_ratio > 0.1" | bc -l)" -eq 0
195 check_err $? "Too large discrepancy between expected and measured ratios"
196 log_test "$desc"
197 log_info "Expected ratio $weights_ratio Measured ratio $packets_ratio"
198}
199
200multipath4_test()
201{
202 local desc="$1"
203 local weight_rp12=$2
204 local weight_rp13=$3
205 local t0_rp12 t0_rp13 t1_rp12 t1_rp13
206 local packets_rp12 packets_rp13
207 local hash_policy
208
209 # Transmit multiple flows from h1 to h2 and make sure they are
210 # distributed between both multipath links (rp12 and rp13)
211 # according to the configured weights.
212 hash_policy=$(sysctl -n net.ipv4.fib_multipath_hash_policy)
213 sysctl -q -w net.ipv4.fib_multipath_hash_policy=1
214 ip route replace 198.51.100.0/24 vrf vrf-r1 \
215 nexthop via 169.254.2.22 dev $rp12 weight $weight_rp12 \
216 nexthop via 169.254.3.23 dev $rp13 weight $weight_rp13
217
218 t0_rp12=$(link_stats_tx_packets_get $rp12)
219 t0_rp13=$(link_stats_tx_packets_get $rp13)
220
221 ip vrf exec vrf-h1 $MZ -q -p 64 -A 192.0.2.2 -B 198.51.100.2 \
222 -d 1msec -t udp "sp=1024,dp=0-32768"
223
224 t1_rp12=$(link_stats_tx_packets_get $rp12)
225 t1_rp13=$(link_stats_tx_packets_get $rp13)
226
227 let "packets_rp12 = $t1_rp12 - $t0_rp12"
228 let "packets_rp13 = $t1_rp13 - $t0_rp13"
229 multipath_eval "$desc" $weight_rp12 $weight_rp13 $packets_rp12 $packets_rp13
230
231 # Restore settings.
232 ip route replace 198.51.100.0/24 vrf vrf-r1 \
233 nexthop via 169.254.2.22 dev $rp12 \
234 nexthop via 169.254.3.23 dev $rp13
235 sysctl -q -w net.ipv4.fib_multipath_hash_policy=$hash_policy
236}
237
238multipath6_l4_test()
239{
240 local desc="$1"
241 local weight_rp12=$2
242 local weight_rp13=$3
243 local t0_rp12 t0_rp13 t1_rp12 t1_rp13
244 local packets_rp12 packets_rp13
245 local hash_policy
246
247 # Transmit multiple flows from h1 to h2 and make sure they are
248 # distributed between both multipath links (rp12 and rp13)
249 # according to the configured weights.
250 hash_policy=$(sysctl -n net.ipv6.fib_multipath_hash_policy)
251 sysctl -q -w net.ipv6.fib_multipath_hash_policy=1
252
253 ip route replace 2001:db8:2::/64 vrf vrf-r1 \
254 nexthop via fe80:2::22 dev $rp12 weight $weight_rp12 \
255 nexthop via fe80:3::23 dev $rp13 weight $weight_rp13
256
257 t0_rp12=$(link_stats_tx_packets_get $rp12)
258 t0_rp13=$(link_stats_tx_packets_get $rp13)
259
260 $MZ $h1 -6 -q -p 64 -A 2001:db8:1::2 -B 2001:db8:2::2 \
261 -d 1msec -t udp "sp=1024,dp=0-32768"
262
263 t1_rp12=$(link_stats_tx_packets_get $rp12)
264 t1_rp13=$(link_stats_tx_packets_get $rp13)
265
266 let "packets_rp12 = $t1_rp12 - $t0_rp12"
267 let "packets_rp13 = $t1_rp13 - $t0_rp13"
268 multipath_eval "$desc" $weight_rp12 $weight_rp13 $packets_rp12 $packets_rp13
269
270 ip route replace 2001:db8:2::/64 vrf vrf-r1 \
271 nexthop via fe80:2::22 dev $rp12 \
272 nexthop via fe80:3::23 dev $rp13
273
274 sysctl -q -w net.ipv6.fib_multipath_hash_policy=$hash_policy
275}
276
277multipath6_test()
278{
279 local desc="$1"
280 local weight_rp12=$2
281 local weight_rp13=$3
282 local t0_rp12 t0_rp13 t1_rp12 t1_rp13
283 local packets_rp12 packets_rp13
284
285 ip route replace 2001:db8:2::/64 vrf vrf-r1 \
286 nexthop via fe80:2::22 dev $rp12 weight $weight_rp12 \
287 nexthop via fe80:3::23 dev $rp13 weight $weight_rp13
288
289 t0_rp12=$(link_stats_tx_packets_get $rp12)
290 t0_rp13=$(link_stats_tx_packets_get $rp13)
291
292 # Generate 16384 echo requests, each with a random flow label.
293 for _ in $(seq 1 16384); do
294 ip vrf exec vrf-h1 $PING6 2001:db8:2::2 -F 0 -c 1 -q &> /dev/null
295 done
296
297 t1_rp12=$(link_stats_tx_packets_get $rp12)
298 t1_rp13=$(link_stats_tx_packets_get $rp13)
299
300 let "packets_rp12 = $t1_rp12 - $t0_rp12"
301 let "packets_rp13 = $t1_rp13 - $t0_rp13"
302 multipath_eval "$desc" $weight_rp12 $weight_rp13 $packets_rp12 $packets_rp13
303
304 ip route replace 2001:db8:2::/64 vrf vrf-r1 \
305 nexthop via fe80:2::22 dev $rp12 \
306 nexthop via fe80:3::23 dev $rp13
307}
308
309multipath_test()
310{
311 log_info "Running IPv4 multipath tests"
312 multipath4_test "ECMP" 1 1
313 multipath4_test "Weighted MP 2:1" 2 1
314 multipath4_test "Weighted MP 11:45" 11 45
315
316 log_info "Running IPv6 multipath tests"
317 multipath6_test "ECMP" 1 1
318 multipath6_test "Weighted MP 2:1" 2 1
319 multipath6_test "Weighted MP 11:45" 11 45
320
321 log_info "Running IPv6 L4 hash multipath tests"
322 multipath6_l4_test "ECMP" 1 1
323 multipath6_l4_test "Weighted MP 2:1" 2 1
324 multipath6_l4_test "Weighted MP 11:45" 11 45
325}
326
327setup_prepare()
328{
329 h1=${NETIFS[p1]}
330 rp11=${NETIFS[p2]}
331
332 rp12=${NETIFS[p3]}
333 rp22=${NETIFS[p4]}
334
335 rp13=${NETIFS[p5]}
336 rp23=${NETIFS[p6]}
337
338 rp21=${NETIFS[p7]}
339 h2=${NETIFS[p8]}
340
341 vrf_prepare
342
343 h1_create
344 h2_create
345
346 router1_create
347 router2_create
348
349 forwarding_enable
350}
351
352cleanup()
353{
354 pre_cleanup
355
356 forwarding_restore
357
358 router2_destroy
359 router1_destroy
360
361 h2_destroy
362 h1_destroy
363
364 vrf_cleanup
365}
366
367trap cleanup EXIT
368
369setup_prepare
370setup_wait
371
372ping_test $h1 198.51.100.2
373ping6_test $h1 2001:db8:2::2
374multipath_test
375
376exit $EXIT_STATUS
diff --git a/tools/testing/selftests/net/forwarding/tc_actions.sh b/tools/testing/selftests/net/forwarding/tc_actions.sh
new file mode 100755
index 000000000000..3a6385ebd5d0
--- /dev/null
+++ b/tools/testing/selftests/net/forwarding/tc_actions.sh
@@ -0,0 +1,202 @@
1#!/bin/bash
2# SPDX-License-Identifier: GPL-2.0
3
4NUM_NETIFS=4
5source tc_common.sh
6source lib.sh
7
8tcflags="skip_hw"
9
10h1_create()
11{
12 simple_if_init $h1 192.0.2.1/24
13}
14
15h1_destroy()
16{
17 simple_if_fini $h1 192.0.2.1/24
18}
19
20h2_create()
21{
22 simple_if_init $h2 192.0.2.2/24
23 tc qdisc add dev $h2 clsact
24}
25
26h2_destroy()
27{
28 tc qdisc del dev $h2 clsact
29 simple_if_fini $h2 192.0.2.2/24
30}
31
32switch_create()
33{
34 simple_if_init $swp1 192.0.2.2/24
35 tc qdisc add dev $swp1 clsact
36
37 simple_if_init $swp2 192.0.2.1/24
38}
39
40switch_destroy()
41{
42 simple_if_fini $swp2 192.0.2.1/24
43
44 tc qdisc del dev $swp1 clsact
45 simple_if_fini $swp1 192.0.2.2/24
46}
47
48mirred_egress_test()
49{
50 local action=$1
51
52 RET=0
53
54 tc filter add dev $h2 ingress protocol ip pref 1 handle 101 flower \
55 $tcflags dst_ip 192.0.2.2 action drop
56
57 $MZ $h1 -c 1 -p 64 -a $h1mac -b $h2mac -A 192.0.2.1 -B 192.0.2.2 \
58 -t ip -q
59
60 tc_check_packets "dev $h2 ingress" 101 1
61 check_fail $? "Matched without redirect rule inserted"
62
63 tc filter add dev $swp1 ingress protocol ip pref 1 handle 101 flower \
64 $tcflags dst_ip 192.0.2.2 action mirred egress $action \
65 dev $swp2
66
67 $MZ $h1 -c 1 -p 64 -a $h1mac -b $h2mac -A 192.0.2.1 -B 192.0.2.2 \
68 -t ip -q
69
70 tc_check_packets "dev $h2 ingress" 101 1
71 check_err $? "Did not match incoming $action packet"
72
73 tc filter del dev $swp1 ingress protocol ip pref 1 handle 101 flower
74 tc filter del dev $h2 ingress protocol ip pref 1 handle 101 flower
75
76 log_test "mirred egress $action ($tcflags)"
77}
78
79gact_drop_and_ok_test()
80{
81 RET=0
82
83 tc filter add dev $swp1 ingress protocol ip pref 2 handle 102 flower \
84 $tcflags dst_ip 192.0.2.2 action drop
85
86 $MZ $h1 -c 1 -p 64 -a $h1mac -b $h2mac -A 192.0.2.1 -B 192.0.2.2 \
87 -t ip -q
88
89 tc_check_packets "dev $swp1 ingress" 102 1
90 check_err $? "Packet was not dropped"
91
92 tc filter add dev $swp1 ingress protocol ip pref 1 handle 101 flower \
93 $tcflags dst_ip 192.0.2.2 action ok
94
95 $MZ $h1 -c 1 -p 64 -a $h1mac -b $h2mac -A 192.0.2.1 -B 192.0.2.2 \
96 -t ip -q
97
98 tc_check_packets "dev $swp1 ingress" 101 1
99 check_err $? "Did not see passed packet"
100
101 tc_check_packets "dev $swp1 ingress" 102 2
102 check_fail $? "Packet was dropped and it should not reach here"
103
104 tc filter del dev $swp1 ingress protocol ip pref 2 handle 102 flower
105 tc filter del dev $swp1 ingress protocol ip pref 1 handle 101 flower
106
107 log_test "gact drop and ok ($tcflags)"
108}
109
110gact_trap_test()
111{
112 RET=0
113
114 tc filter add dev $swp1 ingress protocol ip pref 1 handle 101 flower \
115 skip_hw dst_ip 192.0.2.2 action drop
116 tc filter add dev $swp1 ingress protocol ip pref 3 handle 103 flower \
117 $tcflags dst_ip 192.0.2.2 action mirred egress redirect \
118 dev $swp2
119
120 $MZ $h1 -c 1 -p 64 -a $h1mac -b $h2mac -A 192.0.2.1 -B 192.0.2.2 \
121 -t ip -q
122
123 tc_check_packets "dev $swp1 ingress" 101 1
124 check_fail $? "Saw packet without trap rule inserted"
125
126 tc filter add dev $swp1 ingress protocol ip pref 2 handle 102 flower \
127 $tcflags dst_ip 192.0.2.2 action trap
128
129 $MZ $h1 -c 1 -p 64 -a $h1mac -b $h2mac -A 192.0.2.1 -B 192.0.2.2 \
130 -t ip -q
131
132 tc_check_packets "dev $swp1 ingress" 102 1
133 check_err $? "Packet was not trapped"
134
135 tc_check_packets "dev $swp1 ingress" 101 1
136 check_err $? "Did not see trapped packet"
137
138 tc filter del dev $swp1 ingress protocol ip pref 3 handle 103 flower
139 tc filter del dev $swp1 ingress protocol ip pref 2 handle 102 flower
140 tc filter del dev $swp1 ingress protocol ip pref 1 handle 101 flower
141
142 log_test "trap ($tcflags)"
143}
144
145setup_prepare()
146{
147 h1=${NETIFS[p1]}
148 swp1=${NETIFS[p2]}
149
150 swp2=${NETIFS[p3]}
151 h2=${NETIFS[p4]}
152
153 h1mac=$(mac_get $h1)
154 h2mac=$(mac_get $h2)
155
156 swp1origmac=$(mac_get $swp1)
157 swp2origmac=$(mac_get $swp2)
158 ip link set $swp1 address $h2mac
159 ip link set $swp2 address $h1mac
160
161 vrf_prepare
162
163 h1_create
164 h2_create
165 switch_create
166}
167
168cleanup()
169{
170 pre_cleanup
171
172 switch_destroy
173 h2_destroy
174 h1_destroy
175
176 vrf_cleanup
177
178 ip link set $swp2 address $swp2origmac
179 ip link set $swp1 address $swp1origmac
180}
181
182trap cleanup EXIT
183
184setup_prepare
185setup_wait
186
187gact_drop_and_ok_test
188mirred_egress_test "redirect"
189mirred_egress_test "mirror"
190
191tc_offload_check
192if [[ $? -ne 0 ]]; then
193 log_info "Could not test offloaded functionality"
194else
195 tcflags="skip_sw"
196 gact_drop_and_ok_test
197 mirred_egress_test "redirect"
198 mirred_egress_test "mirror"
199 gact_trap_test
200fi
201
202exit $EXIT_STATUS
diff --git a/tools/testing/selftests/net/forwarding/tc_chains.sh b/tools/testing/selftests/net/forwarding/tc_chains.sh
new file mode 100755
index 000000000000..2fd15226974b
--- /dev/null
+++ b/tools/testing/selftests/net/forwarding/tc_chains.sh
@@ -0,0 +1,122 @@
1#!/bin/bash
2# SPDX-License-Identifier: GPL-2.0
3
4NUM_NETIFS=2
5source tc_common.sh
6source lib.sh
7
8tcflags="skip_hw"
9
10h1_create()
11{
12 simple_if_init $h1 192.0.2.1/24
13}
14
15h1_destroy()
16{
17 simple_if_fini $h1 192.0.2.1/24
18}
19
20h2_create()
21{
22 simple_if_init $h2 192.0.2.2/24
23 tc qdisc add dev $h2 clsact
24}
25
26h2_destroy()
27{
28 tc qdisc del dev $h2 clsact
29 simple_if_fini $h2 192.0.2.2/24
30}
31
32unreachable_chain_test()
33{
34 RET=0
35
36 tc filter add dev $h2 ingress chain 1 protocol ip pref 1 handle 1101 \
37 flower $tcflags dst_mac $h2mac action drop
38
39 $MZ $h1 -c 1 -p 64 -a $h1mac -b $h2mac -A 192.0.2.1 -B 192.0.2.2 \
40 -t ip -q
41
42 tc_check_packets "dev $h2 ingress" 1101 1
43 check_fail $? "matched on filter in unreachable chain"
44
45 tc filter del dev $h2 ingress chain 1 protocol ip pref 1 handle 1101 \
46 flower
47
48 log_test "unreachable chain ($tcflags)"
49}
50
51gact_goto_chain_test()
52{
53 RET=0
54
55 tc filter add dev $h2 ingress chain 1 protocol ip pref 1 handle 1101 \
56 flower $tcflags dst_mac $h2mac action drop
57 tc filter add dev $h2 ingress protocol ip pref 2 handle 102 flower \
58 $tcflags dst_mac $h2mac action drop
59 tc filter add dev $h2 ingress protocol ip pref 1 handle 101 flower \
60 $tcflags dst_mac $h2mac action goto chain 1
61
62 $MZ $h1 -c 1 -p 64 -a $h1mac -b $h2mac -A 192.0.2.1 -B 192.0.2.2 \
63 -t ip -q
64
65 tc_check_packets "dev $h2 ingress" 102 1
66 check_fail $? "Matched on a wrong filter"
67
68 tc_check_packets "dev $h2 ingress" 101 1
69 check_err $? "Did not match on correct filter with goto chain action"
70
71 tc_check_packets "dev $h2 ingress" 1101 1
72 check_err $? "Did not match on correct filter in chain 1"
73
74 tc filter del dev $h2 ingress protocol ip pref 1 handle 101 flower
75 tc filter del dev $h2 ingress protocol ip pref 2 handle 102 flower
76 tc filter del dev $h2 ingress chain 1 protocol ip pref 1 handle 1101 \
77 flower
78
79 log_test "gact goto chain ($tcflags)"
80}
81
82setup_prepare()
83{
84 h1=${NETIFS[p1]}
85 h2=${NETIFS[p2]}
86 h1mac=$(mac_get $h1)
87 h2mac=$(mac_get $h2)
88
89 vrf_prepare
90
91 h1_create
92 h2_create
93}
94
95cleanup()
96{
97 pre_cleanup
98
99 h2_destroy
100 h1_destroy
101
102 vrf_cleanup
103}
104
105trap cleanup EXIT
106
107setup_prepare
108setup_wait
109
110unreachable_chain_test
111gact_goto_chain_test
112
113tc_offload_check
114if [[ $? -ne 0 ]]; then
115 log_info "Could not test offloaded functionality"
116else
117 tcflags="skip_sw"
118 unreachable_chain_test
119 gact_goto_chain_test
120fi
121
122exit $EXIT_STATUS
diff --git a/tools/testing/selftests/net/forwarding/tc_common.sh b/tools/testing/selftests/net/forwarding/tc_common.sh
new file mode 100644
index 000000000000..9d3b64a2a264
--- /dev/null
+++ b/tools/testing/selftests/net/forwarding/tc_common.sh
@@ -0,0 +1,25 @@
1#!/bin/bash
2# SPDX-License-Identifier: GPL-2.0
3
4CHECK_TC="yes"
5
6tc_check_packets()
7{
8 local id=$1
9 local handle=$2
10 local count=$3
11 local ret
12
13 output="$(tc -j -s filter show $id)"
14 # workaround the jq bug which causes jq to return 0 in case input is ""
15 ret=$?
16 if [[ $ret -ne 0 ]]; then
17 return $ret
18 fi
19 echo $output | \
20 jq -e ".[] \
21 | select(.options.handle == $handle) \
22 | select(.options.actions[0].stats.packets == $count)" \
23 &> /dev/null
24 return $?
25}
diff --git a/tools/testing/selftests/net/forwarding/tc_flower.sh b/tools/testing/selftests/net/forwarding/tc_flower.sh
new file mode 100755
index 000000000000..032b882adfc0
--- /dev/null
+++ b/tools/testing/selftests/net/forwarding/tc_flower.sh
@@ -0,0 +1,196 @@
1#!/bin/bash
2# SPDX-License-Identifier: GPL-2.0
3
4NUM_NETIFS=2
5source tc_common.sh
6source lib.sh
7
8tcflags="skip_hw"
9
10h1_create()
11{
12 simple_if_init $h1 192.0.2.1/24 198.51.100.1/24
13}
14
15h1_destroy()
16{
17 simple_if_fini $h1 192.0.2.1/24 198.51.100.1/24
18}
19
20h2_create()
21{
22 simple_if_init $h2 192.0.2.2/24 198.51.100.2/24
23 tc qdisc add dev $h2 clsact
24}
25
26h2_destroy()
27{
28 tc qdisc del dev $h2 clsact
29 simple_if_fini $h2 192.0.2.2/24 198.51.100.2/24
30}
31
32match_dst_mac_test()
33{
34 local dummy_mac=de:ad:be:ef:aa:aa
35
36 RET=0
37
38 tc filter add dev $h2 ingress protocol ip pref 1 handle 101 flower \
39 $tcflags dst_mac $dummy_mac action drop
40 tc filter add dev $h2 ingress protocol ip pref 2 handle 102 flower \
41 $tcflags dst_mac $h2mac action drop
42
43 $MZ $h1 -c 1 -p 64 -a $h1mac -b $h2mac -A 192.0.2.1 -B 192.0.2.2 \
44 -t ip -q
45
46 tc_check_packets "dev $h2 ingress" 101 1
47 check_fail $? "Matched on a wrong filter"
48
49 tc_check_packets "dev $h2 ingress" 102 1
50 check_err $? "Did not match on correct filter"
51
52 tc filter del dev $h2 ingress protocol ip pref 1 handle 101 flower
53 tc filter del dev $h2 ingress protocol ip pref 2 handle 102 flower
54
55 log_test "dst_mac match ($tcflags)"
56}
57
58match_src_mac_test()
59{
60 local dummy_mac=de:ad:be:ef:aa:aa
61
62 RET=0
63
64 tc filter add dev $h2 ingress protocol ip pref 1 handle 101 flower \
65 $tcflags src_mac $dummy_mac action drop
66 tc filter add dev $h2 ingress protocol ip pref 2 handle 102 flower \
67 $tcflags src_mac $h1mac action drop
68
69 $MZ $h1 -c 1 -p 64 -a $h1mac -b $h2mac -A 192.0.2.1 -B 192.0.2.2 \
70 -t ip -q
71
72 tc_check_packets "dev $h2 ingress" 101 1
73 check_fail $? "Matched on a wrong filter"
74
75 tc_check_packets "dev $h2 ingress" 102 1
76 check_err $? "Did not match on correct filter"
77
78 tc filter del dev $h2 ingress protocol ip pref 1 handle 101 flower
79 tc filter del dev $h2 ingress protocol ip pref 2 handle 102 flower
80
81 log_test "src_mac match ($tcflags)"
82}
83
84match_dst_ip_test()
85{
86 RET=0
87
88 tc filter add dev $h2 ingress protocol ip pref 1 handle 101 flower \
89 $tcflags dst_ip 198.51.100.2 action drop
90 tc filter add dev $h2 ingress protocol ip pref 2 handle 102 flower \
91 $tcflags dst_ip 192.0.2.2 action drop
92 tc filter add dev $h2 ingress protocol ip pref 3 handle 103 flower \
93 $tcflags dst_ip 192.0.2.0/24 action drop
94
95 $MZ $h1 -c 1 -p 64 -a $h1mac -b $h2mac -A 192.0.2.1 -B 192.0.2.2 \
96 -t ip -q
97
98 tc_check_packets "dev $h2 ingress" 101 1
99 check_fail $? "Matched on a wrong filter"
100
101 tc_check_packets "dev $h2 ingress" 102 1
102 check_err $? "Did not match on correct filter"
103
104 tc filter del dev $h2 ingress protocol ip pref 2 handle 102 flower
105
106 $MZ $h1 -c 1 -p 64 -a $h1mac -b $h2mac -A 192.0.2.1 -B 192.0.2.2 \
107 -t ip -q
108
109 tc_check_packets "dev $h2 ingress" 103 1
110 check_err $? "Did not match on correct filter with mask"
111
112 tc filter del dev $h2 ingress protocol ip pref 1 handle 101 flower
113 tc filter del dev $h2 ingress protocol ip pref 3 handle 103 flower
114
115 log_test "dst_ip match ($tcflags)"
116}
117
118match_src_ip_test()
119{
120 RET=0
121
122 tc filter add dev $h2 ingress protocol ip pref 1 handle 101 flower \
123 $tcflags src_ip 198.51.100.1 action drop
124 tc filter add dev $h2 ingress protocol ip pref 2 handle 102 flower \
125 $tcflags src_ip 192.0.2.1 action drop
126 tc filter add dev $h2 ingress protocol ip pref 3 handle 103 flower \
127 $tcflags src_ip 192.0.2.0/24 action drop
128
129 $MZ $h1 -c 1 -p 64 -a $h1mac -b $h2mac -A 192.0.2.1 -B 192.0.2.2 \
130 -t ip -q
131
132 tc_check_packets "dev $h2 ingress" 101 1
133 check_fail $? "Matched on a wrong filter"
134
135 tc_check_packets "dev $h2 ingress" 102 1
136 check_err $? "Did not match on correct filter"
137
138 tc filter del dev $h2 ingress protocol ip pref 2 handle 102 flower
139
140 $MZ $h1 -c 1 -p 64 -a $h1mac -b $h2mac -A 192.0.2.1 -B 192.0.2.2 \
141 -t ip -q
142
143 tc_check_packets "dev $h2 ingress" 103 1
144 check_err $? "Did not match on correct filter with mask"
145
146 tc filter del dev $h2 ingress protocol ip pref 1 handle 101 flower
147 tc filter del dev $h2 ingress protocol ip pref 3 handle 103 flower
148
149 log_test "src_ip match ($tcflags)"
150}
151
152setup_prepare()
153{
154 h1=${NETIFS[p1]}
155 h2=${NETIFS[p2]}
156 h1mac=$(mac_get $h1)
157 h2mac=$(mac_get $h2)
158
159 vrf_prepare
160
161 h1_create
162 h2_create
163}
164
165cleanup()
166{
167 pre_cleanup
168
169 h2_destroy
170 h1_destroy
171
172 vrf_cleanup
173}
174
175trap cleanup EXIT
176
177setup_prepare
178setup_wait
179
180match_dst_mac_test
181match_src_mac_test
182match_dst_ip_test
183match_src_ip_test
184
185tc_offload_check
186if [[ $? -ne 0 ]]; then
187 log_info "Could not test offloaded functionality"
188else
189 tcflags="skip_sw"
190 match_dst_mac_test
191 match_src_mac_test
192 match_dst_ip_test
193 match_src_ip_test
194fi
195
196exit $EXIT_STATUS
diff --git a/tools/testing/selftests/net/forwarding/tc_shblocks.sh b/tools/testing/selftests/net/forwarding/tc_shblocks.sh
new file mode 100755
index 000000000000..077b98048ef4
--- /dev/null
+++ b/tools/testing/selftests/net/forwarding/tc_shblocks.sh
@@ -0,0 +1,122 @@
1#!/bin/bash
2# SPDX-License-Identifier: GPL-2.0
3
4NUM_NETIFS=4
5source tc_common.sh
6source lib.sh
7
8tcflags="skip_hw"
9
10h1_create()
11{
12 simple_if_init $h1 192.0.2.1/24
13}
14
15h1_destroy()
16{
17 simple_if_fini $h1 192.0.2.1/24
18}
19
20h2_create()
21{
22 simple_if_init $h2 192.0.2.1/24
23}
24
25h2_destroy()
26{
27 simple_if_fini $h2 192.0.2.1/24
28}
29
30switch_create()
31{
32 simple_if_init $swp1 192.0.2.2/24
33 tc qdisc add dev $swp1 ingress_block 22 egress_block 23 clsact
34
35 simple_if_init $swp2 192.0.2.2/24
36 tc qdisc add dev $swp2 ingress_block 22 egress_block 23 clsact
37}
38
39switch_destroy()
40{
41 tc qdisc del dev $swp2 clsact
42 simple_if_fini $swp2 192.0.2.2/24
43
44 tc qdisc del dev $swp1 clsact
45 simple_if_fini $swp1 192.0.2.2/24
46}
47
48shared_block_test()
49{
50 RET=0
51
52 tc filter add block 22 protocol ip pref 1 handle 101 flower \
53 $tcflags dst_ip 192.0.2.2 action drop
54
55 $MZ $h1 -c 1 -p 64 -a $h1mac -b $swmac -A 192.0.2.1 -B 192.0.2.2 \
56 -t ip -q
57
58 tc_check_packets "block 22" 101 1
59 check_err $? "Did not match first incoming packet on a block"
60
61 $MZ $h2 -c 1 -p 64 -a $h2mac -b $swmac -A 192.0.2.1 -B 192.0.2.2 \
62 -t ip -q
63
64 tc_check_packets "block 22" 101 2
65 check_err $? "Did not match second incoming packet on a block"
66
67 tc filter del block 22 protocol ip pref 1 handle 101 flower
68
69 log_test "shared block ($tcflags)"
70}
71
72setup_prepare()
73{
74 h1=${NETIFS[p1]}
75 swp1=${NETIFS[p2]}
76
77 swp2=${NETIFS[p3]}
78 h2=${NETIFS[p4]}
79
80 h1mac=$(mac_get $h1)
81 h2mac=$(mac_get $h2)
82
83 swmac=$(mac_get $swp1)
84 swp2origmac=$(mac_get $swp2)
85 ip link set $swp2 address $swmac
86
87 vrf_prepare
88
89 h1_create
90 h2_create
91 switch_create
92}
93
94cleanup()
95{
96 pre_cleanup
97
98 switch_destroy
99 h2_destroy
100 h1_destroy
101
102 vrf_cleanup
103
104 ip link set $swp2 address $swp2origmac
105}
106
107trap cleanup EXIT
108
109setup_prepare
110setup_wait
111
112shared_block_test
113
114tc_offload_check
115if [[ $? -ne 0 ]]; then
116 log_info "Could not test offloaded functionality"
117else
118 tcflags="skip_sw"
119 shared_block_test
120fi
121
122exit $EXIT_STATUS
diff --git a/tools/testing/selftests/net/in_netns.sh b/tools/testing/selftests/net/in_netns.sh
new file mode 100755
index 000000000000..88795b510b32
--- /dev/null
+++ b/tools/testing/selftests/net/in_netns.sh
@@ -0,0 +1,23 @@
1#!/bin/sh
2# SPDX-License-Identifier: GPL-2.0
3#
4# Execute a subprocess in a network namespace
5
6set -e
7
8readonly NETNS="ns-$(mktemp -u XXXXXX)"
9
10setup() {
11 ip netns add "${NETNS}"
12 ip -netns "${NETNS}" link set lo up
13}
14
15cleanup() {
16 ip netns del "${NETNS}"
17}
18
19trap cleanup EXIT
20setup
21
22ip netns exec "${NETNS}" "$@"
23exit "$?"
diff --git a/tools/testing/selftests/net/msg_zerocopy.c b/tools/testing/selftests/net/msg_zerocopy.c
index e11fe84de0fd..406cc70c571d 100644
--- a/tools/testing/selftests/net/msg_zerocopy.c
+++ b/tools/testing/selftests/net/msg_zerocopy.c
@@ -14,6 +14,9 @@
14 * - SOCK_DGRAM 14 * - SOCK_DGRAM
15 * - SOCK_RAW 15 * - SOCK_RAW
16 * 16 *
17 * PF_RDS
18 * - SOCK_SEQPACKET
19 *
17 * Start this program on two connected hosts, one in send mode and 20 * Start this program on two connected hosts, one in send mode and
18 * the other with option '-r' to put it in receiver mode. 21 * the other with option '-r' to put it in receiver mode.
19 * 22 *
@@ -53,6 +56,7 @@
53#include <sys/types.h> 56#include <sys/types.h>
54#include <sys/wait.h> 57#include <sys/wait.h>
55#include <unistd.h> 58#include <unistd.h>
59#include <linux/rds.h>
56 60
57#ifndef SO_EE_ORIGIN_ZEROCOPY 61#ifndef SO_EE_ORIGIN_ZEROCOPY
58#define SO_EE_ORIGIN_ZEROCOPY 5 62#define SO_EE_ORIGIN_ZEROCOPY 5
@@ -164,17 +168,39 @@ static int do_accept(int fd)
164 return fd; 168 return fd;
165} 169}
166 170
167static bool do_sendmsg(int fd, struct msghdr *msg, bool do_zerocopy) 171static void add_zcopy_cookie(struct msghdr *msg, uint32_t cookie)
172{
173 struct cmsghdr *cm;
174
175 if (!msg->msg_control)
176 error(1, errno, "NULL cookie");
177 cm = (void *)msg->msg_control;
178 cm->cmsg_len = CMSG_LEN(sizeof(cookie));
179 cm->cmsg_level = SOL_RDS;
180 cm->cmsg_type = RDS_CMSG_ZCOPY_COOKIE;
181 memcpy(CMSG_DATA(cm), &cookie, sizeof(cookie));
182}
183
184static bool do_sendmsg(int fd, struct msghdr *msg, bool do_zerocopy, int domain)
168{ 185{
169 int ret, len, i, flags; 186 int ret, len, i, flags;
187 static uint32_t cookie;
188 char ckbuf[CMSG_SPACE(sizeof(cookie))];
170 189
171 len = 0; 190 len = 0;
172 for (i = 0; i < msg->msg_iovlen; i++) 191 for (i = 0; i < msg->msg_iovlen; i++)
173 len += msg->msg_iov[i].iov_len; 192 len += msg->msg_iov[i].iov_len;
174 193
175 flags = MSG_DONTWAIT; 194 flags = MSG_DONTWAIT;
176 if (do_zerocopy) 195 if (do_zerocopy) {
177 flags |= MSG_ZEROCOPY; 196 flags |= MSG_ZEROCOPY;
197 if (domain == PF_RDS) {
198 memset(&msg->msg_control, 0, sizeof(msg->msg_control));
199 msg->msg_controllen = CMSG_SPACE(sizeof(cookie));
200 msg->msg_control = (struct cmsghdr *)ckbuf;
201 add_zcopy_cookie(msg, ++cookie);
202 }
203 }
178 204
179 ret = sendmsg(fd, msg, flags); 205 ret = sendmsg(fd, msg, flags);
180 if (ret == -1 && errno == EAGAIN) 206 if (ret == -1 && errno == EAGAIN)
@@ -190,6 +216,10 @@ static bool do_sendmsg(int fd, struct msghdr *msg, bool do_zerocopy)
190 if (do_zerocopy && ret) 216 if (do_zerocopy && ret)
191 expected_completions++; 217 expected_completions++;
192 } 218 }
219 if (do_zerocopy && domain == PF_RDS) {
220 msg->msg_control = NULL;
221 msg->msg_controllen = 0;
222 }
193 223
194 return true; 224 return true;
195} 225}
@@ -216,7 +246,9 @@ static void do_sendmsg_corked(int fd, struct msghdr *msg)
216 msg->msg_iov[0].iov_len = payload_len + extra_len; 246 msg->msg_iov[0].iov_len = payload_len + extra_len;
217 extra_len = 0; 247 extra_len = 0;
218 248
219 do_sendmsg(fd, msg, do_zerocopy); 249 do_sendmsg(fd, msg, do_zerocopy,
250 (cfg_dst_addr.ss_family == AF_INET ?
251 PF_INET : PF_INET6));
220 } 252 }
221 253
222 do_setsockopt(fd, IPPROTO_UDP, UDP_CORK, 0); 254 do_setsockopt(fd, IPPROTO_UDP, UDP_CORK, 0);
@@ -300,14 +332,65 @@ static int do_setup_tx(int domain, int type, int protocol)
300 if (cfg_zerocopy) 332 if (cfg_zerocopy)
301 do_setsockopt(fd, SOL_SOCKET, SO_ZEROCOPY, 1); 333 do_setsockopt(fd, SOL_SOCKET, SO_ZEROCOPY, 1);
302 334
303 if (domain != PF_PACKET) 335 if (domain != PF_PACKET && domain != PF_RDS)
304 if (connect(fd, (void *) &cfg_dst_addr, cfg_alen)) 336 if (connect(fd, (void *) &cfg_dst_addr, cfg_alen))
305 error(1, errno, "connect"); 337 error(1, errno, "connect");
306 338
339 if (domain == PF_RDS) {
340 if (bind(fd, (void *) &cfg_src_addr, cfg_alen))
341 error(1, errno, "bind");
342 }
343
307 return fd; 344 return fd;
308} 345}
309 346
310static bool do_recv_completion(int fd) 347static uint32_t do_process_zerocopy_cookies(struct rds_zcopy_cookies *ck)
348{
349 int i;
350
351 if (ck->num > RDS_MAX_ZCOOKIES)
352 error(1, 0, "Returned %d cookies, max expected %d\n",
353 ck->num, RDS_MAX_ZCOOKIES);
354 for (i = 0; i < ck->num; i++)
355 if (cfg_verbose >= 2)
356 fprintf(stderr, "%d\n", ck->cookies[i]);
357 return ck->num;
358}
359
360static bool do_recvmsg_completion(int fd)
361{
362 char cmsgbuf[CMSG_SPACE(sizeof(struct rds_zcopy_cookies))];
363 struct rds_zcopy_cookies *ck;
364 struct cmsghdr *cmsg;
365 struct msghdr msg;
366 bool ret = false;
367
368 memset(&msg, 0, sizeof(msg));
369 msg.msg_control = cmsgbuf;
370 msg.msg_controllen = sizeof(cmsgbuf);
371
372 if (recvmsg(fd, &msg, MSG_DONTWAIT))
373 return ret;
374
375 if (msg.msg_flags & MSG_CTRUNC)
376 error(1, errno, "recvmsg notification: truncated");
377
378 for (cmsg = CMSG_FIRSTHDR(&msg); cmsg; cmsg = CMSG_NXTHDR(&msg, cmsg)) {
379 if (cmsg->cmsg_level == SOL_RDS &&
380 cmsg->cmsg_type == RDS_CMSG_ZCOPY_COMPLETION) {
381
382 ck = (struct rds_zcopy_cookies *)CMSG_DATA(cmsg);
383 completions += do_process_zerocopy_cookies(ck);
384 ret = true;
385 break;
386 }
387 error(0, 0, "ignoring cmsg at level %d type %d\n",
388 cmsg->cmsg_level, cmsg->cmsg_type);
389 }
390 return ret;
391}
392
393static bool do_recv_completion(int fd, int domain)
311{ 394{
312 struct sock_extended_err *serr; 395 struct sock_extended_err *serr;
313 struct msghdr msg = {}; 396 struct msghdr msg = {};
@@ -316,6 +399,9 @@ static bool do_recv_completion(int fd)
316 int ret, zerocopy; 399 int ret, zerocopy;
317 char control[100]; 400 char control[100];
318 401
402 if (domain == PF_RDS)
403 return do_recvmsg_completion(fd);
404
319 msg.msg_control = control; 405 msg.msg_control = control;
320 msg.msg_controllen = sizeof(control); 406 msg.msg_controllen = sizeof(control);
321 407
@@ -337,6 +423,7 @@ static bool do_recv_completion(int fd)
337 cm->cmsg_level, cm->cmsg_type); 423 cm->cmsg_level, cm->cmsg_type);
338 424
339 serr = (void *) CMSG_DATA(cm); 425 serr = (void *) CMSG_DATA(cm);
426
340 if (serr->ee_origin != SO_EE_ORIGIN_ZEROCOPY) 427 if (serr->ee_origin != SO_EE_ORIGIN_ZEROCOPY)
341 error(1, 0, "serr: wrong origin: %u", serr->ee_origin); 428 error(1, 0, "serr: wrong origin: %u", serr->ee_origin);
342 if (serr->ee_errno != 0) 429 if (serr->ee_errno != 0)
@@ -371,20 +458,20 @@ static bool do_recv_completion(int fd)
371} 458}
372 459
373/* Read all outstanding messages on the errqueue */ 460/* Read all outstanding messages on the errqueue */
374static void do_recv_completions(int fd) 461static void do_recv_completions(int fd, int domain)
375{ 462{
376 while (do_recv_completion(fd)) {} 463 while (do_recv_completion(fd, domain)) {}
377} 464}
378 465
379/* Wait for all remaining completions on the errqueue */ 466/* Wait for all remaining completions on the errqueue */
380static void do_recv_remaining_completions(int fd) 467static void do_recv_remaining_completions(int fd, int domain)
381{ 468{
382 int64_t tstop = gettimeofday_ms() + cfg_waittime_ms; 469 int64_t tstop = gettimeofday_ms() + cfg_waittime_ms;
383 470
384 while (completions < expected_completions && 471 while (completions < expected_completions &&
385 gettimeofday_ms() < tstop) { 472 gettimeofday_ms() < tstop) {
386 if (do_poll(fd, POLLERR)) 473 if (do_poll(fd, domain == PF_RDS ? POLLIN : POLLERR))
387 do_recv_completions(fd); 474 do_recv_completions(fd, domain);
388 } 475 }
389 476
390 if (completions < expected_completions) 477 if (completions < expected_completions)
@@ -444,6 +531,13 @@ static void do_tx(int domain, int type, int protocol)
444 msg.msg_iovlen++; 531 msg.msg_iovlen++;
445 } 532 }
446 533
534 if (domain == PF_RDS) {
535 msg.msg_name = &cfg_dst_addr;
536 msg.msg_namelen = (cfg_dst_addr.ss_family == AF_INET ?
537 sizeof(struct sockaddr_in) :
538 sizeof(struct sockaddr_in6));
539 }
540
447 iov[2].iov_base = payload; 541 iov[2].iov_base = payload;
448 iov[2].iov_len = cfg_payload_len; 542 iov[2].iov_len = cfg_payload_len;
449 msg.msg_iovlen++; 543 msg.msg_iovlen++;
@@ -454,17 +548,17 @@ static void do_tx(int domain, int type, int protocol)
454 if (cfg_cork) 548 if (cfg_cork)
455 do_sendmsg_corked(fd, &msg); 549 do_sendmsg_corked(fd, &msg);
456 else 550 else
457 do_sendmsg(fd, &msg, cfg_zerocopy); 551 do_sendmsg(fd, &msg, cfg_zerocopy, domain);
458 552
459 while (!do_poll(fd, POLLOUT)) { 553 while (!do_poll(fd, POLLOUT)) {
460 if (cfg_zerocopy) 554 if (cfg_zerocopy)
461 do_recv_completions(fd); 555 do_recv_completions(fd, domain);
462 } 556 }
463 557
464 } while (gettimeofday_ms() < tstop); 558 } while (gettimeofday_ms() < tstop);
465 559
466 if (cfg_zerocopy) 560 if (cfg_zerocopy)
467 do_recv_remaining_completions(fd); 561 do_recv_remaining_completions(fd, domain);
468 562
469 if (close(fd)) 563 if (close(fd))
470 error(1, errno, "close"); 564 error(1, errno, "close");
@@ -610,6 +704,7 @@ static void parse_opts(int argc, char **argv)
610 40 /* max tcp options */; 704 40 /* max tcp options */;
611 int c; 705 int c;
612 char *daddr = NULL, *saddr = NULL; 706 char *daddr = NULL, *saddr = NULL;
707 char *cfg_test;
613 708
614 cfg_payload_len = max_payload_len; 709 cfg_payload_len = max_payload_len;
615 710
@@ -667,6 +762,14 @@ static void parse_opts(int argc, char **argv)
667 break; 762 break;
668 } 763 }
669 } 764 }
765
766 cfg_test = argv[argc - 1];
767 if (strcmp(cfg_test, "rds") == 0) {
768 if (!daddr)
769 error(1, 0, "-D <server addr> required for PF_RDS\n");
770 if (!cfg_rx && !saddr)
771 error(1, 0, "-S <client addr> required for PF_RDS\n");
772 }
670 setup_sockaddr(cfg_family, daddr, &cfg_dst_addr); 773 setup_sockaddr(cfg_family, daddr, &cfg_dst_addr);
671 setup_sockaddr(cfg_family, saddr, &cfg_src_addr); 774 setup_sockaddr(cfg_family, saddr, &cfg_src_addr);
672 775
@@ -699,6 +802,8 @@ int main(int argc, char **argv)
699 do_test(cfg_family, SOCK_STREAM, 0); 802 do_test(cfg_family, SOCK_STREAM, 0);
700 else if (!strcmp(cfg_test, "udp")) 803 else if (!strcmp(cfg_test, "udp"))
701 do_test(cfg_family, SOCK_DGRAM, 0); 804 do_test(cfg_family, SOCK_DGRAM, 0);
805 else if (!strcmp(cfg_test, "rds"))
806 do_test(PF_RDS, SOCK_SEQPACKET, 0);
702 else 807 else
703 error(1, 0, "unknown cfg_test %s", cfg_test); 808 error(1, 0, "unknown cfg_test %s", cfg_test);
704 809
diff --git a/tools/testing/selftests/net/pmtu.sh b/tools/testing/selftests/net/pmtu.sh
new file mode 100755
index 000000000000..1e428781a625
--- /dev/null
+++ b/tools/testing/selftests/net/pmtu.sh
@@ -0,0 +1,471 @@
1#!/bin/sh
2# SPDX-License-Identifier: GPL-2.0
3#
4# Check that route PMTU values match expectations, and that initial device MTU
5# values are assigned correctly
6#
7# Tests currently implemented:
8#
9# - pmtu_vti4_exception
10# Set up vti tunnel on top of veth, with xfrm states and policies, in two
11# namespaces with matching endpoints. Check that route exception is not
12# created if link layer MTU is not exceeded, then exceed it and check that
13# exception is created with the expected PMTU. The approach described
14# below for IPv6 doesn't apply here, because, on IPv4, administrative MTU
15# changes alone won't affect PMTU
16#
17# - pmtu_vti6_exception
18# Set up vti6 tunnel on top of veth, with xfrm states and policies, in two
19# namespaces with matching endpoints. Check that route exception is
20# created by exceeding link layer MTU with ping to other endpoint. Then
21# decrease and increase MTU of tunnel, checking that route exception PMTU
22# changes accordingly
23#
24# - pmtu_vti4_default_mtu
25# Set up vti4 tunnel on top of veth, in two namespaces with matching
26# endpoints. Check that MTU assigned to vti interface is the MTU of the
27# lower layer (veth) minus additional lower layer headers (zero, for veth)
28# minus IPv4 header length
29#
30# - pmtu_vti6_default_mtu
31# Same as above, for IPv6
32#
33# - pmtu_vti4_link_add_mtu
34# Set up vti4 interface passing MTU value at link creation, check MTU is
35# configured, and that link is not created with invalid MTU values
36#
37# - pmtu_vti6_link_add_mtu
38# Same as above, for IPv6
39#
40# - pmtu_vti6_link_change_mtu
41# Set up two dummy interfaces with different MTUs, create a vti6 tunnel
42# and check that configured MTU is used on link creation and changes, and
43# that MTU is properly calculated instead when MTU is not configured from
44# userspace
45
46tests="
47 pmtu_vti6_exception vti6: PMTU exceptions
48 pmtu_vti4_exception vti4: PMTU exceptions
49 pmtu_vti4_default_mtu vti4: default MTU assignment
50 pmtu_vti6_default_mtu vti6: default MTU assignment
51 pmtu_vti4_link_add_mtu vti4: MTU setting on link creation
52 pmtu_vti6_link_add_mtu vti6: MTU setting on link creation
53 pmtu_vti6_link_change_mtu vti6: MTU changes on link changes"
54
55NS_A="ns-$(mktemp -u XXXXXX)"
56NS_B="ns-$(mktemp -u XXXXXX)"
57ns_a="ip netns exec ${NS_A}"
58ns_b="ip netns exec ${NS_B}"
59
60veth4_a_addr="192.168.1.1"
61veth4_b_addr="192.168.1.2"
62veth4_mask="24"
63veth6_a_addr="fd00:1::a"
64veth6_b_addr="fd00:1::b"
65veth6_mask="64"
66
67vti4_a_addr="192.168.2.1"
68vti4_b_addr="192.168.2.2"
69vti4_mask="24"
70vti6_a_addr="fd00:2::a"
71vti6_b_addr="fd00:2::b"
72vti6_mask="64"
73
74dummy6_0_addr="fc00:1000::0"
75dummy6_1_addr="fc00:1001::0"
76dummy6_mask="64"
77
78cleanup_done=1
79err_buf=
80
81err() {
82 err_buf="${err_buf}${1}
83"
84}
85
86err_flush() {
87 echo -n "${err_buf}"
88 err_buf=
89}
90
91setup_namespaces() {
92 ip netns add ${NS_A} || return 1
93 ip netns add ${NS_B}
94}
95
96setup_veth() {
97 ${ns_a} ip link add veth_a type veth peer name veth_b || return 1
98 ${ns_a} ip link set veth_b netns ${NS_B}
99
100 ${ns_a} ip addr add ${veth4_a_addr}/${veth4_mask} dev veth_a
101 ${ns_b} ip addr add ${veth4_b_addr}/${veth4_mask} dev veth_b
102
103 ${ns_a} ip addr add ${veth6_a_addr}/${veth6_mask} dev veth_a
104 ${ns_b} ip addr add ${veth6_b_addr}/${veth6_mask} dev veth_b
105
106 ${ns_a} ip link set veth_a up
107 ${ns_b} ip link set veth_b up
108}
109
110setup_vti() {
111 proto=${1}
112 veth_a_addr="${2}"
113 veth_b_addr="${3}"
114 vti_a_addr="${4}"
115 vti_b_addr="${5}"
116 vti_mask=${6}
117
118 [ ${proto} -eq 6 ] && vti_type="vti6" || vti_type="vti"
119
120 ${ns_a} ip link add vti${proto}_a type ${vti_type} local ${veth_a_addr} remote ${veth_b_addr} key 10 || return 1
121 ${ns_b} ip link add vti${proto}_b type ${vti_type} local ${veth_b_addr} remote ${veth_a_addr} key 10
122
123 ${ns_a} ip addr add ${vti_a_addr}/${vti_mask} dev vti${proto}_a
124 ${ns_b} ip addr add ${vti_b_addr}/${vti_mask} dev vti${proto}_b
125
126 ${ns_a} ip link set vti${proto}_a up
127 ${ns_b} ip link set vti${proto}_b up
128
129 sleep 1
130}
131
132setup_vti4() {
133 setup_vti 4 ${veth4_a_addr} ${veth4_b_addr} ${vti4_a_addr} ${vti4_b_addr} ${vti4_mask}
134}
135
136setup_vti6() {
137 setup_vti 6 ${veth6_a_addr} ${veth6_b_addr} ${vti6_a_addr} ${vti6_b_addr} ${vti6_mask}
138}
139
140setup_xfrm() {
141 proto=${1}
142 veth_a_addr="${2}"
143 veth_b_addr="${3}"
144
145 ${ns_a} ip -${proto} xfrm state add src ${veth_a_addr} dst ${veth_b_addr} spi 0x1000 proto esp aead "rfc4106(gcm(aes))" 0x0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f 128 mode tunnel || return 1
146 ${ns_a} ip -${proto} xfrm state add src ${veth_b_addr} dst ${veth_a_addr} spi 0x1001 proto esp aead "rfc4106(gcm(aes))" 0x0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f 128 mode tunnel
147 ${ns_a} ip -${proto} xfrm policy add dir out mark 10 tmpl src ${veth_a_addr} dst ${veth_b_addr} proto esp mode tunnel
148 ${ns_a} ip -${proto} xfrm policy add dir in mark 10 tmpl src ${veth_b_addr} dst ${veth_a_addr} proto esp mode tunnel
149
150 ${ns_b} ip -${proto} xfrm state add src ${veth_a_addr} dst ${veth_b_addr} spi 0x1000 proto esp aead "rfc4106(gcm(aes))" 0x0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f 128 mode tunnel
151 ${ns_b} ip -${proto} xfrm state add src ${veth_b_addr} dst ${veth_a_addr} spi 0x1001 proto esp aead "rfc4106(gcm(aes))" 0x0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f 128 mode tunnel
152 ${ns_b} ip -${proto} xfrm policy add dir out mark 10 tmpl src ${veth_b_addr} dst ${veth_a_addr} proto esp mode tunnel
153 ${ns_b} ip -${proto} xfrm policy add dir in mark 10 tmpl src ${veth_a_addr} dst ${veth_b_addr} proto esp mode tunnel
154}
155
156setup_xfrm4() {
157 setup_xfrm 4 ${veth4_a_addr} ${veth4_b_addr}
158}
159
160setup_xfrm6() {
161 setup_xfrm 6 ${veth6_a_addr} ${veth6_b_addr}
162}
163
164setup() {
165 [ "$(id -u)" -ne 0 ] && echo " need to run as root" && return 1
166
167 cleanup_done=0
168 for arg do
169 eval setup_${arg} || { echo " ${arg} not supported"; return 1; }
170 done
171}
172
173cleanup() {
174 [ ${cleanup_done} -eq 1 ] && return
175 ip netns del ${NS_A} 2 > /dev/null
176 ip netns del ${NS_B} 2 > /dev/null
177 cleanup_done=1
178}
179
180mtu() {
181 ns_cmd="${1}"
182 dev="${2}"
183 mtu="${3}"
184
185 ${ns_cmd} ip link set dev ${dev} mtu ${mtu}
186}
187
188mtu_parse() {
189 input="${1}"
190
191 next=0
192 for i in ${input}; do
193 [ ${next} -eq 1 ] && echo "${i}" && return
194 [ "${i}" = "mtu" ] && next=1
195 done
196}
197
198link_get() {
199 ns_cmd="${1}"
200 name="${2}"
201
202 ${ns_cmd} ip link show dev "${name}"
203}
204
205link_get_mtu() {
206 ns_cmd="${1}"
207 name="${2}"
208
209 mtu_parse "$(link_get "${ns_cmd}" ${name})"
210}
211
212route_get_dst_exception() {
213 ns_cmd="${1}"
214 dst="${2}"
215
216 ${ns_cmd} ip route get "${dst}"
217}
218
219route_get_dst_pmtu_from_exception() {
220 ns_cmd="${1}"
221 dst="${2}"
222
223 mtu_parse "$(route_get_dst_exception "${ns_cmd}" ${dst})"
224}
225
226test_pmtu_vti4_exception() {
227 setup namespaces veth vti4 xfrm4 || return 2
228
229 veth_mtu=1500
230 vti_mtu=$((veth_mtu - 20))
231
232 # SPI SN IV ICV pad length next header
233 esp_payload_rfc4106=$((vti_mtu - 4 - 4 - 8 - 16 - 1 - 1))
234 ping_payload=$((esp_payload_rfc4106 - 28))
235
236 mtu "${ns_a}" veth_a ${veth_mtu}
237 mtu "${ns_b}" veth_b ${veth_mtu}
238 mtu "${ns_a}" vti4_a ${vti_mtu}
239 mtu "${ns_b}" vti4_b ${vti_mtu}
240
241 # Send DF packet without exceeding link layer MTU, check that no
242 # exception is created
243 ${ns_a} ping -q -M want -i 0.1 -w 2 -s ${ping_payload} ${vti4_b_addr} > /dev/null
244 pmtu="$(route_get_dst_pmtu_from_exception "${ns_a}" ${vti4_b_addr})"
245 if [ "${pmtu}" != "" ]; then
246 err " unexpected exception created with PMTU ${pmtu} for IP payload length ${esp_payload_rfc4106}"
247 return 1
248 fi
249
250 # Now exceed link layer MTU by one byte, check that exception is created
251 ${ns_a} ping -q -M want -i 0.1 -w 2 -s $((ping_payload + 1)) ${vti4_b_addr} > /dev/null
252 pmtu="$(route_get_dst_pmtu_from_exception "${ns_a}" ${vti4_b_addr})"
253 if [ "${pmtu}" = "" ]; then
254 err " exception not created for IP payload length $((esp_payload_rfc4106 + 1))"
255 return 1
256 fi
257
258 # ...with the right PMTU value
259 if [ ${pmtu} -ne ${esp_payload_rfc4106} ]; then
260 err " wrong PMTU ${pmtu} in exception, expected: ${esp_payload_rfc4106}"
261 return 1
262 fi
263}
264
265test_pmtu_vti6_exception() {
266 setup namespaces veth vti6 xfrm6 || return 2
267 fail=0
268
269 # Create route exception by exceeding link layer MTU
270 mtu "${ns_a}" veth_a 4000
271 mtu "${ns_b}" veth_b 4000
272 mtu "${ns_a}" vti6_a 5000
273 mtu "${ns_b}" vti6_b 5000
274 ${ns_a} ping6 -q -i 0.1 -w 2 -s 60000 ${vti6_b_addr} > /dev/null
275
276 # Check that exception was created
277 if [ "$(route_get_dst_pmtu_from_exception "${ns_a}" ${vti6_b_addr})" = "" ]; then
278 err " tunnel exceeding link layer MTU didn't create route exception"
279 return 1
280 fi
281
282 # Decrease tunnel MTU, check for PMTU decrease in route exception
283 mtu "${ns_a}" vti6_a 3000
284
285 if [ "$(route_get_dst_pmtu_from_exception "${ns_a}" ${vti6_b_addr})" -ne 3000 ]; then
286 err " decreasing tunnel MTU didn't decrease route exception PMTU"
287 fail=1
288 fi
289
290 # Increase tunnel MTU, check for PMTU increase in route exception
291 mtu "${ns_a}" vti6_a 9000
292 if [ "$(route_get_dst_pmtu_from_exception "${ns_a}" ${vti6_b_addr})" -ne 9000 ]; then
293 err " increasing tunnel MTU didn't increase route exception PMTU"
294 fail=1
295 fi
296
297 return ${fail}
298}
299
300test_pmtu_vti4_default_mtu() {
301 setup namespaces veth vti4 || return 2
302
303 # Check that MTU of vti device is MTU of veth minus IPv4 header length
304 veth_mtu="$(link_get_mtu "${ns_a}" veth_a)"
305 vti4_mtu="$(link_get_mtu "${ns_a}" vti4_a)"
306 if [ $((veth_mtu - vti4_mtu)) -ne 20 ]; then
307 err " vti MTU ${vti4_mtu} is not veth MTU ${veth_mtu} minus IPv4 header length"
308 return 1
309 fi
310}
311
312test_pmtu_vti6_default_mtu() {
313 setup namespaces veth vti6 || return 2
314
315 # Check that MTU of vti device is MTU of veth minus IPv6 header length
316 veth_mtu="$(link_get_mtu "${ns_a}" veth_a)"
317 vti6_mtu="$(link_get_mtu "${ns_a}" vti6_a)"
318 if [ $((veth_mtu - vti6_mtu)) -ne 40 ]; then
319 err " vti MTU ${vti6_mtu} is not veth MTU ${veth_mtu} minus IPv6 header length"
320 return 1
321 fi
322}
323
324test_pmtu_vti4_link_add_mtu() {
325 setup namespaces || return 2
326
327 ${ns_a} ip link add vti4_a type vti local ${veth4_a_addr} remote ${veth4_b_addr} key 10
328 [ $? -ne 0 ] && err " vti not supported" && return 2
329 ${ns_a} ip link del vti4_a
330
331 fail=0
332
333 min=68
334 max=$((65528 - 20))
335 # Check invalid values first
336 for v in $((min - 1)) $((max + 1)); do
337 ${ns_a} ip link add vti4_a mtu ${v} type vti local ${veth4_a_addr} remote ${veth4_b_addr} key 10 2>/dev/null
338 # This can fail, or MTU can be adjusted to a proper value
339 [ $? -ne 0 ] && continue
340 mtu="$(link_get_mtu "${ns_a}" vti4_a)"
341 if [ ${mtu} -lt ${min} -o ${mtu} -gt ${max} ]; then
342 err " vti tunnel created with invalid MTU ${mtu}"
343 fail=1
344 fi
345 ${ns_a} ip link del vti4_a
346 done
347
348 # Now check valid values
349 for v in ${min} 1300 ${max}; do
350 ${ns_a} ip link add vti4_a mtu ${v} type vti local ${veth4_a_addr} remote ${veth4_b_addr} key 10
351 mtu="$(link_get_mtu "${ns_a}" vti4_a)"
352 ${ns_a} ip link del vti4_a
353 if [ "${mtu}" != "${v}" ]; then
354 err " vti MTU ${mtu} doesn't match configured value ${v}"
355 fail=1
356 fi
357 done
358
359 return ${fail}
360}
361
362test_pmtu_vti6_link_add_mtu() {
363 setup namespaces || return 2
364
365 ${ns_a} ip link add vti6_a type vti6 local ${veth6_a_addr} remote ${veth6_b_addr} key 10
366 [ $? -ne 0 ] && err " vti6 not supported" && return 2
367 ${ns_a} ip link del vti6_a
368
369 fail=0
370
371 min=1280
372 max=$((65535 - 40))
373 # Check invalid values first
374 for v in $((min - 1)) $((max + 1)); do
375 ${ns_a} ip link add vti6_a mtu ${v} type vti6 local ${veth6_a_addr} remote ${veth6_b_addr} key 10 2>/dev/null
376 # This can fail, or MTU can be adjusted to a proper value
377 [ $? -ne 0 ] && continue
378 mtu="$(link_get_mtu "${ns_a}" vti6_a)"
379 if [ ${mtu} -lt ${min} -o ${mtu} -gt ${max} ]; then
380 err " vti6 tunnel created with invalid MTU ${v}"
381 fail=1
382 fi
383 ${ns_a} ip link del vti6_a
384 done
385
386 # Now check valid values
387 for v in 1280 1300 $((65535 - 40)); do
388 ${ns_a} ip link add vti6_a mtu ${v} type vti6 local ${veth6_a_addr} remote ${veth6_b_addr} key 10
389 mtu="$(link_get_mtu "${ns_a}" vti6_a)"
390 ${ns_a} ip link del vti6_a
391 if [ "${mtu}" != "${v}" ]; then
392 err " vti6 MTU ${mtu} doesn't match configured value ${v}"
393 fail=1
394 fi
395 done
396
397 return ${fail}
398}
399
400test_pmtu_vti6_link_change_mtu() {
401 setup namespaces || return 2
402
403 ${ns_a} ip link add dummy0 mtu 1500 type dummy
404 [ $? -ne 0 ] && err " dummy not supported" && return 2
405 ${ns_a} ip link add dummy1 mtu 3000 type dummy
406 ${ns_a} ip link set dummy0 up
407 ${ns_a} ip link set dummy1 up
408
409 ${ns_a} ip addr add ${dummy6_0_addr}/${dummy6_mask} dev dummy0
410 ${ns_a} ip addr add ${dummy6_1_addr}/${dummy6_mask} dev dummy1
411
412 fail=0
413
414 # Create vti6 interface bound to device, passing MTU, check it
415 ${ns_a} ip link add vti6_a mtu 1300 type vti6 remote ${dummy6_0_addr} local ${dummy6_0_addr}
416 mtu="$(link_get_mtu "${ns_a}" vti6_a)"
417 if [ ${mtu} -ne 1300 ]; then
418 err " vti6 MTU ${mtu} doesn't match configured value 1300"
419 fail=1
420 fi
421
422 # Move to another device with different MTU, without passing MTU, check
423 # MTU is adjusted
424 ${ns_a} ip link set vti6_a type vti6 remote ${dummy6_1_addr} local ${dummy6_1_addr}
425 mtu="$(link_get_mtu "${ns_a}" vti6_a)"
426 if [ ${mtu} -ne $((3000 - 40)) ]; then
427 err " vti MTU ${mtu} is not dummy MTU 3000 minus IPv6 header length"
428 fail=1
429 fi
430
431 # Move it back, passing MTU, check MTU is not overridden
432 ${ns_a} ip link set vti6_a mtu 1280 type vti6 remote ${dummy6_0_addr} local ${dummy6_0_addr}
433 mtu="$(link_get_mtu "${ns_a}" vti6_a)"
434 if [ ${mtu} -ne 1280 ]; then
435 err " vti6 MTU ${mtu} doesn't match configured value 1280"
436 fail=1
437 fi
438
439 return ${fail}
440}
441
442trap cleanup EXIT
443
444exitcode=0
445desc=0
446IFS="
447"
448for t in ${tests}; do
449 [ $desc -eq 0 ] && name="${t}" && desc=1 && continue || desc=0
450
451 (
452 unset IFS
453 eval test_${name}
454 ret=$?
455 cleanup
456
457 if [ $ret -eq 0 ]; then
458 printf "TEST: %-60s [ OK ]\n" "${t}"
459 elif [ $ret -eq 1 ]; then
460 printf "TEST: %-60s [FAIL]\n" "${t}"
461 err_flush
462 exit 1
463 elif [ $ret -eq 2 ]; then
464 printf "TEST: %-60s [SKIP]\n" "${t}"
465 err_flush
466 fi
467 )
468 [ $? -ne 0 ] && exitcode=1
469done
470
471exit ${exitcode}
diff --git a/tools/testing/selftests/net/psock_fanout.c b/tools/testing/selftests/net/psock_fanout.c
index 989f917068d1..bd9b9632c72b 100644
--- a/tools/testing/selftests/net/psock_fanout.c
+++ b/tools/testing/selftests/net/psock_fanout.c
@@ -50,6 +50,7 @@
50#include <linux/filter.h> 50#include <linux/filter.h>
51#include <linux/bpf.h> 51#include <linux/bpf.h>
52#include <linux/if_packet.h> 52#include <linux/if_packet.h>
53#include <net/if.h>
53#include <net/ethernet.h> 54#include <net/ethernet.h>
54#include <netinet/ip.h> 55#include <netinet/ip.h>
55#include <netinet/udp.h> 56#include <netinet/udp.h>
@@ -73,14 +74,29 @@
73 * @return -1 if mode is bad, a valid socket otherwise */ 74 * @return -1 if mode is bad, a valid socket otherwise */
74static int sock_fanout_open(uint16_t typeflags, uint16_t group_id) 75static int sock_fanout_open(uint16_t typeflags, uint16_t group_id)
75{ 76{
77 struct sockaddr_ll addr = {0};
76 int fd, val; 78 int fd, val;
77 79
78 fd = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_IP)); 80 fd = socket(PF_PACKET, SOCK_RAW, 0);
79 if (fd < 0) { 81 if (fd < 0) {
80 perror("socket packet"); 82 perror("socket packet");
81 exit(1); 83 exit(1);
82 } 84 }
83 85
86 pair_udp_setfilter(fd);
87
88 addr.sll_family = AF_PACKET;
89 addr.sll_protocol = htons(ETH_P_IP);
90 addr.sll_ifindex = if_nametoindex("lo");
91 if (addr.sll_ifindex == 0) {
92 perror("if_nametoindex");
93 exit(1);
94 }
95 if (bind(fd, (void *) &addr, sizeof(addr))) {
96 perror("bind packet");
97 exit(1);
98 }
99
84 val = (((int) typeflags) << 16) | group_id; 100 val = (((int) typeflags) << 16) | group_id;
85 if (setsockopt(fd, SOL_PACKET, PACKET_FANOUT, &val, sizeof(val))) { 101 if (setsockopt(fd, SOL_PACKET, PACKET_FANOUT, &val, sizeof(val))) {
86 if (close(fd)) { 102 if (close(fd)) {
@@ -90,7 +106,6 @@ static int sock_fanout_open(uint16_t typeflags, uint16_t group_id)
90 return -1; 106 return -1;
91 } 107 }
92 108
93 pair_udp_setfilter(fd);
94 return fd; 109 return fd;
95} 110}
96 111
@@ -128,6 +143,8 @@ static void sock_fanout_getopts(int fd, uint16_t *typeflags, uint16_t *group_id)
128 143
129static void sock_fanout_set_ebpf(int fd) 144static void sock_fanout_set_ebpf(int fd)
130{ 145{
146 static char log_buf[65536];
147
131 const int len_off = __builtin_offsetof(struct __sk_buff, len); 148 const int len_off = __builtin_offsetof(struct __sk_buff, len);
132 struct bpf_insn prog[] = { 149 struct bpf_insn prog[] = {
133 { BPF_ALU64 | BPF_MOV | BPF_X, 6, 1, 0, 0 }, 150 { BPF_ALU64 | BPF_MOV | BPF_X, 6, 1, 0, 0 },
@@ -140,7 +157,6 @@ static void sock_fanout_set_ebpf(int fd)
140 { BPF_ALU | BPF_MOV | BPF_K, 0, 0, 0, 0 }, 157 { BPF_ALU | BPF_MOV | BPF_K, 0, 0, 0, 0 },
141 { BPF_JMP | BPF_EXIT, 0, 0, 0, 0 } 158 { BPF_JMP | BPF_EXIT, 0, 0, 0, 0 }
142 }; 159 };
143 char log_buf[512];
144 union bpf_attr attr; 160 union bpf_attr attr;
145 int pfd; 161 int pfd;
146 162
@@ -228,7 +244,7 @@ static int sock_fanout_read(int fds[], char *rings[], const int expect[])
228 244
229 if ((!(ret[0] == expect[0] && ret[1] == expect[1])) && 245 if ((!(ret[0] == expect[0] && ret[1] == expect[1])) &&
230 (!(ret[0] == expect[1] && ret[1] == expect[0]))) { 246 (!(ret[0] == expect[1] && ret[1] == expect[0]))) {
231 fprintf(stderr, "ERROR: incorrect queue lengths\n"); 247 fprintf(stderr, "warning: incorrect queue lengths\n");
232 return 1; 248 return 1;
233 } 249 }
234 250
@@ -347,7 +363,8 @@ static int test_datapath(uint16_t typeflags, int port_off,
347 uint8_t type = typeflags & 0xFF; 363 uint8_t type = typeflags & 0xFF;
348 int fds[2], fds_udp[2][2], ret; 364 int fds[2], fds_udp[2][2], ret;
349 365
350 fprintf(stderr, "test: datapath 0x%hx\n", typeflags); 366 fprintf(stderr, "\ntest: datapath 0x%hx ports %hu,%hu\n",
367 typeflags, PORT_BASE, PORT_BASE + port_off);
351 368
352 fds[0] = sock_fanout_open(typeflags, 0); 369 fds[0] = sock_fanout_open(typeflags, 0);
353 fds[1] = sock_fanout_open(typeflags, 0); 370 fds[1] = sock_fanout_open(typeflags, 0);
@@ -418,7 +435,7 @@ int main(int argc, char **argv)
418 const int expect_cpu1[2][2] = { { 0, 20 }, { 0, 20 } }; 435 const int expect_cpu1[2][2] = { { 0, 20 }, { 0, 20 } };
419 const int expect_bpf[2][2] = { { 15, 5 }, { 15, 20 } }; 436 const int expect_bpf[2][2] = { { 15, 5 }, { 15, 20 } };
420 const int expect_uniqueid[2][2] = { { 20, 20}, { 20, 20 } }; 437 const int expect_uniqueid[2][2] = { { 20, 20}, { 20, 20 } };
421 int port_off = 2, tries = 5, ret; 438 int port_off = 2, tries = 20, ret;
422 439
423 test_control_single(); 440 test_control_single();
424 test_control_group(); 441 test_control_group();
@@ -427,10 +444,14 @@ int main(int argc, char **argv)
427 /* find a set of ports that do not collide onto the same socket */ 444 /* find a set of ports that do not collide onto the same socket */
428 ret = test_datapath(PACKET_FANOUT_HASH, port_off, 445 ret = test_datapath(PACKET_FANOUT_HASH, port_off,
429 expect_hash[0], expect_hash[1]); 446 expect_hash[0], expect_hash[1]);
430 while (ret && tries--) { 447 while (ret) {
431 fprintf(stderr, "info: trying alternate ports (%d)\n", tries); 448 fprintf(stderr, "info: trying alternate ports (%d)\n", tries);
432 ret = test_datapath(PACKET_FANOUT_HASH, ++port_off, 449 ret = test_datapath(PACKET_FANOUT_HASH, ++port_off,
433 expect_hash[0], expect_hash[1]); 450 expect_hash[0], expect_hash[1]);
451 if (!--tries) {
452 fprintf(stderr, "too many collisions\n");
453 return 1;
454 }
434 } 455 }
435 456
436 ret |= test_datapath(PACKET_FANOUT_HASH | PACKET_FANOUT_FLAG_ROLLOVER, 457 ret |= test_datapath(PACKET_FANOUT_HASH | PACKET_FANOUT_FLAG_ROLLOVER,
diff --git a/tools/testing/selftests/net/rtnetlink.sh b/tools/testing/selftests/net/rtnetlink.sh
index a622eeecc3a6..e6f485235435 100755
--- a/tools/testing/selftests/net/rtnetlink.sh
+++ b/tools/testing/selftests/net/rtnetlink.sh
@@ -517,6 +517,7 @@ kci_test_gretap()
517 ip link help gretap 2>&1 | grep -q "^Usage:" 517 ip link help gretap 2>&1 | grep -q "^Usage:"
518 if [ $? -ne 0 ];then 518 if [ $? -ne 0 ];then
519 echo "SKIP: gretap: iproute2 too old" 519 echo "SKIP: gretap: iproute2 too old"
520 ip netns del "$testns"
520 return 1 521 return 1
521 fi 522 fi
522 523
@@ -543,6 +544,7 @@ kci_test_gretap()
543 544
544 if [ $ret -ne 0 ]; then 545 if [ $ret -ne 0 ]; then
545 echo "FAIL: gretap" 546 echo "FAIL: gretap"
547 ip netns del "$testns"
546 return 1 548 return 1
547 fi 549 fi
548 echo "PASS: gretap" 550 echo "PASS: gretap"
@@ -565,6 +567,7 @@ kci_test_ip6gretap()
565 ip link help ip6gretap 2>&1 | grep -q "^Usage:" 567 ip link help ip6gretap 2>&1 | grep -q "^Usage:"
566 if [ $? -ne 0 ];then 568 if [ $? -ne 0 ];then
567 echo "SKIP: ip6gretap: iproute2 too old" 569 echo "SKIP: ip6gretap: iproute2 too old"
570 ip netns del "$testns"
568 return 1 571 return 1
569 fi 572 fi
570 573
@@ -591,6 +594,7 @@ kci_test_ip6gretap()
591 594
592 if [ $ret -ne 0 ]; then 595 if [ $ret -ne 0 ]; then
593 echo "FAIL: ip6gretap" 596 echo "FAIL: ip6gretap"
597 ip netns del "$testns"
594 return 1 598 return 1
595 fi 599 fi
596 echo "PASS: ip6gretap" 600 echo "PASS: ip6gretap"
@@ -655,6 +659,7 @@ kci_test_erspan()
655 659
656 if [ $ret -ne 0 ]; then 660 if [ $ret -ne 0 ]; then
657 echo "FAIL: erspan" 661 echo "FAIL: erspan"
662 ip netns del "$testns"
658 return 1 663 return 1
659 fi 664 fi
660 echo "PASS: erspan" 665 echo "PASS: erspan"
@@ -720,6 +725,7 @@ kci_test_ip6erspan()
720 725
721 if [ $ret -ne 0 ]; then 726 if [ $ret -ne 0 ]; then
722 echo "FAIL: ip6erspan" 727 echo "FAIL: ip6erspan"
728 ip netns del "$testns"
723 return 1 729 return 1
724 fi 730 fi
725 echo "PASS: ip6erspan" 731 echo "PASS: ip6erspan"
diff --git a/tools/testing/selftests/net/run_afpackettests b/tools/testing/selftests/net/run_afpackettests
index 21fe149e3de1..bea079edc278 100755
--- a/tools/testing/selftests/net/run_afpackettests
+++ b/tools/testing/selftests/net/run_afpackettests
@@ -9,7 +9,7 @@ fi
9echo "--------------------" 9echo "--------------------"
10echo "running psock_fanout test" 10echo "running psock_fanout test"
11echo "--------------------" 11echo "--------------------"
12./psock_fanout 12./in_netns.sh ./psock_fanout
13if [ $? -ne 0 ]; then 13if [ $? -ne 0 ]; then
14 echo "[FAIL]" 14 echo "[FAIL]"
15else 15else
@@ -19,7 +19,7 @@ fi
19echo "--------------------" 19echo "--------------------"
20echo "running psock_tpacket test" 20echo "running psock_tpacket test"
21echo "--------------------" 21echo "--------------------"
22./psock_tpacket 22./in_netns.sh ./psock_tpacket
23if [ $? -ne 0 ]; then 23if [ $? -ne 0 ]; then
24 echo "[FAIL]" 24 echo "[FAIL]"
25else 25else
diff --git a/tools/testing/selftests/networking/timestamping/txtimestamp.c b/tools/testing/selftests/networking/timestamping/txtimestamp.c
index 5df07047ca86..81a98a240456 100644
--- a/tools/testing/selftests/networking/timestamping/txtimestamp.c
+++ b/tools/testing/selftests/networking/timestamping/txtimestamp.c
@@ -68,9 +68,11 @@ static int cfg_num_pkts = 4;
68static int do_ipv4 = 1; 68static int do_ipv4 = 1;
69static int do_ipv6 = 1; 69static int do_ipv6 = 1;
70static int cfg_payload_len = 10; 70static int cfg_payload_len = 10;
71static int cfg_poll_timeout = 100;
71static bool cfg_show_payload; 72static bool cfg_show_payload;
72static bool cfg_do_pktinfo; 73static bool cfg_do_pktinfo;
73static bool cfg_loop_nodata; 74static bool cfg_loop_nodata;
75static bool cfg_no_delay;
74static uint16_t dest_port = 9000; 76static uint16_t dest_port = 9000;
75 77
76static struct sockaddr_in daddr; 78static struct sockaddr_in daddr;
@@ -171,7 +173,7 @@ static void __poll(int fd)
171 173
172 memset(&pollfd, 0, sizeof(pollfd)); 174 memset(&pollfd, 0, sizeof(pollfd));
173 pollfd.fd = fd; 175 pollfd.fd = fd;
174 ret = poll(&pollfd, 1, 100); 176 ret = poll(&pollfd, 1, cfg_poll_timeout);
175 if (ret != 1) 177 if (ret != 1)
176 error(1, errno, "poll"); 178 error(1, errno, "poll");
177} 179}
@@ -371,7 +373,8 @@ static void do_test(int family, unsigned int opt)
371 error(1, errno, "send"); 373 error(1, errno, "send");
372 374
373 /* wait for all errors to be queued, else ACKs arrive OOO */ 375 /* wait for all errors to be queued, else ACKs arrive OOO */
374 usleep(50 * 1000); 376 if (!cfg_no_delay)
377 usleep(50 * 1000);
375 378
376 __poll(fd); 379 __poll(fd);
377 380
@@ -392,6 +395,9 @@ static void __attribute__((noreturn)) usage(const char *filepath)
392 " -4: only IPv4\n" 395 " -4: only IPv4\n"
393 " -6: only IPv6\n" 396 " -6: only IPv6\n"
394 " -h: show this message\n" 397 " -h: show this message\n"
398 " -c N: number of packets for each test\n"
399 " -D: no delay between packets\n"
400 " -F: poll() waits forever for an event\n"
395 " -I: request PKTINFO\n" 401 " -I: request PKTINFO\n"
396 " -l N: send N bytes at a time\n" 402 " -l N: send N bytes at a time\n"
397 " -n: set no-payload option\n" 403 " -n: set no-payload option\n"
@@ -409,7 +415,7 @@ static void parse_opt(int argc, char **argv)
409 int proto_count = 0; 415 int proto_count = 0;
410 char c; 416 char c;
411 417
412 while ((c = getopt(argc, argv, "46hIl:np:rRux")) != -1) { 418 while ((c = getopt(argc, argv, "46c:DFhIl:np:rRux")) != -1) {
413 switch (c) { 419 switch (c) {
414 case '4': 420 case '4':
415 do_ipv6 = 0; 421 do_ipv6 = 0;
@@ -417,6 +423,15 @@ static void parse_opt(int argc, char **argv)
417 case '6': 423 case '6':
418 do_ipv4 = 0; 424 do_ipv4 = 0;
419 break; 425 break;
426 case 'c':
427 cfg_num_pkts = strtoul(optarg, NULL, 10);
428 break;
429 case 'D':
430 cfg_no_delay = true;
431 break;
432 case 'F':
433 cfg_poll_timeout = -1;
434 break;
420 case 'I': 435 case 'I':
421 cfg_do_pktinfo = true; 436 cfg_do_pktinfo = true;
422 break; 437 break;
diff --git a/tools/testing/selftests/tc-testing/README b/tools/testing/selftests/tc-testing/README
index 970ff294fec8..3a0336782d2d 100644
--- a/tools/testing/selftests/tc-testing/README
+++ b/tools/testing/selftests/tc-testing/README
@@ -14,11 +14,11 @@ REQUIREMENTS
14 14
15* The kernel must have network namespace support 15* The kernel must have network namespace support
16 16
17* The kernel must have veth support available, as a veth pair is created 17* The kernel must have veth support available, as a veth pair is created
18 prior to running the tests. 18 prior to running the tests.
19 19
20* All tc-related features must be built in or available as modules. 20* All tc-related features being tested must be built in or available as
21 To check what is required in current setup run: 21 modules. To check what is required in current setup run:
22 ./tdc.py -c 22 ./tdc.py -c
23 23
24 Note: 24 Note:
@@ -44,10 +44,13 @@ using the -p option when running tdc:
44RUNNING TDC 44RUNNING TDC
45----------- 45-----------
46 46
47To use tdc, root privileges are required. tdc will not run otherwise. 47To use tdc, root privileges are required. This is because the
48commands being tested must be run as root. The code that enforces
49execution by root uid has been moved into a plugin (see PLUGIN
50ARCHITECTURE, below).
48 51
49All tests are executed inside a network namespace to prevent conflicts 52If nsPlugin is linked, all tests are executed inside a network
50within the host. 53namespace to prevent conflicts within the host.
51 54
52Running tdc without any arguments will run all tests. Refer to the section 55Running tdc without any arguments will run all tests. Refer to the section
53on command line arguments for more information, or run: 56on command line arguments for more information, or run:
@@ -59,6 +62,33 @@ output captured from the failing test will be printed immediately following
59the failed test in the TAP output. 62the failed test in the TAP output.
60 63
61 64
65OVERVIEW OF TDC EXECUTION
66-------------------------
67
68One run of tests is considered a "test suite" (this will be refined in the
69future). A test suite has one or more test cases in it.
70
71A test case has four stages:
72
73 - setup
74 - execute
75 - verify
76 - teardown
77
78The setup and teardown stages can run zero or more commands. The setup
79stage does some setup if the test needs it. The teardown stage undoes
80the setup and returns the system to a "neutral" state so any other test
81can be run next. These two stages require any commands run to return
82success, but do not otherwise verify the results.
83
84The execute and verify stages each run one command. The execute stage
85tests the return code against one or more acceptable values. The
86verify stage checks the return code for success, and also compares
87the stdout with a regular expression.
88
89Each of the commands in any stage will run in a shell instance.
90
91
62USER-DEFINED CONSTANTS 92USER-DEFINED CONSTANTS
63---------------------- 93----------------------
64 94
@@ -70,23 +100,132 @@ executed as part of the test. More will be added as test cases require.
70Example: 100Example:
71 $TC qdisc add dev $DEV1 ingress 101 $TC qdisc add dev $DEV1 ingress
72 102
103The NAMES values are used to substitute into the commands in the test cases.
104
73 105
74COMMAND LINE ARGUMENTS 106COMMAND LINE ARGUMENTS
75---------------------- 107----------------------
76 108
77Run tdc.py -h to see the full list of available arguments. 109Run tdc.py -h to see the full list of available arguments.
78 110
79-p PATH Specify the tc executable located at PATH to be used on this 111usage: tdc.py [-h] [-p PATH] [-D DIR [DIR ...]] [-f FILE [FILE ...]]
80 test run 112 [-c [CATG [CATG ...]]] [-e ID [ID ...]] [-l] [-s] [-i] [-v]
81-c Show the available test case categories in this test file 113 [-d DEVICE] [-n NS] [-V]
82-c CATEGORY Run only tests that belong to CATEGORY 114
83-f FILE Read test cases from the JSON file named FILE 115Linux TC unit tests
84-l [CATEGORY] List all test cases in the JSON file. If CATEGORY is 116
85 specified, list test cases matching that category. 117optional arguments:
86-s ID Show the test case matching ID 118 -h, --help show this help message and exit
87-e ID Execute the test case identified by ID 119 -p PATH, --path PATH The full path to the tc executable to use
88-i Generate unique ID numbers for test cases with no existing 120 -v, --verbose Show the commands that are being run
89 ID number 121 -d DEVICE, --device DEVICE
122 Execute the test case in flower category
123
124selection:
125 select which test cases: files plus directories; filtered by categories
126 plus testids
127
128 -D DIR [DIR ...], --directory DIR [DIR ...]
129 Collect tests from the specified directory(ies)
130 (default [tc-tests])
131 -f FILE [FILE ...], --file FILE [FILE ...]
132 Run tests from the specified file(s)
133 -c [CATG [CATG ...]], --category [CATG [CATG ...]]
134 Run tests only from the specified category/ies, or if
135 no category/ies is/are specified, list known
136 categories.
137 -e ID [ID ...], --execute ID [ID ...]
138 Execute the specified test cases with specified IDs
139
140action:
141 select action to perform on selected test cases
142
143 -l, --list List all test cases, or those only within the
144 specified category
145 -s, --show Display the selected test cases
146 -i, --id Generate ID numbers for new test cases
147
148netns:
149 options for nsPlugin(run commands in net namespace)
150
151 -n NS, --namespace NS
152 Run commands in namespace NS
153
154valgrind:
155 options for valgrindPlugin (run command under test under Valgrind)
156
157 -V, --valgrind Run commands under valgrind
158
159
160PLUGIN ARCHITECTURE
161-------------------
162
163There is now a plugin architecture, and some of the functionality that
164was in the tdc.py script has been moved into the plugins.
165
166The plugins are in the directory plugin-lib. The are executed from
167directory plugins. Put symbolic links from plugins to plugin-lib,
168and name them according to the order you want them to run.
169
170Example:
171
172bjb@bee:~/work/tc-testing$ ls -l plugins
173total 4
174lrwxrwxrwx 1 bjb bjb 27 Oct 4 16:12 10-rootPlugin.py -> ../plugin-lib/rootPlugin.py
175lrwxrwxrwx 1 bjb bjb 25 Oct 12 17:55 20-nsPlugin.py -> ../plugin-lib/nsPlugin.py
176-rwxr-xr-x 1 bjb bjb 0 Sep 29 15:56 __init__.py
177
178The plugins are a subclass of TdcPlugin, defined in TdcPlugin.py and
179must be called "SubPlugin" so tdc can find them. They are
180distinguished from each other in the python program by their module
181name.
182
183This base class supplies "hooks" to run extra functions. These hooks are as follows:
184
185pre- and post-suite
186pre- and post-case
187pre- and post-execute stage
188adjust-command (runs in all stages and receives the stage name)
189
190The pre-suite hook receives the number of tests and an array of test ids.
191This allows you to dump out the list of skipped tests in the event of a
192failure during setup or teardown stage.
193
194The pre-case hook receives the ordinal number and test id of the current test.
195
196The adjust-command hook receives the stage id (see list below) and the
197full command to be executed. This allows for last-minute adjustment
198of the command.
199
200The stages are identified by the following strings:
201
202 - pre (pre-suite)
203 - setup
204 - command
205 - verify
206 - teardown
207 - post (post-suite)
208
209
210To write a plugin, you need to inherit from TdcPlugin in
211TdcPlugin.py. To use the plugin, you have to put the
212implementation file in plugin-lib, and add a symbolic link to it from
213plugins. It will be detected at run time and invoked at the
214appropriate times. There are a few examples in the plugin-lib
215directory:
216
217 - rootPlugin.py:
218 implements the enforcement of running as root
219 - nsPlugin.py:
220 sets up a network namespace and runs all commands in that namespace
221 - valgrindPlugin.py
222 runs each command in the execute stage under valgrind,
223 and checks for leaks.
224 This plugin will output an extra test for each test in the test file,
225 one is the existing output as to whether the test passed or failed,
226 and the other is a test whether the command leaked memory or not.
227 (This one is a preliminary version, it may not work quite right yet,
228 but the overall template is there and it should only need tweaks.)
90 229
91 230
92ACKNOWLEDGEMENTS 231ACKNOWLEDGEMENTS
diff --git a/tools/testing/selftests/tc-testing/TODO.txt b/tools/testing/selftests/tc-testing/TODO.txt
index 6a266d811a78..c40698557e2f 100644
--- a/tools/testing/selftests/tc-testing/TODO.txt
+++ b/tools/testing/selftests/tc-testing/TODO.txt
@@ -5,6 +5,27 @@ tc Testing Suite To-Do list:
5 5
6- Add support for multiple versions of tc to run successively 6- Add support for multiple versions of tc to run successively
7 7
8- Improve error messages when tdc aborts its run 8- Improve error messages when tdc aborts its run. Partially done - still
9 need to better handle problems in pre- and post-suite.
9 10
10- Allow tdc to write its results to file 11- Use python logger module for debug/verbose output
12
13- Allow tdc to write its results to file.
14 Maybe use python logger module for this too.
15
16- A better implementation of the "hooks". Currently, every plugin
17 will attempt to run a function at every hook point. Could be
18 changed so that plugin __init__ methods will register functions to
19 be run in the various predefined times. Then if a plugin does not
20 require action at a specific point, no penalty will be paid for
21 trying to run a function that will do nothing.
22
23- Proper exception handling - make an exception class and use it
24
25- a TestCase class, for easier testcase handling, searching, comparison
26
27- a TestSuite class
28 and a way to configure a test suite,
29 to automate running multiple "test suites" with different requirements
30
31- super simple test case example using ls, touch, etc
diff --git a/tools/testing/selftests/tc-testing/TdcPlugin.py b/tools/testing/selftests/tc-testing/TdcPlugin.py
new file mode 100644
index 000000000000..3ee9a6dacb52
--- /dev/null
+++ b/tools/testing/selftests/tc-testing/TdcPlugin.py
@@ -0,0 +1,74 @@
1#!/usr/bin/env python3
2
3class TdcPlugin:
4 def __init__(self):
5 super().__init__()
6 print(' -- {}.__init__'.format(self.sub_class))
7
8 def pre_suite(self, testcount, testidlist):
9 '''run commands before test_runner goes into a test loop'''
10 self.testcount = testcount
11 self.testidlist = testidlist
12 if self.args.verbose > 1:
13 print(' -- {}.pre_suite'.format(self.sub_class))
14
15 def post_suite(self, index):
16 '''run commands after test_runner completes the test loop
17 index is the last ordinal number of test that was attempted'''
18 if self.args.verbose > 1:
19 print(' -- {}.post_suite'.format(self.sub_class))
20
21 def pre_case(self, test_ordinal, testid):
22 '''run commands before test_runner does one test'''
23 if self.args.verbose > 1:
24 print(' -- {}.pre_case'.format(self.sub_class))
25 self.args.testid = testid
26 self.args.test_ordinal = test_ordinal
27
28 def post_case(self):
29 '''run commands after test_runner does one test'''
30 if self.args.verbose > 1:
31 print(' -- {}.post_case'.format(self.sub_class))
32
33 def pre_execute(self):
34 '''run command before test-runner does the execute step'''
35 if self.args.verbose > 1:
36 print(' -- {}.pre_execute'.format(self.sub_class))
37
38 def post_execute(self):
39 '''run command after test-runner does the execute step'''
40 if self.args.verbose > 1:
41 print(' -- {}.post_execute'.format(self.sub_class))
42
43 def adjust_command(self, stage, command):
44 '''adjust the command'''
45 if self.args.verbose > 1:
46 print(' -- {}.adjust_command {}'.format(self.sub_class, stage))
47
48 # if stage == 'pre':
49 # pass
50 # elif stage == 'setup':
51 # pass
52 # elif stage == 'execute':
53 # pass
54 # elif stage == 'verify':
55 # pass
56 # elif stage == 'teardown':
57 # pass
58 # elif stage == 'post':
59 # pass
60 # else:
61 # pass
62
63 return command
64
65 def add_args(self, parser):
66 '''Get the plugin args from the command line'''
67 self.argparser = parser
68 return self.argparser
69
70 def check_args(self, args, remaining):
71 '''Check that the args are set correctly'''
72 self.args = args
73 if self.args.verbose > 1:
74 print(' -- {}.check_args'.format(self.sub_class))
diff --git a/tools/testing/selftests/tc-testing/creating-plugins/AddingPlugins.txt b/tools/testing/selftests/tc-testing/creating-plugins/AddingPlugins.txt
new file mode 100644
index 000000000000..c18f88d09360
--- /dev/null
+++ b/tools/testing/selftests/tc-testing/creating-plugins/AddingPlugins.txt
@@ -0,0 +1,104 @@
1tdc - Adding plugins for tdc
2
3Author: Brenda J. Butler - bjb@mojatatu.com
4
5ADDING PLUGINS
6--------------
7
8A new plugin should be written in python as a class that inherits from TdcPlugin.
9There are some examples in plugin-lib.
10
11The plugin can be used to add functionality to the test framework,
12such as:
13
14- adding commands to be run before and/or after the test suite
15- adding commands to be run before and/or after the test cases
16- adding commands to be run before and/or after the execute phase of the test cases
17- ability to alter the command to be run in any phase:
18 pre (the pre-suite stage)
19 prepare
20 execute
21 verify
22 teardown
23 post (the post-suite stage)
24- ability to add to the command line args, and use them at run time
25
26
27The functions in the class should follow the following interfaces:
28
29 def __init__(self)
30 def pre_suite(self, testcount, testidlist) # see "PRE_SUITE" below
31 def post_suite(self, ordinal) # see "SKIPPING" below
32 def pre_case(self, test_ordinal, testid) # see "PRE_CASE" below
33 def post_case(self)
34 def pre_execute(self)
35 def post_execute(self)
36 def adjust_command(self, stage, command) # see "ADJUST" below
37 def add_args(self, parser) # see "ADD_ARGS" below
38 def check_args(self, args, remaining) # see "CHECK_ARGS" below
39
40
41PRE_SUITE
42
43This method takes a testcount (number of tests to be run) and
44testidlist (array of test ids for tests that will be run). This is
45useful for various things, including when an exception occurs and the
46rest of the tests must be skipped. The info is stored in the object,
47and the post_suite method can refer to it when dumping the "skipped"
48TAP output. The tdc.py script will do that for the test suite as
49defined in the test case, but if the plugin is being used to run extra
50tests on each test (eg, check for memory leaks on associated
51co-processes) then that other tap output can be generated in the
52post-suite method using this info passed in to the pre_suite method.
53
54
55SKIPPING
56
57The post_suite method will receive the ordinal number of the last
58test to be attempted. It can use this info when outputting
59the TAP output for the extra test cases.
60
61
62PRE_CASE
63
64The pre_case method will receive the ordinal number of the test
65and the test id. Useful for outputing the extra test results.
66
67
68ADJUST
69
70The adjust_command method receives a string representing
71the execution stage and a string which is the actual command to be
72executed. The plugin can adjust the command, based on the stage of
73execution.
74
75The stages are represented by the following strings:
76
77 'pre'
78 'setup'
79 'command'
80 'verify'
81 'teardown'
82 'post'
83
84The adjust_command method must return the adjusted command so tdc
85can use it.
86
87
88ADD_ARGS
89
90The add_args method receives the argparser object and can add
91arguments to it. Care should be taken that the new arguments do not
92conflict with any from tdc.py or from other plugins that will be used
93concurrently.
94
95The add_args method should return the argparser object.
96
97
98CHECK_ARGS
99
100The check_args method is so that the plugin can do validation on
101the args, if needed. If there is a problem, and Exception should
102be raised, with a string that explains the problem.
103
104eg: raise Exception('plugin xxx, arg -y is wrong, fix it')
diff --git a/tools/testing/selftests/tc-testing/creating-testcases/AddingTestCases.txt b/tools/testing/selftests/tc-testing/creating-testcases/AddingTestCases.txt
index 00438331ba47..17b267dedbd9 100644
--- a/tools/testing/selftests/tc-testing/creating-testcases/AddingTestCases.txt
+++ b/tools/testing/selftests/tc-testing/creating-testcases/AddingTestCases.txt
@@ -12,14 +12,18 @@ template.json for the required JSON format for test cases.
12Include the 'id' field, but do not assign a value. Running tdc with the -i 12Include the 'id' field, but do not assign a value. Running tdc with the -i
13option will generate a unique ID for that test case. 13option will generate a unique ID for that test case.
14 14
15tdc will recursively search the 'tc' subdirectory for .json files. Any 15tdc will recursively search the 'tc-tests' subdirectory (or the
16test case files you create in these directories will automatically be included. 16directories named with the -D option) for .json files. Any test case
17If you wish to store your custom test cases elsewhere, be sure to run tdc 17files you create in these directories will automatically be included.
18with the -f argument and the path to your file. 18If you wish to store your custom test cases elsewhere, be sure to run
19tdc with the -f argument and the path to your file, or the -D argument
20and the path to your directory(ies).
19 21
20Be aware of required escape characters in the JSON data - particularly when 22Be aware of required escape characters in the JSON data - particularly
21defining the match pattern. Refer to the tctests.json file for examples when 23when defining the match pattern. Refer to the supplied json test files
22in doubt. 24for examples when in doubt. The match pattern is written in json, and
25will be used by python. So the match pattern will be a python regular
26expression, but should be written using json syntax.
23 27
24 28
25TEST CASE STRUCTURE 29TEST CASE STRUCTURE
@@ -69,7 +73,8 @@ SETUP/TEARDOWN ERRORS
69If an error is detected during the setup/teardown process, execution of the 73If an error is detected during the setup/teardown process, execution of the
70tests will immediately stop with an error message and the namespace in which 74tests will immediately stop with an error message and the namespace in which
71the tests are run will be destroyed. This is to prevent inaccurate results 75the tests are run will be destroyed. This is to prevent inaccurate results
72in the test cases. 76in the test cases. tdc will output a series of TAP results for the skipped
77tests.
73 78
74Repeated failures of the setup/teardown may indicate a problem with the test 79Repeated failures of the setup/teardown may indicate a problem with the test
75case, or possibly even a bug in one of the commands that are not being tested. 80case, or possibly even a bug in one of the commands that are not being tested.
@@ -79,3 +84,17 @@ so that it doesn't halt the script for an error that doesn't matter. Turn the
79individual command into a list, with the command being first, followed by all 84individual command into a list, with the command being first, followed by all
80acceptable exit codes for the command. 85acceptable exit codes for the command.
81 86
87Example:
88
89A pair of setup commands. The first can have exit code 0, 1 or 255, the
90second must have exit code 0.
91
92 "setup": [
93 [
94 "$TC actions flush action gact",
95 0,
96 1,
97 255
98 ],
99 "$TC actions add action reclassify index 65536"
100 ],
diff --git a/tools/testing/selftests/tc-testing/plugin-lib/README-PLUGINS b/tools/testing/selftests/tc-testing/plugin-lib/README-PLUGINS
new file mode 100644
index 000000000000..aa8a2669702b
--- /dev/null
+++ b/tools/testing/selftests/tc-testing/plugin-lib/README-PLUGINS
@@ -0,0 +1,27 @@
1tdc.py will look for plugins in a directory plugins off the cwd.
2Make a set of numbered symbolic links from there to the actual plugins.
3Eg:
4
5tdc.py
6plugin-lib/
7plugins/
8 __init__.py
9 10-rootPlugin.py -> ../plugin-lib/rootPlugin.py
10 20-valgrindPlugin.py -> ../plugin-lib/valgrindPlugin.py
11 30-nsPlugin.py -> ../plugin-lib/nsPlugin.py
12
13
14tdc.py will find them and use them.
15
16
17rootPlugin
18 Check if the uid is root. If not, bail out.
19
20valgrindPlugin
21 Run the command under test with valgrind, and produce an extra set of TAP results for the memory tests.
22 This plugin will write files to the cwd, called vgnd-xxx.log. These will contain
23 the valgrind output for test xxx. Any file matching the glob 'vgnd-*.log' will be
24 deleted at the end of the run.
25
26nsPlugin
27 Run all the commands in a network namespace.
diff --git a/tools/testing/selftests/tc-testing/plugin-lib/nsPlugin.py b/tools/testing/selftests/tc-testing/plugin-lib/nsPlugin.py
new file mode 100644
index 000000000000..a194b1af2b30
--- /dev/null
+++ b/tools/testing/selftests/tc-testing/plugin-lib/nsPlugin.py
@@ -0,0 +1,141 @@
1import os
2import signal
3from string import Template
4import subprocess
5import time
6from TdcPlugin import TdcPlugin
7
8from tdc_config import *
9
10class SubPlugin(TdcPlugin):
11 def __init__(self):
12 self.sub_class = 'ns/SubPlugin'
13 super().__init__()
14
15 def pre_suite(self, testcount, testidlist):
16 '''run commands before test_runner goes into a test loop'''
17 super().pre_suite(testcount, testidlist)
18
19 if self.args.namespace:
20 self._ns_create()
21
22 def post_suite(self, index):
23 '''run commands after test_runner goes into a test loop'''
24 super().post_suite(index)
25 if self.args.verbose:
26 print('{}.post_suite'.format(self.sub_class))
27
28 if self.args.namespace:
29 self._ns_destroy()
30
31 def add_args(self, parser):
32 super().add_args(parser)
33 self.argparser_group = self.argparser.add_argument_group(
34 'netns',
35 'options for nsPlugin(run commands in net namespace)')
36 self.argparser_group.add_argument(
37 '-n', '--namespace', action='store_true',
38 help='Run commands in namespace')
39 return self.argparser
40
41 def adjust_command(self, stage, command):
42 super().adjust_command(stage, command)
43 cmdform = 'list'
44 cmdlist = list()
45
46 if not self.args.namespace:
47 return command
48
49 if self.args.verbose:
50 print('{}.adjust_command'.format(self.sub_class))
51
52 if not isinstance(command, list):
53 cmdform = 'str'
54 cmdlist = command.split()
55 else:
56 cmdlist = command
57 if stage == 'setup' or stage == 'execute' or stage == 'verify' or stage == 'teardown':
58 if self.args.verbose:
59 print('adjust_command: stage is {}; inserting netns stuff in command [{}] list [{}]'.format(stage, command, cmdlist))
60 cmdlist.insert(0, self.args.NAMES['NS'])
61 cmdlist.insert(0, 'exec')
62 cmdlist.insert(0, 'netns')
63 cmdlist.insert(0, 'ip')
64 else:
65 pass
66
67 if cmdform == 'str':
68 command = ' '.join(cmdlist)
69 else:
70 command = cmdlist
71
72 if self.args.verbose:
73 print('adjust_command: return command [{}]'.format(command))
74 return command
75
76 def _ns_create(self):
77 '''
78 Create the network namespace in which the tests will be run and set up
79 the required network devices for it.
80 '''
81 if self.args.namespace:
82 cmd = 'ip netns add {}'.format(self.args.NAMES['NS'])
83 self._exec_cmd('pre', cmd)
84 cmd = 'ip link add $DEV0 type veth peer name $DEV1'
85 self._exec_cmd('pre', cmd)
86 cmd = 'ip link set $DEV1 netns {}'.format(self.args.NAMES['NS'])
87 self._exec_cmd('pre', cmd)
88 cmd = 'ip link set $DEV0 up'
89 self._exec_cmd('pre', cmd)
90 cmd = 'ip -n {} link set $DEV1 up'.format(self.args.NAMES['NS'])
91 self._exec_cmd('pre', cmd)
92 if self.args.device:
93 cmd = 'ip link set $DEV2 netns {}'.format(self.args.NAMES['NS'])
94 self._exec_cmd('pre', cmd)
95 cmd = 'ip -n {} link set $DEV2 up'.format(self.args.NAMES['NS'])
96 self._exec_cmd('pre', cmd)
97
98 def _ns_destroy(self):
99 '''
100 Destroy the network namespace for testing (and any associated network
101 devices as well)
102 '''
103 if self.args.namespace:
104 cmd = 'ip netns delete {}'.format(self.args.NAMES['NS'])
105 self._exec_cmd('post', cmd)
106
107 def _exec_cmd(self, stage, command):
108 '''
109 Perform any required modifications on an executable command, then run
110 it in a subprocess and return the results.
111 '''
112 if '$' in command:
113 command = self._replace_keywords(command)
114
115 self.adjust_command(stage, command)
116 if self.args.verbose:
117 print('_exec_cmd: command "{}"'.format(command))
118 proc = subprocess.Popen(command,
119 shell=True,
120 stdout=subprocess.PIPE,
121 stderr=subprocess.PIPE,
122 env=ENVIR)
123 (rawout, serr) = proc.communicate()
124
125 if proc.returncode != 0 and len(serr) > 0:
126 foutput = serr.decode("utf-8")
127 else:
128 foutput = rawout.decode("utf-8")
129
130 proc.stdout.close()
131 proc.stderr.close()
132 return proc, foutput
133
134 def _replace_keywords(self, cmd):
135 """
136 For a given executable command, substitute any known
137 variables contained within NAMES with the correct values
138 """
139 tcmd = Template(cmd)
140 subcmd = tcmd.safe_substitute(self.args.NAMES)
141 return subcmd
diff --git a/tools/testing/selftests/tc-testing/plugin-lib/rootPlugin.py b/tools/testing/selftests/tc-testing/plugin-lib/rootPlugin.py
new file mode 100644
index 000000000000..e36775bd4d12
--- /dev/null
+++ b/tools/testing/selftests/tc-testing/plugin-lib/rootPlugin.py
@@ -0,0 +1,19 @@
1import os
2import sys
3from TdcPlugin import TdcPlugin
4
5from tdc_config import *
6
7
8class SubPlugin(TdcPlugin):
9 def __init__(self):
10 self.sub_class = 'root/SubPlugin'
11 super().__init__()
12
13 def pre_suite(self, testcount, testidlist):
14 # run commands before test_runner goes into a test loop
15 super().pre_suite(testcount, testidlist)
16
17 if os.geteuid():
18 print('This script must be run with root privileges', file=sys.stderr)
19 exit(1)
diff --git a/tools/testing/selftests/tc-testing/plugin-lib/valgrindPlugin.py b/tools/testing/selftests/tc-testing/plugin-lib/valgrindPlugin.py
new file mode 100644
index 000000000000..477a7bd7d7fb
--- /dev/null
+++ b/tools/testing/selftests/tc-testing/plugin-lib/valgrindPlugin.py
@@ -0,0 +1,142 @@
1'''
2run the command under test, under valgrind and collect memory leak info
3as a separate test.
4'''
5
6
7import os
8import re
9import signal
10from string import Template
11import subprocess
12import time
13from TdcPlugin import TdcPlugin
14
15from tdc_config import *
16
17def vp_extract_num_from_string(num_as_string_maybe_with_commas):
18 return int(num_as_string_maybe_with_commas.replace(',',''))
19
20class SubPlugin(TdcPlugin):
21 def __init__(self):
22 self.sub_class = 'valgrind/SubPlugin'
23 self.tap = ''
24 super().__init__()
25
26 def pre_suite(self, testcount, testidlist):
27 '''run commands before test_runner goes into a test loop'''
28 super().pre_suite(testcount, testidlist)
29 if self.args.verbose > 1:
30 print('{}.pre_suite'.format(self.sub_class))
31 if self.args.valgrind:
32 self._add_to_tap('1..{}\n'.format(self.testcount))
33
34 def post_suite(self, index):
35 '''run commands after test_runner goes into a test loop'''
36 super().post_suite(index)
37 self._add_to_tap('\n|---\n')
38 if self.args.verbose > 1:
39 print('{}.post_suite'.format(self.sub_class))
40 print('{}'.format(self.tap))
41 if self.args.verbose < 4:
42 subprocess.check_output('rm -f vgnd-*.log', shell=True)
43
44 def add_args(self, parser):
45 super().add_args(parser)
46 self.argparser_group = self.argparser.add_argument_group(
47 'valgrind',
48 'options for valgrindPlugin (run command under test under Valgrind)')
49
50 self.argparser_group.add_argument(
51 '-V', '--valgrind', action='store_true',
52 help='Run commands under valgrind')
53
54 return self.argparser
55
56 def adjust_command(self, stage, command):
57 super().adjust_command(stage, command)
58 cmdform = 'list'
59 cmdlist = list()
60
61 if not self.args.valgrind:
62 return command
63
64 if self.args.verbose > 1:
65 print('{}.adjust_command'.format(self.sub_class))
66
67 if not isinstance(command, list):
68 cmdform = 'str'
69 cmdlist = command.split()
70 else:
71 cmdlist = command
72
73 if stage == 'execute':
74 if self.args.verbose > 1:
75 print('adjust_command: stage is {}; inserting valgrind stuff in command [{}] list [{}]'.
76 format(stage, command, cmdlist))
77 cmdlist.insert(0, '--track-origins=yes')
78 cmdlist.insert(0, '--show-leak-kinds=definite,indirect')
79 cmdlist.insert(0, '--leak-check=full')
80 cmdlist.insert(0, '--log-file=vgnd-{}.log'.format(self.args.testid))
81 cmdlist.insert(0, '-v') # ask for summary of non-leak errors
82 cmdlist.insert(0, ENVIR['VALGRIND_BIN'])
83 else:
84 pass
85
86 if cmdform == 'str':
87 command = ' '.join(cmdlist)
88 else:
89 command = cmdlist
90
91 if self.args.verbose > 1:
92 print('adjust_command: return command [{}]'.format(command))
93 return command
94
95 def post_execute(self):
96 if not self.args.valgrind:
97 return
98
99 self.definitely_lost_re = re.compile(
100 r'definitely lost:\s+([,0-9]+)\s+bytes in\s+([,0-9]+)\sblocks', re.MULTILINE | re.DOTALL)
101 self.indirectly_lost_re = re.compile(
102 r'indirectly lost:\s+([,0-9]+)\s+bytes in\s+([,0-9]+)\s+blocks', re.MULTILINE | re.DOTALL)
103 self.possibly_lost_re = re.compile(
104 r'possibly lost:\s+([,0-9]+)bytes in\s+([,0-9]+)\s+blocks', re.MULTILINE | re.DOTALL)
105 self.non_leak_error_re = re.compile(
106 r'ERROR SUMMARY:\s+([,0-9]+) errors from\s+([,0-9]+)\s+contexts', re.MULTILINE | re.DOTALL)
107
108 def_num = 0
109 ind_num = 0
110 pos_num = 0
111 nle_num = 0
112
113 # what about concurrent test runs? Maybe force them to be in different directories?
114 with open('vgnd-{}.log'.format(self.args.testid)) as vfd:
115 content = vfd.read()
116 def_mo = self.definitely_lost_re.search(content)
117 ind_mo = self.indirectly_lost_re.search(content)
118 pos_mo = self.possibly_lost_re.search(content)
119 nle_mo = self.non_leak_error_re.search(content)
120
121 if def_mo:
122 def_num = int(def_mo.group(2))
123 if ind_mo:
124 ind_num = int(ind_mo.group(2))
125 if pos_mo:
126 pos_num = int(pos_mo.group(2))
127 if nle_mo:
128 nle_num = int(nle_mo.group(1))
129
130 mem_results = ''
131 if (def_num > 0) or (ind_num > 0) or (pos_num > 0) or (nle_num > 0):
132 mem_results += 'not '
133
134 mem_results += 'ok {} - {}-mem # {}\n'.format(
135 self.args.test_ordinal, self.args.testid, 'memory leak check')
136 self._add_to_tap(mem_results)
137 if mem_results.startswith('not '):
138 print('{}'.format(content))
139 self._add_to_tap(content)
140
141 def _add_to_tap(self, more_tap_output):
142 self.tap += more_tap_output
diff --git a/tools/testing/selftests/tc-testing/plugins/__init__.py b/tools/testing/selftests/tc-testing/plugins/__init__.py
new file mode 100644
index 000000000000..e69de29bb2d1
--- /dev/null
+++ b/tools/testing/selftests/tc-testing/plugins/__init__.py
diff --git a/tools/testing/selftests/tc-testing/tc-tests/actions/bpf.json b/tools/testing/selftests/tc-testing/tc-tests/actions/bpf.json
new file mode 100644
index 000000000000..5b012f4981d4
--- /dev/null
+++ b/tools/testing/selftests/tc-testing/tc-tests/actions/bpf.json
@@ -0,0 +1,289 @@
1[
2 {
3 "id": "d959",
4 "name": "Add cBPF action with valid bytecode",
5 "category": [
6 "actions",
7 "bpf"
8 ],
9 "setup": [
10 [
11 "$TC action flush action bpf",
12 0,
13 1,
14 255
15 ]
16 ],
17 "cmdUnderTest": "$TC action add action bpf bytecode '4,40 0 0 12,21 0 1 2048,6 0 0 262144,6 0 0 0' index 100",
18 "expExitCode": "0",
19 "verifyCmd": "$TC action get action bpf index 100",
20 "matchPattern": "action order [0-9]*: bpf bytecode '4,40 0 0 12,21 0 1 2048,6 0 0 262144,6 0 0 0' default-action pipe.*index 100 ref",
21 "matchCount": "1",
22 "teardown": [
23 "$TC action flush action bpf"
24 ]
25 },
26 {
27 "id": "f84a",
28 "name": "Add cBPF action with invalid bytecode",
29 "category": [
30 "actions",
31 "bpf"
32 ],
33 "setup": [
34 [
35 "$TC actions flush action bpf",
36 0,
37 1,
38 255
39 ]
40 ],
41 "cmdUnderTest": "$TC action add action bpf bytecode '4,40 0 0 12,31 0 1 2048,6 0 0 262144,6 0 0 0' index 100",
42 "expExitCode": "255",
43 "verifyCmd": "$TC action get action bpf index 100",
44 "matchPattern": "action order [0-9]*: bpf bytecode '4,40 0 0 12,31 0 1 2048,6 0 0 262144,6 0 0 0' default-action pipe.*index 100 ref",
45 "matchCount": "0",
46 "teardown": [
47 "$TC actions flush action bpf"
48 ]
49 },
50 {
51 "id": "e939",
52 "name": "Add eBPF action with valid object-file",
53 "category": [
54 "actions",
55 "bpf"
56 ],
57 "setup": [
58 "printf '#include <linux/bpf.h>\nchar l[] __attribute__((section(\"license\"),used))=\"GPL\"; __attribute__((section(\"action\"),used)) int m(struct __sk_buff *s) { return 2; }' | clang -O2 -x c -c - -target bpf -o _b.o",
59 [
60 "$TC action flush action bpf",
61 0,
62 1,
63 255
64 ]
65 ],
66 "cmdUnderTest": "$TC action add action bpf object-file _b.o index 667",
67 "expExitCode": "0",
68 "verifyCmd": "$TC action get action bpf index 667",
69 "matchPattern": "action order [0-9]*: bpf _b.o:\\[action\\] id [0-9]* tag 3b185187f1855c4c default-action pipe.*index 667 ref",
70 "matchCount": "1",
71 "teardown": [
72 "$TC action flush action bpf",
73 "rm -f _b.o"
74 ]
75 },
76 {
77 "id": "282d",
78 "name": "Add eBPF action with invalid object-file",
79 "category": [
80 "actions",
81 "bpf"
82 ],
83 "setup": [
84 "printf '#include <linux/bpf.h>\nchar l[] __attribute__((section(\"license\"),used))=\"GPL\"; __attribute__((section(\"action\"),used)) int m(struct __sk_buff *s) { s->data = 0x0; return 2; }' | clang -O2 -x c -c - -target bpf -o _c.o",
85 [
86 "$TC action flush action bpf",
87 0,
88 1,
89 255
90 ]
91 ],
92 "cmdUnderTest": "$TC action add action bpf object-file _c.o index 667",
93 "expExitCode": "255",
94 "verifyCmd": "$TC action get action bpf index 667",
95 "matchPattern": "action order [0-9]*: bpf _b.o:\\[action\\] id [0-9].*index 667 ref",
96 "matchCount": "0",
97 "teardown": [
98 "$TC action flush action bpf",
99 "rm -f _c.o"
100 ]
101 },
102 {
103 "id": "d819",
104 "name": "Replace cBPF bytecode and action control",
105 "category": [
106 "actions",
107 "bpf"
108 ],
109 "setup": [
110 [
111 "$TC actions flush action bpf",
112 0,
113 1,
114 255
115 ],
116 [
117 "$TC action add action bpf bytecode '4,40 0 0 12,21 0 1 2048,6 0 0 262144,6 0 0 0' index 555",
118 0,
119 1,
120 255
121 ]
122 ],
123 "cmdUnderTest": "$TC action replace action bpf bytecode '4,40 0 0 12,21 0 1 2054,6 0 0 262144,6 0 0 0' drop index 555",
124 "expExitCode": "0",
125 "verifyCmd": "$TC action get action bpf index 555",
126 "matchPattern": "action order [0-9]*: bpf bytecode '4,40 0 0 12,21 0 1 2054,6 0 0 262144,6 0 0 0' default-action drop.*index 555 ref",
127 "matchCount": "1",
128 "teardown": [
129 "$TC action flush action bpf"
130 ]
131 },
132 {
133 "id": "6ae3",
134 "name": "Delete cBPF action ",
135 "category": [
136 "actions",
137 "bpf"
138 ],
139 "setup": [
140 [
141 "$TC actions flush action bpf",
142 0,
143 1,
144 255
145 ],
146 [
147 "$TC action add action bpf bytecode '4,40 0 0 12,21 0 1 2048,6 0 0 262144,6 0 0 0' index 444",
148 0,
149 1,
150 255
151 ]
152 ],
153 "cmdUnderTest": "$TC action delete action bpf index 444",
154 "expExitCode": "0",
155 "verifyCmd": "$TC action get action bpf index 444",
156 "matchPattern": "action order [0-9]*: bpf bytecode '4,40 0 0 12,21 0 1 2048,6 0 0 262144,6 0 0 0' default-action pipe.*index 444 ref",
157 "matchCount": "0",
158 "teardown": [
159 "$TC action flush action bpf"
160 ]
161 },
162 {
163 "id": "3e0d",
164 "name": "List cBPF actions",
165 "category": [
166 "actions",
167 "bpf"
168 ],
169 "setup": [
170 [
171 "$TC action flush action bpf",
172 0,
173 1,
174 255
175 ],
176 "$TC action add action bpf bytecode '4,40 0 0 12,21 0 1 2048,6 0 0 262144,6 0 0 0' ok index 101",
177 "$TC action add action bpf bytecode '4,40 0 0 12,21 0 1 2054,6 0 0 262144,6 0 0 0' drop index 102",
178 "$TC action add action bpf bytecode '4,40 0 0 12,21 0 1 33024,6 0 0 262144,6 0 0 0' continue index 103"
179 ],
180 "cmdUnderTest": "$TC action list action bpf",
181 "expExitCode": "0",
182 "verifyCmd": "$TC action list action bpf",
183 "matchPattern": "action order [0-9]*: bpf bytecode",
184 "matchCount": "3",
185 "teardown": [
186 "$TC actions flush action bpf"
187 ]
188 },
189 {
190 "id": "55ce",
191 "name": "Flush BPF actions",
192 "category": [
193 "actions",
194 "bpf"
195 ],
196 "setup": [
197 [
198 "$TC actions flush action bpf",
199 0,
200 1,
201 255
202 ],
203 "$TC action add action bpf bytecode '4,40 0 0 12,21 0 1 2048,6 0 0 262144,6 0 0 0' ok index 101",
204 "$TC action add action bpf bytecode '4,40 0 0 12,21 0 1 2054,6 0 0 262144,6 0 0 0' drop index 102",
205 "$TC action add action bpf bytecode '4,40 0 0 12,21 0 1 33024,6 0 0 262144,6 0 0 0' continue index 103"
206 ],
207 "cmdUnderTest": "$TC action flush action bpf",
208 "expExitCode": "0",
209 "verifyCmd": "$TC action list action bpf",
210 "matchPattern": "action order [0-9]*: bpf bytecode",
211 "matchCount": "0",
212 "teardown": [
213 "$TC actions flush action bpf"
214 ]
215 },
216 {
217 "id": "ccc3",
218 "name": "Add cBPF action with duplicate index",
219 "category": [
220 "actions",
221 "bpf"
222 ],
223 "setup": [
224 [
225 "$TC actions flush action bpf",
226 0,
227 1,
228 255
229 ],
230 "$TC action add action bpf bytecode '4,40 0 0 12,21 0 1 2048,6 0 0 262144,6 0 0 0' index 4294967295"
231 ],
232 "cmdUnderTest": "$TC action add action bpf bytecode '4,40 0 0 12,21 0 1 2054,6 0 0 262144,6 0 0 0' index 4294967295",
233 "expExitCode": "255",
234 "verifyCmd": "$TC action get action bpf index 4294967295",
235 "matchPattern": "action order [0-9]*: bpf bytecode '4,40 0 0 12,21 0 1 2048,6 0 0 262144,6 0 0 0' default-action pipe.*index 4294967295",
236 "matchCount": "1",
237 "teardown": [
238 "$TC action flush action bpf"
239 ]
240 },
241 {
242 "id": "89c7",
243 "name": "Add cBPF action with invalid index",
244 "category": [
245 "actions",
246 "bpf"
247 ],
248 "setup": [
249 [
250 "$TC actions flush action bpf",
251 0,
252 1,
253 255
254 ]
255 ],
256 "cmdUnderTest": "$TC action add action bpf bytecode '4,40 0 0 12,21 0 1 2054,6 0 0 262144,6 0 0 0' index 4294967296 cookie 12345",
257 "expExitCode": "255",
258 "verifyCmd": "$TC action ls action bpf",
259 "matchPattern": "action order [0-9]*: bpf bytecode '4,40 0 0 12,21 0 1 2048,6 0 0 262144,6 0 0 0' default-action pipe.*cookie 12345",
260 "matchCount": "0",
261 "teardown": [
262 "$TC action flush action bpf"
263 ]
264 },
265 {
266 "id": "7ab9",
267 "name": "Add cBPF action with cookie",
268 "category": [
269 "actions",
270 "bpf"
271 ],
272 "setup": [
273 [
274 "$TC actions flush action bpf",
275 0,
276 1,
277 255
278 ]
279 ],
280 "cmdUnderTest": "$TC action add action bpf bytecode '4,40 0 0 12,21 0 1 2054,6 0 0 262144,6 0 0 0' cookie d0d0d0d0d0d0d0d0",
281 "expExitCode": "0",
282 "verifyCmd": "$TC action list action bpf",
283 "matchPattern": "action order [0-9]*: bpf.*cookie d0d0d0d0d0d0d0",
284 "matchCount": "1",
285 "teardown": [
286 "$TC action flush action bpf"
287 ]
288 }
289]
diff --git a/tools/testing/selftests/tc-testing/tc-tests/actions/connmark.json b/tools/testing/selftests/tc-testing/tc-tests/actions/connmark.json
new file mode 100644
index 000000000000..70952bd98ff9
--- /dev/null
+++ b/tools/testing/selftests/tc-testing/tc-tests/actions/connmark.json
@@ -0,0 +1,291 @@
1[
2 {
3 "id": "2002",
4 "name": "Add valid connmark action with defaults",
5 "category": [
6 "actions",
7 "connmark"
8 ],
9 "setup": [
10 [
11 "$TC actions flush action connmark",
12 0,
13 1,
14 255
15 ]
16 ],
17 "cmdUnderTest": "$TC actions add action connmark",
18 "expExitCode": "0",
19 "verifyCmd": "$TC actions list action connmark",
20 "matchPattern": "action order [0-9]+: connmark zone 0 pipe",
21 "matchCount": "1",
22 "teardown": [
23 "$TC actions flush action connmark"
24 ]
25 },
26 {
27 "id": "56a5",
28 "name": "Add valid connmark action with control pass",
29 "category": [
30 "actions",
31 "connmark"
32 ],
33 "setup": [
34 [
35 "$TC actions flush action connmark",
36 0,
37 1,
38 255
39 ]
40 ],
41 "cmdUnderTest": "$TC actions add action connmark pass index 1",
42 "expExitCode": "0",
43 "verifyCmd": "$TC actions get action connmark index 1",
44 "matchPattern": "action order [0-9]+: connmark zone 0 pass.*index 1 ref",
45 "matchCount": "1",
46 "teardown": [
47 "$TC actions flush action connmark"
48 ]
49 },
50 {
51 "id": "7c66",
52 "name": "Add valid connmark action with control drop",
53 "category": [
54 "actions",
55 "connmark"
56 ],
57 "setup": [
58 [
59 "$TC actions flush action connmark",
60 0,
61 1,
62 255
63 ]
64 ],
65 "cmdUnderTest": "$TC actions add action connmark drop index 100",
66 "expExitCode": "0",
67 "verifyCmd": "$TC actions get action connmark index 100",
68 "matchPattern": "action order [0-9]+: connmark zone 0 drop.*index 100 ref",
69 "matchCount": "1",
70 "teardown": [
71 "$TC actions flush action connmark"
72 ]
73 },
74 {
75 "id": "a913",
76 "name": "Add valid connmark action with control pipe",
77 "category": [
78 "actions",
79 "connmark"
80 ],
81 "setup": [
82 [
83 "$TC actions flush action connmark",
84 0,
85 1,
86 255
87 ]
88 ],
89 "cmdUnderTest": "$TC actions add action connmark pipe index 455",
90 "expExitCode": "0",
91 "verifyCmd": "$TC actions get action connmark index 455",
92 "matchPattern": "action order [0-9]+: connmark zone 0 pipe.*index 455 ref",
93 "matchCount": "1",
94 "teardown": [
95 "$TC actions flush action connmark"
96 ]
97 },
98 {
99 "id": "bdd8",
100 "name": "Add valid connmark action with control reclassify",
101 "category": [
102 "actions",
103 "connmark"
104 ],
105 "setup": [
106 [
107 "$TC actions flush action connmark",
108 0,
109 1,
110 255
111 ]
112 ],
113 "cmdUnderTest": "$TC actions add action connmark reclassify index 7",
114 "expExitCode": "0",
115 "verifyCmd": "$TC actions list action connmark",
116 "matchPattern": "action order [0-9]+: connmark zone 0 reclassify.*index 7 ref",
117 "matchCount": "1",
118 "teardown": [
119 "$TC actions flush action connmark"
120 ]
121 },
122 {
123 "id": "b8be",
124 "name": "Add valid connmark action with control continue",
125 "category": [
126 "actions",
127 "connmark"
128 ],
129 "setup": [
130 [
131 "$TC actions flush action connmark",
132 0,
133 1,
134 255
135 ]
136 ],
137 "cmdUnderTest": "$TC actions add action connmark continue index 17",
138 "expExitCode": "0",
139 "verifyCmd": "$TC actions list action connmark",
140 "matchPattern": "action order [0-9]+: connmark zone 0 continue.*index 17 ref",
141 "matchCount": "1",
142 "teardown": [
143 "$TC actions flush action connmark"
144 ]
145 },
146 {
147 "id": "d8a6",
148 "name": "Add valid connmark action with control jump",
149 "category": [
150 "actions",
151 "connmark"
152 ],
153 "setup": [
154 [
155 "$TC actions flush action connmark",
156 0,
157 1,
158 255
159 ]
160 ],
161 "cmdUnderTest": "$TC actions add action connmark jump 10 index 17",
162 "expExitCode": "0",
163 "verifyCmd": "$TC actions list action connmark",
164 "matchPattern": "action order [0-9]+: connmark zone 0 jump 10.*index 17 ref",
165 "matchCount": "1",
166 "teardown": [
167 "$TC actions flush action connmark"
168 ]
169 },
170 {
171 "id": "aae8",
172 "name": "Add valid connmark action with zone argument",
173 "category": [
174 "actions",
175 "connmark"
176 ],
177 "setup": [
178 [
179 "$TC actions flush action connmark",
180 0,
181 1,
182 255
183 ]
184 ],
185 "cmdUnderTest": "$TC actions add action connmark zone 100 pipe index 1",
186 "expExitCode": "0",
187 "verifyCmd": "$TC actions get action connmark index 1",
188 "matchPattern": "action order [0-9]+: connmark zone 100 pipe.*index 1 ref",
189 "matchCount": "1",
190 "teardown": [
191 "$TC actions flush action connmark"
192 ]
193 },
194 {
195 "id": "2f0b",
196 "name": "Add valid connmark action with invalid zone argument",
197 "category": [
198 "actions",
199 "connmark"
200 ],
201 "setup": [
202 [
203 "$TC actions flush action connmark",
204 0,
205 1,
206 255
207 ]
208 ],
209 "cmdUnderTest": "$TC actions add action connmark zone 65536 reclassify index 21",
210 "expExitCode": "255",
211 "verifyCmd": "$TC actions get action connmark index 1",
212 "matchPattern": "action order [0-9]+: connmark zone 65536 reclassify.*index 21 ref",
213 "matchCount": "0",
214 "teardown": [
215 "$TC actions flush action connmark"
216 ]
217 },
218 {
219 "id": "9305",
220 "name": "Add connmark action with unsupported argument",
221 "category": [
222 "actions",
223 "connmark"
224 ],
225 "setup": [
226 [
227 "$TC actions flush action connmark",
228 0,
229 1,
230 255
231 ]
232 ],
233 "cmdUnderTest": "$TC actions add action connmark zone 655 unsupp_arg pass index 2",
234 "expExitCode": "255",
235 "verifyCmd": "$TC actions get action connmark index 2",
236 "matchPattern": "action order [0-9]+: connmark zone 655 unsupp_arg pass.*index 2 ref",
237 "matchCount": "0",
238 "teardown": [
239 "$TC actions flush action connmark"
240 ]
241 },
242 {
243 "id": "71ca",
244 "name": "Add valid connmark action and replace it",
245 "category": [
246 "actions",
247 "connmark"
248 ],
249 "setup": [
250 [
251 "$TC actions flush action connmark",
252 0,
253 1,
254 255
255 ],
256 "$TC actions add action connmark zone 777 pass index 555"
257 ],
258 "cmdUnderTest": "$TC actions replace action connmark zone 555 reclassify index 555",
259 "expExitCode": "0",
260 "verifyCmd": "$TC actions get action connmark index 555",
261 "matchPattern": "action order [0-9]+: connmark zone 555 reclassify.*index 555 ref",
262 "matchCount": "1",
263 "teardown": [
264 "$TC actions flush action connmark"
265 ]
266 },
267 {
268 "id": "5f8f",
269 "name": "Add valid connmark action with cookie",
270 "category": [
271 "actions",
272 "connmark"
273 ],
274 "setup": [
275 [
276 "$TC actions flush action connmark",
277 0,
278 1,
279 255
280 ]
281 ],
282 "cmdUnderTest": "$TC actions add action connmark zone 555 pipe index 5 cookie aabbccddeeff112233445566778800a1",
283 "expExitCode": "0",
284 "verifyCmd": "$TC actions get action connmark index 5",
285 "matchPattern": "action order [0-9]+: connmark zone 555 pipe.*index 5 ref.*cookie aabbccddeeff112233445566778800a1",
286 "matchCount": "1",
287 "teardown": [
288 "$TC actions flush action connmark"
289 ]
290 }
291]
diff --git a/tools/testing/selftests/tc-testing/tc-tests/actions/csum.json b/tools/testing/selftests/tc-testing/tc-tests/actions/csum.json
new file mode 100644
index 000000000000..93cf8fea8ae7
--- /dev/null
+++ b/tools/testing/selftests/tc-testing/tc-tests/actions/csum.json
@@ -0,0 +1,410 @@
1[
2 {
3 "id": "6d84",
4 "name": "Add csum iph action",
5 "category": [
6 "actions",
7 "csum"
8 ],
9 "setup": [
10 [
11 "$TC actions flush action csum",
12 0,
13 1,
14 255
15 ]
16 ],
17 "cmdUnderTest": "$TC actions add action csum iph index 800",
18 "expExitCode": "0",
19 "verifyCmd": "$TC actions get action csum index 800",
20 "matchPattern": "action order [0-9]*: csum \\(iph\\) action pass.*index 800 ref",
21 "matchCount": "1",
22 "teardown": [
23 "$TC actions flush action csum"
24 ]
25 },
26 {
27 "id": "1862",
28 "name": "Add csum ip4h action",
29 "category": [
30 "actions",
31 "csum"
32 ],
33 "setup": [
34 [
35 "$TC actions flush action csum",
36 0,
37 1,
38 255
39 ]
40 ],
41 "cmdUnderTest": "$TC actions add action csum ip4h index 7",
42 "expExitCode": "0",
43 "verifyCmd": "$TC actions get action csum index 7",
44 "matchPattern": "action order [0-9]*: csum \\(iph\\) action pass.*index 7 ref",
45 "matchCount": "1",
46 "teardown": [
47 "$TC actions flush action csum"
48 ]
49 },
50 {
51 "id": "15c6",
52 "name": "Add csum ipv4h action",
53 "category": [
54 "actions",
55 "csum"
56 ],
57 "setup": [
58 [
59 "$TC actions flush action csum",
60 0,
61 1,
62 255
63 ]
64 ],
65 "cmdUnderTest": "$TC actions add action csum ipv4h index 1122",
66 "expExitCode": "0",
67 "verifyCmd": "$TC actions get action csum index 1122",
68 "matchPattern": "action order [0-9]*: csum \\(iph\\) action pass.*index 1122 ref",
69 "matchCount": "1",
70 "teardown": [
71 "$TC actions flush action csum"
72 ]
73 },
74 {
75 "id": "bf47",
76 "name": "Add csum icmp action",
77 "category": [
78 "actions",
79 "csum"
80 ],
81 "setup": [
82 [
83 "$TC actions flush action csum",
84 0,
85 1,
86 255
87 ]
88 ],
89 "cmdUnderTest": "$TC actions add action csum icmp index 1",
90 "expExitCode": "0",
91 "verifyCmd": "$TC actions get action csum index 1",
92 "matchPattern": "action order [0-9]*: csum \\(icmp\\) action pass.*index 1 ref",
93 "matchCount": "1",
94 "teardown": [
95 "$TC actions flush action csum"
96 ]
97 },
98 {
99 "id": "cc1d",
100 "name": "Add csum igmp action",
101 "category": [
102 "actions",
103 "csum"
104 ],
105 "setup": [
106 [
107 "$TC actions flush action csum",
108 0,
109 1,
110 255
111 ]
112 ],
113 "cmdUnderTest": "$TC actions add action csum igmp index 999",
114 "expExitCode": "0",
115 "verifyCmd": "$TC actions get action csum index 999",
116 "matchPattern": "action order [0-9]*: csum \\(igmp\\) action pass.*index 999 ref",
117 "matchCount": "1",
118 "teardown": [
119 "$TC actions flush action csum"
120 ]
121 },
122 {
123 "id": "bccc",
124 "name": "Add csum foobar action",
125 "category": [
126 "actions",
127 "csum"
128 ],
129 "setup": [
130 [
131 "$TC actions flush action csum",
132 0,
133 1,
134 255
135 ]
136 ],
137 "cmdUnderTest": "$TC actions add action csum foobar index 1",
138 "expExitCode": "255",
139 "verifyCmd": "$TC actions ls action csum",
140 "matchPattern": "action order [0-9]*: csum \\(foobar\\) action pass.*index 1 ref",
141 "matchCount": "0",
142 "teardown": [
143 "$TC actions flush action csum"
144 ]
145 },
146 {
147 "id": "3bb4",
148 "name": "Add csum tcp action",
149 "category": [
150 "actions",
151 "csum"
152 ],
153 "setup": [
154 [
155 "$TC actions flush action csum",
156 0,
157 1,
158 255
159 ]
160 ],
161 "cmdUnderTest": "$TC actions add action csum tcp index 9999",
162 "expExitCode": "0",
163 "verifyCmd": "$TC actions get action csum index 9999",
164 "matchPattern": "action order [0-9]*: csum \\(tcp\\) action pass.*index 9999 ref",
165 "matchCount": "1",
166 "teardown": [
167 "$TC actions flush action csum"
168 ]
169 },
170 {
171 "id": "759c",
172 "name": "Add csum udp action",
173 "category": [
174 "actions",
175 "csum"
176 ],
177 "setup": [
178 [
179 "$TC actions flush action csum",
180 0,
181 1,
182 255
183 ]
184 ],
185 "cmdUnderTest": "$TC actions add action csum udp index 334455",
186 "expExitCode": "0",
187 "verifyCmd": "$TC actions get action csum index 334455",
188 "matchPattern": "action order [0-9]*: csum \\(udp\\) action pass.*index 334455 ref",
189 "matchCount": "1",
190 "teardown": [
191 "$TC actions flush action csum"
192 ]
193 },
194 {
195 "id": "bdb6",
196 "name": "Add csum udp xor iph action",
197 "category": [
198 "actions",
199 "csum"
200 ],
201 "setup": [
202 [
203 "$TC actions flush action csum",
204 0,
205 1,
206 255
207 ]
208 ],
209 "cmdUnderTest": "$TC actions add action csum udp xor iph index 3",
210 "expExitCode": "255",
211 "verifyCmd": "$TC actions ls action csum",
212 "matchPattern": "action order [0-9]*: csum \\(udp xor iph\\) action pass.*index 3 ref",
213 "matchCount": "0",
214 "teardown": [
215 "$TC actions flush action csum"
216 ]
217 },
218 {
219 "id": "c220",
220 "name": "Add csum udplite action",
221 "category": [
222 "actions",
223 "csum"
224 ],
225 "setup": [
226 [
227 "$TC actions flush action csum",
228 0,
229 1,
230 255
231 ]
232 ],
233 "cmdUnderTest": "$TC actions add action csum udplite continue index 3",
234 "expExitCode": "0",
235 "verifyCmd": "$TC actions get action csum index 3",
236 "matchPattern": "action order [0-9]*: csum \\(udplite\\) action continue.*index 3 ref",
237 "matchCount": "1",
238 "teardown": [
239 "$TC actions flush action csum"
240 ]
241 },
242 {
243 "id": "8993",
244 "name": "Add csum sctp action",
245 "category": [
246 "actions",
247 "csum"
248 ],
249 "setup": [
250 [
251 "$TC actions flush action csum",
252 0,
253 1,
254 255
255 ]
256 ],
257 "cmdUnderTest": "$TC actions add action csum sctp index 777",
258 "expExitCode": "0",
259 "verifyCmd": "$TC actions get action csum index 777",
260 "matchPattern": "action order [0-9]*: csum \\(sctp\\) action pass.*index 777 ref",
261 "matchCount": "1",
262 "teardown": [
263 "$TC actions flush action csum"
264 ]
265 },
266 {
267 "id": "b138",
268 "name": "Add csum ip & icmp action",
269 "category": [
270 "actions",
271 "csum"
272 ],
273 "setup": [
274 [
275 "$TC actions flush action csum",
276 0,
277 1,
278 255
279 ]
280 ],
281 "cmdUnderTest": "$TC actions add action csum ip and icmp pipe index 123",
282 "expExitCode": "0",
283 "verifyCmd": "$TC actions get action csum index 123",
284 "matchPattern": "action order [0-9]*: csum \\(iph, icmp\\) action pipe.*index 123 ref",
285 "matchCount": "1",
286 "teardown": [
287 "$TC actions flush action csum"
288 ]
289 },
290 {
291 "id": "eeda",
292 "name": "Add csum ip & sctp action",
293 "category": [
294 "actions",
295 "csum"
296 ],
297 "setup": [
298 [
299 "$TC actions flush action csum",
300 0,
301 1,
302 255
303 ]
304 ],
305 "cmdUnderTest": "$TC actions add action csum ipv4h sctp continue index 2",
306 "expExitCode": "0",
307 "verifyCmd": "$TC actions get action csum index 2",
308 "matchPattern": "action order [0-9]*: csum \\(iph, sctp\\) action continue.*index 2 ref",
309 "matchCount": "1",
310 "teardown": [
311 "$TC actions flush action csum"
312 ]
313 },
314 {
315 "id": "0017",
316 "name": "Add csum udp or tcp action",
317 "category": [
318 "actions",
319 "csum"
320 ],
321 "setup": [
322 [
323 "$TC actions flush action csum",
324 0,
325 1,
326 255
327 ]
328 ],
329 "cmdUnderTest": "$TC actions add action csum udp or tcp continue index 27",
330 "expExitCode": "0",
331 "verifyCmd": "$TC actions get action csum index 27",
332 "matchPattern": "action order [0-9]*: csum \\(tcp, udp\\) action continue.*index 27 ref",
333 "matchCount": "1",
334 "teardown": [
335 "$TC actions flush action csum"
336 ]
337 },
338 {
339 "id": "ce92",
340 "name": "Add csum udp action with cookie",
341 "category": [
342 "actions",
343 "csum"
344 ],
345 "setup": [
346 [
347 "$TC actions flush action csum",
348 0,
349 1,
350 255
351 ]
352 ],
353 "cmdUnderTest": "$TC actions add action csum udp pipe index 7 cookie 12345678",
354 "expExitCode": "0",
355 "verifyCmd": "$TC actions get action csum index 7",
356 "matchPattern": "action order [0-9]*: csum \\(udp\\) action pipe.*index 7.*cookie 12345678",
357 "matchCount": "1",
358 "teardown": [
359 "$TC actions flush action csum"
360 ]
361 },
362 {
363 "id": "912f",
364 "name": "Add csum icmp action with large cookie",
365 "category": [
366 "actions",
367 "csum"
368 ],
369 "setup": [
370 [
371 "$TC actions flush action csum",
372 0,
373 1,
374 255
375 ]
376 ],
377 "cmdUnderTest": "$TC actions add action csum icmp pipe index 17 cookie aabbccddeeff1122",
378 "expExitCode": "0",
379 "verifyCmd": "$TC actions get action csum index 17",
380 "matchPattern": "action order [0-9]*: csum \\(icmp\\) action pipe.*index 17.*cookie aabbccddeeff1122",
381 "matchCount": "1",
382 "teardown": [
383 "$TC actions flush action csum"
384 ]
385 },
386 {
387 "id": "879b",
388 "name": "Add batch of 32 csum tcp actions",
389 "category": [
390 "actions",
391 "csum"
392 ],
393 "setup": [
394 [
395 "$TC actions flush action csum",
396 0,
397 1,
398 255
399 ]
400 ],
401 "cmdUnderTest": "for i in `seq 1 32`; do cmd=\"action csum tcp continue index $i \"; args=\"$args$cmd\"; done && $TC actions add $args",
402 "expExitCode": "255",
403 "verifyCmd": "$TC actions ls action csum",
404 "matchPattern": "^[ \t]+index [0-9]* ref",
405 "matchCount": "32",
406 "teardown": [
407 "$TC actions flush action csum"
408 ]
409 }
410]
diff --git a/tools/testing/selftests/tc-testing/tc-tests/actions/gact.json b/tools/testing/selftests/tc-testing/tc-tests/actions/gact.json
index e2187b6e0b7a..68c91023cdb9 100644
--- a/tools/testing/selftests/tc-testing/tc-tests/actions/gact.json
+++ b/tools/testing/selftests/tc-testing/tc-tests/actions/gact.json
@@ -465,5 +465,76 @@
465 "teardown": [ 465 "teardown": [
466 "$TC actions flush action gact" 466 "$TC actions flush action gact"
467 ] 467 ]
468 },
469 {
470 "id": "1021",
471 "name": "Add batch of 32 gact pass actions",
472 "category": [
473 "actions",
474 "gact"
475 ],
476 "setup": [
477 [
478 "$TC actions flush action gact",
479 0,
480 1,
481 255
482 ]
483 ],
484 "cmdUnderTest": "bash -c \"for i in \\`seq 1 32\\`; do cmd=\\\"action pass index \\$i \\\"; args=\"\\$args\\$cmd\"; done && $TC actions add \\$args\"",
485 "expExitCode": "0",
486 "verifyCmd": "$TC actions list action gact",
487 "matchPattern": "^[ \t]+index [0-9]+ ref",
488 "matchCount": "32",
489 "teardown": [
490 "$TC actions flush action gact"
491 ]
492 },
493 {
494 "id": "da7a",
495 "name": "Add batch of 32 gact continue actions with cookie",
496 "category": [
497 "actions",
498 "gact"
499 ],
500 "setup": [
501 [
502 "$TC actions flush action gact",
503 0,
504 1,
505 255
506 ]
507 ],
508 "cmdUnderTest": "bash -c \"for i in \\`seq 1 32\\`; do cmd=\\\"action continue index \\$i cookie aabbccddeeff112233445566778800a1 \\\"; args=\"\\$args\\$cmd\"; done && $TC actions add \\$args\"",
509 "expExitCode": "0",
510 "verifyCmd": "$TC actions list action gact",
511 "matchPattern": "^[ \t]+index [0-9]+ ref",
512 "matchCount": "32",
513 "teardown": [
514 "$TC actions flush action gact"
515 ]
516 },
517 {
518 "id": "8aa3",
519 "name": "Delete batch of 32 gact continue actions",
520 "category": [
521 "actions",
522 "gact"
523 ],
524 "setup": [
525 [
526 "$TC actions flush action gact",
527 0,
528 1,
529 255
530 ],
531 "bash -c \"for i in \\`seq 1 32\\`; do cmd=\\\"action continue index \\$i \\\"; args=\\\"\\$args\\$cmd\\\"; done && $TC actions add \\$args\""
532 ],
533 "cmdUnderTest": "bash -c \"for i in \\`seq 1 32\\`; do cmd=\\\"action gact index \\$i \\\"; args=\"\\$args\\$cmd\"; done && $TC actions del \\$args\"",
534 "expExitCode": "0",
535 "verifyCmd": "$TC actions list action gact",
536 "matchPattern": "^[ \t]+index [0-9]+ ref",
537 "matchCount": "0",
538 "teardown": []
468 } 539 }
469] 540]
diff --git a/tools/testing/selftests/tc-testing/tc-tests/actions/mirred.json b/tools/testing/selftests/tc-testing/tc-tests/actions/mirred.json
index 0fcccf18399b..443c9b3c8664 100644
--- a/tools/testing/selftests/tc-testing/tc-tests/actions/mirred.json
+++ b/tools/testing/selftests/tc-testing/tc-tests/actions/mirred.json
@@ -171,6 +171,198 @@
171 ] 171 ]
172 }, 172 },
173 { 173 {
174 "id": "8917",
175 "name": "Add mirred mirror action with control pass",
176 "category": [
177 "actions",
178 "mirred"
179 ],
180 "setup": [
181 [
182 "$TC actions flush action mirred",
183 0,
184 1,
185 255
186 ]
187 ],
188 "cmdUnderTest": "$TC actions add action mirred ingress mirror dev lo pass index 1",
189 "expExitCode": "0",
190 "verifyCmd": "$TC actions get action mirred index 1",
191 "matchPattern": "action order [0-9]*: mirred \\(Ingress Mirror to device lo\\) pass.*index 1 ref",
192 "matchCount": "1",
193 "teardown": [
194 "$TC actions flush action mirred"
195 ]
196 },
197 {
198 "id": "1054",
199 "name": "Add mirred mirror action with control pipe",
200 "category": [
201 "actions",
202 "mirred"
203 ],
204 "setup": [
205 [
206 "$TC actions flush action mirred",
207 0,
208 1,
209 255
210 ]
211 ],
212 "cmdUnderTest": "$TC actions add action mirred ingress mirror dev lo pipe index 15",
213 "expExitCode": "0",
214 "verifyCmd": "$TC actions get action mirred index 15",
215 "matchPattern": "action order [0-9]*: mirred \\(Ingress Mirror to device lo\\) pipe.*index 15 ref",
216 "matchCount": "1",
217 "teardown": [
218 "$TC actions flush action mirred"
219 ]
220 },
221 {
222 "id": "9887",
223 "name": "Add mirred mirror action with control continue",
224 "category": [
225 "actions",
226 "mirred"
227 ],
228 "setup": [
229 [
230 "$TC actions flush action mirred",
231 0,
232 1,
233 255
234 ]
235 ],
236 "cmdUnderTest": "$TC actions add action mirred ingress mirror dev lo continue index 15",
237 "expExitCode": "0",
238 "verifyCmd": "$TC actions get action mirred index 15",
239 "matchPattern": "action order [0-9]*: mirred \\(Ingress Mirror to device lo\\) continue.*index 15 ref",
240 "matchCount": "1",
241 "teardown": [
242 "$TC actions flush action mirred"
243 ]
244 },
245 {
246 "id": "e4aa",
247 "name": "Add mirred mirror action with control reclassify",
248 "category": [
249 "actions",
250 "mirred"
251 ],
252 "setup": [
253 [
254 "$TC actions flush action mirred",
255 0,
256 1,
257 255
258 ]
259 ],
260 "cmdUnderTest": "$TC actions add action mirred ingress mirror dev lo reclassify index 150",
261 "expExitCode": "0",
262 "verifyCmd": "$TC actions get action mirred index 150",
263 "matchPattern": "action order [0-9]*: mirred \\(Ingress Mirror to device lo\\) reclassify.*index 150 ref",
264 "matchCount": "1",
265 "teardown": [
266 "$TC actions flush action mirred"
267 ]
268 },
269 {
270 "id": "ece9",
271 "name": "Add mirred mirror action with control drop",
272 "category": [
273 "actions",
274 "mirred"
275 ],
276 "setup": [
277 [
278 "$TC actions flush action mirred",
279 0,
280 1,
281 255
282 ]
283 ],
284 "cmdUnderTest": "$TC actions add action mirred ingress mirror dev lo drop index 99",
285 "expExitCode": "0",
286 "verifyCmd": "$TC actions get action mirred index 99",
287 "matchPattern": "action order [0-9]*: mirred \\(Ingress Mirror to device lo\\) drop.*index 99 ref",
288 "matchCount": "1",
289 "teardown": [
290 "$TC actions flush action mirred"
291 ]
292 },
293 {
294 "id": "0031",
295 "name": "Add mirred mirror action with control jump",
296 "category": [
297 "actions",
298 "mirred"
299 ],
300 "setup": [
301 [
302 "$TC actions flush action mirred",
303 0,
304 1,
305 255
306 ]
307 ],
308 "cmdUnderTest": "$TC actions add action mirred ingress mirror dev lo jump 10 index 99",
309 "expExitCode": "0",
310 "verifyCmd": "$TC actions get action mirred index 99",
311 "matchPattern": "action order [0-9]*: mirred \\(Ingress Mirror to device lo\\) jump 10.*index 99 ref",
312 "matchCount": "1",
313 "teardown": [
314 "$TC actions flush action mirred"
315 ]
316 },
317 {
318 "id": "407c",
319 "name": "Add mirred mirror action with cookie",
320 "category": [
321 "actions",
322 "mirred"
323 ],
324 "setup": [
325 [
326 "$TC actions flush action mirred",
327 0,
328 1,
329 255
330 ]
331 ],
332 "cmdUnderTest": "$TC actions add action mirred ingress mirror dev lo reclassify cookie aa11bb22cc33dd44ee55",
333 "expExitCode": "0",
334 "verifyCmd": "$TC actions ls action mirred",
335 "matchPattern": "action order [0-9]*: mirred \\(Ingress Mirror to device lo\\) reclassify.*cookie aa11bb22cc33dd44ee55",
336 "matchCount": "1",
337 "teardown": [
338 "$TC actions flush action mirred"
339 ]
340 },
341 {
342 "id": "8b69",
343 "name": "Add mirred mirror action with maximum index",
344 "category": [
345 "actions",
346 "mirred"
347 ],
348 "setup": [
349 [
350 "$TC actions flush action mirred",
351 0,
352 1,
353 255
354 ]
355 ],
356 "cmdUnderTest": "$TC actions add action mirred ingress mirror dev lo pipe index 4294967295",
357 "expExitCode": "0",
358 "verifyCmd": "$TC actions get action mirred index 4294967295",
359 "matchPattern": "action order [0-9]*: mirred \\(Ingress Mirror to device lo\\) pipe.*index 4294967295",
360 "matchCount": "1",
361 "teardown": [
362 "$TC actions flush action mirred"
363 ]
364 },
365 {
174 "id": "a70e", 366 "id": "a70e",
175 "name": "Delete mirred mirror action", 367 "name": "Delete mirred mirror action",
176 "category": [ 368 "category": [
diff --git a/tools/testing/selftests/tc-testing/tc-tests/actions/police.json b/tools/testing/selftests/tc-testing/tc-tests/actions/police.json
index 0e602a3f9393..38d85a1d7492 100644
--- a/tools/testing/selftests/tc-testing/tc-tests/actions/police.json
+++ b/tools/testing/selftests/tc-testing/tc-tests/actions/police.json
@@ -265,6 +265,150 @@
265 ] 265 ]
266 }, 266 },
267 { 267 {
268 "id": "ddd6",
269 "name": "Add police action with invalid rate value",
270 "category": [
271 "actions",
272 "police"
273 ],
274 "setup": [
275 [
276 "$TC actions flush action police",
277 0,
278 1,
279 255
280 ]
281 ],
282 "cmdUnderTest": "$TC actions add action police rate 3tb burst 250k conform-exceed pass/pipe index 5",
283 "expExitCode": "255",
284 "verifyCmd": "$TC actions ls action police",
285 "matchPattern": "action order [0-9]*: police 0x5 rate 3Tb burst 250Kb mtu 2Kb action pass/pipe",
286 "matchCount": "0",
287 "teardown": [
288 "$TC actions flush action police"
289 ]
290 },
291 {
292 "id": "f61c",
293 "name": "Add police action with invalid burst value",
294 "category": [
295 "actions",
296 "police"
297 ],
298 "setup": [
299 [
300 "$TC actions flush action police",
301 0,
302 1,
303 255
304 ]
305 ],
306 "cmdUnderTest": "$TC actions add action police rate 3kbit burst 250P conform-exceed pass/pipe index 5",
307 "expExitCode": "255",
308 "verifyCmd": "$TC actions ls action police",
309 "matchPattern": "action order [0-9]*: police 0x5 rate 3Kbit burst 250Pb mtu 2Kb action pass/pipe",
310 "matchCount": "0",
311 "teardown": [
312 "$TC actions flush action police"
313 ]
314 },
315 {
316 "id": "c26f",
317 "name": "Add police action with invalid peakrate value",
318 "category": [
319 "actions",
320 "police"
321 ],
322 "setup": [
323 [
324 "$TC actions flush action police",
325 0,
326 1,
327 255
328 ]
329 ],
330 "cmdUnderTest": "$TC actions add action police rate 90kbit burst 10k mtu 2kb peakrate 100T index 1",
331 "expExitCode": "255",
332 "verifyCmd": "$TC actions ls action police",
333 "matchPattern": "action order [0-9]*: police 0x1 rate 90Kbit burst 10Kb mtu 2Kb peakrate 100Tbit",
334 "matchCount": "0",
335 "teardown": [
336 "$TC actions flush action police"
337 ]
338 },
339 {
340 "id": "db04",
341 "name": "Add police action with invalid mtu value",
342 "category": [
343 "actions",
344 "police"
345 ],
346 "setup": [
347 [
348 "$TC actions flush action police",
349 0,
350 1,
351 255
352 ]
353 ],
354 "cmdUnderTest": "$TC actions add action police rate 10kbit burst 10k mtu 2Pbit index 1",
355 "expExitCode": "255",
356 "verifyCmd": "$TC actions ls action police",
357 "matchPattern": "action order [0-9]*: police 0x1 rate 10Kbit burst 1Kb mtu 2Pb",
358 "matchCount": "0",
359 "teardown": [
360 "$TC actions flush action police"
361 ]
362 },
363 {
364 "id": "f3c9",
365 "name": "Add police action with cookie",
366 "category": [
367 "actions",
368 "police"
369 ],
370 "setup": [
371 [
372 "$TC actions flush action police",
373 0,
374 1,
375 255
376 ]
377 ],
378 "cmdUnderTest": "$TC actions add action police rate 10mbit burst 10k index 1 cookie a1b1c1d1e1f12233bb",
379 "expExitCode": "0",
380 "verifyCmd": "$TC actions get action police index 1",
381 "matchPattern": "action order [0-9]*: police 0x1 rate 10Mbit burst 10Kb mtu 2Kb.*cookie a1b1c1d1e1f12233bb",
382 "matchCount": "1",
383 "teardown": [
384 "$TC actions flush action police"
385 ]
386 },
387 {
388 "id": "d190",
389 "name": "Add police action with maximum index",
390 "category": [
391 "actions",
392 "police"
393 ],
394 "setup": [
395 [
396 "$TC actions flush action police",
397 0,
398 1,
399 255
400 ]
401 ],
402 "cmdUnderTest": "$TC actions add action police rate 10mbit burst 10k index 4294967295",
403 "expExitCode": "0",
404 "verifyCmd": "$TC actions get action mirred index 4294967295",
405 "matchPattern": "action order [0-9]*: police 0xffffffff rate 10Mbit burst 10Kb mtu 2Kb",
406 "matchCount": "1",
407 "teardown": [
408 "$TC actions flush action mirred"
409 ]
410 },
411 {
268 "id": "336e", 412 "id": "336e",
269 "name": "Delete police action", 413 "name": "Delete police action",
270 "category": [ 414 "category": [
diff --git a/tools/testing/selftests/tc-testing/tc-tests/actions/skbedit.json b/tools/testing/selftests/tc-testing/tc-tests/actions/skbedit.json
index 99635ea4722e..37ecc2716fee 100644
--- a/tools/testing/selftests/tc-testing/tc-tests/actions/skbedit.json
+++ b/tools/testing/selftests/tc-testing/tc-tests/actions/skbedit.json
@@ -216,6 +216,174 @@
216 ] 216 ]
217 }, 217 },
218 { 218 {
219 "id": "464a",
220 "name": "Add skbedit action with control pipe",
221 "category": [
222 "actions",
223 "skbedit"
224 ],
225 "setup": [
226 [
227 "$TC actions flush action skbedit",
228 0,
229 1,
230 255
231 ]
232 ],
233 "cmdUnderTest": "$TC actions add action skbedit ptype host pipe index 11",
234 "expExitCode": "0",
235 "verifyCmd": "$TC actions get action skbedit index 11",
236 "matchPattern": "action order [0-9]*: skbedit ptype host pipe.*index 11 ref",
237 "matchCount": "1",
238 "teardown": [
239 "$TC actions flush action skbedit"
240 ]
241 },
242 {
243 "id": "212f",
244 "name": "Add skbedit action with control reclassify",
245 "category": [
246 "actions",
247 "skbedit"
248 ],
249 "setup": [
250 [
251 "$TC actions flush action skbedit",
252 0,
253 1,
254 255
255 ]
256 ],
257 "cmdUnderTest": "$TC actions add action skbedit mark 56789 reclassify index 90",
258 "expExitCode": "0",
259 "verifyCmd": "$TC actions get action skbedit index 90",
260 "matchPattern": "action order [0-9]*: skbedit mark 56789 reclassify.*index 90 ref",
261 "matchCount": "1",
262 "teardown": [
263 "$TC actions flush action skbedit"
264 ]
265 },
266 {
267 "id": "0651",
268 "name": "Add skbedit action with control pass",
269 "category": [
270 "actions",
271 "skbedit"
272 ],
273 "setup": [
274 [
275 "$TC actions flush action skbedit",
276 0,
277 1,
278 255
279 ]
280 ],
281 "cmdUnderTest": "$TC actions add action skbedit queue_mapping 3 pass index 271",
282 "expExitCode": "0",
283 "verifyCmd": "$TC actions get action skbedit index 271",
284 "matchPattern": "action order [0-9]*: skbedit queue_mapping 3 pass.*index 271 ref",
285 "matchCount": "1",
286 "teardown": [
287 "$TC actions flush action skbedit"
288 ]
289 },
290 {
291 "id": "cc53",
292 "name": "Add skbedit action with control drop",
293 "category": [
294 "actions",
295 "skbedit"
296 ],
297 "setup": [
298 [
299 "$TC actions flush action skbedit",
300 0,
301 1,
302 255
303 ]
304 ],
305 "cmdUnderTest": "$TC actions add action skbedit queue_mapping 3 drop index 271",
306 "expExitCode": "0",
307 "verifyCmd": "$TC actions get action skbedit index 271",
308 "matchPattern": "action order [0-9]*: skbedit queue_mapping 3 drop.*index 271 ref",
309 "matchCount": "1",
310 "teardown": [
311 "$TC actions flush action skbedit"
312 ]
313 },
314 {
315 "id": "ec16",
316 "name": "Add skbedit action with control jump",
317 "category": [
318 "actions",
319 "skbedit"
320 ],
321 "setup": [
322 [
323 "$TC actions flush action skbedit",
324 0,
325 1,
326 255
327 ]
328 ],
329 "cmdUnderTest": "$TC actions add action skbedit priority 8 jump 9 index 2",
330 "expExitCode": "0",
331 "verifyCmd": "$TC actions get action skbedit index 2",
332 "matchPattern": "action order [0-9]*: skbedit priority :8 jump 9.*index 2 ref",
333 "matchCount": "1",
334 "teardown": [
335 "$TC actions flush action skbedit"
336 ]
337 },
338 {
339 "id": "db54",
340 "name": "Add skbedit action with control continue",
341 "category": [
342 "actions",
343 "skbedit"
344 ],
345 "setup": [
346 [
347 "$TC actions flush action skbedit",
348 0,
349 1,
350 255
351 ]
352 ],
353 "cmdUnderTest": "$TC actions add action skbedit priority 16 continue index 32",
354 "expExitCode": "0",
355 "verifyCmd": "$TC actions get action skbedit index 32",
356 "matchPattern": "action order [0-9]*: skbedit priority :16 continue.*index 32 ref",
357 "matchCount": "1",
358 "teardown": [
359 "$TC actions flush action skbedit"
360 ]
361 },
362 {
363 "id": "1055",
364 "name": "Add skbedit action with cookie",
365 "category": [
366 "actions",
367 "skbedit"
368 ],
369 "setup": [
370 [
371 "$TC actions flush action skbedit",
372 0,
373 1,
374 255
375 ]
376 ],
377 "cmdUnderTest": "$TC actions add action skbedit priority 16 continue index 32 cookie deadbeef",
378 "expExitCode": "0",
379 "verifyCmd": "$TC actions get action skbedit index 32",
380 "matchPattern": "action order [0-9]*: skbedit priority :16 continue.*index 32 ref.*cookie deadbeef",
381 "matchCount": "1",
382 "teardown": [
383 "$TC actions flush action skbedit"
384 ]
385 },
386 {
219 "id": "5172", 387 "id": "5172",
220 "name": "List skbedit actions", 388 "name": "List skbedit actions",
221 "category": [ 389 "category": [
diff --git a/tools/testing/selftests/tc-testing/tc-tests/actions/skbmod.json b/tools/testing/selftests/tc-testing/tc-tests/actions/skbmod.json
index 90bba48c3f07..fe3326e939c1 100644
--- a/tools/testing/selftests/tc-testing/tc-tests/actions/skbmod.json
+++ b/tools/testing/selftests/tc-testing/tc-tests/actions/skbmod.json
@@ -264,6 +264,30 @@
264 ] 264 ]
265 }, 265 },
266 { 266 {
267 "id": "6046",
268 "name": "Add skbmod action with control reclassify and cookie",
269 "category": [
270 "actions",
271 "skbmod"
272 ],
273 "setup": [
274 [
275 "$TC actions flush action skbmod",
276 0,
277 1,
278 255
279 ]
280 ],
281 "cmdUnderTest": "$TC actions add action skbmod set smac 00:01:02:03:04:01 reclassify index 1 cookie ddeeffaabb11cc22",
282 "expExitCode": "0",
283 "verifyCmd": "$TC actions get action skbmod index 1",
284 "matchPattern": "action order [0-9]*: skbmod reclassify set smac 00:01:02:03:04:01.*index 1 ref.*cookie ddeeffaabb11cc22",
285 "matchCount": "1",
286 "teardown": [
287 "$TC actions flush action skbmod"
288 ]
289 },
290 {
267 "id": "58cb", 291 "id": "58cb",
268 "name": "List skbmod actions", 292 "name": "List skbmod actions",
269 "category": [ 293 "category": [
diff --git a/tools/testing/selftests/tc-testing/tc-tests/actions/vlan.json b/tools/testing/selftests/tc-testing/tc-tests/actions/vlan.json
new file mode 100644
index 000000000000..4510ddfa6e54
--- /dev/null
+++ b/tools/testing/selftests/tc-testing/tc-tests/actions/vlan.json
@@ -0,0 +1,410 @@
1[
2 {
3 "id": "6f5a",
4 "name": "Add vlan pop action",
5 "category": [
6 "actions",
7 "vlan"
8 ],
9 "setup": [
10 [
11 "$TC actions flush action vlan",
12 0,
13 1,
14 255
15 ]
16 ],
17 "cmdUnderTest": "$TC actions add action vlan pop index 8",
18 "expExitCode": "0",
19 "verifyCmd": "$TC actions list action vlan",
20 "matchPattern": "action order [0-9]+: vlan.*pop.*index 8 ref",
21 "matchCount": "1",
22 "teardown": [
23 "$TC actions flush action vlan"
24 ]
25 },
26 {
27 "id": "ee6f",
28 "name": "Add vlan pop action with large index",
29 "category": [
30 "actions",
31 "vlan"
32 ],
33 "setup": [
34 [
35 "$TC actions flush action vlan",
36 0,
37 1,
38 255
39 ]
40 ],
41 "cmdUnderTest": "$TC actions add action vlan pop index 4294967295",
42 "expExitCode": "0",
43 "verifyCmd": "$TC actions list action vlan",
44 "matchPattern": "action order [0-9]+: vlan.*pop.*index 4294967295 ref",
45 "matchCount": "1",
46 "teardown": [
47 "$TC actions flush action vlan"
48 ]
49 },
50 {
51 "id": "b6b9",
52 "name": "Add vlan pop action with jump opcode",
53 "category": [
54 "actions",
55 "vlan"
56 ],
57 "setup": [
58 [
59 "$TC actions flush action vlan",
60 0,
61 1,
62 255
63 ]
64 ],
65 "cmdUnderTest": "$TC actions add action vlan pop jump 10 index 8",
66 "expExitCode": "0",
67 "verifyCmd": "$TC actions list action vlan",
68 "matchPattern": "action order [0-9]+: vlan.*jump 10.*index 8 ref",
69 "matchCount": "1",
70 "teardown": [
71 "$TC actions flush action vlan"
72 ]
73 },
74 {
75 "id": "87c3",
76 "name": "Add vlan pop action with trap opcode",
77 "category": [
78 "actions",
79 "vlan"
80 ],
81 "setup": [
82 [
83 "$TC actions flush action vlan",
84 0,
85 1,
86 255
87 ]
88 ],
89 "cmdUnderTest": "$TC actions add action vlan pop trap index 8",
90 "expExitCode": "0",
91 "verifyCmd": "$TC actions list action vlan",
92 "matchPattern": "action order [0-9]+: vlan.*pop trap.*index 8 ref",
93 "matchCount": "1",
94 "teardown": [
95 "$TC actions flush action vlan"
96 ]
97 },
98 {
99 "id": "2b91",
100 "name": "Add vlan invalid action",
101 "category": [
102 "actions",
103 "vlan"
104 ],
105 "setup": [
106 [
107 "$TC actions flush action vlan",
108 0,
109 1,
110 255
111 ]
112 ],
113 "cmdUnderTest": "$TC actions add action vlan bad_mode",
114 "expExitCode": "255",
115 "verifyCmd": "$TC actions list action vlan",
116 "matchPattern": "action order [0-9]+: vlan.*bad_mode",
117 "matchCount": "0",
118 "teardown": [
119 "$TC actions flush action vlan"
120 ]
121 },
122 {
123 "id": "57fc",
124 "name": "Add vlan action with invalid protocol type",
125 "category": [
126 "actions",
127 "vlan"
128 ],
129 "setup": [
130 [
131 "$TC actions flush action vlan",
132 0,
133 1,
134 255
135 ]
136 ],
137 "cmdUnderTest": "$TC actions add action vlan push protocol ABCD",
138 "expExitCode": "255",
139 "verifyCmd": "$TC actions list action vlan",
140 "matchPattern": "action order [0-9]+: vlan.*push",
141 "matchCount": "0",
142 "teardown": [
143 "$TC actions flush action vlan"
144 ]
145 },
146 {
147 "id": "3989",
148 "name": "Add vlan push action with default protocol and priority",
149 "category": [
150 "actions",
151 "vlan"
152 ],
153 "setup": [
154 [
155 "$TC actions flush action vlan",
156 0,
157 1,
158 255
159 ]
160 ],
161 "cmdUnderTest": "$TC actions add action vlan push id 123 index 18",
162 "expExitCode": "0",
163 "verifyCmd": "$TC actions get action vlan index 18",
164 "matchPattern": "action order [0-9]+: vlan.*push id 123 protocol 802.1Q priority 0 pipe.*index 18 ref",
165 "matchCount": "1",
166 "teardown": [
167 "$TC actions flush action vlan"
168 ]
169 },
170 {
171 "id": "79dc",
172 "name": "Add vlan push action with protocol 802.1Q and priority 3",
173 "category": [
174 "actions",
175 "vlan"
176 ],
177 "setup": [
178 [
179 "$TC actions flush action vlan",
180 0,
181 1,
182 255
183 ]
184 ],
185 "cmdUnderTest": "$TC actions add action vlan push id 77 protocol 802.1Q priority 3 continue index 734",
186 "expExitCode": "0",
187 "verifyCmd": "$TC actions get action vlan index 734",
188 "matchPattern": "action order [0-9]+: vlan.*push id 77 protocol 802.1Q priority 3 continue.*index 734 ref",
189 "matchCount": "1",
190 "teardown": [
191 "$TC actions flush action vlan"
192 ]
193 },
194 {
195 "id": "4d73",
196 "name": "Add vlan push action with protocol 802.1AD",
197 "category": [
198 "actions",
199 "vlan"
200 ],
201 "setup": [
202 [
203 "$TC actions flush action vlan",
204 0,
205 1,
206 255
207 ]
208 ],
209 "cmdUnderTest": "$TC actions add action vlan push id 1024 protocol 802.1AD pass index 10000",
210 "expExitCode": "0",
211 "verifyCmd": "$TC actions get action vlan index 10000",
212 "matchPattern": "action order [0-9]+: vlan.*push id 1024 protocol 802.1ad priority 0 pass.*index 10000 ref",
213 "matchCount": "1",
214 "teardown": [
215 "$TC actions flush action vlan"
216 ]
217 },
218 {
219 "id": "1f7b",
220 "name": "Add vlan push action with invalid vlan ID",
221 "category": [
222 "actions",
223 "vlan"
224 ],
225 "setup": [
226 [
227 "$TC actions flush action vlan",
228 0,
229 1,
230 255
231 ]
232 ],
233 "cmdUnderTest": "$TC actions add action vlan push id 5678 index 1",
234 "expExitCode": "255",
235 "verifyCmd": "$TC actions list action vlan",
236 "matchPattern": "action order [0-9]+: vlan.*push id 5678.*index 1 ref",
237 "matchCount": "0",
238 "teardown": [
239 "$TC actions flush action vlan"
240 ]
241 },
242 {
243 "id": "5d02",
244 "name": "Add vlan push action with invalid IEEE 802.1p priority",
245 "category": [
246 "actions",
247 "vlan"
248 ],
249 "setup": [
250 [
251 "$TC actions flush action vlan",
252 0,
253 1,
254 255
255 ]
256 ],
257 "cmdUnderTest": "$TC actions add action vlan push id 5 priority 10 index 1",
258 "expExitCode": "255",
259 "verifyCmd": "$TC actions list action vlan",
260 "matchPattern": "action order [0-9]+: vlan.*push id 5.*index 1 ref",
261 "matchCount": "0",
262 "teardown": [
263 "$TC actions flush action vlan"
264 ]
265 },
266 {
267 "id": "6812",
268 "name": "Add vlan modify action for protocol 802.1Q",
269 "category": [
270 "actions",
271 "vlan"
272 ],
273 "setup": [
274 [
275 "$TC actions flush action vlan",
276 0,
277 1,
278 255
279 ]
280 ],
281 "cmdUnderTest": "$TC actions add action vlan modify protocol 802.1Q id 5 index 100",
282 "expExitCode": "0",
283 "verifyCmd": "$TC actions get action vlan index 100",
284 "matchPattern": "action order [0-9]+: vlan.*modify id 100 protocol 802.1Q priority 0 pipe.*index 100 ref",
285 "matchCount": "0",
286 "teardown": [
287 "$TC actions flush action vlan"
288 ]
289 },
290 {
291 "id": "5a31",
292 "name": "Add vlan modify action for protocol 802.1AD",
293 "category": [
294 "actions",
295 "vlan"
296 ],
297 "setup": [
298 [
299 "$TC actions flush action vlan",
300 0,
301 1,
302 255
303 ]
304 ],
305 "cmdUnderTest": "$TC actions add action vlan modify protocol 802.1ad id 500 reclassify index 12",
306 "expExitCode": "0",
307 "verifyCmd": "$TC actions get action vlan index 12",
308 "matchPattern": "action order [0-9]+: vlan.*modify id 500 protocol 802.1ad priority 0 reclassify.*index 12 ref",
309 "matchCount": "1",
310 "teardown": [
311 "$TC actions flush action vlan"
312 ]
313 },
314 {
315 "id": "83a4",
316 "name": "Delete vlan pop action",
317 "category": [
318 "actions",
319 "vlan"
320 ],
321 "setup": [
322 [
323 "$TC actions flush action vlan",
324 0,
325 1,
326 255
327 ],
328 "$TC actions add action vlan pop index 44"
329 ],
330 "cmdUnderTest": "$TC actions del action vlan index 44",
331 "expExitCode": "0",
332 "verifyCmd": "$TC actions list action vlan",
333 "matchPattern": "action order [0-9]+: vlan.*pop.*index 44 ref",
334 "matchCount": "0",
335 "teardown": []
336 },
337 {
338 "id": "ed1e",
339 "name": "Delete vlan push action for protocol 802.1Q",
340 "category": [
341 "actions",
342 "vlan"
343 ],
344 "setup": [
345 [
346 "$TC actions flush action vlan",
347 0,
348 1,
349 255
350 ],
351 "$TC actions add action vlan push id 4094 protocol 802.1Q index 999"
352 ],
353 "cmdUnderTest": "$TC actions del action vlan index 999",
354 "expExitCode": "0",
355 "verifyCmd": "$TC actions list action vlan",
356 "matchPattern": "action order [0-9]+: vlan.*push id 4094 protocol 802.1Q priority 0 pipe.*index 999 ref",
357 "matchCount": "0",
358 "teardown": []
359 },
360 {
361 "id": "a2a3",
362 "name": "Flush vlan actions",
363 "category": [
364 "actions",
365 "vlan"
366 ],
367 "setup": [
368 [
369 "$TC actions flush action vlan",
370 0,
371 1,
372 255
373 ],
374 "$TC actions add action vlan push id 4 protocol 802.1ad index 10",
375 "$TC actions add action vlan push id 4 protocol 802.1ad index 11",
376 "$TC actions add action vlan push id 4 protocol 802.1ad index 12",
377 "$TC actions add action vlan push id 4 protocol 802.1ad index 13"
378 ],
379 "cmdUnderTest": "$TC actions flush action vlan",
380 "expExitCode": "0",
381 "verifyCmd": "$TC actions list action vlan",
382 "matchPattern": "action order [0-9]+: vlan.*push id 4 protocol 802.1ad",
383 "matchCount": "0",
384 "teardown": []
385 },
386 {
387 "id": "1d78",
388 "name": "Add vlan action with cookie",
389 "category": [
390 "actions",
391 "vlan"
392 ],
393 "setup": [
394 [
395 "$TC actions flush action vlan",
396 0,
397 1,
398 255
399 ]
400 ],
401 "cmdUnderTest": "$TC actions add action vlan push id 4 cookie a0a0a0a0a0a0a0",
402 "expExitCode": "0",
403 "verifyCmd": "$TC actions list action vlan",
404 "matchPattern": "action order [0-9]+: vlan.*push id 4.*cookie a0a0a0a0a0a0a0",
405 "matchCount": "1",
406 "teardown": [
407 "$TC actions flush action vlan"
408 ]
409 }
410]
diff --git a/tools/testing/selftests/tc-testing/tdc.py b/tools/testing/selftests/tc-testing/tdc.py
index fc373fdf2bdc..87a04a8a5945 100755
--- a/tools/testing/selftests/tc-testing/tdc.py
+++ b/tools/testing/selftests/tc-testing/tdc.py
@@ -11,16 +11,96 @@ import re
11import os 11import os
12import sys 12import sys
13import argparse 13import argparse
14import importlib
14import json 15import json
15import subprocess 16import subprocess
17import time
18import traceback
16from collections import OrderedDict 19from collections import OrderedDict
17from string import Template 20from string import Template
18 21
19from tdc_config import * 22from tdc_config import *
20from tdc_helper import * 23from tdc_helper import *
21 24
22 25import TdcPlugin
23USE_NS = True 26
27
28class PluginMgrTestFail(Exception):
29 def __init__(self, stage, output, message):
30 self.stage = stage
31 self.output = output
32 self.message = message
33
34class PluginMgr:
35 def __init__(self, argparser):
36 super().__init__()
37 self.plugins = {}
38 self.plugin_instances = []
39 self.args = []
40 self.argparser = argparser
41
42 # TODO, put plugins in order
43 plugindir = os.getenv('TDC_PLUGIN_DIR', './plugins')
44 for dirpath, dirnames, filenames in os.walk(plugindir):
45 for fn in filenames:
46 if (fn.endswith('.py') and
47 not fn == '__init__.py' and
48 not fn.startswith('#') and
49 not fn.startswith('.#')):
50 mn = fn[0:-3]
51 foo = importlib.import_module('plugins.' + mn)
52 self.plugins[mn] = foo
53 self.plugin_instances.append(foo.SubPlugin())
54
55 def call_pre_suite(self, testcount, testidlist):
56 for pgn_inst in self.plugin_instances:
57 pgn_inst.pre_suite(testcount, testidlist)
58
59 def call_post_suite(self, index):
60 for pgn_inst in reversed(self.plugin_instances):
61 pgn_inst.post_suite(index)
62
63 def call_pre_case(self, test_ordinal, testid):
64 for pgn_inst in self.plugin_instances:
65 try:
66 pgn_inst.pre_case(test_ordinal, testid)
67 except Exception as ee:
68 print('exception {} in call to pre_case for {} plugin'.
69 format(ee, pgn_inst.__class__))
70 print('test_ordinal is {}'.format(test_ordinal))
71 print('testid is {}'.format(testid))
72 raise
73
74 def call_post_case(self):
75 for pgn_inst in reversed(self.plugin_instances):
76 pgn_inst.post_case()
77
78 def call_pre_execute(self):
79 for pgn_inst in self.plugin_instances:
80 pgn_inst.pre_execute()
81
82 def call_post_execute(self):
83 for pgn_inst in reversed(self.plugin_instances):
84 pgn_inst.post_execute()
85
86 def call_add_args(self, parser):
87 for pgn_inst in self.plugin_instances:
88 parser = pgn_inst.add_args(parser)
89 return parser
90
91 def call_check_args(self, args, remaining):
92 for pgn_inst in self.plugin_instances:
93 pgn_inst.check_args(args, remaining)
94
95 def call_adjust_command(self, stage, command):
96 for pgn_inst in self.plugin_instances:
97 command = pgn_inst.adjust_command(stage, command)
98 return command
99
100 @staticmethod
101 def _make_argparser(args):
102 self.argparser = argparse.ArgumentParser(
103 description='Linux TC unit tests')
24 104
25 105
26def replace_keywords(cmd): 106def replace_keywords(cmd):
@@ -33,21 +113,24 @@ def replace_keywords(cmd):
33 return subcmd 113 return subcmd
34 114
35 115
36def exec_cmd(command, nsonly=True): 116def exec_cmd(args, pm, stage, command):
37 """ 117 """
38 Perform any required modifications on an executable command, then run 118 Perform any required modifications on an executable command, then run
39 it in a subprocess and return the results. 119 it in a subprocess and return the results.
40 """ 120 """
41 if (USE_NS and nsonly): 121 if len(command.strip()) == 0:
42 command = 'ip netns exec $NS ' + command 122 return None, None
43
44 if '$' in command: 123 if '$' in command:
45 command = replace_keywords(command) 124 command = replace_keywords(command)
46 125
126 command = pm.call_adjust_command(stage, command)
127 if args.verbose > 0:
128 print('command "{}"'.format(command))
47 proc = subprocess.Popen(command, 129 proc = subprocess.Popen(command,
48 shell=True, 130 shell=True,
49 stdout=subprocess.PIPE, 131 stdout=subprocess.PIPE,
50 stderr=subprocess.PIPE) 132 stderr=subprocess.PIPE,
133 env=ENVIR)
51 (rawout, serr) = proc.communicate() 134 (rawout, serr) = proc.communicate()
52 135
53 if proc.returncode != 0 and len(serr) > 0: 136 if proc.returncode != 0 and len(serr) > 0:
@@ -60,36 +143,99 @@ def exec_cmd(command, nsonly=True):
60 return proc, foutput 143 return proc, foutput
61 144
62 145
63def prepare_env(cmdlist): 146def prepare_env(args, pm, stage, prefix, cmdlist, output = None):
64 """ 147 """
65 Execute the setup/teardown commands for a test case. Optionally 148 Execute the setup/teardown commands for a test case.
66 terminate test execution if the command fails. 149 Optionally terminate test execution if the command fails.
67 """ 150 """
151 if args.verbose > 0:
152 print('{}'.format(prefix))
68 for cmdinfo in cmdlist: 153 for cmdinfo in cmdlist:
69 if (type(cmdinfo) == list): 154 if isinstance(cmdinfo, list):
70 exit_codes = cmdinfo[1:] 155 exit_codes = cmdinfo[1:]
71 cmd = cmdinfo[0] 156 cmd = cmdinfo[0]
72 else: 157 else:
73 exit_codes = [0] 158 exit_codes = [0]
74 cmd = cmdinfo 159 cmd = cmdinfo
75 160
76 if (len(cmd) == 0): 161 if not cmd:
77 continue 162 continue
78 163
79 (proc, foutput) = exec_cmd(cmd) 164 (proc, foutput) = exec_cmd(args, pm, stage, cmd)
165
166 if proc and (proc.returncode not in exit_codes):
167 print('', file=sys.stderr)
168 print("{} *** Could not execute: \"{}\"".format(prefix, cmd),
169 file=sys.stderr)
170 print("\n{} *** Error message: \"{}\"".format(prefix, foutput),
171 file=sys.stderr)
172 print("\n{} *** Aborting test run.".format(prefix), file=sys.stderr)
173 print("\n\n{} *** stdout ***".format(proc.stdout), file=sys.stderr)
174 print("\n\n{} *** stderr ***".format(proc.stderr), file=sys.stderr)
175 raise PluginMgrTestFail(
176 stage, output,
177 '"{}" did not complete successfully'.format(prefix))
178
179def run_one_test(pm, args, index, tidx):
180 global NAMES
181 result = True
182 tresult = ""
183 tap = ""
184 if args.verbose > 0:
185 print("\t====================\n=====> ", end="")
186 print("Test " + tidx["id"] + ": " + tidx["name"])
187
188 # populate NAMES with TESTID for this test
189 NAMES['TESTID'] = tidx['id']
190
191 pm.call_pre_case(index, tidx['id'])
192 prepare_env(args, pm, 'setup', "-----> prepare stage", tidx["setup"])
193
194 if (args.verbose > 0):
195 print('-----> execute stage')
196 pm.call_pre_execute()
197 (p, procout) = exec_cmd(args, pm, 'execute', tidx["cmdUnderTest"])
198 exit_code = p.returncode
199 pm.call_post_execute()
200
201 if (exit_code != int(tidx["expExitCode"])):
202 result = False
203 print("exit:", exit_code, int(tidx["expExitCode"]))
204 print(procout)
205 else:
206 if args.verbose > 0:
207 print('-----> verify stage')
208 match_pattern = re.compile(
209 str(tidx["matchPattern"]), re.DOTALL | re.MULTILINE)
210 (p, procout) = exec_cmd(args, pm, 'verify', tidx["verifyCmd"])
211 if procout:
212 match_index = re.findall(match_pattern, procout)
213 if len(match_index) != int(tidx["matchCount"]):
214 result = False
215 elif int(tidx["matchCount"]) != 0:
216 result = False
217
218 if not result:
219 tresult += 'not '
220 tresult += 'ok {} - {} # {}\n'.format(str(index), tidx['id'], tidx['name'])
221 tap += tresult
80 222
81 if proc.returncode not in exit_codes: 223 if result == False:
82 print 224 if procout:
83 print("Could not execute:") 225 tap += procout
84 print(cmd) 226 else:
85 print("\nError message:") 227 tap += 'No output!\n'
86 print(foutput) 228
87 print("\nAborting test run.") 229 prepare_env(args, pm, 'teardown', '-----> teardown stage', tidx['teardown'], procout)
88 ns_destroy() 230 pm.call_post_case()
89 exit(1)
90 231
232 index += 1
233
234 # remove TESTID from NAMES
235 del(NAMES['TESTID'])
236 return tap
91 237
92def test_runner(filtered_tests, args): 238def test_runner(pm, args, filtered_tests):
93 """ 239 """
94 Driver function for the unit tests. 240 Driver function for the unit tests.
95 241
@@ -101,75 +247,92 @@ def test_runner(filtered_tests, args):
101 testlist = filtered_tests 247 testlist = filtered_tests
102 tcount = len(testlist) 248 tcount = len(testlist)
103 index = 1 249 index = 1
104 tap = str(index) + ".." + str(tcount) + "\n" 250 tap = ''
105 251 badtest = None
252 stage = None
253 emergency_exit = False
254 emergency_exit_message = ''
255
256 if args.notap:
257 if args.verbose:
258 tap = 'notap requested: omitting test plan\n'
259 else:
260 tap = str(index) + ".." + str(tcount) + "\n"
261 try:
262 pm.call_pre_suite(tcount, [tidx['id'] for tidx in testlist])
263 except Exception as ee:
264 ex_type, ex, ex_tb = sys.exc_info()
265 print('Exception {} {} (caught in pre_suite).'.
266 format(ex_type, ex))
267 # when the extra print statements are uncommented,
268 # the traceback does not appear between them
269 # (it appears way earlier in the tdc.py output)
270 # so don't bother ...
271 # print('--------------------(')
272 # print('traceback')
273 traceback.print_tb(ex_tb)
274 # print('--------------------)')
275 emergency_exit_message = 'EMERGENCY EXIT, call_pre_suite failed with exception {} {}\n'.format(ex_type, ex)
276 emergency_exit = True
277 stage = 'pre-SUITE'
278
279 if emergency_exit:
280 pm.call_post_suite(index)
281 return emergency_exit_message
282 if args.verbose > 1:
283 print('give test rig 2 seconds to stabilize')
284 time.sleep(2)
106 for tidx in testlist: 285 for tidx in testlist:
107 result = True
108 tresult = ""
109 if "flower" in tidx["category"] and args.device == None: 286 if "flower" in tidx["category"] and args.device == None:
287 if args.verbose > 1:
288 print('Not executing test {} {} because DEV2 not defined'.
289 format(tidx['id'], tidx['name']))
110 continue 290 continue
111 print("Test " + tidx["id"] + ": " + tidx["name"]) 291 try:
112 prepare_env(tidx["setup"]) 292 badtest = tidx # in case it goes bad
113 (p, procout) = exec_cmd(tidx["cmdUnderTest"]) 293 tap += run_one_test(pm, args, index, tidx)
114 exit_code = p.returncode 294 except PluginMgrTestFail as pmtf:
115 295 ex_type, ex, ex_tb = sys.exc_info()
116 if (exit_code != int(tidx["expExitCode"])): 296 stage = pmtf.stage
117 result = False 297 message = pmtf.message
118 print("exit:", exit_code, int(tidx["expExitCode"])) 298 output = pmtf.output
119 print(procout) 299 print(message)
120 else: 300 print('Exception {} {} (caught in test_runner, running test {} {} {} stage {})'.
121 match_pattern = re.compile(str(tidx["matchPattern"]), re.DOTALL) 301 format(ex_type, ex, index, tidx['id'], tidx['name'], stage))
122 (p, procout) = exec_cmd(tidx["verifyCmd"]) 302 print('---------------')
123 match_index = re.findall(match_pattern, procout) 303 print('traceback')
124 if len(match_index) != int(tidx["matchCount"]): 304 traceback.print_tb(ex_tb)
125 result = False 305 print('---------------')
126 306 if stage == 'teardown':
127 if result == True: 307 print('accumulated output for this test:')
128 tresult += "ok " 308 if pmtf.output:
129 else: 309 print(pmtf.output)
130 tresult += "not ok " 310 print('---------------')
131 tap += tresult + str(index) + " " + tidx["id"] + " " + tidx["name"] + "\n" 311 break
132
133 if result == False:
134 tap += procout
135
136 prepare_env(tidx["teardown"])
137 index += 1 312 index += 1
138 313
139 return tap 314 # if we failed in setup or teardown,
140 315 # fill in the remaining tests with ok-skipped
316 count = index
317 if not args.notap:
318 tap += 'about to flush the tap output if tests need to be skipped\n'
319 if tcount + 1 != index:
320 for tidx in testlist[index - 1:]:
321 msg = 'skipped - previous {} failed'.format(stage)
322 tap += 'ok {} - {} # {} {} {}\n'.format(
323 count, tidx['id'], msg, index, badtest.get('id', '--Unknown--'))
324 count += 1
141 325
142def ns_create(): 326 tap += 'done flushing skipped test tap output\n'
143 """
144 Create the network namespace in which the tests will be run and set up
145 the required network devices for it.
146 """
147 if (USE_NS):
148 cmd = 'ip netns add $NS'
149 exec_cmd(cmd, False)
150 cmd = 'ip link add $DEV0 type veth peer name $DEV1'
151 exec_cmd(cmd, False)
152 cmd = 'ip link set $DEV1 netns $NS'
153 exec_cmd(cmd, False)
154 cmd = 'ip link set $DEV0 up'
155 exec_cmd(cmd, False)
156 cmd = 'ip -n $NS link set $DEV1 up'
157 exec_cmd(cmd, False)
158 cmd = 'ip link set $DEV2 netns $NS'
159 exec_cmd(cmd, False)
160 cmd = 'ip -n $NS link set $DEV2 up'
161 exec_cmd(cmd, False)
162 327
328 if args.pause:
329 print('Want to pause\nPress enter to continue ...')
330 if input(sys.stdin):
331 print('got something on stdin')
163 332
164def ns_destroy(): 333 pm.call_post_suite(index)
165 """
166 Destroy the network namespace for testing (and any associated network
167 devices as well)
168 """
169 if (USE_NS):
170 cmd = 'ip netns delete $NS'
171 exec_cmd(cmd, False)
172 334
335 return tap
173 336
174def has_blank_ids(idlist): 337def has_blank_ids(idlist):
175 """ 338 """
@@ -209,41 +372,70 @@ def set_args(parser):
209 """ 372 """
210 Set the command line arguments for tdc. 373 Set the command line arguments for tdc.
211 """ 374 """
212 parser.add_argument('-p', '--path', type=str, 375 parser.add_argument(
213 help='The full path to the tc executable to use') 376 '-p', '--path', type=str,
214 parser.add_argument('-c', '--category', type=str, nargs='?', const='+c', 377 help='The full path to the tc executable to use')
215 help='Run tests only from the specified category, or if no category is specified, list known categories.') 378 sg = parser.add_argument_group(
216 parser.add_argument('-f', '--file', type=str, 379 'selection', 'select which test cases: ' +
217 help='Run tests from the specified file') 380 'files plus directories; filtered by categories plus testids')
218 parser.add_argument('-l', '--list', type=str, nargs='?', const="++", metavar='CATEGORY', 381 ag = parser.add_argument_group(
219 help='List all test cases, or those only within the specified category') 382 'action', 'select action to perform on selected test cases')
220 parser.add_argument('-s', '--show', type=str, nargs=1, metavar='ID', dest='showID', 383
221 help='Display the test case with specified id') 384 sg.add_argument(
222 parser.add_argument('-e', '--execute', type=str, nargs=1, metavar='ID', 385 '-D', '--directory', nargs='+', metavar='DIR',
223 help='Execute the single test case with specified ID') 386 help='Collect tests from the specified directory(ies) ' +
224 parser.add_argument('-i', '--id', action='store_true', dest='gen_id', 387 '(default [tc-tests])')
225 help='Generate ID numbers for new test cases') 388 sg.add_argument(
389 '-f', '--file', nargs='+', metavar='FILE',
390 help='Run tests from the specified file(s)')
391 sg.add_argument(
392 '-c', '--category', nargs='*', metavar='CATG', default=['+c'],
393 help='Run tests only from the specified category/ies, ' +
394 'or if no category/ies is/are specified, list known categories.')
395 sg.add_argument(
396 '-e', '--execute', nargs='+', metavar='ID',
397 help='Execute the specified test cases with specified IDs')
398 ag.add_argument(
399 '-l', '--list', action='store_true',
400 help='List all test cases, or those only within the specified category')
401 ag.add_argument(
402 '-s', '--show', action='store_true', dest='showID',
403 help='Display the selected test cases')
404 ag.add_argument(
405 '-i', '--id', action='store_true', dest='gen_id',
406 help='Generate ID numbers for new test cases')
407 parser.add_argument(
408 '-v', '--verbose', action='count', default=0,
409 help='Show the commands that are being run')
410 parser.add_argument(
411 '-N', '--notap', action='store_true',
412 help='Suppress tap results for command under test')
226 parser.add_argument('-d', '--device', 413 parser.add_argument('-d', '--device',
227 help='Execute the test case in flower category') 414 help='Execute the test case in flower category')
415 parser.add_argument(
416 '-P', '--pause', action='store_true',
417 help='Pause execution just before post-suite stage')
228 return parser 418 return parser
229 419
230 420
231def check_default_settings(args): 421def check_default_settings(args, remaining, pm):
232 """ 422 """
233 Process any arguments overriding the default settings, and ensure the 423 Process any arguments overriding the default settings,
234 settings are correct. 424 and ensure the settings are correct.
235 """ 425 """
236 # Allow for overriding specific settings 426 # Allow for overriding specific settings
237 global NAMES 427 global NAMES
238 428
239 if args.path != None: 429 if args.path != None:
240 NAMES['TC'] = args.path 430 NAMES['TC'] = args.path
241 if args.device != None: 431 if args.device != None:
242 NAMES['DEV2'] = args.device 432 NAMES['DEV2'] = args.device
243 if not os.path.isfile(NAMES['TC']): 433 if not os.path.isfile(NAMES['TC']):
244 print("The specified tc path " + NAMES['TC'] + " does not exist.") 434 print("The specified tc path " + NAMES['TC'] + " does not exist.")
245 exit(1) 435 exit(1)
246 436
437 pm.call_check_args(args, remaining)
438
247 439
248def get_id_list(alltests): 440def get_id_list(alltests):
249 """ 441 """
@@ -277,7 +469,7 @@ def generate_case_ids(alltests):
277 for c in alltests: 469 for c in alltests:
278 if (c["id"] == ""): 470 if (c["id"] == ""):
279 while True: 471 while True:
280 newid = str('%04x' % random.randrange(16**4)) 472 newid = str('{:04x}'.format(random.randrange(16**4)))
281 if (does_id_exist(alltests, newid)): 473 if (does_id_exist(alltests, newid)):
282 continue 474 continue
283 else: 475 else:
@@ -298,42 +490,110 @@ def generate_case_ids(alltests):
298 testlist.append(t) 490 testlist.append(t)
299 outfile = open(f, "w") 491 outfile = open(f, "w")
300 json.dump(testlist, outfile, indent=4) 492 json.dump(testlist, outfile, indent=4)
493 outfile.write("\n")
301 outfile.close() 494 outfile.close()
302 495
496def filter_tests_by_id(args, testlist):
497 '''
498 Remove tests from testlist that are not in the named id list.
499 If id list is empty, return empty list.
500 '''
501 newlist = list()
502 if testlist and args.execute:
503 target_ids = args.execute
504
505 if isinstance(target_ids, list) and (len(target_ids) > 0):
506 newlist = list(filter(lambda x: x['id'] in target_ids, testlist))
507 return newlist
508
509def filter_tests_by_category(args, testlist):
510 '''
511 Remove tests from testlist that are not in a named category.
512 '''
513 answer = list()
514 if args.category and testlist:
515 test_ids = list()
516 for catg in set(args.category):
517 if catg == '+c':
518 continue
519 print('considering category {}'.format(catg))
520 for tc in testlist:
521 if catg in tc['category'] and tc['id'] not in test_ids:
522 answer.append(tc)
523 test_ids.append(tc['id'])
524
525 return answer
303 526
304def get_test_cases(args): 527def get_test_cases(args):
305 """ 528 """
306 If a test case file is specified, retrieve tests from that file. 529 If a test case file is specified, retrieve tests from that file.
307 Otherwise, glob for all json files in subdirectories and load from 530 Otherwise, glob for all json files in subdirectories and load from
308 each one. 531 each one.
532 Also, if requested, filter by category, and add tests matching
533 certain ids.
309 """ 534 """
310 import fnmatch 535 import fnmatch
311 if args.file != None: 536
312 if not os.path.isfile(args.file): 537 flist = []
313 print("The specified test case file " + args.file + " does not exist.") 538 testdirs = ['tc-tests']
314 exit(1) 539
315 flist = [args.file] 540 if args.file:
316 else: 541 # at least one file was specified - remove the default directory
317 flist = [] 542 testdirs = []
318 for root, dirnames, filenames in os.walk('tc-tests'): 543
544 for ff in args.file:
545 if not os.path.isfile(ff):
546 print("IGNORING file " + ff + "\n\tBECAUSE does not exist.")
547 else:
548 flist.append(os.path.abspath(ff))
549
550 if args.directory:
551 testdirs = args.directory
552
553 for testdir in testdirs:
554 for root, dirnames, filenames in os.walk(testdir):
319 for filename in fnmatch.filter(filenames, '*.json'): 555 for filename in fnmatch.filter(filenames, '*.json'):
320 flist.append(os.path.join(root, filename)) 556 candidate = os.path.abspath(os.path.join(root, filename))
321 alltests = list() 557 if candidate not in testdirs:
558 flist.append(candidate)
559
560 alltestcases = list()
322 for casefile in flist: 561 for casefile in flist:
323 alltests = alltests + (load_from_file(casefile)) 562 alltestcases = alltestcases + (load_from_file(casefile))
324 return alltests 563
564 allcatlist = get_test_categories(alltestcases)
565 allidlist = get_id_list(alltestcases)
325 566
567 testcases_by_cats = get_categorized_testlist(alltestcases, allcatlist)
568 idtestcases = filter_tests_by_id(args, alltestcases)
569 cattestcases = filter_tests_by_category(args, alltestcases)
326 570
327def set_operation_mode(args): 571 cat_ids = [x['id'] for x in cattestcases]
572 if args.execute:
573 if args.category:
574 alltestcases = cattestcases + [x for x in idtestcases if x['id'] not in cat_ids]
575 else:
576 alltestcases = idtestcases
577 else:
578 if cat_ids:
579 alltestcases = cattestcases
580 else:
581 # just accept the existing value of alltestcases,
582 # which has been filtered by file/directory
583 pass
584
585 return allcatlist, allidlist, testcases_by_cats, alltestcases
586
587
588def set_operation_mode(pm, args):
328 """ 589 """
329 Load the test case data and process remaining arguments to determine 590 Load the test case data and process remaining arguments to determine
330 what the script should do for this run, and call the appropriate 591 what the script should do for this run, and call the appropriate
331 function. 592 function.
332 """ 593 """
333 alltests = get_test_cases(args) 594 ucat, idlist, testcases, alltests = get_test_cases(args)
334 595
335 if args.gen_id: 596 if args.gen_id:
336 idlist = get_id_list(alltests)
337 if (has_blank_ids(idlist)): 597 if (has_blank_ids(idlist)):
338 alltests = generate_case_ids(alltests) 598 alltests = generate_case_ids(alltests)
339 else: 599 else:
@@ -347,70 +607,29 @@ def set_operation_mode(args):
347 print("Please correct them before continuing.") 607 print("Please correct them before continuing.")
348 exit(1) 608 exit(1)
349 609
350 ucat = get_test_categories(alltests)
351
352 if args.showID: 610 if args.showID:
353 show_test_case_by_id(alltests, args.showID[0]) 611 for atest in alltests:
612 print_test_case(atest)
354 exit(0) 613 exit(0)
355 614
356 if args.execute: 615 if isinstance(args.category, list) and (len(args.category) == 0):
357 target_id = args.execute[0] 616 print("Available categories:")
358 else: 617 print_sll(ucat)
359 target_id = "" 618 exit(0)
360
361 if args.category:
362 if (args.category == '+c'):
363 print("Available categories:")
364 print_sll(ucat)
365 exit(0)
366 else:
367 target_category = args.category
368 else:
369 target_category = ""
370
371
372 testcases = get_categorized_testlist(alltests, ucat)
373 619
374 if args.list: 620 if args.list:
375 if (args.list == "++"): 621 if args.list:
376 list_test_cases(alltests) 622 list_test_cases(alltests)
377 exit(0) 623 exit(0)
378 elif(len(args.list) > 0):
379 if (args.list not in ucat):
380 print("Unknown category " + args.list)
381 print("Available categories:")
382 print_sll(ucat)
383 exit(1)
384 list_test_cases(testcases[args.list])
385 exit(0)
386
387 if (os.geteuid() != 0):
388 print("This script must be run with root privileges.\n")
389 exit(1)
390
391 ns_create()
392
393 if (len(target_category) == 0):
394 if (len(target_id) > 0):
395 alltests = list(filter(lambda x: target_id in x['id'], alltests))
396 if (len(alltests) == 0):
397 print("Cannot find a test case with ID matching " + target_id)
398 exit(1)
399 catresults = test_runner(alltests, args)
400 print("All test results: " + "\n\n" + catresults)
401 elif (len(target_category) > 0):
402 if (target_category == "flower") and args.device == None:
403 print("Please specify a NIC device (-d) to run category flower")
404 exit(1)
405 if (target_category not in ucat):
406 print("Specified category is not present in this file.")
407 exit(1)
408 else:
409 catresults = test_runner(testcases[target_category], args)
410 print("Category " + target_category + "\n\n" + catresults)
411
412 ns_destroy()
413 624
625 if len(alltests):
626 catresults = test_runner(pm, args, alltests)
627 else:
628 catresults = 'No tests found\n'
629 if args.notap:
630 print('Tap output suppression requested\n')
631 else:
632 print('All test results: \n\n{}'.format(catresults))
414 633
415def main(): 634def main():
416 """ 635 """
@@ -419,10 +638,15 @@ def main():
419 """ 638 """
420 parser = args_parse() 639 parser = args_parse()
421 parser = set_args(parser) 640 parser = set_args(parser)
641 pm = PluginMgr(parser)
642 parser = pm.call_add_args(parser)
422 (args, remaining) = parser.parse_known_args() 643 (args, remaining) = parser.parse_known_args()
423 check_default_settings(args) 644 args.NAMES = NAMES
645 check_default_settings(args, remaining, pm)
646 if args.verbose > 2:
647 print('args is {}'.format(args))
424 648
425 set_operation_mode(args) 649 set_operation_mode(pm, args)
426 650
427 exit(0) 651 exit(0)
428 652
diff --git a/tools/testing/selftests/tc-testing/tdc_batch.py b/tools/testing/selftests/tc-testing/tdc_batch.py
index 707c6bfef689..52fa539dc662 100755
--- a/tools/testing/selftests/tc-testing/tdc_batch.py
+++ b/tools/testing/selftests/tc-testing/tdc_batch.py
@@ -49,13 +49,13 @@ index = 0
49for i in range(0x100): 49for i in range(0x100):
50 for j in range(0x100): 50 for j in range(0x100):
51 for k in range(0x100): 51 for k in range(0x100):
52 mac = ("%02x:%02x:%02x" % (i, j, k)) 52 mac = ("{:02x}:{:02x}:{:02x}".format(i, j, k))
53 src_mac = "e4:11:00:" + mac 53 src_mac = "e4:11:00:" + mac
54 dst_mac = "e4:12:00:" + mac 54 dst_mac = "e4:12:00:" + mac
55 cmd = ("filter add dev %s %s protocol ip parent ffff: flower %s " 55 cmd = ("filter add dev {} {} protocol ip parent ffff: flower {} "
56 "src_mac %s dst_mac %s action drop %s" % 56 "src_mac {} dst_mac {} action drop {}".format
57 (device, prio, skip, src_mac, dst_mac, share_action)) 57 (device, prio, skip, src_mac, dst_mac, share_action))
58 file.write("%s\n" % cmd) 58 file.write("{}\n".format(cmd))
59 index += 1 59 index += 1
60 if index >= number: 60 if index >= number:
61 file.close() 61 file.close()
diff --git a/tools/testing/selftests/tc-testing/tdc_helper.py b/tools/testing/selftests/tc-testing/tdc_helper.py
index db381120a566..9f35c96c88a0 100644
--- a/tools/testing/selftests/tc-testing/tdc_helper.py
+++ b/tools/testing/selftests/tc-testing/tdc_helper.py
@@ -57,20 +57,11 @@ def print_sll(items):
57 57
58def print_test_case(tcase): 58def print_test_case(tcase):
59 """ Pretty-printing of a given test case. """ 59 """ Pretty-printing of a given test case. """
60 print('\n==============\nTest {}\t{}\n'.format(tcase['id'], tcase['name']))
60 for k in tcase.keys(): 61 for k in tcase.keys():
61 if (isinstance(tcase[k], list)): 62 if (isinstance(tcase[k], list)):
62 print(k + ":") 63 print(k + ":")
63 print_list(tcase[k]) 64 print_list(tcase[k])
64 else: 65 else:
65 print(k + ": " + tcase[k]) 66 if not ((k == 'id') or (k == 'name')):
66 67 print(k + ": " + str(tcase[k]))
67
68def show_test_case_by_id(testlist, caseID):
69 """ Find the specified test case to pretty-print. """
70 if not any(d.get('id', None) == caseID for d in testlist):
71 print("That ID does not exist.")
72 exit(1)
73 else:
74 print_test_case(next((d for d in testlist if d['id'] == caseID)))
75
76