From 1599b218d511afad570be6048108d0746f78bfe9 Mon Sep 17 00:00:00 2001 From: Shannon Nelson <shannon.nelson@oracle.com> Date: Tue, 19 Jun 2018 22:42:42 -0700 Subject: selftests: rtnetlink: hide complaint from terminated monitor Set up the "ip xfrm monitor" subprogram so as to not see a "Terminated" message when the subprogram is killed. Fixes: 5e596ee171ba ("selftests: add xfrm state-policy-monitor to rtnetlink.sh") Reported-by: Anders Roxell <anders.roxell@linaro.org> Signed-off-by: Shannon Nelson <shannon.nelson@oracle.com> Signed-off-by: David S. Miller <davem@davemloft.net> --- tools/testing/selftests/net/rtnetlink.sh | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'tools') diff --git a/tools/testing/selftests/net/rtnetlink.sh b/tools/testing/selftests/net/rtnetlink.sh index 0d7a44fa30af..4cd252f4e077 100755 --- a/tools/testing/selftests/net/rtnetlink.sh +++ b/tools/testing/selftests/net/rtnetlink.sh @@ -535,8 +535,7 @@ kci_test_ipsec() # start the monitor in the background tmpfile=`mktemp ipsectestXXX` - ip x m > $tmpfile & - mpid=$! + mpid=`(ip x m > $tmpfile & echo $!) 2>/dev/null` sleep 0.2 ipsecid="proto esp src $srcip dst $dstip spi 0x07" -- cgit v1.2.3-58-ga151 From 7000d53b865ab46e0c557e2bf7ccf05d33d0624c Mon Sep 17 00:00:00 2001 From: Shannon Nelson <shannon.nelson@oracle.com> Date: Tue, 19 Jun 2018 22:42:43 -0700 Subject: selftests: rtnetlink: use a local IP address for IPsec tests Find an IP address on this machine to use as a source IP, and make up a destination IP address based on the source IP. No actual messages will be sent, just a couple of IPsec rules are created and deleted. Fixes: 5e596ee171ba ("selftests: add xfrm state-policy-monitor to rtnetlink.sh") Reported-by: Anders Roxell <anders.roxell@linaro.org> Signed-off-by: Shannon Nelson <shannon.nelson@oracle.com> Signed-off-by: David S. Miller <davem@davemloft.net> --- tools/testing/selftests/net/rtnetlink.sh | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) (limited to 'tools') diff --git a/tools/testing/selftests/net/rtnetlink.sh b/tools/testing/selftests/net/rtnetlink.sh index 4cd252f4e077..a90eb31abd59 100755 --- a/tools/testing/selftests/net/rtnetlink.sh +++ b/tools/testing/selftests/net/rtnetlink.sh @@ -525,8 +525,12 @@ kci_test_macsec() #------------------------------------------------------------------- kci_test_ipsec() { - srcip="14.0.0.52" - dstip="14.0.0.70" + # find an ip address on this machine and make up a destination + srcip=`ip -o addr | awk '/inet / { print $4; }' | grep -v "^127" | head -1 | cut -f1 -d/` + net=`echo $srcip | cut -f1-3 -d.` + base=`echo $srcip | cut -f4 -d.` + dstip="$net."`expr $base + 1` + algo="aead rfc4106(gcm(aes)) 0x3132333435363738393031323334353664636261 128" # flush to be sure there's nothing configured -- cgit v1.2.3-58-ga151 From 5b1e7f9ebd5653dc4cc026671ca07d8ab2419a99 Mon Sep 17 00:00:00 2001 From: Petr Machata <petrm@mellanox.com> Date: Mon, 25 Jun 2018 10:48:18 +0300 Subject: selftests: forwarding: Test routed bridge interface Add test for cases where bridge itself acts as a router interface, with front panel port attached to the bridge in question. In the first test (router_bridge.sh), VLAN memberships are not configured in any way, and everything uses default PVID of 1. Thus traffic in $h1 and $h2 is untagged. This test ensures that the previous patches didn't break a currently working scenario. In the second test (router_bridge_vlan.sh), a VLAN 555 pvid untagged is added to the bridge CPU port, with that VLAN leaving the bridge tagged through its sole member port. The traffic is therefore expected to come out tagged at $h1. This tests the fix introduced in the previous patches. Signed-off-by: Petr Machata <petrm@mellanox.com> Signed-off-by: Ido Schimmel <idosch@mellanox.com> Signed-off-by: David S. Miller <davem@davemloft.net> --- .../selftests/net/forwarding/router_bridge.sh | 113 ++++++++++++++++++ .../selftests/net/forwarding/router_bridge_vlan.sh | 132 +++++++++++++++++++++ 2 files changed, 245 insertions(+) create mode 100755 tools/testing/selftests/net/forwarding/router_bridge.sh create mode 100755 tools/testing/selftests/net/forwarding/router_bridge_vlan.sh (limited to 'tools') diff --git a/tools/testing/selftests/net/forwarding/router_bridge.sh b/tools/testing/selftests/net/forwarding/router_bridge.sh new file mode 100755 index 000000000000..ebc596a272f7 --- /dev/null +++ b/tools/testing/selftests/net/forwarding/router_bridge.sh @@ -0,0 +1,113 @@ +#!/bin/bash +# SPDX-License-Identifier: GPL-2.0 + +ALL_TESTS=" + ping_ipv4 + ping_ipv6 +" +NUM_NETIFS=4 +source lib.sh + +h1_create() +{ + simple_if_init $h1 192.0.2.1/28 2001:db8:1::1/64 + ip -4 route add 192.0.2.128/28 vrf v$h1 nexthop via 192.0.2.2 + ip -6 route add 2001:db8:2::/64 vrf v$h1 nexthop via 2001:db8:1::2 +} + +h1_destroy() +{ + ip -6 route del 2001:db8:2::/64 vrf v$h1 + ip -4 route del 192.0.2.128/28 vrf v$h1 + simple_if_fini $h1 192.0.2.1/28 2001:db8:1::1/64 +} + +h2_create() +{ + simple_if_init $h2 192.0.2.130/28 2001:db8:2::2/64 + ip -4 route add 192.0.2.0/28 vrf v$h2 nexthop via 192.0.2.129 + ip -6 route add 2001:db8:1::/64 vrf v$h2 nexthop via 2001:db8:2::1 +} + +h2_destroy() +{ + ip -6 route del 2001:db8:1::/64 vrf v$h2 + ip -4 route del 192.0.2.0/28 vrf v$h2 + simple_if_fini $h2 192.0.2.130/28 2001:db8:2::2/64 +} + +router_create() +{ + ip link add name br1 type bridge vlan_filtering 1 + ip link set dev br1 up + + ip link set dev $swp1 master br1 + ip link set dev $swp1 up + __addr_add_del br1 add 192.0.2.2/28 2001:db8:1::2/64 + + ip link set dev $swp2 up + __addr_add_del $swp2 add 192.0.2.129/28 2001:db8:2::1/64 +} + +router_destroy() +{ + __addr_add_del $swp2 del 192.0.2.129/28 2001:db8:2::1/64 + ip link set dev $swp2 down + + __addr_add_del br1 del 192.0.2.2/28 2001:db8:1::2/64 + ip link set dev $swp1 down + ip link set dev $swp1 nomaster + + ip link del dev br1 +} + +setup_prepare() +{ + h1=${NETIFS[p1]} + swp1=${NETIFS[p2]} + + swp2=${NETIFS[p3]} + h2=${NETIFS[p4]} + + vrf_prepare + + h1_create + h2_create + + router_create + + forwarding_enable +} + +cleanup() +{ + pre_cleanup + + forwarding_restore + + router_destroy + + h2_destroy + h1_destroy + + vrf_cleanup +} + +ping_ipv4() +{ + ping_test $h1 192.0.2.130 +} + +ping_ipv6() +{ + ping6_test $h1 2001:db8:2::2 +} + +trap cleanup EXIT + +setup_prepare +setup_wait + +tests_run + +exit $EXIT_STATUS diff --git a/tools/testing/selftests/net/forwarding/router_bridge_vlan.sh b/tools/testing/selftests/net/forwarding/router_bridge_vlan.sh new file mode 100755 index 000000000000..fef88eb4b873 --- /dev/null +++ b/tools/testing/selftests/net/forwarding/router_bridge_vlan.sh @@ -0,0 +1,132 @@ +#!/bin/bash +# SPDX-License-Identifier: GPL-2.0 + +ALL_TESTS=" + ping_ipv4 + ping_ipv6 + vlan +" +NUM_NETIFS=4 +source lib.sh + +h1_create() +{ + simple_if_init $h1 + vlan_create $h1 555 v$h1 192.0.2.1/28 2001:db8:1::1/64 + ip -4 route add 192.0.2.128/28 vrf v$h1 nexthop via 192.0.2.2 + ip -6 route add 2001:db8:2::/64 vrf v$h1 nexthop via 2001:db8:1::2 +} + +h1_destroy() +{ + ip -6 route del 2001:db8:2::/64 vrf v$h1 + ip -4 route del 192.0.2.128/28 vrf v$h1 + vlan_destroy $h1 555 + simple_if_fini $h1 +} + +h2_create() +{ + simple_if_init $h2 192.0.2.130/28 2001:db8:2::2/64 + ip -4 route add 192.0.2.0/28 vrf v$h2 nexthop via 192.0.2.129 + ip -6 route add 2001:db8:1::/64 vrf v$h2 nexthop via 2001:db8:2::1 +} + +h2_destroy() +{ + ip -6 route del 2001:db8:1::/64 vrf v$h2 + ip -4 route del 192.0.2.0/28 vrf v$h2 + simple_if_fini $h2 192.0.2.130/28 +} + +router_create() +{ + ip link add name br1 type bridge vlan_filtering 1 + ip link set dev br1 up + + ip link set dev $swp1 master br1 + ip link set dev $swp1 up + + bridge vlan add dev br1 vid 555 self pvid untagged + bridge vlan add dev $swp1 vid 555 + + __addr_add_del br1 add 192.0.2.2/28 2001:db8:1::2/64 + + ip link set dev $swp2 up + __addr_add_del $swp2 add 192.0.2.129/28 2001:db8:2::1/64 +} + +router_destroy() +{ + __addr_add_del $swp2 del 192.0.2.129/28 2001:db8:2::1/64 + ip link set dev $swp2 down + + __addr_add_del br1 del 192.0.2.2/28 2001:db8:1::2/64 + ip link set dev $swp1 down + ip link set dev $swp1 nomaster + + ip link del dev br1 +} + +setup_prepare() +{ + h1=${NETIFS[p1]} + swp1=${NETIFS[p2]} + + swp2=${NETIFS[p3]} + h2=${NETIFS[p4]} + + vrf_prepare + + h1_create + h2_create + + router_create + + forwarding_enable +} + +cleanup() +{ + pre_cleanup + + forwarding_restore + + router_destroy + + h2_destroy + h1_destroy + + vrf_cleanup +} + +vlan() +{ + RET=0 + + bridge vlan add dev br1 vid 333 self + check_err $? "Can't add a non-PVID VLAN" + bridge vlan del dev br1 vid 333 self + check_err $? "Can't remove a non-PVID VLAN" + + log_test "vlan" +} + +ping_ipv4() +{ + ping_test $h1 192.0.2.130 +} + +ping_ipv6() +{ + ping6_test $h1 2001:db8:2::2 +} + +trap cleanup EXIT + +setup_prepare +setup_wait + +tests_run + +exit $EXIT_STATUS -- cgit v1.2.3-58-ga151 From 99672eb6c63c5207e4e1a6283ec4fc46aafeb07c Mon Sep 17 00:00:00 2001 From: Petr Machata <petrm@mellanox.com> Date: Mon, 25 Jun 2018 16:43:55 +0200 Subject: selftests: net: Test headroom handling of ip6_gre devices Commit 5691484df961 ("net: ip6_gre: Fix headroom request in ip6erspan_tunnel_xmit()") and commit 01b8d064d58b ("net: ip6_gre: Request headroom in __gre6_xmit()") fix problems in reserving headroom in the packets tunneled through ip6gre/tap and ip6erspan netdevices. These two patches included snippets that reproduced the issues. This patch elevates the snippets to a full-fledged test case. Suggested-by: David Miller <davem@davemloft.net> Signed-off-by: Petr Machata <petrm@mellanox.com> Signed-off-by: David S. Miller <davem@davemloft.net> --- tools/testing/selftests/net/ip6_gre_headroom.sh | 65 +++++++++++++++++++++++++ 1 file changed, 65 insertions(+) create mode 100755 tools/testing/selftests/net/ip6_gre_headroom.sh (limited to 'tools') diff --git a/tools/testing/selftests/net/ip6_gre_headroom.sh b/tools/testing/selftests/net/ip6_gre_headroom.sh new file mode 100755 index 000000000000..5b41e8bb6e2d --- /dev/null +++ b/tools/testing/selftests/net/ip6_gre_headroom.sh @@ -0,0 +1,65 @@ +#!/bin/bash +# SPDX-License-Identifier: GPL-2.0 +# +# Test that enough headroom is reserved for the first packet passing through an +# IPv6 GRE-like netdevice. + +setup_prepare() +{ + ip link add h1 type veth peer name swp1 + ip link add h3 type veth peer name swp3 + + ip link set dev h1 up + ip address add 192.0.2.1/28 dev h1 + + ip link add dev vh3 type vrf table 20 + ip link set dev h3 master vh3 + ip link set dev vh3 up + ip link set dev h3 up + + ip link set dev swp3 up + ip address add dev swp3 2001:db8:2::1/64 + ip address add dev swp3 2001:db8:2::3/64 + + ip link set dev swp1 up + tc qdisc add dev swp1 clsact + + ip link add name er6 type ip6erspan \ + local 2001:db8:2::1 remote 2001:db8:2::2 oseq okey 123 + ip link set dev er6 up + + ip link add name gt6 type ip6gretap \ + local 2001:db8:2::3 remote 2001:db8:2::4 + ip link set dev gt6 up + + sleep 1 +} + +cleanup() +{ + ip link del dev gt6 + ip link del dev er6 + ip link del dev swp1 + ip link del dev swp3 + ip link del dev vh3 +} + +test_headroom() +{ + local type=$1; shift + local tundev=$1; shift + + tc filter add dev swp1 ingress pref 1000 matchall skip_hw \ + action mirred egress mirror dev $tundev + ping -I h1 192.0.2.2 -c 1 -w 2 &> /dev/null + tc filter del dev swp1 ingress pref 1000 + + # If it doesn't panic, it passes. + printf "TEST: %-60s [PASS]\n" "$type headroom" +} + +trap cleanup EXIT + +setup_prepare +test_headroom ip6gretap gt6 +test_headroom ip6erspan er6 -- cgit v1.2.3-58-ga151 From 27a2628b3c24ce8ef23bd393f5514f8f45188f08 Mon Sep 17 00:00:00 2001 From: Petr Machata <petrm@mellanox.com> Date: Tue, 26 Jun 2018 01:20:32 +0200 Subject: selftests: forwarding: mirror_gre_vlan_bridge_1q: Unset rp_filter The IP addresses of tunnel endpoint at H3 are set at the VLAN device $h3.555. Therefore when test_gretap_untagged_egress() sets vlan 555 to egress untagged at $swp3, $h3's rp_filter rejects these packets. The test then spuriously fails. Therefore turn off net.ipv4.conf.{all, $h3}.rp_filter. Fixes: 9c7c8a82442c ("selftests: forwarding: mirror_gre_vlan_bridge_1q: Add more tests") Signed-off-by: Petr Machata <petrm@mellanox.com> Reviewed-by: Ido Schimmel <idosch@mellanox.com> Signed-off-by: David S. Miller <davem@davemloft.net> --- .../selftests/net/forwarding/mirror_gre_vlan_bridge_1q.sh | 9 +++++++++ 1 file changed, 9 insertions(+) (limited to 'tools') diff --git a/tools/testing/selftests/net/forwarding/mirror_gre_vlan_bridge_1q.sh b/tools/testing/selftests/net/forwarding/mirror_gre_vlan_bridge_1q.sh index 5dbc7a08f4bd..1ac5038ae256 100755 --- a/tools/testing/selftests/net/forwarding/mirror_gre_vlan_bridge_1q.sh +++ b/tools/testing/selftests/net/forwarding/mirror_gre_vlan_bridge_1q.sh @@ -39,6 +39,12 @@ setup_prepare() swp3=${NETIFS[p5]} h3=${NETIFS[p6]} + # gt4's remote address is at $h3.555, not $h3. Thus the packets arriving + # directly to $h3 for test_gretap_untagged_egress() are rejected by + # rp_filter and the test spuriously fails. + sysctl_set net.ipv4.conf.all.rp_filter 0 + sysctl_set net.ipv4.conf.$h3.rp_filter 0 + vrf_prepare mirror_gre_topo_create @@ -65,6 +71,9 @@ cleanup() mirror_gre_topo_destroy vrf_cleanup + + sysctl_restore net.ipv4.conf.$h3.rp_filter + sysctl_restore net.ipv4.conf.all.rp_filter } test_vlan_match() -- cgit v1.2.3-58-ga151 From b2c478723c7f8fa78d4fbdb9d7de4191249b92e0 Mon Sep 17 00:00:00 2001 From: Petr Machata <petrm@mellanox.com> Date: Tue, 26 Jun 2018 02:06:06 +0200 Subject: selftests: forwarding: Move multipath_eval() to lib.sh This function will be useful for the GRE multipath test that is coming later. Signed-off-by: Petr Machata <petrm@mellanox.com> Reviewed-by: Ido Schimmel <idosch@mellanox.com> Signed-off-by: David S. Miller <davem@davemloft.net> --- tools/testing/selftests/net/forwarding/lib.sh | 39 ++++++++++++++++++++++ .../selftests/net/forwarding/router_multipath.sh | 39 ---------------------- 2 files changed, 39 insertions(+), 39 deletions(-) (limited to 'tools') diff --git a/tools/testing/selftests/net/forwarding/lib.sh b/tools/testing/selftests/net/forwarding/lib.sh index 7b18a53aa556..7fae805147ae 100644 --- a/tools/testing/selftests/net/forwarding/lib.sh +++ b/tools/testing/selftests/net/forwarding/lib.sh @@ -557,6 +557,45 @@ tests_run() done } +multipath_eval() +{ + local desc="$1" + local weight_rp12=$2 + local weight_rp13=$3 + local packets_rp12=$4 + local packets_rp13=$5 + local weights_ratio packets_ratio diff + + RET=0 + + if [[ "$packets_rp12" -eq "0" || "$packets_rp13" -eq "0" ]]; then + check_err 1 "Packet difference is 0" + log_test "Multipath" + log_info "Expected ratio $weights_ratio" + return + fi + + if [[ "$weight_rp12" -gt "$weight_rp13" ]]; then + weights_ratio=$(echo "scale=2; $weight_rp12 / $weight_rp13" \ + | bc -l) + packets_ratio=$(echo "scale=2; $packets_rp12 / $packets_rp13" \ + | bc -l) + else + weights_ratio=$(echo "scale=2; $weight_rp13 / $weight_rp12" | \ + bc -l) + packets_ratio=$(echo "scale=2; $packets_rp13 / $packets_rp12" | \ + bc -l) + fi + + diff=$(echo $weights_ratio - $packets_ratio | bc -l) + diff=${diff#-} + + test "$(echo "$diff / $weights_ratio > 0.15" | bc -l)" -eq 0 + check_err $? "Too large discrepancy between expected and measured ratios" + log_test "$desc" + log_info "Expected ratio $weights_ratio Measured ratio $packets_ratio" +} + ############################################################################## # Tests diff --git a/tools/testing/selftests/net/forwarding/router_multipath.sh b/tools/testing/selftests/net/forwarding/router_multipath.sh index 8b6d0fb6d604..79a209927962 100755 --- a/tools/testing/selftests/net/forwarding/router_multipath.sh +++ b/tools/testing/selftests/net/forwarding/router_multipath.sh @@ -159,45 +159,6 @@ router2_destroy() vrf_destroy "vrf-r2" } -multipath_eval() -{ - local desc="$1" - local weight_rp12=$2 - local weight_rp13=$3 - local packets_rp12=$4 - local packets_rp13=$5 - local weights_ratio packets_ratio diff - - RET=0 - - if [[ "$packets_rp12" -eq "0" || "$packets_rp13" -eq "0" ]]; then - check_err 1 "Packet difference is 0" - log_test "Multipath" - log_info "Expected ratio $weights_ratio" - return - fi - - if [[ "$weight_rp12" -gt "$weight_rp13" ]]; then - weights_ratio=$(echo "scale=2; $weight_rp12 / $weight_rp13" \ - | bc -l) - packets_ratio=$(echo "scale=2; $packets_rp12 / $packets_rp13" \ - | bc -l) - else - weights_ratio=$(echo "scale=2; $weight_rp13 / $weight_rp12" | \ - bc -l) - packets_ratio=$(echo "scale=2; $packets_rp13 / $packets_rp12" | \ - bc -l) - fi - - diff=$(echo $weights_ratio - $packets_ratio | bc -l) - diff=${diff#-} - - test "$(echo "$diff / $weights_ratio > 0.15" | bc -l)" -eq 0 - check_err $? "Too large discrepancy between expected and measured ratios" - log_test "$desc" - log_info "Expected ratio $weights_ratio Measured ratio $packets_ratio" -} - multipath4_test() { local desc="$1" -- cgit v1.2.3-58-ga151 From 1b86fa3bbacef0a8e9961257557801ab1b5bef78 Mon Sep 17 00:00:00 2001 From: Petr Machata <petrm@mellanox.com> Date: Tue, 26 Jun 2018 02:07:08 +0200 Subject: selftests: forwarding: multipath_eval(): Improve style - Change the indentation of the function body from 7 spaces to one tab. - Move initialization of weights_ratio up so that it can be referenced from the error message about packet difference being zero. - Move |'s consistently to continuation line, which reindent. Signed-off-by: Petr Machata <petrm@mellanox.com> Reviewed-by: Ido Schimmel <idosch@mellanox.com> Signed-off-by: David S. Miller <davem@davemloft.net> --- tools/testing/selftests/net/forwarding/lib.sh | 74 ++++++++++++++------------- 1 file changed, 39 insertions(+), 35 deletions(-) (limited to 'tools') diff --git a/tools/testing/selftests/net/forwarding/lib.sh b/tools/testing/selftests/net/forwarding/lib.sh index 7fae805147ae..911d753c4ff0 100644 --- a/tools/testing/selftests/net/forwarding/lib.sh +++ b/tools/testing/selftests/net/forwarding/lib.sh @@ -559,41 +559,45 @@ tests_run() multipath_eval() { - local desc="$1" - local weight_rp12=$2 - local weight_rp13=$3 - local packets_rp12=$4 - local packets_rp13=$5 - local weights_ratio packets_ratio diff - - RET=0 - - if [[ "$packets_rp12" -eq "0" || "$packets_rp13" -eq "0" ]]; then - check_err 1 "Packet difference is 0" - log_test "Multipath" - log_info "Expected ratio $weights_ratio" - return - fi - - if [[ "$weight_rp12" -gt "$weight_rp13" ]]; then - weights_ratio=$(echo "scale=2; $weight_rp12 / $weight_rp13" \ - | bc -l) - packets_ratio=$(echo "scale=2; $packets_rp12 / $packets_rp13" \ - | bc -l) - else - weights_ratio=$(echo "scale=2; $weight_rp13 / $weight_rp12" | \ - bc -l) - packets_ratio=$(echo "scale=2; $packets_rp13 / $packets_rp12" | \ - bc -l) - fi - - diff=$(echo $weights_ratio - $packets_ratio | bc -l) - diff=${diff#-} - - test "$(echo "$diff / $weights_ratio > 0.15" | bc -l)" -eq 0 - check_err $? "Too large discrepancy between expected and measured ratios" - log_test "$desc" - log_info "Expected ratio $weights_ratio Measured ratio $packets_ratio" + local desc="$1" + local weight_rp12=$2 + local weight_rp13=$3 + local packets_rp12=$4 + local packets_rp13=$5 + local weights_ratio packets_ratio diff + + RET=0 + + if [[ "$weight_rp12" -gt "$weight_rp13" ]]; then + weights_ratio=$(echo "scale=2; $weight_rp12 / $weight_rp13" \ + | bc -l) + else + weights_ratio=$(echo "scale=2; $weight_rp13 / $weight_rp12" \ + | bc -l) + fi + + if [[ "$packets_rp12" -eq "0" || "$packets_rp13" -eq "0" ]]; then + check_err 1 "Packet difference is 0" + log_test "Multipath" + log_info "Expected ratio $weights_ratio" + return + fi + + if [[ "$weight_rp12" -gt "$weight_rp13" ]]; then + packets_ratio=$(echo "scale=2; $packets_rp12 / $packets_rp13" \ + | bc -l) + else + packets_ratio=$(echo "scale=2; $packets_rp13 / $packets_rp12" \ + | bc -l) + fi + + diff=$(echo $weights_ratio - $packets_ratio | bc -l) + diff=${diff#-} + + test "$(echo "$diff / $weights_ratio > 0.15" | bc -l)" -eq 0 + check_err $? "Too large discrepancy between expected and measured ratios" + log_test "$desc" + log_info "Expected ratio $weights_ratio Measured ratio $packets_ratio" } ############################################################################## -- cgit v1.2.3-58-ga151 From a66d62d84225e1c2debaa238cff07a7df86c5aa8 Mon Sep 17 00:00:00 2001 From: Petr Machata <petrm@mellanox.com> Date: Tue, 26 Jun 2018 02:07:45 +0200 Subject: selftests: forwarding: tc_rule_stats_get: Parameterize direction The GRE multipath tests need stats on an egress counter. Change tc_rule_stats_get() to take direction as an optional argument, with default of ingress. Take the opportunity to change line continuation character from | to \. Move the | to the next line, which indent. Signed-off-by: Petr Machata <petrm@mellanox.com> Reviewed-by: Ido Schimmel <idosch@mellanox.com> Signed-off-by: David S. Miller <davem@davemloft.net> --- tools/testing/selftests/net/forwarding/lib.sh | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'tools') diff --git a/tools/testing/selftests/net/forwarding/lib.sh b/tools/testing/selftests/net/forwarding/lib.sh index 911d753c4ff0..f94ea4bafa13 100644 --- a/tools/testing/selftests/net/forwarding/lib.sh +++ b/tools/testing/selftests/net/forwarding/lib.sh @@ -383,9 +383,10 @@ tc_rule_stats_get() { local dev=$1; shift local pref=$1; shift + local dir=$1; shift - tc -j -s filter show dev $dev ingress pref $pref | - jq '.[1].options.actions[].stats.packets' + tc -j -s filter show dev $dev ${dir:-ingress} pref $pref \ + | jq '.[1].options.actions[].stats.packets' } mac_get() -- cgit v1.2.3-58-ga151 From 3368b22379d67a92b0da8f2a76a6f0764177601d Mon Sep 17 00:00:00 2001 From: Petr Machata <petrm@mellanox.com> Date: Tue, 26 Jun 2018 02:08:00 +0200 Subject: selftests: forwarding: lib: Extract interface-init functions The function simple_if_init() does two things: it creates a VRF, then moves an interface into this VRF and configures addresses. The latter comes in handy when adding more interfaces into a VRF later on. The situation is similar for simple_if_fini(). Therefore split the interface remastering and address de/initialization logic to a new pair of helpers __simple_if_init() / __simple_if_fini(), and defer to these helpers from simple_if_init() and simple_if_fini(). Signed-off-by: Petr Machata <petrm@mellanox.com> Reviewed-by: Ido Schimmel <idosch@mellanox.com> Signed-off-by: David S. Miller <davem@davemloft.net> --- tools/testing/selftests/net/forwarding/lib.sh | 32 +++++++++++++++++++++------ 1 file changed, 25 insertions(+), 7 deletions(-) (limited to 'tools') diff --git a/tools/testing/selftests/net/forwarding/lib.sh b/tools/testing/selftests/net/forwarding/lib.sh index f94ea4bafa13..1dfdf14894e2 100644 --- a/tools/testing/selftests/net/forwarding/lib.sh +++ b/tools/testing/selftests/net/forwarding/lib.sh @@ -287,6 +287,29 @@ __addr_add_del() done } +__simple_if_init() +{ + local if_name=$1; shift + local vrf_name=$1; shift + local addrs=("${@}") + + ip link set dev $if_name master $vrf_name + ip link set dev $if_name up + + __addr_add_del $if_name add "${addrs[@]}" +} + +__simple_if_fini() +{ + local if_name=$1; shift + local addrs=("${@}") + + __addr_add_del $if_name del "${addrs[@]}" + + ip link set dev $if_name down + ip link set dev $if_name nomaster +} + simple_if_init() { local if_name=$1 @@ -298,11 +321,8 @@ simple_if_init() array=("${@}") vrf_create $vrf_name - ip link set dev $if_name master $vrf_name ip link set dev $vrf_name up - ip link set dev $if_name up - - __addr_add_del $if_name add "${array[@]}" + __simple_if_init $if_name $vrf_name "${array[@]}" } simple_if_fini() @@ -315,9 +335,7 @@ simple_if_fini() vrf_name=v$if_name array=("${@}") - __addr_add_del $if_name del "${array[@]}" - - ip link set dev $if_name down + __simple_if_fini $if_name "${array[@]}" vrf_destroy $vrf_name } -- cgit v1.2.3-58-ga151 From 54818c4c4b9379f942db7d19e5ff23fb08f6bc79 Mon Sep 17 00:00:00 2001 From: Petr Machata <petrm@mellanox.com> Date: Tue, 26 Jun 2018 02:08:05 +0200 Subject: selftests: forwarding: Test multipath tunneling Add a GRE-tunneling test such that there are two tunnels involved, with a multipath route listing both as next hops. Similarly to router_multipath.sh, test that the distribution of traffic to the tunnels honors the configured weights. Signed-off-by: Petr Machata <petrm@mellanox.com> Reviewed-by: Ido Schimmel <idosch@mellanox.com> Signed-off-by: David S. Miller <davem@davemloft.net> --- .../selftests/net/forwarding/gre_multipath.sh | 354 +++++++++++++++++++++ 1 file changed, 354 insertions(+) create mode 100755 tools/testing/selftests/net/forwarding/gre_multipath.sh (limited to 'tools') diff --git a/tools/testing/selftests/net/forwarding/gre_multipath.sh b/tools/testing/selftests/net/forwarding/gre_multipath.sh new file mode 100755 index 000000000000..982cc8c23200 --- /dev/null +++ b/tools/testing/selftests/net/forwarding/gre_multipath.sh @@ -0,0 +1,354 @@ +#!/bin/bash +# SPDX-License-Identifier: GPL-2.0 + +# Test traffic distribution when a wECMP route forwards traffic to two GRE +# tunnels. +# +# +-------------------------+ +# | H1 | +# | $h1 + | +# | 192.0.2.1/28 | | +# | 2001:db8:1::1/64 | | +# +-------------------|-----+ +# | +# +-------------------|------------------------+ +# | SW1 | | +# | $ol1 + | +# | 192.0.2.2/28 | +# | 2001:db8:1::2/64 | +# | | +# | + g1a (gre) + g1b (gre) | +# | loc=192.0.2.65 loc=192.0.2.81 | +# | rem=192.0.2.66 --. rem=192.0.2.82 --. | +# | tos=inherit | tos=inherit | | +# | .------------------' | | +# | | .------------------' | +# | v v | +# | + $ul1.111 (vlan) + $ul1.222 (vlan) | +# | | 192.0.2.129/28 | 192.0.2.145/28 | +# | \ / | +# | \________________/ | +# | | | +# | + $ul1 | +# +------------|-------------------------------+ +# | +# +------------|-------------------------------+ +# | SW2 + $ul2 | +# | _______|________ | +# | / \ | +# | / \ | +# | + $ul2.111 (vlan) + $ul2.222 (vlan) | +# | ^ 192.0.2.130/28 ^ 192.0.2.146/28 | +# | | | | +# | | '------------------. | +# | '------------------. | | +# | + g2a (gre) | + g2b (gre) | | +# | loc=192.0.2.66 | loc=192.0.2.82 | | +# | rem=192.0.2.65 --' rem=192.0.2.81 --' | +# | tos=inherit tos=inherit | +# | | +# | $ol2 + | +# | 192.0.2.17/28 | | +# | 2001:db8:2::1/64 | | +# +-------------------|------------------------+ +# | +# +-------------------|-----+ +# | H2 | | +# | $h2 + | +# | 192.0.2.18/28 | +# | 2001:db8:2::2/64 | +# +-------------------------+ + +ALL_TESTS=" + ping_ipv4 + ping_ipv6 + multipath_ipv4 + multipath_ipv6 + multipath_ipv6_l4 +" + +NUM_NETIFS=6 +source lib.sh + +h1_create() +{ + simple_if_init $h1 192.0.2.1/28 2001:db8:1::1/64 + ip route add vrf v$h1 192.0.2.16/28 via 192.0.2.2 + ip route add vrf v$h1 2001:db8:2::/64 via 2001:db8:1::2 +} + +h1_destroy() +{ + ip route del vrf v$h1 2001:db8:2::/64 via 2001:db8:1::2 + ip route del vrf v$h1 192.0.2.16/28 via 192.0.2.2 + simple_if_fini $h1 192.0.2.1/28 +} + +sw1_create() +{ + simple_if_init $ol1 192.0.2.2/28 2001:db8:1::2/64 + __simple_if_init $ul1 v$ol1 + vlan_create $ul1 111 v$ol1 192.0.2.129/28 + vlan_create $ul1 222 v$ol1 192.0.2.145/28 + + tunnel_create g1a gre 192.0.2.65 192.0.2.66 tos inherit dev v$ol1 + __simple_if_init g1a v$ol1 192.0.2.65/32 + ip route add vrf v$ol1 192.0.2.66/32 via 192.0.2.130 + + tunnel_create g1b gre 192.0.2.81 192.0.2.82 tos inherit dev v$ol1 + __simple_if_init g1b v$ol1 192.0.2.81/32 + ip route add vrf v$ol1 192.0.2.82/32 via 192.0.2.146 + + ip route add vrf v$ol1 192.0.2.16/28 \ + nexthop dev g1a \ + nexthop dev g1b + ip route add vrf v$ol1 2001:db8:2::/64 \ + nexthop dev g1a \ + nexthop dev g1b + + tc qdisc add dev $ul1 clsact + tc filter add dev $ul1 egress pref 111 prot 802.1q \ + flower vlan_id 111 action pass + tc filter add dev $ul1 egress pref 222 prot 802.1q \ + flower vlan_id 222 action pass +} + +sw1_destroy() +{ + tc qdisc del dev $ul1 clsact + + ip route del vrf v$ol1 2001:db8:2::/64 + ip route del vrf v$ol1 192.0.2.16/28 + + ip route del vrf v$ol1 192.0.2.82/32 via 192.0.2.146 + __simple_if_fini g1b 192.0.2.81/32 + tunnel_destroy g1b + + ip route del vrf v$ol1 192.0.2.66/32 via 192.0.2.130 + __simple_if_fini g1a 192.0.2.65/32 + tunnel_destroy g1a + + vlan_destroy $ul1 222 + vlan_destroy $ul1 111 + __simple_if_fini $ul1 + simple_if_fini $ol1 192.0.2.2/28 2001:db8:1::2/64 +} + +sw2_create() +{ + simple_if_init $ol2 192.0.2.17/28 2001:db8:2::1/64 + __simple_if_init $ul2 v$ol2 + vlan_create $ul2 111 v$ol2 192.0.2.130/28 + vlan_create $ul2 222 v$ol2 192.0.2.146/28 + + tunnel_create g2a gre 192.0.2.66 192.0.2.65 tos inherit dev v$ol2 + __simple_if_init g2a v$ol2 192.0.2.66/32 + ip route add vrf v$ol2 192.0.2.65/32 via 192.0.2.129 + + tunnel_create g2b gre 192.0.2.82 192.0.2.81 tos inherit dev v$ol2 + __simple_if_init g2b v$ol2 192.0.2.82/32 + ip route add vrf v$ol2 192.0.2.81/32 via 192.0.2.145 + + ip route add vrf v$ol2 192.0.2.0/28 \ + nexthop dev g2a \ + nexthop dev g2b + ip route add vrf v$ol2 2001:db8:1::/64 \ + nexthop dev g2a \ + nexthop dev g2b +} + +sw2_destroy() +{ + ip route del vrf v$ol2 2001:db8:1::/64 + ip route del vrf v$ol2 192.0.2.0/28 + + ip route del vrf v$ol2 192.0.2.81/32 via 192.0.2.145 + __simple_if_fini g2b 192.0.2.82/32 + tunnel_destroy g2b + + ip route del vrf v$ol2 192.0.2.65/32 via 192.0.2.129 + __simple_if_fini g2a 192.0.2.66/32 + tunnel_destroy g2a + + vlan_destroy $ul2 222 + vlan_destroy $ul2 111 + __simple_if_fini $ul2 + simple_if_fini $ol2 192.0.2.17/28 2001:db8:2::1/64 +} + +h2_create() +{ + simple_if_init $h2 192.0.2.18/28 2001:db8:2::2/64 + ip route add vrf v$h2 192.0.2.0/28 via 192.0.2.17 + ip route add vrf v$h2 2001:db8:1::/64 via 2001:db8:2::1 +} + +h2_destroy() +{ + ip route del vrf v$h2 2001:db8:1::/64 via 2001:db8:2::1 + ip route del vrf v$h2 192.0.2.0/28 via 192.0.2.17 + simple_if_fini $h2 192.0.2.18/28 2001:db8:2::2/64 +} + +setup_prepare() +{ + h1=${NETIFS[p1]} + ol1=${NETIFS[p2]} + + ul1=${NETIFS[p3]} + ul2=${NETIFS[p4]} + + ol2=${NETIFS[p5]} + h2=${NETIFS[p6]} + + vrf_prepare + h1_create + sw1_create + sw2_create + h2_create +} + +cleanup() +{ + pre_cleanup + + h2_destroy + sw2_destroy + sw1_destroy + h1_destroy + vrf_cleanup +} + +multipath4_test() +{ + local what=$1; shift + local weight1=$1; shift + local weight2=$1; shift + + sysctl_set net.ipv4.fib_multipath_hash_policy 1 + ip route replace vrf v$ol1 192.0.2.16/28 \ + nexthop dev g1a weight $weight1 \ + nexthop dev g1b weight $weight2 + + local t0_111=$(tc_rule_stats_get $ul1 111 egress) + local t0_222=$(tc_rule_stats_get $ul1 222 egress) + + ip vrf exec v$h1 \ + $MZ $h1 -q -p 64 -A 192.0.2.1 -B 192.0.2.18 \ + -d 1msec -t udp "sp=1024,dp=0-32768" + + local t1_111=$(tc_rule_stats_get $ul1 111 egress) + local t1_222=$(tc_rule_stats_get $ul1 222 egress) + + local d111=$((t1_111 - t0_111)) + local d222=$((t1_222 - t0_222)) + multipath_eval "$what" $weight1 $weight2 $d111 $d222 + + ip route replace vrf v$ol1 192.0.2.16/28 \ + nexthop dev g1a \ + nexthop dev g1b + sysctl_restore net.ipv4.fib_multipath_hash_policy +} + +multipath6_l4_test() +{ + local what=$1; shift + local weight1=$1; shift + local weight2=$1; shift + + sysctl_set net.ipv6.fib_multipath_hash_policy 1 + ip route replace vrf v$ol1 2001:db8:2::/64 \ + nexthop dev g1a weight $weight1 \ + nexthop dev g1b weight $weight2 + + local t0_111=$(tc_rule_stats_get $ul1 111 egress) + local t0_222=$(tc_rule_stats_get $ul1 222 egress) + + ip vrf exec v$h1 \ + $MZ $h1 -6 -q -p 64 -A 2001:db8:1::1 -B 2001:db8:2::2 \ + -d 1msec -t udp "sp=1024,dp=0-32768" + + local t1_111=$(tc_rule_stats_get $ul1 111 egress) + local t1_222=$(tc_rule_stats_get $ul1 222 egress) + + local d111=$((t1_111 - t0_111)) + local d222=$((t1_222 - t0_222)) + multipath_eval "$what" $weight1 $weight2 $d111 $d222 + + ip route replace vrf v$ol1 2001:db8:2::/64 \ + nexthop dev g1a \ + nexthop dev g1b + sysctl_restore net.ipv6.fib_multipath_hash_policy +} + +multipath6_test() +{ + local what=$1; shift + local weight1=$1; shift + local weight2=$1; shift + + ip route replace vrf v$ol1 2001:db8:2::/64 \ + nexthop dev g1a weight $weight1 \ + nexthop dev g1b weight $weight2 + + local t0_111=$(tc_rule_stats_get $ul1 111 egress) + local t0_222=$(tc_rule_stats_get $ul1 222 egress) + + # Generate 16384 echo requests, each with a random flow label. + for ((i=0; i < 16384; ++i)); do + ip vrf exec v$h1 $PING6 2001:db8:2::2 -F 0 -c 1 -q &> /dev/null + done + + local t1_111=$(tc_rule_stats_get $ul1 111 egress) + local t1_222=$(tc_rule_stats_get $ul1 222 egress) + + local d111=$((t1_111 - t0_111)) + local d222=$((t1_222 - t0_222)) + multipath_eval "$what" $weight1 $weight2 $d111 $d222 + + ip route replace vrf v$ol1 2001:db8:2::/64 \ + nexthop dev g1a \ + nexthop dev g1b +} + +ping_ipv4() +{ + ping_test $h1 192.0.2.18 +} + +ping_ipv6() +{ + ping6_test $h1 2001:db8:2::2 +} + +multipath_ipv4() +{ + log_info "Running IPv4 multipath tests" + multipath4_test "ECMP" 1 1 + multipath4_test "Weighted MP 2:1" 2 1 + multipath4_test "Weighted MP 11:45" 11 45 +} + +multipath_ipv6() +{ + log_info "Running IPv6 multipath tests" + multipath6_test "ECMP" 1 1 + multipath6_test "Weighted MP 2:1" 2 1 + multipath6_test "Weighted MP 11:45" 11 45 +} + +multipath_ipv6_l4() +{ + log_info "Running IPv6 L4 hash multipath tests" + multipath6_l4_test "ECMP" 1 1 + multipath6_l4_test "Weighted MP 2:1" 2 1 + multipath6_l4_test "Weighted MP 11:45" 11 45 +} + +trap cleanup EXIT + +setup_prepare +setup_wait +tests_run + +exit $EXIT_STATUS -- cgit v1.2.3-58-ga151 From 18ec44f6effbd7bd6f7ec95e8ccd324b05219224 Mon Sep 17 00:00:00 2001 From: Petr Machata <petrm@mellanox.com> Date: Tue, 26 Jun 2018 02:08:17 +0200 Subject: selftests: forwarding: README: Require diagrams ASCII art diagrams are well suited for presenting the topology that a test uses while being easy to embed directly in the test file iteslf. They make the information very easy to grasp even for simple topologies, and for more complex ones they are almost essential, as figuring out the interconnects from the script itself proves to be difficult. Therefore state the requirement for topology ASCII art in README. Signed-off-by: Petr Machata <petrm@mellanox.com> Reviewed-by: Ido Schimmel <idosch@mellanox.com> Signed-off-by: David S. Miller <davem@davemloft.net> --- tools/testing/selftests/net/forwarding/README | 2 ++ 1 file changed, 2 insertions(+) (limited to 'tools') diff --git a/tools/testing/selftests/net/forwarding/README b/tools/testing/selftests/net/forwarding/README index 4a0964c42860..b8a2af8fcfb7 100644 --- a/tools/testing/selftests/net/forwarding/README +++ b/tools/testing/selftests/net/forwarding/README @@ -46,6 +46,8 @@ Guidelines for Writing Tests o Where possible, reuse an existing topology for different tests instead of recreating the same topology. +o Tests that use anything but the most trivial topologies should include + an ASCII art showing the topology. o Where possible, IPv6 and IPv4 addresses shall conform to RFC 3849 and RFC 5737, respectively. o Where possible, tests shall be written so that they can be reused by -- cgit v1.2.3-58-ga151 From 22adedd304668f0a2001284ddf225b49e9c05c64 Mon Sep 17 00:00:00 2001 From: Toke Høiland-Jørgensen <toke@toke.dk> Date: Mon, 25 Jun 2018 14:25:02 +0200 Subject: trace_helpers.c: Add helpers to poll multiple perf FDs for events MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add two new helper functions to trace_helpers that supports polling multiple perf file descriptors for events. These are used to the XDP perf_event_output example, which needs to work with one perf fd per CPU. Reviewed-by: Jakub Kicinski <jakub.kicinski@netronome.com> Signed-off-by: Toke Høiland-Jørgensen <toke@toke.dk> Acked-by: Song Liu <songliubraving@fb.com> Signed-off-by: Daniel Borkmann <daniel@iogearbox.net> --- tools/testing/selftests/bpf/trace_helpers.c | 48 +++++++++++++++++++++++++++-- tools/testing/selftests/bpf/trace_helpers.h | 4 +++ 2 files changed, 50 insertions(+), 2 deletions(-) (limited to 'tools') diff --git a/tools/testing/selftests/bpf/trace_helpers.c b/tools/testing/selftests/bpf/trace_helpers.c index 3868dcb63420..cabe2a3a3b30 100644 --- a/tools/testing/selftests/bpf/trace_helpers.c +++ b/tools/testing/selftests/bpf/trace_helpers.c @@ -88,7 +88,7 @@ static int page_size; static int page_cnt = 8; static struct perf_event_mmap_page *header; -int perf_event_mmap(int fd) +int perf_event_mmap_header(int fd, struct perf_event_mmap_page **header) { void *base; int mmap_size; @@ -102,10 +102,15 @@ int perf_event_mmap(int fd) return -1; } - header = base; + *header = base; return 0; } +int perf_event_mmap(int fd) +{ + return perf_event_mmap_header(fd, &header); +} + static int perf_event_poll(int fd) { struct pollfd pfd = { .fd = fd, .events = POLLIN }; @@ -163,3 +168,42 @@ int perf_event_poller(int fd, perf_event_print_fn output_fn) return ret; } + +int perf_event_poller_multi(int *fds, struct perf_event_mmap_page **headers, + int num_fds, perf_event_print_fn output_fn) +{ + enum bpf_perf_event_ret ret; + struct pollfd *pfds; + void *buf = NULL; + size_t len = 0; + int i; + + pfds = calloc(num_fds, sizeof(*pfds)); + if (!pfds) + return LIBBPF_PERF_EVENT_ERROR; + + for (i = 0; i < num_fds; i++) { + pfds[i].fd = fds[i]; + pfds[i].events = POLLIN; + } + + for (;;) { + poll(pfds, num_fds, 1000); + for (i = 0; i < num_fds; i++) { + if (!pfds[i].revents) + continue; + + ret = bpf_perf_event_read_simple(headers[i], + page_cnt * page_size, + page_size, &buf, &len, + bpf_perf_event_print, + output_fn); + if (ret != LIBBPF_PERF_EVENT_CONT) + break; + } + } + free(buf); + free(pfds); + + return ret; +} diff --git a/tools/testing/selftests/bpf/trace_helpers.h b/tools/testing/selftests/bpf/trace_helpers.h index 3b4bcf7f5084..18924f23db1b 100644 --- a/tools/testing/selftests/bpf/trace_helpers.h +++ b/tools/testing/selftests/bpf/trace_helpers.h @@ -3,6 +3,7 @@ #define __TRACE_HELPER_H #include <libbpf.h> +#include <linux/perf_event.h> struct ksym { long addr; @@ -16,6 +17,9 @@ long ksym_get_addr(const char *name); typedef enum bpf_perf_event_ret (*perf_event_print_fn)(void *data, int size); int perf_event_mmap(int fd); +int perf_event_mmap_header(int fd, struct perf_event_mmap_page **header); /* return LIBBPF_PERF_EVENT_DONE or LIBBPF_PERF_EVENT_ERROR */ int perf_event_poller(int fd, perf_event_print_fn output_fn); +int perf_event_poller_multi(int *fds, struct perf_event_mmap_page **headers, + int num_fds, perf_event_print_fn output_fn); #endif -- cgit v1.2.3-58-ga151 From a7f7547f5e2b90554b0f3d1604899a4f26dba92d Mon Sep 17 00:00:00 2001 From: Andrey Ignatov <rdna@fb.com> Date: Tue, 26 Jun 2018 14:22:41 -0700 Subject: selftests/bpf: Test sys_connect BPF hooks with TFO TCP Fast Open is triggered by sys_sendmsg with MSG_FASTOPEN flag for SOCK_STREAM socket. Even though it's sys_sendmsg, it eventually calls __inet_stream_connect the same way sys_connect does for TCP. __inet_stream_connect, in turn, already has BPF hooks for sys_connect. That means TFO is already covered by BPF_CGROUP_INET{4,6}_CONNECT and the only missing piece is selftest. The patch adds selftest for TFO. Signed-off-by: Andrey Ignatov <rdna@fb.com> Signed-off-by: Daniel Borkmann <daniel@iogearbox.net> --- tools/testing/selftests/bpf/test_sock_addr.c | 37 +++++++++++++++++++++++----- 1 file changed, 31 insertions(+), 6 deletions(-) (limited to 'tools') diff --git a/tools/testing/selftests/bpf/test_sock_addr.c b/tools/testing/selftests/bpf/test_sock_addr.c index a5e76b9219b9..2e45c92d1111 100644 --- a/tools/testing/selftests/bpf/test_sock_addr.c +++ b/tools/testing/selftests/bpf/test_sock_addr.c @@ -998,8 +998,9 @@ int init_pktinfo(int domain, struct cmsghdr *cmsg) return 0; } -static int sendmsg_to_server(const struct sockaddr_storage *addr, - socklen_t addr_len, int set_cmsg, int *syscall_err) +static int sendmsg_to_server(int type, const struct sockaddr_storage *addr, + socklen_t addr_len, int set_cmsg, int flags, + int *syscall_err) { union { char buf[CMSG_SPACE(sizeof(struct in6_pktinfo))]; @@ -1022,7 +1023,7 @@ static int sendmsg_to_server(const struct sockaddr_storage *addr, goto err; } - fd = socket(domain, SOCK_DGRAM, 0); + fd = socket(domain, type, 0); if (fd == -1) { log_err("Failed to create client socket"); goto err; @@ -1052,7 +1053,7 @@ static int sendmsg_to_server(const struct sockaddr_storage *addr, } } - if (sendmsg(fd, &hdr, 0) != sizeof(data)) { + if (sendmsg(fd, &hdr, flags) != sizeof(data)) { log_err("Fail to send message to server"); *syscall_err = errno; goto err; @@ -1066,6 +1067,15 @@ out: return fd; } +static int fastconnect_to_server(const struct sockaddr_storage *addr, + socklen_t addr_len) +{ + int sendmsg_err; + + return sendmsg_to_server(SOCK_STREAM, addr, addr_len, /*set_cmsg*/0, + MSG_FASTOPEN, &sendmsg_err); +} + static int recvmsg_from_client(int sockfd, struct sockaddr_storage *src_addr) { struct timeval tv; @@ -1185,6 +1195,20 @@ static int run_connect_test_case(const struct sock_addr_test *test) if (cmp_local_ip(clientfd, &expected_src_addr)) goto err; + if (test->type == SOCK_STREAM) { + /* Test TCP Fast Open scenario */ + clientfd = fastconnect_to_server(&requested_addr, addr_len); + if (clientfd == -1) + goto err; + + /* Make sure src and dst addrs were overridden properly */ + if (cmp_peer_addr(clientfd, &expected_addr)) + goto err; + + if (cmp_local_ip(clientfd, &expected_src_addr)) + goto err; + } + goto out; err: err = -1; @@ -1222,8 +1246,9 @@ static int run_sendmsg_test_case(const struct sock_addr_test *test) if (clientfd >= 0) close(clientfd); - clientfd = sendmsg_to_server(&requested_addr, addr_len, - set_cmsg, &err); + clientfd = sendmsg_to_server(test->type, &requested_addr, + addr_len, set_cmsg, /*flags*/0, + &err); if (err) goto out; else if (clientfd == -1) -- cgit v1.2.3-58-ga151 From 5092ad4dd52dddc9957ebb8adde99f7a22c29f6a Mon Sep 17 00:00:00 2001 From: Keara Leibovitz <kleib@mojatatu.com> Date: Tue, 26 Jun 2018 10:16:28 -0400 Subject: tc-tests: add an extreme-case csum action test Added an extreme-case test for all 7 csum action headers. Signed-off-by: Keara Leibovitz <kleib@mojatatu.com> Signed-off-by: David S. Miller <davem@davemloft.net> --- .../tc-testing/tc-tests/actions/csum.json | 24 ++++++++++++++++++++++ 1 file changed, 24 insertions(+) (limited to 'tools') diff --git a/tools/testing/selftests/tc-testing/tc-tests/actions/csum.json b/tools/testing/selftests/tc-testing/tc-tests/actions/csum.json index 3a2f51fc7fd4..a022792d392a 100644 --- a/tools/testing/selftests/tc-testing/tc-tests/actions/csum.json +++ b/tools/testing/selftests/tc-testing/tc-tests/actions/csum.json @@ -335,6 +335,30 @@ "$TC actions flush action csum" ] }, + { + "id": "b10b", + "name": "Add all 7 csum actions", + "category": [ + "actions", + "csum" + ], + "setup": [ + [ + "$TC actions flush action csum", + 0, + 1, + 255 + ] + ], + "cmdUnderTest": "$TC actions add action csum icmp ip4h sctp igmp udplite udp tcp index 7", + "expExitCode": "0", + "verifyCmd": "$TC actions get action csum index 7", + "matchPattern": "action order [0-9]*: csum \\(iph, icmp, igmp, tcp, udp, udplite, sctp\\).*index 7 ref", + "matchCount": "1", + "teardown": [ + "$TC actions flush action csum" + ] + }, { "id": "ce92", "name": "Add csum udp action with cookie", -- cgit v1.2.3-58-ga151 From fd0e418d6b1d6dfa193f2196534aef1c95c205fc Mon Sep 17 00:00:00 2001 From: Shannon Nelson <shannon.nelson@oracle.com> Date: Tue, 26 Jun 2018 10:07:52 -0700 Subject: selftests: rtnetlink: clear the return code at start of ipsec test Following the custom from the other functions, clear the global ret code before starting the test so as to not have previously failed tests cause us to thing this test has failed. Reported-by: Anders Roxell <anders.roxell@linaro.org> Signed-off-by: Shannon Nelson <shannon.nelson@oracle.com> Signed-off-by: David S. Miller <davem@davemloft.net> --- tools/testing/selftests/net/rtnetlink.sh | 2 ++ 1 file changed, 2 insertions(+) (limited to 'tools') diff --git a/tools/testing/selftests/net/rtnetlink.sh b/tools/testing/selftests/net/rtnetlink.sh index a90eb31abd59..a80a489a7296 100755 --- a/tools/testing/selftests/net/rtnetlink.sh +++ b/tools/testing/selftests/net/rtnetlink.sh @@ -525,6 +525,8 @@ kci_test_macsec() #------------------------------------------------------------------- kci_test_ipsec() { + ret=0 + # find an ip address on this machine and make up a destination srcip=`ip -o addr | awk '/inet / { print $4; }' | grep -v "^127" | head -1 | cut -f1 -d/` net=`echo $srcip | cut -f1-3 -d.` -- cgit v1.2.3-58-ga151 From c3eba0a4ebf0dedabf30804d4cfc31cdd87b5398 Mon Sep 17 00:00:00 2001 From: Shannon Nelson <shannon.nelson@oracle.com> Date: Tue, 26 Jun 2018 10:07:53 -0700 Subject: selftests: rtnetlink: use dummydev as a test device We really shouldn't mess with local system settings, so let's use the already created dummy device instead for ipsec testing. Oh, and let's put the temp file into a proper directory. Signed-off-by: Shannon Nelson <shannon.nelson@oracle.com> Signed-off-by: David S. Miller <davem@davemloft.net> --- tools/testing/selftests/net/rtnetlink.sh | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) (limited to 'tools') diff --git a/tools/testing/selftests/net/rtnetlink.sh b/tools/testing/selftests/net/rtnetlink.sh index a80a489a7296..d92905f18c7c 100755 --- a/tools/testing/selftests/net/rtnetlink.sh +++ b/tools/testing/selftests/net/rtnetlink.sh @@ -526,21 +526,19 @@ kci_test_macsec() kci_test_ipsec() { ret=0 - - # find an ip address on this machine and make up a destination - srcip=`ip -o addr | awk '/inet / { print $4; }' | grep -v "^127" | head -1 | cut -f1 -d/` - net=`echo $srcip | cut -f1-3 -d.` - base=`echo $srcip | cut -f4 -d.` - dstip="$net."`expr $base + 1` - algo="aead rfc4106(gcm(aes)) 0x3132333435363738393031323334353664636261 128" + srcip=192.168.123.1 + dstip=192.168.123.2 + spi=7 + + ip addr add $srcip dev $devdummy # flush to be sure there's nothing configured ip x s flush ; ip x p flush check_err $? # start the monitor in the background - tmpfile=`mktemp ipsectestXXX` + tmpfile=`mktemp /var/run/ipsectestXXX` mpid=`(ip x m > $tmpfile & echo $!) 2>/dev/null` sleep 0.2 @@ -604,6 +602,7 @@ kci_test_ipsec() check_err $? ip x p flush check_err $? + ip addr del $srcip/32 dev $devdummy if [ $ret -ne 0 ]; then echo "FAIL: ipsec" -- cgit v1.2.3-58-ga151 From 2766a11161cc6995421bc5730eb4819231f40cfb Mon Sep 17 00:00:00 2001 From: Shannon Nelson <shannon.nelson@oracle.com> Date: Tue, 26 Jun 2018 10:07:55 -0700 Subject: selftests: rtnetlink: add ipsec offload API test Using the netdevsim as a device for testing, try out the XFRM commands for setting up IPsec hardware offloads. Signed-off-by: Shannon Nelson <shannon.nelson@oracle.com> Signed-off-by: David S. Miller <davem@davemloft.net> --- tools/testing/selftests/net/rtnetlink.sh | 114 +++++++++++++++++++++++++++++++ 1 file changed, 114 insertions(+) (limited to 'tools') diff --git a/tools/testing/selftests/net/rtnetlink.sh b/tools/testing/selftests/net/rtnetlink.sh index d92905f18c7c..08c341b49760 100755 --- a/tools/testing/selftests/net/rtnetlink.sh +++ b/tools/testing/selftests/net/rtnetlink.sh @@ -611,6 +611,119 @@ kci_test_ipsec() echo "PASS: ipsec" } +#------------------------------------------------------------------- +# Example commands +# ip x s add proto esp src 14.0.0.52 dst 14.0.0.70 \ +# spi 0x07 mode transport reqid 0x07 replay-window 32 \ +# aead 'rfc4106(gcm(aes))' 1234567890123456dcba 128 \ +# sel src 14.0.0.52/24 dst 14.0.0.70/24 +# offload dev sim1 dir out +# ip x p add dir out src 14.0.0.52/24 dst 14.0.0.70/24 \ +# tmpl proto esp src 14.0.0.52 dst 14.0.0.70 \ +# spi 0x07 mode transport reqid 0x07 +# +#------------------------------------------------------------------- +kci_test_ipsec_offload() +{ + ret=0 + algo="aead rfc4106(gcm(aes)) 0x3132333435363738393031323334353664636261 128" + srcip=192.168.123.3 + dstip=192.168.123.4 + dev=simx1 + sysfsd=/sys/kernel/debug/netdevsim/$dev + sysfsf=$sysfsd/ipsec + + # setup netdevsim since dummydev doesn't have offload support + modprobe netdevsim + check_err $? + if [ $ret -ne 0 ]; then + echo "FAIL: ipsec_offload can't load netdevsim" + return 1 + fi + + ip link add $dev type netdevsim + ip addr add $srcip dev $dev + ip link set $dev up + if [ ! -d $sysfsd ] ; then + echo "FAIL: ipsec_offload can't create device $dev" + return 1 + fi + if [ ! -f $sysfsf ] ; then + echo "FAIL: ipsec_offload netdevsim doesn't support IPsec offload" + return 1 + fi + + # flush to be sure there's nothing configured + ip x s flush ; ip x p flush + + # create offloaded SAs, both in and out + ip x p add dir out src $srcip/24 dst $dstip/24 \ + tmpl proto esp src $srcip dst $dstip spi 9 \ + mode transport reqid 42 + check_err $? + ip x p add dir out src $dstip/24 dst $srcip/24 \ + tmpl proto esp src $dstip dst $srcip spi 9 \ + mode transport reqid 42 + check_err $? + + ip x s add proto esp src $srcip dst $dstip spi 9 \ + mode transport reqid 42 $algo sel src $srcip/24 dst $dstip/24 \ + offload dev $dev dir out + check_err $? + ip x s add proto esp src $dstip dst $srcip spi 9 \ + mode transport reqid 42 $algo sel src $dstip/24 dst $srcip/24 \ + offload dev $dev dir in + check_err $? + if [ $ret -ne 0 ]; then + echo "FAIL: ipsec_offload can't create SA" + return 1 + fi + + # does offload show up in ip output + lines=`ip x s list | grep -c "crypto offload parameters: dev $dev dir"` + if [ $lines -ne 2 ] ; then + echo "FAIL: ipsec_offload SA offload missing from list output" + check_err 1 + fi + + # use ping to exercise the Tx path + ping -I $dev -c 3 -W 1 -i 0 $dstip >/dev/null + + # does driver have correct offload info + diff $sysfsf - << EOF +SA count=2 tx=3 +sa[0] tx ipaddr=0x00000000 00000000 00000000 00000000 +sa[0] spi=0x00000009 proto=0x32 salt=0x61626364 crypt=1 +sa[0] key=0x34333231 38373635 32313039 36353433 +sa[1] rx ipaddr=0x00000000 00000000 00000000 037ba8c0 +sa[1] spi=0x00000009 proto=0x32 salt=0x61626364 crypt=1 +sa[1] key=0x34333231 38373635 32313039 36353433 +EOF + if [ $? -ne 0 ] ; then + echo "FAIL: ipsec_offload incorrect driver data" + check_err 1 + fi + + # does offload get removed from driver + ip x s flush + ip x p flush + lines=`grep -c "SA count=0" $sysfsf` + if [ $lines -ne 1 ] ; then + echo "FAIL: ipsec_offload SA not removed from driver" + check_err 1 + fi + + # clean up any leftovers + ip link del $dev + rmmod netdevsim + + if [ $ret -ne 0 ]; then + echo "FAIL: ipsec_offload" + return 1 + fi + echo "PASS: ipsec_offload" +} + kci_test_gretap() { testns="testns" @@ -865,6 +978,7 @@ kci_test_rtnl() kci_test_encap kci_test_macsec kci_test_ipsec + kci_test_ipsec_offload kci_del_dummy } -- cgit v1.2.3-58-ga151 From 010079bac0ce294f0ce177c0363e0e7579ed7f0c Mon Sep 17 00:00:00 2001 From: Petr Machata <petrm@mellanox.com> Date: Thu, 28 Jun 2018 18:56:20 +0200 Subject: selftests: forwarding: lib: Split out setup_wait_dev() Split out of setup_wait() a function setup_wait_dev() that waits for a single device. This gives tests the opportunity to wait for a selected device after they tinkered with its upness. Signed-off-by: Petr Machata <petrm@mellanox.com> Signed-off-by: David S. Miller <davem@davemloft.net> --- tools/testing/selftests/net/forwarding/lib.sh | 25 ++++++++++++++++--------- 1 file changed, 16 insertions(+), 9 deletions(-) (limited to 'tools') diff --git a/tools/testing/selftests/net/forwarding/lib.sh b/tools/testing/selftests/net/forwarding/lib.sh index 1dfdf14894e2..ac1df4860fbe 100644 --- a/tools/testing/selftests/net/forwarding/lib.sh +++ b/tools/testing/selftests/net/forwarding/lib.sh @@ -185,18 +185,25 @@ log_info() echo "INFO: $msg" } +setup_wait_dev() +{ + local dev=$1; shift + + while true; do + ip link show dev $dev up \ + | grep 'state UP' &> /dev/null + if [[ $? -ne 0 ]]; then + sleep 1 + else + break + fi + done +} + setup_wait() { for i in $(eval echo {1..$NUM_NETIFS}); do - while true; do - ip link show dev ${NETIFS[p$i]} up \ - | grep 'state UP' &> /dev/null - if [[ $? -ne 0 ]]; then - sleep 1 - else - break - fi - done + setup_wait_dev ${NETIFS[p$i]} done # Make sure links are ready. -- cgit v1.2.3-58-ga151 From ac0fcadf03f8f86be28980d7aa0a09c202ec4f78 Mon Sep 17 00:00:00 2001 From: Petr Machata <petrm@mellanox.com> Date: Thu, 28 Jun 2018 18:56:28 +0200 Subject: selftests: forwarding: lib: Avoid trapping soft devices There are several cases where traffic that would normally be forwarded in silicon needs to be observed in slow path. That's achieved by trapping such traffic, and the functions trap_install() and trap_uninstall() realize that. However, such treatment is obviously wrong if the device in question is actually a soft device not backed by an ASIC. Therefore try to trap if possible, but fall back to inserting a continue if not. Signed-off-by: Petr Machata <petrm@mellanox.com> Signed-off-by: David S. Miller <davem@davemloft.net> --- tools/testing/selftests/net/forwarding/lib.sh | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) (limited to 'tools') diff --git a/tools/testing/selftests/net/forwarding/lib.sh b/tools/testing/selftests/net/forwarding/lib.sh index ac1df4860fbe..d1f14f83979e 100644 --- a/tools/testing/selftests/net/forwarding/lib.sh +++ b/tools/testing/selftests/net/forwarding/lib.sh @@ -479,9 +479,15 @@ trap_install() local dev=$1; shift local direction=$1; shift - # For slow-path testing, we need to install a trap to get to - # slow path the packets that would otherwise be switched in HW. - tc filter add dev $dev $direction pref 1 flower skip_sw action trap + # Some devices may not support or need in-hardware trapping of traffic + # (e.g. the veth pairs that this library creates for non-existent + # loopbacks). Use continue instead, so that there is a filter in there + # (some tests check counters), and so that other filters are still + # processed. + tc filter add dev $dev $direction pref 1 \ + flower skip_sw action trap 2>/dev/null \ + || tc filter add dev $dev $direction pref 1 \ + flower action continue } trap_uninstall() @@ -489,11 +495,13 @@ trap_uninstall() local dev=$1; shift local direction=$1; shift - tc filter del dev $dev $direction pref 1 flower skip_sw + tc filter del dev $dev $direction pref 1 flower } slow_path_trap_install() { + # For slow-path testing, we need to install a trap to get to + # slow path the packets that would otherwise be switched in HW. if [ "${tcflags/skip_hw}" != "$tcflags" ]; then trap_install "$@" fi -- cgit v1.2.3-58-ga151 From ec9fdc99f5a6a2cfe4061e807fcb0cc1129f0a2d Mon Sep 17 00:00:00 2001 From: Petr Machata <petrm@mellanox.com> Date: Thu, 28 Jun 2018 18:56:33 +0200 Subject: selftests: forwarding: Tweak tc filters for mirror-to-gretap tests When running mirror_gre_bridge_1d_vlan tests on veth, several issues cause spurious failures: - vlan_ethtype should be ip, not ipv6 even in mirror-to-ip6gretap case, because the overlay packet is still IPv4. - Similarly ip_proto matches the innermost IP protocol, so can't be used to filter out GRE packet. Drop the corresponding condition. - Because the above fixes the filters to match in slow path as well, they need to be made skip_hw so as not to double-count packets. Signed-off-by: Petr Machata <petrm@mellanox.com> Signed-off-by: David S. Miller <davem@davemloft.net> --- tools/testing/selftests/net/forwarding/mirror_gre_bridge_1d_vlan.sh | 6 ++++-- tools/testing/selftests/net/forwarding/mirror_gre_lib.sh | 2 +- tools/testing/selftests/net/forwarding/mirror_gre_vlan_bridge_1q.sh | 6 ++++-- 3 files changed, 9 insertions(+), 5 deletions(-) (limited to 'tools') diff --git a/tools/testing/selftests/net/forwarding/mirror_gre_bridge_1d_vlan.sh b/tools/testing/selftests/net/forwarding/mirror_gre_bridge_1d_vlan.sh index 3bb4c2ba7b14..197e769c2ed1 100755 --- a/tools/testing/selftests/net/forwarding/mirror_gre_bridge_1d_vlan.sh +++ b/tools/testing/selftests/net/forwarding/mirror_gre_bridge_1d_vlan.sh @@ -74,12 +74,14 @@ test_vlan_match() test_gretap() { - test_vlan_match gt4 'vlan_id 555 vlan_ethtype ip' "mirror to gretap" + test_vlan_match gt4 'skip_hw vlan_id 555 vlan_ethtype ip' \ + "mirror to gretap" } test_ip6gretap() { - test_vlan_match gt6 'vlan_id 555 vlan_ethtype ipv6' "mirror to ip6gretap" + test_vlan_match gt6 'skip_hw vlan_id 555 vlan_ethtype ip' \ + "mirror to ip6gretap" } test_gretap_stp() diff --git a/tools/testing/selftests/net/forwarding/mirror_gre_lib.sh b/tools/testing/selftests/net/forwarding/mirror_gre_lib.sh index 619b469365be..1c18e332cd4f 100644 --- a/tools/testing/selftests/net/forwarding/mirror_gre_lib.sh +++ b/tools/testing/selftests/net/forwarding/mirror_gre_lib.sh @@ -62,7 +62,7 @@ full_test_span_gre_dir_vlan_ips() "$backward_type" "$ip1" "$ip2" tc filter add dev $h3 ingress pref 77 prot 802.1q \ - flower $vlan_match ip_proto 0x2f \ + flower $vlan_match \ action pass mirror_test v$h1 $ip1 $ip2 $h3 77 10 tc filter del dev $h3 ingress pref 77 diff --git a/tools/testing/selftests/net/forwarding/mirror_gre_vlan_bridge_1q.sh b/tools/testing/selftests/net/forwarding/mirror_gre_vlan_bridge_1q.sh index 1ac5038ae256..d3e75bb6a2d8 100755 --- a/tools/testing/selftests/net/forwarding/mirror_gre_vlan_bridge_1q.sh +++ b/tools/testing/selftests/net/forwarding/mirror_gre_vlan_bridge_1q.sh @@ -88,12 +88,14 @@ test_vlan_match() test_gretap() { - test_vlan_match gt4 'vlan_id 555 vlan_ethtype ip' "mirror to gretap" + test_vlan_match gt4 'skip_hw vlan_id 555 vlan_ethtype ip' \ + "mirror to gretap" } test_ip6gretap() { - test_vlan_match gt6 'vlan_id 555 vlan_ethtype ipv6' "mirror to ip6gretap" + test_vlan_match gt6 'skip_hw vlan_id 555 vlan_ethtype ip' \ + "mirror to ip6gretap" } test_span_gre_forbidden_cpu() -- cgit v1.2.3-58-ga151 From 4e74cc7c5d3f58cb0e2534cd68716c5746d52d07 Mon Sep 17 00:00:00 2001 From: Petr Machata <petrm@mellanox.com> Date: Thu, 28 Jun 2018 18:56:39 +0200 Subject: selftests: forwarding: mirror_gre_changes: Fix waiting for neighbor When running the test on soft devices, there's no mechanism to gratuitously start resolving the neighbor for remote tunnel endpoint. So instead of passively waiting, wait for the device to be up, and then probe the neighbor with a ping. Signed-off-by: Petr Machata <petrm@mellanox.com> Signed-off-by: David S. Miller <davem@davemloft.net> --- tools/testing/selftests/net/forwarding/mirror_gre_changes.sh | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) (limited to 'tools') diff --git a/tools/testing/selftests/net/forwarding/mirror_gre_changes.sh b/tools/testing/selftests/net/forwarding/mirror_gre_changes.sh index aa29d46186a8..135902aa8b11 100755 --- a/tools/testing/selftests/net/forwarding/mirror_gre_changes.sh +++ b/tools/testing/selftests/net/forwarding/mirror_gre_changes.sh @@ -122,15 +122,8 @@ test_span_gre_egress_up() # After setting the device up, wait for neighbor to get resolved so that # we can expect mirroring to work. ip link set dev $swp3 up - while true; do - ip neigh sh dev $swp3 $remote_ip nud reachable | - grep -q ^ - if [[ $? -ne 0 ]]; then - sleep 1 - else - break - fi - done + setup_wait_dev $swp3 + ping -c 1 -I $swp3 $remote_ip &>/dev/null quick_test_span_gre_dir $tundev ingress mirror_uninstall $swp1 ingress -- cgit v1.2.3-58-ga151 From 180390c470e1b6e3e19b4f969524182eeb5e4e6c Mon Sep 17 00:00:00 2001 From: Keara Leibovitz <kleib@mojatatu.com> Date: Fri, 29 Jun 2018 10:47:31 -0400 Subject: tc-testing: initial version of tunnel_key unit tests Create unittests for the tc tunnel_key action. v2: For the tests expecting failures, added non-zero exit codes in the teardowns. This prevents those tests from failing if the act_tunnel_key module is unloaded. Signed-off-by: Keara Leibovitz <kleib@mojatatu.com> Signed-off-by: David S. Miller <davem@davemloft.net> --- .../tc-testing/tc-tests/actions/tunnel_key.json | 749 +++++++++++++++++++++ 1 file changed, 749 insertions(+) create mode 100644 tools/testing/selftests/tc-testing/tc-tests/actions/tunnel_key.json (limited to 'tools') diff --git a/tools/testing/selftests/tc-testing/tc-tests/actions/tunnel_key.json b/tools/testing/selftests/tc-testing/tc-tests/actions/tunnel_key.json new file mode 100644 index 000000000000..d878ce1c7d08 --- /dev/null +++ b/tools/testing/selftests/tc-testing/tc-tests/actions/tunnel_key.json @@ -0,0 +1,749 @@ +[ + { + "id": "2b11", + "name": "Add tunnel_key set action with mandatory parameters", + "category": [ + "actions", + "tunnel_key" + ], + "setup": [ + [ + "$TC actions flush action tunnel_key", + 0, + 1, + 255 + ] + ], + "cmdUnderTest": "$TC actions add action tunnel_key set src_ip 10.10.10.1 dst_ip 20.20.20.2 id 1", + "expExitCode": "0", + "verifyCmd": "$TC actions list action tunnel_key", + "matchPattern": "action order [0-9]+: tunnel_key.*set.*src_ip 10.10.10.1.*dst_ip 20.20.20.2.*key_id 1", + "matchCount": "1", + "teardown": [ + "$TC actions flush action tunnel_key" + ] + }, + { + "id": "dc6b", + "name": "Add tunnel_key set action with missing mandatory src_ip parameter", + "category": [ + "actions", + "tunnel_key" + ], + "setup": [ + [ + "$TC actions flush action tunnel_key", + 0, + 1, + 255 + ] + ], + "cmdUnderTest": "$TC actions add action tunnel_key set dst_ip 20.20.20.2 id 100", + "expExitCode": "255", + "verifyCmd": "$TC actions list action tunnel_key", + "matchPattern": "action order [0-9]+: tunnel_key set.*dst_ip 20.20.20.2.*key_id 100", + "matchCount": "0", + "teardown": [ + [ + "$TC actions flush action tunnel_key", + 0, + 1, + 255 + ] + ] + }, + { + "id": "7f25", + "name": "Add tunnel_key set action with missing mandatory dst_ip parameter", + "category": [ + "actions", + "tunnel_key" + ], + "setup": [ + [ + "$TC actions flush action tunnel_key", + 0, + 1, + 255 + ] + ], + "cmdUnderTest": "$TC actions add action tunnel_key set src_ip 10.10.10.1 id 100", + "expExitCode": "255", + "verifyCmd": "$TC actions list action tunnel_key", + "matchPattern": "action order [0-9]+: tunnel_key set.*src_ip 10.10.10.1.*key_id 100", + "matchCount": "0", + "teardown": [ + [ + "$TC actions flush action tunnel_key", + 0, + 1, + 255 + ] + ] + }, + { + "id": "ba4e", + "name": "Add tunnel_key set action with missing mandatory id parameter", + "category": [ + "actions", + "tunnel_key" + ], + "setup": [ + [ + "$TC actions flush action tunnel_key", + 0, + 1, + 255 + ] + ], + "cmdUnderTest": "$TC actions add action tunnel_key set src_ip 10.10.10.1 dst_ip 20.20.20.2", + "expExitCode": "255", + "verifyCmd": "$TC actions list action tunnel_key", + "matchPattern": "action order [0-9]+: tunnel_key set.*src_ip 10.10.10.1.*dst_ip 20.20.20.2", + "matchCount": "0", + "teardown": [ + [ + "$TC actions flush action tunnel_key", + 0, + 1, + 255 + ] + ] + }, + { + "id": "a5e0", + "name": "Add tunnel_key set action with invalid src_ip parameter", + "category": [ + "actions", + "tunnel_key" + ], + "setup": [ + [ + "$TC actions flush action tunnel_key", + 0, + 1, + 255 + ] + ], + "cmdUnderTest": "$TC actions add action tunnel_key set src_ip 300.168.100.1 dst_ip 192.168.200.1 id 7 index 1", + "expExitCode": "1", + "verifyCmd": "$TC actions get action tunnel_key index 1", + "matchPattern": "action order [0-9]+: tunnel_key set.*src_ip 300.168.100.1.*dst_ip 192.168.200.1.*key_id 7.*index 1 ref", + "matchCount": "0", + "teardown": [ + [ + "$TC actions flush action tunnel_key", + 0, + 1, + 255 + ] + ] + }, + { + "id": "eaa8", + "name": "Add tunnel_key set action with invalid dst_ip parameter", + "category": [ + "actions", + "tunnel_key" + ], + "setup": [ + [ + "$TC actions flush action tunnel_key", + 0, + 1, + 255 + ] + ], + "cmdUnderTest": "$TC actions add action tunnel_key set src_ip 192.168.100.1 dst_ip 192.168.800.1 id 10 index 11", + "expExitCode": "1", + "verifyCmd": "$TC actions get action tunnel_key index 11", + "matchPattern": "action order [0-9]+: tunnel_key set.*src_ip 192.168.100.1.*dst_ip 192.168.800.1.*key_id 10.*index 11 ref", + "matchCount": "0", + "teardown": [ + [ + "$TC actions flush action tunnel_key", + 0, + 1, + 255 + ] + ] + }, + { + "id": "3b09", + "name": "Add tunnel_key set action with invalid id parameter", + "category": [ + "actions", + "tunnel_key" + ], + "setup": [ + [ + "$TC actions flush action tunnel_key", + 0, + 1, + 255 + ] + ], + "cmdUnderTest": "$TC actions add action tunnel_key set src_ip 1.1.1.1 dst_ip 2.2.2.2 id 112233445566778899 index 1", + "expExitCode": "255", + "verifyCmd": "$TC actions get action tunnel_key index 1", + "matchPattern": "action order [0-9]+: tunnel_key set.*src_ip 1.1.1.1.*dst_ip 2.2.2.2.*key_id 112233445566778899.*index 1 ref", + "matchCount": "0", + "teardown": [ + [ + "$TC actions flush action tunnel_key", + 0, + 1, + 255 + ] + ] + }, + { + "id": "9625", + "name": "Add tunnel_key set action with invalid dst_port parameter", + "category": [ + "actions", + "tunnel_key" + ], + "setup": [ + [ + "$TC actions flush action tunnel_key", + 0, + 1, + 255 + ] + ], + "cmdUnderTest": "$TC actions add action tunnel_key set src_ip 1.1.1.1 dst_ip 2.2.2.2 id 11 dst_port 998877 index 1", + "expExitCode": "255", + "verifyCmd": "$TC actions get action tunnel_key index 1", + "matchPattern": "action order [0-9]+: tunnel_key set.*src_ip 1.1.1.1.*dst_ip 2.2.2.2.*key_id 11.*dst_port 998877.*index 1 ref", + "matchCount": "0", + "teardown": [ + [ + "$TC actions flush action tunnel_key", + 0, + 1, + 255 + ] + ] + }, + { + "id": "05af", + "name": "Add tunnel_key set action with optional dst_port parameter", + "category": [ + "actions", + "tunnel_key" + ], + "setup": [ + [ + "$TC actions flush action tunnel_key", + 0, + 1, + 255 + ] + ], + "cmdUnderTest": "$TC actions add action tunnel_key set src_ip 192.168.100.1 dst_ip 192.168.200.1 id 789 dst_port 4000 index 10", + "expExitCode": "0", + "verifyCmd": "$TC actions get action tunnel_key index 10", + "matchPattern": "action order [0-9]+: tunnel_key.*set.*src_ip 192.168.100.1.*dst_ip 192.168.200.1.*key_id 789.*dst_port 4000.*index 10 ref", + "matchCount": "1", + "teardown": [ + "$TC actions flush action tunnel_key" + ] + }, + { + "id": "da80", + "name": "Add tunnel_key set action with index at 32-bit maximum", + "category": [ + "actions", + "tunnel_key" + ], + "setup": [ + [ + "$TC actions flush action tunnel_key", + 0, + 1, + 255 + ] + ], + "cmdUnderTest": "$TC actions add action tunnel_key set src_ip 1.1.1.1 dst_ip 2.2.2.2 id 11 index 4294967295", + "expExitCode": "0", + "verifyCmd": "$TC actions get action tunnel_key index 4294967295", + "matchPattern": "action order [0-9]+: tunnel_key.*set.*src_ip 1.1.1.1.*dst_ip 2.2.2.2.*id 11.*index 4294967295 ref", + "matchCount": "1", + "teardown": [ + "$TC actions flush action tunnel_key" + ] + }, + { + "id": "d407", + "name": "Add tunnel_key set action with index exceeding 32-bit maximum", + "category": [ + "actions", + "tunnel_key" + ], + "setup": [ + [ + "$TC actions flush action tunnel_key", + 0, + 1, + 255 + ] + ], + "cmdUnderTest": "$TC actions add action tunnel_key set src_ip 1.1.1.1 dst_ip 2.2.2.2 id 11 index 4294967295678", + "expExitCode": "255", + "verifyCmd": "$TC actions get action tunnel_key index 4294967295678", + "matchPattern": "action order [0-9]+: tunnel_key set.*index 4294967295678 ref", + "matchCount": "0", + "teardown": [ + [ + "$TC actions flush action tunnel_key", + 0, + 1, + 255 + ] + ] + }, + { + "id": "5cba", + "name": "Add tunnel_key set action with id value at 32-bit maximum", + "category": [ + "actions", + "tunnel_key" + ], + "setup": [ + [ + "$TC actions flush action tunnel_key", + 0, + 1, + 255 + ] + ], + "cmdUnderTest": "$TC actions add action tunnel_key set src_ip 1.1.1.1 dst_ip 2.2.2.2 id 4294967295 index 1", + "expExitCode": "0", + "verifyCmd": "$TC actions get action tunnel_key index 1", + "matchPattern": "action order [0-9]+: tunnel_key.*set.*src_ip 1.1.1.1.*dst_ip 2.2.2.2.*key_id 4294967295.*index 1", + "matchCount": "1", + "teardown": [ + "$TC actions flush action tunnel_key" + ] + }, + { + "id": "e84a", + "name": "Add tunnel_key set action with id value exceeding 32-bit maximum", + "category": [ + "actions", + "tunnel_key" + ], + "setup": [ + [ + "$TC actions flush action tunnel_key", + 0, + 1, + 255 + ] + ], + "cmdUnderTest": "$TC actions add action tunnel_key set src_ip 1.1.1.1 dst_ip 2.2.2.2 id 42949672955 index 1", + "expExitCode": "255", + "verifyCmd": "$TC actions get action tunnel_key index 4294967295", + "matchPattern": "action order [0-9]+: tunnel_key.*set.*src_ip 1.1.1.1.*dst_ip 2.2.2.2.*key_id 42949672955.*index 1", + "matchCount": "0", + "teardown": [ + [ + "$TC actions flush action tunnel_key", + 0, + 1, + 255 + ] + ] + }, + { + "id": "9c19", + "name": "Add tunnel_key set action with dst_port value at 16-bit maximum", + "category": [ + "actions", + "tunnel_key" + ], + "setup": [ + [ + "$TC actions flush action tunnel_key", + 0, + 1, + 255 + ] + ], + "cmdUnderTest": "$TC actions add action tunnel_key set src_ip 1.1.1.1 dst_ip 2.2.2.2 id 429 dst_port 65535 index 1", + "expExitCode": "0", + "verifyCmd": "$TC actions get action tunnel_key index 1", + "matchPattern": "action order [0-9]+: tunnel_key.*set.*src_ip 1.1.1.1.*dst_ip 2.2.2.2.*key_id 429.*dst_port 65535.*index 1", + "matchCount": "1", + "teardown": [ + "$TC actions flush action tunnel_key" + ] + }, + { + "id": "3bd9", + "name": "Add tunnel_key set action with dst_port value exceeding 16-bit maximum", + "category": [ + "actions", + "tunnel_key" + ], + "setup": [ + [ + "$TC actions flush action tunnel_key", + 0, + 1, + 255 + ] + ], + "cmdUnderTest": "$TC actions add action tunnel_key set src_ip 1.1.1.1 dst_ip 2.2.2.2 id 429 dst_port 65535789 index 1", + "expExitCode": "255", + "verifyCmd": "$TC actions get action tunnel_key index 1", + "matchPattern": "action order [0-9]+: tunnel_key.*set.*src_ip 1.1.1.1.*dst_ip 2.2.2.2.*key_id 429.*dst_port 65535789.*index 1", + "matchCount": "0", + "teardown": [ + [ + "$TC actions flush action tunnel_key", + 0, + 1, + 255 + ] + ] + }, + { + "id": "68e2", + "name": "Add tunnel_key unset action", + "category": [ + "actions", + "tunnel_key" + ], + "setup": [ + [ + "$TC actions flush action tunnel_key", + 0, + 1, + 255 + ] + ], + "cmdUnderTest": "$TC actions add action tunnel_key unset index 1", + "expExitCode": "0", + "verifyCmd": "$TC actions get action tunnel_key index 1", + "matchPattern": "action order [0-9]+: tunnel_key.*unset.*index 1 ref", + "matchCount": "1", + "teardown": [ + "$TC actions flush action tunnel_key" + ] + }, + { + "id": "6192", + "name": "Add tunnel_key unset continue action", + "category": [ + "actions", + "tunnel_key" + ], + "setup": [ + [ + "$TC actions flush action tunnel_key", + 0, + 1, + 255 + ] + ], + "cmdUnderTest": "$TC actions add action tunnel_key unset continue index 1", + "expExitCode": "0", + "verifyCmd": "$TC actions get action tunnel_key index 1", + "matchPattern": "action order [0-9]+: tunnel_key.*unset continue.*index 1 ref", + "matchCount": "1", + "teardown": [ + "$TC actions flush action tunnel_key" + ] + }, + { + "id": "061d", + "name": "Add tunnel_key set continue action with cookie", + "category": [ + "actions", + "tunnel_key" + ], + "setup": [ + [ + "$TC actions flush action tunnel_key", + 0, + 1, + 255 + ] + ], + "cmdUnderTest": "$TC actions add action tunnel_key set src_ip 192.168.10.1 dst_ip 192.168.20.2 id 123 continue index 1 cookie aa11bb22cc33dd44ee55ff66aa11b1b2", + "expExitCode": "0", + "verifyCmd": "$TC actions get action tunnel_key index 1", + "matchPattern": "action order [0-9]+: tunnel_key.*set.*src_ip 192.168.10.1.*dst_ip 192.168.20.2.*key_id 123.*csum continue.*index 1.*cookie aa11bb22cc33dd44ee55ff66aa11b1b2", + "matchCount": "1", + "teardown": [ + "$TC actions flush action tunnel_key" + ] + }, + { + "id": "8acb", + "name": "Add tunnel_key set continue action with invalid cookie", + "category": [ + "actions", + "tunnel_key" + ], + "setup": [ + [ + "$TC actions flush action tunnel_key", + 0, + 1, + 255 + ] + ], + "cmdUnderTest": "$TC actions add action tunnel_key set src_ip 192.168.10.1 dst_ip 192.168.20.2 id 123 continue index 1 cookie aa11bb22cc33dd44ee55ff66aa11b1b2777888", + "expExitCode": "255", + "verifyCmd": "$TC actions get action tunnel_key index 1", + "matchPattern": "action order [0-9]+: tunnel_key.*set.*src_ip 192.168.10.1.*dst_ip 192.168.20.2.*key_id 123.*csum continue.*index 1.*cookie aa11bb22cc33dd44ee55ff66aa11b1b2777888", + "matchCount": "0", + "teardown": [ + [ + "$TC actions flush action tunnel_key", + 0, + 1, + 255 + ] + ] + }, + { + "id": "a07e", + "name": "Add tunnel_key action with no set/unset command specified", + "category": [ + "actions", + "tunnel_key" + ], + "setup": [ + [ + "$TC actions flush action tunnel_key", + 0, + 1, + 255 + ] + ], + "cmdUnderTest": "$TC actions add action tunnel_key src_ip 10.10.10.1 dst_ip 20.20.20.2 id 1", + "expExitCode": "255", + "verifyCmd": "$TC actions get action tunnel_key index 1", + "matchPattern": "action order [0-9]+: tunnel_key.*src_ip 10.10.10.1.*dst_ip 20.20.20.2.*key_id 1", + "matchCount": "0", + "teardown": [ + [ + "$TC actions flush action tunnel_key", + 0, + 1, + 255 + ] + ] + }, + { + "id": "b227", + "name": "Add tunnel_key action with csum option", + "category": [ + "actions", + "tunnel_key" + ], + "setup": [ + [ + "$TC actions flush action tunnel_key", + 0, + 1, + 255 + ] + ], + "cmdUnderTest": "$TC actions add action tunnel_key set src_ip 10.10.10.1 dst_ip 20.20.20.2 id 1 csum index 99", + "expExitCode": "0", + "verifyCmd": "$TC actions get action tunnel_key index 99", + "matchPattern": "action order [0-9]+: tunnel_key.*src_ip 10.10.10.1.*dst_ip 20.20.20.2.*key_id 1.*csum pipe.*index 99", + "matchCount": "1", + "teardown": [ + "$TC actions flush action tunnel_key" + ] + }, + { + "id": "58a7", + "name": "Add tunnel_key action with nocsum option", + "category": [ + "actions", + "tunnel_key" + ], + "setup": [ + [ + "$TC actions flush action tunnel_key", + 0, + 1, + 255 + ] + ], + "cmdUnderTest": "$TC actions add action tunnel_key set src_ip 10.10.10.1 dst_ip 10.10.10.2 id 7823 nocsum index 234", + "expExitCode": "0", + "verifyCmd": "$TC actions get action tunnel_key index 234", + "matchPattern": "action order [0-9]+: tunnel_key.*src_ip 10.10.10.1.*dst_ip 10.10.10.2.*key_id 7823.*nocsum pipe.*index 234", + "matchCount": "1", + "teardown": [ + "$TC actions flush action tunnel_key" + ] + }, + { + "id": "2575", + "name": "Add tunnel_key action with not-supported parameter", + "category": [ + "actions", + "tunnel_key" + ], + "setup": [ + [ + "$TC actions flush action tunnel_key", + 0, + 1, + 255 + ] + ], + "cmdUnderTest": "$TC actions add action tunnel_key set src_ip 10.10.10.1 dst_ip 10.10.10.2 id 7 foobar 999 index 4", + "expExitCode": "255", + "verifyCmd": "$TC actions get action tunnel_key index 4", + "matchPattern": "action order [0-9]+: tunnel_key.*src_ip 10.10.10.1.*dst_ip 10.10.10.2.*key_id 7.*foobar 999.*index 4", + "matchCount": "0", + "teardown": [ + [ + "$TC actions flush action tunnel_key", + 0, + 1, + 255 + ] + ] + }, + { + "id": "7a88", + "name": "Add tunnel_key action with cookie parameter", + "category": [ + "actions", + "tunnel_key" + ], + "setup": [ + [ + "$TC actions flush action tunnel_key", + 0, + 1, + 255 + ] + ], + "cmdUnderTest": "$TC actions add action tunnel_key set src_ip 10.10.10.1 dst_ip 10.10.10.2 id 7 index 4 cookie aa11bb22cc33dd44ee55ff66aa11b1b2", + "expExitCode": "0", + "verifyCmd": "$TC actions get action tunnel_key index 4", + "matchPattern": "action order [0-9]+: tunnel_key.*set.*src_ip 10.10.10.1.*dst_ip 10.10.10.2.*key_id 7.*dst_port 0.*csum pipe.*index 4 ref.*cookie aa11bb22cc33dd44ee55ff66aa11b1b2", + "matchCount": "1", + "teardown": [ + "$TC actions flush action tunnel_key" + ] + }, + { + "id": "7afc", + "name": "Replace tunnel_key set action with all parameters", + "category": [ + "actions", + "tunnel_key" + ], + "setup": [ + [ + "$TC actions flush action tunnel_key", + 0, + 1, + 255 + ], + "$TC actions add action tunnel_key set src_ip 10.10.10.1 dst_ip 20.20.20.2 dst_port 3128 csum id 1 index 1" + ], + "cmdUnderTest": "$TC actions replace action tunnel_key set src_ip 11.11.11.1 dst_ip 21.21.21.2 dst_port 3129 nocsum id 11 index 1", + "expExitCode": "0", + "verifyCmd": "$TC actions get action tunnel_key index 1", + "matchPattern": "action order [0-9]+: tunnel_key.*set.*src_ip 11.11.11.1.*dst_ip 21.21.21.2.*key_id 11.*dst_port 3129.*nocsum pipe.*index 1", + "matchCount": "1", + "teardown": [ + "$TC actions flush action tunnel_key" + ] + }, + { + "id": "364d", + "name": "Replace tunnel_key set action with all parameters and cookie", + "category": [ + "actions", + "tunnel_key" + ], + "setup": [ + [ + "$TC actions flush action tunnel_key", + 0, + 1, + 255 + ], + "$TC actions add action tunnel_key set src_ip 10.10.10.1 dst_ip 20.20.20.2 dst_port 3128 nocsum id 1 index 1 cookie aabbccddeeff112233445566778800a" + ], + "cmdUnderTest": "$TC actions replace action tunnel_key set src_ip 11.11.11.1 dst_ip 21.21.21.2 dst_port 3129 id 11 csum reclassify index 1 cookie a1b1c1d1", + "expExitCode": "0", + "verifyCmd": "$TC actions get action tunnel_key index 1", + "matchPattern": "action order [0-9]+: tunnel_key.*set.*src_ip 11.11.11.1.*dst_ip 21.21.21.2.*key_id 11.*dst_port 3129.*csum reclassify.*index 1.*cookie a1b1c1d1", + "matchCount": "1", + "teardown": [ + "$TC actions flush action tunnel_key" + ] + }, + { + "id": "937c", + "name": "Fetch all existing tunnel_key actions", + "category": [ + "actions", + "tunnel_key" + ], + "setup": [ + [ + "$TC actions flush action tunnel_key", + 0, + 1, + 255 + ], + "$TC actions add action tunnel_key set src_ip 10.10.10.1 dst_ip 20.20.20.2 dst_port 3128 nocsum id 1 pipe index 1", + "$TC actions add action tunnel_key set src_ip 11.10.10.1 dst_ip 21.20.20.2 dst_port 3129 csum id 2 jump 10 index 2", + "$TC actions add action tunnel_key set src_ip 12.10.10.1 dst_ip 22.20.20.2 dst_port 3130 csum id 3 pass index 3", + "$TC actions add action tunnel_key set src_ip 13.10.10.1 dst_ip 23.20.20.2 dst_port 3131 nocsum id 4 continue index 4" + ], + "cmdUnderTest": "$TC actions list action tunnel_key", + "expExitCode": "0", + "verifyCmd": "$TC actions list action tunnel_key", + "matchPattern": "action order [0-9]+: tunnel_key.*set.*src_ip 10.10.10.1.*dst_ip 20.20.20.2.*key_id 1.*dst_port 3128.*nocsum pipe.*index 1.*set.*src_ip 11.10.10.1.*dst_ip 21.20.20.2.*key_id 2.*dst_port 3129.*csum jump 10.*index 2.*set.*src_ip 12.10.10.1.*dst_ip 22.20.20.2.*key_id 3.*dst_port 3130.*csum pass.*index 3.*set.*src_ip 13.10.10.1.*dst_ip 23.20.20.2.*key_id 4.*dst_port 3131.*nocsum continue.*index 4", + "matchCount": "1", + "teardown": [ + "$TC actions flush action tunnel_key" + ] + }, + { + "id": "6783", + "name": "Flush all existing tunnel_key actions", + "category": [ + "actions", + "tunnel_key" + ], + "setup": [ + [ + "$TC actions flush action tunnel_key", + 0, + 1, + 255 + ], + "$TC actions add action tunnel_key set src_ip 10.10.10.1 dst_ip 20.20.20.2 dst_port 3128 nocsum id 1 pipe index 1", + "$TC actions add action tunnel_key set src_ip 11.10.10.1 dst_ip 21.20.20.2 dst_port 3129 csum id 2 reclassify index 2", + "$TC actions add action tunnel_key set src_ip 12.10.10.1 dst_ip 22.20.20.2 dst_port 3130 csum id 3 pass index 3", + "$TC actions add action tunnel_key set src_ip 13.10.10.1 dst_ip 23.20.20.2 dst_port 3131 nocsum id 4 continue index 4" + ], + "cmdUnderTest": "$TC actions flush action tunnel_key", + "expExitCode": "0", + "verifyCmd": "$TC actions list action tunnel_key", + "matchPattern": "action order [0-9]+:.*", + "matchCount": "0", + "teardown": [ + "$TC actions flush action tunnel_key" + ] + } +] -- cgit v1.2.3-58-ga151 From 87d8fb18cbe919342a1909a1e4168c9a63b9bbbf Mon Sep 17 00:00:00 2001 From: Yuval Mintz <yuvalm@mellanox.com> Date: Sat, 30 Jun 2018 02:45:19 +0200 Subject: selftests: forwarding: Allow lib.sh sourcing from other directories The devlink related scripts are mlxsw-specific. As a result, they'll reside in a different directory - but would still need the common logic implemented in lib.sh. So as a preliminary step, allow lib.sh to be sourced from other directories as well. Signed-off-by: Yuval Mintz <yuvalm@mellanox.com> Signed-off-by: Petr Machata <petrm@mellanox.com> Signed-off-by: David S. Miller <davem@davemloft.net> --- tools/testing/selftests/net/forwarding/lib.sh | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) (limited to 'tools') diff --git a/tools/testing/selftests/net/forwarding/lib.sh b/tools/testing/selftests/net/forwarding/lib.sh index d1f14f83979e..59272824ef37 100644 --- a/tools/testing/selftests/net/forwarding/lib.sh +++ b/tools/testing/selftests/net/forwarding/lib.sh @@ -14,8 +14,13 @@ PAUSE_ON_CLEANUP=${PAUSE_ON_CLEANUP:=no} NETIF_TYPE=${NETIF_TYPE:=veth} NETIF_CREATE=${NETIF_CREATE:=yes} -if [[ -f forwarding.config ]]; then - source forwarding.config +relative_path="${BASH_SOURCE%/*}" +if [[ "$relative_path" == "${BASH_SOURCE}" ]]; then + relative_path="." +fi + +if [[ -f $relative_path/forwarding.config ]]; then + source "$relative_path/forwarding.config" fi ############################################################################## -- cgit v1.2.3-58-ga151 From 96fa91d281229390b75a004d49582400b5f93cb1 Mon Sep 17 00:00:00 2001 From: Petr Machata <petrm@mellanox.com> Date: Sat, 30 Jun 2018 02:45:47 +0200 Subject: selftests: forwarding: lib: Add check_err_fail() In the scale testing scenarios, one usually has a condition that is expected to either fail, or pass, depending on which side of the scale is being tested. To capture this logic, add a function check_err_fail(), which dispatches either to check_err() or check_fail(), depending on the value of the first argument, should_fail. Signed-off-by: Petr Machata <petrm@mellanox.com> Reviewed-by: Yuval Mintz <yuvalm@mellanox.com> Signed-off-by: David S. Miller <davem@davemloft.net> --- tools/testing/selftests/net/forwarding/lib.sh | 13 +++++++++++++ 1 file changed, 13 insertions(+) (limited to 'tools') diff --git a/tools/testing/selftests/net/forwarding/lib.sh b/tools/testing/selftests/net/forwarding/lib.sh index 59272824ef37..5f4b7ed2c65a 100644 --- a/tools/testing/selftests/net/forwarding/lib.sh +++ b/tools/testing/selftests/net/forwarding/lib.sh @@ -156,6 +156,19 @@ check_fail() fi } +check_err_fail() +{ + local should_fail=$1; shift + local err=$1; shift + local what=$1; shift + + if ((should_fail)); then + check_fail $err "$what succeeded, but should have failed" + else + check_err $err "$what failed" + fi +} + log_test() { local test_name=$1 -- cgit v1.2.3-58-ga151 From 68d9cea594b5da185e5b048e9b4fa0dadeafe88a Mon Sep 17 00:00:00 2001 From: Petr Machata <petrm@mellanox.com> Date: Sat, 30 Jun 2018 02:46:05 +0200 Subject: selftests: forwarding: lib: Parameterize NUM_NETIFS in two functions setup_wait() and tc_offload_check() both assume that all NUM_NETIFS interfaces are relevant for a given test. However, the scale test script acts as an umbrella for a number of sub-tests, some of which may not require all the interfaces. Thus it's suboptimal for tc_offload_check() to query all the interfaces. In case of setup_wait() it's incorrect, because the sub-test in question of course doesn't configure any interfaces beyond what it needs, and setup_wait() then ends up waiting indefinitely for the extraneous interfaces to come up. For that reason, give setup_wait() and tc_offload_check() an optional parameter with a number of interfaces to probe. Fall back to global NUM_NETIFS if the parameter is not given. Signed-off-by: Petr Machata <petrm@mellanox.com> Reviewed-by: Yuval Mintz <yuvalm@mellanox.com> Signed-off-by: David S. Miller <davem@davemloft.net> --- tools/testing/selftests/net/forwarding/lib.sh | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) (limited to 'tools') diff --git a/tools/testing/selftests/net/forwarding/lib.sh b/tools/testing/selftests/net/forwarding/lib.sh index 5f4b7ed2c65a..e073918bbe15 100644 --- a/tools/testing/selftests/net/forwarding/lib.sh +++ b/tools/testing/selftests/net/forwarding/lib.sh @@ -220,7 +220,9 @@ setup_wait_dev() setup_wait() { - for i in $(eval echo {1..$NUM_NETIFS}); do + local num_netifs=${1:-$NUM_NETIFS} + + for ((i = 1; i <= num_netifs; ++i)); do setup_wait_dev ${NETIFS[p$i]} done @@ -481,7 +483,9 @@ forwarding_restore() tc_offload_check() { - for i in $(eval echo {1..$NUM_NETIFS}); do + local num_netifs=${1:-$NUM_NETIFS} + + for ((i = 1; i <= num_netifs; ++i)); do ethtool -k ${NETIFS[p$i]} \ | grep "hw-tc-offload: on" &> /dev/null if [[ $? -ne 0 ]]; then -- cgit v1.2.3-58-ga151 From bc7cbb1e9f4ccb147fefb7502ab8acdcd52b9a29 Mon Sep 17 00:00:00 2001 From: Petr Machata <petrm@mellanox.com> Date: Sat, 30 Jun 2018 02:47:08 +0200 Subject: selftests: forwarding: Add devlink_lib.sh This helper library contains wrappers to devlink functionality agnostic to the underlying device. Signed-off-by: Yuval Mintz <yuvalm@mellanox.com> [petrm@mellanox.com: Split this out from another patch.] Signed-off-by: Petr Machata <petrm@mellanox.com> Signed-off-by: David S. Miller <davem@davemloft.net> --- .../selftests/net/forwarding/devlink_lib.sh | 108 +++++++++++++++++++++ 1 file changed, 108 insertions(+) create mode 100644 tools/testing/selftests/net/forwarding/devlink_lib.sh (limited to 'tools') diff --git a/tools/testing/selftests/net/forwarding/devlink_lib.sh b/tools/testing/selftests/net/forwarding/devlink_lib.sh new file mode 100644 index 000000000000..5ab1e5f43022 --- /dev/null +++ b/tools/testing/selftests/net/forwarding/devlink_lib.sh @@ -0,0 +1,108 @@ +#!/bin/bash +# SPDX-License-Identifier: GPL-2.0 + +############################################################################## +# Source library + +relative_path="${BASH_SOURCE%/*}" +if [[ "$relative_path" == "${BASH_SOURCE}" ]]; then + relative_path="." +fi + +source "$relative_path/lib.sh" + +############################################################################## +# Defines + +DEVLINK_DEV=$(devlink port show | grep "${NETIFS[p1]}" | \ + grep -v "${NETIFS[p1]}[0-9]" | cut -d" " -f1 | \ + rev | cut -d"/" -f2- | rev) +if [ -z "$DEVLINK_DEV" ]; then + echo "SKIP: ${NETIFS[p1]} has no devlink device registered for it" + exit 1 +fi +if [[ "$(echo $DEVLINK_DEV | grep -c pci)" -eq 0 ]]; then + echo "SKIP: devlink device's bus is not PCI" + exit 1 +fi + +DEVLINK_VIDDID=$(lspci -s $(echo $DEVLINK_DEV | cut -d"/" -f2) \ + -n | cut -d" " -f3) + +############################################################################## +# Sanity checks + +devlink -j resource show "$DEVLINK_DEV" &> /dev/null +if [ $? -ne 0 ]; then + echo "SKIP: iproute2 too old, missing devlink resource support" + exit 1 +fi + +############################################################################## +# Devlink helpers + +devlink_resource_names_to_path() +{ + local resource + local path="" + + for resource in "${@}"; do + if [ "$path" == "" ]; then + path="$resource" + else + path="${path}/$resource" + fi + done + + echo "$path" +} + +devlink_resource_get() +{ + local name=$1 + local resource_name=.[][\"$DEVLINK_DEV\"] + + resource_name="$resource_name | .[] | select (.name == \"$name\")" + + shift + for resource in "${@}"; do + resource_name="${resource_name} | .[\"resources\"][] | \ + select (.name == \"$resource\")" + done + + devlink -j resource show "$DEVLINK_DEV" | jq "$resource_name" +} + +devlink_resource_size_get() +{ + local size=$(devlink_resource_get "$@" | jq '.["size_new"]') + + if [ "$size" == "null" ]; then + devlink_resource_get "$@" | jq '.["size"]' + else + echo "$size" + fi +} + +devlink_resource_size_set() +{ + local new_size=$1 + local path + + shift + path=$(devlink_resource_names_to_path "$@") + devlink resource set "$DEVLINK_DEV" path "$path" size "$new_size" + check_err $? "Failed setting path $path to size $size" +} + +devlink_reload() +{ + local still_pending + + devlink dev reload "$DEVLINK_DEV" &> /dev/null + check_err $? "Failed reload" + + still_pending=$(devlink resource show "$DEVLINK_DEV" | \ + grep -c "size_new") + check_err $still_pending "Failed reload - There are still unset sizes" +} -- cgit v1.2.3-58-ga151 From 5aeba3e89b3ee4e541dc882d23c7332893dd4b5a Mon Sep 17 00:00:00 2001 From: Petr Machata <petrm@mellanox.com> Date: Sat, 30 Jun 2018 02:48:12 +0200 Subject: selftests: mlxsw: Add devlink_lib_spectrum.sh This library builds on top of devlink_lib.sh and contains functionality specific to Spectrum ASICs, e.g., re-partitioning the various KVD sub-parts. Signed-off-by: Yuval Mintz <yuvalm@mellanox.com> [petrm@mellanox.com: Split this out from another patch. Fix line length in devlink_sp_read_kvd_defaults().] Signed-off-by: Petr Machata <petrm@mellanox.com> Signed-off-by: David S. Miller <davem@davemloft.net> --- MAINTAINERS | 1 + .../net/mlxsw/spectrum/devlink_lib_spectrum.sh | 119 +++++++++++++++++++++ 2 files changed, 120 insertions(+) create mode 100644 tools/testing/selftests/drivers/net/mlxsw/spectrum/devlink_lib_spectrum.sh (limited to 'tools') diff --git a/MAINTAINERS b/MAINTAINERS index 3b2e592aa521..7ed37c057ebd 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -9154,6 +9154,7 @@ S: Supported W: http://www.mellanox.com Q: http://patchwork.ozlabs.org/project/netdev/list/ F: drivers/net/ethernet/mellanox/mlxsw/ +F: tools/testing/selftests/drivers/net/mlxsw/ MELLANOX FIRMWARE FLASH LIBRARY (mlxfw) M: mlxsw@mellanox.com diff --git a/tools/testing/selftests/drivers/net/mlxsw/spectrum/devlink_lib_spectrum.sh b/tools/testing/selftests/drivers/net/mlxsw/spectrum/devlink_lib_spectrum.sh new file mode 100644 index 000000000000..73035e25085d --- /dev/null +++ b/tools/testing/selftests/drivers/net/mlxsw/spectrum/devlink_lib_spectrum.sh @@ -0,0 +1,119 @@ +#!/bin/bash +# SPDX-License-Identifier: GPL-2.0 + +source "../../../../net/forwarding/devlink_lib.sh" + +if [ "$DEVLINK_VIDDID" != "15b3:cb84" ]; then + echo "SKIP: test is tailored for Mellanox Spectrum" + exit 1 +fi + +# Needed for returning to default +declare -A KVD_DEFAULTS + +KVD_CHILDREN="linear hash_single hash_double" +KVDL_CHILDREN="singles chunks large_chunks" + +devlink_sp_resource_minimize() +{ + local size + local i + + for i in $KVD_CHILDREN; do + size=$(devlink_resource_get kvd "$i" | jq '.["size_min"]') + devlink_resource_size_set "$size" kvd "$i" + done + + for i in $KVDL_CHILDREN; do + size=$(devlink_resource_get kvd linear "$i" | \ + jq '.["size_min"]') + devlink_resource_size_set "$size" kvd linear "$i" + done +} + +devlink_sp_size_kvd_to_default() +{ + local need_reload=0 + local i + + for i in $KVD_CHILDREN; do + local size=$(echo "${KVD_DEFAULTS[kvd_$i]}" | jq '.["size"]') + current_size=$(devlink_resource_size_get kvd "$i") + + if [ "$size" -ne "$current_size" ]; then + devlink_resource_size_set "$size" kvd "$i" + need_reload=1 + fi + done + + for i in $KVDL_CHILDREN; do + local size=$(echo "${KVD_DEFAULTS[kvd_linear_$i]}" | \ + jq '.["size"]') + current_size=$(devlink_resource_size_get kvd linear "$i") + + if [ "$size" -ne "$current_size" ]; then + devlink_resource_size_set "$size" kvd linear "$i" + need_reload=1 + fi + done + + if [ "$need_reload" -ne "0" ]; then + devlink_reload + fi +} + +devlink_sp_read_kvd_defaults() +{ + local key + local i + + KVD_DEFAULTS[kvd]=$(devlink_resource_get "kvd") + for i in $KVD_CHILDREN; do + key=kvd_$i + KVD_DEFAULTS[$key]=$(devlink_resource_get kvd "$i") + done + + for i in $KVDL_CHILDREN; do + key=kvd_linear_$i + KVD_DEFAULTS[$key]=$(devlink_resource_get kvd linear "$i") + done +} + +KVD_PROFILES="default scale ipv4_max" + +devlink_sp_resource_kvd_profile_set() +{ + local profile=$1 + + case "$profile" in + scale) + devlink_resource_size_set 64000 kvd linear + devlink_resource_size_set 15616 kvd linear singles + devlink_resource_size_set 32000 kvd linear chunks + devlink_resource_size_set 16384 kvd linear large_chunks + devlink_resource_size_set 128000 kvd hash_single + devlink_resource_size_set 48000 kvd hash_double + devlink_reload + ;; + ipv4_max) + devlink_resource_size_set 64000 kvd linear + devlink_resource_size_set 15616 kvd linear singles + devlink_resource_size_set 32000 kvd linear chunks + devlink_resource_size_set 16384 kvd linear large_chunks + devlink_resource_size_set 144000 kvd hash_single + devlink_resource_size_set 32768 kvd hash_double + devlink_reload + ;; + default) + devlink_resource_size_set 98304 kvd linear + devlink_resource_size_set 16384 kvd linear singles + devlink_resource_size_set 49152 kvd linear chunks + devlink_resource_size_set 32768 kvd linear large_chunks + devlink_resource_size_set 87040 kvd hash_single + devlink_resource_size_set 60416 kvd hash_double + devlink_reload + ;; + *) + check_err 1 "Unknown profile $profile" + esac +} -- cgit v1.2.3-58-ga151 From b030c338116438700fc61fe572d82034f190d3e8 Mon Sep 17 00:00:00 2001 From: Yuval Mintz <yuvalm@mellanox.com> Date: Sat, 30 Jun 2018 02:48:27 +0200 Subject: selftests: mlxsw: Add devlink KVD resource test Add a selftest that can be used to perform basic sanity of the devlink resource API as well as test the behavior of KVD manipulation in the driver. This is the first case of a HW-only test - in order to test the devlink resource a driver capable of exposing resources has to be provided first. Signed-off-by: Yuval Mintz <yuvalm@mellanox.com> [petrm@mellanox.com: Extracted two patches out of this patch. Tweaked commit message.] Signed-off-by: Petr Machata <petrm@mellanox.com> Signed-off-by: David S. Miller <davem@davemloft.net> --- .../net/mlxsw/spectrum/devlink_resources.sh | 117 +++++++++++++++++++++ 1 file changed, 117 insertions(+) create mode 100755 tools/testing/selftests/drivers/net/mlxsw/spectrum/devlink_resources.sh (limited to 'tools') diff --git a/tools/testing/selftests/drivers/net/mlxsw/spectrum/devlink_resources.sh b/tools/testing/selftests/drivers/net/mlxsw/spectrum/devlink_resources.sh new file mode 100755 index 000000000000..b1fe960e398a --- /dev/null +++ b/tools/testing/selftests/drivers/net/mlxsw/spectrum/devlink_resources.sh @@ -0,0 +1,117 @@ +#!/bin/bash +# SPDX-License-Identifier: GPL-2.0 + +NUM_NETIFS=1 +source devlink_lib_spectrum.sh + +setup_prepare() +{ + devlink_sp_read_kvd_defaults +} + +cleanup() +{ + pre_cleanup + devlink_sp_size_kvd_to_default +} + +trap cleanup EXIT + +setup_prepare + +profiles_test() +{ + local i + + log_info "Running profile tests" + + for i in $KVD_PROFILES; do + RET=0 + devlink_sp_resource_kvd_profile_set $i + log_test "'$i' profile" + done + + # Default is explicitly tested at end to ensure it's actually applied + RET=0 + devlink_sp_resource_kvd_profile_set "default" + log_test "'default' profile" +} + +resources_min_test() +{ + local size + local i + local j + + log_info "Running KVD-minimum tests" + + for i in $KVD_CHILDREN; do + RET=0 + size=$(devlink_resource_get kvd "$i" | jq '.["size_min"]') + devlink_resource_size_set "$size" kvd "$i" + + # In case of linear, need to minimize sub-resources as well + if [[ "$i" == "linear" ]]; then + for j in $KVDL_CHILDREN; do + devlink_resource_size_set 0 kvd linear "$j" + done + fi + + devlink_reload + devlink_sp_size_kvd_to_default + log_test "'$i' minimize [$size]" + done +} + +resources_max_test() +{ + local min_size + local size + local i + local j + + log_info "Running KVD-maximum tests" + for i in $KVD_CHILDREN; do + RET=0 + devlink_sp_resource_minimize + + # Calculate the maximum possible size for the given partition + size=$(devlink_resource_size_get kvd) + for j in $KVD_CHILDREN; do + if [ "$i" != "$j" ]; then + min_size=$(devlink_resource_get kvd "$j" | \ + jq '.["size_min"]') + size=$((size - min_size)) + fi + done + + # Test almost maximum size + devlink_resource_size_set "$((size - 128))" kvd "$i" + devlink_reload + log_test "'$i' almost maximize [$((size - 128))]" + + # Test above maximum size + devlink resource set "$DEVLINK_DEV" \ + path "kvd/$i" size $((size + 128)) &> /dev/null + check_fail $? "Set kvd/$i to size $((size + 128)) should fail" + log_test "'$i' Overflow rejection [$((size + 128))]" + + # Test maximum size + if [ "$i" == "hash_single" ] || [ "$i" == "hash_double" ]; then + echo "SKIP: Observed problem with exact max $i" + continue + fi + + devlink_resource_size_set "$size" kvd "$i" + devlink_reload + log_test "'$i' maximize [$size]" + + devlink_sp_size_kvd_to_default + done +} + +profiles_test +resources_min_test +resources_max_test + +exit "$RET" -- cgit v1.2.3-58-ga151 From d98307c52b19354830af205a1a8346d13e2b1fa7 Mon Sep 17 00:00:00 2001 From: Arkadi Sharshevsky <arkadis@mellanox.com> Date: Sat, 30 Jun 2018 02:49:32 +0200 Subject: selftests: mlxsw: Add router test This test aims for both stand alone and internal usage by the resource infra. The test receives the number routes to offload and checks: - The routes were offloaded correctly - Traffic for each route. Signed-off-by: Arkadi Sharshevsky <arkadis@mellanox.com> Signed-off-by: Yuval Mintz <yuvalm@mellanox.com> Reviewed-by: Petr Machata <petrm@mellanox.com> Signed-off-by: Petr Machata <petrm@mellanox.com> Signed-off-by: David S. Miller <davem@davemloft.net> --- .../selftests/drivers/net/mlxsw/router_scale.sh | 167 +++++++++++++++++++++ 1 file changed, 167 insertions(+) create mode 100644 tools/testing/selftests/drivers/net/mlxsw/router_scale.sh (limited to 'tools') diff --git a/tools/testing/selftests/drivers/net/mlxsw/router_scale.sh b/tools/testing/selftests/drivers/net/mlxsw/router_scale.sh new file mode 100644 index 000000000000..d231649b4f01 --- /dev/null +++ b/tools/testing/selftests/drivers/net/mlxsw/router_scale.sh @@ -0,0 +1,167 @@ +#!/bin/bash +# SPDX-License-Identifier: GPL-2.0 + +ROUTER_NUM_NETIFS=4 + +router_h1_create() +{ + simple_if_init $h1 192.0.1.1/24 + ip route add 193.0.0.0/8 via 192.0.1.2 dev $h1 +} + +router_h1_destroy() +{ + ip route del 193.0.0.0/8 via 192.0.1.2 dev $h1 + simple_if_fini $h1 192.0.1.1/24 +} + +router_h2_create() +{ + simple_if_init $h2 192.0.2.1/24 + tc qdisc add dev $h2 handle ffff: ingress +} + +router_h2_destroy() +{ + tc qdisc del dev $h2 handle ffff: ingress + simple_if_fini $h2 192.0.2.1/24 +} + +router_create() +{ + ip link set dev $rp1 up + ip link set dev $rp2 up + + ip address add 192.0.1.2/24 dev $rp1 + ip address add 192.0.2.2/24 dev $rp2 +} + +router_destroy() +{ + ip address del 192.0.2.2/24 dev $rp2 + ip address del 192.0.1.2/24 dev $rp1 + + ip link set dev $rp2 down + ip link set dev $rp1 down +} + +router_setup_prepare() +{ + h1=${NETIFS[p1]} + rp1=${NETIFS[p2]} + + rp2=${NETIFS[p3]} + h2=${NETIFS[p4]} + + h1mac=$(mac_get $h1) + rp1mac=$(mac_get $rp1) + + vrf_prepare + + router_h1_create + router_h2_create + + router_create +} + +router_offload_validate() +{ + local route_count=$1 + local offloaded_count + + offloaded_count=$(ip route | grep -o 'offload' | wc -l) + [[ $offloaded_count -ge $route_count ]] +} + +router_routes_create() +{ + local route_count=$1 + local count=0 + + ROUTE_FILE="$(mktemp)" + + for i in {0..255} + do + for j in {0..255} + do + for k in {0..255} + do + if [[ $count -eq $route_count ]]; then + break 3 + fi + + echo route add 193.${i}.${j}.${k}/32 via \ + 192.0.2.1 dev $rp2 >> $ROUTE_FILE + ((count++)) + done + done + done + + ip -b $ROUTE_FILE &> /dev/null +} + +router_routes_destroy() +{ + if [[ -v ROUTE_FILE ]]; then + rm -f $ROUTE_FILE + fi +} + +router_test() +{ + local route_count=$1 + local should_fail=$2 + local count=0 + + RET=0 + + router_routes_create $route_count + + router_offload_validate $route_count + check_err_fail $should_fail $? "Offload of $route_count routes" + if [[ $RET -ne 0 ]] || [[ $should_fail -eq 1 ]]; then + return + fi + + tc filter add dev $h2 ingress protocol ip pref 1 flower \ + skip_sw dst_ip 193.0.0.0/8 action drop + + for i in {0..255} + do + for j in {0..255} + do + for k in {0..255} + do + if [[ $count -eq $route_count ]]; then + break 3 + fi + + $MZ $h1 -c 1 -p 64 -a $h1mac -b $rp1mac \ + -A 192.0.1.1 -B 193.${i}.${j}.${k} \ + -t ip -q + ((count++)) + done + done + done + + tc_check_packets "dev $h2 ingress" 1 $route_count + check_err $? "Offload mismatch" + + tc filter del dev $h2 ingress protocol ip pref 1 flower \ + skip_sw dst_ip 193.0.0.0/8 action drop + + router_routes_destroy +} + +router_cleanup() +{ + pre_cleanup + + router_routes_destroy + router_destroy + + router_h2_destroy + router_h1_destroy + + vrf_cleanup +} -- cgit v1.2.3-58-ga151 From c51a744a28bad6faec696b56c176519bea0e73bc Mon Sep 17 00:00:00 2001 From: Yuval Mintz <yuvalm@mellanox.com> Date: Sat, 30 Jun 2018 02:50:28 +0200 Subject: selftests: mlxsw: Add target for router test on spectrum IPv4 routes in Spectrum are based on the kvd single-hash, but as it's a hash we need to assume we cannot reach 100% of its capacity. Add a wrapper that provides us with good/bad target numbers for the Spectrum ASIC. Signed-off-by: Yuval Mintz <yuvalm@mellanox.com> Reviewed-by: Petr Machata <petrm@mellanox.com> [petrm@mellanox.com: Drop shebang.] Signed-off-by: Petr Machata <petrm@mellanox.com> Signed-off-by: David S. Miller <davem@davemloft.net> --- .../drivers/net/mlxsw/spectrum/router_scale.sh | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) create mode 100644 tools/testing/selftests/drivers/net/mlxsw/spectrum/router_scale.sh (limited to 'tools') diff --git a/tools/testing/selftests/drivers/net/mlxsw/spectrum/router_scale.sh b/tools/testing/selftests/drivers/net/mlxsw/spectrum/router_scale.sh new file mode 100644 index 000000000000..21c4697d5bab --- /dev/null +++ b/tools/testing/selftests/drivers/net/mlxsw/spectrum/router_scale.sh @@ -0,0 +1,18 @@ +# SPDX-License-Identifier: GPL-2.0 +source ../router_scale.sh + +router_get_target() +{ + local should_fail=$1 + local target + + target=$(devlink_resource_size_get kvd hash_single) + + if [[ $should_fail -eq 0 ]]; then + target=$((target * 85 / 100)) + else + target=$((target + 1)) + fi + + echo $target +} -- cgit v1.2.3-58-ga151 From d67a94e81fda793fc2cd8562501117720e221634 Mon Sep 17 00:00:00 2001 From: Petr Machata <petrm@mellanox.com> Date: Sat, 30 Jun 2018 02:51:05 +0200 Subject: selftests: mlxsw: Add tc flower scale test Add test of capacity to offload flower. This is a generic portion of the test that is meant to be called from a driver that supplies a particular number of rules to be tested with. Signed-off-by: Petr Machata <petrm@mellanox.com> Reviewed-by: Yuval Mintz <yuvalm@mellanox.com> Signed-off-by: David S. Miller <davem@davemloft.net> --- .../selftests/drivers/net/mlxsw/tc_flower_scale.sh | 134 +++++++++++++++++++++ 1 file changed, 134 insertions(+) create mode 100644 tools/testing/selftests/drivers/net/mlxsw/tc_flower_scale.sh (limited to 'tools') diff --git a/tools/testing/selftests/drivers/net/mlxsw/tc_flower_scale.sh b/tools/testing/selftests/drivers/net/mlxsw/tc_flower_scale.sh new file mode 100644 index 000000000000..a6d733d2a4b4 --- /dev/null +++ b/tools/testing/selftests/drivers/net/mlxsw/tc_flower_scale.sh @@ -0,0 +1,134 @@ +#!/bin/bash +# SPDX-License-Identifier: GPL-2.0 + +# Test for resource limit of offloaded flower rules. The test adds a given +# number of flower matches for different IPv6 addresses, then generates traffic, +# and ensures each was hit exactly once. This file contains functions to set up +# a testing topology and run the test, and is meant to be sourced from a test +# script that calls the testing routine with a given number of rules. + +TC_FLOWER_NUM_NETIFS=2 + +tc_flower_h1_create() +{ + simple_if_init $h1 + tc qdisc add dev $h1 clsact +} + +tc_flower_h1_destroy() +{ + tc qdisc del dev $h1 clsact + simple_if_fini $h1 +} + +tc_flower_h2_create() +{ + simple_if_init $h2 + tc qdisc add dev $h2 clsact +} + +tc_flower_h2_destroy() +{ + tc qdisc del dev $h2 clsact + simple_if_fini $h2 +} + +tc_flower_setup_prepare() +{ + h1=${NETIFS[p1]} + h2=${NETIFS[p2]} + + vrf_prepare + + tc_flower_h1_create + tc_flower_h2_create +} + +tc_flower_cleanup() +{ + pre_cleanup + + tc_flower_h2_destroy + tc_flower_h1_destroy + + vrf_cleanup + + if [[ -v TC_FLOWER_BATCH_FILE ]]; then + rm -f $TC_FLOWER_BATCH_FILE + fi +} + +tc_flower_addr() +{ + local num=$1; shift + + printf "2001:db8:1::%x" $num +} + +tc_flower_rules_create() +{ + local count=$1; shift + local should_fail=$1; shift + + TC_FLOWER_BATCH_FILE="$(mktemp)" + + for ((i = 0; i < count; ++i)); do + cat >> $TC_FLOWER_BATCH_FILE <<-EOF + filter add dev $h2 ingress \ + prot ipv6 \ + pref 1000 \ + flower $tcflags dst_ip $(tc_flower_addr $i) \ + action drop + EOF + done + + tc -b $TC_FLOWER_BATCH_FILE + check_err_fail $should_fail $? "Rule insertion" +} + +__tc_flower_test() +{ + local count=$1; shift + local should_fail=$1; shift + local last=$((count - 1)) + + tc_flower_rules_create $count $should_fail + + for ((i = 0; i < count; ++i)); do + $MZ $h1 -q -c 1 -t ip -p 20 -b bc -6 \ + -A 2001:db8:2::1 \ + -B $(tc_flower_addr $i) + done + + MISMATCHES=$( + tc -j -s filter show dev $h2 ingress | + jq -r '[ .[] | select(.kind == "flower") | .options | + values as $rule | .actions[].stats.packets | + select(. != 1) | "\(.) on \($rule.keys.dst_ip)" ] | + join(", ")' + ) + + test -z "$MISMATCHES" + check_err $? "Expected to capture 1 packet for each IP, but got $MISMATCHES" +} + +tc_flower_test() +{ + local count=$1; shift + local should_fail=$1; shift + + # We use lower 16 bits of IPv6 address for match. Also there are only 16 + # bits of rule priority space. + if ((count > 65536)); then + check_err 1 "Invalid count of $count. At most 65536 rules supported" + return + fi + + if ! tc_offload_check $TC_FLOWER_NUM_NETIFS; then + check_err 1 "Could not test offloaded functionality" + return + fi + + tcflags="skip_sw" + __tc_flower_test $count $should_fail +} -- cgit v1.2.3-58-ga151 From 741a7661f077bf906761fbaef31f3b5aa393c734 Mon Sep 17 00:00:00 2001 From: Petr Machata <petrm@mellanox.com> Date: Sat, 30 Jun 2018 02:51:55 +0200 Subject: selftests: mlxsw: Add target for tc flower test on spectrum Add a wrapper around mlxsw/tc_flower_scale.sh that parameterizes the generic tc flower scale test template with Spectrum-specific target values. Signed-off-by: Petr Machata <petrm@mellanox.com> Reviewed-by: Yuval Mintz <yuvalm@mellanox.com> Signed-off-by: David S. Miller <davem@davemloft.net> --- .../drivers/net/mlxsw/spectrum/tc_flower_scale.sh | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 tools/testing/selftests/drivers/net/mlxsw/spectrum/tc_flower_scale.sh (limited to 'tools') diff --git a/tools/testing/selftests/drivers/net/mlxsw/spectrum/tc_flower_scale.sh b/tools/testing/selftests/drivers/net/mlxsw/spectrum/tc_flower_scale.sh new file mode 100644 index 000000000000..f9bfd8937765 --- /dev/null +++ b/tools/testing/selftests/drivers/net/mlxsw/spectrum/tc_flower_scale.sh @@ -0,0 +1,19 @@ +# SPDX-License-Identifier: GPL-2.0 +source ../tc_flower_scale.sh + +tc_flower_get_target() +{ + local should_fail=$1; shift + + # 6144 (6x1024) is the theoretical maximum. + # One bank of 512 rules is taken by the 18-byte MC router rule. + # One rule is the ACL catch-all. + # 6144 - 512 - 1 = 5631 + local target=5631 + + if ((! should_fail)); then + echo $target + else + echo $((target + 1)) + fi +} -- cgit v1.2.3-58-ga151 From b973b78aaeae44d650f534b617a2b62e5a83327a Mon Sep 17 00:00:00 2001 From: Petr Machata <petrm@mellanox.com> Date: Sat, 30 Jun 2018 02:52:34 +0200 Subject: selftests: mlxsw: Add scale test for mirror-to-gretap Test that it's possible to offload a given number of mirrors. Signed-off-by: Petr Machata <petrm@mellanox.com> Reviewed-by: Jiri Pirko <jiri@mellanox.com> Signed-off-by: David S. Miller <davem@davemloft.net> --- .../drivers/net/mlxsw/mirror_gre_scale.sh | 197 +++++++++++++++++++++ 1 file changed, 197 insertions(+) create mode 100644 tools/testing/selftests/drivers/net/mlxsw/mirror_gre_scale.sh (limited to 'tools') diff --git a/tools/testing/selftests/drivers/net/mlxsw/mirror_gre_scale.sh b/tools/testing/selftests/drivers/net/mlxsw/mirror_gre_scale.sh new file mode 100644 index 000000000000..6f3a70df63bc --- /dev/null +++ b/tools/testing/selftests/drivers/net/mlxsw/mirror_gre_scale.sh @@ -0,0 +1,197 @@ +# SPDX-License-Identifier: GPL-2.0 + +# Test offloading a number of mirrors-to-gretap. The test creates a number of +# tunnels. Then it adds one flower mirror for each of the tunnels, matching a +# given host IP. Then it generates traffic at each of the host IPs and checks +# that the traffic has been mirrored at the appropriate tunnel. +# +# +--------------------------+ +--------------------------+ +# | H1 | | H2 | +# | + $h1 | | $h2 + | +# | | 2001:db8:1:X::1/64 | | 2001:db8:1:X::2/64 | | +# +-----|--------------------+ +--------------------|-----+ +# | | +# +-----|-------------------------------------------------------------|-----+ +# | SW o--> mirrors | | +# | +---|-------------------------------------------------------------|---+ | +# | | + $swp1 BR $swp2 + | | +# | +---------------------------------------------------------------------+ | +# | | +# | + $swp3 + gt6-<X> (ip6gretap) | +# | | 2001:db8:2:X::1/64 : loc=2001:db8:2:X::1 | +# | | : rem=2001:db8:2:X::2 | +# | | : ttl=100 | +# | | : tos=inherit | +# | | : | +# +-----|--------------------------------:----------------------------------+ +# | : +# +-----|--------------------------------:----------------------------------+ +# | H3 + $h3 + h3-gt6-<X> (ip6gretap) | +# | 2001:db8:2:X::2/64 loc=2001:db8:2:X::2 | +# | rem=2001:db8:2:X::1 | +# | ttl=100 | +# | tos=inherit | +# | | +# +-------------------------------------------------------------------------+ + +source ../../../../net/forwarding/mirror_lib.sh + +MIRROR_NUM_NETIFS=6 + +mirror_gre_ipv6_addr() +{ + local net=$1; shift + local num=$1; shift + + printf "2001:db8:%x:%x" $net $num +} + +mirror_gre_tunnels_create() +{ + local count=$1; shift + local should_fail=$1; shift + + MIRROR_GRE_BATCH_FILE="$(mktemp)" + for ((i=0; i < count; ++i)); do + local match_dip=$(mirror_gre_ipv6_addr 1 $i)::2 + local htun=h3-gt6-$i + local tun=gt6-$i + + ((mirror_gre_tunnels++)) + + ip address add dev $h1 $(mirror_gre_ipv6_addr 1 $i)::1/64 + ip address add dev $h2 $(mirror_gre_ipv6_addr 1 $i)::2/64 + + ip address add dev $swp3 $(mirror_gre_ipv6_addr 2 $i)::1/64 + ip address add dev $h3 $(mirror_gre_ipv6_addr 2 $i)::2/64 + + tunnel_create $tun ip6gretap \ + $(mirror_gre_ipv6_addr 2 $i)::1 \ + $(mirror_gre_ipv6_addr 2 $i)::2 \ + ttl 100 tos inherit allow-localremote + + tunnel_create $htun ip6gretap \ + $(mirror_gre_ipv6_addr 2 $i)::2 \ + $(mirror_gre_ipv6_addr 2 $i)::1 + ip link set $htun vrf v$h3 + matchall_sink_create $htun + + cat >> $MIRROR_GRE_BATCH_FILE <<-EOF + filter add dev $swp1 ingress pref 1000 \ + protocol ipv6 \ + flower $tcflags dst_ip $match_dip \ + action mirred egress mirror dev $tun + EOF + done + + tc -b $MIRROR_GRE_BATCH_FILE + check_err_fail $should_fail $? "Mirror rule insertion" +} + +mirror_gre_tunnels_destroy() +{ + local count=$1; shift + + for ((i=0; i < count; ++i)); do + local htun=h3-gt6-$i + local tun=gt6-$i + + ip address del dev $h3 $(mirror_gre_ipv6_addr 2 $i)::2/64 + ip address del dev $swp3 $(mirror_gre_ipv6_addr 2 $i)::1/64 + + ip address del dev $h2 $(mirror_gre_ipv6_addr 1 $i)::2/64 + ip address del dev $h1 $(mirror_gre_ipv6_addr 1 $i)::1/64 + + tunnel_destroy $htun + tunnel_destroy $tun + done +} + +__mirror_gre_test() +{ + local count=$1; shift + local should_fail=$1; shift + + mirror_gre_tunnels_create $count $should_fail + if ((should_fail)); then + return + fi + + sleep 5 + + for ((i = 0; i < count; ++i)); do + local dip=$(mirror_gre_ipv6_addr 1 $i)::2 + local htun=h3-gt6-$i + local message + + icmp6_capture_install $htun + mirror_test v$h1 "" $dip $htun 100 10 + icmp6_capture_uninstall $htun + done +} + +mirror_gre_test() +{ + local count=$1; shift + local should_fail=$1; shift + + if ! tc_offload_check $TC_FLOWER_NUM_NETIFS; then + check_err 1 "Could not test offloaded functionality" + return + fi + + tcflags="skip_sw" + __mirror_gre_test $count $should_fail +} + +mirror_gre_setup_prepare() +{ + h1=${NETIFS[p1]} + swp1=${NETIFS[p2]} + + swp2=${NETIFS[p3]} + h2=${NETIFS[p4]} + + swp3=${NETIFS[p5]} + h3=${NETIFS[p6]} + + mirror_gre_tunnels=0 + + vrf_prepare + + simple_if_init $h1 + simple_if_init $h2 + simple_if_init $h3 + + ip link add name br1 type bridge vlan_filtering 1 + ip link set dev br1 up + + ip link set dev $swp1 master br1 + ip link set dev $swp1 up + tc qdisc add dev $swp1 clsact + + ip link set dev $swp2 master br1 + ip link set dev $swp2 up + + ip link set dev $swp3 up +} + +mirror_gre_cleanup() +{ + mirror_gre_tunnels_destroy $mirror_gre_tunnels + + ip link set dev $swp3 down + + ip link set dev $swp2 down + + tc qdisc del dev $swp1 clsact + ip link set dev $swp1 down + + ip link del dev br1 + + simple_if_fini $h3 + simple_if_fini $h2 + simple_if_fini $h1 + + vrf_cleanup +} -- cgit v1.2.3-58-ga151 From 9136074d56419fc502efeea1c80b63af0e4da3e7 Mon Sep 17 00:00:00 2001 From: Petr Machata <petrm@mellanox.com> Date: Sat, 30 Jun 2018 02:53:14 +0200 Subject: selftests: mlxsw: Add target for mirror-to-gretap test on spectrum Add a wrapper around mlxsw/mirror_gre_scale.sh that parameterized number of offloadable mirrors on Spectrum machines. Signed-off-by: Petr Machata <petrm@mellanox.com> Reviewed-by: Jiri Pirko <jiri@mellanox.com> Signed-off-by: David S. Miller <davem@davemloft.net> --- .../drivers/net/mlxsw/spectrum/mirror_gre_scale.sh | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 tools/testing/selftests/drivers/net/mlxsw/spectrum/mirror_gre_scale.sh (limited to 'tools') diff --git a/tools/testing/selftests/drivers/net/mlxsw/spectrum/mirror_gre_scale.sh b/tools/testing/selftests/drivers/net/mlxsw/spectrum/mirror_gre_scale.sh new file mode 100644 index 000000000000..8d2186c7c62b --- /dev/null +++ b/tools/testing/selftests/drivers/net/mlxsw/spectrum/mirror_gre_scale.sh @@ -0,0 +1,13 @@ +# SPDX-License-Identifier: GPL-2.0 +source ../mirror_gre_scale.sh + +mirror_gre_get_target() +{ + local should_fail=$1; shift + + if ((! should_fail)); then + echo 3 + else + echo 4 + fi +} -- cgit v1.2.3-58-ga151 From 1b6130df62d0346698c5465736e36d935605af0c Mon Sep 17 00:00:00 2001 From: Yuval Mintz <yuvalm@mellanox.com> Date: Sat, 30 Jun 2018 02:53:52 +0200 Subject: selftests: mlxsw: Add scale test for resources Add a scale test capable of validating that offloaded network functionality is indeed functional at scale when configured to the different KVD profiles available. Start by testing offloaded routes are functional at scale by passing traffic on each one of them in turn. Signed-off-by: Yuval Mintz <yuvalm@mellanox.com> Signed-off-by: Petr Machata <petrm@mellanox.com> Signed-off-by: David S. Miller <davem@davemloft.net> --- .../drivers/net/mlxsw/spectrum/resource_scale.sh | 55 ++++++++++++++++++++++ 1 file changed, 55 insertions(+) create mode 100755 tools/testing/selftests/drivers/net/mlxsw/spectrum/resource_scale.sh (limited to 'tools') diff --git a/tools/testing/selftests/drivers/net/mlxsw/spectrum/resource_scale.sh b/tools/testing/selftests/drivers/net/mlxsw/spectrum/resource_scale.sh new file mode 100755 index 000000000000..a0a80e1a69e8 --- /dev/null +++ b/tools/testing/selftests/drivers/net/mlxsw/spectrum/resource_scale.sh @@ -0,0 +1,55 @@ +#!/bin/bash +# SPDX-License-Identifier: GPL-2.0 + +NUM_NETIFS=6 +source ../../../../net/forwarding/lib.sh +source ../../../../net/forwarding/tc_common.sh +source devlink_lib_spectrum.sh + +current_test="" + +cleanup() +{ + pre_cleanup + if [ ! -z $current_test ]; then + ${current_test}_cleanup + fi + devlink_sp_size_kvd_to_default +} + +devlink_sp_read_kvd_defaults +trap cleanup EXIT + +ALL_TESTS="router tc_flower mirror_gre" +for current_test in ${TESTS:-$ALL_TESTS}; do + source ${current_test}_scale.sh + + num_netifs_var=${current_test^^}_NUM_NETIFS + num_netifs=${!num_netifs_var:-$NUM_NETIFS} + + for profile in $KVD_PROFILES; do + RET=0 + devlink_sp_resource_kvd_profile_set $profile + if [[ $RET -gt 0 ]]; then + log_test "'$current_test' [$profile] setting" + continue + fi + + for should_fail in 0 1; do + RET=0 + target=$(${current_test}_get_target "$should_fail") + ${current_test}_setup_prepare + setup_wait $num_netifs + ${current_test}_test "$target" "$should_fail" + ${current_test}_cleanup + if [[ "$should_fail" -eq 0 ]]; then + log_test "'$current_test' [$profile] $target" + else + log_test "'$current_test' [$profile] overflow $target" + fi + done + done +done +current_test="" + +exit "$RET" -- cgit v1.2.3-58-ga151 From c256429fbd01d6993a2e19ff24f252514a0b84a5 Mon Sep 17 00:00:00 2001 From: Jakub Kicinski <jakub.kicinski@netronome.com> Date: Thu, 28 Jun 2018 14:41:35 -0700 Subject: tools: bpftool: use correct make variable type to improve compilation time Commit 4bfe3bd3cc35 ("tools/bpftool: use version from the kernel source tree") added version to bpftool. The version used is equal to the kernel version and obtained by running make kernelversion against kernel source tree. Version is then communicated to the sources with a command line define set in CFLAGS. Use a simply expanded variable for the version, otherwise the recursive make will run every time CFLAGS are used. This brings the single-job compilation time for me from almost 16 sec down to less than 4 sec. Signed-off-by: Jakub Kicinski <jakub.kicinski@netronome.com> Reviewed-by: Quentin Monnet <quentin.monnet@netronome.com> Signed-off-by: Daniel Borkmann <daniel@iogearbox.net> --- tools/bpf/bpftool/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'tools') diff --git a/tools/bpf/bpftool/Makefile b/tools/bpf/bpftool/Makefile index 892dbf095bff..0911b00b25cc 100644 --- a/tools/bpf/bpftool/Makefile +++ b/tools/bpf/bpftool/Makefile @@ -23,7 +23,7 @@ endif LIBBPF = $(BPF_PATH)libbpf.a -BPFTOOL_VERSION=$(shell make --no-print-directory -sC ../../.. kernelversion) +BPFTOOL_VERSION := $(shell make --no-print-directory -sC ../../.. kernelversion) $(LIBBPF): FORCE $(Q)$(MAKE) -C $(BPF_DIR) OUTPUT=$(OUTPUT) $(OUTPUT)libbpf.a FEATURES_DUMP=$(FEATURE_DUMP_EXPORT) -- cgit v1.2.3-58-ga151 From d9b683d7464ec9e8298d82c00b9033c96e27a187 Mon Sep 17 00:00:00 2001 From: Jakub Kicinski <jakub.kicinski@netronome.com> Date: Thu, 28 Jun 2018 14:41:36 -0700 Subject: tools: libbpf: add section names for missing program types Specify default section names for BPF_PROG_TYPE_LIRC_MODE2 and BPF_PROG_TYPE_LWT_SEG6LOCAL, these are the only two missing right now. Signed-off-by: Jakub Kicinski <jakub.kicinski@netronome.com> Reviewed-by: Quentin Monnet <quentin.monnet@netronome.com> Signed-off-by: Daniel Borkmann <daniel@iogearbox.net> --- tools/lib/bpf/libbpf.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'tools') diff --git a/tools/lib/bpf/libbpf.c b/tools/lib/bpf/libbpf.c index a1e96b5de5ff..a1491e95edd0 100644 --- a/tools/lib/bpf/libbpf.c +++ b/tools/lib/bpf/libbpf.c @@ -2037,9 +2037,11 @@ static const struct { BPF_PROG_SEC("lwt_in", BPF_PROG_TYPE_LWT_IN), BPF_PROG_SEC("lwt_out", BPF_PROG_TYPE_LWT_OUT), BPF_PROG_SEC("lwt_xmit", BPF_PROG_TYPE_LWT_XMIT), + BPF_PROG_SEC("lwt_seg6local", BPF_PROG_TYPE_LWT_SEG6LOCAL), BPF_PROG_SEC("sockops", BPF_PROG_TYPE_SOCK_OPS), BPF_PROG_SEC("sk_skb", BPF_PROG_TYPE_SK_SKB), BPF_PROG_SEC("sk_msg", BPF_PROG_TYPE_SK_MSG), + BPF_PROG_SEC("lirc_mode2", BPF_PROG_TYPE_LIRC_MODE2), BPF_SA_PROG_SEC("cgroup/bind4", BPF_CGROUP_INET4_BIND), BPF_SA_PROG_SEC("cgroup/bind6", BPF_CGROUP_INET6_BIND), BPF_SA_PROG_SEC("cgroup/connect4", BPF_CGROUP_INET4_CONNECT), -- cgit v1.2.3-58-ga151 From 9aba36139a5f9ee1d11a60d0a3a90944b8d56385 Mon Sep 17 00:00:00 2001 From: Jakub Kicinski <jakub.kicinski@netronome.com> Date: Thu, 28 Jun 2018 14:41:37 -0700 Subject: tools: libbpf: allow setting ifindex for programs and maps Users of bpf_object__open()/bpf_object__load() APIs may want to load the programs and maps onto a device for offload. Allow setting ifindex on those sub-objects. Signed-off-by: Jakub Kicinski <jakub.kicinski@netronome.com> Reviewed-by: Quentin Monnet <quentin.monnet@netronome.com> Signed-off-by: Daniel Borkmann <daniel@iogearbox.net> --- tools/lib/bpf/libbpf.c | 10 ++++++++++ tools/lib/bpf/libbpf.h | 2 ++ 2 files changed, 12 insertions(+) (limited to 'tools') diff --git a/tools/lib/bpf/libbpf.c b/tools/lib/bpf/libbpf.c index a1491e95edd0..7bc02d93e277 100644 --- a/tools/lib/bpf/libbpf.c +++ b/tools/lib/bpf/libbpf.c @@ -1896,6 +1896,11 @@ void *bpf_program__priv(struct bpf_program *prog) return prog ? prog->priv : ERR_PTR(-EINVAL); } +void bpf_program__set_ifindex(struct bpf_program *prog, __u32 ifindex) +{ + prog->prog_ifindex = ifindex; +} + const char *bpf_program__title(struct bpf_program *prog, bool needs_copy) { const char *title; @@ -2122,6 +2127,11 @@ void *bpf_map__priv(struct bpf_map *map) return map ? map->priv : ERR_PTR(-EINVAL); } +void bpf_map__set_ifindex(struct bpf_map *map, __u32 ifindex) +{ + map->map_ifindex = ifindex; +} + struct bpf_map * bpf_map__next(struct bpf_map *prev, struct bpf_object *obj) { diff --git a/tools/lib/bpf/libbpf.h b/tools/lib/bpf/libbpf.h index 09976531aa74..564f4be9bae0 100644 --- a/tools/lib/bpf/libbpf.h +++ b/tools/lib/bpf/libbpf.h @@ -109,6 +109,7 @@ int bpf_program__set_priv(struct bpf_program *prog, void *priv, bpf_program_clear_priv_t clear_priv); void *bpf_program__priv(struct bpf_program *prog); +void bpf_program__set_ifindex(struct bpf_program *prog, __u32 ifindex); const char *bpf_program__title(struct bpf_program *prog, bool needs_copy); @@ -251,6 +252,7 @@ typedef void (*bpf_map_clear_priv_t)(struct bpf_map *, void *); int bpf_map__set_priv(struct bpf_map *map, void *priv, bpf_map_clear_priv_t clear_priv); void *bpf_map__priv(struct bpf_map *map); +void bpf_map__set_ifindex(struct bpf_map *map, __u32 ifindex); int bpf_map__pin(struct bpf_map *map, const char *path); long libbpf_get_error(const void *ptr); -- cgit v1.2.3-58-ga151 From 9a94f277c4fb84815f450418d37bfc568d57b5cc Mon Sep 17 00:00:00 2001 From: Jakub Kicinski <jakub.kicinski@netronome.com> Date: Thu, 28 Jun 2018 14:41:38 -0700 Subject: tools: libbpf: restore the ability to load programs from .text section libbpf used to be able to load programs from the default section called '.text'. It's not very common to leave sections unnamed, but if it happens libbpf will fail to load the programs reporting -EINVAL from the kernel. The -EINVAL comes from bpf_obj_name_cpy() because since 48cca7e44f9f ("libbpf: add support for bpf_call") libbpf does not resolve program names for programs in '.text', defaulting to '.text'. '.text', however, does not pass the (isalnum(*src) || *src == '_') check in bpf_obj_name_cpy(). With few extra lines of code we can limit the pseudo call assumptions only to objects which actually contain code relocations. Signed-off-by: Jakub Kicinski <jakub.kicinski@netronome.com> Reviewed-by: Quentin Monnet <quentin.monnet@netronome.com> Signed-off-by: Daniel Borkmann <daniel@iogearbox.net> --- tools/lib/bpf/libbpf.c | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) (limited to 'tools') diff --git a/tools/lib/bpf/libbpf.c b/tools/lib/bpf/libbpf.c index 7bc02d93e277..e2401b95f08d 100644 --- a/tools/lib/bpf/libbpf.c +++ b/tools/lib/bpf/libbpf.c @@ -234,6 +234,7 @@ struct bpf_object { size_t nr_maps; bool loaded; + bool has_pseudo_calls; /* * Information when doing elf related work. Only valid if fd @@ -400,10 +401,6 @@ bpf_object__init_prog_names(struct bpf_object *obj) const char *name = NULL; prog = &obj->programs[pi]; - if (prog->idx == obj->efile.text_shndx) { - name = ".text"; - goto skip_search; - } for (si = 0; si < symbols->d_size / sizeof(GElf_Sym) && !name; si++) { @@ -426,12 +423,15 @@ bpf_object__init_prog_names(struct bpf_object *obj) } } + if (!name && prog->idx == obj->efile.text_shndx) + name = ".text"; + if (!name) { pr_warning("failed to find sym for prog %s\n", prog->section_name); return -EINVAL; } -skip_search: + prog->name = strdup(name); if (!prog->name) { pr_warning("failed to allocate memory for prog sym %s\n", @@ -981,6 +981,7 @@ bpf_program__collect_reloc(struct bpf_program *prog, GElf_Shdr *shdr, prog->reloc_desc[i].type = RELO_CALL; prog->reloc_desc[i].insn_idx = insn_idx; prog->reloc_desc[i].text_off = sym.st_value; + obj->has_pseudo_calls = true; continue; } @@ -1426,6 +1427,12 @@ out: return err; } +static bool bpf_program__is_function_storage(struct bpf_program *prog, + struct bpf_object *obj) +{ + return prog->idx == obj->efile.text_shndx && obj->has_pseudo_calls; +} + static int bpf_object__load_progs(struct bpf_object *obj) { @@ -1433,7 +1440,7 @@ bpf_object__load_progs(struct bpf_object *obj) int err; for (i = 0; i < obj->nr_programs; i++) { - if (obj->programs[i].idx == obj->efile.text_shndx) + if (bpf_program__is_function_storage(&obj->programs[i], obj)) continue; err = bpf_program__load(&obj->programs[i], obj->license, @@ -2247,7 +2254,7 @@ int bpf_prog_load_xattr(const struct bpf_prog_load_attr *attr, bpf_program__set_expected_attach_type(prog, expected_attach_type); - if (prog->idx != obj->efile.text_shndx && !first_prog) + if (!bpf_program__is_function_storage(prog, obj) && !first_prog) first_prog = prog; } -- cgit v1.2.3-58-ga151 From eac7d84519a35fc9fc8071b7d93803b2b075e2a7 Mon Sep 17 00:00:00 2001 From: Jakub Kicinski <jakub.kicinski@netronome.com> Date: Thu, 28 Jun 2018 14:41:39 -0700 Subject: tools: libbpf: don't return '.text' as a program for multi-function programs Make bpf_program__next() skip over '.text' section if object file has pseudo calls. The '.text' section is hardly a program in that case, it's more of a storage for code of functions other than main. Signed-off-by: Jakub Kicinski <jakub.kicinski@netronome.com> Reviewed-by: Quentin Monnet <quentin.monnet@netronome.com> Signed-off-by: Daniel Borkmann <daniel@iogearbox.net> --- tools/lib/bpf/libbpf.c | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) (limited to 'tools') diff --git a/tools/lib/bpf/libbpf.c b/tools/lib/bpf/libbpf.c index e2401b95f08d..38ed3e92e393 100644 --- a/tools/lib/bpf/libbpf.c +++ b/tools/lib/bpf/libbpf.c @@ -1865,8 +1865,8 @@ void *bpf_object__priv(struct bpf_object *obj) return obj ? obj->priv : ERR_PTR(-EINVAL); } -struct bpf_program * -bpf_program__next(struct bpf_program *prev, struct bpf_object *obj) +static struct bpf_program * +__bpf_program__next(struct bpf_program *prev, struct bpf_object *obj) { size_t idx; @@ -1887,6 +1887,18 @@ bpf_program__next(struct bpf_program *prev, struct bpf_object *obj) return &obj->programs[idx]; } +struct bpf_program * +bpf_program__next(struct bpf_program *prev, struct bpf_object *obj) +{ + struct bpf_program *prog = prev; + + do { + prog = __bpf_program__next(prog, obj); + } while (prog && bpf_program__is_function_storage(prog, obj)); + + return prog; +} + int bpf_program__set_priv(struct bpf_program *prog, void *priv, bpf_program_clear_priv_t clear_priv) { -- cgit v1.2.3-58-ga151 From 71e07ddcdc03000e37acfc6e757f70c81a963d58 Mon Sep 17 00:00:00 2001 From: Jakub Kicinski <jakub.kicinski@netronome.com> Date: Thu, 28 Jun 2018 14:41:40 -0700 Subject: tools: bpftool: drop unnecessary Author comments Drop my author comments, those are from the early days of bpftool and make little sense in tree, where we have quite a few people contributing and git to attribute the work. While at it bump some copyrights. Signed-off-by: Jakub Kicinski <jakub.kicinski@netronome.com> Reviewed-by: Quentin Monnet <quentin.monnet@netronome.com> Signed-off-by: Daniel Borkmann <daniel@iogearbox.net> --- tools/bpf/bpftool/common.c | 2 -- tools/bpf/bpftool/main.c | 4 +--- tools/bpf/bpftool/main.h | 2 -- tools/bpf/bpftool/map.c | 2 -- tools/bpf/bpftool/prog.c | 4 +--- 5 files changed, 2 insertions(+), 12 deletions(-) (limited to 'tools') diff --git a/tools/bpf/bpftool/common.c b/tools/bpf/bpftool/common.c index 32f9e397a6c0..b432daea4520 100644 --- a/tools/bpf/bpftool/common.c +++ b/tools/bpf/bpftool/common.c @@ -31,8 +31,6 @@ * SOFTWARE. */ -/* Author: Jakub Kicinski <kubakici@wp.pl> */ - #include <ctype.h> #include <errno.h> #include <fcntl.h> diff --git a/tools/bpf/bpftool/main.c b/tools/bpf/bpftool/main.c index eea7f14355f3..d15a62be6cf0 100644 --- a/tools/bpf/bpftool/main.c +++ b/tools/bpf/bpftool/main.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2017 Netronome Systems, Inc. + * Copyright (C) 2017-2018 Netronome Systems, Inc. * * This software is dual licensed under the GNU General License Version 2, * June 1991 as shown in the file COPYING in the top-level directory of this @@ -31,8 +31,6 @@ * SOFTWARE. */ -/* Author: Jakub Kicinski <kubakici@wp.pl> */ - #include <bfd.h> #include <ctype.h> #include <errno.h> diff --git a/tools/bpf/bpftool/main.h b/tools/bpf/bpftool/main.h index 63fdb310b9a4..d39f7ef01d23 100644 --- a/tools/bpf/bpftool/main.h +++ b/tools/bpf/bpftool/main.h @@ -31,8 +31,6 @@ * SOFTWARE. */ -/* Author: Jakub Kicinski <kubakici@wp.pl> */ - #ifndef __BPF_TOOL_H #define __BPF_TOOL_H diff --git a/tools/bpf/bpftool/map.c b/tools/bpf/bpftool/map.c index 097b1a5e046b..5989e1575ae4 100644 --- a/tools/bpf/bpftool/map.c +++ b/tools/bpf/bpftool/map.c @@ -31,8 +31,6 @@ * SOFTWARE. */ -/* Author: Jakub Kicinski <kubakici@wp.pl> */ - #include <assert.h> #include <errno.h> #include <fcntl.h> diff --git a/tools/bpf/bpftool/prog.c b/tools/bpf/bpftool/prog.c index 05f42a46d6ed..fd8cd9b51621 100644 --- a/tools/bpf/bpftool/prog.c +++ b/tools/bpf/bpftool/prog.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2017 Netronome Systems, Inc. + * Copyright (C) 2017-2018 Netronome Systems, Inc. * * This software is dual licensed under the GNU General License Version 2, * June 1991 as shown in the file COPYING in the top-level directory of this @@ -31,8 +31,6 @@ * SOFTWARE. */ -/* Author: Jakub Kicinski <kubakici@wp.pl> */ - #include <errno.h> #include <fcntl.h> #include <stdarg.h> -- cgit v1.2.3-58-ga151 From ef347a340b1a8507c22ee3cf981cd5cd64188431 Mon Sep 17 00:00:00 2001 From: Jakub Kicinski <jakub.kicinski@netronome.com> Date: Thu, 28 Jun 2018 14:41:41 -0700 Subject: tools: bpftool: add missing --bpffs to completions --bpffs is not suggested by bash completions. Signed-off-by: Jakub Kicinski <jakub.kicinski@netronome.com> Reviewed-by: Quentin Monnet <quentin.monnet@netronome.com> Signed-off-by: Daniel Borkmann <daniel@iogearbox.net> --- tools/bpf/bpftool/bash-completion/bpftool | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'tools') diff --git a/tools/bpf/bpftool/bash-completion/bpftool b/tools/bpf/bpftool/bash-completion/bpftool index 1e1083321643..b0b8022d3570 100644 --- a/tools/bpf/bpftool/bash-completion/bpftool +++ b/tools/bpf/bpftool/bash-completion/bpftool @@ -182,7 +182,7 @@ _bpftool() if [[ -z $object ]]; then case $cur in -*) - local c='--version --json --pretty' + local c='--version --json --pretty --bpffs' COMPREPLY=( $( compgen -W "$c" -- "$cur" ) ) return 0 ;; -- cgit v1.2.3-58-ga151 From 121c58bed01a45043381ce52b8556e851dbaa123 Mon Sep 17 00:00:00 2001 From: Jakub Kicinski <jakub.kicinski@netronome.com> Date: Thu, 28 Jun 2018 14:41:42 -0700 Subject: tools: bpftool: deal with options upfront Remove options (in getopt() sense, i.e. starting with a dash like -n or --NAME) while parsing arguments for bash completions. This allows us to refer to position-dependent parameters better, and complete options at any point. Signed-off-by: Jakub Kicinski <jakub.kicinski@netronome.com> Reviewed-by: Quentin Monnet <quentin.monnet@netronome.com> Signed-off-by: Daniel Borkmann <daniel@iogearbox.net> --- tools/bpf/bpftool/bash-completion/bpftool | 32 ++++++++++++++++++++----------- 1 file changed, 21 insertions(+), 11 deletions(-) (limited to 'tools') diff --git a/tools/bpf/bpftool/bash-completion/bpftool b/tools/bpf/bpftool/bash-completion/bpftool index b0b8022d3570..fffd76f4998b 100644 --- a/tools/bpf/bpftool/bash-completion/bpftool +++ b/tools/bpf/bpftool/bash-completion/bpftool @@ -153,6 +153,13 @@ _bpftool() local cur prev words objword _init_completion || return + # Deal with options + if [[ ${words[cword]} == -* ]]; then + local c='--version --json --pretty --bpffs' + COMPREPLY=( $( compgen -W "$c" -- "$cur" ) ) + return 0 + fi + # Deal with simplest keywords case $prev in help|hex|opcodes|visual) @@ -172,20 +179,23 @@ _bpftool() ;; esac - # Search for object and command - local object command cmdword - for (( cmdword=1; cmdword < ${#words[@]}-1; cmdword++ )); do - [[ -n $object ]] && command=${words[cmdword]} && break - [[ ${words[cmdword]} != -* ]] && object=${words[cmdword]} + # Remove all options so completions don't have to deal with them. + local i + for (( i=1; i < ${#words[@]}; )); do + if [[ ${words[i]::1} == - ]]; then + words=( "${words[@]:0:i}" "${words[@]:i+1}" ) + [[ $i -le $cword ]] && cword=$(( cword - 1 )) + else + i=$(( ++i )) + fi done + cur=${words[cword]} + prev=${words[cword - 1]} - if [[ -z $object ]]; then + local object=${words[1]} command=${words[2]} + + if [[ -z $object || $cword -eq 1 ]]; then case $cur in - -*) - local c='--version --json --pretty --bpffs' - COMPREPLY=( $( compgen -W "$c" -- "$cur" ) ) - return 0 - ;; *) COMPREPLY=( $( compgen -W "$( bpftool help 2>&1 | \ command sed \ -- cgit v1.2.3-58-ga151 From 35c31d5c323f14ddd70c3382ca0a65b0b92d02ee Mon Sep 17 00:00:00 2001 From: Petr Machata <petrm@mellanox.com> Date: Mon, 2 Jul 2018 19:58:49 +0200 Subject: selftests: forwarding: Test mirror-to-gretap w/ UL 802.1d Test for "tc action mirred egress mirror" that mirrors to gretap when the underlay route points at a VLAN-unaware bridge (802.1d). Signed-off-by: Petr Machata <petrm@mellanox.com> Signed-off-by: David S. Miller <davem@davemloft.net> --- .../net/forwarding/mirror_gre_bridge_1d.sh | 132 +++++++++++++++++++++ 1 file changed, 132 insertions(+) create mode 100755 tools/testing/selftests/net/forwarding/mirror_gre_bridge_1d.sh (limited to 'tools') diff --git a/tools/testing/selftests/net/forwarding/mirror_gre_bridge_1d.sh b/tools/testing/selftests/net/forwarding/mirror_gre_bridge_1d.sh new file mode 100755 index 000000000000..c5095da7f6bf --- /dev/null +++ b/tools/testing/selftests/net/forwarding/mirror_gre_bridge_1d.sh @@ -0,0 +1,132 @@ +#!/bin/bash +# SPDX-License-Identifier: GPL-2.0 + +# Test for "tc action mirred egress mirror" when the underlay route points at a +# bridge device without vlan filtering (802.1d). +# +# This test uses standard topology for testing mirror-to-gretap. See +# mirror_gre_topo_lib.sh for more details. The full topology is as follows: +# +# +---------------------+ +---------------------+ +# | H1 | | H2 | +# | + $h1 | | $h2 + | +# | | 192.0.2.1/28 | | 192.0.2.2/28 | | +# +-----|---------------+ +---------------|-----+ +# | | +# +-----|-------------------------------------------------------------|-----+ +# | SW o---> mirror | | +# | +---|-------------------------------------------------------------|---+ | +# | | + $swp1 + br1 (802.1q bridge) $swp2 + | | +# | +---------------------------------------------------------------------+ | +# | | +# | +---------------------------------------------------------------------+ | +# | | + br2 (802.1d bridge) | | +# | | 192.0.2.129/28 | | +# | | + $swp3 2001:db8:2::1/64 | | +# | +---|-----------------------------------------------------------------+ | +# | | ^ ^ | +# | | + gt6 (ip6gretap) | + gt4 (gretap) | | +# | | : loc=2001:db8:2::1 | : loc=192.0.2.129 | | +# | | : rem=2001:db8:2::2 -+ : rem=192.0.2.130 -+ | +# | | : ttl=100 : ttl=100 | +# | | : tos=inherit : tos=inherit | +# +-----|---------------------:----------------------:----------------------+ +# | : : +# +-----|---------------------:----------------------:----------------------+ +# | H3 + $h3 + h3-gt6(ip6gretap) + h3-gt4 (gretap) | +# | 192.0.2.130/28 loc=2001:db8:2::2 loc=192.0.2.130 | +# | 2001:db8:2::2/64 rem=2001:db8:2::1 rem=192.0.2.129 | +# | ttl=100 ttl=100 | +# | tos=inherit tos=inherit | +# +-------------------------------------------------------------------------+ + +ALL_TESTS=" + test_gretap + test_ip6gretap +" + +NUM_NETIFS=6 +source lib.sh +source mirror_lib.sh +source mirror_gre_lib.sh +source mirror_gre_topo_lib.sh + +setup_prepare() +{ + h1=${NETIFS[p1]} + swp1=${NETIFS[p2]} + + swp2=${NETIFS[p3]} + h2=${NETIFS[p4]} + + swp3=${NETIFS[p5]} + h3=${NETIFS[p6]} + + vrf_prepare + mirror_gre_topo_create + + ip link add name br2 type bridge vlan_filtering 0 + ip link set dev br2 up + + ip link set dev $swp3 master br2 + ip route add 192.0.2.130/32 dev br2 + ip -6 route add 2001:db8:2::2/128 dev br2 + + ip address add dev br2 192.0.2.129/28 + ip address add dev br2 2001:db8:2::1/64 + + ip address add dev $h3 192.0.2.130/28 + ip address add dev $h3 2001:db8:2::2/64 +} + +cleanup() +{ + pre_cleanup + + ip address del dev $h3 2001:db8:2::2/64 + ip address del dev $h3 192.0.2.130/28 + ip link del dev br2 + + mirror_gre_topo_destroy + vrf_cleanup +} + +test_gretap() +{ + full_test_span_gre_dir gt4 ingress 8 0 "mirror to gretap" + full_test_span_gre_dir gt4 egress 0 8 "mirror to gretap" +} + +test_ip6gretap() +{ + full_test_span_gre_dir gt6 ingress 8 0 "mirror to ip6gretap" + full_test_span_gre_dir gt6 egress 0 8 "mirror to ip6gretap" +} + +test_all() +{ + slow_path_trap_install $swp1 ingress + slow_path_trap_install $swp1 egress + + tests_run + + slow_path_trap_uninstall $swp1 egress + slow_path_trap_uninstall $swp1 ingress +} + +trap cleanup EXIT + +setup_prepare +setup_wait + +tcflags="skip_hw" +test_all + +if ! tc_offload_check; then + echo "WARN: Could not test offloaded functionality" +else + tcflags="skip_sw" + test_all +fi + +exit $EXIT_STATUS -- cgit v1.2.3-58-ga151 From 239e754af854137f300e7d3ea199684c3e02a888 Mon Sep 17 00:00:00 2001 From: Petr Machata <petrm@mellanox.com> Date: Mon, 2 Jul 2018 19:58:56 +0200 Subject: selftests: forwarding: Test mirror-to-gretap w/ UL 802.1q Test for "tc action mirred egress mirror" that mirrors to gretap when the underlay route points at a VLAN-aware bridge (802.1q). Signed-off-by: Petr Machata <petrm@mellanox.com> Signed-off-by: David S. Miller <davem@davemloft.net> --- .../net/forwarding/mirror_gre_bridge_1q.sh | 126 +++++++++++++++++++++ 1 file changed, 126 insertions(+) create mode 100755 tools/testing/selftests/net/forwarding/mirror_gre_bridge_1q.sh (limited to 'tools') diff --git a/tools/testing/selftests/net/forwarding/mirror_gre_bridge_1q.sh b/tools/testing/selftests/net/forwarding/mirror_gre_bridge_1q.sh new file mode 100755 index 000000000000..a3402cd8d5b6 --- /dev/null +++ b/tools/testing/selftests/net/forwarding/mirror_gre_bridge_1q.sh @@ -0,0 +1,126 @@ +#!/bin/bash +# SPDX-License-Identifier: GPL-2.0 + +# Test for "tc action mirred egress mirror" when the underlay route points at a +# bridge device with vlan filtering (802.1q). +# +# This test uses standard topology for testing mirror-to-gretap. See +# mirror_gre_topo_lib.sh for more details. The full topology is as follows: +# +# +---------------------+ +---------------------+ +# | H1 | | H2 | +# | + $h1 | | $h2 + | +# | | 192.0.2.1/28 | | 192.0.2.2/28 | | +# +-----|---------------+ +---------------|-----+ +# | | +# +-----|---------------------------------------------------------------|-----+ +# | SW o---> mirror | | +# | +---|---------------------------------------------------------------|---+ | +# | | + $swp1 + br1 (802.1q bridge) $swp2 + | | +# | | 192.0.2.129/28 | | +# | | + $swp3 2001:db8:2::1/64 | | +# | | | vid555 vid555[pvid,untagged] | | +# | +---|-------------------------------------------------------------------+ | +# | | ^ ^ | +# | | + gt6 (ip6gretap) | + gt4 (gretap) | | +# | | : loc=2001:db8:2::1 | : loc=192.0.2.129 | | +# | | : rem=2001:db8:2::2 -+ : rem=192.0.2.130 -+ | +# | | : ttl=100 : ttl=100 | +# | | : tos=inherit : tos=inherit | +# +-----|---------------------:------------------------:----------------------+ +# | : : +# +-----|---------------------:------------------------:----------------------+ +# | H3 + $h3 + h3-gt6(ip6gretap) + h3-gt4 (gretap) | +# | | loc=2001:db8:2::2 loc=192.0.2.130 | +# | + $h3.555 rem=2001:db8:2::1 rem=192.0.2.129 | +# | 192.0.2.130/28 ttl=100 ttl=100 | +# | 2001:db8:2::2/64 tos=inherit tos=inherit | +# +---------------------------------------------------------------------------+ + +ALL_TESTS=" + test_gretap + test_ip6gretap +" + +NUM_NETIFS=6 +source lib.sh +source mirror_lib.sh +source mirror_gre_lib.sh +source mirror_gre_topo_lib.sh + +setup_prepare() +{ + h1=${NETIFS[p1]} + swp1=${NETIFS[p2]} + + swp2=${NETIFS[p3]} + h2=${NETIFS[p4]} + + swp3=${NETIFS[p5]} + h3=${NETIFS[p6]} + + vrf_prepare + mirror_gre_topo_create + + ip link set dev $swp3 master br1 + bridge vlan add dev br1 vid 555 pvid untagged self + ip address add dev br1 192.0.2.129/28 + ip address add dev br1 2001:db8:2::1/64 + + ip -4 route add 192.0.2.130/32 dev br1 + ip -6 route add 2001:db8:2::2/128 dev br1 + + vlan_create $h3 555 v$h3 192.0.2.130/28 2001:db8:2::2/64 + bridge vlan add dev $swp3 vid 555 +} + +cleanup() +{ + pre_cleanup + + ip link set dev $swp3 nomaster + vlan_destroy $h3 555 + + mirror_gre_topo_destroy + vrf_cleanup +} + +test_gretap() +{ + full_test_span_gre_dir gt4 ingress 8 0 "mirror to gretap" + full_test_span_gre_dir gt4 egress 0 8 "mirror to gretap" +} + +test_ip6gretap() +{ + full_test_span_gre_dir gt6 ingress 8 0 "mirror to ip6gretap" + full_test_span_gre_dir gt6 egress 0 8 "mirror to ip6gretap" +} + +tests() +{ + slow_path_trap_install $swp1 ingress + slow_path_trap_install $swp1 egress + + tests_run + + slow_path_trap_uninstall $swp1 egress + slow_path_trap_uninstall $swp1 ingress +} + +trap cleanup EXIT + +setup_prepare +setup_wait + +tcflags="skip_hw" +tests + +if ! tc_offload_check; then + echo "WARN: Could not test offloaded functionality" +else + tcflags="skip_sw" + tests +fi + +exit $EXIT_STATUS -- cgit v1.2.3-58-ga151 From 967450c54300fd858b24e044ee960a1133c916a4 Mon Sep 17 00:00:00 2001 From: Nikolay Aleksandrov <nikolay@cumulusnetworks.com> Date: Tue, 3 Jul 2018 15:42:43 +0300 Subject: selftests: forwarding: lib: extract ping and ping6 so they can be reused Extract ping and ping6 command execution so the return value can be checked by the caller, this is needed for port isolation tests that are intended to fail. Signed-off-by: Nikolay Aleksandrov <nikolay@cumulusnetworks.com> Signed-off-by: David S. Miller <davem@davemloft.net> --- tools/testing/selftests/net/forwarding/lib.sh | 22 ++++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-) (limited to 'tools') diff --git a/tools/testing/selftests/net/forwarding/lib.sh b/tools/testing/selftests/net/forwarding/lib.sh index e073918bbe15..2bb9cf303c53 100644 --- a/tools/testing/selftests/net/forwarding/lib.sh +++ b/tools/testing/selftests/net/forwarding/lib.sh @@ -659,30 +659,40 @@ multipath_eval() ############################################################################## # Tests -ping_test() +ping_do() { local if_name=$1 local dip=$2 local vrf_name - RET=0 - vrf_name=$(master_name_get $if_name) ip vrf exec $vrf_name $PING $dip -c 10 -i 0.1 -w 2 &> /dev/null +} + +ping_test() +{ + RET=0 + + ping_do $1 $2 check_err $? log_test "ping" } -ping6_test() +ping6_do() { local if_name=$1 local dip=$2 local vrf_name - RET=0 - vrf_name=$(master_name_get $if_name) ip vrf exec $vrf_name $PING6 $dip -c 10 -i 0.1 -w 2 &> /dev/null +} + +ping6_test() +{ + RET=0 + + ping6_do $1 $2 check_err $? log_test "ping6" } -- cgit v1.2.3-58-ga151 From a14e9fafaa3437a5e73e6ff521d3a351f173799b Mon Sep 17 00:00:00 2001 From: Nikolay Aleksandrov <nikolay@cumulusnetworks.com> Date: Tue, 3 Jul 2018 15:42:44 +0300 Subject: selftests: forwarding: test for bridge port isolation This test checks if the bridge port isolation feature works as expected by performing ping/ping6 tests between hosts that are isolated (should not work) and between an isolated and non-isolated hosts (should work). Same test is performed for flooding from and to isolated and non-isolated ports. Signed-off-by: Nikolay Aleksandrov <nikolay@cumulusnetworks.com> Signed-off-by: David S. Miller <davem@davemloft.net> --- .../net/forwarding/bridge_port_isolation.sh | 151 +++++++++++++++++++++ 1 file changed, 151 insertions(+) create mode 100755 tools/testing/selftests/net/forwarding/bridge_port_isolation.sh (limited to 'tools') diff --git a/tools/testing/selftests/net/forwarding/bridge_port_isolation.sh b/tools/testing/selftests/net/forwarding/bridge_port_isolation.sh new file mode 100755 index 000000000000..a43b4645c4de --- /dev/null +++ b/tools/testing/selftests/net/forwarding/bridge_port_isolation.sh @@ -0,0 +1,151 @@ +#!/bin/bash +# SPDX-License-Identifier: GPL-2.0 + +ALL_TESTS="ping_ipv4 ping_ipv6 flooding" +NUM_NETIFS=6 +CHECK_TC="yes" +source lib.sh + +h1_create() +{ + simple_if_init $h1 192.0.2.1/24 2001:db8:1::1/64 +} + +h1_destroy() +{ + simple_if_fini $h1 192.0.2.1/24 2001:db8:1::1/64 +} + +h2_create() +{ + simple_if_init $h2 192.0.2.2/24 2001:db8:1::2/64 +} + +h2_destroy() +{ + simple_if_fini $h2 192.0.2.2/24 2001:db8:1::2/64 +} + +h3_create() +{ + simple_if_init $h3 192.0.2.3/24 2001:db8:1::3/64 +} + +h3_destroy() +{ + simple_if_fini $h3 192.0.2.3/24 2001:db8:1::3/64 +} + +switch_create() +{ + ip link add dev br0 type bridge + + ip link set dev $swp1 master br0 + ip link set dev $swp2 master br0 + ip link set dev $swp3 master br0 + + ip link set dev $swp1 type bridge_slave isolated on + check_err $? "Can't set isolation on port $swp1" + ip link set dev $swp2 type bridge_slave isolated on + check_err $? "Can't set isolation on port $swp2" + ip link set dev $swp3 type bridge_slave isolated off + check_err $? "Can't disable isolation on port $swp3" + + ip link set dev br0 up + ip link set dev $swp1 up + ip link set dev $swp2 up + ip link set dev $swp3 up +} + +switch_destroy() +{ + ip link set dev $swp3 down + ip link set dev $swp2 down + ip link set dev $swp1 down + + ip link del dev br0 +} + +setup_prepare() +{ + h1=${NETIFS[p1]} + swp1=${NETIFS[p2]} + + swp2=${NETIFS[p3]} + h2=${NETIFS[p4]} + + swp3=${NETIFS[p5]} + h3=${NETIFS[p6]} + + vrf_prepare + + h1_create + h2_create + h3_create + + switch_create +} + +cleanup() +{ + pre_cleanup + + switch_destroy + + h3_destroy + h2_destroy + h1_destroy + + vrf_cleanup +} + +ping_ipv4() +{ + RET=0 + ping_do $h1 192.0.2.2 + check_fail $? "Ping worked when it should not have" + + RET=0 + ping_do $h3 192.0.2.2 + check_err $? "Ping didn't work when it should have" + + log_test "Isolated port ping" +} + +ping_ipv6() +{ + RET=0 + ping6_do $h1 2001:db8:1::2 + check_fail $? "Ping6 worked when it should not have" + + RET=0 + ping6_do $h3 2001:db8:1::2 + check_err $? "Ping6 didn't work when it should have" + + log_test "Isolated port ping6" +} + +flooding() +{ + local mac=de:ad:be:ef:13:37 + local ip=192.0.2.100 + + RET=0 + flood_test_do false $mac $ip $h1 $h2 + check_err $? "Packet was flooded when it should not have been" + + RET=0 + flood_test_do true $mac $ip $h3 $h2 + check_err $? "Packet was not flooded when it should have been" + + log_test "Isolated port flooding" +} + +trap cleanup EXIT + +setup_prepare +setup_wait + +tests_run + +exit $EXIT_STATUS -- cgit v1.2.3-58-ga151 From 2058b38371d0dbd84831a085db64b996d623f81a Mon Sep 17 00:00:00 2001 From: Roman Gushchin <guro@fb.com> Date: Fri, 6 Jul 2018 14:28:14 -0700 Subject: bpftool: introduce cgroup tree command This commit introduces a new bpftool command: cgroup tree. The idea is to iterate over the whole cgroup tree and print all attached programs. I was debugging a bpf/systemd issue, and found, that there is no simple way to listen all bpf programs attached to cgroups. I did master something in bash, but after some time got tired of it, and decided, that adding a dedicated bpftool command could be a better idea. So, here it is: $ sudo ./bpftool cgroup tree CgroupPath ID AttachType AttachFlags Name /sys/fs/cgroup/system.slice/systemd-machined.service 18 ingress 17 egress /sys/fs/cgroup/system.slice/systemd-logind.service 20 ingress 19 egress /sys/fs/cgroup/system.slice/systemd-udevd.service 16 ingress 15 egress /sys/fs/cgroup/system.slice/systemd-journald.service 14 ingress 13 egress Signed-off-by: Roman Gushchin <guro@fb.com> Acked-by: Jakub Kicinski <jakub.kicinski@netronome.com> Cc: Quentin Monnet <quentin.monnet@netronome.com> Cc: Daniel Borkmann <daniel@iogearbox.net> Cc: Alexei Starovoitov <ast@kernel.org> Signed-off-by: Daniel Borkmann <daniel@iogearbox.net> --- tools/bpf/bpftool/cgroup.c | 170 +++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 165 insertions(+), 5 deletions(-) (limited to 'tools') diff --git a/tools/bpf/bpftool/cgroup.c b/tools/bpf/bpftool/cgroup.c index 16bee011e16c..ee7a9765c6b3 100644 --- a/tools/bpf/bpftool/cgroup.c +++ b/tools/bpf/bpftool/cgroup.c @@ -2,7 +2,12 @@ // Copyright (C) 2017 Facebook // Author: Roman Gushchin <guro@fb.com> +#define _XOPEN_SOURCE 500 +#include <errno.h> #include <fcntl.h> +#include <ftw.h> +#include <mntent.h> +#include <stdio.h> #include <stdlib.h> #include <string.h> #include <sys/stat.h> @@ -53,7 +58,8 @@ static enum bpf_attach_type parse_attach_type(const char *str) } static int show_bpf_prog(int id, const char *attach_type_str, - const char *attach_flags_str) + const char *attach_flags_str, + int level) { struct bpf_prog_info info = {}; __u32 info_len = sizeof(info); @@ -78,7 +84,8 @@ static int show_bpf_prog(int id, const char *attach_type_str, jsonw_string_field(json_wtr, "name", info.name); jsonw_end_object(json_wtr); } else { - printf("%-8u %-15s %-15s %-15s\n", info.id, + printf("%s%-8u %-15s %-15s %-15s\n", level ? " " : "", + info.id, attach_type_str, attach_flags_str, info.name); @@ -88,7 +95,20 @@ static int show_bpf_prog(int id, const char *attach_type_str, return 0; } -static int show_attached_bpf_progs(int cgroup_fd, enum bpf_attach_type type) +static int count_attached_bpf_progs(int cgroup_fd, enum bpf_attach_type type) +{ + __u32 prog_cnt = 0; + int ret; + + ret = bpf_prog_query(cgroup_fd, type, 0, NULL, NULL, &prog_cnt); + if (ret) + return -1; + + return prog_cnt; +} + +static int show_attached_bpf_progs(int cgroup_fd, enum bpf_attach_type type, + int level) { __u32 prog_ids[1024] = {0}; char *attach_flags_str; @@ -123,7 +143,7 @@ static int show_attached_bpf_progs(int cgroup_fd, enum bpf_attach_type type) for (iter = 0; iter < prog_cnt; iter++) show_bpf_prog(prog_ids[iter], attach_type_strings[type], - attach_flags_str); + attach_flags_str, level); return 0; } @@ -161,7 +181,7 @@ static int do_show(int argc, char **argv) * If we were able to get the show for at least one * attach type, let's return 0. */ - if (show_attached_bpf_progs(cgroup_fd, type) == 0) + if (show_attached_bpf_progs(cgroup_fd, type, 0) == 0) ret = 0; } @@ -173,6 +193,143 @@ exit: return ret; } +/* + * To distinguish nftw() errors and do_show_tree_fn() errors + * and avoid duplicating error messages, let's return -2 + * from do_show_tree_fn() in case of error. + */ +#define NFTW_ERR -1 +#define SHOW_TREE_FN_ERR -2 +static int do_show_tree_fn(const char *fpath, const struct stat *sb, + int typeflag, struct FTW *ftw) +{ + enum bpf_attach_type type; + bool skip = true; + int cgroup_fd; + + if (typeflag != FTW_D) + return 0; + + cgroup_fd = open(fpath, O_RDONLY); + if (cgroup_fd < 0) { + p_err("can't open cgroup %s: %s", fpath, strerror(errno)); + return SHOW_TREE_FN_ERR; + } + + for (type = 0; type < __MAX_BPF_ATTACH_TYPE; type++) { + int count = count_attached_bpf_progs(cgroup_fd, type); + + if (count < 0 && errno != EINVAL) { + p_err("can't query bpf programs attached to %s: %s", + fpath, strerror(errno)); + close(cgroup_fd); + return SHOW_TREE_FN_ERR; + } + if (count > 0) { + skip = false; + break; + } + } + + if (skip) { + close(cgroup_fd); + return 0; + } + + if (json_output) { + jsonw_start_object(json_wtr); + jsonw_string_field(json_wtr, "cgroup", fpath); + jsonw_name(json_wtr, "programs"); + jsonw_start_array(json_wtr); + } else { + printf("%s\n", fpath); + } + + for (type = 0; type < __MAX_BPF_ATTACH_TYPE; type++) + show_attached_bpf_progs(cgroup_fd, type, ftw->level); + + if (json_output) { + jsonw_end_array(json_wtr); + jsonw_end_object(json_wtr); + } + + close(cgroup_fd); + + return 0; +} + +static char *find_cgroup_root(void) +{ + struct mntent *mnt; + FILE *f; + + f = fopen("/proc/mounts", "r"); + if (f == NULL) + return NULL; + + while ((mnt = getmntent(f))) { + if (strcmp(mnt->mnt_type, "cgroup2") == 0) { + fclose(f); + return strdup(mnt->mnt_dir); + } + } + + fclose(f); + return NULL; +} + +static int do_show_tree(int argc, char **argv) +{ + char *cgroup_root; + int ret; + + switch (argc) { + case 0: + cgroup_root = find_cgroup_root(); + if (!cgroup_root) { + p_err("cgroup v2 isn't mounted"); + return -1; + } + break; + case 1: + cgroup_root = argv[0]; + break; + default: + p_err("too many parameters for cgroup tree"); + return -1; + } + + + if (json_output) + jsonw_start_array(json_wtr); + else + printf("%s\n" + "%-8s %-15s %-15s %-15s\n", + "CgroupPath", + "ID", "AttachType", "AttachFlags", "Name"); + + switch (nftw(cgroup_root, do_show_tree_fn, 1024, FTW_MOUNT)) { + case NFTW_ERR: + p_err("can't iterate over %s: %s", cgroup_root, + strerror(errno)); + ret = -1; + break; + case SHOW_TREE_FN_ERR: + ret = -1; + break; + default: + ret = 0; + } + + if (json_output) + jsonw_end_array(json_wtr); + + if (argc == 0) + free(cgroup_root); + + return ret; +} + static int do_attach(int argc, char **argv) { enum bpf_attach_type attach_type; @@ -289,6 +446,7 @@ static int do_help(int argc, char **argv) fprintf(stderr, "Usage: %s %s { show | list } CGROUP\n" + " %s %s tree [CGROUP_ROOT]\n" " %s %s attach CGROUP ATTACH_TYPE PROG [ATTACH_FLAGS]\n" " %s %s detach CGROUP ATTACH_TYPE PROG\n" " %s %s help\n" @@ -298,6 +456,7 @@ static int do_help(int argc, char **argv) " " HELP_SPEC_PROGRAM "\n" " " HELP_SPEC_OPTIONS "\n" "", + bin_name, argv[-2], bin_name, argv[-2], bin_name, argv[-2], bin_name, argv[-2], bin_name, argv[-2]); @@ -307,6 +466,7 @@ static int do_help(int argc, char **argv) static const struct cmd cmds[] = { { "show", do_show }, { "list", do_show }, + { "tree", do_show_tree }, { "attach", do_attach }, { "detach", do_detach }, { "help", do_help }, -- cgit v1.2.3-58-ga151 From 7d31a0a168979e671e25073c492c29c42610e7e2 Mon Sep 17 00:00:00 2001 From: Roman Gushchin <guro@fb.com> Date: Fri, 6 Jul 2018 14:28:15 -0700 Subject: bpftool: document cgroup tree command Describe cgroup tree command in the corresponding bpftool man page. Signed-off-by: Roman Gushchin <guro@fb.com> Acked-by: Jakub Kicinski <jakub.kicinski@netronome.com> Cc: Quentin Monnet <quentin.monnet@netronome.com> Cc: Daniel Borkmann <daniel@iogearbox.net> Cc: Alexei Starovoitov <ast@kernel.org> Signed-off-by: Daniel Borkmann <daniel@iogearbox.net> --- tools/bpf/bpftool/Documentation/bpftool-cgroup.rst | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) (limited to 'tools') diff --git a/tools/bpf/bpftool/Documentation/bpftool-cgroup.rst b/tools/bpf/bpftool/Documentation/bpftool-cgroup.rst index 7b0e6d453e92..edbe81534c6d 100644 --- a/tools/bpf/bpftool/Documentation/bpftool-cgroup.rst +++ b/tools/bpf/bpftool/Documentation/bpftool-cgroup.rst @@ -15,12 +15,13 @@ SYNOPSIS *OPTIONS* := { { **-j** | **--json** } [{ **-p** | **--pretty** }] | { **-f** | **--bpffs** } } *COMMANDS* := - { **show** | **list** | **attach** | **detach** | **help** } + { **show** | **list** | **tree** | **attach** | **detach** | **help** } MAP COMMANDS ============= | **bpftool** **cgroup { show | list }** *CGROUP* +| **bpftool** **cgroup tree** [*CGROUP_ROOT*] | **bpftool** **cgroup attach** *CGROUP* *ATTACH_TYPE* *PROG* [*ATTACH_FLAGS*] | **bpftool** **cgroup detach** *CGROUP* *ATTACH_TYPE* *PROG* | **bpftool** **cgroup help** @@ -39,6 +40,15 @@ DESCRIPTION Output will start with program ID followed by attach type, attach flags and program name. + **bpftool cgroup tree** [*CGROUP_ROOT*] + Iterate over all cgroups in *CGROUP_ROOT* and list all + attached programs. If *CGROUP_ROOT* is not specified, + bpftool uses cgroup v2 mountpoint. + + The output is similar to the output of cgroup show/list + commands: it starts with absolute cgroup path, followed by + program ID, attach type, attach flags and program name. + **bpftool cgroup attach** *CGROUP* *ATTACH_TYPE* *PROG* [*ATTACH_FLAGS*] Attach program *PROG* to the cgroup *CGROUP* with attach type *ATTACH_TYPE* and optional *ATTACH_FLAGS*. -- cgit v1.2.3-58-ga151 From 02000b55850deeadffe433e4b4930a8831f477de Mon Sep 17 00:00:00 2001 From: Roman Gushchin <guro@fb.com> Date: Fri, 6 Jul 2018 14:28:16 -0700 Subject: bpftool: add bash completion for cgroup tree command This commit adds a bash completion to the bpftool cgroup tree command. Signed-off-by: Roman Gushchin <guro@fb.com> Cc: Jakub Kicinski <jakub.kicinski@netronome.com> Cc: Quentin Monnet <quentin.monnet@netronome.com> Cc: Daniel Borkmann <daniel@iogearbox.net> Cc: Alexei Starovoitov <ast@kernel.org> Acked-by: Jakub Kicinski <jakub.kicinski@netronome.com> Signed-off-by: Daniel Borkmann <daniel@iogearbox.net> --- tools/bpf/bpftool/bash-completion/bpftool | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'tools') diff --git a/tools/bpf/bpftool/bash-completion/bpftool b/tools/bpf/bpftool/bash-completion/bpftool index fffd76f4998b..ce0bc0cda361 100644 --- a/tools/bpf/bpftool/bash-completion/bpftool +++ b/tools/bpf/bpftool/bash-completion/bpftool @@ -414,6 +414,10 @@ _bpftool() _filedir return 0 ;; + tree) + _filedir + return 0 + ;; attach|detach) local ATTACH_TYPES='ingress egress sock_create sock_ops \ device bind4 bind6 post_bind4 post_bind6 connect4 \ @@ -455,7 +459,7 @@ _bpftool() *) [[ $prev == $object ]] && \ COMPREPLY=( $( compgen -W 'help attach detach \ - show list' -- "$cur" ) ) + show list tree' -- "$cur" ) ) ;; esac ;; -- cgit v1.2.3-58-ga151 From e88bc0f25b99294b039b965e92a68213d594ba5b Mon Sep 17 00:00:00 2001 From: Petr Machata <petrm@mellanox.com> Date: Thu, 5 Jul 2018 21:10:59 +0200 Subject: selftests: forwarding: Allow importing dependent libraries The next patch introduces a new mlxsw-specific test that uses mirror_gre_lib.sh and mirror_gre_topo_lib.sh. However when sourcing their own deps, these libraries assume that the test that's running is in the same directory. That's not the case for driver-specific tests. So change the libraries to source their deps through $relative_path. That variable is set up by lib.sh, which should be imported by the test in question in any case. Signed-off-by: Petr Machata <petrm@mellanox.com> Signed-off-by: David S. Miller <davem@davemloft.net> --- tools/testing/selftests/net/forwarding/mirror_gre_lib.sh | 2 +- tools/testing/selftests/net/forwarding/mirror_gre_topo_lib.sh | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'tools') diff --git a/tools/testing/selftests/net/forwarding/mirror_gre_lib.sh b/tools/testing/selftests/net/forwarding/mirror_gre_lib.sh index 1c18e332cd4f..fac486178ef7 100644 --- a/tools/testing/selftests/net/forwarding/mirror_gre_lib.sh +++ b/tools/testing/selftests/net/forwarding/mirror_gre_lib.sh @@ -1,6 +1,6 @@ # SPDX-License-Identifier: GPL-2.0 -source mirror_lib.sh +source "$relative_path/mirror_lib.sh" quick_test_span_gre_dir_ips() { diff --git a/tools/testing/selftests/net/forwarding/mirror_gre_topo_lib.sh b/tools/testing/selftests/net/forwarding/mirror_gre_topo_lib.sh index 253419564708..39c03e2867f4 100644 --- a/tools/testing/selftests/net/forwarding/mirror_gre_topo_lib.sh +++ b/tools/testing/selftests/net/forwarding/mirror_gre_topo_lib.sh @@ -33,7 +33,7 @@ # | | # +-------------------------------------------------------------------------+ -source mirror_topo_lib.sh +source "$relative_path/mirror_topo_lib.sh" mirror_gre_topo_h3_create() { -- cgit v1.2.3-58-ga151 From 1ba97e6725a91f2f005699565a12267445a74f1f Mon Sep 17 00:00:00 2001 From: Petr Machata <petrm@mellanox.com> Date: Thu, 5 Jul 2018 21:09:04 +0200 Subject: selftests: mlxsw: Add mlxsw-specific test for mirror to gretap Test several aspects of offloading mirror to gretap and ip6gretap netdevices that are specific to mlxsw, such as requirements for TTL and TOS values. Signed-off-by: Petr Machata <petrm@mellanox.com> Reviewed-by: Jiri Pirko <jiri@mellanox.com> Signed-off-by: David S. Miller <davem@davemloft.net> --- .../selftests/drivers/net/mlxsw/mirror_gre.sh | 217 +++++++++++++++++++++ 1 file changed, 217 insertions(+) create mode 100755 tools/testing/selftests/drivers/net/mlxsw/mirror_gre.sh (limited to 'tools') diff --git a/tools/testing/selftests/drivers/net/mlxsw/mirror_gre.sh b/tools/testing/selftests/drivers/net/mlxsw/mirror_gre.sh new file mode 100755 index 000000000000..76f1ab4898d9 --- /dev/null +++ b/tools/testing/selftests/drivers/net/mlxsw/mirror_gre.sh @@ -0,0 +1,217 @@ +#!/bin/bash +# SPDX-License-Identifier: GPL-2.0 + +# This test uses standard topology for testing gretap. See +# ../../../net/forwarding/mirror_gre_topo_lib.sh for more details. +# +# Test offloading various features of offloading gretap mirrors specific to +# mlxsw. + +lib_dir=$(dirname $0)/../../../net/forwarding + +NUM_NETIFS=6 +source $lib_dir/lib.sh +source $lib_dir/mirror_lib.sh +source $lib_dir/mirror_gre_lib.sh +source $lib_dir/mirror_gre_topo_lib.sh + +setup_keyful() +{ + tunnel_create gt6-key ip6gretap 2001:db8:3::1 2001:db8:3::2 \ + ttl 100 tos inherit allow-localremote \ + key 1234 + + tunnel_create h3-gt6-key ip6gretap 2001:db8:3::2 2001:db8:3::1 \ + key 1234 + ip link set h3-gt6-key vrf v$h3 + matchall_sink_create h3-gt6-key + + ip address add dev $swp3 2001:db8:3::1/64 + ip address add dev $h3 2001:db8:3::2/64 +} + +cleanup_keyful() +{ + ip address del dev $h3 2001:db8:3::2/64 + ip address del dev $swp3 2001:db8:3::1/64 + + tunnel_destroy h3-gt6-key + tunnel_destroy gt6-key +} + +setup_soft() +{ + # Set up a topology for testing underlay routes that point at an + # unsupported soft device. + + tunnel_create gt6-soft ip6gretap 2001:db8:4::1 2001:db8:4::2 \ + ttl 100 tos inherit allow-localremote + + tunnel_create h3-gt6-soft ip6gretap 2001:db8:4::2 2001:db8:4::1 + ip link set h3-gt6-soft vrf v$h3 + matchall_sink_create h3-gt6-soft + + ip link add name v1 type veth peer name v2 + ip link set dev v1 up + ip address add dev v1 2001:db8:4::1/64 + + ip link set dev v2 vrf v$h3 + ip link set dev v2 up + ip address add dev v2 2001:db8:4::2/64 +} + +cleanup_soft() +{ + ip link del dev v1 + + tunnel_destroy h3-gt6-soft + tunnel_destroy gt6-soft +} + +setup_prepare() +{ + h1=${NETIFS[p1]} + swp1=${NETIFS[p2]} + + swp2=${NETIFS[p3]} + h2=${NETIFS[p4]} + + swp3=${NETIFS[p5]} + h3=${NETIFS[p6]} + + vrf_prepare + mirror_gre_topo_create + + ip address add dev $swp3 2001:db8:2::1/64 + ip address add dev $h3 2001:db8:2::2/64 + + ip address add dev $swp3 192.0.2.129/28 + ip address add dev $h3 192.0.2.130/28 + + setup_keyful + setup_soft +} + +cleanup() +{ + pre_cleanup + + cleanup_soft + cleanup_keyful + + ip address del dev $h3 2001:db8:2::2/64 + ip address del dev $swp3 2001:db8:2::1/64 + + ip address del dev $h3 192.0.2.130/28 + ip address del dev $swp3 192.0.2.129/28 + + mirror_gre_topo_destroy + vrf_cleanup +} + +test_span_gre_ttl_inherit() +{ + local tundev=$1; shift + local type=$1; shift + local what=$1; shift + + RET=0 + + ip link set dev $tundev type $type ttl inherit + mirror_install $swp1 ingress $tundev "matchall $tcflags" + fail_test_span_gre_dir $tundev ingress + + ip link set dev $tundev type $type ttl 100 + + quick_test_span_gre_dir $tundev ingress + mirror_uninstall $swp1 ingress + + log_test "$what: no offload on TTL of inherit ($tcflags)" +} + +test_span_gre_tos_fixed() +{ + local tundev=$1; shift + local type=$1; shift + local what=$1; shift + + RET=0 + + ip link set dev $tundev type $type tos 0x10 + mirror_install $swp1 ingress $tundev "matchall $tcflags" + fail_test_span_gre_dir $tundev ingress + + ip link set dev $tundev type $type tos inherit + quick_test_span_gre_dir $tundev ingress + mirror_uninstall $swp1 ingress + + log_test "$what: no offload on a fixed TOS ($tcflags)" +} + +test_span_failable() +{ + local should_fail=$1; shift + local tundev=$1; shift + local what=$1; shift + + RET=0 + + mirror_install $swp1 ingress $tundev "matchall $tcflags" + if ((should_fail)); then + fail_test_span_gre_dir $tundev ingress + else + quick_test_span_gre_dir $tundev ingress + fi + mirror_uninstall $swp1 ingress + + log_test "$what: should_fail=$should_fail ($tcflags)" +} + +test_failable() +{ + local should_fail=$1; shift + + test_span_failable $should_fail gt6-key "mirror to keyful gretap" + test_span_failable $should_fail gt6-soft "mirror to gretap w/ soft underlay" +} + +test_sw() +{ + slow_path_trap_install $swp1 ingress + slow_path_trap_install $swp1 egress + + test_failable 0 + + slow_path_trap_uninstall $swp1 egress + slow_path_trap_uninstall $swp1 ingress +} + +test_hw() +{ + test_failable 1 + + test_span_gre_tos_fixed gt4 gretap "mirror to gretap" + test_span_gre_tos_fixed gt6 ip6gretap "mirror to ip6gretap" + + test_span_gre_ttl_inherit gt4 gretap "mirror to gretap" + test_span_gre_ttl_inherit gt6 ip6gretap "mirror to ip6gretap" +} + +trap cleanup EXIT + +setup_prepare +setup_wait + +if ! tc_offload_check; then + check_err 1 "Could not test offloaded functionality" + log_test "mlxsw-specific tests for mirror to gretap" + exit +fi + +tcflags="skip_hw" +test_sw + +tcflags="skip_sw" +test_hw + +exit $EXIT_STATUS -- cgit v1.2.3-58-ga151 From db560d1612f81c932df8d31e6f3bf7581c32ba91 Mon Sep 17 00:00:00 2001 From: Petr Machata <petrm@mellanox.com> Date: Sun, 8 Jul 2018 19:58:55 +0200 Subject: selftests: forwarding: mirror_lib: Tighten up VLAN capture The function do_test_span_vlan_dir_ips() is used for testing whether mirrored packets are VLAN-encapsulated. But since it only considers VLAN encapsulation, it may end up matching unmirrored ARP traffic as well. One consequence is a rare failure of mirror_gre_vlan_bridge_1q's test_gretap_untagged_egress. Decreasing ping cadence in mirror_test() makes the problem easily reproducible. Therefore tighten up the match criterion to only count those 802.1q packets where the next header is IP. Signed-off-by: Petr Machata <petrm@mellanox.com> Signed-off-by: David S. Miller <davem@davemloft.net> --- tools/testing/selftests/net/forwarding/mirror_lib.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'tools') diff --git a/tools/testing/selftests/net/forwarding/mirror_lib.sh b/tools/testing/selftests/net/forwarding/mirror_lib.sh index d36dc26c6c51..07991e1025c7 100644 --- a/tools/testing/selftests/net/forwarding/mirror_lib.sh +++ b/tools/testing/selftests/net/forwarding/mirror_lib.sh @@ -105,7 +105,7 @@ do_test_span_vlan_dir_ips() # Install the capture as skip_hw to avoid double-counting of packets. # The traffic is meant for local box anyway, so will be trapped to # kernel. - vlan_capture_install $dev "skip_hw vlan_id $vid" + vlan_capture_install $dev "skip_hw vlan_id $vid vlan_ethtype ip" mirror_test v$h1 $ip1 $ip2 $dev 100 $expect mirror_test v$h2 $ip2 $ip1 $dev 100 $expect vlan_capture_uninstall $dev -- cgit v1.2.3-58-ga151 From 7479efc71f6da5fde6c67d91380ebc0fb455c53d Mon Sep 17 00:00:00 2001 From: Jakub Kicinski <jakub.kicinski@netronome.com> Date: Tue, 10 Jul 2018 14:42:55 -0700 Subject: selftests/bpf: remove duplicated word from test offloads Trivial removal of duplicated "mode" in error message. Signed-off-by: Jakub Kicinski <jakub.kicinski@netronome.com> Reviewed-by: Quentin Monnet <quentin.monnet@netronome.com> Signed-off-by: Daniel Borkmann <daniel@iogearbox.net> --- tools/testing/selftests/bpf/test_offload.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'tools') diff --git a/tools/testing/selftests/bpf/test_offload.py b/tools/testing/selftests/bpf/test_offload.py index be800d0e7a84..a257e4b08392 100755 --- a/tools/testing/selftests/bpf/test_offload.py +++ b/tools/testing/selftests/bpf/test_offload.py @@ -830,7 +830,7 @@ try: check_extack_nsim(err, "program loaded with different flags.", args) ret, _, err = sim.unset_xdp("", force=True, fail=False, include_stderr=True) - fail(ret == 0, "Removed program with a bad mode mode") + fail(ret == 0, "Removed program with a bad mode") check_extack_nsim(err, "program loaded with different flags.", args) start_test("Test MTU restrictions...") -- cgit v1.2.3-58-ga151 From 219f860d2a11b285ac2e880ca4a73e0d36811c0c Mon Sep 17 00:00:00 2001 From: Jakub Kicinski <jakub.kicinski@netronome.com> Date: Tue, 10 Jul 2018 14:42:56 -0700 Subject: selftests/bpf: add Error: prefix in check_extack helper Currently the test only checks errors, not warnings, so save typing and prefix the extack messages with "Error:" inside the check helper. Signed-off-by: Jakub Kicinski <jakub.kicinski@netronome.com> Reviewed-by: Quentin Monnet <quentin.monnet@netronome.com> Signed-off-by: Daniel Borkmann <daniel@iogearbox.net> --- tools/testing/selftests/bpf/test_offload.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'tools') diff --git a/tools/testing/selftests/bpf/test_offload.py b/tools/testing/selftests/bpf/test_offload.py index a257e4b08392..f8d9bd81d9a4 100755 --- a/tools/testing/selftests/bpf/test_offload.py +++ b/tools/testing/selftests/bpf/test_offload.py @@ -547,11 +547,11 @@ def check_extack(output, reference, args): if skip_extack: return lines = output.split("\n") - comp = len(lines) >= 2 and lines[1] == reference + comp = len(lines) >= 2 and lines[1] == 'Error: ' + reference fail(not comp, "Missing or incorrect netlink extack message") def check_extack_nsim(output, reference, args): - check_extack(output, "Error: netdevsim: " + reference, args) + check_extack(output, "netdevsim: " + reference, args) def check_no_extack(res, needle): fail((res[1] + res[2]).count(needle) or (res[1] + res[2]).count("Warning:"), @@ -654,7 +654,7 @@ try: ret, _, err = sim.cls_bpf_add_filter(obj, skip_sw=True, fail=False, include_stderr=True) fail(ret == 0, "TC filter loaded without enabling TC offloads") - check_extack(err, "Error: TC offload is disabled on net device.", args) + check_extack(err, "TC offload is disabled on net device.", args) sim.wait_for_flush() sim.set_ethtool_tc_offloads(True) @@ -694,7 +694,7 @@ try: skip_sw=True, fail=False, include_stderr=True) fail(ret == 0, "Offloaded a filter to chain other than 0") - check_extack(err, "Error: Driver supports only offload of chain 0.", args) + check_extack(err, "Driver supports only offload of chain 0.", args) sim.tc_flush_filters() start_test("Test TC replace...") -- cgit v1.2.3-58-ga151 From 8d1fc3de3d9f9bda0d8ec719d8686e9c5b432573 Mon Sep 17 00:00:00 2001 From: Jakub Kicinski <jakub.kicinski@netronome.com> Date: Tue, 10 Jul 2018 14:42:57 -0700 Subject: tools: bpftool: refactor argument parsing for prog load Add a new macro for printing more informative message than straight usage() when parameters are missing, and use it for prog do_load(). Save the object and pin path argument to variables for clarity. Signed-off-by: Jakub Kicinski <jakub.kicinski@netronome.com> Reviewed-by: Quentin Monnet <quentin.monnet@netronome.com> Signed-off-by: Daniel Borkmann <daniel@iogearbox.net> --- tools/bpf/bpftool/main.h | 15 +++++++++++++++ tools/bpf/bpftool/prog.c | 11 +++++++---- 2 files changed, 22 insertions(+), 4 deletions(-) (limited to 'tools') diff --git a/tools/bpf/bpftool/main.h b/tools/bpf/bpftool/main.h index d39f7ef01d23..15b6c49ae533 100644 --- a/tools/bpf/bpftool/main.h +++ b/tools/bpf/bpftool/main.h @@ -50,6 +50,21 @@ #define NEXT_ARG() ({ argc--; argv++; if (argc < 0) usage(); }) #define NEXT_ARGP() ({ (*argc)--; (*argv)++; if (*argc < 0) usage(); }) #define BAD_ARG() ({ p_err("what is '%s'?", *argv); -1; }) +#define GET_ARG() ({ argc--; *argv++; }) +#define REQ_ARGS(cnt) \ + ({ \ + int _cnt = (cnt); \ + bool _res; \ + \ + if (argc < _cnt) { \ + p_err("'%s' needs at least %d arguments, %d found", \ + argv[-1], _cnt, argc); \ + _res = false; \ + } else { \ + _res = true; \ + } \ + _res; \ + }) #define ERR_MAX_LEN 1024 diff --git a/tools/bpf/bpftool/prog.c b/tools/bpf/bpftool/prog.c index a740da99d477..a5ef46c59029 100644 --- a/tools/bpf/bpftool/prog.c +++ b/tools/bpf/bpftool/prog.c @@ -681,18 +681,21 @@ static int do_pin(int argc, char **argv) static int do_load(int argc, char **argv) { + const char *objfile, *pinfile; struct bpf_object *obj; int prog_fd; - if (argc != 2) - usage(); + if (!REQ_ARGS(2)) + return -1; + objfile = GET_ARG(); + pinfile = GET_ARG(); - if (bpf_prog_load(argv[0], BPF_PROG_TYPE_UNSPEC, &obj, &prog_fd)) { + if (bpf_prog_load(objfile, BPF_PROG_TYPE_UNSPEC, &obj, &prog_fd)) { p_err("failed to load program"); return -1; } - if (do_pin_fd(prog_fd, argv[1])) + if (do_pin_fd(prog_fd, pinfile)) goto err_close_obj; if (json_output) -- cgit v1.2.3-58-ga151 From ba6dd679a3e81af023ec091c2fb7c82003a27316 Mon Sep 17 00:00:00 2001 From: Jakub Kicinski <jakub.kicinski@netronome.com> Date: Tue, 10 Jul 2018 14:42:58 -0700 Subject: tools: bpftool: add support for loading programs for offload Extend the bpftool prog load command to also accept "dev" parameter, which will allow us to load programs onto devices. Signed-off-by: Jakub Kicinski <jakub.kicinski@netronome.com> Reviewed-by: Quentin Monnet <quentin.monnet@netronome.com> Signed-off-by: Daniel Borkmann <daniel@iogearbox.net> --- tools/bpf/bpftool/Documentation/bpftool-prog.rst | 6 ++-- tools/bpf/bpftool/bash-completion/bpftool | 23 ++++++++++++++-- tools/bpf/bpftool/prog.c | 35 ++++++++++++++++++++++-- 3 files changed, 58 insertions(+), 6 deletions(-) (limited to 'tools') diff --git a/tools/bpf/bpftool/Documentation/bpftool-prog.rst b/tools/bpf/bpftool/Documentation/bpftool-prog.rst index 43d34a5c3ec5..41723c6acaa6 100644 --- a/tools/bpf/bpftool/Documentation/bpftool-prog.rst +++ b/tools/bpf/bpftool/Documentation/bpftool-prog.rst @@ -24,7 +24,7 @@ MAP COMMANDS | **bpftool** **prog dump xlated** *PROG* [{**file** *FILE* | **opcodes** | **visual**}] | **bpftool** **prog dump jited** *PROG* [{**file** *FILE* | **opcodes**}] | **bpftool** **prog pin** *PROG* *FILE* -| **bpftool** **prog load** *OBJ* *FILE* +| **bpftool** **prog load** *OBJ* *FILE* [**dev** *NAME*] | **bpftool** **prog help** | | *PROG* := { **id** *PROG_ID* | **pinned** *FILE* | **tag** *PROG_TAG* } @@ -64,8 +64,10 @@ DESCRIPTION Note: *FILE* must be located in *bpffs* mount. - **bpftool prog load** *OBJ* *FILE* + **bpftool prog load** *OBJ* *FILE* [**dev** *NAME*] Load bpf program from binary *OBJ* and pin as *FILE*. + If **dev** *NAME* is specified program will be loaded onto + given networking device (offload). Note: *FILE* must be located in *bpffs* mount. diff --git a/tools/bpf/bpftool/bash-completion/bpftool b/tools/bpf/bpftool/bash-completion/bpftool index ce0bc0cda361..238c2f80092a 100644 --- a/tools/bpf/bpftool/bash-completion/bpftool +++ b/tools/bpf/bpftool/bash-completion/bpftool @@ -99,6 +99,12 @@ _bpftool_get_prog_tags() command sed -n 's/.*"tag": "\(.*\)",$/\1/p' )" -- "$cur" ) ) } +_sysfs_get_netdevs() +{ + COMPREPLY+=( $( compgen -W "$( ls /sys/class/net 2>/dev/null )" -- \ + "$cur" ) ) +} + # For bpftool map update: retrieve type of the map to update. _bpftool_map_update_map_type() { @@ -262,8 +268,21 @@ _bpftool() return 0 ;; load) - _filedir - return 0 + if [[ ${#words[@]} -lt 6 ]]; then + _filedir + return 0 + fi + + case $prev in + dev) + _sysfs_get_netdevs + return 0 + ;; + *) + _bpftool_once_attr 'dev' + return 0 + ;; + esac ;; *) [[ $prev == $object ]] && \ diff --git a/tools/bpf/bpftool/prog.c b/tools/bpf/bpftool/prog.c index a5ef46c59029..21c74de7156f 100644 --- a/tools/bpf/bpftool/prog.c +++ b/tools/bpf/bpftool/prog.c @@ -39,6 +39,7 @@ #include <string.h> #include <time.h> #include <unistd.h> +#include <net/if.h> #include <sys/types.h> #include <sys/stat.h> @@ -681,6 +682,9 @@ static int do_pin(int argc, char **argv) static int do_load(int argc, char **argv) { + struct bpf_prog_load_attr attr = { + .prog_type = BPF_PROG_TYPE_UNSPEC, + }; const char *objfile, *pinfile; struct bpf_object *obj; int prog_fd; @@ -690,7 +694,34 @@ static int do_load(int argc, char **argv) objfile = GET_ARG(); pinfile = GET_ARG(); - if (bpf_prog_load(objfile, BPF_PROG_TYPE_UNSPEC, &obj, &prog_fd)) { + while (argc) { + if (is_prefix(*argv, "dev")) { + NEXT_ARG(); + + if (attr.ifindex) { + p_err("offload device already specified"); + return -1; + } + if (!REQ_ARGS(1)) + return -1; + + attr.ifindex = if_nametoindex(*argv); + if (!attr.ifindex) { + p_err("unrecognized netdevice '%s': %s", + *argv, strerror(errno)); + return -1; + } + NEXT_ARG(); + } else { + p_err("expected no more arguments or 'dev', got: '%s'?", + *argv); + return -1; + } + } + + attr.file = objfile; + + if (bpf_prog_load_xattr(&attr, &obj, &prog_fd)) { p_err("failed to load program"); return -1; } @@ -722,7 +753,7 @@ static int do_help(int argc, char **argv) " %s %s dump xlated PROG [{ file FILE | opcodes | visual }]\n" " %s %s dump jited PROG [{ file FILE | opcodes }]\n" " %s %s pin PROG FILE\n" - " %s %s load OBJ FILE\n" + " %s %s load OBJ FILE [dev NAME]\n" " %s %s help\n" "\n" " " HELP_SPEC_PROGRAM "\n" -- cgit v1.2.3-58-ga151 From b60df2a0e11fcd24186c312b0307ab8494031e27 Mon Sep 17 00:00:00 2001 From: Jakub Kicinski <jakub.kicinski@netronome.com> Date: Tue, 10 Jul 2018 14:42:59 -0700 Subject: tools: libbpf: expose the prog type guessing from section name logic libbpf can guess program type based on ELF section names. As libbpf becomes more popular its association between section name strings and types becomes more of a standard. Allow libbpf users to use the same logic for matching strings to types, e.g. when the string originates from command line. Signed-off-by: Jakub Kicinski <jakub.kicinski@netronome.com> Reviewed-by: Quentin Monnet <quentin.monnet@netronome.com> Acked-by: Andrey Ignatov <rdna@fb.com> Signed-off-by: Daniel Borkmann <daniel@iogearbox.net> --- tools/lib/bpf/libbpf.c | 43 +++++++++++++++++++++++++------------------ tools/lib/bpf/libbpf.h | 3 +++ 2 files changed, 28 insertions(+), 18 deletions(-) (limited to 'tools') diff --git a/tools/lib/bpf/libbpf.c b/tools/lib/bpf/libbpf.c index 38ed3e92e393..42f31eff5f3f 100644 --- a/tools/lib/bpf/libbpf.c +++ b/tools/lib/bpf/libbpf.c @@ -2081,23 +2081,31 @@ static const struct { #undef BPF_S_PROG_SEC #undef BPF_SA_PROG_SEC -static int bpf_program__identify_section(struct bpf_program *prog) +int libbpf_prog_type_by_name(const char *name, enum bpf_prog_type *prog_type, + enum bpf_attach_type *expected_attach_type) { int i; - if (!prog->section_name) - goto err; - - for (i = 0; i < ARRAY_SIZE(section_names); i++) - if (strncmp(prog->section_name, section_names[i].sec, - section_names[i].len) == 0) - return i; + if (!name) + return -EINVAL; -err: - pr_warning("failed to guess program type based on section name %s\n", - prog->section_name); + for (i = 0; i < ARRAY_SIZE(section_names); i++) { + if (strncmp(name, section_names[i].sec, section_names[i].len)) + continue; + *prog_type = section_names[i].prog_type; + *expected_attach_type = section_names[i].expected_attach_type; + return 0; + } + return -EINVAL; +} - return -1; +static int +bpf_program__identify_section(struct bpf_program *prog, + enum bpf_prog_type *prog_type, + enum bpf_attach_type *expected_attach_type) +{ + return libbpf_prog_type_by_name(prog->section_name, prog_type, + expected_attach_type); } int bpf_map__fd(struct bpf_map *map) @@ -2230,7 +2238,6 @@ int bpf_prog_load_xattr(const struct bpf_prog_load_attr *attr, enum bpf_prog_type prog_type; struct bpf_object *obj; struct bpf_map *map; - int section_idx; int err; if (!attr) @@ -2252,14 +2259,14 @@ int bpf_prog_load_xattr(const struct bpf_prog_load_attr *attr, prog->prog_ifindex = attr->ifindex; expected_attach_type = attr->expected_attach_type; if (prog_type == BPF_PROG_TYPE_UNSPEC) { - section_idx = bpf_program__identify_section(prog); - if (section_idx < 0) { + err = bpf_program__identify_section(prog, &prog_type, + &expected_attach_type); + if (err < 0) { + pr_warning("failed to guess program type based on section name %s\n", + prog->section_name); bpf_object__close(obj); return -EINVAL; } - prog_type = section_names[section_idx].prog_type; - expected_attach_type = - section_names[section_idx].expected_attach_type; } bpf_program__set_type(prog, prog_type); diff --git a/tools/lib/bpf/libbpf.h b/tools/lib/bpf/libbpf.h index 564f4be9bae0..d1ce5c828e2e 100644 --- a/tools/lib/bpf/libbpf.h +++ b/tools/lib/bpf/libbpf.h @@ -92,6 +92,9 @@ int bpf_object__set_priv(struct bpf_object *obj, void *priv, bpf_object_clear_priv_t clear_priv); void *bpf_object__priv(struct bpf_object *prog); +int libbpf_prog_type_by_name(const char *name, enum bpf_prog_type *prog_type, + enum bpf_attach_type *expected_attach_type); + /* Accessors of bpf_program */ struct bpf_program; struct bpf_program *bpf_program__next(struct bpf_program *prog, -- cgit v1.2.3-58-ga151 From 49f2cba3e57a4d71e3e7001cc2934b563ee495f4 Mon Sep 17 00:00:00 2001 From: Jakub Kicinski <jakub.kicinski@netronome.com> Date: Tue, 10 Jul 2018 14:43:00 -0700 Subject: tools: bpftool: allow users to specify program type for prog load Sometimes program section names don't match with libbpf's expectation. In particular XDP's default section names differ between libbpf and iproute2. Allow users to pass program type on command line. Name the types like the libbpf expected section names. Signed-off-by: Jakub Kicinski <jakub.kicinski@netronome.com> Reviewed-by: Quentin Monnet <quentin.monnet@netronome.com> Signed-off-by: Daniel Borkmann <daniel@iogearbox.net> --- tools/bpf/bpftool/Documentation/bpftool-prog.rst | 15 ++++++-- tools/bpf/bpftool/bash-completion/bpftool | 6 ++++ tools/bpf/bpftool/prog.c | 44 ++++++++++++++++++++++-- 3 files changed, 60 insertions(+), 5 deletions(-) (limited to 'tools') diff --git a/tools/bpf/bpftool/Documentation/bpftool-prog.rst b/tools/bpf/bpftool/Documentation/bpftool-prog.rst index 41723c6acaa6..e53e1ad2caf0 100644 --- a/tools/bpf/bpftool/Documentation/bpftool-prog.rst +++ b/tools/bpf/bpftool/Documentation/bpftool-prog.rst @@ -24,10 +24,19 @@ MAP COMMANDS | **bpftool** **prog dump xlated** *PROG* [{**file** *FILE* | **opcodes** | **visual**}] | **bpftool** **prog dump jited** *PROG* [{**file** *FILE* | **opcodes**}] | **bpftool** **prog pin** *PROG* *FILE* -| **bpftool** **prog load** *OBJ* *FILE* [**dev** *NAME*] +| **bpftool** **prog load** *OBJ* *FILE* [**type** *TYPE*] [**dev** *NAME*] | **bpftool** **prog help** | | *PROG* := { **id** *PROG_ID* | **pinned** *FILE* | **tag** *PROG_TAG* } +| *TYPE* := { +| **socket** | **kprobe** | **kretprobe** | **classifier** | **action** | +| **tracepoint** | **raw_tracepoint** | **xdp** | **perf_event** | **cgroup/skb** | +| **cgroup/sock** | **cgroup/dev** | **lwt_in** | **lwt_out** | **lwt_xmit** | +| **lwt_seg6local** | **sockops** | **sk_skb** | **sk_msg** | **lirc_mode2** | +| **cgroup/bind4** | **cgroup/bind6** | **cgroup/post_bind4** | **cgroup/post_bind6** | +| **cgroup/connect4** | **cgroup/connect6** | **cgroup/sendmsg4** | **cgroup/sendmsg6** +| } + DESCRIPTION =========== @@ -64,8 +73,10 @@ DESCRIPTION Note: *FILE* must be located in *bpffs* mount. - **bpftool prog load** *OBJ* *FILE* [**dev** *NAME*] + **bpftool prog load** *OBJ* *FILE* [**type** *TYPE*] [**dev** *NAME*] Load bpf program from binary *OBJ* and pin as *FILE*. + **type** is optional, if not specified program type will be + inferred from section names. If **dev** *NAME* is specified program will be loaded onto given networking device (offload). diff --git a/tools/bpf/bpftool/bash-completion/bpftool b/tools/bpf/bpftool/bash-completion/bpftool index 238c2f80092a..caf8711993be 100644 --- a/tools/bpf/bpftool/bash-completion/bpftool +++ b/tools/bpf/bpftool/bash-completion/bpftool @@ -274,11 +274,17 @@ _bpftool() fi case $prev in + type) + COMPREPLY=( $( compgen -W "socket kprobe kretprobe classifier action tracepoint raw_tracepoint xdp perf_event cgroup/skb cgroup/sock cgroup/dev lwt_in lwt_out lwt_xmit lwt_seg6local sockops sk_skb sk_msg lirc_mode2 cgroup/bind4 cgroup/bind6 cgroup/connect4 cgroup/connect6 cgroup/sendmsg4 cgroup/sendmsg6 cgroup/post_bind4 cgroup/post_bind6" -- \ + "$cur" ) ) + return 0 + ;; dev) _sysfs_get_netdevs return 0 ;; *) + _bpftool_once_attr 'type' _bpftool_once_attr 'dev' return 0 ;; diff --git a/tools/bpf/bpftool/prog.c b/tools/bpf/bpftool/prog.c index 21c74de7156f..98695585bbb6 100644 --- a/tools/bpf/bpftool/prog.c +++ b/tools/bpf/bpftool/prog.c @@ -688,6 +688,7 @@ static int do_load(int argc, char **argv) const char *objfile, *pinfile; struct bpf_object *obj; int prog_fd; + int err; if (!REQ_ARGS(2)) return -1; @@ -695,7 +696,37 @@ static int do_load(int argc, char **argv) pinfile = GET_ARG(); while (argc) { - if (is_prefix(*argv, "dev")) { + if (is_prefix(*argv, "type")) { + char *type; + + NEXT_ARG(); + + if (attr.prog_type != BPF_PROG_TYPE_UNSPEC) { + p_err("program type already specified"); + return -1; + } + if (!REQ_ARGS(1)) + return -1; + + /* Put a '/' at the end of type to appease libbpf */ + type = malloc(strlen(*argv) + 2); + if (!type) { + p_err("mem alloc failed"); + return -1; + } + *type = 0; + strcat(type, *argv); + strcat(type, "/"); + + err = libbpf_prog_type_by_name(type, &attr.prog_type, + &attr.expected_attach_type); + free(type); + if (err < 0) { + p_err("unknown program type '%s'", *argv); + return err; + } + NEXT_ARG(); + } else if (is_prefix(*argv, "dev")) { NEXT_ARG(); if (attr.ifindex) { @@ -713,7 +744,7 @@ static int do_load(int argc, char **argv) } NEXT_ARG(); } else { - p_err("expected no more arguments or 'dev', got: '%s'?", + p_err("expected no more arguments, 'type' or 'dev', got: '%s'?", *argv); return -1; } @@ -753,10 +784,17 @@ static int do_help(int argc, char **argv) " %s %s dump xlated PROG [{ file FILE | opcodes | visual }]\n" " %s %s dump jited PROG [{ file FILE | opcodes }]\n" " %s %s pin PROG FILE\n" - " %s %s load OBJ FILE [dev NAME]\n" + " %s %s load OBJ FILE [type TYPE] [dev NAME]\n" " %s %s help\n" "\n" " " HELP_SPEC_PROGRAM "\n" + " TYPE := { socket | kprobe | kretprobe | classifier | action |\n" + " tracepoint | raw_tracepoint | xdp | perf_event | cgroup/skb |\n" + " cgroup/sock | cgroup/dev | lwt_in | lwt_out | lwt_xmit |\n" + " lwt_seg6local | sockops | sk_skb | sk_msg | lirc_mode2 |\n" + " cgroup/bind4 | cgroup/bind6 | cgroup/post_bind4 |\n" + " cgroup/post_bind6 | cgroup/connect4 | cgroup/connect6 |\n" + " cgroup/sendmsg4 | cgroup/sendmsg6 }\n" " " HELP_SPEC_OPTIONS "\n" "", bin_name, argv[-2], bin_name, argv[-2], bin_name, argv[-2], -- cgit v1.2.3-58-ga151 From f83fb22c6c68fdbc98c76291c9e12a40d1eb7ca5 Mon Sep 17 00:00:00 2001 From: Jakub Kicinski <jakub.kicinski@netronome.com> Date: Tue, 10 Jul 2018 14:43:01 -0700 Subject: tools: libbpf: recognize offload neutral maps Add helper to libbpf for recognizing maps which should not have ifindex set when program is loaded. These maps only contain host metadata and therefore are not marked for offload, e.g. the perf event map. Use this helper in bpf_prog_load_xattr(). Signed-off-by: Jakub Kicinski <jakub.kicinski@netronome.com> Reviewed-by: Quentin Monnet <quentin.monnet@netronome.com> Signed-off-by: Daniel Borkmann <daniel@iogearbox.net> --- tools/lib/bpf/libbpf.c | 8 +++++++- tools/lib/bpf/libbpf.h | 1 + 2 files changed, 8 insertions(+), 1 deletion(-) (limited to 'tools') diff --git a/tools/lib/bpf/libbpf.c b/tools/lib/bpf/libbpf.c index 42f31eff5f3f..30992305f4c1 100644 --- a/tools/lib/bpf/libbpf.c +++ b/tools/lib/bpf/libbpf.c @@ -2154,6 +2154,11 @@ void *bpf_map__priv(struct bpf_map *map) return map ? map->priv : ERR_PTR(-EINVAL); } +bool bpf_map__is_offload_neutral(struct bpf_map *map) +{ + return map->def.type == BPF_MAP_TYPE_PERF_EVENT_ARRAY; +} + void bpf_map__set_ifindex(struct bpf_map *map, __u32 ifindex) { map->map_ifindex = ifindex; @@ -2278,7 +2283,8 @@ int bpf_prog_load_xattr(const struct bpf_prog_load_attr *attr, } bpf_map__for_each(map, obj) { - map->map_ifindex = attr->ifindex; + if (!bpf_map__is_offload_neutral(map)) + map->map_ifindex = attr->ifindex; } if (!first_prog) { diff --git a/tools/lib/bpf/libbpf.h b/tools/lib/bpf/libbpf.h index d1ce5c828e2e..749acf58a5da 100644 --- a/tools/lib/bpf/libbpf.h +++ b/tools/lib/bpf/libbpf.h @@ -255,6 +255,7 @@ typedef void (*bpf_map_clear_priv_t)(struct bpf_map *, void *); int bpf_map__set_priv(struct bpf_map *map, void *priv, bpf_map_clear_priv_t clear_priv); void *bpf_map__priv(struct bpf_map *map); +bool bpf_map__is_offload_neutral(struct bpf_map *map); void bpf_map__set_ifindex(struct bpf_map *map, __u32 ifindex); int bpf_map__pin(struct bpf_map *map, const char *path); -- cgit v1.2.3-58-ga151 From 07f2d4eac2a850fc9649b27aa935cdcd263fb1ff Mon Sep 17 00:00:00 2001 From: Jakub Kicinski <jakub.kicinski@netronome.com> Date: Tue, 10 Jul 2018 14:43:02 -0700 Subject: tools: libbpf: add extended attributes version of bpf_object__open() Similarly to bpf_prog_load() users of bpf_object__open() may need to specify the expected program type. Program type is needed at open to avoid the kernel version check for program types which don't require it. Signed-off-by: Jakub Kicinski <jakub.kicinski@netronome.com> Reviewed-by: Quentin Monnet <quentin.monnet@netronome.com> Acked-by: Andrey Ignatov <rdna@fb.com> Signed-off-by: Daniel Borkmann <daniel@iogearbox.net> --- tools/lib/bpf/libbpf.c | 26 ++++++++++++++++++++------ tools/lib/bpf/libbpf.h | 6 ++++++ 2 files changed, 26 insertions(+), 6 deletions(-) (limited to 'tools') diff --git a/tools/lib/bpf/libbpf.c b/tools/lib/bpf/libbpf.c index 30992305f4c1..06cd534d2fba 100644 --- a/tools/lib/bpf/libbpf.c +++ b/tools/lib/bpf/libbpf.c @@ -1520,15 +1520,26 @@ out: return ERR_PTR(err); } -struct bpf_object *bpf_object__open(const char *path) +struct bpf_object *bpf_object__open_xattr(struct bpf_object_open_attr *attr) { /* param validation */ - if (!path) + if (!attr->file) return NULL; - pr_debug("loading %s\n", path); + pr_debug("loading %s\n", attr->file); + + return __bpf_object__open(attr->file, NULL, 0, + bpf_prog_type__needs_kver(attr->prog_type)); +} + +struct bpf_object *bpf_object__open(const char *path) +{ + struct bpf_object_open_attr attr = { + .file = path, + .prog_type = BPF_PROG_TYPE_UNSPEC, + }; - return __bpf_object__open(path, NULL, 0, true); + return bpf_object__open_xattr(&attr); } struct bpf_object *bpf_object__open_buffer(void *obj_buf, @@ -2238,6 +2249,10 @@ int bpf_prog_load(const char *file, enum bpf_prog_type type, int bpf_prog_load_xattr(const struct bpf_prog_load_attr *attr, struct bpf_object **pobj, int *prog_fd) { + struct bpf_object_open_attr open_attr = { + .file = attr->file, + .prog_type = attr->prog_type, + }; struct bpf_program *prog, *first_prog = NULL; enum bpf_attach_type expected_attach_type; enum bpf_prog_type prog_type; @@ -2250,8 +2265,7 @@ int bpf_prog_load_xattr(const struct bpf_prog_load_attr *attr, if (!attr->file) return -EINVAL; - obj = __bpf_object__open(attr->file, NULL, 0, - bpf_prog_type__needs_kver(attr->prog_type)); + obj = bpf_object__open_xattr(&open_attr); if (IS_ERR_OR_NULL(obj)) return -ENOENT; diff --git a/tools/lib/bpf/libbpf.h b/tools/lib/bpf/libbpf.h index 749acf58a5da..e911ad32d02e 100644 --- a/tools/lib/bpf/libbpf.h +++ b/tools/lib/bpf/libbpf.h @@ -66,7 +66,13 @@ void libbpf_set_print(libbpf_print_fn_t warn, /* Hide internal to user */ struct bpf_object; +struct bpf_object_open_attr { + const char *file; + enum bpf_prog_type prog_type; +}; + struct bpf_object *bpf_object__open(const char *path); +struct bpf_object *bpf_object__open_xattr(struct bpf_object_open_attr *attr); struct bpf_object *bpf_object__open_buffer(void *obj_buf, size_t obj_buf_sz, const char *name); -- cgit v1.2.3-58-ga151 From c8406848badd0a0b040b0d286e612678662a2ab3 Mon Sep 17 00:00:00 2001 From: Jakub Kicinski <jakub.kicinski@netronome.com> Date: Tue, 10 Jul 2018 14:43:03 -0700 Subject: tools: bpftool: reimplement bpf_prog_load() for prog load bpf_prog_load() is a very useful helper but it doesn't give us full flexibility of modifying the BPF objects before loading. Open code bpf_prog_load() in bpftool so we can add extra logic in following commits. Signed-off-by: Jakub Kicinski <jakub.kicinski@netronome.com> Reviewed-by: Quentin Monnet <quentin.monnet@netronome.com> Acked-by: Alexei Starovoitov <ast@kernel.org> Signed-off-by: Daniel Borkmann <daniel@iogearbox.net> --- tools/bpf/bpftool/prog.c | 61 +++++++++++++++++++++++++++++++++++++----------- 1 file changed, 48 insertions(+), 13 deletions(-) (limited to 'tools') diff --git a/tools/bpf/bpftool/prog.c b/tools/bpf/bpftool/prog.c index 98695585bbb6..2bdd5ecd1aad 100644 --- a/tools/bpf/bpftool/prog.c +++ b/tools/bpf/bpftool/prog.c @@ -43,6 +43,8 @@ #include <sys/types.h> #include <sys/stat.h> +#include <linux/err.h> + #include <bpf.h> #include <libbpf.h> @@ -682,17 +684,20 @@ static int do_pin(int argc, char **argv) static int do_load(int argc, char **argv) { - struct bpf_prog_load_attr attr = { + enum bpf_attach_type expected_attach_type; + struct bpf_object_open_attr attr = { .prog_type = BPF_PROG_TYPE_UNSPEC, }; - const char *objfile, *pinfile; + struct bpf_program *prog; struct bpf_object *obj; - int prog_fd; + struct bpf_map *map; + const char *pinfile; + __u32 ifindex = 0; int err; if (!REQ_ARGS(2)) return -1; - objfile = GET_ARG(); + attr.file = GET_ARG(); pinfile = GET_ARG(); while (argc) { @@ -719,7 +724,7 @@ static int do_load(int argc, char **argv) strcat(type, "/"); err = libbpf_prog_type_by_name(type, &attr.prog_type, - &attr.expected_attach_type); + &expected_attach_type); free(type); if (err < 0) { p_err("unknown program type '%s'", *argv); @@ -729,15 +734,15 @@ static int do_load(int argc, char **argv) } else if (is_prefix(*argv, "dev")) { NEXT_ARG(); - if (attr.ifindex) { + if (ifindex) { p_err("offload device already specified"); return -1; } if (!REQ_ARGS(1)) return -1; - attr.ifindex = if_nametoindex(*argv); - if (!attr.ifindex) { + ifindex = if_nametoindex(*argv); + if (!ifindex) { p_err("unrecognized netdevice '%s': %s", *argv, strerror(errno)); return -1; @@ -750,14 +755,44 @@ static int do_load(int argc, char **argv) } } - attr.file = objfile; - - if (bpf_prog_load_xattr(&attr, &obj, &prog_fd)) { - p_err("failed to load program"); + obj = bpf_object__open_xattr(&attr); + if (IS_ERR_OR_NULL(obj)) { + p_err("failed to open object file"); return -1; } - if (do_pin_fd(prog_fd, pinfile)) + prog = bpf_program__next(NULL, obj); + if (!prog) { + p_err("object file doesn't contain any bpf program"); + goto err_close_obj; + } + + bpf_program__set_ifindex(prog, ifindex); + if (attr.prog_type == BPF_PROG_TYPE_UNSPEC) { + const char *sec_name = bpf_program__title(prog, false); + + err = libbpf_prog_type_by_name(sec_name, &attr.prog_type, + &expected_attach_type); + if (err < 0) { + p_err("failed to guess program type based on section name %s\n", + sec_name); + goto err_close_obj; + } + } + bpf_program__set_type(prog, attr.prog_type); + bpf_program__set_expected_attach_type(prog, expected_attach_type); + + bpf_map__for_each(map, obj) + if (!bpf_map__is_offload_neutral(map)) + bpf_map__set_ifindex(map, ifindex); + + err = bpf_object__load(obj); + if (err) { + p_err("failed to load object file"); + goto err_close_obj; + } + + if (do_pin_fd(bpf_program__fd(prog), pinfile)) goto err_close_obj; if (json_output) -- cgit v1.2.3-58-ga151 From 8d13406c02f9c38f106416e1dbe0e68059b9f59a Mon Sep 17 00:00:00 2001 From: Jakub Kicinski <jakub.kicinski@netronome.com> Date: Tue, 10 Jul 2018 14:43:04 -0700 Subject: tools: libbpf: move library error code into a separate file libbpf_strerror() depends on XSI-compliant (POSIX) version of strerror_r(), which prevents us from using GNU-extensions in libbpf.c, like reallocarray() or dup3(). Move error printing code into a separate file to allow it to continue using POSIX strerror_r(). No functional changes. Signed-off-by: Jakub Kicinski <jakub.kicinski@netronome.com> Signed-off-by: Daniel Borkmann <daniel@iogearbox.net> --- tools/lib/bpf/Build | 2 +- tools/lib/bpf/libbpf.c | 48 ---------------------------- tools/lib/bpf/libbpf_errno.c | 74 ++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 75 insertions(+), 49 deletions(-) create mode 100644 tools/lib/bpf/libbpf_errno.c (limited to 'tools') diff --git a/tools/lib/bpf/Build b/tools/lib/bpf/Build index 6070e655042d..13a861135127 100644 --- a/tools/lib/bpf/Build +++ b/tools/lib/bpf/Build @@ -1 +1 @@ -libbpf-y := libbpf.o bpf.o nlattr.o btf.o +libbpf-y := libbpf.o bpf.o nlattr.o btf.o libbpf_errno.o diff --git a/tools/lib/bpf/libbpf.c b/tools/lib/bpf/libbpf.c index 06cd534d2fba..f732237610e5 100644 --- a/tools/lib/bpf/libbpf.c +++ b/tools/lib/bpf/libbpf.c @@ -95,54 +95,6 @@ void libbpf_set_print(libbpf_print_fn_t warn, #define STRERR_BUFSIZE 128 -#define ERRNO_OFFSET(e) ((e) - __LIBBPF_ERRNO__START) -#define ERRCODE_OFFSET(c) ERRNO_OFFSET(LIBBPF_ERRNO__##c) -#define NR_ERRNO (__LIBBPF_ERRNO__END - __LIBBPF_ERRNO__START) - -static const char *libbpf_strerror_table[NR_ERRNO] = { - [ERRCODE_OFFSET(LIBELF)] = "Something wrong in libelf", - [ERRCODE_OFFSET(FORMAT)] = "BPF object format invalid", - [ERRCODE_OFFSET(KVERSION)] = "'version' section incorrect or lost", - [ERRCODE_OFFSET(ENDIAN)] = "Endian mismatch", - [ERRCODE_OFFSET(INTERNAL)] = "Internal error in libbpf", - [ERRCODE_OFFSET(RELOC)] = "Relocation failed", - [ERRCODE_OFFSET(VERIFY)] = "Kernel verifier blocks program loading", - [ERRCODE_OFFSET(PROG2BIG)] = "Program too big", - [ERRCODE_OFFSET(KVER)] = "Incorrect kernel version", - [ERRCODE_OFFSET(PROGTYPE)] = "Kernel doesn't support this program type", - [ERRCODE_OFFSET(WRNGPID)] = "Wrong pid in netlink message", - [ERRCODE_OFFSET(INVSEQ)] = "Invalid netlink sequence", -}; - -int libbpf_strerror(int err, char *buf, size_t size) -{ - if (!buf || !size) - return -1; - - err = err > 0 ? err : -err; - - if (err < __LIBBPF_ERRNO__START) { - int ret; - - ret = strerror_r(err, buf, size); - buf[size - 1] = '\0'; - return ret; - } - - if (err < __LIBBPF_ERRNO__END) { - const char *msg; - - msg = libbpf_strerror_table[ERRNO_OFFSET(err)]; - snprintf(buf, size, "%s", msg); - buf[size - 1] = '\0'; - return 0; - } - - snprintf(buf, size, "Unknown libbpf error %d", err); - buf[size - 1] = '\0'; - return -1; -} - #define CHECK_ERR(action, err, out) do { \ err = action; \ if (err) \ diff --git a/tools/lib/bpf/libbpf_errno.c b/tools/lib/bpf/libbpf_errno.c new file mode 100644 index 000000000000..d9ba851bd7f9 --- /dev/null +++ b/tools/lib/bpf/libbpf_errno.c @@ -0,0 +1,74 @@ +// SPDX-License-Identifier: LGPL-2.1 + +/* + * Copyright (C) 2013-2015 Alexei Starovoitov <ast@kernel.org> + * Copyright (C) 2015 Wang Nan <wangnan0@huawei.com> + * Copyright (C) 2015 Huawei Inc. + * Copyright (C) 2017 Nicira, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License (not later!) + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, see <http://www.gnu.org/licenses> + */ + +#include <stdio.h> +#include <string.h> + +#include "libbpf.h" + +#define ERRNO_OFFSET(e) ((e) - __LIBBPF_ERRNO__START) +#define ERRCODE_OFFSET(c) ERRNO_OFFSET(LIBBPF_ERRNO__##c) +#define NR_ERRNO (__LIBBPF_ERRNO__END - __LIBBPF_ERRNO__START) + +static const char *libbpf_strerror_table[NR_ERRNO] = { + [ERRCODE_OFFSET(LIBELF)] = "Something wrong in libelf", + [ERRCODE_OFFSET(FORMAT)] = "BPF object format invalid", + [ERRCODE_OFFSET(KVERSION)] = "'version' section incorrect or lost", + [ERRCODE_OFFSET(ENDIAN)] = "Endian mismatch", + [ERRCODE_OFFSET(INTERNAL)] = "Internal error in libbpf", + [ERRCODE_OFFSET(RELOC)] = "Relocation failed", + [ERRCODE_OFFSET(VERIFY)] = "Kernel verifier blocks program loading", + [ERRCODE_OFFSET(PROG2BIG)] = "Program too big", + [ERRCODE_OFFSET(KVER)] = "Incorrect kernel version", + [ERRCODE_OFFSET(PROGTYPE)] = "Kernel doesn't support this program type", + [ERRCODE_OFFSET(WRNGPID)] = "Wrong pid in netlink message", + [ERRCODE_OFFSET(INVSEQ)] = "Invalid netlink sequence", +}; + +int libbpf_strerror(int err, char *buf, size_t size) +{ + if (!buf || !size) + return -1; + + err = err > 0 ? err : -err; + + if (err < __LIBBPF_ERRNO__START) { + int ret; + + ret = strerror_r(err, buf, size); + buf[size - 1] = '\0'; + return ret; + } + + if (err < __LIBBPF_ERRNO__END) { + const char *msg; + + msg = libbpf_strerror_table[ERRNO_OFFSET(err)]; + snprintf(buf, size, "%s", msg); + buf[size - 1] = '\0'; + return 0; + } + + snprintf(buf, size, "Unknown libbpf error %d", err); + buf[size - 1] = '\0'; + return -1; +} -- cgit v1.2.3-58-ga151 From 531b014e7a2fedaeff0b19b2934d830cd4b35dc0 Mon Sep 17 00:00:00 2001 From: Jakub Kicinski <jakub.kicinski@netronome.com> Date: Tue, 10 Jul 2018 14:43:05 -0700 Subject: tools: bpf: make use of reallocarray reallocarray() is a safer variant of realloc which checks for multiplication overflow in case of array allocation. Since it's not available in Glibc < 2.26 import kernel's overflow.h and add a static inline implementation when needed. Use feature detection to probe for existence of reallocarray. Signed-off-by: Jakub Kicinski <jakub.kicinski@netronome.com> Reviewed-by: Quentin Monnet <quentin.monnet@netronome.com> Reviewed-by: Jiong Wang <jiong.wang@netronome.com> Signed-off-by: Daniel Borkmann <daniel@iogearbox.net> --- tools/bpf/bpftool/Makefile | 6 +- tools/bpf/bpftool/main.h | 1 + tools/bpf/bpftool/xlated_dumper.c | 6 +- tools/build/feature/Makefile | 4 + tools/build/feature/test-reallocarray.c | 8 + tools/include/linux/compiler-gcc.h | 4 + tools/include/linux/overflow.h | 278 ++++++++++++++++++++++++++++++++ tools/include/tools/libc_compat.h | 20 +++ tools/lib/bpf/Makefile | 6 +- tools/lib/bpf/libbpf.c | 10 +- 10 files changed, 334 insertions(+), 9 deletions(-) create mode 100644 tools/build/feature/test-reallocarray.c create mode 100644 tools/include/linux/overflow.h create mode 100644 tools/include/tools/libc_compat.h (limited to 'tools') diff --git a/tools/bpf/bpftool/Makefile b/tools/bpf/bpftool/Makefile index 0911b00b25cc..6c4830e18879 100644 --- a/tools/bpf/bpftool/Makefile +++ b/tools/bpf/bpftool/Makefile @@ -52,7 +52,7 @@ INSTALL ?= install RM ?= rm -f FEATURE_USER = .bpftool -FEATURE_TESTS = libbfd disassembler-four-args +FEATURE_TESTS = libbfd disassembler-four-args reallocarray FEATURE_DISPLAY = libbfd disassembler-four-args check_feat := 1 @@ -75,6 +75,10 @@ ifeq ($(feature-disassembler-four-args), 1) CFLAGS += -DDISASM_FOUR_ARGS_SIGNATURE endif +ifeq ($(feature-reallocarray), 0) +CFLAGS += -DCOMPAT_NEED_REALLOCARRAY +endif + include $(wildcard $(OUTPUT)*.d) all: $(OUTPUT)bpftool diff --git a/tools/bpf/bpftool/main.h b/tools/bpf/bpftool/main.h index 15b6c49ae533..1e02e4031693 100644 --- a/tools/bpf/bpftool/main.h +++ b/tools/bpf/bpftool/main.h @@ -42,6 +42,7 @@ #include <linux/compiler.h> #include <linux/kernel.h> #include <linux/hashtable.h> +#include <tools/libc_compat.h> #include "json_writer.h" diff --git a/tools/bpf/bpftool/xlated_dumper.c b/tools/bpf/bpftool/xlated_dumper.c index b97f1da60dd1..3284759df98a 100644 --- a/tools/bpf/bpftool/xlated_dumper.c +++ b/tools/bpf/bpftool/xlated_dumper.c @@ -35,6 +35,7 @@ * POSSIBILITY OF SUCH DAMAGE. */ +#define _GNU_SOURCE #include <stdarg.h> #include <stdio.h> #include <stdlib.h> @@ -66,9 +67,8 @@ void kernel_syms_load(struct dump_data *dd) while (!feof(fp)) { if (!fgets(buff, sizeof(buff), fp)) break; - tmp = realloc(dd->sym_mapping, - (dd->sym_count + 1) * - sizeof(*dd->sym_mapping)); + tmp = reallocarray(dd->sym_mapping, dd->sym_count + 1, + sizeof(*dd->sym_mapping)); if (!tmp) { out: free(dd->sym_mapping); diff --git a/tools/build/feature/Makefile b/tools/build/feature/Makefile index dac9563b5470..0516259be70f 100644 --- a/tools/build/feature/Makefile +++ b/tools/build/feature/Makefile @@ -14,6 +14,7 @@ FILES= \ test-libaudit.bin \ test-libbfd.bin \ test-disassembler-four-args.bin \ + test-reallocarray.bin \ test-liberty.bin \ test-liberty-z.bin \ test-cplus-demangle.bin \ @@ -204,6 +205,9 @@ $(OUTPUT)test-libbfd.bin: $(OUTPUT)test-disassembler-four-args.bin: $(BUILD) -DPACKAGE='"perf"' -lbfd -lopcodes +$(OUTPUT)test-reallocarray.bin: + $(BUILD) + $(OUTPUT)test-liberty.bin: $(CC) $(CFLAGS) -Wall -Werror -o $@ test-libbfd.c -DPACKAGE='"perf"' $(LDFLAGS) -lbfd -ldl -liberty diff --git a/tools/build/feature/test-reallocarray.c b/tools/build/feature/test-reallocarray.c new file mode 100644 index 000000000000..8170de35150d --- /dev/null +++ b/tools/build/feature/test-reallocarray.c @@ -0,0 +1,8 @@ +// SPDX-License-Identifier: GPL-2.0 +#define _GNU_SOURCE +#include <stdlib.h> + +int main(void) +{ + return !!reallocarray(NULL, 1, 1); +} diff --git a/tools/include/linux/compiler-gcc.h b/tools/include/linux/compiler-gcc.h index 70fe61295733..0d35f18006a1 100644 --- a/tools/include/linux/compiler-gcc.h +++ b/tools/include/linux/compiler-gcc.h @@ -36,3 +36,7 @@ #endif #define __printf(a, b) __attribute__((format(printf, a, b))) #define __scanf(a, b) __attribute__((format(scanf, a, b))) + +#if GCC_VERSION >= 50100 +#define COMPILER_HAS_GENERIC_BUILTIN_OVERFLOW 1 +#endif diff --git a/tools/include/linux/overflow.h b/tools/include/linux/overflow.h new file mode 100644 index 000000000000..8712ff70995f --- /dev/null +++ b/tools/include/linux/overflow.h @@ -0,0 +1,278 @@ +/* SPDX-License-Identifier: GPL-2.0 OR MIT */ +#ifndef __LINUX_OVERFLOW_H +#define __LINUX_OVERFLOW_H + +#include <linux/compiler.h> + +/* + * In the fallback code below, we need to compute the minimum and + * maximum values representable in a given type. These macros may also + * be useful elsewhere, so we provide them outside the + * COMPILER_HAS_GENERIC_BUILTIN_OVERFLOW block. + * + * It would seem more obvious to do something like + * + * #define type_min(T) (T)(is_signed_type(T) ? (T)1 << (8*sizeof(T)-1) : 0) + * #define type_max(T) (T)(is_signed_type(T) ? ((T)1 << (8*sizeof(T)-1)) - 1 : ~(T)0) + * + * Unfortunately, the middle expressions, strictly speaking, have + * undefined behaviour, and at least some versions of gcc warn about + * the type_max expression (but not if -fsanitize=undefined is in + * effect; in that case, the warning is deferred to runtime...). + * + * The slightly excessive casting in type_min is to make sure the + * macros also produce sensible values for the exotic type _Bool. [The + * overflow checkers only almost work for _Bool, but that's + * a-feature-not-a-bug, since people shouldn't be doing arithmetic on + * _Bools. Besides, the gcc builtins don't allow _Bool* as third + * argument.] + * + * Idea stolen from + * https://mail-index.netbsd.org/tech-misc/2007/02/05/0000.html - + * credit to Christian Biere. + */ +#define is_signed_type(type) (((type)(-1)) < (type)1) +#define __type_half_max(type) ((type)1 << (8*sizeof(type) - 1 - is_signed_type(type))) +#define type_max(T) ((T)((__type_half_max(T) - 1) + __type_half_max(T))) +#define type_min(T) ((T)((T)-type_max(T)-(T)1)) + + +#ifdef COMPILER_HAS_GENERIC_BUILTIN_OVERFLOW +/* + * For simplicity and code hygiene, the fallback code below insists on + * a, b and *d having the same type (similar to the min() and max() + * macros), whereas gcc's type-generic overflow checkers accept + * different types. Hence we don't just make check_add_overflow an + * alias for __builtin_add_overflow, but add type checks similar to + * below. + */ +#define check_add_overflow(a, b, d) ({ \ + typeof(a) __a = (a); \ + typeof(b) __b = (b); \ + typeof(d) __d = (d); \ + (void) (&__a == &__b); \ + (void) (&__a == __d); \ + __builtin_add_overflow(__a, __b, __d); \ +}) + +#define check_sub_overflow(a, b, d) ({ \ + typeof(a) __a = (a); \ + typeof(b) __b = (b); \ + typeof(d) __d = (d); \ + (void) (&__a == &__b); \ + (void) (&__a == __d); \ + __builtin_sub_overflow(__a, __b, __d); \ +}) + +#define check_mul_overflow(a, b, d) ({ \ + typeof(a) __a = (a); \ + typeof(b) __b = (b); \ + typeof(d) __d = (d); \ + (void) (&__a == &__b); \ + (void) (&__a == __d); \ + __builtin_mul_overflow(__a, __b, __d); \ +}) + +#else + + +/* Checking for unsigned overflow is relatively easy without causing UB. */ +#define __unsigned_add_overflow(a, b, d) ({ \ + typeof(a) __a = (a); \ + typeof(b) __b = (b); \ + typeof(d) __d = (d); \ + (void) (&__a == &__b); \ + (void) (&__a == __d); \ + *__d = __a + __b; \ + *__d < __a; \ +}) +#define __unsigned_sub_overflow(a, b, d) ({ \ + typeof(a) __a = (a); \ + typeof(b) __b = (b); \ + typeof(d) __d = (d); \ + (void) (&__a == &__b); \ + (void) (&__a == __d); \ + *__d = __a - __b; \ + __a < __b; \ +}) +/* + * If one of a or b is a compile-time constant, this avoids a division. + */ +#define __unsigned_mul_overflow(a, b, d) ({ \ + typeof(a) __a = (a); \ + typeof(b) __b = (b); \ + typeof(d) __d = (d); \ + (void) (&__a == &__b); \ + (void) (&__a == __d); \ + *__d = __a * __b; \ + __builtin_constant_p(__b) ? \ + __b > 0 && __a > type_max(typeof(__a)) / __b : \ + __a > 0 && __b > type_max(typeof(__b)) / __a; \ +}) + +/* + * For signed types, detecting overflow is much harder, especially if + * we want to avoid UB. But the interface of these macros is such that + * we must provide a result in *d, and in fact we must produce the + * result promised by gcc's builtins, which is simply the possibly + * wrapped-around value. Fortunately, we can just formally do the + * operations in the widest relevant unsigned type (u64) and then + * truncate the result - gcc is smart enough to generate the same code + * with and without the (u64) casts. + */ + +/* + * Adding two signed integers can overflow only if they have the same + * sign, and overflow has happened iff the result has the opposite + * sign. + */ +#define __signed_add_overflow(a, b, d) ({ \ + typeof(a) __a = (a); \ + typeof(b) __b = (b); \ + typeof(d) __d = (d); \ + (void) (&__a == &__b); \ + (void) (&__a == __d); \ + *__d = (u64)__a + (u64)__b; \ + (((~(__a ^ __b)) & (*__d ^ __a)) \ + & type_min(typeof(__a))) != 0; \ +}) + +/* + * Subtraction is similar, except that overflow can now happen only + * when the signs are opposite. In this case, overflow has happened if + * the result has the opposite sign of a. + */ +#define __signed_sub_overflow(a, b, d) ({ \ + typeof(a) __a = (a); \ + typeof(b) __b = (b); \ + typeof(d) __d = (d); \ + (void) (&__a == &__b); \ + (void) (&__a == __d); \ + *__d = (u64)__a - (u64)__b; \ + ((((__a ^ __b)) & (*__d ^ __a)) \ + & type_min(typeof(__a))) != 0; \ +}) + +/* + * Signed multiplication is rather hard. gcc always follows C99, so + * division is truncated towards 0. This means that we can write the + * overflow check like this: + * + * (a > 0 && (b > MAX/a || b < MIN/a)) || + * (a < -1 && (b > MIN/a || b < MAX/a) || + * (a == -1 && b == MIN) + * + * The redundant casts of -1 are to silence an annoying -Wtype-limits + * (included in -Wextra) warning: When the type is u8 or u16, the + * __b_c_e in check_mul_overflow obviously selects + * __unsigned_mul_overflow, but unfortunately gcc still parses this + * code and warns about the limited range of __b. + */ + +#define __signed_mul_overflow(a, b, d) ({ \ + typeof(a) __a = (a); \ + typeof(b) __b = (b); \ + typeof(d) __d = (d); \ + typeof(a) __tmax = type_max(typeof(a)); \ + typeof(a) __tmin = type_min(typeof(a)); \ + (void) (&__a == &__b); \ + (void) (&__a == __d); \ + *__d = (u64)__a * (u64)__b; \ + (__b > 0 && (__a > __tmax/__b || __a < __tmin/__b)) || \ + (__b < (typeof(__b))-1 && (__a > __tmin/__b || __a < __tmax/__b)) || \ + (__b == (typeof(__b))-1 && __a == __tmin); \ +}) + + +#define check_add_overflow(a, b, d) \ + __builtin_choose_expr(is_signed_type(typeof(a)), \ + __signed_add_overflow(a, b, d), \ + __unsigned_add_overflow(a, b, d)) + +#define check_sub_overflow(a, b, d) \ + __builtin_choose_expr(is_signed_type(typeof(a)), \ + __signed_sub_overflow(a, b, d), \ + __unsigned_sub_overflow(a, b, d)) + +#define check_mul_overflow(a, b, d) \ + __builtin_choose_expr(is_signed_type(typeof(a)), \ + __signed_mul_overflow(a, b, d), \ + __unsigned_mul_overflow(a, b, d)) + + +#endif /* COMPILER_HAS_GENERIC_BUILTIN_OVERFLOW */ + +/** + * array_size() - Calculate size of 2-dimensional array. + * + * @a: dimension one + * @b: dimension two + * + * Calculates size of 2-dimensional array: @a * @b. + * + * Returns: number of bytes needed to represent the array or SIZE_MAX on + * overflow. + */ +static inline __must_check size_t array_size(size_t a, size_t b) +{ + size_t bytes; + + if (check_mul_overflow(a, b, &bytes)) + return SIZE_MAX; + + return bytes; +} + +/** + * array3_size() - Calculate size of 3-dimensional array. + * + * @a: dimension one + * @b: dimension two + * @c: dimension three + * + * Calculates size of 3-dimensional array: @a * @b * @c. + * + * Returns: number of bytes needed to represent the array or SIZE_MAX on + * overflow. + */ +static inline __must_check size_t array3_size(size_t a, size_t b, size_t c) +{ + size_t bytes; + + if (check_mul_overflow(a, b, &bytes)) + return SIZE_MAX; + if (check_mul_overflow(bytes, c, &bytes)) + return SIZE_MAX; + + return bytes; +} + +static inline __must_check size_t __ab_c_size(size_t n, size_t size, size_t c) +{ + size_t bytes; + + if (check_mul_overflow(n, size, &bytes)) + return SIZE_MAX; + if (check_add_overflow(bytes, c, &bytes)) + return SIZE_MAX; + + return bytes; +} + +/** + * struct_size() - Calculate size of structure with trailing array. + * @p: Pointer to the structure. + * @member: Name of the array member. + * @n: Number of elements in the array. + * + * Calculates size of memory needed for structure @p followed by an + * array of @n @member elements. + * + * Return: number of bytes needed or SIZE_MAX on overflow. + */ +#define struct_size(p, member, n) \ + __ab_c_size(n, \ + sizeof(*(p)->member) + __must_be_array((p)->member),\ + sizeof(*(p))) + +#endif /* __LINUX_OVERFLOW_H */ diff --git a/tools/include/tools/libc_compat.h b/tools/include/tools/libc_compat.h new file mode 100644 index 000000000000..664ced8cb1b0 --- /dev/null +++ b/tools/include/tools/libc_compat.h @@ -0,0 +1,20 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* Copyright (C) 2018 Netronome Systems, Inc. */ + +#ifndef __TOOLS_LIBC_COMPAT_H +#define __TOOLS_LIBC_COMPAT_H + +#include <stdlib.h> +#include <linux/overflow.h> + +#ifdef COMPAT_NEED_REALLOCARRAY +static inline void *reallocarray(void *ptr, size_t nmemb, size_t size) +{ + size_t bytes; + + if (unlikely(check_mul_overflow(nmemb, size, &bytes))) + return NULL; + return realloc(ptr, bytes); +} +#endif +#endif diff --git a/tools/lib/bpf/Makefile b/tools/lib/bpf/Makefile index 5390e7725e43..7a8e4c98ef1a 100644 --- a/tools/lib/bpf/Makefile +++ b/tools/lib/bpf/Makefile @@ -66,7 +66,7 @@ ifndef VERBOSE endif FEATURE_USER = .libbpf -FEATURE_TESTS = libelf libelf-getphdrnum libelf-mmap bpf +FEATURE_TESTS = libelf libelf-getphdrnum libelf-mmap bpf reallocarray FEATURE_DISPLAY = libelf bpf INCLUDES = -I. -I$(srctree)/tools/include -I$(srctree)/tools/arch/$(ARCH)/include/uapi -I$(srctree)/tools/include/uapi -I$(srctree)/tools/perf @@ -120,6 +120,10 @@ ifeq ($(feature-libelf-getphdrnum), 1) override CFLAGS += -DHAVE_ELF_GETPHDRNUM_SUPPORT endif +ifeq ($(feature-reallocarray), 0) + override CFLAGS += -DCOMPAT_NEED_REALLOCARRAY +endif + # Append required CFLAGS override CFLAGS += $(EXTRA_WARNINGS) override CFLAGS += -Werror -Wall diff --git a/tools/lib/bpf/libbpf.c b/tools/lib/bpf/libbpf.c index f732237610e5..fd2c4433863d 100644 --- a/tools/lib/bpf/libbpf.c +++ b/tools/lib/bpf/libbpf.c @@ -22,6 +22,7 @@ * License along with this program; if not, see <http://www.gnu.org/licenses> */ +#define _GNU_SOURCE #include <stdlib.h> #include <stdio.h> #include <stdarg.h> @@ -41,6 +42,7 @@ #include <sys/stat.h> #include <sys/types.h> #include <sys/vfs.h> +#include <tools/libc_compat.h> #include <libelf.h> #include <gelf.h> @@ -321,7 +323,7 @@ bpf_object__add_program(struct bpf_object *obj, void *data, size_t size, progs = obj->programs; nr_progs = obj->nr_programs; - progs = realloc(progs, sizeof(progs[0]) * (nr_progs + 1)); + progs = reallocarray(progs, nr_progs + 1, sizeof(progs[0])); if (!progs) { /* * In this case the original obj->programs @@ -822,8 +824,8 @@ static int bpf_object__elf_collect(struct bpf_object *obj) continue; } - reloc = realloc(reloc, - sizeof(*obj->efile.reloc) * nr_reloc); + reloc = reallocarray(reloc, nr_reloc, + sizeof(*obj->efile.reloc)); if (!reloc) { pr_warning("realloc failed\n"); err = -ENOMEM; @@ -1115,7 +1117,7 @@ bpf_program__reloc_text(struct bpf_program *prog, struct bpf_object *obj, return -LIBBPF_ERRNO__RELOC; } new_cnt = prog->insns_cnt + text->insns_cnt; - new_insn = realloc(prog->insns, new_cnt * sizeof(*insn)); + new_insn = reallocarray(prog->insns, new_cnt, sizeof(*insn)); if (!new_insn) { pr_warning("oom in prog realloc\n"); return -ENOMEM; -- cgit v1.2.3-58-ga151 From 26736eb9a483715c2e971a8866f55fbb156903e2 Mon Sep 17 00:00:00 2001 From: Jakub Kicinski <jakub.kicinski@netronome.com> Date: Tue, 10 Jul 2018 14:43:06 -0700 Subject: tools: libbpf: allow map reuse More advanced applications may want to only replace programs without destroying associated maps. Allow libbpf users to achieve that. Instead of always creating all of the maps at load time, expose to users an API to reconstruct the map object from already existing map. The map parameters are read from the kernel and replace the parameters of the ELF map. libbpf does not restrict the map replacement, i.e. the reused map does not have to be compatible with the ELF map definition. We relay on the verifier for checking the compatibility between maps and programs. The ELF map definition is completely overwritten by the information read from the kernel, to make sure libbpf's view of map object corresponds to the actual map. Signed-off-by: Jakub Kicinski <jakub.kicinski@netronome.com> Reviewed-by: Quentin Monnet <quentin.monnet@netronome.com> Acked-by: Andrey Ignatov <rdna@fb.com> Signed-off-by: Daniel Borkmann <daniel@iogearbox.net> --- tools/lib/bpf/libbpf.c | 53 ++++++++++++++++++++++++++++++++++++++++++++++++++ tools/lib/bpf/libbpf.h | 1 + 2 files changed, 54 insertions(+) (limited to 'tools') diff --git a/tools/lib/bpf/libbpf.c b/tools/lib/bpf/libbpf.c index fd2c4433863d..955f8eafbf41 100644 --- a/tools/lib/bpf/libbpf.c +++ b/tools/lib/bpf/libbpf.c @@ -1035,6 +1035,53 @@ static int bpf_map_find_btf_info(struct bpf_map *map, const struct btf *btf) return 0; } +int bpf_map__reuse_fd(struct bpf_map *map, int fd) +{ + struct bpf_map_info info = {}; + __u32 len = sizeof(info); + int new_fd, err; + char *new_name; + + err = bpf_obj_get_info_by_fd(fd, &info, &len); + if (err) + return err; + + new_name = strdup(info.name); + if (!new_name) + return -errno; + + new_fd = open("/", O_RDONLY | O_CLOEXEC); + if (new_fd < 0) + goto err_free_new_name; + + new_fd = dup3(fd, new_fd, O_CLOEXEC); + if (new_fd < 0) + goto err_close_new_fd; + + err = zclose(map->fd); + if (err) + goto err_close_new_fd; + free(map->name); + + map->fd = new_fd; + map->name = new_name; + map->def.type = info.type; + map->def.key_size = info.key_size; + map->def.value_size = info.value_size; + map->def.max_entries = info.max_entries; + map->def.map_flags = info.map_flags; + map->btf_key_type_id = info.btf_key_type_id; + map->btf_value_type_id = info.btf_value_type_id; + + return 0; + +err_close_new_fd: + close(new_fd); +err_free_new_name: + free(new_name); + return -errno; +} + static int bpf_object__create_maps(struct bpf_object *obj) { @@ -1047,6 +1094,12 @@ bpf_object__create_maps(struct bpf_object *obj) struct bpf_map_def *def = &map->def; int *pfd = &map->fd; + if (map->fd >= 0) { + pr_debug("skip map create (preset) %s: fd=%d\n", + map->name, map->fd); + continue; + } + create_attr.name = map->name; create_attr.map_ifindex = map->map_ifindex; create_attr.map_type = def->type; diff --git a/tools/lib/bpf/libbpf.h b/tools/lib/bpf/libbpf.h index e911ad32d02e..1f8fc2060460 100644 --- a/tools/lib/bpf/libbpf.h +++ b/tools/lib/bpf/libbpf.h @@ -261,6 +261,7 @@ typedef void (*bpf_map_clear_priv_t)(struct bpf_map *, void *); int bpf_map__set_priv(struct bpf_map *map, void *priv, bpf_map_clear_priv_t clear_priv); void *bpf_map__priv(struct bpf_map *map); +int bpf_map__reuse_fd(struct bpf_map *map, int fd); bool bpf_map__is_offload_neutral(struct bpf_map *map); void bpf_map__set_ifindex(struct bpf_map *map, __u32 ifindex); int bpf_map__pin(struct bpf_map *map, const char *path); -- cgit v1.2.3-58-ga151 From 3ff5a4dc5d890963e669fc99cc62ee07d1da24e8 Mon Sep 17 00:00:00 2001 From: Jakub Kicinski <jakub.kicinski@netronome.com> Date: Tue, 10 Jul 2018 14:43:07 -0700 Subject: tools: bpftool: allow reuse of maps with bpftool prog load Add map parameter to prog load which will allow reuse of existing maps instead of creating new ones. We need feature detection and compat code for reallocarray, since it's not available in many libc versions. Signed-off-by: Jakub Kicinski <jakub.kicinski@netronome.com> Reviewed-by: Quentin Monnet <quentin.monnet@netronome.com> Acked-by: Alexei Starovoitov <ast@kernel.org> Signed-off-by: Daniel Borkmann <daniel@iogearbox.net> --- tools/bpf/bpftool/Documentation/bpftool-prog.rst | 20 ++- tools/bpf/bpftool/bash-completion/bpftool | 67 +++++++++- tools/bpf/bpftool/main.h | 3 + tools/bpf/bpftool/map.c | 4 +- tools/bpf/bpftool/prog.c | 148 +++++++++++++++++++++-- 5 files changed, 219 insertions(+), 23 deletions(-) (limited to 'tools') diff --git a/tools/bpf/bpftool/Documentation/bpftool-prog.rst b/tools/bpf/bpftool/Documentation/bpftool-prog.rst index e53e1ad2caf0..64156a16d530 100644 --- a/tools/bpf/bpftool/Documentation/bpftool-prog.rst +++ b/tools/bpf/bpftool/Documentation/bpftool-prog.rst @@ -24,9 +24,10 @@ MAP COMMANDS | **bpftool** **prog dump xlated** *PROG* [{**file** *FILE* | **opcodes** | **visual**}] | **bpftool** **prog dump jited** *PROG* [{**file** *FILE* | **opcodes**}] | **bpftool** **prog pin** *PROG* *FILE* -| **bpftool** **prog load** *OBJ* *FILE* [**type** *TYPE*] [**dev** *NAME*] +| **bpftool** **prog load** *OBJ* *FILE* [**type** *TYPE*] [**map** {**idx** *IDX* | **name** *NAME*} *MAP*] [**dev** *NAME*] | **bpftool** **prog help** | +| *MAP* := { **id** *MAP_ID* | **pinned** *FILE* } | *PROG* := { **id** *PROG_ID* | **pinned** *FILE* | **tag** *PROG_TAG* } | *TYPE* := { | **socket** | **kprobe** | **kretprobe** | **classifier** | **action** | @@ -73,10 +74,17 @@ DESCRIPTION Note: *FILE* must be located in *bpffs* mount. - **bpftool prog load** *OBJ* *FILE* [**type** *TYPE*] [**dev** *NAME*] + **bpftool prog load** *OBJ* *FILE* [**type** *TYPE*] [**map** {**idx** *IDX* | **name** *NAME*} *MAP*] [**dev** *NAME*] Load bpf program from binary *OBJ* and pin as *FILE*. **type** is optional, if not specified program type will be inferred from section names. + By default bpftool will create new maps as declared in the ELF + object being loaded. **map** parameter allows for the reuse + of existing maps. It can be specified multiple times, each + time for a different map. *IDX* refers to index of the map + to be replaced in the ELF file counting from 0, while *NAME* + allows to replace a map by name. *MAP* specifies the map to + use, referring to it by **id** or through a **pinned** file. If **dev** *NAME* is specified program will be loaded onto given networking device (offload). @@ -172,6 +180,14 @@ EXAMPLES mov %rbx,0x0(%rbp) 48 89 5d 00 +| +| **# bpftool prog load xdp1_kern.o /sys/fs/bpf/xdp1 type xdp map name rxcnt id 7** +| **# bpftool prog show pinned /sys/fs/bpf/xdp1** +| 9: xdp name xdp_prog1 tag 539ec6ce11b52f98 gpl +| loaded_at 2018-06-25T16:17:31-0700 uid 0 +| xlated 488B jited 336B memlock 4096B map_ids 7 +| **# rm /sys/fs/bpf/xdp1** +| SEE ALSO ======== diff --git a/tools/bpf/bpftool/bash-completion/bpftool b/tools/bpf/bpftool/bash-completion/bpftool index caf8711993be..598066c40191 100644 --- a/tools/bpf/bpftool/bash-completion/bpftool +++ b/tools/bpf/bpftool/bash-completion/bpftool @@ -99,6 +99,29 @@ _bpftool_get_prog_tags() command sed -n 's/.*"tag": "\(.*\)",$/\1/p' )" -- "$cur" ) ) } +_bpftool_get_obj_map_names() +{ + local obj + + obj=$1 + + maps=$(objdump -j maps -t $obj 2>/dev/null | \ + command awk '/g . maps/ {print $NF}') + + COMPREPLY+=( $( compgen -W "$maps" -- "$cur" ) ) +} + +_bpftool_get_obj_map_idxs() +{ + local obj + + obj=$1 + + nmaps=$(objdump -j maps -t $obj 2>/dev/null | grep -c 'g . maps') + + COMPREPLY+=( $( compgen -W "$(seq 0 $((nmaps - 1)))" -- "$cur" ) ) +} + _sysfs_get_netdevs() { COMPREPLY+=( $( compgen -W "$( ls /sys/class/net 2>/dev/null )" -- \ @@ -220,12 +243,14 @@ _bpftool() # Completion depends on object and command in use case $object in prog) - case $prev in - id) - _bpftool_get_prog_ids - return 0 - ;; - esac + if [[ $command != "load" ]]; then + case $prev in + id) + _bpftool_get_prog_ids + return 0 + ;; + esac + fi local PROG_TYPE='id pinned tag' case $command in @@ -268,22 +293,52 @@ _bpftool() return 0 ;; load) + local obj + if [[ ${#words[@]} -lt 6 ]]; then _filedir return 0 fi + obj=${words[3]} + + if [[ ${words[-4]} == "map" ]]; then + COMPREPLY=( $( compgen -W "id pinned" -- "$cur" ) ) + return 0 + fi + if [[ ${words[-3]} == "map" ]]; then + if [[ ${words[-2]} == "idx" ]]; then + _bpftool_get_obj_map_idxs $obj + elif [[ ${words[-2]} == "name" ]]; then + _bpftool_get_obj_map_names $obj + fi + return 0 + fi + if [[ ${words[-2]} == "map" ]]; then + COMPREPLY=( $( compgen -W "idx name" -- "$cur" ) ) + return 0 + fi + case $prev in type) COMPREPLY=( $( compgen -W "socket kprobe kretprobe classifier action tracepoint raw_tracepoint xdp perf_event cgroup/skb cgroup/sock cgroup/dev lwt_in lwt_out lwt_xmit lwt_seg6local sockops sk_skb sk_msg lirc_mode2 cgroup/bind4 cgroup/bind6 cgroup/connect4 cgroup/connect6 cgroup/sendmsg4 cgroup/sendmsg6 cgroup/post_bind4 cgroup/post_bind6" -- \ "$cur" ) ) return 0 ;; + id) + _bpftool_get_map_ids + return 0 + ;; + pinned) + _filedir + return 0 + ;; dev) _sysfs_get_netdevs return 0 ;; *) + COMPREPLY=( $( compgen -W "map" -- "$cur" ) ) _bpftool_once_attr 'type' _bpftool_once_attr 'dev' return 0 diff --git a/tools/bpf/bpftool/main.h b/tools/bpf/bpftool/main.h index 1e02e4031693..41004bb2644a 100644 --- a/tools/bpf/bpftool/main.h +++ b/tools/bpf/bpftool/main.h @@ -75,6 +75,8 @@ "PROG := { id PROG_ID | pinned FILE | tag PROG_TAG }" #define HELP_SPEC_OPTIONS \ "OPTIONS := { {-j|--json} [{-p|--pretty}] | {-f|--bpffs} }" +#define HELP_SPEC_MAP \ + "MAP := { id MAP_ID | pinned FILE }" enum bpf_obj_type { BPF_OBJ_UNKNOWN, @@ -136,6 +138,7 @@ int do_cgroup(int argc, char **arg); int do_perf(int argc, char **arg); int prog_parse_fd(int *argc, char ***argv); +int map_parse_fd(int *argc, char ***argv); int map_parse_fd_and_info(int *argc, char ***argv, void *info, __u32 *info_len); void disasm_print_insn(unsigned char *image, ssize_t len, int opcodes, diff --git a/tools/bpf/bpftool/map.c b/tools/bpf/bpftool/map.c index 5989e1575ae4..e2baec1122fb 100644 --- a/tools/bpf/bpftool/map.c +++ b/tools/bpf/bpftool/map.c @@ -93,7 +93,7 @@ static void *alloc_value(struct bpf_map_info *info) return malloc(info->value_size); } -static int map_parse_fd(int *argc, char ***argv) +int map_parse_fd(int *argc, char ***argv) { int fd; @@ -824,7 +824,7 @@ static int do_help(int argc, char **argv) " %s %s event_pipe MAP [cpu N index M]\n" " %s %s help\n" "\n" - " MAP := { id MAP_ID | pinned FILE }\n" + " " HELP_SPEC_MAP "\n" " DATA := { [hex] BYTES }\n" " " HELP_SPEC_PROGRAM "\n" " VALUE := { DATA | MAP | PROG }\n" diff --git a/tools/bpf/bpftool/prog.c b/tools/bpf/bpftool/prog.c index 2bdd5ecd1aad..dce960d22106 100644 --- a/tools/bpf/bpftool/prog.c +++ b/tools/bpf/bpftool/prog.c @@ -31,6 +31,7 @@ * SOFTWARE. */ +#define _GNU_SOURCE #include <errno.h> #include <fcntl.h> #include <stdarg.h> @@ -682,18 +683,34 @@ static int do_pin(int argc, char **argv) return err; } +struct map_replace { + int idx; + int fd; + char *name; +}; + +int map_replace_compar(const void *p1, const void *p2) +{ + const struct map_replace *a = p1, *b = p2; + + return a->idx - b->idx; +} + static int do_load(int argc, char **argv) { enum bpf_attach_type expected_attach_type; struct bpf_object_open_attr attr = { .prog_type = BPF_PROG_TYPE_UNSPEC, }; + struct map_replace *map_replace = NULL; + unsigned int old_map_fds = 0; struct bpf_program *prog; struct bpf_object *obj; struct bpf_map *map; const char *pinfile; + unsigned int i, j; __u32 ifindex = 0; - int err; + int idx, err; if (!REQ_ARGS(2)) return -1; @@ -708,16 +725,16 @@ static int do_load(int argc, char **argv) if (attr.prog_type != BPF_PROG_TYPE_UNSPEC) { p_err("program type already specified"); - return -1; + goto err_free_reuse_maps; } if (!REQ_ARGS(1)) - return -1; + goto err_free_reuse_maps; /* Put a '/' at the end of type to appease libbpf */ type = malloc(strlen(*argv) + 2); if (!type) { p_err("mem alloc failed"); - return -1; + goto err_free_reuse_maps; } *type = 0; strcat(type, *argv); @@ -728,37 +745,81 @@ static int do_load(int argc, char **argv) free(type); if (err < 0) { p_err("unknown program type '%s'", *argv); - return err; + goto err_free_reuse_maps; } NEXT_ARG(); + } else if (is_prefix(*argv, "map")) { + char *endptr, *name; + int fd; + + NEXT_ARG(); + + if (!REQ_ARGS(4)) + goto err_free_reuse_maps; + + if (is_prefix(*argv, "idx")) { + NEXT_ARG(); + + idx = strtoul(*argv, &endptr, 0); + if (*endptr) { + p_err("can't parse %s as IDX", *argv); + goto err_free_reuse_maps; + } + name = NULL; + } else if (is_prefix(*argv, "name")) { + NEXT_ARG(); + + name = *argv; + idx = -1; + } else { + p_err("expected 'idx' or 'name', got: '%s'?", + *argv); + goto err_free_reuse_maps; + } + NEXT_ARG(); + + fd = map_parse_fd(&argc, &argv); + if (fd < 0) + goto err_free_reuse_maps; + + map_replace = reallocarray(map_replace, old_map_fds + 1, + sizeof(*map_replace)); + if (!map_replace) { + p_err("mem alloc failed"); + goto err_free_reuse_maps; + } + map_replace[old_map_fds].idx = idx; + map_replace[old_map_fds].name = name; + map_replace[old_map_fds].fd = fd; + old_map_fds++; } else if (is_prefix(*argv, "dev")) { NEXT_ARG(); if (ifindex) { p_err("offload device already specified"); - return -1; + goto err_free_reuse_maps; } if (!REQ_ARGS(1)) - return -1; + goto err_free_reuse_maps; ifindex = if_nametoindex(*argv); if (!ifindex) { p_err("unrecognized netdevice '%s': %s", *argv, strerror(errno)); - return -1; + goto err_free_reuse_maps; } NEXT_ARG(); } else { - p_err("expected no more arguments, 'type' or 'dev', got: '%s'?", + p_err("expected no more arguments, 'type', 'map' or 'dev', got: '%s'?", *argv); - return -1; + goto err_free_reuse_maps; } } obj = bpf_object__open_xattr(&attr); if (IS_ERR_OR_NULL(obj)) { p_err("failed to open object file"); - return -1; + goto err_free_reuse_maps; } prog = bpf_program__next(NULL, obj); @@ -782,10 +843,62 @@ static int do_load(int argc, char **argv) bpf_program__set_type(prog, attr.prog_type); bpf_program__set_expected_attach_type(prog, expected_attach_type); - bpf_map__for_each(map, obj) + qsort(map_replace, old_map_fds, sizeof(*map_replace), + map_replace_compar); + + /* After the sort maps by name will be first on the list, because they + * have idx == -1. Resolve them. + */ + j = 0; + while (j < old_map_fds && map_replace[j].name) { + i = 0; + bpf_map__for_each(map, obj) { + if (!strcmp(bpf_map__name(map), map_replace[j].name)) { + map_replace[j].idx = i; + break; + } + i++; + } + if (map_replace[j].idx == -1) { + p_err("unable to find map '%s'", map_replace[j].name); + goto err_close_obj; + } + j++; + } + /* Resort if any names were resolved */ + if (j) + qsort(map_replace, old_map_fds, sizeof(*map_replace), + map_replace_compar); + + /* Set ifindex and name reuse */ + j = 0; + idx = 0; + bpf_map__for_each(map, obj) { if (!bpf_map__is_offload_neutral(map)) bpf_map__set_ifindex(map, ifindex); + if (j < old_map_fds && idx == map_replace[j].idx) { + err = bpf_map__reuse_fd(map, map_replace[j++].fd); + if (err) { + p_err("unable to set up map reuse: %d", err); + goto err_close_obj; + } + + /* Next reuse wants to apply to the same map */ + if (j < old_map_fds && map_replace[j].idx == idx) { + p_err("replacement for map idx %d specified more than once", + idx); + goto err_close_obj; + } + } + + idx++; + } + if (j < old_map_fds) { + p_err("map idx '%d' not used", map_replace[j].idx); + goto err_close_obj; + } + err = bpf_object__load(obj); if (err) { p_err("failed to load object file"); @@ -799,11 +912,18 @@ static int do_load(int argc, char **argv) jsonw_null(json_wtr); bpf_object__close(obj); + for (i = 0; i < old_map_fds; i++) + close(map_replace[i].fd); + free(map_replace); return 0; err_close_obj: bpf_object__close(obj); +err_free_reuse_maps: + for (i = 0; i < old_map_fds; i++) + close(map_replace[i].fd); + free(map_replace); return -1; } @@ -819,9 +939,11 @@ static int do_help(int argc, char **argv) " %s %s dump xlated PROG [{ file FILE | opcodes | visual }]\n" " %s %s dump jited PROG [{ file FILE | opcodes }]\n" " %s %s pin PROG FILE\n" - " %s %s load OBJ FILE [type TYPE] [dev NAME]\n" + " %s %s load OBJ FILE [type TYPE] [dev NAME] \\\n" + " [map { idx IDX | name NAME } MAP]\n" " %s %s help\n" "\n" + " " HELP_SPEC_MAP "\n" " " HELP_SPEC_PROGRAM "\n" " TYPE := { socket | kprobe | kretprobe | classifier | action |\n" " tracepoint | raw_tracepoint | xdp | perf_event | cgroup/skb |\n" -- cgit v1.2.3-58-ga151 From 42801298386c7c261f376a7f08446b2633fe7bc2 Mon Sep 17 00:00:00 2001 From: Petr Machata <petrm@mellanox.com> Date: Tue, 10 Jul 2018 14:44:26 +0200 Subject: selftests: forwarding: mirror_gre_nh: Unset rp_filter on host VRF The mirrored packets arrive at $h3 encapsulated in GRE/IPv4, with IP address from 192.0.2.128/28 network. However the interface is configured as a member of 192.0.2.160/28 and there's no route directing traffic from the former network through that interface. Correspondingly, the RP filter on the VRF rejects it. Therefore turn off the VRF's RP filter. Signed-off-by: Petr Machata <petrm@mellanox.com> Signed-off-by: David S. Miller <davem@davemloft.net> --- tools/testing/selftests/net/forwarding/mirror_gre_nh.sh | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'tools') diff --git a/tools/testing/selftests/net/forwarding/mirror_gre_nh.sh b/tools/testing/selftests/net/forwarding/mirror_gre_nh.sh index 8fa681eb90e7..6f9ef1820e93 100755 --- a/tools/testing/selftests/net/forwarding/mirror_gre_nh.sh +++ b/tools/testing/selftests/net/forwarding/mirror_gre_nh.sh @@ -35,6 +35,8 @@ setup_prepare() vrf_prepare mirror_gre_topo_create + sysctl_set net.ipv4.conf.v$h3.rp_filter 0 + ip address add dev $swp3 192.0.2.161/28 ip address add dev $h3 192.0.2.162/28 ip address add dev gt4 192.0.2.129/32 @@ -61,6 +63,8 @@ cleanup() ip address del dev $h3 192.0.2.162/28 ip address del dev $swp3 192.0.2.161/28 + sysctl_restore net.ipv4.conf.v$h3.rp_filter 0 + mirror_gre_topo_destroy vrf_cleanup -- cgit v1.2.3-58-ga151 From 9b8ca3795199f333269859b0c8393f810b9616a3 Mon Sep 17 00:00:00 2001 From: Quentin Monnet <quentin.monnet@netronome.com> Date: Thu, 12 Jul 2018 12:52:23 +0100 Subject: tools: bpf: synchronise BPF UAPI header with tools Update with latest changes from include/uapi/linux/bpf.h header. Signed-off-by: Quentin Monnet <quentin.monnet@netronome.com> Reviewed-by: Jakub Kicinski <jakub.kicinski@netronome.com> Signed-off-by: Daniel Borkmann <daniel@iogearbox.net> --- tools/include/uapi/linux/bpf.h | 32 ++++++++++++++++++++++++-------- 1 file changed, 24 insertions(+), 8 deletions(-) (limited to 'tools') diff --git a/tools/include/uapi/linux/bpf.h b/tools/include/uapi/linux/bpf.h index 59b19b6a40d7..6bcb287a888d 100644 --- a/tools/include/uapi/linux/bpf.h +++ b/tools/include/uapi/linux/bpf.h @@ -1826,7 +1826,7 @@ union bpf_attr { * A non-negative value equal to or less than *size* on success, * or a negative error in case of failure. * - * int skb_load_bytes_relative(const struct sk_buff *skb, u32 offset, void *to, u32 len, u32 start_header) + * int bpf_skb_load_bytes_relative(const struct sk_buff *skb, u32 offset, void *to, u32 len, u32 start_header) * Description * This helper is similar to **bpf_skb_load_bytes**\ () in that * it provides an easy way to load *len* bytes from *offset* @@ -1857,7 +1857,8 @@ union bpf_attr { * is resolved), the nexthop address is returned in ipv4_dst * or ipv6_dst based on family, smac is set to mac address of * egress device, dmac is set to nexthop mac address, rt_metric - * is set to metric from route (IPv4/IPv6 only). + * is set to metric from route (IPv4/IPv6 only), and ifindex + * is set to the device index of the nexthop from the FIB lookup. * * *plen* argument is the size of the passed in struct. * *flags* argument can be a combination of one or more of the @@ -1873,9 +1874,10 @@ union bpf_attr { * *ctx* is either **struct xdp_md** for XDP programs or * **struct sk_buff** tc cls_act programs. * Return - * Egress device index on success, 0 if packet needs to continue - * up the stack for further processing or a negative error in case - * of failure. + * * < 0 if any input argument is invalid + * * 0 on success (packet is forwarded, nexthop neighbor exists) + * * > 0 one of **BPF_FIB_LKUP_RET_** codes explaining why the + * packet is not forwarded or needs assist from full stack * * int bpf_sock_hash_update(struct bpf_sock_ops_kern *skops, struct bpf_map *map, void *key, u64 flags) * Description @@ -2031,7 +2033,6 @@ union bpf_attr { * This helper is only available is the kernel was compiled with * the **CONFIG_BPF_LIRC_MODE2** configuration option set to * "**y**". - * * Return * 0 * @@ -2051,7 +2052,6 @@ union bpf_attr { * This helper is only available is the kernel was compiled with * the **CONFIG_BPF_LIRC_MODE2** configuration option set to * "**y**". - * * Return * 0 * @@ -2612,6 +2612,18 @@ struct bpf_raw_tracepoint_args { #define BPF_FIB_LOOKUP_DIRECT BIT(0) #define BPF_FIB_LOOKUP_OUTPUT BIT(1) +enum { + BPF_FIB_LKUP_RET_SUCCESS, /* lookup successful */ + BPF_FIB_LKUP_RET_BLACKHOLE, /* dest is blackholed; can be dropped */ + BPF_FIB_LKUP_RET_UNREACHABLE, /* dest is unreachable; can be dropped */ + BPF_FIB_LKUP_RET_PROHIBIT, /* dest not allowed; can be dropped */ + BPF_FIB_LKUP_RET_NOT_FWDED, /* packet is not forwarded */ + BPF_FIB_LKUP_RET_FWD_DISABLED, /* fwding is not enabled on ingress */ + BPF_FIB_LKUP_RET_UNSUPP_LWT, /* fwd requires encapsulation */ + BPF_FIB_LKUP_RET_NO_NEIGH, /* no neighbor entry for nh */ + BPF_FIB_LKUP_RET_FRAG_NEEDED, /* fragmentation required to fwd */ +}; + struct bpf_fib_lookup { /* input: network family for lookup (AF_INET, AF_INET6) * output: network family of egress nexthop @@ -2625,7 +2637,11 @@ struct bpf_fib_lookup { /* total length of packet from network header - used for MTU check */ __u16 tot_len; - __u32 ifindex; /* L3 device index for lookup */ + + /* input: L3 device index for lookup + * output: device index from FIB lookup + */ + __u32 ifindex; union { /* inputs to lookup */ -- cgit v1.2.3-58-ga151 From 86f7d85cec9e1dba735ac5bd4b751b1908fedf80 Mon Sep 17 00:00:00 2001 From: Quentin Monnet <quentin.monnet@netronome.com> Date: Thu, 12 Jul 2018 12:52:24 +0100 Subject: tools: bpf: build and install man page for eBPF helpers from bpftool/ Provide a new Makefile.helpers in tools/bpf, in order to build and install the man page for eBPF helpers. This Makefile is also included in the one used to build bpftool documentation, so that it can be called either on its own (cd tools/bpf && make -f Makefile.helpers) or from bpftool directory (cd tools/bpf/bpftool && make doc, or cd tools/bpf/bpftool/Documentation && make helpers). Makefile.helpers is not added directly to bpftool to avoid changing its Makefile too much (helpers are not 100% directly related with bpftool). But the possibility to build the page from bpftool directory makes us able to package the helpers man page with bpftool, and to install it along with bpftool documentation, so that the doc for helpers becomes easily available to developers through the "man" program. Cc: linux-man@vger.kernel.org Suggested-by: Daniel Borkmann <daniel@iogearbox.net> Signed-off-by: Quentin Monnet <quentin.monnet@netronome.com> Reviewed-by: Jakub Kicinski <jakub.kicinski@netronome.com> Signed-off-by: Daniel Borkmann <daniel@iogearbox.net> --- tools/bpf/Makefile.helpers | 59 ++++++++++++++++++++++++++++++++ tools/bpf/bpftool/Documentation/Makefile | 13 ++++--- 2 files changed, 67 insertions(+), 5 deletions(-) create mode 100644 tools/bpf/Makefile.helpers (limited to 'tools') diff --git a/tools/bpf/Makefile.helpers b/tools/bpf/Makefile.helpers new file mode 100644 index 000000000000..c34fea77f39f --- /dev/null +++ b/tools/bpf/Makefile.helpers @@ -0,0 +1,59 @@ +ifndef allow-override + include ../scripts/Makefile.include + include ../scripts/utilities.mak +else + # Assume Makefile.helpers is being run from bpftool/Documentation + # subdirectory. Go up two more directories to fetch bpf.h header and + # associated script. + UP2DIR := ../../ +endif + +INSTALL ?= install +RM ?= rm -f +RMDIR ?= rmdir --ignore-fail-on-non-empty + +ifeq ($(V),1) + Q = +else + Q = @ +endif + +prefix ?= /usr/local +mandir ?= $(prefix)/man +man7dir = $(mandir)/man7 + +HELPERS_RST = bpf-helpers.rst +MAN7_RST = $(HELPERS_RST) + +_DOC_MAN7 = $(patsubst %.rst,%.7,$(MAN7_RST)) +DOC_MAN7 = $(addprefix $(OUTPUT),$(_DOC_MAN7)) + +helpers: man7 +man7: $(DOC_MAN7) + +RST2MAN_DEP := $(shell command -v rst2man 2>/dev/null) + +$(OUTPUT)$(HELPERS_RST): $(UP2DIR)../../include/uapi/linux/bpf.h + $(QUIET_GEN)$(UP2DIR)../../scripts/bpf_helpers_doc.py --filename $< > $@ + +$(OUTPUT)%.7: $(OUTPUT)%.rst +ifndef RST2MAN_DEP + $(error "rst2man not found, but required to generate man pages") +endif + $(QUIET_GEN)rst2man $< > $@ + +helpers-clean: + $(call QUIET_CLEAN, eBPF_helpers-manpage) + $(Q)$(RM) $(DOC_MAN7) $(OUTPUT)$(HELPERS_RST) + +helpers-install: helpers + $(call QUIET_INSTALL, eBPF_helpers-manpage) + $(Q)$(INSTALL) -d -m 755 $(DESTDIR)$(man7dir) + $(Q)$(INSTALL) -m 644 $(DOC_MAN7) $(DESTDIR)$(man7dir) + +helpers-uninstall: + $(call QUIET_UNINST, eBPF_helpers-manpage) + $(Q)$(RM) $(addprefix $(DESTDIR)$(man7dir)/,$(_DOC_MAN7)) + $(Q)$(RMDIR) $(DESTDIR)$(man7dir) + +.PHONY: helpers helpers-clean helpers-install helpers-uninstall diff --git a/tools/bpf/bpftool/Documentation/Makefile b/tools/bpf/bpftool/Documentation/Makefile index a9d47c1558bb..f7663a3e60c9 100644 --- a/tools/bpf/bpftool/Documentation/Makefile +++ b/tools/bpf/bpftool/Documentation/Makefile @@ -15,12 +15,15 @@ prefix ?= /usr/local mandir ?= $(prefix)/man man8dir = $(mandir)/man8 -MAN8_RST = $(wildcard *.rst) +# Load targets for building eBPF helpers man page. +include ../../Makefile.helpers + +MAN8_RST = $(filter-out $(HELPERS_RST),$(wildcard *.rst)) _DOC_MAN8 = $(patsubst %.rst,%.8,$(MAN8_RST)) DOC_MAN8 = $(addprefix $(OUTPUT),$(_DOC_MAN8)) -man: man8 +man: man8 helpers man8: $(DOC_MAN8) RST2MAN_DEP := $(shell command -v rst2man 2>/dev/null) @@ -31,16 +34,16 @@ ifndef RST2MAN_DEP endif $(QUIET_GEN)rst2man $< > $@ -clean: +clean: helpers-clean $(call QUIET_CLEAN, Documentation) $(Q)$(RM) $(DOC_MAN8) -install: man +install: man helpers-install $(call QUIET_INSTALL, Documentation-man) $(Q)$(INSTALL) -d -m 755 $(DESTDIR)$(man8dir) $(Q)$(INSTALL) -m 644 $(DOC_MAN8) $(DESTDIR)$(man8dir) -uninstall: +uninstall: helpers-uninstall $(call QUIET_UNINST, Documentation-man) $(Q)$(RM) $(addprefix $(DESTDIR)$(man8dir)/,$(_DOC_MAN8)) $(Q)$(RMDIR) $(DESTDIR)$(man8dir) -- cgit v1.2.3-58-ga151 From cba54f9cf4ec1126dde5576e025691c27f669d85 Mon Sep 17 00:00:00 2001 From: Pieter Jansen van Vuuren <pieter.jansenvanvuuren@netronome.com> Date: Tue, 10 Jul 2018 18:22:31 -0700 Subject: tc-testing: add geneve options in tunnel_key unit tests Extend tc tunnel_key action unit tests with geneve options. Tests include testing single and multiple geneve options, as well as testing geneve options that are expected to fail. Signed-off-by: Pieter Jansen van Vuuren <pieter.jansenvanvuuren@netronome.com> Acked-by: Lucas Bates <lucasb@mojatatu.com> Signed-off-by: David S. Miller <davem@davemloft.net> --- .../tc-testing/tc-tests/actions/tunnel_key.json | 168 +++++++++++++++++++++ 1 file changed, 168 insertions(+) (limited to 'tools') diff --git a/tools/testing/selftests/tc-testing/tc-tests/actions/tunnel_key.json b/tools/testing/selftests/tc-testing/tc-tests/actions/tunnel_key.json index d878ce1c7d08..10b2d894e436 100644 --- a/tools/testing/selftests/tc-testing/tc-tests/actions/tunnel_key.json +++ b/tools/testing/selftests/tc-testing/tc-tests/actions/tunnel_key.json @@ -640,6 +640,174 @@ "$TC actions flush action tunnel_key" ] }, + { + "id": "4f20", + "name": "Add tunnel_key action with a single geneve option parameter", + "category": [ + "actions", + "tunnel_key" + ], + "setup": [ + [ + "$TC actions flush action tunnel_key", + 0, + 1, + 255 + ] + ], + "cmdUnderTest": "$TC actions add action tunnel_key set src_ip 1.1.1.1 dst_ip 2.2.2.2 id 42 dst_port 6081 geneve_opts 0102:80:00880022 index 1", + "expExitCode": "0", + "verifyCmd": "$TC actions get action tunnel_key index 1", + "matchPattern": "action order [0-9]+: tunnel_key.*set.*src_ip 1.1.1.1.*dst_ip 2.2.2.2.*key_id 42.*dst_port 6081.*geneve_opt 0102:80:00880022.*index 1", + "matchCount": "1", + "teardown": [ + "$TC actions flush action tunnel_key" + ] + }, + { + "id": "e33d", + "name": "Add tunnel_key action with multiple geneve options parameter", + "category": [ + "actions", + "tunnel_key" + ], + "setup": [ + [ + "$TC actions flush action tunnel_key", + 0, + 1, + 255 + ] + ], + "cmdUnderTest": "$TC actions add action tunnel_key set src_ip 1.1.1.1 dst_ip 2.2.2.2 id 42 dst_port 6081 geneve_opts 0102:80:00880022,0408:42:0040007611223344,0111:02:1020304011223344 index 1", + "expExitCode": "0", + "verifyCmd": "$TC actions get action tunnel_key index 1", + "matchPattern": "action order [0-9]+: tunnel_key.*set.*src_ip 1.1.1.1.*dst_ip 2.2.2.2.*key_id 42.*dst_port 6081.*geneve_opt 0102:80:00880022,0408:42:0040007611223344,0111:02:1020304011223344.*index 1", + "matchCount": "1", + "teardown": [ + "$TC actions flush action tunnel_key" + ] + }, + { + "id": "0778", + "name": "Add tunnel_key action with invalid class geneve option parameter", + "category": [ + "actions", + "tunnel_key" + ], + "setup": [ + [ + "$TC actions flush action tunnel_key", + 0, + 1, + 255 + ] + ], + "cmdUnderTest": "$TC actions add action tunnel_key set src_ip 1.1.1.1 dst_ip 2.2.2.2 id 42 dst_port 6081 geneve_opts 824212:80:00880022 index 1", + "expExitCode": "255", + "verifyCmd": "$TC actions get action tunnel_key index 1", + "matchPattern": "action order [0-9]+: tunnel_key.*set.*src_ip 1.1.1.1.*dst_ip 2.2.2.2.*key_id 42.*dst_port 6081.*geneve_opt 824212:80:00880022.*index 1", + "matchCount": "0", + "teardown": [ + "$TC actions flush action tunnel_key" + ] + }, + { + "id": "4ae8", + "name": "Add tunnel_key action with invalid type geneve option parameter", + "category": [ + "actions", + "tunnel_key" + ], + "setup": [ + [ + "$TC actions flush action tunnel_key", + 0, + 1, + 255 + ] + ], + "cmdUnderTest": "$TC actions add action tunnel_key set src_ip 1.1.1.1 dst_ip 2.2.2.2 id 42 dst_port 6081 geneve_opts 0102:4224:00880022 index 1", + "expExitCode": "255", + "verifyCmd": "$TC actions get action tunnel_key index 1", + "matchPattern": "action order [0-9]+: tunnel_key.*set.*src_ip 1.1.1.1.*dst_ip 2.2.2.2.*key_id 42.*dst_port 6081.*geneve_opt 0102:4224:00880022.*index 1", + "matchCount": "0", + "teardown": [ + "$TC actions flush action tunnel_key" + ] + }, + { + "id": "4039", + "name": "Add tunnel_key action with short data length geneve option parameter", + "category": [ + "actions", + "tunnel_key" + ], + "setup": [ + [ + "$TC actions flush action tunnel_key", + 0, + 1, + 255 + ] + ], + "cmdUnderTest": "$TC actions add action tunnel_key set src_ip 1.1.1.1 dst_ip 2.2.2.2 id 42 dst_port 6081 geneve_opts 0102:80:4288 index 1", + "expExitCode": "255", + "verifyCmd": "$TC actions get action tunnel_key index 1", + "matchPattern": "action order [0-9]+: tunnel_key.*set.*src_ip 1.1.1.1.*dst_ip 2.2.2.2.*key_id 42.*dst_port 6081.*geneve_opt 0102:80:4288.*index 1", + "matchCount": "0", + "teardown": [ + "$TC actions flush action tunnel_key" + ] + }, + { + "id": "26a6", + "name": "Add tunnel_key action with non-multiple of 4 data length geneve option parameter", + "category": [ + "actions", + "tunnel_key" + ], + "setup": [ + [ + "$TC actions flush action tunnel_key", + 0, + 1, + 255 + ] + ], + "cmdUnderTest": "$TC actions add action tunnel_key set src_ip 1.1.1.1 dst_ip 2.2.2.2 id 42 dst_port 6081 geneve_opts 0102:80:4288428822 index 1", + "expExitCode": "255", + "verifyCmd": "$TC actions get action tunnel_key index 1", + "matchPattern": "action order [0-9]+: tunnel_key.*set.*src_ip 1.1.1.1.*dst_ip 2.2.2.2.*key_id 42.*dst_port 6081.*geneve_opt 0102:80:4288428822.*index 1", + "matchCount": "0", + "teardown": [ + "$TC actions flush action tunnel_key" + ] + }, + { + "id": "f44d", + "name": "Add tunnel_key action with incomplete geneve options parameter", + "category": [ + "actions", + "tunnel_key" + ], + "setup": [ + [ + "$TC actions flush action tunnel_key", + 0, + 1, + 255 + ] + ], + "cmdUnderTest": "$TC actions add action tunnel_key set src_ip 1.1.1.1 dst_ip 2.2.2.2 id 42 dst_port 6081 geneve_opts 0102:80:00880022,0408:42: index 1", + "expExitCode": "255", + "verifyCmd": "$TC actions get action tunnel_key index 1", + "matchPattern": "action order [0-9]+: tunnel_key.*set.*src_ip 1.1.1.1.*dst_ip 2.2.2.2.*key_id 42.*dst_port 6081.*geneve_opt 0102:80:00880022,0408:42:.*index 1", + "matchCount": "0", + "teardown": [ + "$TC actions flush action tunnel_key" + ] + }, { "id": "7afc", "name": "Replace tunnel_key set action with all parameters", -- cgit v1.2.3-58-ga151 From 05296620f6d14dce0030b87e1e57891a770fb65c Mon Sep 17 00:00:00 2001 From: Jakub Kicinski <jakub.kicinski@netronome.com> Date: Wed, 11 Jul 2018 20:36:40 -0700 Subject: xdp: factor out common program/flags handling from drivers Basic operations drivers perform during xdp setup and query can be moved to helpers in the core. Encapsulate program and flags into a structure and add helpers. Note that the structure is intended as the "main" program information source in the driver. Most drivers will additionally place the program pointer in their fast path or ring structures. The helpers don't have a huge impact now, but they will decrease the code duplication when programs can be installed in HW and driver at the same time. Encapsulating the basic operations in helpers will hopefully also reduce the number of changes to drivers which adopt them. Helpers could really be static inline, but they depend on definition of struct netdev_bpf which means they'd have to be placed in netdevice.h, an already 4500 line header. Signed-off-by: Jakub Kicinski <jakub.kicinski@netronome.com> Reviewed-by: Quentin Monnet <quentin.monnet@netronome.com> Signed-off-by: Daniel Borkmann <daniel@iogearbox.net> --- drivers/net/ethernet/netronome/nfp/nfp_net.h | 6 ++-- .../net/ethernet/netronome/nfp/nfp_net_common.c | 28 +++++++----------- drivers/net/netdevsim/bpf.c | 16 +++------- drivers/net/netdevsim/netdevsim.h | 4 +-- include/net/xdp.h | 13 +++++++++ net/core/xdp.c | 34 ++++++++++++++++++++++ tools/testing/selftests/bpf/test_offload.py | 4 +-- 7 files changed, 67 insertions(+), 38 deletions(-) (limited to 'tools') diff --git a/drivers/net/ethernet/netronome/nfp/nfp_net.h b/drivers/net/ethernet/netronome/nfp/nfp_net.h index 2a71a9ffd095..2021dda595b7 100644 --- a/drivers/net/ethernet/netronome/nfp/nfp_net.h +++ b/drivers/net/ethernet/netronome/nfp/nfp_net.h @@ -553,8 +553,7 @@ struct nfp_net_dp { * @rss_cfg: RSS configuration * @rss_key: RSS secret key * @rss_itbl: RSS indirection table - * @xdp_flags: Flags with which XDP prog was loaded - * @xdp_prog: XDP prog (for ctrl path, both DRV and HW modes) + * @xdp: Information about the attached XDP program * @max_r_vecs: Number of allocated interrupt vectors for RX/TX * @max_tx_rings: Maximum number of TX rings supported by the Firmware * @max_rx_rings: Maximum number of RX rings supported by the Firmware @@ -610,8 +609,7 @@ struct nfp_net { u8 rss_key[NFP_NET_CFG_RSS_KEY_SZ]; u8 rss_itbl[NFP_NET_CFG_RSS_ITBL_SZ]; - u32 xdp_flags; - struct bpf_prog *xdp_prog; + struct xdp_attachment_info xdp; unsigned int max_tx_rings; unsigned int max_rx_rings; diff --git a/drivers/net/ethernet/netronome/nfp/nfp_net_common.c b/drivers/net/ethernet/netronome/nfp/nfp_net_common.c index d20714598613..4bb589dbffbc 100644 --- a/drivers/net/ethernet/netronome/nfp/nfp_net_common.c +++ b/drivers/net/ethernet/netronome/nfp/nfp_net_common.c @@ -3417,34 +3417,29 @@ nfp_net_xdp_setup_drv(struct nfp_net *nn, struct bpf_prog *prog, return nfp_net_ring_reconfig(nn, dp, extack); } -static int -nfp_net_xdp_setup(struct nfp_net *nn, struct bpf_prog *prog, u32 flags, - struct netlink_ext_ack *extack) +static int nfp_net_xdp_setup(struct nfp_net *nn, struct netdev_bpf *bpf) { struct bpf_prog *drv_prog, *offload_prog; int err; - if (nn->xdp_prog && (flags ^ nn->xdp_flags) & XDP_FLAGS_MODES) + if (!xdp_attachment_flags_ok(&nn->xdp, bpf)) return -EBUSY; /* Load both when no flags set to allow easy activation of driver path * when program is replaced by one which can't be offloaded. */ - drv_prog = flags & XDP_FLAGS_HW_MODE ? NULL : prog; - offload_prog = flags & XDP_FLAGS_DRV_MODE ? NULL : prog; + drv_prog = bpf->flags & XDP_FLAGS_HW_MODE ? NULL : bpf->prog; + offload_prog = bpf->flags & XDP_FLAGS_DRV_MODE ? NULL : bpf->prog; - err = nfp_net_xdp_setup_drv(nn, drv_prog, extack); + err = nfp_net_xdp_setup_drv(nn, drv_prog, bpf->extack); if (err) return err; - err = nfp_app_xdp_offload(nn->app, nn, offload_prog, extack); - if (err && flags & XDP_FLAGS_HW_MODE) + err = nfp_app_xdp_offload(nn->app, nn, offload_prog, bpf->extack); + if (err && bpf->flags & XDP_FLAGS_HW_MODE) return err; - if (nn->xdp_prog) - bpf_prog_put(nn->xdp_prog); - nn->xdp_prog = prog; - nn->xdp_flags = flags; + xdp_attachment_setup(&nn->xdp, bpf); return 0; } @@ -3456,12 +3451,9 @@ static int nfp_net_xdp(struct net_device *netdev, struct netdev_bpf *xdp) switch (xdp->command) { case XDP_SETUP_PROG: case XDP_SETUP_PROG_HW: - return nfp_net_xdp_setup(nn, xdp->prog, xdp->flags, - xdp->extack); + return nfp_net_xdp_setup(nn, xdp); case XDP_QUERY_PROG: - xdp->prog_id = nn->xdp_prog ? nn->xdp_prog->aux->id : 0; - xdp->prog_flags = nn->xdp_prog ? nn->xdp_flags : 0; - return 0; + return xdp_attachment_query(&nn->xdp, xdp); default: return nfp_app_bpf(nn->app, nn, xdp); } diff --git a/drivers/net/netdevsim/bpf.c b/drivers/net/netdevsim/bpf.c index 712e6f918065..c485d97b5df4 100644 --- a/drivers/net/netdevsim/bpf.c +++ b/drivers/net/netdevsim/bpf.c @@ -199,10 +199,8 @@ static int nsim_xdp_set_prog(struct netdevsim *ns, struct netdev_bpf *bpf) { int err; - if (ns->xdp_prog && (bpf->flags ^ ns->xdp_flags) & XDP_FLAGS_MODES) { - NSIM_EA(bpf->extack, "program loaded with different flags"); + if (!xdp_attachment_flags_ok(&ns->xdp, bpf)) return -EBUSY; - } if (bpf->command == XDP_SETUP_PROG && !ns->bpf_xdpdrv_accept) { NSIM_EA(bpf->extack, "driver XDP disabled in DebugFS"); @@ -219,11 +217,7 @@ static int nsim_xdp_set_prog(struct netdevsim *ns, struct netdev_bpf *bpf) return err; } - if (ns->xdp_prog) - bpf_prog_put(ns->xdp_prog); - - ns->xdp_prog = bpf->prog; - ns->xdp_flags = bpf->flags; + xdp_attachment_setup(&ns->xdp, bpf); if (!bpf->prog) ns->xdp_prog_mode = XDP_ATTACHED_NONE; @@ -567,9 +561,7 @@ int nsim_bpf(struct net_device *dev, struct netdev_bpf *bpf) nsim_bpf_destroy_prog(bpf->offload.prog); return 0; case XDP_QUERY_PROG: - bpf->prog_id = ns->xdp_prog ? ns->xdp_prog->aux->id : 0; - bpf->prog_flags = ns->xdp_prog ? ns->xdp_flags : 0; - return 0; + return xdp_attachment_query(&ns->xdp, bpf); case XDP_SETUP_PROG: err = nsim_setup_prog_checks(ns, bpf); if (err) @@ -636,6 +628,6 @@ void nsim_bpf_uninit(struct netdevsim *ns) { WARN_ON(!list_empty(&ns->bpf_bound_progs)); WARN_ON(!list_empty(&ns->bpf_bound_maps)); - WARN_ON(ns->xdp_prog); + WARN_ON(ns->xdp.prog); WARN_ON(ns->bpf_offloaded); } diff --git a/drivers/net/netdevsim/netdevsim.h b/drivers/net/netdevsim/netdevsim.h index d8a7cc995e88..69ffb4a2d14b 100644 --- a/drivers/net/netdevsim/netdevsim.h +++ b/drivers/net/netdevsim/netdevsim.h @@ -18,6 +18,7 @@ #include <linux/list.h> #include <linux/netdevice.h> #include <linux/u64_stats_sync.h> +#include <net/xdp.h> #define DRV_NAME "netdevsim" @@ -67,9 +68,8 @@ struct netdevsim { struct bpf_prog *bpf_offloaded; u32 bpf_offloaded_id; - u32 xdp_flags; + struct xdp_attachment_info xdp; int xdp_prog_mode; - struct bpf_prog *xdp_prog; u32 prog_id_gen; diff --git a/include/net/xdp.h b/include/net/xdp.h index 2deea7166a34..fcb033f51d8c 100644 --- a/include/net/xdp.h +++ b/include/net/xdp.h @@ -144,4 +144,17 @@ xdp_data_meta_unsupported(const struct xdp_buff *xdp) return unlikely(xdp->data_meta > xdp->data); } +struct xdp_attachment_info { + struct bpf_prog *prog; + u32 flags; +}; + +struct netdev_bpf; +int xdp_attachment_query(struct xdp_attachment_info *info, + struct netdev_bpf *bpf); +bool xdp_attachment_flags_ok(struct xdp_attachment_info *info, + struct netdev_bpf *bpf); +void xdp_attachment_setup(struct xdp_attachment_info *info, + struct netdev_bpf *bpf); + #endif /* __LINUX_NET_XDP_H__ */ diff --git a/net/core/xdp.c b/net/core/xdp.c index 31c58719b5a9..57285383ed00 100644 --- a/net/core/xdp.c +++ b/net/core/xdp.c @@ -3,8 +3,11 @@ * Copyright (c) 2017 Jesper Dangaard Brouer, Red Hat Inc. * Released under terms in GPL version 2. See COPYING. */ +#include <linux/bpf.h> +#include <linux/filter.h> #include <linux/types.h> #include <linux/mm.h> +#include <linux/netdevice.h> #include <linux/slab.h> #include <linux/idr.h> #include <linux/rhashtable.h> @@ -370,3 +373,34 @@ void xdp_return_buff(struct xdp_buff *xdp) __xdp_return(xdp->data, &xdp->rxq->mem, true, xdp->handle); } EXPORT_SYMBOL_GPL(xdp_return_buff); + +int xdp_attachment_query(struct xdp_attachment_info *info, + struct netdev_bpf *bpf) +{ + bpf->prog_id = info->prog ? info->prog->aux->id : 0; + bpf->prog_flags = info->prog ? info->flags : 0; + return 0; +} +EXPORT_SYMBOL_GPL(xdp_attachment_query); + +bool xdp_attachment_flags_ok(struct xdp_attachment_info *info, + struct netdev_bpf *bpf) +{ + if (info->prog && (bpf->flags ^ info->flags) & XDP_FLAGS_MODES) { + NL_SET_ERR_MSG(bpf->extack, + "program loaded with different flags"); + return false; + } + return true; +} +EXPORT_SYMBOL_GPL(xdp_attachment_flags_ok); + +void xdp_attachment_setup(struct xdp_attachment_info *info, + struct netdev_bpf *bpf) +{ + if (info->prog) + bpf_prog_put(info->prog); + info->prog = bpf->prog; + info->flags = bpf->flags; +} +EXPORT_SYMBOL_GPL(xdp_attachment_setup); diff --git a/tools/testing/selftests/bpf/test_offload.py b/tools/testing/selftests/bpf/test_offload.py index f8d9bd81d9a4..40401e9e9351 100755 --- a/tools/testing/selftests/bpf/test_offload.py +++ b/tools/testing/selftests/bpf/test_offload.py @@ -821,7 +821,7 @@ try: ret, _, err = sim.set_xdp(obj, "", force=True, fail=False, include_stderr=True) fail(ret == 0, "Replaced XDP program with a program in different mode") - check_extack_nsim(err, "program loaded with different flags.", args) + check_extack(err, "program loaded with different flags.", args) start_test("Test XDP prog remove with bad flags...") ret, _, err = sim.unset_xdp("offload", force=True, @@ -831,7 +831,7 @@ try: ret, _, err = sim.unset_xdp("", force=True, fail=False, include_stderr=True) fail(ret == 0, "Removed program with a bad mode") - check_extack_nsim(err, "program loaded with different flags.", args) + check_extack(err, "program loaded with different flags.", args) start_test("Test MTU restrictions...") ret, _ = sim.set_mtu(9000, fail=False) -- cgit v1.2.3-58-ga151 From 799e173d7125155c00e9492c8212c5e41333049f Mon Sep 17 00:00:00 2001 From: Jakub Kicinski <jakub.kicinski@netronome.com> Date: Wed, 11 Jul 2018 20:36:42 -0700 Subject: netdevsim: add support for simultaneous driver and hw XDP Allow netdevsim to accept driver and offload attachment of XDP BPF programs at the same time. Signed-off-by: Jakub Kicinski <jakub.kicinski@netronome.com> Reviewed-by: Quentin Monnet <quentin.monnet@netronome.com> Signed-off-by: Daniel Borkmann <daniel@iogearbox.net> --- drivers/net/netdevsim/bpf.c | 32 +++++++++-------------------- drivers/net/netdevsim/netdev.c | 3 +-- drivers/net/netdevsim/netdevsim.h | 2 +- tools/testing/selftests/bpf/test_offload.py | 8 -------- 4 files changed, 12 insertions(+), 33 deletions(-) (limited to 'tools') diff --git a/drivers/net/netdevsim/bpf.c b/drivers/net/netdevsim/bpf.c index 5544c9b51173..c36d2a768202 100644 --- a/drivers/net/netdevsim/bpf.c +++ b/drivers/net/netdevsim/bpf.c @@ -92,7 +92,7 @@ static const struct bpf_prog_offload_ops nsim_bpf_analyzer_ops = { static bool nsim_xdp_offload_active(struct netdevsim *ns) { - return ns->xdp_prog_mode == XDP_ATTACHED_HW; + return ns->xdp_hw.prog; } static void nsim_prog_set_loaded(struct bpf_prog *prog, bool loaded) @@ -195,11 +195,13 @@ static int nsim_xdp_offload_prog(struct netdevsim *ns, struct netdev_bpf *bpf) return nsim_bpf_offload(ns, bpf->prog, nsim_xdp_offload_active(ns)); } -static int nsim_xdp_set_prog(struct netdevsim *ns, struct netdev_bpf *bpf) +static int +nsim_xdp_set_prog(struct netdevsim *ns, struct netdev_bpf *bpf, + struct xdp_attachment_info *xdp) { int err; - if (!xdp_attachment_flags_ok(&ns->xdp, bpf)) + if (!xdp_attachment_flags_ok(xdp, bpf)) return -EBUSY; if (bpf->command == XDP_SETUP_PROG && !ns->bpf_xdpdrv_accept) { @@ -217,14 +219,7 @@ static int nsim_xdp_set_prog(struct netdevsim *ns, struct netdev_bpf *bpf) return err; } - xdp_attachment_setup(&ns->xdp, bpf); - - if (!bpf->prog) - ns->xdp_prog_mode = XDP_ATTACHED_NONE; - else if (bpf->command == XDP_SETUP_PROG) - ns->xdp_prog_mode = XDP_ATTACHED_DRV; - else - ns->xdp_prog_mode = XDP_ATTACHED_HW; + xdp_attachment_setup(xdp, bpf); return 0; } @@ -284,10 +279,6 @@ static int nsim_setup_prog_checks(struct netdevsim *ns, struct netdev_bpf *bpf) NSIM_EA(bpf->extack, "MTU too large w/ XDP enabled"); return -EINVAL; } - if (nsim_xdp_offload_active(ns)) { - NSIM_EA(bpf->extack, "xdp offload active, can't load drv prog"); - return -EBUSY; - } return 0; } @@ -561,25 +552,21 @@ int nsim_bpf(struct net_device *dev, struct netdev_bpf *bpf) nsim_bpf_destroy_prog(bpf->offload.prog); return 0; case XDP_QUERY_PROG: - if (ns->xdp_prog_mode != XDP_ATTACHED_DRV) - return 0; return xdp_attachment_query(&ns->xdp, bpf); case XDP_QUERY_PROG_HW: - if (ns->xdp_prog_mode != XDP_ATTACHED_HW) - return 0; - return xdp_attachment_query(&ns->xdp, bpf); + return xdp_attachment_query(&ns->xdp_hw, bpf); case XDP_SETUP_PROG: err = nsim_setup_prog_checks(ns, bpf); if (err) return err; - return nsim_xdp_set_prog(ns, bpf); + return nsim_xdp_set_prog(ns, bpf, &ns->xdp); case XDP_SETUP_PROG_HW: err = nsim_setup_prog_hw_checks(ns, bpf); if (err) return err; - return nsim_xdp_set_prog(ns, bpf); + return nsim_xdp_set_prog(ns, bpf, &ns->xdp_hw); case BPF_OFFLOAD_MAP_ALLOC: if (!ns->bpf_map_accept) return -EOPNOTSUPP; @@ -635,5 +622,6 @@ void nsim_bpf_uninit(struct netdevsim *ns) WARN_ON(!list_empty(&ns->bpf_bound_progs)); WARN_ON(!list_empty(&ns->bpf_bound_maps)); WARN_ON(ns->xdp.prog); + WARN_ON(ns->xdp_hw.prog); WARN_ON(ns->bpf_offloaded); } diff --git a/drivers/net/netdevsim/netdev.c b/drivers/net/netdevsim/netdev.c index b2f9d0df93b0..a7b179f0d954 100644 --- a/drivers/net/netdevsim/netdev.c +++ b/drivers/net/netdevsim/netdev.c @@ -228,8 +228,7 @@ static int nsim_change_mtu(struct net_device *dev, int new_mtu) { struct netdevsim *ns = netdev_priv(dev); - if (ns->xdp_prog_mode == XDP_ATTACHED_DRV && - new_mtu > NSIM_XDP_MAX_MTU) + if (ns->xdp.prog && new_mtu > NSIM_XDP_MAX_MTU) return -EBUSY; dev->mtu = new_mtu; diff --git a/drivers/net/netdevsim/netdevsim.h b/drivers/net/netdevsim/netdevsim.h index 69ffb4a2d14b..0aeabbe81cc6 100644 --- a/drivers/net/netdevsim/netdevsim.h +++ b/drivers/net/netdevsim/netdevsim.h @@ -69,7 +69,7 @@ struct netdevsim { u32 bpf_offloaded_id; struct xdp_attachment_info xdp; - int xdp_prog_mode; + struct xdp_attachment_info xdp_hw; u32 prog_id_gen; diff --git a/tools/testing/selftests/bpf/test_offload.py b/tools/testing/selftests/bpf/test_offload.py index 40401e9e9351..4f982a0255c2 100755 --- a/tools/testing/selftests/bpf/test_offload.py +++ b/tools/testing/selftests/bpf/test_offload.py @@ -814,20 +814,12 @@ try: "Device parameters reported for non-offloaded program") start_test("Test XDP prog replace with bad flags...") - ret, _, err = sim.set_xdp(obj, "offload", force=True, - fail=False, include_stderr=True) - fail(ret == 0, "Replaced XDP program with a program in different mode") - check_extack_nsim(err, "program loaded with different flags.", args) ret, _, err = sim.set_xdp(obj, "", force=True, fail=False, include_stderr=True) fail(ret == 0, "Replaced XDP program with a program in different mode") check_extack(err, "program loaded with different flags.", args) start_test("Test XDP prog remove with bad flags...") - ret, _, err = sim.unset_xdp("offload", force=True, - fail=False, include_stderr=True) - fail(ret == 0, "Removed program with a bad mode mode") - check_extack_nsim(err, "program loaded with different flags.", args) ret, _, err = sim.unset_xdp("", force=True, fail=False, include_stderr=True) fail(ret == 0, "Removed program with a bad mode") -- cgit v1.2.3-58-ga151 From 99dadb6e3ec130800d199ec42fff03242b1be1eb Mon Sep 17 00:00:00 2001 From: Jakub Kicinski <jakub.kicinski@netronome.com> Date: Wed, 11 Jul 2018 20:36:43 -0700 Subject: selftests/bpf: add test for multiple programs Add tests for having an XDP program attached in the driver and another one attached in HW simultaneously. Signed-off-by: Jakub Kicinski <jakub.kicinski@netronome.com> Reviewed-by: Quentin Monnet <quentin.monnet@netronome.com> Signed-off-by: Daniel Borkmann <daniel@iogearbox.net> --- tools/testing/selftests/bpf/test_offload.py | 63 +++++++++++++++++++++++++++++ 1 file changed, 63 insertions(+) (limited to 'tools') diff --git a/tools/testing/selftests/bpf/test_offload.py b/tools/testing/selftests/bpf/test_offload.py index 4f982a0255c2..b746227eaff2 100755 --- a/tools/testing/selftests/bpf/test_offload.py +++ b/tools/testing/selftests/bpf/test_offload.py @@ -339,6 +339,11 @@ class NetdevSim: self.dfs = DebugfsDir(self.dfs_dir) return self.dfs + def dfs_read(self, f): + path = os.path.join(self.dfs_dir, f) + _, data = cmd('cat %s' % (path)) + return data.strip() + def dfs_num_bound_progs(self): path = os.path.join(self.dfs_dir, "bpf_bound_progs") _, progs = cmd('ls %s' % (path)) @@ -814,6 +819,10 @@ try: "Device parameters reported for non-offloaded program") start_test("Test XDP prog replace with bad flags...") + ret, _, err = sim.set_xdp(obj, "generic", force=True, + fail=False, include_stderr=True) + fail(ret == 0, "Replaced XDP program with a program in different mode") + fail(err.count("File exists") != 1, "Replaced driver XDP with generic") ret, _, err = sim.set_xdp(obj, "", force=True, fail=False, include_stderr=True) fail(ret == 0, "Replaced XDP program with a program in different mode") @@ -883,6 +892,60 @@ try: rm(pin_file) bpftool_prog_list_wait(expected=0) + start_test("Test multi-attachment XDP - attach...") + sim.set_xdp(obj, "offload") + xdp = sim.ip_link_show(xdp=True)["xdp"] + offloaded = sim.dfs_read("bpf_offloaded_id") + fail("prog" not in xdp, "Base program not reported in single program mode") + fail(len(ipl["xdp"]["attached"]) != 1, + "Wrong attached program count with one program") + + sim.set_xdp(obj, "") + two_xdps = sim.ip_link_show(xdp=True)["xdp"] + offloaded2 = sim.dfs_read("bpf_offloaded_id") + + fail(two_xdps["mode"] != 4, "Bad mode reported with multiple programs") + fail("prog" in two_xdps, "Base program reported in multi program mode") + fail(xdp["attached"][0] not in two_xdps["attached"], + "Offload program not reported after driver activated") + fail(len(two_xdps["attached"]) != 2, + "Wrong attached program count with two programs") + fail(two_xdps["attached"][0]["prog"]["id"] == + two_xdps["attached"][1]["prog"]["id"], + "offloaded and drv programs have the same id") + fail(offloaded != offloaded2, + "offload ID changed after loading driver program") + + start_test("Test multi-attachment XDP - replace...") + ret, _, err = sim.set_xdp(obj, "offload", fail=False, include_stderr=True) + fail(err.count("busy") != 1, "Replaced one of programs without -force") + + start_test("Test multi-attachment XDP - detach...") + ret, _, err = sim.unset_xdp("drv", force=True, + fail=False, include_stderr=True) + fail(ret == 0, "Removed program with a bad mode") + check_extack(err, "program loaded with different flags.", args) + + sim.unset_xdp("offload") + xdp = sim.ip_link_show(xdp=True)["xdp"] + offloaded = sim.dfs_read("bpf_offloaded_id") + + fail(xdp["mode"] != 1, "Bad mode reported after multiple programs") + fail("prog" not in xdp, + "Base program not reported after multi program mode") + fail(xdp["attached"][0] not in two_xdps["attached"], + "Offload program not reported after driver activated") + fail(len(ipl["xdp"]["attached"]) != 1, + "Wrong attached program count with remaining programs") + fail(offloaded != "0", "offload ID reported with only driver program left") + + start_test("Test multi-attachment XDP - device remove...") + sim.set_xdp(obj, "offload") + sim.remove() + + sim = NetdevSim() + sim.set_ethtool_tc_offloads(True) + start_test("Test mixing of TC and XDP...") sim.tc_add_ingress() sim.set_xdp(obj, "offload") -- cgit v1.2.3-58-ga151 From db42a21a1e8d11a30a06050281c2723ec78b63a7 Mon Sep 17 00:00:00 2001 From: Jakub Kicinski <jakub.kicinski@netronome.com> Date: Fri, 13 Jul 2018 19:08:59 -0700 Subject: tools: include reallocarray feature test in FEATURE_TESTS_BASIC perf propagates its feature check results to libbpf. This means features for which perf probes must be a superset of libbpf's required features. perf depends on FEATURE_TESTS_BASIC for its list of features. commit 531b014e7a2f ("tools: bpf: make use of reallocarray") added reallocarray use to libbpf, make perf also perform the reallocarray feature check. Fixes: 531b014e7a2f ("tools: bpf: make use of reallocarray") Reported-by: Guenter Roeck <linux@roeck-us.net> Signed-off-by: Jakub Kicinski <jakub.kicinski@netronome.com> Tested-by: Guenter Roeck <linux@roeck-us.net> Signed-off-by: Daniel Borkmann <daniel@iogearbox.net> --- tools/build/Makefile.feature | 1 + 1 file changed, 1 insertion(+) (limited to 'tools') diff --git a/tools/build/Makefile.feature b/tools/build/Makefile.feature index 5b6dda3b1ca8..f216b2f5c3d7 100644 --- a/tools/build/Makefile.feature +++ b/tools/build/Makefile.feature @@ -57,6 +57,7 @@ FEATURE_TESTS_BASIC := \ libunwind-aarch64 \ pthread-attr-setaffinity-np \ pthread-barrier \ + reallocarray \ stackprotector-all \ timerfd \ libdw-dwarf-unwind \ -- cgit v1.2.3-58-ga151 From 92b57121ca79b286bef4f304e887272f3f2d86bb Mon Sep 17 00:00:00 2001 From: Okash Khawaja <osk@fb.com> Date: Fri, 13 Jul 2018 21:57:02 -0700 Subject: bpf: btf: export btf types and name by offset from lib This patch introduces btf__resolve_type() function and exports two existing functions from libbpf. btf__resolve_type follows modifier types like const and typedef until it hits a type which actually takes up memory, and then returns it. This function follows similar pattern to btf__resolve_size but instead of computing size, it just returns the type. These functions will be used in the followig patch which parses information inside array of `struct btf_type *`. btf_name_by_offset is used for printing variable names. Signed-off-by: Okash Khawaja <osk@fb.com> Acked-by: Martin KaFai Lau <kafai@fb.com> Acked-by: Song Liu <songliubraving@fb.com> Reviewed-by: Jakub Kicinski <jakub.kicinski@netronome.com> Signed-off-by: Daniel Borkmann <daniel@iogearbox.net> --- tools/lib/bpf/btf.c | 65 ++++++++++++++++++++++++++++++++++++----------------- tools/lib/bpf/btf.h | 3 +++ 2 files changed, 48 insertions(+), 20 deletions(-) (limited to 'tools') diff --git a/tools/lib/bpf/btf.c b/tools/lib/bpf/btf.c index 8c54a4b6f187..03161be094b4 100644 --- a/tools/lib/bpf/btf.c +++ b/tools/lib/bpf/btf.c @@ -17,6 +17,11 @@ #define BTF_MAX_NR_TYPES 65535 +#define IS_MODIFIER(k) (((k) == BTF_KIND_TYPEDEF) || \ + ((k) == BTF_KIND_VOLATILE) || \ + ((k) == BTF_KIND_CONST) || \ + ((k) == BTF_KIND_RESTRICT)) + static struct btf_type btf_void; struct btf { @@ -33,14 +38,6 @@ struct btf { int fd; }; -static const char *btf_name_by_offset(const struct btf *btf, uint32_t offset) -{ - if (offset < btf->hdr->str_len) - return &btf->strings[offset]; - else - return NULL; -} - static int btf_add_type(struct btf *btf, struct btf_type *t) { if (btf->types_size - btf->nr_types < 2) { @@ -190,15 +187,6 @@ static int btf_parse_type_sec(struct btf *btf, btf_print_fn_t err_log) return 0; } -static const struct btf_type *btf_type_by_id(const struct btf *btf, - uint32_t type_id) -{ - if (type_id > btf->nr_types) - return NULL; - - return btf->types[type_id]; -} - static bool btf_type_is_void(const struct btf_type *t) { return t == &btf_void || BTF_INFO_KIND(t->info) == BTF_KIND_FWD; @@ -234,7 +222,7 @@ int64_t btf__resolve_size(const struct btf *btf, uint32_t type_id) int64_t size = -1; int i; - t = btf_type_by_id(btf, type_id); + t = btf__type_by_id(btf, type_id); for (i = 0; i < MAX_RESOLVE_DEPTH && !btf_type_is_void_or_null(t); i++) { size = btf_type_size(t); @@ -259,7 +247,7 @@ int64_t btf__resolve_size(const struct btf *btf, uint32_t type_id) return -EINVAL; } - t = btf_type_by_id(btf, type_id); + t = btf__type_by_id(btf, type_id); } if (size < 0) @@ -271,6 +259,26 @@ int64_t btf__resolve_size(const struct btf *btf, uint32_t type_id) return nelems * size; } +int btf__resolve_type(const struct btf *btf, __u32 type_id) +{ + const struct btf_type *t; + int depth = 0; + + t = btf__type_by_id(btf, type_id); + while (depth < MAX_RESOLVE_DEPTH && + !btf_type_is_void_or_null(t) && + IS_MODIFIER(BTF_INFO_KIND(t->info))) { + type_id = t->type; + t = btf__type_by_id(btf, type_id); + depth++; + } + + if (depth == MAX_RESOLVE_DEPTH || btf_type_is_void_or_null(t)) + return -EINVAL; + + return type_id; +} + int32_t btf__find_by_name(const struct btf *btf, const char *type_name) { uint32_t i; @@ -280,7 +288,7 @@ int32_t btf__find_by_name(const struct btf *btf, const char *type_name) for (i = 1; i <= btf->nr_types; i++) { const struct btf_type *t = btf->types[i]; - const char *name = btf_name_by_offset(btf, t->name_off); + const char *name = btf__name_by_offset(btf, t->name_off); if (name && !strcmp(type_name, name)) return i; @@ -371,3 +379,20 @@ int btf__fd(const struct btf *btf) { return btf->fd; } + +const char *btf__name_by_offset(const struct btf *btf, __u32 offset) +{ + if (offset < btf->hdr->str_len) + return &btf->strings[offset]; + else + return NULL; +} + +const struct btf_type *btf__type_by_id(const struct btf *btf, + __u32 type_id) +{ + if (type_id > btf->nr_types) + return NULL; + + return btf->types[type_id]; +} diff --git a/tools/lib/bpf/btf.h b/tools/lib/bpf/btf.h index 74bb344035bb..24f361d99a5e 100644 --- a/tools/lib/bpf/btf.h +++ b/tools/lib/bpf/btf.h @@ -17,6 +17,9 @@ void btf__free(struct btf *btf); struct btf *btf__new(uint8_t *data, uint32_t size, btf_print_fn_t err_log); int32_t btf__find_by_name(const struct btf *btf, const char *type_name); int64_t btf__resolve_size(const struct btf *btf, uint32_t type_id); +int btf__resolve_type(const struct btf *btf, __u32 type_id); int btf__fd(const struct btf *btf); +const char *btf__name_by_offset(const struct btf *btf, __u32 offset); +const struct btf_type *btf__type_by_id(const struct btf *btf, __u32 type_id); #endif -- cgit v1.2.3-58-ga151 From b12d6ec09730b2646e85c348e599b592348dd0e3 Mon Sep 17 00:00:00 2001 From: Okash Khawaja <osk@fb.com> Date: Fri, 13 Jul 2018 21:57:03 -0700 Subject: bpf: btf: add btf print functionality This consumes functionality exported in the previous patch. It does the main job of printing with BTF data. This is used in the following patch to provide a more readable output of a map's dump. It relies on json_writer to do json printing. Below is sample output where map keys are ints and values are of type struct A: typedef int int_type; enum E { E0, E1, }; struct B { int x; int y; }; struct A { int m; unsigned long long n; char o; int p[8]; int q[4][8]; enum E r; void *s; struct B t; const int u; int_type v; unsigned int w1: 3; unsigned int w2: 3; }; $ sudo bpftool map dump id 14 [{ "key": 0, "value": { "m": 1, "n": 2, "o": "c", "p": [15,16,17,18,15,16,17,18 ], "q": [[25,26,27,28,25,26,27,28 ],[35,36,37,38,35,36,37,38 ],[45,46,47,48,45,46,47,48 ],[55,56,57,58,55,56,57,58 ] ], "r": 1, "s": 0x7ffd80531cf8, "t": { "x": 5, "y": 10 }, "u": 100, "v": 20, "w1": 0x7, "w2": 0x3 } } ] This patch uses json's {} and [] to imply struct/union and array. More explicit information can be added later. For example, a command line option can be introduced to print whether a key or value is struct or union, name of a struct etc. This will however come at the expense of duplicating info when, for example, printing an array of structs. enums are printed as ints without their names. Signed-off-by: Okash Khawaja <osk@fb.com> Acked-by: Martin KaFai Lau <kafai@fb.com> Reviewed-by: Jakub Kicinski <jakub.kicinski@netronome.com> Signed-off-by: Daniel Borkmann <daniel@iogearbox.net> --- tools/bpf/bpftool/btf_dumper.c | 251 +++++++++++++++++++++++++++++++++++++++++ tools/bpf/bpftool/main.h | 15 +++ 2 files changed, 266 insertions(+) create mode 100644 tools/bpf/bpftool/btf_dumper.c (limited to 'tools') diff --git a/tools/bpf/bpftool/btf_dumper.c b/tools/bpf/bpftool/btf_dumper.c new file mode 100644 index 000000000000..55bc512a1831 --- /dev/null +++ b/tools/bpf/bpftool/btf_dumper.c @@ -0,0 +1,251 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Copyright (c) 2018 Facebook */ + +#include <ctype.h> +#include <stdio.h> /* for (FILE *) used by json_writer */ +#include <string.h> +#include <asm/byteorder.h> +#include <linux/bitops.h> +#include <linux/btf.h> +#include <linux/err.h> + +#include "btf.h" +#include "json_writer.h" +#include "main.h" + +#define BITS_PER_BYTE_MASK (BITS_PER_BYTE - 1) +#define BITS_PER_BYTE_MASKED(bits) ((bits) & BITS_PER_BYTE_MASK) +#define BITS_ROUNDDOWN_BYTES(bits) ((bits) >> 3) +#define BITS_ROUNDUP_BYTES(bits) \ + (BITS_ROUNDDOWN_BYTES(bits) + !!BITS_PER_BYTE_MASKED(bits)) + +static int btf_dumper_do_type(const struct btf_dumper *d, __u32 type_id, + __u8 bit_offset, const void *data); + +static void btf_dumper_ptr(const void *data, json_writer_t *jw, + bool is_plain_text) +{ + if (is_plain_text) + jsonw_printf(jw, "%p", *(unsigned long *)data); + else + jsonw_printf(jw, "%u", *(unsigned long *)data); +} + +static int btf_dumper_modifier(const struct btf_dumper *d, __u32 type_id, + const void *data) +{ + int actual_type_id; + + actual_type_id = btf__resolve_type(d->btf, type_id); + if (actual_type_id < 0) + return actual_type_id; + + return btf_dumper_do_type(d, actual_type_id, 0, data); +} + +static void btf_dumper_enum(const void *data, json_writer_t *jw) +{ + jsonw_printf(jw, "%d", *(int *)data); +} + +static int btf_dumper_array(const struct btf_dumper *d, __u32 type_id, + const void *data) +{ + const struct btf_type *t = btf__type_by_id(d->btf, type_id); + struct btf_array *arr = (struct btf_array *)(t + 1); + long long elem_size; + int ret = 0; + __u32 i; + + elem_size = btf__resolve_size(d->btf, arr->type); + if (elem_size < 0) + return elem_size; + + jsonw_start_array(d->jw); + for (i = 0; i < arr->nelems; i++) { + ret = btf_dumper_do_type(d, arr->type, 0, + data + i * elem_size); + if (ret) + break; + } + + jsonw_end_array(d->jw); + return ret; +} + +static void btf_dumper_int_bits(__u32 int_type, __u8 bit_offset, + const void *data, json_writer_t *jw, + bool is_plain_text) +{ + int left_shift_bits, right_shift_bits; + int nr_bits = BTF_INT_BITS(int_type); + int total_bits_offset; + int bytes_to_copy; + int bits_to_copy; + __u64 print_num; + + total_bits_offset = bit_offset + BTF_INT_OFFSET(int_type); + data += BITS_ROUNDDOWN_BYTES(total_bits_offset); + bit_offset = BITS_PER_BYTE_MASKED(total_bits_offset); + bits_to_copy = bit_offset + nr_bits; + bytes_to_copy = BITS_ROUNDUP_BYTES(bits_to_copy); + + print_num = 0; + memcpy(&print_num, data, bytes_to_copy); +#if defined(__BIG_ENDIAN_BITFIELD) + left_shift_bits = bit_offset; +#elif defined(__LITTLE_ENDIAN_BITFIELD) + left_shift_bits = 64 - bits_to_copy; +#else +#error neither big nor little endian +#endif + right_shift_bits = 64 - nr_bits; + + print_num <<= left_shift_bits; + print_num >>= right_shift_bits; + if (is_plain_text) + jsonw_printf(jw, "0x%llx", print_num); + else + jsonw_printf(jw, "%llu", print_num); +} + +static int btf_dumper_int(const struct btf_type *t, __u8 bit_offset, + const void *data, json_writer_t *jw, + bool is_plain_text) +{ + __u32 *int_type; + __u32 nr_bits; + + int_type = (__u32 *)(t + 1); + nr_bits = BTF_INT_BITS(*int_type); + /* if this is bit field */ + if (bit_offset || BTF_INT_OFFSET(*int_type) || + BITS_PER_BYTE_MASKED(nr_bits)) { + btf_dumper_int_bits(*int_type, bit_offset, data, jw, + is_plain_text); + return 0; + } + + switch (BTF_INT_ENCODING(*int_type)) { + case 0: + if (BTF_INT_BITS(*int_type) == 64) + jsonw_printf(jw, "%lu", *(__u64 *)data); + else if (BTF_INT_BITS(*int_type) == 32) + jsonw_printf(jw, "%u", *(__u32 *)data); + else if (BTF_INT_BITS(*int_type) == 16) + jsonw_printf(jw, "%hu", *(__u16 *)data); + else if (BTF_INT_BITS(*int_type) == 8) + jsonw_printf(jw, "%hhu", *(__u8 *)data); + else + btf_dumper_int_bits(*int_type, bit_offset, data, jw, + is_plain_text); + break; + case BTF_INT_SIGNED: + if (BTF_INT_BITS(*int_type) == 64) + jsonw_printf(jw, "%ld", *(long long *)data); + else if (BTF_INT_BITS(*int_type) == 32) + jsonw_printf(jw, "%d", *(int *)data); + else if (BTF_INT_BITS(*int_type) == 16) + jsonw_printf(jw, "%hd", *(short *)data); + else if (BTF_INT_BITS(*int_type) == 8) + jsonw_printf(jw, "%hhd", *(char *)data); + else + btf_dumper_int_bits(*int_type, bit_offset, data, jw, + is_plain_text); + break; + case BTF_INT_CHAR: + if (isprint(*(char *)data)) + jsonw_printf(jw, "\"%c\"", *(char *)data); + else + if (is_plain_text) + jsonw_printf(jw, "0x%hhx", *(char *)data); + else + jsonw_printf(jw, "\"\\u00%02hhx\"", + *(char *)data); + break; + case BTF_INT_BOOL: + jsonw_bool(jw, *(int *)data); + break; + default: + /* shouldn't happen */ + return -EINVAL; + } + + return 0; +} + +static int btf_dumper_struct(const struct btf_dumper *d, __u32 type_id, + const void *data) +{ + const struct btf_type *t; + struct btf_member *m; + const void *data_off; + int ret = 0; + int i, vlen; + + t = btf__type_by_id(d->btf, type_id); + if (!t) + return -EINVAL; + + vlen = BTF_INFO_VLEN(t->info); + jsonw_start_object(d->jw); + m = (struct btf_member *)(t + 1); + + for (i = 0; i < vlen; i++) { + data_off = data + BITS_ROUNDDOWN_BYTES(m[i].offset); + jsonw_name(d->jw, btf__name_by_offset(d->btf, m[i].name_off)); + ret = btf_dumper_do_type(d, m[i].type, + BITS_PER_BYTE_MASKED(m[i].offset), + data_off); + if (ret) + break; + } + + jsonw_end_object(d->jw); + + return ret; +} + +static int btf_dumper_do_type(const struct btf_dumper *d, __u32 type_id, + __u8 bit_offset, const void *data) +{ + const struct btf_type *t = btf__type_by_id(d->btf, type_id); + + switch (BTF_INFO_KIND(t->info)) { + case BTF_KIND_INT: + return btf_dumper_int(t, bit_offset, data, d->jw, + d->is_plain_text); + case BTF_KIND_STRUCT: + case BTF_KIND_UNION: + return btf_dumper_struct(d, type_id, data); + case BTF_KIND_ARRAY: + return btf_dumper_array(d, type_id, data); + case BTF_KIND_ENUM: + btf_dumper_enum(data, d->jw); + return 0; + case BTF_KIND_PTR: + btf_dumper_ptr(data, d->jw, d->is_plain_text); + return 0; + case BTF_KIND_UNKN: + jsonw_printf(d->jw, "(unknown)"); + return 0; + case BTF_KIND_FWD: + /* map key or value can't be forward */ + jsonw_printf(d->jw, "(fwd-kind-invalid)"); + return -EINVAL; + case BTF_KIND_TYPEDEF: + case BTF_KIND_VOLATILE: + case BTF_KIND_CONST: + case BTF_KIND_RESTRICT: + return btf_dumper_modifier(d, type_id, data); + default: + jsonw_printf(d->jw, "(unsupported-kind"); + return -EINVAL; + } +} + +int btf_dumper_type(const struct btf_dumper *d, __u32 type_id, + const void *data) +{ + return btf_dumper_do_type(d, type_id, 0, data); +} diff --git a/tools/bpf/bpftool/main.h b/tools/bpf/bpftool/main.h index 41004bb2644a..238e734d75b3 100644 --- a/tools/bpf/bpftool/main.h +++ b/tools/bpf/bpftool/main.h @@ -150,4 +150,19 @@ unsigned int get_page_size(void); unsigned int get_possible_cpus(void); const char *ifindex_to_bfd_name_ns(__u32 ifindex, __u64 ns_dev, __u64 ns_ino); +struct btf_dumper { + const struct btf *btf; + json_writer_t *jw; + bool is_plain_text; +}; + +/* btf_dumper_type - print data along with type information + * @d: an instance containing context for dumping types + * @type_id: index in btf->types array. this points to the type to be dumped + * @data: pointer the actual data, i.e. the values to be printed + * + * Returns zero on success and negative error code otherwise + */ +int btf_dumper_type(const struct btf_dumper *d, __u32 type_id, + const void *data); #endif -- cgit v1.2.3-58-ga151 From 2d3feca8c44f315624b7c0f5b8361e2f54405c12 Mon Sep 17 00:00:00 2001 From: Okash Khawaja <osk@fb.com> Date: Fri, 13 Jul 2018 21:57:04 -0700 Subject: bpf: btf: print map dump and lookup with btf info This patch augments the output of bpftool's map dump and map lookup commands to print data along side btf info, if the correspondin btf info is available. The outputs for each of map dump and map lookup commands are augmented in two ways: 1. when neither of -j and -p are supplied, btf-ful map data is printed whose aim is human readability. This means no commitments for json- or backward- compatibility. 2. when either -j or -p are supplied, a new json object named "formatted" is added for each key-value pair. This object contains the same data as the key-value pair, but with btf info. "formatted" object promises json- and backward- compatibility. Below is a sample output. $ bpftool map dump -p id 8 [{ "key": ["0x0f","0x00","0x00","0x00" ], "value": ["0x03", "0x00", "0x00", "0x00", ... ], "formatted": { "key": 15, "value": { "int_field": 3, ... } } } ] This patch calls btf_dumper introduced in previous patch to accomplish the above. Indeed, btf-ful info is only displayed if btf data for the given map is available. Otherwise existing output is displayed as-is. Signed-off-by: Okash Khawaja <osk@fb.com> Acked-by: Martin KaFai Lau <kafai@fb.com> Reviewed-by: Jakub Kicinski <jakub.kicinski@netronome.com> Signed-off-by: Daniel Borkmann <daniel@iogearbox.net> --- tools/bpf/bpftool/map.c | 217 ++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 201 insertions(+), 16 deletions(-) (limited to 'tools') diff --git a/tools/bpf/bpftool/map.c b/tools/bpf/bpftool/map.c index e2baec1122fb..9c8191845585 100644 --- a/tools/bpf/bpftool/map.c +++ b/tools/bpf/bpftool/map.c @@ -34,6 +34,7 @@ #include <assert.h> #include <errno.h> #include <fcntl.h> +#include <linux/err.h> #include <stdbool.h> #include <stdio.h> #include <stdlib.h> @@ -44,6 +45,8 @@ #include <bpf.h> +#include "btf.h" +#include "json_writer.h" #include "main.h" static const char * const map_type_name[] = { @@ -148,8 +151,109 @@ int map_parse_fd_and_info(int *argc, char ***argv, void *info, __u32 *info_len) return fd; } +static int do_dump_btf(const struct btf_dumper *d, + struct bpf_map_info *map_info, void *key, + void *value) +{ + int ret; + + /* start of key-value pair */ + jsonw_start_object(d->jw); + + jsonw_name(d->jw, "key"); + + ret = btf_dumper_type(d, map_info->btf_key_type_id, key); + if (ret) + goto err_end_obj; + + jsonw_name(d->jw, "value"); + + ret = btf_dumper_type(d, map_info->btf_value_type_id, value); + +err_end_obj: + /* end of key-value pair */ + jsonw_end_object(d->jw); + + return ret; +} + +static int get_btf(struct bpf_map_info *map_info, struct btf **btf) +{ + struct bpf_btf_info btf_info = { 0 }; + __u32 len = sizeof(btf_info); + __u32 last_size; + int btf_fd; + void *ptr; + int err; + + err = 0; + *btf = NULL; + btf_fd = bpf_btf_get_fd_by_id(map_info->btf_id); + if (btf_fd < 0) + return 0; + + /* we won't know btf_size until we call bpf_obj_get_info_by_fd(). so + * let's start with a sane default - 4KiB here - and resize it only if + * bpf_obj_get_info_by_fd() needs a bigger buffer. + */ + btf_info.btf_size = 4096; + last_size = btf_info.btf_size; + ptr = malloc(last_size); + if (!ptr) { + err = -ENOMEM; + goto exit_free; + } + + bzero(ptr, last_size); + btf_info.btf = ptr_to_u64(ptr); + err = bpf_obj_get_info_by_fd(btf_fd, &btf_info, &len); + + if (!err && btf_info.btf_size > last_size) { + void *temp_ptr; + + last_size = btf_info.btf_size; + temp_ptr = realloc(ptr, last_size); + if (!temp_ptr) { + err = -ENOMEM; + goto exit_free; + } + ptr = temp_ptr; + bzero(ptr, last_size); + btf_info.btf = ptr_to_u64(ptr); + err = bpf_obj_get_info_by_fd(btf_fd, &btf_info, &len); + } + + if (err || btf_info.btf_size > last_size) { + err = errno; + goto exit_free; + } + + *btf = btf__new((__u8 *)btf_info.btf, btf_info.btf_size, NULL); + if (IS_ERR(*btf)) { + err = PTR_ERR(btf); + *btf = NULL; + } + +exit_free: + close(btf_fd); + free(ptr); + + return err; +} + +static json_writer_t *get_btf_writer(void) +{ + json_writer_t *jw = jsonw_new(stdout); + + if (!jw) + return NULL; + jsonw_pretty(jw, true); + + return jw; +} + static void print_entry_json(struct bpf_map_info *info, unsigned char *key, - unsigned char *value) + unsigned char *value, struct btf *btf) { jsonw_start_object(json_wtr); @@ -158,6 +262,16 @@ static void print_entry_json(struct bpf_map_info *info, unsigned char *key, print_hex_data_json(key, info->key_size); jsonw_name(json_wtr, "value"); print_hex_data_json(value, info->value_size); + if (btf) { + struct btf_dumper d = { + .btf = btf, + .jw = json_wtr, + .is_plain_text = false, + }; + + jsonw_name(json_wtr, "formatted"); + do_dump_btf(&d, info, key, value); + } } else { unsigned int i, n; @@ -508,10 +622,12 @@ static int do_show(int argc, char **argv) static int do_dump(int argc, char **argv) { + struct bpf_map_info info = {}; void *key, *value, *prev_key; unsigned int num_elems = 0; - struct bpf_map_info info = {}; __u32 len = sizeof(info); + json_writer_t *btf_wtr; + struct btf *btf = NULL; int err; int fd; @@ -537,8 +653,27 @@ static int do_dump(int argc, char **argv) } prev_key = NULL; + + err = get_btf(&info, &btf); + if (err) { + p_err("failed to get btf"); + goto exit_free; + } + if (json_output) jsonw_start_array(json_wtr); + else + if (btf) { + btf_wtr = get_btf_writer(); + if (!btf_wtr) { + p_info("failed to create json writer for btf. falling back to plain output"); + btf__free(btf); + btf = NULL; + } else { + jsonw_start_array(btf_wtr); + } + } + while (true) { err = bpf_map_get_next_key(fd, prev_key, key); if (err) { @@ -549,9 +684,19 @@ static int do_dump(int argc, char **argv) if (!bpf_map_lookup_elem(fd, key, value)) { if (json_output) - print_entry_json(&info, key, value); + print_entry_json(&info, key, value, btf); else - print_entry_plain(&info, key, value); + if (btf) { + struct btf_dumper d = { + .btf = btf, + .jw = btf_wtr, + .is_plain_text = true, + }; + + do_dump_btf(&d, &info, key, value); + } else { + print_entry_plain(&info, key, value); + } } else { if (json_output) { jsonw_name(json_wtr, "key"); @@ -574,14 +719,19 @@ static int do_dump(int argc, char **argv) if (json_output) jsonw_end_array(json_wtr); - else + else if (btf) { + jsonw_end_array(btf_wtr); + jsonw_destroy(&btf_wtr); + } else { printf("Found %u element%s\n", num_elems, num_elems != 1 ? "s" : ""); + } exit_free: free(key); free(value); close(fd); + btf__free(btf); return err; } @@ -637,6 +787,8 @@ static int do_lookup(int argc, char **argv) { struct bpf_map_info info = {}; __u32 len = sizeof(info); + json_writer_t *btf_wtr; + struct btf *btf = NULL; void *key, *value; int err; int fd; @@ -661,27 +813,60 @@ static int do_lookup(int argc, char **argv) goto exit_free; err = bpf_map_lookup_elem(fd, key, value); - if (!err) { - if (json_output) - print_entry_json(&info, key, value); - else + if (err) { + if (errno == ENOENT) { + if (json_output) { + jsonw_null(json_wtr); + } else { + printf("key:\n"); + fprint_hex(stdout, key, info.key_size, " "); + printf("\n\nNot found\n"); + } + } else { + p_err("lookup failed: %s", strerror(errno)); + } + + goto exit_free; + } + + /* here means bpf_map_lookup_elem() succeeded */ + err = get_btf(&info, &btf); + if (err) { + p_err("failed to get btf"); + goto exit_free; + } + + if (json_output) { + print_entry_json(&info, key, value, btf); + } else if (btf) { + /* if here json_wtr wouldn't have been initialised, + * so let's create separate writer for btf + */ + btf_wtr = get_btf_writer(); + if (!btf_wtr) { + p_info("failed to create json writer for btf. falling back to plain output"); + btf__free(btf); + btf = NULL; print_entry_plain(&info, key, value); - } else if (errno == ENOENT) { - if (json_output) { - jsonw_null(json_wtr); } else { - printf("key:\n"); - fprint_hex(stdout, key, info.key_size, " "); - printf("\n\nNot found\n"); + struct btf_dumper d = { + .btf = btf, + .jw = btf_wtr, + .is_plain_text = true, + }; + + do_dump_btf(&d, &info, key, value); + jsonw_destroy(&btf_wtr); } } else { - p_err("lookup failed: %s", strerror(errno)); + print_entry_plain(&info, key, value); } exit_free: free(key); free(value); close(fd); + btf__free(btf); return err; } -- cgit v1.2.3-58-ga151 From 060a7fccd394f1600be86536b1d3e8cdd66637c2 Mon Sep 17 00:00:00 2001 From: Andrey Ignatov <rdna@fb.com> Date: Wed, 11 Jul 2018 17:33:33 -0700 Subject: bpf: Sync bpf.h to tools/ Sync BPF_SOCK_OPS_TCP_LISTEN_CB related UAPI changes to tools/. Signed-off-by: Andrey Ignatov <rdna@fb.com> Acked-by: Alexei Starovoitov <ast@kernel.org> Signed-off-by: Daniel Borkmann <daniel@iogearbox.net> --- tools/include/uapi/linux/bpf.h | 3 +++ 1 file changed, 3 insertions(+) (limited to 'tools') diff --git a/tools/include/uapi/linux/bpf.h b/tools/include/uapi/linux/bpf.h index 6bcb287a888d..870113916cac 100644 --- a/tools/include/uapi/linux/bpf.h +++ b/tools/include/uapi/linux/bpf.h @@ -2555,6 +2555,9 @@ enum { * Arg1: old_state * Arg2: new_state */ + BPF_SOCK_OPS_TCP_LISTEN_CB, /* Called on listen(2), right after + * socket transition to LISTEN state. + */ }; /* List of TCP states. There is a build check in net/ipv4/tcp.c to detect -- cgit v1.2.3-58-ga151 From 04c13411151c220b41998020c32ac97f33b58683 Mon Sep 17 00:00:00 2001 From: Andrey Ignatov <rdna@fb.com> Date: Wed, 11 Jul 2018 17:33:34 -0700 Subject: selftests/bpf: Fix const'ness in cgroup_helpers Lack of const in cgroup helpers signatures forces to write ugly client code. Fix it. Signed-off-by: Andrey Ignatov <rdna@fb.com> Acked-by: Alexei Starovoitov <ast@kernel.org> Signed-off-by: Daniel Borkmann <daniel@iogearbox.net> --- tools/testing/selftests/bpf/cgroup_helpers.c | 6 +++--- tools/testing/selftests/bpf/cgroup_helpers.h | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) (limited to 'tools') diff --git a/tools/testing/selftests/bpf/cgroup_helpers.c b/tools/testing/selftests/bpf/cgroup_helpers.c index c87b4e052ce9..cf16948aad4a 100644 --- a/tools/testing/selftests/bpf/cgroup_helpers.c +++ b/tools/testing/selftests/bpf/cgroup_helpers.c @@ -118,7 +118,7 @@ static int join_cgroup_from_top(char *cgroup_path) * * On success, it returns 0, otherwise on failure it returns 1. */ -int join_cgroup(char *path) +int join_cgroup(const char *path) { char cgroup_path[PATH_MAX + 1]; @@ -158,7 +158,7 @@ void cleanup_cgroup_environment(void) * On success, it returns the file descriptor. On failure it returns 0. * If there is a failure, it prints the error to stderr. */ -int create_and_get_cgroup(char *path) +int create_and_get_cgroup(const char *path) { char cgroup_path[PATH_MAX + 1]; int fd; @@ -186,7 +186,7 @@ int create_and_get_cgroup(char *path) * which is an invalid cgroup id. * If there is a failure, it prints the error to stderr. */ -unsigned long long get_cgroup_id(char *path) +unsigned long long get_cgroup_id(const char *path) { int dirfd, err, flags, mount_id, fhsize; union { diff --git a/tools/testing/selftests/bpf/cgroup_helpers.h b/tools/testing/selftests/bpf/cgroup_helpers.h index 20a4a5dcd469..d64bb8957090 100644 --- a/tools/testing/selftests/bpf/cgroup_helpers.h +++ b/tools/testing/selftests/bpf/cgroup_helpers.h @@ -9,10 +9,10 @@ __FILE__, __LINE__, clean_errno(), ##__VA_ARGS__) -int create_and_get_cgroup(char *path); -int join_cgroup(char *path); +int create_and_get_cgroup(const char *path); +int join_cgroup(const char *path); int setup_cgroup_environment(void); void cleanup_cgroup_environment(void); -unsigned long long get_cgroup_id(char *path); +unsigned long long get_cgroup_id(const char *path); #endif -- cgit v1.2.3-58-ga151 From c65267e5ff418c81d7fea7025850ed4f190a1289 Mon Sep 17 00:00:00 2001 From: Andrey Ignatov <rdna@fb.com> Date: Wed, 11 Jul 2018 17:33:35 -0700 Subject: selftests/bpf: Switch test_tcpbpf_user to cgroup_helpers Switch to cgroup_helpers to simplify the code and fix cgroup cleanup: before cgroup was not cleaned up after the test. It also removes SYSTEM macro, that only printed error, but didn't terminate the test. Signed-off-by: Andrey Ignatov <rdna@fb.com> Acked-by: Alexei Starovoitov <ast@kernel.org> Signed-off-by: Daniel Borkmann <daniel@iogearbox.net> --- tools/testing/selftests/bpf/Makefile | 1 + tools/testing/selftests/bpf/test_tcpbpf_user.c | 55 ++++++++++---------------- 2 files changed, 22 insertions(+), 34 deletions(-) (limited to 'tools') diff --git a/tools/testing/selftests/bpf/Makefile b/tools/testing/selftests/bpf/Makefile index 7a6214e9ae58..478bf1bcbbf5 100644 --- a/tools/testing/selftests/bpf/Makefile +++ b/tools/testing/selftests/bpf/Makefile @@ -61,6 +61,7 @@ $(OUTPUT)/test_dev_cgroup: cgroup_helpers.c $(OUTPUT)/test_sock: cgroup_helpers.c $(OUTPUT)/test_sock_addr: cgroup_helpers.c $(OUTPUT)/test_sockmap: cgroup_helpers.c +$(OUTPUT)/test_tcpbpf_user: cgroup_helpers.c $(OUTPUT)/test_progs: trace_helpers.c $(OUTPUT)/get_cgroup_id_user: cgroup_helpers.c diff --git a/tools/testing/selftests/bpf/test_tcpbpf_user.c b/tools/testing/selftests/bpf/test_tcpbpf_user.c index 84ab5163c828..fa97ec6428de 100644 --- a/tools/testing/selftests/bpf/test_tcpbpf_user.c +++ b/tools/testing/selftests/bpf/test_tcpbpf_user.c @@ -1,25 +1,18 @@ // SPDX-License-Identifier: GPL-2.0 #include <stdio.h> #include <stdlib.h> -#include <stdio.h> #include <unistd.h> #include <errno.h> -#include <signal.h> #include <string.h> -#include <assert.h> -#include <linux/perf_event.h> -#include <linux/ptrace.h> #include <linux/bpf.h> -#include <sys/ioctl.h> -#include <sys/time.h> #include <sys/types.h> -#include <sys/stat.h> -#include <fcntl.h> #include <bpf/bpf.h> #include <bpf/libbpf.h> -#include "bpf_util.h" + #include "bpf_rlimit.h" -#include <linux/perf_event.h> +#include "bpf_util.h" +#include "cgroup_helpers.h" + #include "test_tcpbpf.h" static int bpf_find_map(const char *test, struct bpf_object *obj, @@ -35,42 +28,32 @@ static int bpf_find_map(const char *test, struct bpf_object *obj, return bpf_map__fd(map); } -#define SYSTEM(CMD) \ - do { \ - if (system(CMD)) { \ - printf("system(%s) FAILS!\n", CMD); \ - } \ - } while (0) - int main(int argc, char **argv) { const char *file = "test_tcpbpf_kern.o"; struct tcpbpf_globals g = {0}; - int cg_fd, prog_fd, map_fd; + const char *cg_path = "/foo"; bool debug_flag = false; int error = EXIT_FAILURE; struct bpf_object *obj; - char cmd[100], *dir; - struct stat buffer; + int prog_fd, map_fd; + int cg_fd = -1; __u32 key = 0; - int pid; int rv; if (argc > 1 && strcmp(argv[1], "-d") == 0) debug_flag = true; - dir = "/tmp/cgroupv2/foo"; + if (setup_cgroup_environment()) + goto err; + + cg_fd = create_and_get_cgroup(cg_path); + if (!cg_fd) + goto err; - if (stat(dir, &buffer) != 0) { - SYSTEM("mkdir -p /tmp/cgroupv2"); - SYSTEM("mount -t cgroup2 none /tmp/cgroupv2"); - SYSTEM("mkdir -p /tmp/cgroupv2/foo"); - } - pid = (int) getpid(); - sprintf(cmd, "echo %d >> /tmp/cgroupv2/foo/cgroup.procs", pid); - SYSTEM(cmd); + if (join_cgroup(cg_path)) + goto err; - cg_fd = open(dir, O_DIRECTORY, O_RDONLY); if (bpf_prog_load(file, BPF_PROG_TYPE_SOCK_OPS, &obj, &prog_fd)) { printf("FAILED: load_bpf_file failed for: %s\n", file); goto err; @@ -83,7 +66,10 @@ int main(int argc, char **argv) goto err; } - SYSTEM("./tcp_server.py"); + if (system("./tcp_server.py")) { + printf("FAILED: TCP server\n"); + goto err; + } map_fd = bpf_find_map(__func__, obj, "global_map"); if (map_fd < 0) @@ -123,6 +109,7 @@ int main(int argc, char **argv) error = 0; err: bpf_prog_detach(cg_fd, BPF_CGROUP_SOCK_OPS); + close(cg_fd); + cleanup_cgroup_environment(); return error; - } -- cgit v1.2.3-58-ga151 From 2044e4ef0be29f0154b078a956a3d8331315298a Mon Sep 17 00:00:00 2001 From: Andrey Ignatov <rdna@fb.com> Date: Wed, 11 Jul 2018 17:33:36 -0700 Subject: selftests/bpf: Better verification in test_tcpbpf Reduce amount of copy/paste for debug info when result is verified in the test and keep that info together with values being checked so that they won't get out of sync. It also improves debug experience: instead of checking manually what doesn't match in debug output for all fields, only unexpected field is printed. Signed-off-by: Andrey Ignatov <rdna@fb.com> Acked-by: Alexei Starovoitov <ast@kernel.org> Signed-off-by: Daniel Borkmann <daniel@iogearbox.net> --- tools/testing/selftests/bpf/test_tcpbpf_user.c | 64 ++++++++++++++++---------- 1 file changed, 39 insertions(+), 25 deletions(-) (limited to 'tools') diff --git a/tools/testing/selftests/bpf/test_tcpbpf_user.c b/tools/testing/selftests/bpf/test_tcpbpf_user.c index fa97ec6428de..971f1644b9c7 100644 --- a/tools/testing/selftests/bpf/test_tcpbpf_user.c +++ b/tools/testing/selftests/bpf/test_tcpbpf_user.c @@ -1,4 +1,5 @@ // SPDX-License-Identifier: GPL-2.0 +#include <inttypes.h> #include <stdio.h> #include <stdlib.h> #include <unistd.h> @@ -15,6 +16,42 @@ #include "test_tcpbpf.h" +#define EXPECT_EQ(expected, actual, fmt) \ + do { \ + if ((expected) != (actual)) { \ + printf(" Value of: " #actual "\n" \ + " Actual: %" fmt "\n" \ + " Expected: %" fmt "\n", \ + (actual), (expected)); \ + goto err; \ + } \ + } while (0) + +int verify_result(const struct tcpbpf_globals *result) +{ + __u32 expected_events; + + expected_events = ((1 << BPF_SOCK_OPS_TIMEOUT_INIT) | + (1 << BPF_SOCK_OPS_RWND_INIT) | + (1 << BPF_SOCK_OPS_TCP_CONNECT_CB) | + (1 << BPF_SOCK_OPS_ACTIVE_ESTABLISHED_CB) | + (1 << BPF_SOCK_OPS_PASSIVE_ESTABLISHED_CB) | + (1 << BPF_SOCK_OPS_NEEDS_ECN) | + (1 << BPF_SOCK_OPS_STATE_CB)); + + EXPECT_EQ(expected_events, result->event_map, "#" PRIx32); + EXPECT_EQ(501ULL, result->bytes_received, "llu"); + EXPECT_EQ(1002ULL, result->bytes_acked, "llu"); + EXPECT_EQ(1, result->data_segs_in, PRIu32); + EXPECT_EQ(1, result->data_segs_out, PRIu32); + EXPECT_EQ(0x80, result->bad_cb_test_rv, PRIu32); + EXPECT_EQ(0, result->good_cb_test_rv, PRIu32); + + return 0; +err: + return -1; +} + static int bpf_find_map(const char *test, struct bpf_object *obj, const char *name) { @@ -33,7 +70,6 @@ int main(int argc, char **argv) const char *file = "test_tcpbpf_kern.o"; struct tcpbpf_globals g = {0}; const char *cg_path = "/foo"; - bool debug_flag = false; int error = EXIT_FAILURE; struct bpf_object *obj; int prog_fd, map_fd; @@ -41,9 +77,6 @@ int main(int argc, char **argv) __u32 key = 0; int rv; - if (argc > 1 && strcmp(argv[1], "-d") == 0) - debug_flag = true; - if (setup_cgroup_environment()) goto err; @@ -81,30 +114,11 @@ int main(int argc, char **argv) goto err; } - if (g.bytes_received != 501 || g.bytes_acked != 1002 || - g.data_segs_in != 1 || g.data_segs_out != 1 || - (g.event_map ^ 0x47e) != 0 || g.bad_cb_test_rv != 0x80 || - g.good_cb_test_rv != 0) { + if (verify_result(&g)) { printf("FAILED: Wrong stats\n"); - if (debug_flag) { - printf("\n"); - printf("bytes_received: %d (expecting 501)\n", - (int)g.bytes_received); - printf("bytes_acked: %d (expecting 1002)\n", - (int)g.bytes_acked); - printf("data_segs_in: %d (expecting 1)\n", - g.data_segs_in); - printf("data_segs_out: %d (expecting 1)\n", - g.data_segs_out); - printf("event_map: 0x%x (at least 0x47e)\n", - g.event_map); - printf("bad_cb_test_rv: 0x%x (expecting 0x80)\n", - g.bad_cb_test_rv); - printf("good_cb_test_rv:0x%x (expecting 0)\n", - g.good_cb_test_rv); - } goto err; } + printf("PASSED!\n"); error = 0; err: -- cgit v1.2.3-58-ga151 From 78d8e26d46bc2ed73ab6a0e369a342478fda4ce0 Mon Sep 17 00:00:00 2001 From: Andrey Ignatov <rdna@fb.com> Date: Wed, 11 Jul 2018 17:33:37 -0700 Subject: selftests/bpf: Test case for BPF_SOCK_OPS_TCP_LISTEN_CB Cover new TCP-BPF callback in test_tcpbpf: when listen() is called on socket, set BPF_SOCK_OPS_STATE_CB_FLAG so that BPF_SOCK_OPS_STATE_CB callback can be called on future state transition, and when such a transition happens (TCP_LISTEN -> TCP_CLOSE), track it in the map and verify it in user space later. Signed-off-by: Andrey Ignatov <rdna@fb.com> Acked-by: Alexei Starovoitov <ast@kernel.org> Signed-off-by: Daniel Borkmann <daniel@iogearbox.net> --- tools/testing/selftests/bpf/test_tcpbpf.h | 1 + tools/testing/selftests/bpf/test_tcpbpf_kern.c | 17 ++++++++++++----- tools/testing/selftests/bpf/test_tcpbpf_user.c | 4 +++- 3 files changed, 16 insertions(+), 6 deletions(-) (limited to 'tools') diff --git a/tools/testing/selftests/bpf/test_tcpbpf.h b/tools/testing/selftests/bpf/test_tcpbpf.h index 2fe43289943c..7bcfa6207005 100644 --- a/tools/testing/selftests/bpf/test_tcpbpf.h +++ b/tools/testing/selftests/bpf/test_tcpbpf.h @@ -12,5 +12,6 @@ struct tcpbpf_globals { __u32 good_cb_test_rv; __u64 bytes_received; __u64 bytes_acked; + __u32 num_listen; }; #endif diff --git a/tools/testing/selftests/bpf/test_tcpbpf_kern.c b/tools/testing/selftests/bpf/test_tcpbpf_kern.c index 3e645ee41ed5..4b7fd540cea9 100644 --- a/tools/testing/selftests/bpf/test_tcpbpf_kern.c +++ b/tools/testing/selftests/bpf/test_tcpbpf_kern.c @@ -96,15 +96,22 @@ int bpf_testcb(struct bpf_sock_ops *skops) if (!gp) break; g = *gp; - g.total_retrans = skops->total_retrans; - g.data_segs_in = skops->data_segs_in; - g.data_segs_out = skops->data_segs_out; - g.bytes_received = skops->bytes_received; - g.bytes_acked = skops->bytes_acked; + if (skops->args[0] == BPF_TCP_LISTEN) { + g.num_listen++; + } else { + g.total_retrans = skops->total_retrans; + g.data_segs_in = skops->data_segs_in; + g.data_segs_out = skops->data_segs_out; + g.bytes_received = skops->bytes_received; + g.bytes_acked = skops->bytes_acked; + } bpf_map_update_elem(&global_map, &key, &g, BPF_ANY); } break; + case BPF_SOCK_OPS_TCP_LISTEN_CB: + bpf_sock_ops_cb_flags_set(skops, BPF_SOCK_OPS_STATE_CB_FLAG); + break; default: rv = -1; } diff --git a/tools/testing/selftests/bpf/test_tcpbpf_user.c b/tools/testing/selftests/bpf/test_tcpbpf_user.c index 971f1644b9c7..a275c2971376 100644 --- a/tools/testing/selftests/bpf/test_tcpbpf_user.c +++ b/tools/testing/selftests/bpf/test_tcpbpf_user.c @@ -37,7 +37,8 @@ int verify_result(const struct tcpbpf_globals *result) (1 << BPF_SOCK_OPS_ACTIVE_ESTABLISHED_CB) | (1 << BPF_SOCK_OPS_PASSIVE_ESTABLISHED_CB) | (1 << BPF_SOCK_OPS_NEEDS_ECN) | - (1 << BPF_SOCK_OPS_STATE_CB)); + (1 << BPF_SOCK_OPS_STATE_CB) | + (1 << BPF_SOCK_OPS_TCP_LISTEN_CB)); EXPECT_EQ(expected_events, result->event_map, "#" PRIx32); EXPECT_EQ(501ULL, result->bytes_received, "llu"); @@ -46,6 +47,7 @@ int verify_result(const struct tcpbpf_globals *result) EXPECT_EQ(1, result->data_segs_out, PRIu32); EXPECT_EQ(0x80, result->bad_cb_test_rv, PRIu32); EXPECT_EQ(0, result->good_cb_test_rv, PRIu32); + EXPECT_EQ(1, result->num_listen, PRIu32); return 0; err: -- cgit v1.2.3-58-ga151 From 7f657d5bf507be0f059405306e860c4b3e40854b Mon Sep 17 00:00:00 2001 From: Dave Watson <davejwatson@fb.com> Date: Thu, 12 Jul 2018 10:59:20 -0700 Subject: selftests: tls: add selftests for TLS sockets Add selftests for tls socket. Tests various iov and message options, poll blocking and nonblocking behavior, partial message sends / receives, and control message data. Tests should pass regardless of if TLS is enabled in the kernel or not, and print a warning message if not. Signed-off-by: Dave Watson <davejwatson@fb.com> Signed-off-by: David S. Miller <davem@davemloft.net> --- tools/testing/selftests/net/Makefile | 2 +- tools/testing/selftests/net/tls.c | 692 +++++++++++++++++++++++++++++++++++ 2 files changed, 693 insertions(+), 1 deletion(-) create mode 100644 tools/testing/selftests/net/tls.c (limited to 'tools') diff --git a/tools/testing/selftests/net/Makefile b/tools/testing/selftests/net/Makefile index 663e11e85727..9cca68e440a0 100644 --- a/tools/testing/selftests/net/Makefile +++ b/tools/testing/selftests/net/Makefile @@ -13,7 +13,7 @@ TEST_GEN_FILES += psock_fanout psock_tpacket msg_zerocopy TEST_GEN_FILES += tcp_mmap tcp_inq psock_snd TEST_GEN_FILES += udpgso udpgso_bench_tx udpgso_bench_rx TEST_GEN_PROGS = reuseport_bpf reuseport_bpf_cpu reuseport_bpf_numa -TEST_GEN_PROGS += reuseport_dualstack reuseaddr_conflict +TEST_GEN_PROGS += reuseport_dualstack reuseaddr_conflict tls include ../lib.mk diff --git a/tools/testing/selftests/net/tls.c b/tools/testing/selftests/net/tls.c new file mode 100644 index 000000000000..b3ebf2646e52 --- /dev/null +++ b/tools/testing/selftests/net/tls.c @@ -0,0 +1,692 @@ +// SPDX-License-Identifier: GPL-2.0 + +#define _GNU_SOURCE + +#include <arpa/inet.h> +#include <errno.h> +#include <error.h> +#include <fcntl.h> +#include <poll.h> +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> + +#include <linux/tls.h> +#include <linux/tcp.h> +#include <linux/socket.h> + +#include <sys/types.h> +#include <sys/sendfile.h> +#include <sys/socket.h> +#include <sys/stat.h> + +#include "../kselftest_harness.h" + +#define TLS_PAYLOAD_MAX_LEN 16384 +#define SOL_TLS 282 + +FIXTURE(tls) +{ + int fd, cfd; + bool notls; +}; + +FIXTURE_SETUP(tls) +{ + struct tls12_crypto_info_aes_gcm_128 tls12; + struct sockaddr_in addr; + socklen_t len; + int sfd, ret; + + self->notls = false; + len = sizeof(addr); + + memset(&tls12, 0, sizeof(tls12)); + tls12.info.version = TLS_1_2_VERSION; + tls12.info.cipher_type = TLS_CIPHER_AES_GCM_128; + + addr.sin_family = AF_INET; + addr.sin_addr.s_addr = htonl(INADDR_ANY); + addr.sin_port = 0; + + self->fd = socket(AF_INET, SOCK_STREAM, 0); + sfd = socket(AF_INET, SOCK_STREAM, 0); + + ret = bind(sfd, &addr, sizeof(addr)); + ASSERT_EQ(ret, 0); + ret = listen(sfd, 10); + ASSERT_EQ(ret, 0); + + ret = getsockname(sfd, &addr, &len); + ASSERT_EQ(ret, 0); + + ret = connect(self->fd, &addr, sizeof(addr)); + ASSERT_EQ(ret, 0); + + ret = setsockopt(self->fd, IPPROTO_TCP, TCP_ULP, "tls", sizeof("tls")); + if (ret != 0) { + self->notls = true; + printf("Failure setting TCP_ULP, testing without tls\n"); + } + + if (!self->notls) { + ret = setsockopt(self->fd, SOL_TLS, TLS_TX, &tls12, + sizeof(tls12)); + ASSERT_EQ(ret, 0); + } + + self->cfd = accept(sfd, &addr, &len); + ASSERT_GE(self->cfd, 0); + + if (!self->notls) { + ret = setsockopt(self->cfd, IPPROTO_TCP, TCP_ULP, "tls", + sizeof("tls")); + ASSERT_EQ(ret, 0); + + ret = setsockopt(self->cfd, SOL_TLS, TLS_RX, &tls12, + sizeof(tls12)); + ASSERT_EQ(ret, 0); + } + + close(sfd); +} + +FIXTURE_TEARDOWN(tls) +{ + close(self->fd); + close(self->cfd); +} + +TEST_F(tls, sendfile) +{ + int filefd = open("/proc/self/exe", O_RDONLY); + struct stat st; + + EXPECT_GE(filefd, 0); + fstat(filefd, &st); + EXPECT_GE(sendfile(self->fd, filefd, 0, st.st_size), 0); +} + +TEST_F(tls, send_then_sendfile) +{ + int filefd = open("/proc/self/exe", O_RDONLY); + char const *test_str = "test_send"; + int to_send = strlen(test_str) + 1; + char recv_buf[10]; + struct stat st; + char *buf; + + EXPECT_GE(filefd, 0); + fstat(filefd, &st); + buf = (char *)malloc(st.st_size); + + EXPECT_EQ(send(self->fd, test_str, to_send, 0), to_send); + EXPECT_EQ(recv(self->cfd, recv_buf, to_send, 0), to_send); + EXPECT_EQ(memcmp(test_str, recv_buf, to_send), 0); + + EXPECT_GE(sendfile(self->fd, filefd, 0, st.st_size), 0); + EXPECT_EQ(recv(self->cfd, buf, st.st_size, 0), st.st_size); +} + +TEST_F(tls, recv_max) +{ + unsigned int send_len = TLS_PAYLOAD_MAX_LEN; + char recv_mem[TLS_PAYLOAD_MAX_LEN]; + char buf[TLS_PAYLOAD_MAX_LEN]; + + EXPECT_GE(send(self->fd, buf, send_len, 0), 0); + EXPECT_NE(recv(self->cfd, recv_mem, send_len, 0), -1); + EXPECT_EQ(memcmp(buf, recv_mem, send_len), 0); +} + +TEST_F(tls, recv_small) +{ + char const *test_str = "test_read"; + int send_len = 10; + char buf[10]; + + send_len = strlen(test_str) + 1; + EXPECT_EQ(send(self->fd, test_str, send_len, 0), send_len); + EXPECT_NE(recv(self->cfd, buf, send_len, 0), -1); + EXPECT_EQ(memcmp(buf, test_str, send_len), 0); +} + +TEST_F(tls, msg_more) +{ + char const *test_str = "test_read"; + int send_len = 10; + char buf[10 * 2]; + + EXPECT_EQ(send(self->fd, test_str, send_len, MSG_MORE), send_len); + EXPECT_EQ(recv(self->cfd, buf, send_len, MSG_DONTWAIT), -1); + EXPECT_EQ(send(self->fd, test_str, send_len, 0), send_len); + EXPECT_EQ(recv(self->cfd, buf, send_len * 2, MSG_DONTWAIT), + send_len * 2); + EXPECT_EQ(memcmp(buf, test_str, send_len), 0); +} + +TEST_F(tls, sendmsg_single) +{ + struct msghdr msg; + + char const *test_str = "test_sendmsg"; + size_t send_len = 13; + struct iovec vec; + char buf[13]; + + vec.iov_base = (char *)test_str; + vec.iov_len = send_len; + memset(&msg, 0, sizeof(struct msghdr)); + msg.msg_iov = &vec; + msg.msg_iovlen = 1; + EXPECT_EQ(sendmsg(self->fd, &msg, 0), send_len); + EXPECT_EQ(recv(self->cfd, buf, send_len, 0), send_len); + EXPECT_EQ(memcmp(buf, test_str, send_len), 0); +} + +TEST_F(tls, sendmsg_large) +{ + void *mem = malloc(16384); + size_t send_len = 16384; + size_t sends = 128; + struct msghdr msg; + size_t recvs = 0; + size_t sent = 0; + + memset(&msg, 0, sizeof(struct msghdr)); + while (sent++ < sends) { + struct iovec vec = { (void *)mem, send_len }; + + msg.msg_iov = &vec; + msg.msg_iovlen = 1; + EXPECT_EQ(sendmsg(self->cfd, &msg, 0), send_len); + } + + while (recvs++ < sends) + EXPECT_NE(recv(self->fd, mem, send_len, 0), -1); + + free(mem); +} + +TEST_F(tls, sendmsg_multiple) +{ + char const *test_str = "test_sendmsg_multiple"; + struct iovec vec[5]; + char *test_strs[5]; + struct msghdr msg; + int total_len = 0; + int len_cmp = 0; + int iov_len = 5; + char *buf; + int i; + + memset(&msg, 0, sizeof(struct msghdr)); + for (i = 0; i < iov_len; i++) { + test_strs[i] = (char *)malloc(strlen(test_str) + 1); + snprintf(test_strs[i], strlen(test_str) + 1, "%s", test_str); + vec[i].iov_base = (void *)test_strs[i]; + vec[i].iov_len = strlen(test_strs[i]) + 1; + total_len += vec[i].iov_len; + } + msg.msg_iov = vec; + msg.msg_iovlen = iov_len; + + EXPECT_EQ(sendmsg(self->cfd, &msg, 0), total_len); + buf = malloc(total_len); + EXPECT_NE(recv(self->fd, buf, total_len, 0), -1); + for (i = 0; i < iov_len; i++) { + EXPECT_EQ(memcmp(test_strs[i], buf + len_cmp, + strlen(test_strs[i])), + 0); + len_cmp += strlen(buf + len_cmp) + 1; + } + for (i = 0; i < iov_len; i++) + free(test_strs[i]); + free(buf); +} + +TEST_F(tls, sendmsg_multiple_stress) +{ + char const *test_str = "abcdefghijklmno"; + struct iovec vec[1024]; + char *test_strs[1024]; + int iov_len = 1024; + int total_len = 0; + char buf[1 << 14]; + struct msghdr msg; + int len_cmp = 0; + int i; + + memset(&msg, 0, sizeof(struct msghdr)); + for (i = 0; i < iov_len; i++) { + test_strs[i] = (char *)malloc(strlen(test_str) + 1); + snprintf(test_strs[i], strlen(test_str) + 1, "%s", test_str); + vec[i].iov_base = (void *)test_strs[i]; + vec[i].iov_len = strlen(test_strs[i]) + 1; + total_len += vec[i].iov_len; + } + msg.msg_iov = vec; + msg.msg_iovlen = iov_len; + + EXPECT_EQ(sendmsg(self->fd, &msg, 0), total_len); + EXPECT_NE(recv(self->cfd, buf, total_len, 0), -1); + + for (i = 0; i < iov_len; i++) + len_cmp += strlen(buf + len_cmp) + 1; + + for (i = 0; i < iov_len; i++) + free(test_strs[i]); +} + +TEST_F(tls, splice_from_pipe) +{ + int send_len = TLS_PAYLOAD_MAX_LEN; + char mem_send[TLS_PAYLOAD_MAX_LEN]; + char mem_recv[TLS_PAYLOAD_MAX_LEN]; + int p[2]; + + ASSERT_GE(pipe(p), 0); + EXPECT_GE(write(p[1], mem_send, send_len), 0); + EXPECT_GE(splice(p[0], NULL, self->fd, NULL, send_len, 0), 0); + EXPECT_GE(recv(self->cfd, mem_recv, send_len, 0), 0); + EXPECT_EQ(memcmp(mem_send, mem_recv, send_len), 0); +} + +TEST_F(tls, splice_from_pipe2) +{ + int send_len = 16000; + char mem_send[16000]; + char mem_recv[16000]; + int p2[2]; + int p[2]; + + ASSERT_GE(pipe(p), 0); + ASSERT_GE(pipe(p2), 0); + EXPECT_GE(write(p[1], mem_send, 8000), 0); + EXPECT_GE(splice(p[0], NULL, self->fd, NULL, 8000, 0), 0); + EXPECT_GE(write(p2[1], mem_send + 8000, 8000), 0); + EXPECT_GE(splice(p2[0], NULL, self->fd, NULL, 8000, 0), 0); + EXPECT_GE(recv(self->cfd, mem_recv, send_len, 0), 0); + EXPECT_EQ(memcmp(mem_send, mem_recv, send_len), 0); +} + +TEST_F(tls, send_and_splice) +{ + int send_len = TLS_PAYLOAD_MAX_LEN; + char mem_send[TLS_PAYLOAD_MAX_LEN]; + char mem_recv[TLS_PAYLOAD_MAX_LEN]; + char const *test_str = "test_read"; + int send_len2 = 10; + char buf[10]; + int p[2]; + + ASSERT_GE(pipe(p), 0); + EXPECT_EQ(send(self->fd, test_str, send_len2, 0), send_len2); + EXPECT_NE(recv(self->cfd, buf, send_len2, 0), -1); + EXPECT_EQ(memcmp(test_str, buf, send_len2), 0); + + EXPECT_GE(write(p[1], mem_send, send_len), send_len); + EXPECT_GE(splice(p[0], NULL, self->fd, NULL, send_len, 0), send_len); + + EXPECT_GE(recv(self->cfd, mem_recv, send_len, 0), 0); + EXPECT_EQ(memcmp(mem_send, mem_recv, send_len), 0); +} + +TEST_F(tls, splice_to_pipe) +{ + int send_len = TLS_PAYLOAD_MAX_LEN; + char mem_send[TLS_PAYLOAD_MAX_LEN]; + char mem_recv[TLS_PAYLOAD_MAX_LEN]; + int p[2]; + + ASSERT_GE(pipe(p), 0); + EXPECT_GE(send(self->fd, mem_send, send_len, 0), 0); + EXPECT_GE(splice(self->cfd, NULL, p[1], NULL, send_len, 0), 0); + EXPECT_GE(read(p[0], mem_recv, send_len), 0); + EXPECT_EQ(memcmp(mem_send, mem_recv, send_len), 0); +} + +TEST_F(tls, recvmsg_single) +{ + char const *test_str = "test_recvmsg_single"; + int send_len = strlen(test_str) + 1; + char buf[20]; + struct msghdr hdr; + struct iovec vec; + + memset(&hdr, 0, sizeof(hdr)); + EXPECT_EQ(send(self->fd, test_str, send_len, 0), send_len); + vec.iov_base = (char *)buf; + vec.iov_len = send_len; + hdr.msg_iovlen = 1; + hdr.msg_iov = &vec; + EXPECT_NE(recvmsg(self->cfd, &hdr, 0), -1); + EXPECT_EQ(memcmp(test_str, buf, send_len), 0); +} + +TEST_F(tls, recvmsg_single_max) +{ + int send_len = TLS_PAYLOAD_MAX_LEN; + char send_mem[TLS_PAYLOAD_MAX_LEN]; + char recv_mem[TLS_PAYLOAD_MAX_LEN]; + struct iovec vec; + struct msghdr hdr; + + EXPECT_EQ(send(self->fd, send_mem, send_len, 0), send_len); + vec.iov_base = (char *)recv_mem; + vec.iov_len = TLS_PAYLOAD_MAX_LEN; + + hdr.msg_iovlen = 1; + hdr.msg_iov = &vec; + EXPECT_NE(recvmsg(self->cfd, &hdr, 0), -1); + EXPECT_EQ(memcmp(send_mem, recv_mem, send_len), 0); +} + +TEST_F(tls, recvmsg_multiple) +{ + unsigned int msg_iovlen = 1024; + unsigned int len_compared = 0; + struct iovec vec[1024]; + char *iov_base[1024]; + unsigned int iov_len = 16; + int send_len = 1 << 14; + char buf[1 << 14]; + struct msghdr hdr; + int i; + + EXPECT_EQ(send(self->fd, buf, send_len, 0), send_len); + for (i = 0; i < msg_iovlen; i++) { + iov_base[i] = (char *)malloc(iov_len); + vec[i].iov_base = iov_base[i]; + vec[i].iov_len = iov_len; + } + + hdr.msg_iovlen = msg_iovlen; + hdr.msg_iov = vec; + EXPECT_NE(recvmsg(self->cfd, &hdr, 0), -1); + for (i = 0; i < msg_iovlen; i++) + len_compared += iov_len; + + for (i = 0; i < msg_iovlen; i++) + free(iov_base[i]); +} + +TEST_F(tls, single_send_multiple_recv) +{ + unsigned int total_len = TLS_PAYLOAD_MAX_LEN * 2; + unsigned int send_len = TLS_PAYLOAD_MAX_LEN; + char send_mem[TLS_PAYLOAD_MAX_LEN * 2]; + char recv_mem[TLS_PAYLOAD_MAX_LEN * 2]; + + EXPECT_GE(send(self->fd, send_mem, total_len, 0), 0); + memset(recv_mem, 0, total_len); + + EXPECT_NE(recv(self->cfd, recv_mem, send_len, 0), -1); + EXPECT_NE(recv(self->cfd, recv_mem + send_len, send_len, 0), -1); + EXPECT_EQ(memcmp(send_mem, recv_mem, total_len), 0); +} + +TEST_F(tls, multiple_send_single_recv) +{ + unsigned int total_len = 2 * 10; + unsigned int send_len = 10; + char recv_mem[2 * 10]; + char send_mem[10]; + + EXPECT_GE(send(self->fd, send_mem, send_len, 0), 0); + EXPECT_GE(send(self->fd, send_mem, send_len, 0), 0); + memset(recv_mem, 0, total_len); + EXPECT_EQ(recv(self->cfd, recv_mem, total_len, 0), total_len); + + EXPECT_EQ(memcmp(send_mem, recv_mem, send_len), 0); + EXPECT_EQ(memcmp(send_mem, recv_mem + send_len, send_len), 0); +} + +TEST_F(tls, recv_partial) +{ + char const *test_str = "test_read_partial"; + char const *test_str_first = "test_read"; + char const *test_str_second = "_partial"; + int send_len = strlen(test_str) + 1; + char recv_mem[18]; + + memset(recv_mem, 0, sizeof(recv_mem)); + EXPECT_EQ(send(self->fd, test_str, send_len, 0), send_len); + EXPECT_NE(recv(self->cfd, recv_mem, strlen(test_str_first), 0), -1); + EXPECT_EQ(memcmp(test_str_first, recv_mem, strlen(test_str_first)), 0); + memset(recv_mem, 0, sizeof(recv_mem)); + EXPECT_NE(recv(self->cfd, recv_mem, strlen(test_str_second), 0), -1); + EXPECT_EQ(memcmp(test_str_second, recv_mem, strlen(test_str_second)), + 0); +} + +TEST_F(tls, recv_nonblock) +{ + char buf[4096]; + bool err; + + EXPECT_EQ(recv(self->cfd, buf, sizeof(buf), MSG_DONTWAIT), -1); + err = (errno == EAGAIN || errno == EWOULDBLOCK); + EXPECT_EQ(err, true); +} + +TEST_F(tls, recv_peek) +{ + char const *test_str = "test_read_peek"; + int send_len = strlen(test_str) + 1; + char buf[15]; + + EXPECT_EQ(send(self->fd, test_str, send_len, 0), send_len); + EXPECT_NE(recv(self->cfd, buf, send_len, MSG_PEEK), -1); + EXPECT_EQ(memcmp(test_str, buf, send_len), 0); + memset(buf, 0, sizeof(buf)); + EXPECT_NE(recv(self->cfd, buf, send_len, 0), -1); + EXPECT_EQ(memcmp(test_str, buf, send_len), 0); +} + +TEST_F(tls, recv_peek_multiple) +{ + char const *test_str = "test_read_peek"; + int send_len = strlen(test_str) + 1; + unsigned int num_peeks = 100; + char buf[15]; + int i; + + EXPECT_EQ(send(self->fd, test_str, send_len, 0), send_len); + for (i = 0; i < num_peeks; i++) { + EXPECT_NE(recv(self->cfd, buf, send_len, MSG_PEEK), -1); + EXPECT_EQ(memcmp(test_str, buf, send_len), 0); + memset(buf, 0, sizeof(buf)); + } + EXPECT_NE(recv(self->cfd, buf, send_len, 0), -1); + EXPECT_EQ(memcmp(test_str, buf, send_len), 0); +} + +TEST_F(tls, pollin) +{ + char const *test_str = "test_poll"; + struct pollfd fd = { 0, 0, 0 }; + char buf[10]; + int send_len = 10; + + EXPECT_EQ(send(self->fd, test_str, send_len, 0), send_len); + fd.fd = self->cfd; + fd.events = POLLIN; + + EXPECT_EQ(poll(&fd, 1, 20), 1); + EXPECT_EQ(fd.revents & POLLIN, 1); + EXPECT_EQ(recv(self->cfd, buf, send_len, 0), send_len); + /* Test timing out */ + EXPECT_EQ(poll(&fd, 1, 20), 0); +} + +TEST_F(tls, poll_wait) +{ + char const *test_str = "test_poll_wait"; + int send_len = strlen(test_str) + 1; + struct pollfd fd = { 0, 0, 0 }; + char recv_mem[15]; + + fd.fd = self->cfd; + fd.events = POLLIN; + EXPECT_EQ(send(self->fd, test_str, send_len, 0), send_len); + /* Set timeout to inf. secs */ + EXPECT_EQ(poll(&fd, 1, -1), 1); + EXPECT_EQ(fd.revents & POLLIN, 1); + EXPECT_EQ(recv(self->cfd, recv_mem, send_len, 0), send_len); +} + +TEST_F(tls, blocking) +{ + size_t data = 100000; + int res = fork(); + + EXPECT_NE(res, -1); + + if (res) { + /* parent */ + size_t left = data; + char buf[16384]; + int status; + int pid2; + + while (left) { + int res = send(self->fd, buf, + left > 16384 ? 16384 : left, 0); + + EXPECT_GE(res, 0); + left -= res; + } + + pid2 = wait(&status); + EXPECT_EQ(status, 0); + EXPECT_EQ(res, pid2); + } else { + /* child */ + size_t left = data; + char buf[16384]; + + while (left) { + int res = recv(self->cfd, buf, + left > 16384 ? 16384 : left, 0); + + EXPECT_GE(res, 0); + left -= res; + } + } +} + +TEST_F(tls, nonblocking) +{ + size_t data = 100000; + int sendbuf = 100; + int flags; + int res; + + flags = fcntl(self->fd, F_GETFL, 0); + fcntl(self->fd, F_SETFL, flags | O_NONBLOCK); + fcntl(self->cfd, F_SETFL, flags | O_NONBLOCK); + + /* Ensure nonblocking behavior by imposing a small send + * buffer. + */ + EXPECT_EQ(setsockopt(self->fd, SOL_SOCKET, SO_SNDBUF, + &sendbuf, sizeof(sendbuf)), 0); + + res = fork(); + EXPECT_NE(res, -1); + + if (res) { + /* parent */ + bool eagain = false; + size_t left = data; + char buf[16384]; + int status; + int pid2; + + while (left) { + int res = send(self->fd, buf, + left > 16384 ? 16384 : left, 0); + + if (res == -1 && errno == EAGAIN) { + eagain = true; + usleep(10000); + continue; + } + EXPECT_GE(res, 0); + left -= res; + } + + EXPECT_TRUE(eagain); + pid2 = wait(&status); + + EXPECT_EQ(status, 0); + EXPECT_EQ(res, pid2); + } else { + /* child */ + bool eagain = false; + size_t left = data; + char buf[16384]; + + while (left) { + int res = recv(self->cfd, buf, + left > 16384 ? 16384 : left, 0); + + if (res == -1 && errno == EAGAIN) { + eagain = true; + usleep(10000); + continue; + } + EXPECT_GE(res, 0); + left -= res; + } + EXPECT_TRUE(eagain); + } +} + +TEST_F(tls, control_msg) +{ + if (self->notls) + return; + + char cbuf[CMSG_SPACE(sizeof(char))]; + char const *test_str = "test_read"; + int cmsg_len = sizeof(char); + char record_type = 100; + struct cmsghdr *cmsg; + struct msghdr msg; + int send_len = 10; + struct iovec vec; + char buf[10]; + + vec.iov_base = (char *)test_str; + vec.iov_len = 10; + memset(&msg, 0, sizeof(struct msghdr)); + msg.msg_iov = &vec; + msg.msg_iovlen = 1; + msg.msg_control = cbuf; + msg.msg_controllen = sizeof(cbuf); + cmsg = CMSG_FIRSTHDR(&msg); + cmsg->cmsg_level = SOL_TLS; + /* test sending non-record types. */ + cmsg->cmsg_type = TLS_SET_RECORD_TYPE; + cmsg->cmsg_len = CMSG_LEN(cmsg_len); + *CMSG_DATA(cmsg) = record_type; + msg.msg_controllen = cmsg->cmsg_len; + + EXPECT_EQ(sendmsg(self->fd, &msg, 0), send_len); + /* Should fail because we didn't provide a control message */ + EXPECT_EQ(recv(self->cfd, buf, send_len, 0), -1); + + vec.iov_base = buf; + EXPECT_EQ(recvmsg(self->cfd, &msg, 0), send_len); + cmsg = CMSG_FIRSTHDR(&msg); + EXPECT_NE(cmsg, NULL); + EXPECT_EQ(cmsg->cmsg_level, SOL_TLS); + EXPECT_EQ(cmsg->cmsg_type, TLS_GET_RECORD_TYPE); + record_type = *((unsigned char *)CMSG_DATA(cmsg)); + EXPECT_EQ(record_type, 100); + EXPECT_EQ(memcmp(buf, test_str, send_len), 0); +} + +TEST_HARNESS_MAIN -- cgit v1.2.3-58-ga151 From b4b5bffd6c19b1005d2e4c90971bf562b8b4a55b Mon Sep 17 00:00:00 2001 From: Jakub Kicinski <jakub.kicinski@netronome.com> Date: Mon, 16 Jul 2018 10:57:15 -0700 Subject: tools: libbpf: remove libelf-getphdrnum feature detection libbpf does not depend on libelf-getphdrnum feature, don't check it. $ git grep HAVE_ELF_GETPHDRNUM_SUPPORT tools/perf/Makefile.config: CFLAGS += -DHAVE_ELF_GETPHDRNUM_SUPPORT tools/perf/util/symbol-elf.c:#ifndef HAVE_ELF_GETPHDRNUM_SUPPORT Signed-off-by: Jakub Kicinski <jakub.kicinski@netronome.com> Reviewed-by: Quentin Monnet <quentin.monnet@netronome.com> Acked-by: Daniel Borkmann <daniel@iogearbox.net> Signed-off-by: Alexei Starovoitov <ast@kernel.org> --- tools/lib/bpf/Makefile | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) (limited to 'tools') diff --git a/tools/lib/bpf/Makefile b/tools/lib/bpf/Makefile index 7a8e4c98ef1a..d49902e818b5 100644 --- a/tools/lib/bpf/Makefile +++ b/tools/lib/bpf/Makefile @@ -66,7 +66,7 @@ ifndef VERBOSE endif FEATURE_USER = .libbpf -FEATURE_TESTS = libelf libelf-getphdrnum libelf-mmap bpf reallocarray +FEATURE_TESTS = libelf libelf-mmap bpf reallocarray FEATURE_DISPLAY = libelf bpf INCLUDES = -I. -I$(srctree)/tools/include -I$(srctree)/tools/arch/$(ARCH)/include/uapi -I$(srctree)/tools/include/uapi -I$(srctree)/tools/perf @@ -116,10 +116,6 @@ ifeq ($(feature-libelf-mmap), 1) override CFLAGS += -DHAVE_LIBELF_MMAP_SUPPORT endif -ifeq ($(feature-libelf-getphdrnum), 1) - override CFLAGS += -DHAVE_ELF_GETPHDRNUM_SUPPORT -endif - ifeq ($(feature-reallocarray), 0) override CFLAGS += -DCOMPAT_NEED_REALLOCARRAY endif -- cgit v1.2.3-58-ga151 From dc989d2ce2c2bc4b5cdc92a9d2c8a5c857448475 Mon Sep 17 00:00:00 2001 From: Jakub Kicinski <jakub.kicinski@netronome.com> Date: Mon, 16 Jul 2018 10:57:16 -0700 Subject: tools: bpftool: don't pass FEATURES_DUMP to libbpf bpftool does not export features it probed for, i.e. FEATURE_DUMP_EXPORT is always empty, so don't try to communicate the features to libbpf. It has no effect. Signed-off-by: Jakub Kicinski <jakub.kicinski@netronome.com> Reviewed-by: Quentin Monnet <quentin.monnet@netronome.com> Acked-by: Daniel Borkmann <daniel@iogearbox.net> Signed-off-by: Alexei Starovoitov <ast@kernel.org> --- tools/bpf/bpftool/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'tools') diff --git a/tools/bpf/bpftool/Makefile b/tools/bpf/bpftool/Makefile index 6c4830e18879..74288a2197ab 100644 --- a/tools/bpf/bpftool/Makefile +++ b/tools/bpf/bpftool/Makefile @@ -26,7 +26,7 @@ LIBBPF = $(BPF_PATH)libbpf.a BPFTOOL_VERSION := $(shell make --no-print-directory -sC ../../.. kernelversion) $(LIBBPF): FORCE - $(Q)$(MAKE) -C $(BPF_DIR) OUTPUT=$(OUTPUT) $(OUTPUT)libbpf.a FEATURES_DUMP=$(FEATURE_DUMP_EXPORT) + $(Q)$(MAKE) -C $(BPF_DIR) OUTPUT=$(OUTPUT) $(OUTPUT)libbpf.a $(LIBBPF)-clean: $(call QUIET_CLEAN, libbpf) -- cgit v1.2.3-58-ga151 From d6d6071388e9fc81e11c6f4392666a9f072befe6 Mon Sep 17 00:00:00 2001 From: Jakub Kicinski <jakub.kicinski@netronome.com> Date: Tue, 17 Jul 2018 10:53:21 -0700 Subject: netdevsim: associate bound programs with shared dev Move bound program information from netdevsim to shared sub-object, as programs will soon be shared between netdevs of the same ASIC. Signed-off-by: Jakub Kicinski <jakub.kicinski@netronome.com> Reviewed-by: Quentin Monnet <quentin.monnet@netronome.com> Signed-off-by: Daniel Borkmann <daniel@iogearbox.net> --- drivers/net/netdevsim/bpf.c | 30 +++++++++++++++++------------ drivers/net/netdevsim/netdevsim.h | 11 ++++++----- tools/testing/selftests/bpf/test_offload.py | 5 +++-- 3 files changed, 27 insertions(+), 19 deletions(-) (limited to 'tools') diff --git a/drivers/net/netdevsim/bpf.c b/drivers/net/netdevsim/bpf.c index c36d2a768202..357f9e62f306 100644 --- a/drivers/net/netdevsim/bpf.c +++ b/drivers/net/netdevsim/bpf.c @@ -238,8 +238,8 @@ static int nsim_bpf_create_prog(struct netdevsim *ns, struct bpf_prog *prog) state->state = "verify"; /* Program id is not populated yet when we create the state. */ - sprintf(name, "%u", ns->prog_id_gen++); - state->ddir = debugfs_create_dir(name, ns->ddir_bpf_bound_progs); + sprintf(name, "%u", ns->sdev->prog_id_gen++); + state->ddir = debugfs_create_dir(name, ns->sdev->ddir_bpf_bound_progs); if (IS_ERR_OR_NULL(state->ddir)) { kfree(state); return -ENOMEM; @@ -250,7 +250,7 @@ static int nsim_bpf_create_prog(struct netdevsim *ns, struct bpf_prog *prog) &state->state, &nsim_bpf_string_fops); debugfs_create_bool("loaded", 0400, state->ddir, &state->is_loaded); - list_add_tail(&state->l, &ns->bpf_bound_progs); + list_add_tail(&state->l, &ns->sdev->bpf_bound_progs); prog->aux->offload->dev_priv = state; @@ -497,7 +497,7 @@ nsim_bpf_map_alloc(struct netdevsim *ns, struct bpf_offloaded_map *offmap) } offmap->dev_ops = &nsim_bpf_map_ops; - list_add_tail(&nmap->l, &ns->bpf_bound_maps); + list_add_tail(&nmap->l, &ns->sdev->bpf_bound_maps); return 0; @@ -582,8 +582,15 @@ int nsim_bpf(struct net_device *dev, struct netdev_bpf *bpf) int nsim_bpf_init(struct netdevsim *ns) { - INIT_LIST_HEAD(&ns->bpf_bound_progs); - INIT_LIST_HEAD(&ns->bpf_bound_maps); + if (ns->sdev->refcnt == 1) { + INIT_LIST_HEAD(&ns->sdev->bpf_bound_progs); + INIT_LIST_HEAD(&ns->sdev->bpf_bound_maps); + + ns->sdev->ddir_bpf_bound_progs = + debugfs_create_dir("bpf_bound_progs", ns->sdev->ddir); + if (IS_ERR_OR_NULL(ns->sdev->ddir_bpf_bound_progs)) + return -ENOMEM; + } debugfs_create_u32("bpf_offloaded_id", 0400, ns->ddir, &ns->bpf_offloaded_id); @@ -593,10 +600,6 @@ int nsim_bpf_init(struct netdevsim *ns) &ns->bpf_bind_accept); debugfs_create_u32("bpf_bind_verifier_delay", 0600, ns->ddir, &ns->bpf_bind_verifier_delay); - ns->ddir_bpf_bound_progs = - debugfs_create_dir("bpf_bound_progs", ns->ddir); - if (IS_ERR_OR_NULL(ns->ddir_bpf_bound_progs)) - return -ENOMEM; ns->bpf_tc_accept = true; debugfs_create_bool("bpf_tc_accept", 0600, ns->ddir, @@ -619,9 +622,12 @@ int nsim_bpf_init(struct netdevsim *ns) void nsim_bpf_uninit(struct netdevsim *ns) { - WARN_ON(!list_empty(&ns->bpf_bound_progs)); - WARN_ON(!list_empty(&ns->bpf_bound_maps)); WARN_ON(ns->xdp.prog); WARN_ON(ns->xdp_hw.prog); WARN_ON(ns->bpf_offloaded); + + if (ns->sdev->refcnt == 1) { + WARN_ON(!list_empty(&ns->sdev->bpf_bound_progs)); + WARN_ON(!list_empty(&ns->sdev->bpf_bound_maps)); + } } diff --git a/drivers/net/netdevsim/netdevsim.h b/drivers/net/netdevsim/netdevsim.h index 8743ce74d2d9..98f26fa1e671 100644 --- a/drivers/net/netdevsim/netdevsim.h +++ b/drivers/net/netdevsim/netdevsim.h @@ -35,6 +35,12 @@ struct netdevsim_shared_dev { u32 switch_id; struct dentry *ddir; + + struct dentry *ddir_bpf_bound_progs; + u32 prog_id_gen; + + struct list_head bpf_bound_progs; + struct list_head bpf_bound_maps; }; #define NSIM_IPSEC_MAX_SA_COUNT 33 @@ -79,12 +85,8 @@ struct netdevsim { struct xdp_attachment_info xdp; struct xdp_attachment_info xdp_hw; - u32 prog_id_gen; - bool bpf_bind_accept; u32 bpf_bind_verifier_delay; - struct dentry *ddir_bpf_bound_progs; - struct list_head bpf_bound_progs; bool bpf_tc_accept; bool bpf_tc_non_bound_accept; @@ -92,7 +94,6 @@ struct netdevsim { bool bpf_xdpoffload_accept; bool bpf_map_accept; - struct list_head bpf_bound_maps; #if IS_ENABLED(CONFIG_NET_DEVLINK) struct devlink *devlink; #endif diff --git a/tools/testing/selftests/bpf/test_offload.py b/tools/testing/selftests/bpf/test_offload.py index b746227eaff2..ee1abef384ea 100755 --- a/tools/testing/selftests/bpf/test_offload.py +++ b/tools/testing/selftests/bpf/test_offload.py @@ -314,6 +314,7 @@ class NetdevSim: self.ns = "" self.dfs_dir = '/sys/kernel/debug/netdevsim/%s' % (self.dev['ifname']) + self.sdev_dir = self.dfs_dir + '/sdev/' self.dfs_refresh() def __getitem__(self, key): @@ -345,12 +346,12 @@ class NetdevSim: return data.strip() def dfs_num_bound_progs(self): - path = os.path.join(self.dfs_dir, "bpf_bound_progs") + path = os.path.join(self.sdev_dir, "bpf_bound_progs") _, progs = cmd('ls %s' % (path)) return len(progs.split()) def dfs_get_bound_progs(self, expected): - progs = DebugfsDir(os.path.join(self.dfs_dir, "bpf_bound_progs")) + progs = DebugfsDir(os.path.join(self.sdev_dir, "bpf_bound_progs")) if expected is not None: if len(progs) != expected: fail(True, "%d BPF programs bound, expected %d" % -- cgit v1.2.3-58-ga151 From 7736b6ed665a8a339f6499f4b0f162386d46fd86 Mon Sep 17 00:00:00 2001 From: Jakub Kicinski <jakub.kicinski@netronome.com> Date: Tue, 17 Jul 2018 10:53:29 -0700 Subject: selftests/bpf: add test for sharing objects between netdevs Add tests for sharing programs and maps between different netdevs. Use netdevsim's ability to pretend multiple netdevs belong to the same "ASIC". Signed-off-by: Jakub Kicinski <jakub.kicinski@netronome.com> Reviewed-by: Quentin Monnet <quentin.monnet@netronome.com> Signed-off-by: Daniel Borkmann <daniel@iogearbox.net> --- tools/testing/selftests/bpf/test_offload.py | 146 +++++++++++++++++++++++++++- 1 file changed, 142 insertions(+), 4 deletions(-) (limited to 'tools') diff --git a/tools/testing/selftests/bpf/test_offload.py b/tools/testing/selftests/bpf/test_offload.py index ee1abef384ea..d59642e70f56 100755 --- a/tools/testing/selftests/bpf/test_offload.py +++ b/tools/testing/selftests/bpf/test_offload.py @@ -158,8 +158,9 @@ def tool(name, args, flags, JSON=True, ns="", fail=True, include_stderr=False): else: return ret, out -def bpftool(args, JSON=True, ns="", fail=True): - return tool("bpftool", args, {"json":"-p"}, JSON=JSON, ns=ns, fail=fail) +def bpftool(args, JSON=True, ns="", fail=True, include_stderr=False): + return tool("bpftool", args, {"json":"-p"}, JSON=JSON, ns=ns, + fail=fail, include_stderr=include_stderr) def bpftool_prog_list(expected=None, ns=""): _, progs = bpftool("prog show", JSON=True, ns=ns, fail=True) @@ -201,6 +202,21 @@ def bpftool_map_list_wait(expected=0, n_retry=20): time.sleep(0.05) raise Exception("Time out waiting for map counts to stabilize want %d, have %d" % (expected, nmaps)) +def bpftool_prog_load(sample, file_name, maps=[], prog_type="xdp", dev=None, + fail=True, include_stderr=False): + args = "prog load %s %s" % (os.path.join(bpf_test_dir, sample), file_name) + if prog_type is not None: + args += " type " + prog_type + if dev is not None: + args += " dev " + dev + if len(maps): + args += " map " + " map ".join(maps) + + res = bpftool(args, fail=fail, include_stderr=include_stderr) + if res[0] == 0: + files.append(file_name) + return res + def ip(args, force=False, JSON=True, ns="", fail=True, include_stderr=False): if force: args = "-force " + args @@ -307,7 +323,9 @@ class NetdevSim: Class for netdevsim netdevice and its attributes. """ - def __init__(self): + def __init__(self, link=None): + self.link = link + self.dev = self._netdevsim_create() devs.append(self) @@ -321,8 +339,9 @@ class NetdevSim: return self.dev[key] def _netdevsim_create(self): + link = "" if self.link is None else "link " + self.link.dev['ifname'] _, old = ip("link show") - ip("link add sim%d type netdevsim") + ip("link add sim%d {link} type netdevsim".format(link=link)) _, new = ip("link show") for dev in new: @@ -848,6 +867,25 @@ try: sim.set_mtu(1500) sim.wait_for_flush() + start_test("Test non-offload XDP attaching to HW...") + bpftool_prog_load("sample_ret0.o", "/sys/fs/bpf/nooffload") + nooffload = bpf_pinned("/sys/fs/bpf/nooffload") + ret, _, err = sim.set_xdp(nooffload, "offload", + fail=False, include_stderr=True) + fail(ret == 0, "attached non-offloaded XDP program to HW") + check_extack_nsim(err, "xdpoffload of non-bound program.", args) + rm("/sys/fs/bpf/nooffload") + + start_test("Test offload XDP attaching to drv...") + bpftool_prog_load("sample_ret0.o", "/sys/fs/bpf/offload", + dev=sim['ifname']) + offload = bpf_pinned("/sys/fs/bpf/offload") + ret, _, err = sim.set_xdp(offload, "drv", fail=False, include_stderr=True) + fail(ret == 0, "attached offloaded XDP program to drv") + check_extack(err, "using device-bound program without HW_MODE flag is not supported.", args) + rm("/sys/fs/bpf/offload") + sim.wait_for_flush() + start_test("Test XDP offload...") _, _, err = sim.set_xdp(obj, "offload", verbose=True, include_stderr=True) ipl = sim.ip_link_show(xdp=True) @@ -1141,6 +1179,106 @@ try: fail(ret == 0, "netdevsim didn't refuse to create a map with offload disabled") + sim.remove() + + start_test("Test multi-dev ASIC program reuse...") + simA = NetdevSim() + simB1 = NetdevSim() + simB2 = NetdevSim(link=simB1) + simB3 = NetdevSim(link=simB1) + sims = (simA, simB1, simB2, simB3) + simB = (simB1, simB2, simB3) + + bpftool_prog_load("sample_map_ret0.o", "/sys/fs/bpf/nsimA", + dev=simA['ifname']) + progA = bpf_pinned("/sys/fs/bpf/nsimA") + bpftool_prog_load("sample_map_ret0.o", "/sys/fs/bpf/nsimB", + dev=simB1['ifname']) + progB = bpf_pinned("/sys/fs/bpf/nsimB") + + simA.set_xdp(progA, "offload", JSON=False) + for d in simB: + d.set_xdp(progB, "offload", JSON=False) + + start_test("Test multi-dev ASIC cross-dev replace...") + ret, _ = simA.set_xdp(progB, "offload", force=True, JSON=False, fail=False) + fail(ret == 0, "cross-ASIC program allowed") + for d in simB: + ret, _ = d.set_xdp(progA, "offload", force=True, JSON=False, fail=False) + fail(ret == 0, "cross-ASIC program allowed") + + start_test("Test multi-dev ASIC cross-dev install...") + for d in sims: + d.unset_xdp("offload") + + ret, _, err = simA.set_xdp(progB, "offload", force=True, JSON=False, + fail=False, include_stderr=True) + fail(ret == 0, "cross-ASIC program allowed") + check_extack_nsim(err, "program bound to different dev.", args) + for d in simB: + ret, _, err = d.set_xdp(progA, "offload", force=True, JSON=False, + fail=False, include_stderr=True) + fail(ret == 0, "cross-ASIC program allowed") + check_extack_nsim(err, "program bound to different dev.", args) + + start_test("Test multi-dev ASIC cross-dev map reuse...") + + mapA = bpftool("prog show %s" % (progA))[1]["map_ids"][0] + mapB = bpftool("prog show %s" % (progB))[1]["map_ids"][0] + + ret, _ = bpftool_prog_load("sample_map_ret0.o", "/sys/fs/bpf/nsimB_", + dev=simB3['ifname'], + maps=["idx 0 id %d" % (mapB)], + fail=False) + fail(ret != 0, "couldn't reuse a map on the same ASIC") + rm("/sys/fs/bpf/nsimB_") + + ret, _, err = bpftool_prog_load("sample_map_ret0.o", "/sys/fs/bpf/nsimA_", + dev=simA['ifname'], + maps=["idx 0 id %d" % (mapB)], + fail=False, include_stderr=True) + fail(ret == 0, "could reuse a map on a different ASIC") + fail(err.count("offload device mismatch between prog and map") == 0, + "error message missing for cross-ASIC map") + + ret, _, err = bpftool_prog_load("sample_map_ret0.o", "/sys/fs/bpf/nsimB_", + dev=simB1['ifname'], + maps=["idx 0 id %d" % (mapA)], + fail=False, include_stderr=True) + fail(ret == 0, "could reuse a map on a different ASIC") + fail(err.count("offload device mismatch between prog and map") == 0, + "error message missing for cross-ASIC map") + + start_test("Test multi-dev ASIC cross-dev destruction...") + bpftool_prog_list_wait(expected=2) + + simA.remove() + bpftool_prog_list_wait(expected=1) + + ifnameB = bpftool("prog show %s" % (progB))[1]["dev"]["ifname"] + fail(ifnameB != simB1['ifname'], "program not bound to originial device") + simB1.remove() + bpftool_prog_list_wait(expected=1) + + start_test("Test multi-dev ASIC cross-dev destruction - move...") + ifnameB = bpftool("prog show %s" % (progB))[1]["dev"]["ifname"] + fail(ifnameB not in (simB2['ifname'], simB3['ifname']), + "program not bound to remaining devices") + + simB2.remove() + ifnameB = bpftool("prog show %s" % (progB))[1]["dev"]["ifname"] + fail(ifnameB != simB3['ifname'], "program not bound to remaining device") + + simB3.remove() + bpftool_prog_list_wait(expected=0) + + start_test("Test multi-dev ASIC cross-dev destruction - orphaned...") + ret, out = bpftool("prog show %s" % (progB), fail=False) + fail(ret == 0, "got information about orphaned program") + fail("error" not in out, "no error reported for get info on orphaned") + fail(out["error"] != "can't get prog info: No such device", + "wrong error for get info on orphaned") + print("%s: OK" % (os.path.basename(__file__))) finally: -- cgit v1.2.3-58-ga151 From 088cbac6bea4d383b9e01f042b50fe00898714f5 Mon Sep 17 00:00:00 2001 From: Keara Leibovitz <kleib@mojatatu.com> Date: Tue, 17 Jul 2018 12:12:54 -0400 Subject: tc-tests: initial version of fw filter unit tests Create initial unit tests for the tc fw filter. Signed-off-by: Keara Leibovitz <kleib@mojatatu.com> Signed-off-by: David S. Miller <davem@davemloft.net> --- .../selftests/tc-testing/tc-tests/filters/fw.json | 1049 ++++++++++++++++++++ 1 file changed, 1049 insertions(+) create mode 100644 tools/testing/selftests/tc-testing/tc-tests/filters/fw.json (limited to 'tools') diff --git a/tools/testing/selftests/tc-testing/tc-tests/filters/fw.json b/tools/testing/selftests/tc-testing/tc-tests/filters/fw.json new file mode 100644 index 000000000000..3b97cfd7e0f8 --- /dev/null +++ b/tools/testing/selftests/tc-testing/tc-tests/filters/fw.json @@ -0,0 +1,1049 @@ +[ + { + "id": "901f", + "name": "Add fw filter with prio at 32-bit maxixum", + "category": [ + "filter", + "fw" + ], + "setup": [ + "$TC qdisc add dev $DEV1 ingress" + ], + "cmdUnderTest": "$TC filter add dev $DEV1 parent ffff: handle 1 prio 65535 fw action ok", + "expExitCode": "0", + "verifyCmd": "$TC filter get dev $DEV1 parent ffff: handle 1 prio 65535 protocol all fw", + "matchPattern": "pref 65535 fw.*handle 0x1.*gact action pass", + "matchCount": "1", + "teardown": [ + "$TC qdisc del dev $DEV1 ingress" + ] + }, + { + "id": "51e2", + "name": "Add fw filter with prio exceeding 32-bit maxixum", + "category": [ + "filter", + "fw" + ], + "setup": [ + "$TC qdisc add dev $DEV1 ingress" + ], + "cmdUnderTest": "$TC filter add dev $DEV1 parent ffff: handle 1 prio 65536 fw action ok", + "expExitCode": "255", + "verifyCmd": "$TC filter get dev $DEV1 parent ffff: handle 1 prio 65536 protocol all fw", + "matchPattern": "pref 65536 fw.*handle 0x1.*gact action pass", + "matchCount": "0", + "teardown": [ + "$TC qdisc del dev $DEV1 ingress" + ] + }, + { + "id": "d987", + "name": "Add fw filter with action ok", + "category": [ + "filter", + "fw" + ], + "setup": [ + "$TC qdisc add dev $DEV1 ingress" + ], + "cmdUnderTest": "$TC filter add dev $DEV1 parent ffff: handle 1 prio 1 fw action ok", + "expExitCode": "0", + "verifyCmd": "$TC filter get dev $DEV1 parent ffff: handle 1 prio 1 protocol all fw", + "matchPattern": "handle 0x1.*gact action pass", + "matchCount": "1", + "teardown": [ + "$TC qdisc del dev $DEV1 ingress" + ] + }, + { + "id": "affe", + "name": "Add fw filter with action continue", + "category": [ + "filter", + "fw" + ], + "setup": [ + "$TC qdisc add dev $DEV1 ingress" + ], + "cmdUnderTest": "$TC filter add dev $DEV1 parent ffff: handle 1 prio 1 fw action continue", + "expExitCode": "0", + "verifyCmd": "$TC filter get dev $DEV1 parent ffff: handle 1 prio 1 protocol all fw", + "matchPattern": "handle 0x1.*gact action continue", + "matchCount": "1", + "teardown": [ + "$TC qdisc del dev $DEV1 ingress" + ] + }, + { + "id": "28bc", + "name": "Add fw filter with action pipe", + "category": [ + "filter", + "fw" + ], + "setup": [ + "$TC qdisc add dev $DEV1 ingress" + ], + "cmdUnderTest": "$TC filter add dev $DEV1 parent ffff: handle 1 prio 1 fw action pipe", + "expExitCode": "0", + "verifyCmd": "$TC filter get dev $DEV1 parent ffff: handle 1 prio 1 protocol all fw", + "matchPattern": "handle 0x1.*gact action pipe", + "matchCount": "1", + "teardown": [ + "$TC qdisc del dev $DEV1 ingress" + ] + }, + { + "id": "8da2", + "name": "Add fw filter with action drop", + "category": [ + "filter", + "fw" + ], + "setup": [ + "$TC qdisc add dev $DEV1 ingress" + ], + "cmdUnderTest": "$TC filter add dev $DEV1 parent ffff: handle 1 prio 1 fw action drop", + "expExitCode": "0", + "verifyCmd": "$TC filter get dev $DEV1 parent ffff: handle 1 protocol all prio 1 fw", + "matchPattern": "handle 0x1.*gact action drop", + "matchCount": "1", + "teardown": [ + "$TC qdisc del dev $DEV1 ingress" + ] + }, + { + "id": "9436", + "name": "Add fw filter with action reclassify", + "category": [ + "filter", + "fw" + ], + "setup": [ + "$TC qdisc add dev $DEV1 ingress" + ], + "cmdUnderTest": "$TC filter add dev $DEV1 parent ffff: handle 1 prio 1 fw action reclassify", + "expExitCode": "0", + "verifyCmd": "$TC filter get dev $DEV1 parent ffff: handle 1 prio 1 protocol all fw", + "matchPattern": "handle 0x1.*gact action reclassify", + "matchCount": "1", + "teardown": [ + "$TC qdisc del dev $DEV1 ingress" + ] + }, + { + "id": "95bb", + "name": "Add fw filter with action jump 10", + "category": [ + "filter", + "fw" + ], + "setup": [ + "$TC qdisc add dev $DEV1 ingress" + ], + "cmdUnderTest": "$TC filter add dev $DEV1 parent ffff: handle 1 prio 1 fw action jump 10", + "expExitCode": "0", + "verifyCmd": "$TC filter get dev $DEV1 parent ffff: handle 1 prio 1 protocol all fw", + "matchPattern": "handle 0x1.*gact action jump 10", + "matchCount": "1", + "teardown": [ + "$TC qdisc del dev $DEV1 ingress" + ] + }, + { + "id": "3d74", + "name": "Add fw filter with action goto chain 5", + "category": [ + "filter", + "fw" + ], + "setup": [ + "$TC qdisc add dev $DEV1 ingress" + ], + "cmdUnderTest": "$TC filter add dev $DEV1 parent ffff: handle 1 prio 1 fw action goto chain 5", + "expExitCode": "0", + "verifyCmd": "$TC filter get dev $DEV1 parent ffff: handle 1 prio 1 protocol all fw", + "matchPattern": "handle 0x1.*gact action goto chain 5", + "matchCount": "1", + "teardown": [ + "$TC qdisc del dev $DEV1 ingress" + ] + }, + { + "id": "eb8f", + "name": "Add fw filter with invalid action", + "category": [ + "filter", + "fw" + ], + "setup": [ + "$TC qdisc add dev $DEV1 ingress" + ], + "cmdUnderTest": "$TC filter add dev $DEV1 parent ffff: handle 1 prio 1 fw action pump", + "expExitCode": "255", + "verifyCmd": "$TC filter get dev $DEV1 parent ffff: handle 1 prio 1 protocol all fw", + "matchPattern": "handle 0x1.*gact action pump", + "matchCount": "0", + "teardown": [ + "$TC qdisc del dev $DEV1 ingress" + ] + }, + { + "id": "6a79", + "name": "Add fw filter with missing mandatory action", + "category": [ + "filter", + "fw" + ], + "setup": [ + "$TC qdisc add dev $DEV1 ingress" + ], + "cmdUnderTest": "$TC filter add dev $DEV1 parent ffff: handle 1 prio 1 fw", + "expExitCode": "2", + "verifyCmd": "$TC filter get dev $DEV1 parent ffff: handle 1 prio 1 protocol all fw", + "matchPattern": "filter protocol all pref [0-9]+ fw.*handle 0x1", + "matchCount": "0", + "teardown": [ + "$TC qdisc del dev $DEV1 ingress" + ] + }, + { + "id": "8298", + "name": "Add fw filter with cookie", + "category": [ + "filter", + "fw" + ], + "setup": [ + "$TC qdisc add dev $DEV1 ingress" + ], + "cmdUnderTest": "$TC filter add dev $DEV1 parent ffff: handle 1 prio 2 fw action pipe cookie aa11bb22cc33dd44ee55ff66aa11b1b2", + "expExitCode": "0", + "verifyCmd": "$TC filter get dev $DEV1 parent ffff: handle 1 prio 2 protocol all fw", + "matchPattern": "pref 2 fw.*handle 0x1.*gact action pipe.*cookie aa11bb22cc33dd44ee55ff66aa11b1b2", + "matchCount": "1", + "teardown": [ + "$TC qdisc del dev $DEV1 ingress" + ] + }, + { + "id": "a88c", + "name": "Add fw filter with invalid cookie", + "category": [ + "filter", + "fw" + ], + "setup": [ + "$TC qdisc add dev $DEV1 ingress" + ], + "cmdUnderTest": "$TC filter add dev $DEV1 parent ffff: handle 1 prio 2 fw action continue cookie aa11bb22cc33dd44ee55ff66aa11b1b2777888", + "expExitCode": "255", + "verifyCmd": "$TC filter get dev $DEV1 parent ffff: handle 1 prio 2 protocol all fw", + "matchPattern": "pref 2 fw.*handle 0x1.*gact action continue.*cookie aa11bb22cc33dd44ee55ff66aa11b1b2777888", + "matchCount": "0", + "teardown": [ + "$TC qdisc del dev $DEV1 ingress" + ] + }, + { + "id": "10f6", + "name": "Add fw filter with handle in hex", + "category": [ + "filter", + "fw" + ], + "setup": [ + "$TC qdisc add dev $DEV1 ingress" + ], + "cmdUnderTest": "$TC filter add dev $DEV1 parent ffff: handle 0xa1b2ff prio 1 fw action ok", + "expExitCode": "0", + "verifyCmd": "$TC filter get dev $DEV1 parent ffff: handle 0xa1b2ff prio 1 protocol all fw", + "matchPattern": "fw.*handle 0xa1b2ff.*gact action pass", + "matchCount": "1", + "teardown": [ + "$TC qdisc del dev $DEV1 ingress" + ] + }, + { + "id": "9d51", + "name": "Add fw filter with handle at 32-bit maximum", + "category": [ + "filter", + "fw" + ], + "setup": [ + "$TC qdisc add dev $DEV1 ingress" + ], + "cmdUnderTest": "$TC filter add dev $DEV1 parent ffff: handle 4294967295 prio 1 fw action ok", + "expExitCode": "0", + "verifyCmd": "$TC filter get dev $DEV1 parent ffff: handle 4294967295 prio 1 protocol all fw", + "matchPattern": "fw.*handle 0xffffffff.*gact action pass", + "matchCount": "1", + "teardown": [ + "$TC qdisc del dev $DEV1 ingress" + ] + }, + { + "id": "d939", + "name": "Add fw filter with handle exceeding 32-bit maximum", + "category": [ + "filter", + "fw" + ], + "setup": [ + "$TC qdisc add dev $DEV1 ingress" + ], + "cmdUnderTest": "$TC filter add dev $DEV1 parent ffff: handle 4294967296 prio 1 fw action ok", + "expExitCode": "1", + "verifyCmd": "$TC filter get dev $DEV1 parent ffff: handle 4294967296 prio 1 protocol all fw", + "matchPattern": "fw.*handle 0x.*gact action pass", + "matchCount": "0", + "teardown": [ + "$TC qdisc del dev $DEV1 ingress" + ] + }, + { + "id": "658c", + "name": "Add fw filter with mask in hex", + "category": [ + "filter", + "fw" + ], + "setup": [ + "$TC qdisc add dev $DEV1 ingress" + ], + "cmdUnderTest": "$TC filter add dev $DEV1 parent ffff: handle 10/0xa1b2f prio 1 fw action ok", + "expExitCode": "0", + "verifyCmd": "$TC filter get dev $DEV1 parent ffff: handle 10 prio 1 protocol all fw", + "matchPattern": "fw.*handle 0xa/0xa1b2f", + "matchCount": "1", + "teardown": [ + "$TC qdisc del dev $DEV1 ingress" + ] + }, + { + "id": "86be", + "name": "Add fw filter with mask at 32-bit maximum", + "category": [ + "filter", + "fw" + ], + "setup": [ + "$TC qdisc add dev $DEV1 ingress" + ], + "cmdUnderTest": "$TC filter add dev $DEV1 parent ffff: handle 10/4294967295 prio 1 fw action ok", + "expExitCode": "0", + "verifyCmd": "$TC filter get dev $DEV1 parent ffff: handle 10 prio 1 protocol all fw", + "matchPattern": "fw.*handle 0xa[^/]", + "matchCount": "1", + "teardown": [ + "$TC qdisc del dev $DEV1 ingress" + ] + }, + { + "id": "e635", + "name": "Add fw filter with mask exceeding 32-bit maximum", + "category": [ + "filter", + "fw" + ], + "setup": [ + "$TC qdisc add dev $DEV1 ingress" + ], + "cmdUnderTest": "$TC filter add dev $DEV1 parent ffff: handle 10/4294967296 prio 1 fw action ok", + "expExitCode": "1", + "verifyCmd": "$TC filter get dev $DEV1 parent ffff: handle 10 prio 1 protocol all fw", + "matchPattern": "fw.*handle 0xa", + "matchCount": "0", + "teardown": [ + "$TC qdisc del dev $DEV1 ingress" + ] + }, + { + "id": "6cab", + "name": "Add fw filter with handle/mask in hex", + "category": [ + "filter", + "fw" + ], + "setup": [ + "$TC qdisc add dev $DEV1 ingress" + ], + "cmdUnderTest": "$TC filter add dev $DEV1 parent ffff: handle 0xa1b2cdff/0x1a2bffdc prio 1 fw action ok", + "expExitCode": "0", + "verifyCmd": "$TC filter get dev $DEV1 parent ffff: handle 0xa1b2cdff prio 1 protocol all fw", + "matchPattern": "fw.*handle 0xa1b2cdff/0x1a2bffdc", + "matchCount": "1", + "teardown": [ + "$TC qdisc del dev $DEV1 ingress" + ] + }, + { + "id": "8700", + "name": "Add fw filter with handle/mask at 32-bit maximum", + "category": [ + "filter", + "fw" + ], + "setup": [ + "$TC qdisc add dev $DEV1 ingress" + ], + "cmdUnderTest": "$TC filter add dev $DEV1 parent ffff: handle 4294967295/4294967295 prio 1 fw action ok", + "expExitCode": "0", + "verifyCmd": "$TC filter get dev $DEV1 parent ffff: handle 0xffffffff prio 1 protocol all fw", + "matchPattern": "fw.*handle 0xffffffff[^/]", + "matchCount": "1", + "teardown": [ + "$TC qdisc del dev $DEV1 ingress" + ] + }, + { + "id": "7d62", + "name": "Add fw filter with handle/mask exceeding 32-bit maximum", + "category": [ + "filter", + "fw" + ], + "setup": [ + "$TC qdisc add dev $DEV1 ingress" + ], + "cmdUnderTest": "$TC filter add dev $DEV1 parent ffff: handle 4294967296/4294967296 prio 1 fw action ok", + "expExitCode": "1", + "verifyCmd": "$TC filter get dev $DEV1 parent ffff: handle 10 prio 1 protocol all fw", + "matchPattern": "fw.*handle", + "matchCount": "0", + "teardown": [ + "$TC qdisc del dev $DEV1 ingress" + ] + }, + { + "id": "7b69", + "name": "Add fw filter with missing mandatory handle", + "category": [ + "filter", + "fw" + ], + "setup": [ + "$TC qdisc add dev $DEV1 ingress" + ], + "cmdUnderTest": "$TC filter add dev $DEV1 parent ffff: prio 1 fw action ok", + "expExitCode": "2", + "verifyCmd": "$TC filter show dev $DEV1 parent ffff:", + "matchPattern": "filter protocol all.*fw.*handle.*gact action pass", + "matchCount": "0", + "teardown": [ + "$TC qdisc del dev $DEV1 ingress" + ] + }, + { + "id": "d68b", + "name": "Add fw filter with invalid parent", + "category": [ + "filter", + "fw" + ], + "setup": [ + "$TC qdisc add dev $DEV1 ingress" + ], + "cmdUnderTest": "$TC filter add dev $DEV1 parent aa11b1b2: handle 1 prio 1 fw action ok", + "expExitCode": "255", + "verifyCmd": "$TC filter dev $DEV1 parent aa11b1b2: handle 1 prio 1 protocol all fw", + "matchPattern": "filter protocol all pref 1 fw.*handle 0x1.*gact action pass", + "matchCount": "0", + "teardown": [ + "$TC qdisc del dev $DEV1 ingress" + ] + }, + { + "id": "66e0", + "name": "Add fw filter with missing mandatory parent id", + "category": [ + "filter", + "fw" + ], + "setup": [ + "$TC qdisc add dev $DEV1 ingress" + ], + "cmdUnderTest": "$TC filter add dev $DEV1 handle 1 prio 1 fw action ok", + "expExitCode": "2", + "verifyCmd": "$TC filter get dev $DEV1 parent ffff: handle 1 prio 1 protocol all fw", + "matchPattern": "pref [0-9]+ fw.*handle 0x1.*gact action pass", + "matchCount": "0", + "teardown": [ + "$TC qdisc del dev $DEV1 ingress" + ] + }, + { + "id": "0ff3", + "name": "Add fw filter with classid", + "category": [ + "filter", + "fw" + ], + "setup": [ + "$TC qdisc add dev $DEV1 ingress" + ], + "cmdUnderTest": "$TC filter add dev $DEV1 parent ffff: handle 1 prio 1 fw classid 3 action ok", + "expExitCode": "0", + "verifyCmd": "$TC filter get dev $DEV1 parent ffff: handle 1 prio 1 protocol all fw", + "matchPattern": "fw.*handle 0x1 classid :3.*gact action pass", + "matchCount": "1", + "teardown": [ + "$TC qdisc del dev $DEV1 ingress" + ] + }, + { + "id": "9849", + "name": "Add fw filter with classid at root", + "category": [ + "filter", + "fw" + ], + "setup": [ + "$TC qdisc add dev $DEV1 ingress" + ], + "cmdUnderTest": "$TC filter add dev $DEV1 parent ffff: handle 1 prio 1 fw classid ffff:ffff action ok", + "expExitCode": "0", + "verifyCmd": "$TC filter get dev $DEV1 parent ffff: handle 1 prio 1 protocol all fw", + "matchPattern": "pref 1 fw.*handle 0x1 classid root.*gact action pass", + "matchCount": "1", + "teardown": [ + "$TC qdisc del dev $DEV1 ingress" + ] + }, + { + "id": "b7ff", + "name": "Add fw filter with classid - keeps last 8 (hex) digits", + "category": [ + "filter", + "fw" + ], + "setup": [ + "$TC qdisc add dev $DEV1 ingress" + ], + "cmdUnderTest": "$TC filter add dev $DEV1 parent ffff: handle 1 prio 1 fw classid 98765fedcb action ok", + "expExitCode": "0", + "verifyCmd": "$TC filter get dev $DEV1 parent ffff: handle 1 prio 1 protocol all fw", + "matchPattern": "fw.*handle 0x1 classid 765f:edcb.*gact action pass", + "matchCount": "1", + "teardown": [ + "$TC qdisc del dev $DEV1 ingress" + ] + }, + { + "id": "2b18", + "name": "Add fw filter with invalid classid", + "category": [ + "filter", + "fw" + ], + "setup": [ + "$TC qdisc add dev $DEV1 ingress" + ], + "cmdUnderTest": "$TC filter add dev $DEV1 parent ffff: handle 1 prio 1 fw classid 6789defg action ok", + "expExitCode": "1", + "verifyCmd": "$TC filter get dev $DEV1 parent ffff: handle 1 prio 1 protocol all fw", + "matchPattern": "fw.*handle 0x1 classid 6789:defg.*gact action pass", + "matchCount": "0", + "teardown": [ + "$TC qdisc del dev $DEV1 ingress" + ] + }, + { + "id": "fade", + "name": "Add fw filter with flowid", + "category": [ + "filter", + "fw" + ], + "setup": [ + "$TC qdisc add dev $DEV1 ingress" + ], + "cmdUnderTest": "$TC filter add dev $DEV1 parent ffff: handle 10 prio 1 fw flowid 1:10 action ok", + "expExitCode": "0", + "verifyCmd": "$TC filter get dev $DEV1 parent ffff: handle 10 prio 1 protocol all fw", + "matchPattern": "filter parent ffff: protocol all pref 1 fw.*handle 0xa classid 1:10.*gact action pass", + "matchCount": "1", + "teardown": [ + "$TC qdisc del dev $DEV1 ingress" + ] + }, + { + "id": "33af", + "name": "Add fw filter with flowid then classid (same arg, takes second)", + "category": [ + "filter", + "fw" + ], + "setup": [ + "$TC qdisc add dev $DEV1 ingress" + ], + "cmdUnderTest": "$TC filter add dev $DEV1 parent ffff: handle 11 prio 1 fw flowid 10 classid 4 action ok", + "expExitCode": "0", + "verifyCmd": "$TC filter get dev $DEV1 parent ffff: handle 11 prio 1 protocol all fw", + "matchPattern": "filter parent ffff: protocol all pref 1 fw.*handle 0xb classid :4.*gact action pass", + "matchCount": "1", + "teardown": [ + "$TC qdisc del dev $DEV1 ingress" + ] + }, + { + "id": "8a8c", + "name": "Add fw filter with classid then flowid (same arg, takes second)", + "category": [ + "filter", + "fw" + ], + "setup": [ + "$TC qdisc add dev $DEV1 ingress" + ], + "cmdUnderTest": "$TC filter add dev $DEV1 parent ffff: handle 11 prio 1 fw classid 4 flowid 10 action ok", + "expExitCode": "0", + "verifyCmd": "$TC filter get dev $DEV1 parent ffff: handle 11 prio 1 protocol all fw", + "matchPattern": "filter parent ffff: protocol all pref 1 fw.*handle 0xb classid :10.*gact action pass", + "matchCount": "1", + "teardown": [ + "$TC qdisc del dev $DEV1 ingress" + ] + }, + { + "id": "b50d", + "name": "Add fw filter with handle val/mask and flowid 10:1000", + "category": [ + "filter", + "fw" + ], + "setup": [ + "$TC qdisc add dev $DEV1 ingress" + ], + "cmdUnderTest": "$TC filter add dev $DEV1 parent ffff: prio 3 handle 10/0xff fw flowid 10:1000 action ok", + "expExitCode": "0", + "verifyCmd": "$TC filter get dev $DEV1 parent ffff: handle 10 prio 3 protocol all fw", + "matchPattern": "filter parent ffff: protocol all pref 3 fw.*handle 0xa/0xff classid 10:1000.*gact action pass", + "matchCount": "1", + "teardown": [ + "$TC qdisc del dev $DEV1 ingress" + ] + }, + { + "id": "7207", + "name": "Add fw filter with protocol ip", + "category": [ + "filter", + "fw" + ], + "setup": [ + "$TC qdisc add dev $DEV1 ingress" + ], + "cmdUnderTest": "$TC filter add dev $DEV1 parent ffff: protocol ip prio 1 handle 3 fw action ok", + "expExitCode": "0", + "verifyCmd": "$TC filter get dev $DEV1 parent ffff: handle 3 prio 1 protocol ip fw", + "matchPattern": "filter parent ffff: protocol ip pref 1 fw.*handle 0x3.*gact action pass.*index [0-9]+ ref [0-9]+ bind [0-9]+", + "matchCount": "1", + "teardown": [ + "$TC qdisc del dev $DEV1 ingress" + ] + }, + { + "id": "306d", + "name": "Add fw filter with protocol ipv6", + "category": [ + "filter", + "fw" + ], + "setup": [ + "$TC qdisc add dev $DEV1 ingress" + ], + "cmdUnderTest": "$TC filter add dev $DEV1 parent ffff: protocol ipv6 prio 2 handle 4 fw action ok", + "expExitCode": "0", + "verifyCmd": "$TC filter get dev $DEV1 parent ffff: handle 4 prio 2 protocol ipv6 fw", + "matchPattern": "filter parent ffff: protocol ipv6 pref 2 fw.*handle 0x4.*gact action pass.*index [0-9]+ ref [0-9]+ bind [0-9]+", + "matchCount": "1", + "teardown": [ + "$TC qdisc del dev $DEV1 ingress" + ] + }, + { + "id": "9a78", + "name": "Add fw filter with protocol arp", + "category": [ + "filter", + "fw" + ], + "setup": [ + "$TC qdisc add dev $DEV1 ingress" + ], + "cmdUnderTest": "$TC filter add dev $DEV1 parent ffff: protocol arp prio 5 handle 7 fw action drop", + "expExitCode": "0", + "verifyCmd": "$TC filter get dev $DEV1 parent ffff: handle 7 prio 5 protocol arp fw", + "matchPattern": "filter parent ffff: protocol arp pref 5 fw.*handle 0x7.*gact action drop.*index [0-9]+ ref [0-9]+ bind [0-9]+", + "matchCount": "1", + "teardown": [ + "$TC qdisc del dev $DEV1 ingress" + ] + }, + { + "id": "1821", + "name": "Add fw filter with protocol 802_3", + "category": [ + "filter", + "fw" + ], + "setup": [ + "$TC qdisc add dev $DEV1 ingress" + ], + "cmdUnderTest": "$TC filter add dev $DEV1 parent ffff: protocol 802_3 handle 1 prio 1 fw action ok", + "expExitCode": "0", + "verifyCmd": "$TC filter get dev $DEV1 parent ffff: handle 1 prio 1 protocol 802_3 fw", + "matchPattern": "filter parent ffff: protocol 802_3 pref 1 fw.*handle 0x1.*gact action pass", + "matchCount": "1", + "teardown": [ + "$TC qdisc del dev $DEV1 ingress" + ] + }, + { + "id": "2260", + "name": "Add fw filter with invalid protocol", + "category": [ + "filter", + "fw" + ], + "setup": [ + "$TC qdisc add dev $DEV1 ingress" + ], + "cmdUnderTest": "$TC filter add dev $DEV1 parent ffff: protocol igmp handle 1 prio 1 fw action ok", + "expExitCode": "255", + "verifyCmd": "$TC filter get dev $DEV1 parent ffff: handle 1 prio 1 protocol igmp fw", + "matchPattern": "filter parent ffff: protocol igmp pref 1 fw.*handle 0x1.*gact action pass", + "matchCount": "0", + "teardown": [ + "$TC qdisc del dev $DEV1 ingress" + ] + }, + { + "id": "09d7", + "name": "Add fw filters protocol 802_3 and ip with conflicting priorities", + "category": [ + "filter", + "fw" + ], + "setup": [ + "$TC qdisc add dev $DEV1 ingress", + "$TC filter add dev $DEV1 parent ffff: protocol 802_3 prio 3 handle 7 fw action ok" + ], + "cmdUnderTest": "$TC filter add dev $DEV1 parent ffff: protocol ip prio 3 handle 8 fw action ok", + "expExitCode": "2", + "verifyCmd": "$TC filter get dev $DEV1 parent ffff: handle 8 prio 3 protocol ip fw", + "matchPattern": "filter parent ffff: protocol ip pref 3 fw.*handle 0x8", + "matchCount": "0", + "teardown": [ + "$TC qdisc del dev $DEV1 ingress" + ] + }, + { + "id": "6973", + "name": "Add fw filters with same index, same action", + "category": [ + "filter", + "fw" + ], + "setup": [ + "$TC qdisc add dev $DEV1 ingress", + "$TC filter add dev $DEV1 parent ffff: prio 6 handle 2 fw action continue index 5" + ], + "cmdUnderTest": "$TC filter add dev $DEV1 parent ffff: prio 8 handle 4 fw action continue index 5", + "expExitCode": "0", + "verifyCmd": "$TC filter get dev $DEV1 parent ffff: handle 4 prio 8 protocol all fw", + "matchPattern": "filter parent ffff: protocol all pref 8 fw.*handle 0x4.*gact action continue.*index 5 ref 2 bind 2", + "matchCount": "1", + "teardown": [ + "$TC qdisc del dev $DEV1 ingress" + ] + }, + { + "id": "fc06", + "name": "Add fw filters with action police", + "category": [ + "filter", + "fw" + ], + "setup": [ + "$TC qdisc add dev $DEV1 ingress" + ], + "cmdUnderTest": "$TC filter add dev $DEV1 parent ffff: prio 3 handle 4 fw action police rate 1kbit burst 10k index 5", + "expExitCode": "0", + "verifyCmd": "$TC filter get dev $DEV1 parent ffff: handle 4 prio 3 protocol all fw", + "matchPattern": "filter parent ffff: protocol all pref 3 fw.*handle 0x4.*police 0x5 rate 1Kbit burst 10Kb mtu 2Kb action reclassify overhead 0b.*ref 1 bind 1", + "matchCount": "1", + "teardown": [ + "$TC qdisc del dev $DEV1 ingress" + ] + }, + { + "id": "aac7", + "name": "Add fw filters with action police linklayer atm", + "category": [ + "filter", + "fw" + ], + "setup": [ + "$TC qdisc add dev $DEV1 ingress" + ], + "cmdUnderTest": "$TC filter add dev $DEV1 parent ffff: prio 3 handle 4 fw action police rate 2mbit burst 200k linklayer atm index 8", + "expExitCode": "0", + "verifyCmd": "$TC filter get dev $DEV1 parent ffff: handle 4 prio 3 protocol all fw", + "matchPattern": "filter parent ffff: protocol all pref 3 fw.*handle 0x4.*police 0x8 rate 2Mbit burst 200Kb mtu 2Kb action reclassify overhead 0b linklayer atm.*ref 1 bind 1", + "matchCount": "1", + "teardown": [ + "$TC qdisc del dev $DEV1 ingress" + ] + }, + { + "id": "5339", + "name": "Del entire fw filter", + "category": [ + "filter", + "fw" + ], + "setup": [ + "$TC qdisc add dev $DEV1 ingress", + "$TC filter add dev $DEV1 parent ffff: handle 5 prio 7 fw action pass", + "$TC filter add dev $DEV1 parent ffff: handle 3 prio 9 fw action pass" + ], + "cmdUnderTest": "$TC filter del dev $DEV1 parent ffff:", + "expExitCode": "0", + "verifyCmd": "$TC filter show dev $DEV1 parent ffff:", + "matchPattern": "protocol all pref.*handle.*gact action pass", + "matchCount": "0", + "teardown": [ + "$TC qdisc del dev $DEV1 ingress" + ] + }, + { + "id": "0e99", + "name": "Del single fw filter x1", + "__comment__": "First of two tests to check that one filter is there and the other isn't", + "category": [ + "filter", + "fw" + ], + "setup": [ + "$TC qdisc add dev $DEV1 ingress", + "$TC filter add dev $DEV1 parent ffff: handle 5 prio 7 fw action pass", + "$TC filter add dev $DEV1 parent ffff: handle 3 prio 9 fw action pass" + ], + "cmdUnderTest": "$TC filter del dev $DEV1 parent ffff: handle 3 prio 9 fw action pass", + "expExitCode": "0", + "verifyCmd": "$TC filter show dev $DEV1 parent ffff:", + "matchPattern": "protocol all pref 7.*handle 0x5.*gact action pass", + "matchCount": "1", + "teardown": [ + "$TC qdisc del dev $DEV1 ingress" + ] + }, + { + "id": "f54c", + "name": "Del single fw filter x2", + "__comment__": "Second of two tests to check that one filter is there and the other isn't", + "category": [ + "filter", + "fw" + ], + "setup": [ + "$TC qdisc add dev $DEV1 ingress", + "$TC filter add dev $DEV1 parent ffff: handle 5 prio 7 fw action pass", + "$TC filter add dev $DEV1 parent ffff: handle 3 prio 9 fw action pass" + ], + "cmdUnderTest": "$TC filter del dev $DEV1 parent ffff: handle 3 prio 9 fw action pass", + "expExitCode": "0", + "verifyCmd": "$TC filter show dev $DEV1 parent ffff:", + "matchPattern": "protocol all pref 9.*handle 0x3.*gact action pass", + "matchCount": "0", + "teardown": [ + "$TC qdisc del dev $DEV1 ingress" + ] + }, + { + "id": "ba94", + "name": "Del fw filter by prio", + "category": [ + "filter", + "fw" + ], + "setup": [ + "$TC qdisc add dev $DEV1 ingress", + "$TC filter add dev $DEV1 parent ffff: handle 1 prio 4 fw action ok", + "$TC filter add dev $DEV1 parent ffff: handle 2 prio 4 fw action ok" + ], + "cmdUnderTest": "$TC filter del dev $DEV1 parent ffff: prio 4", + "expExitCode": "0", + "verifyCmd": "$TC filter show dev $DEV1 parent ffff:", + "matchPattern": "pref 4 fw.*gact action pass", + "matchCount": "0", + "teardown": [ + "$TC qdisc del dev $DEV1 ingress" + ] + }, + { + "id": "4acb", + "name": "Del fw filter by chain", + "category": [ + "filter", + "fw" + ], + "setup": [ + "$TC qdisc add dev $DEV1 ingress", + "$TC filter add dev $DEV1 parent ffff: handle 4 prio 2 chain 13 fw action pipe", + "$TC filter add dev $DEV1 parent ffff: handle 3 prio 5 chain 13 fw action pipe" + ], + "cmdUnderTest": "$TC filter del dev $DEV1 parent ffff: chain 13", + "expExitCode": "0", + "verifyCmd": "$TC filter show dev $DEV1 parent ffff:", + "matchPattern": "fw chain 13 handle.*gact action pipe", + "matchCount": "0", + "teardown": [ + "$TC qdisc del dev $DEV1 ingress" + ] + }, + { + "id": "3424", + "name": "Del fw filter by action (invalid)", + "category": [ + "filter", + "fw" + ], + "setup": [ + "$TC qdisc add dev $DEV1 ingress", + "$TC filter add dev $DEV1 parent ffff: handle 2 prio 4 fw action drop" + ], + "cmdUnderTest": "$TC filter del dev $DEV1 parent ffff: fw action drop", + "expExitCode": "2", + "verifyCmd": "$TC filter get dev $DEV1 parent ffff: handle 2 prio 4 protocol all fw", + "matchPattern": "handle 0x2.*gact action drop", + "matchCount": "1", + "teardown": [ + "$TC qdisc del dev $DEV1 ingress" + ] + }, + { + "id": "da89", + "name": "Del fw filter by handle (invalid)", + "category": [ + "filter", + "fw" + ], + "setup": [ + "$TC qdisc add dev $DEV1 ingress", + "$TC filter add dev $DEV1 parent ffff: handle 3 prio 4 fw action continue" + ], + "cmdUnderTest": "$TC filter del dev $DEV1 parent ffff: handle 3 fw", + "expExitCode": "2", + "verifyCmd": "$TC filter get dev $DEV1 parent ffff: handle 3 prio 4 protocol all fw", + "matchPattern": "handle 0x3.*gact action continue", + "matchCount": "1", + "teardown": [ + "$TC qdisc del dev $DEV1 ingress" + ] + }, + { + "id": "4d95", + "name": "Del fw filter by protocol (invalid)", + "category": [ + "filter", + "fw" + ], + "setup": [ + "$TC qdisc add dev $DEV1 ingress", + "$TC filter add dev $DEV1 parent ffff: handle 4 prio 2 protocol arp fw action pipe" + ], + "cmdUnderTest": "$TC filter del dev $DEV1 parent ffff: protocol arp fw", + "expExitCode": "2", + "verifyCmd": "$TC filter get dev $DEV1 parent ffff: handle 4 prio 2 protocol arp fw", + "matchPattern": "filter parent ffff: protocol arp.*handle 0x4.*gact action pipe", + "matchCount": "1", + "teardown": [ + "$TC qdisc del dev $DEV1 ingress" + ] + }, + { + "id": "4736", + "name": "Del fw filter by flowid (invalid)", + "category": [ + "filter", + "fw" + ], + "setup": [ + "$TC qdisc add dev $DEV1 ingress", + "$TC filter add dev $DEV1 parent ffff: handle 4 prio 2 fw action pipe flowid 45" + ], + "cmdUnderTest": "$TC filter del dev $DEV1 parent ffff: fw flowid 45", + "expExitCode": "2", + "verifyCmd": "$TC filter show dev $DEV1 parent ffff:", + "matchPattern": "handle 0x4.*gact action pipe", + "matchCount": "1", + "teardown": [ + "$TC qdisc del dev $DEV1 ingress" + ] + }, + { + "id": "3dcb", + "name": "Replace fw filter action", + "category": [ + "filter", + "fw" + ], + "setup": [ + "$TC qdisc add dev $DEV1 ingress", + "$TC filter add dev $DEV1 parent ffff: handle 1 prio 2 fw action ok" + ], + "cmdUnderTest": "$TC filter replace dev $DEV1 parent ffff: handle 1 prio 2 fw action pipe", + "expExitCode": "0", + "verifyCmd": "$TC filter show dev $DEV1 parent ffff:", + "matchPattern": "pref 2 fw.*handle 0x1.*gact action pipe", + "matchCount": "1", + "teardown": [ + "$TC qdisc del dev $DEV1 ingress" + ] + }, + { + "id": "eb4d", + "name": "Replace fw filter classid", + "category": [ + "filter", + "fw" + ], + "setup": [ + "$TC qdisc add dev $DEV1 ingress", + "$TC filter add dev $DEV1 parent ffff: handle 1 prio 2 fw action ok" + ], + "cmdUnderTest": "$TC filter replace dev $DEV1 parent ffff: handle 1 prio 2 fw action pipe classid 2", + "expExitCode": "0", + "verifyCmd": "$TC filter show dev $DEV1 parent ffff:", + "matchPattern": "pref 2 fw.*handle 0x1 classid :2.*gact action pipe", + "matchCount": "1", + "teardown": [ + "$TC qdisc del dev $DEV1 ingress" + ] + }, + { + "id": "67ec", + "name": "Replace fw filter index", + "category": [ + "filter", + "fw" + ], + "setup": [ + "$TC qdisc add dev $DEV1 ingress", + "$TC filter add dev $DEV1 parent ffff: handle 1 prio 2 fw action ok index 3" + ], + "cmdUnderTest": "$TC filter replace dev $DEV1 parent ffff: handle 1 prio 2 fw action ok index 16", + "expExitCode": "0", + "verifyCmd": "$TC filter show dev $DEV1 parent ffff:", + "matchPattern": "pref 2 fw.*handle 0x1.*gact action pass.*index 16", + "matchCount": "1", + "teardown": [ + "$TC qdisc del dev $DEV1 ingress" + ] + } +] -- cgit v1.2.3-58-ga151 From 9a2ad3623868752e4f4d0a04a4dc43d5fcde07ce Mon Sep 17 00:00:00 2001 From: Petr Machata <petrm@mellanox.com> Date: Mon, 23 Jul 2018 12:33:08 +0200 Subject: selftests: forwarding: gre_multipath: Drop IPv6 tests Support for device-only IPv6 multipath next hops was dropped in commit 33bd5ac54dc4 ("net/ipv6: Revert attempt to simplify route replace and append") and as of commit b5d2d75e079a ("net/ipv6: Do not allow device only routes via the multipath API"), attempts to add a next hop like that yield an explicit diagnostic. Correspondingly, drop the IPv6 parts of GRE multipath test that are supposed to test that code. Signed-off-by: Petr Machata <petrm@mellanox.com> Reviewed-by: David Ahern <dsahern@gmail.com> Signed-off-by: David S. Miller <davem@davemloft.net> --- .../selftests/net/forwarding/gre_multipath.sh | 113 ++------------------- 1 file changed, 6 insertions(+), 107 deletions(-) (limited to 'tools') diff --git a/tools/testing/selftests/net/forwarding/gre_multipath.sh b/tools/testing/selftests/net/forwarding/gre_multipath.sh index 982cc8c23200..3b1e047f7617 100755 --- a/tools/testing/selftests/net/forwarding/gre_multipath.sh +++ b/tools/testing/selftests/net/forwarding/gre_multipath.sh @@ -8,14 +8,12 @@ # | H1 | # | $h1 + | # | 192.0.2.1/28 | | -# | 2001:db8:1::1/64 | | # +-------------------|-----+ # | # +-------------------|------------------------+ # | SW1 | | # | $ol1 + | # | 192.0.2.2/28 | -# | 2001:db8:1::2/64 | # | | # | + g1a (gre) + g1b (gre) | # | loc=192.0.2.65 loc=192.0.2.81 | @@ -49,22 +47,17 @@ # | | # | $ol2 + | # | 192.0.2.17/28 | | -# | 2001:db8:2::1/64 | | # +-------------------|------------------------+ # | # +-------------------|-----+ # | H2 | | # | $h2 + | # | 192.0.2.18/28 | -# | 2001:db8:2::2/64 | # +-------------------------+ ALL_TESTS=" ping_ipv4 - ping_ipv6 multipath_ipv4 - multipath_ipv6 - multipath_ipv6_l4 " NUM_NETIFS=6 @@ -74,19 +67,17 @@ h1_create() { simple_if_init $h1 192.0.2.1/28 2001:db8:1::1/64 ip route add vrf v$h1 192.0.2.16/28 via 192.0.2.2 - ip route add vrf v$h1 2001:db8:2::/64 via 2001:db8:1::2 } h1_destroy() { - ip route del vrf v$h1 2001:db8:2::/64 via 2001:db8:1::2 ip route del vrf v$h1 192.0.2.16/28 via 192.0.2.2 simple_if_fini $h1 192.0.2.1/28 } sw1_create() { - simple_if_init $ol1 192.0.2.2/28 2001:db8:1::2/64 + simple_if_init $ol1 192.0.2.2/28 __simple_if_init $ul1 v$ol1 vlan_create $ul1 111 v$ol1 192.0.2.129/28 vlan_create $ul1 222 v$ol1 192.0.2.145/28 @@ -102,9 +93,6 @@ sw1_create() ip route add vrf v$ol1 192.0.2.16/28 \ nexthop dev g1a \ nexthop dev g1b - ip route add vrf v$ol1 2001:db8:2::/64 \ - nexthop dev g1a \ - nexthop dev g1b tc qdisc add dev $ul1 clsact tc filter add dev $ul1 egress pref 111 prot 802.1q \ @@ -117,7 +105,6 @@ sw1_destroy() { tc qdisc del dev $ul1 clsact - ip route del vrf v$ol1 2001:db8:2::/64 ip route del vrf v$ol1 192.0.2.16/28 ip route del vrf v$ol1 192.0.2.82/32 via 192.0.2.146 @@ -131,12 +118,12 @@ sw1_destroy() vlan_destroy $ul1 222 vlan_destroy $ul1 111 __simple_if_fini $ul1 - simple_if_fini $ol1 192.0.2.2/28 2001:db8:1::2/64 + simple_if_fini $ol1 192.0.2.2/28 } sw2_create() { - simple_if_init $ol2 192.0.2.17/28 2001:db8:2::1/64 + simple_if_init $ol2 192.0.2.17/28 __simple_if_init $ul2 v$ol2 vlan_create $ul2 111 v$ol2 192.0.2.130/28 vlan_create $ul2 222 v$ol2 192.0.2.146/28 @@ -152,14 +139,10 @@ sw2_create() ip route add vrf v$ol2 192.0.2.0/28 \ nexthop dev g2a \ nexthop dev g2b - ip route add vrf v$ol2 2001:db8:1::/64 \ - nexthop dev g2a \ - nexthop dev g2b } sw2_destroy() { - ip route del vrf v$ol2 2001:db8:1::/64 ip route del vrf v$ol2 192.0.2.0/28 ip route del vrf v$ol2 192.0.2.81/32 via 192.0.2.145 @@ -173,21 +156,19 @@ sw2_destroy() vlan_destroy $ul2 222 vlan_destroy $ul2 111 __simple_if_fini $ul2 - simple_if_fini $ol2 192.0.2.17/28 2001:db8:2::1/64 + simple_if_fini $ol2 192.0.2.17/28 } h2_create() { - simple_if_init $h2 192.0.2.18/28 2001:db8:2::2/64 + simple_if_init $h2 192.0.2.18/28 ip route add vrf v$h2 192.0.2.0/28 via 192.0.2.17 - ip route add vrf v$h2 2001:db8:1::/64 via 2001:db8:2::1 } h2_destroy() { - ip route del vrf v$h2 2001:db8:1::/64 via 2001:db8:2::1 ip route del vrf v$h2 192.0.2.0/28 via 192.0.2.17 - simple_if_fini $h2 192.0.2.18/28 2001:db8:2::2/64 + simple_if_fini $h2 192.0.2.18/28 } setup_prepare() @@ -250,77 +231,11 @@ multipath4_test() sysctl_restore net.ipv4.fib_multipath_hash_policy } -multipath6_l4_test() -{ - local what=$1; shift - local weight1=$1; shift - local weight2=$1; shift - - sysctl_set net.ipv6.fib_multipath_hash_policy 1 - ip route replace vrf v$ol1 2001:db8:2::/64 \ - nexthop dev g1a weight $weight1 \ - nexthop dev g1b weight $weight2 - - local t0_111=$(tc_rule_stats_get $ul1 111 egress) - local t0_222=$(tc_rule_stats_get $ul1 222 egress) - - ip vrf exec v$h1 \ - $MZ $h1 -6 -q -p 64 -A 2001:db8:1::1 -B 2001:db8:2::2 \ - -d 1msec -t udp "sp=1024,dp=0-32768" - - local t1_111=$(tc_rule_stats_get $ul1 111 egress) - local t1_222=$(tc_rule_stats_get $ul1 222 egress) - - local d111=$((t1_111 - t0_111)) - local d222=$((t1_222 - t0_222)) - multipath_eval "$what" $weight1 $weight2 $d111 $d222 - - ip route replace vrf v$ol1 2001:db8:2::/64 \ - nexthop dev g1a \ - nexthop dev g1b - sysctl_restore net.ipv6.fib_multipath_hash_policy -} - -multipath6_test() -{ - local what=$1; shift - local weight1=$1; shift - local weight2=$1; shift - - ip route replace vrf v$ol1 2001:db8:2::/64 \ - nexthop dev g1a weight $weight1 \ - nexthop dev g1b weight $weight2 - - local t0_111=$(tc_rule_stats_get $ul1 111 egress) - local t0_222=$(tc_rule_stats_get $ul1 222 egress) - - # Generate 16384 echo requests, each with a random flow label. - for ((i=0; i < 16384; ++i)); do - ip vrf exec v$h1 $PING6 2001:db8:2::2 -F 0 -c 1 -q &> /dev/null - done - - local t1_111=$(tc_rule_stats_get $ul1 111 egress) - local t1_222=$(tc_rule_stats_get $ul1 222 egress) - - local d111=$((t1_111 - t0_111)) - local d222=$((t1_222 - t0_222)) - multipath_eval "$what" $weight1 $weight2 $d111 $d222 - - ip route replace vrf v$ol1 2001:db8:2::/64 \ - nexthop dev g1a \ - nexthop dev g1b -} - ping_ipv4() { ping_test $h1 192.0.2.18 } -ping_ipv6() -{ - ping6_test $h1 2001:db8:2::2 -} - multipath_ipv4() { log_info "Running IPv4 multipath tests" @@ -329,22 +244,6 @@ multipath_ipv4() multipath4_test "Weighted MP 11:45" 11 45 } -multipath_ipv6() -{ - log_info "Running IPv6 multipath tests" - multipath6_test "ECMP" 1 1 - multipath6_test "Weighted MP 2:1" 2 1 - multipath6_test "Weighted MP 11:45" 11 45 -} - -multipath_ipv6_l4() -{ - log_info "Running IPv6 L4 hash multipath tests" - multipath6_l4_test "ECMP" 1 1 - multipath6_l4_test "Weighted MP 2:1" 2 1 - multipath6_l4_test "Weighted MP 11:45" 11 45 -} - trap cleanup EXIT setup_prepare -- cgit v1.2.3-58-ga151 From 7f333cbf2b5bb48045e4f31de1c33fb701ffa45a Mon Sep 17 00:00:00 2001 From: Jiri Pirko <jiri@mellanox.com> Date: Mon, 23 Jul 2018 09:24:05 +0200 Subject: selftests: forwarding: move shblock tc support check to a separate helper The shared block support is only needed for tc_shblock.sh. No need to require that for other test. Signed-off-by: Jiri Pirko <jiri@mellanox.com> Signed-off-by: David S. Miller <davem@davemloft.net> --- tools/testing/selftests/net/forwarding/lib.sh | 3 +++ tools/testing/selftests/net/forwarding/tc_shblocks.sh | 2 ++ 2 files changed, 5 insertions(+) (limited to 'tools') diff --git a/tools/testing/selftests/net/forwarding/lib.sh b/tools/testing/selftests/net/forwarding/lib.sh index 2bb9cf303c53..ec94395a54a7 100644 --- a/tools/testing/selftests/net/forwarding/lib.sh +++ b/tools/testing/selftests/net/forwarding/lib.sh @@ -33,7 +33,10 @@ check_tc_version() echo "SKIP: iproute2 too old; tc is missing JSON support" exit 1 fi +} +check_tc_shblock_support() +{ tc filter help 2>&1 | grep block &> /dev/null if [[ $? -ne 0 ]]; then echo "SKIP: iproute2 too old; tc is missing shared block support" diff --git a/tools/testing/selftests/net/forwarding/tc_shblocks.sh b/tools/testing/selftests/net/forwarding/tc_shblocks.sh index b5b917203815..9826a446e2c0 100755 --- a/tools/testing/selftests/net/forwarding/tc_shblocks.sh +++ b/tools/testing/selftests/net/forwarding/tc_shblocks.sh @@ -105,6 +105,8 @@ cleanup() ip link set $swp2 address $swp2origmac } +check_tc_shblock_support + trap cleanup EXIT setup_prepare -- cgit v1.2.3-58-ga151 From 2d73c8871fae2da72f4717c15d4256ec6ba208c4 Mon Sep 17 00:00:00 2001 From: Jiri Pirko <jiri@mellanox.com> Date: Mon, 23 Jul 2018 09:24:06 +0200 Subject: selftests: forwarding: add tests for TC chains creation adn destruction Add basic sanity tests for TC chains. Signed-off-by: Jiri Pirko <jiri@mellanox.com> Signed-off-by: David S. Miller <davem@davemloft.net> --- tools/testing/selftests/net/forwarding/lib.sh | 9 +++++++++ .../testing/selftests/net/forwarding/tc_chains.sh | 23 +++++++++++++++++++++- 2 files changed, 31 insertions(+), 1 deletion(-) (limited to 'tools') diff --git a/tools/testing/selftests/net/forwarding/lib.sh b/tools/testing/selftests/net/forwarding/lib.sh index ec94395a54a7..158d59ffee40 100644 --- a/tools/testing/selftests/net/forwarding/lib.sh +++ b/tools/testing/selftests/net/forwarding/lib.sh @@ -44,6 +44,15 @@ check_tc_shblock_support() fi } +check_tc_chain_support() +{ + tc help 2>&1|grep chain &> /dev/null + if [[ $? -ne 0 ]]; then + echo "SKIP: iproute2 too old; tc is missing chain support" + exit 1 + fi +} + if [[ "$(id -u)" -ne 0 ]]; then echo "SKIP: need root privileges" exit 0 diff --git a/tools/testing/selftests/net/forwarding/tc_chains.sh b/tools/testing/selftests/net/forwarding/tc_chains.sh index d2c783e94df3..2730887a3b3a 100755 --- a/tools/testing/selftests/net/forwarding/tc_chains.sh +++ b/tools/testing/selftests/net/forwarding/tc_chains.sh @@ -1,7 +1,7 @@ #!/bin/bash # SPDX-License-Identifier: GPL-2.0 -ALL_TESTS="unreachable_chain_test gact_goto_chain_test" +ALL_TESTS="unreachable_chain_test gact_goto_chain_test create_destroy_chain" NUM_NETIFS=2 source tc_common.sh source lib.sh @@ -80,6 +80,25 @@ gact_goto_chain_test() log_test "gact goto chain ($tcflags)" } +create_destroy_chain() +{ + RET=0 + + tc chain add dev $h2 ingress + check_err $? "Failed to create default chain" + + tc chain add dev $h2 ingress chain 1 + check_err $? "Failed to create chain 1" + + tc chain del dev $h2 ingress + check_err $? "Failed to destroy default chain" + + tc chain del dev $h2 ingress chain 1 + check_err $? "Failed to destroy chain 1" + + log_test "create destroy chain" +} + setup_prepare() { h1=${NETIFS[p1]} @@ -103,6 +122,8 @@ cleanup() vrf_cleanup } +check_tc_chain_support + trap cleanup EXIT setup_prepare -- cgit v1.2.3-58-ga151 From d159b381795b138532436cce1229f5305c6f1a21 Mon Sep 17 00:00:00 2001 From: Jiri Pirko <jiri@mellanox.com> Date: Mon, 23 Jul 2018 09:24:07 +0200 Subject: selftests: forwarding: add tests for TC chain templates Add basic sanity tests for TC chain templates. Signed-off-by: Jiri Pirko <jiri@mellanox.com> Signed-off-by: David S. Miller <davem@davemloft.net> --- .../testing/selftests/net/forwarding/tc_chains.sh | 44 +++++++++++++++++++++- 1 file changed, 43 insertions(+), 1 deletion(-) (limited to 'tools') diff --git a/tools/testing/selftests/net/forwarding/tc_chains.sh b/tools/testing/selftests/net/forwarding/tc_chains.sh index 2730887a3b3a..031e322e28b3 100755 --- a/tools/testing/selftests/net/forwarding/tc_chains.sh +++ b/tools/testing/selftests/net/forwarding/tc_chains.sh @@ -1,7 +1,8 @@ #!/bin/bash # SPDX-License-Identifier: GPL-2.0 -ALL_TESTS="unreachable_chain_test gact_goto_chain_test create_destroy_chain" +ALL_TESTS="unreachable_chain_test gact_goto_chain_test create_destroy_chain \ + template_filter_fits" NUM_NETIFS=2 source tc_common.sh source lib.sh @@ -99,6 +100,47 @@ create_destroy_chain() log_test "create destroy chain" } +template_filter_fits() +{ + RET=0 + + tc chain add dev $h2 ingress protocol ip \ + flower dst_mac 00:00:00:00:00:00/FF:FF:FF:FF:FF:FF &> /dev/null + tc chain add dev $h2 ingress chain 1 protocol ip \ + flower src_mac 00:00:00:00:00:00/FF:FF:FF:FF:FF:FF &> /dev/null + + tc filter add dev $h2 ingress protocol ip pref 1 handle 1101 \ + flower dst_mac $h2mac action drop + check_err $? "Failed to insert filter which fits template" + + tc filter add dev $h2 ingress protocol ip pref 1 handle 1102 \ + flower src_mac $h2mac action drop &> /dev/null + check_fail $? "Incorrectly succeded to insert filter which does not template" + + tc filter add dev $h2 ingress chain 1 protocol ip pref 1 handle 1101 \ + flower src_mac $h2mac action drop + check_err $? "Failed to insert filter which fits template" + + tc filter add dev $h2 ingress chain 1 protocol ip pref 1 handle 1102 \ + flower dst_mac $h2mac action drop &> /dev/null + check_fail $? "Incorrectly succeded to insert filter which does not template" + + tc filter del dev $h2 ingress chain 1 protocol ip pref 1 handle 1102 \ + flower &> /dev/null + tc filter del dev $h2 ingress chain 1 protocol ip pref 1 handle 1101 \ + flower &> /dev/null + + tc filter del dev $h2 ingress protocol ip pref 1 handle 1102 \ + flower &> /dev/null + tc filter del dev $h2 ingress protocol ip pref 1 handle 1101 \ + flower &> /dev/null + + tc chain del dev $h2 ingress chain 1 + tc chain del dev $h2 ingress + + log_test "template filter fits" +} + setup_prepare() { h1=${NETIFS[p1]} -- cgit v1.2.3-58-ga151 From 2cc512c1fa1ee99879d55d1cb4e3fd0e6eab35b3 Mon Sep 17 00:00:00 2001 From: YueHaibing <yuehaibing@huawei.com> Date: Tue, 24 Jul 2018 10:55:24 +0800 Subject: bpf: btf: fix inconsistent IS_ERR and PTR_ERR Fix inconsistent IS_ERR and PTR_ERR in get_btf, the proper pointer to be passed as argument is '*btf' This issue was detected with the help of Coccinelle. Fixes: 2d3feca8c44f ("bpf: btf: print map dump and lookup with btf info") Signed-off-by: YueHaibing <yuehaibing@huawei.com> Acked-by: David S. Miller <davem@davemloft.net> Acked-by: Jakub Kicinski <jakub.kicinski@netronome.com> Acked-by: Martin KaFai Lau <kafai@fb.com> Signed-off-by: Daniel Borkmann <daniel@iogearbox.net> --- tools/bpf/bpftool/map.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'tools') diff --git a/tools/bpf/bpftool/map.c b/tools/bpf/bpftool/map.c index 9c8191845585..0ee3ba479d87 100644 --- a/tools/bpf/bpftool/map.c +++ b/tools/bpf/bpftool/map.c @@ -230,7 +230,7 @@ static int get_btf(struct bpf_map_info *map_info, struct btf **btf) *btf = btf__new((__u8 *)btf_info.btf, btf_info.btf_size, NULL); if (IS_ERR(*btf)) { - err = PTR_ERR(btf); + err = PTR_ERR(*btf); *btf = NULL; } -- cgit v1.2.3-58-ga151 From e66565f3bee141748d2c3b2ed0d4ecd455f634fa Mon Sep 17 00:00:00 2001 From: Jeremy Cline <jcline@redhat.com> Date: Tue, 24 Jul 2018 15:53:34 -0400 Subject: bpf: Add Python 3 support to selftests scripts for bpf Adjust tcp_client.py and tcp_server.py to work with Python 3 by using the print function, marking string literals as bytes, and using the newer exception syntax. This should be functionally equivalent and supports Python 3+. Signed-off-by: Jeremy Cline <jcline@redhat.com> Signed-off-by: Daniel Borkmann <daniel@iogearbox.net> --- tools/testing/selftests/bpf/tcp_client.py | 12 ++++++------ tools/testing/selftests/bpf/tcp_server.py | 16 ++++++++-------- 2 files changed, 14 insertions(+), 14 deletions(-) (limited to 'tools') diff --git a/tools/testing/selftests/bpf/tcp_client.py b/tools/testing/selftests/bpf/tcp_client.py index 481dccdf140c..7f8200a8702b 100755 --- a/tools/testing/selftests/bpf/tcp_client.py +++ b/tools/testing/selftests/bpf/tcp_client.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python2 +#!/usr/bin/env python3 # # SPDX-License-Identifier: GPL-2.0 # @@ -9,11 +9,11 @@ import subprocess import select def read(sock, n): - buf = '' + buf = b'' while len(buf) < n: rem = n - len(buf) try: s = sock.recv(rem) - except (socket.error), e: return '' + except (socket.error) as e: return b'' buf += s return buf @@ -22,7 +22,7 @@ def send(sock, s): count = 0 while count < total: try: n = sock.send(s) - except (socket.error), e: n = 0 + except (socket.error) as e: n = 0 if n == 0: return count; count += n @@ -39,10 +39,10 @@ try: except socket.error as e: sys.exit(1) -buf = '' +buf = b'' n = 0 while n < 1000: - buf += '+' + buf += b'+' n += 1 sock.settimeout(1); diff --git a/tools/testing/selftests/bpf/tcp_server.py b/tools/testing/selftests/bpf/tcp_server.py index bc454d7d0be2..b39903fca4c8 100755 --- a/tools/testing/selftests/bpf/tcp_server.py +++ b/tools/testing/selftests/bpf/tcp_server.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python2 +#!/usr/bin/env python3 # # SPDX-License-Identifier: GPL-2.0 # @@ -9,11 +9,11 @@ import subprocess import select def read(sock, n): - buf = '' + buf = b'' while len(buf) < n: rem = n - len(buf) try: s = sock.recv(rem) - except (socket.error), e: return '' + except (socket.error) as e: return b'' buf += s return buf @@ -22,7 +22,7 @@ def send(sock, s): count = 0 while count < total: try: n = sock.send(s) - except (socket.error), e: n = 0 + except (socket.error) as e: n = 0 if n == 0: return count; count += n @@ -43,7 +43,7 @@ host = socket.gethostname() try: serverSocket.bind((host, 0)) except socket.error as msg: - print 'bind fails: ', msg + print('bind fails: ' + str(msg)) sn = serverSocket.getsockname() serverPort = sn[1] @@ -51,10 +51,10 @@ serverPort = sn[1] cmdStr = ("./tcp_client.py %d &") % (serverPort) os.system(cmdStr) -buf = '' +buf = b'' n = 0 while n < 500: - buf += '.' + buf += b'.' n += 1 serverSocket.listen(MAX_PORTS) @@ -79,5 +79,5 @@ while True: serverSocket.close() sys.exit(0) else: - print 'Select timeout!' + print('Select timeout!') sys.exit(1) -- cgit v1.2.3-58-ga151 From 08a852528e9678f0854af331f19747f2b2a73c06 Mon Sep 17 00:00:00 2001 From: Taeung Song <treeze.taeung@gmail.com> Date: Wed, 25 Jul 2018 15:36:51 +0900 Subject: tools/bpftool: ignore build products For untracked things of tools/bpf, add this. Reviewed-by: Jakub Kicinski <jakub.kicinski@netronome.com> Signed-off-by: Taeung Song <treeze.taeung@gmail.com> Signed-off-by: Daniel Borkmann <daniel@iogearbox.net> --- tools/bpf/.gitignore | 5 +++++ tools/bpf/bpftool/.gitignore | 2 ++ 2 files changed, 7 insertions(+) create mode 100644 tools/bpf/.gitignore (limited to 'tools') diff --git a/tools/bpf/.gitignore b/tools/bpf/.gitignore new file mode 100644 index 000000000000..dfe2bd5a4b95 --- /dev/null +++ b/tools/bpf/.gitignore @@ -0,0 +1,5 @@ +FEATURE-DUMP.bpf +bpf_asm +bpf_dbg +bpf_exp.yacc.* +bpf_jit_disasm diff --git a/tools/bpf/bpftool/.gitignore b/tools/bpf/bpftool/.gitignore index d7e678c2d396..67167e44b726 100644 --- a/tools/bpf/bpftool/.gitignore +++ b/tools/bpf/bpftool/.gitignore @@ -1,3 +1,5 @@ *.d bpftool +bpftool*.8 +bpf-helpers.* FEATURE-DUMP.bpftool -- cgit v1.2.3-58-ga151 From 5a967512bb812d7a4b7e9f3930f49d6d88533dc3 Mon Sep 17 00:00:00 2001 From: Jiri Pirko <jiri@mellanox.com> Date: Thu, 26 Jul 2018 11:38:34 +0200 Subject: selftests: forwarding: add tests for TC chain get and dump operations Signed-off-by: Jiri Pirko <jiri@mellanox.com> Signed-off-by: David S. Miller <davem@davemloft.net> --- tools/testing/selftests/net/forwarding/tc_chains.sh | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) (limited to 'tools') diff --git a/tools/testing/selftests/net/forwarding/tc_chains.sh b/tools/testing/selftests/net/forwarding/tc_chains.sh index 031e322e28b3..2934fb5ed2a2 100755 --- a/tools/testing/selftests/net/forwarding/tc_chains.sh +++ b/tools/testing/selftests/net/forwarding/tc_chains.sh @@ -88,9 +88,30 @@ create_destroy_chain() tc chain add dev $h2 ingress check_err $? "Failed to create default chain" + output="$(tc -j chain get dev $h2 ingress)" + check_err $? "Failed to get default chain" + + echo $output | jq -e ".[] | select(.chain == 0)" &> /dev/null + check_err $? "Unexpected output for default chain" + tc chain add dev $h2 ingress chain 1 check_err $? "Failed to create chain 1" + output="$(tc -j chain get dev $h2 ingress chain 1)" + check_err $? "Failed to get chain 1" + + echo $output | jq -e ".[] | select(.chain == 1)" &> /dev/null + check_err $? "Unexpected output for chain 1" + + output="$(tc -j chain show dev $h2 ingress)" + check_err $? "Failed to dump chains" + + echo $output | jq -e ".[] | select(.chain == 0)" &> /dev/null + check_err $? "Can't find default chain in dump" + + echo $output | jq -e ".[] | select(.chain == 1)" &> /dev/null + check_err $? "Can't find chain 1 in dump" + tc chain del dev $h2 ingress check_err $? "Failed to destroy default chain" -- cgit v1.2.3-58-ga151 From eb91f42ef074467e0c441a9789dc1fc0519a25ad Mon Sep 17 00:00:00 2001 From: Anders Roxell <anders.roxell@linaro.org> Date: Thu, 26 Jul 2018 11:53:58 +0200 Subject: selftests/net: add tls to .gitignore Add the tls binary to .gitignore Fixes: 7f657d5bf507 ("selftests: tls: add selftests for TLS sockets") Signed-off-by: Anders Roxell <anders.roxell@linaro.org> Signed-off-by: David S. Miller <davem@davemloft.net> --- tools/testing/selftests/net/.gitignore | 1 + 1 file changed, 1 insertion(+) (limited to 'tools') diff --git a/tools/testing/selftests/net/.gitignore b/tools/testing/selftests/net/.gitignore index 1a0ac3a29ec5..78b24cf76f40 100644 --- a/tools/testing/selftests/net/.gitignore +++ b/tools/testing/selftests/net/.gitignore @@ -13,3 +13,4 @@ udpgso udpgso_bench_rx udpgso_bench_tx tcp_inq +tls -- cgit v1.2.3-58-ga151 From 1e960043e8ae65d6f53b3414586a3fb634461908 Mon Sep 17 00:00:00 2001 From: Jakub Kicinski <jakub.kicinski@netronome.com> Date: Thu, 26 Jul 2018 14:32:18 -0700 Subject: tools: libbpf: handle NULL program gracefully in bpf_program__nth_fd() bpf_map__fd() handles NULL map gracefully and returns -EINVAL. bpf_program__fd() and bpf_program__nth_fd() crash in this case. Make the behaviour more consistent by validating prog pointer as well. Signed-off-by: Jakub Kicinski <jakub.kicinski@netronome.com> Reviewed-by: Quentin Monnet <quentin.monnet@netronome.com> Signed-off-by: Daniel Borkmann <daniel@iogearbox.net> --- tools/lib/bpf/libbpf.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'tools') diff --git a/tools/lib/bpf/libbpf.c b/tools/lib/bpf/libbpf.c index 955f8eafbf41..afa9860db755 100644 --- a/tools/lib/bpf/libbpf.c +++ b/tools/lib/bpf/libbpf.c @@ -1991,6 +1991,9 @@ int bpf_program__nth_fd(struct bpf_program *prog, int n) { int fd; + if (!prog) + return -EINVAL; + if (n >= prog->instances.nr || n < 0) { pr_warning("Can't get the %dth fd from program %s: only %d instances\n", n, prog->section_name, prog->instances.nr); -- cgit v1.2.3-58-ga151 From 6d4b198b0b23ca2a75785173a9b27afd1eee7040 Mon Sep 17 00:00:00 2001 From: Jakub Kicinski <jakub.kicinski@netronome.com> Date: Thu, 26 Jul 2018 14:32:19 -0700 Subject: tools: libbpf: add bpf_object__find_program_by_title() Allow users to find programs by section names. Signed-off-by: Jakub Kicinski <jakub.kicinski@netronome.com> Reviewed-by: Quentin Monnet <quentin.monnet@netronome.com> Signed-off-by: Daniel Borkmann <daniel@iogearbox.net> --- tools/lib/bpf/libbpf.c | 12 ++++++++++++ tools/lib/bpf/libbpf.h | 3 +++ 2 files changed, 15 insertions(+) (limited to 'tools') diff --git a/tools/lib/bpf/libbpf.c b/tools/lib/bpf/libbpf.c index afa9860db755..857d3d16968e 100644 --- a/tools/lib/bpf/libbpf.c +++ b/tools/lib/bpf/libbpf.c @@ -873,6 +873,18 @@ bpf_object__find_prog_by_idx(struct bpf_object *obj, int idx) return NULL; } +struct bpf_program * +bpf_object__find_program_by_title(struct bpf_object *obj, const char *title) +{ + struct bpf_program *pos; + + bpf_object__for_each_program(pos, obj) { + if (pos->section_name && !strcmp(pos->section_name, title)) + return pos; + } + return NULL; +} + static int bpf_program__collect_reloc(struct bpf_program *prog, GElf_Shdr *shdr, Elf_Data *data, struct bpf_object *obj) diff --git a/tools/lib/bpf/libbpf.h b/tools/lib/bpf/libbpf.h index 1f8fc2060460..a295fe2f822b 100644 --- a/tools/lib/bpf/libbpf.h +++ b/tools/lib/bpf/libbpf.h @@ -86,6 +86,9 @@ const char *bpf_object__name(struct bpf_object *obj); unsigned int bpf_object__kversion(struct bpf_object *obj); int bpf_object__btf_fd(const struct bpf_object *obj); +struct bpf_program * +bpf_object__find_program_by_title(struct bpf_object *obj, const char *title); + struct bpf_object *bpf_object__next(struct bpf_object *prev); #define bpf_object__for_each_safe(pos, tmp) \ for ((pos) = bpf_object__next(NULL), \ -- cgit v1.2.3-58-ga151 From d159261f3662a89a5cd4fae041107ee511d9552e Mon Sep 17 00:00:00 2001 From: Petr Machata <petrm@mellanox.com> Date: Fri, 27 Jul 2018 15:27:02 +0300 Subject: selftests: mlxsw: Add test for trust-DSCP Add a test that exercises the new code. Send DSCP-tagged packets, and observe how they are prioritized in the switch and the DSCP is updated on egress again. Signed-off-by: Petr Machata <petrm@mellanox.com> Signed-off-by: Ido Schimmel <idosch@mellanox.com> Signed-off-by: David S. Miller <davem@davemloft.net> --- .../selftests/drivers/net/mlxsw/qos_dscp_bridge.sh | 248 +++++++++++++++++++++ 1 file changed, 248 insertions(+) create mode 100755 tools/testing/selftests/drivers/net/mlxsw/qos_dscp_bridge.sh (limited to 'tools') diff --git a/tools/testing/selftests/drivers/net/mlxsw/qos_dscp_bridge.sh b/tools/testing/selftests/drivers/net/mlxsw/qos_dscp_bridge.sh new file mode 100755 index 000000000000..418319f19108 --- /dev/null +++ b/tools/testing/selftests/drivers/net/mlxsw/qos_dscp_bridge.sh @@ -0,0 +1,248 @@ +#!/bin/bash +# SPDX-License-Identifier: GPL-2.0 + +# Test for DSCP prioritization and rewrite. Packets ingress $swp1 with a DSCP +# tag and are prioritized according to the map at $swp1. They egress $swp2 and +# the DSCP value is updated to match the map at that interface. The updated DSCP +# tag is verified at $h2. +# +# ICMP responses are produced with the same DSCP tag that arrived at $h2. They +# go through prioritization at $swp2 and DSCP retagging at $swp1. The tag is +# verified at $h1--it should match the original tag. +# +# +----------------------+ +----------------------+ +# | H1 | | H2 | +# | + $h1 | | $h2 + | +# | | 192.0.2.1/28 | | 192.0.2.2/28 | | +# +----|-----------------+ +----------------|-----+ +# | | +# +----|----------------------------------------------------------------|-----+ +# | SW | | | +# | +-|----------------------------------------------------------------|-+ | +# | | + $swp1 BR $swp2 + | | +# | | APP=0,5,10 .. 7,5,17 APP=0,5,20 .. 7,5,27 | | +# | +--------------------------------------------------------------------+ | +# +---------------------------------------------------------------------------+ + +ALL_TESTS=" + ping_ipv4 + test_dscp +" + +lib_dir=$(dirname $0)/../../../net/forwarding + +NUM_NETIFS=4 +source $lib_dir/lib.sh + +__dscp_capture_add_del() +{ + local add_del=$1; shift + local dev=$1; shift + local base=$1; shift + local dscp; + + for prio in {0..7}; do + dscp=$((base + prio)) + __icmp_capture_add_del $add_del $dscp "" $dev \ + "ip_tos $((dscp << 2))" + done +} + +dscp_capture_install() +{ + local dev=$1; shift + local base=$1; shift + + __dscp_capture_add_del add $dev $base +} + +dscp_capture_uninstall() +{ + local dev=$1; shift + local base=$1; shift + + __dscp_capture_add_del del $dev $base +} + +h1_create() +{ + local dscp; + + simple_if_init $h1 192.0.2.1/28 + tc qdisc add dev $h1 clsact + dscp_capture_install $h1 10 +} + +h1_destroy() +{ + dscp_capture_uninstall $h1 10 + tc qdisc del dev $h1 clsact + simple_if_fini $h1 192.0.2.1/28 +} + +h2_create() +{ + simple_if_init $h2 192.0.2.2/28 + tc qdisc add dev $h2 clsact + dscp_capture_install $h2 20 +} + +h2_destroy() +{ + dscp_capture_uninstall $h2 20 + tc qdisc del dev $h2 clsact + simple_if_fini $h2 192.0.2.2/28 +} + +dscp_map() +{ + local base=$1; shift + + for prio in {0..7}; do + echo app=$prio,5,$((base + prio)) + done +} + +lldpad_wait() +{ + local dev=$1; shift + + while lldptool -t -i $dev -V APP -c app | grep -q pending; do + echo "$dev: waiting for lldpad to push pending APP updates" + sleep 5 + done +} + +switch_create() +{ + ip link add name br1 type bridge vlan_filtering 1 + ip link set dev br1 up + ip link set dev $swp1 master br1 + ip link set dev $swp1 up + ip link set dev $swp2 master br1 + ip link set dev $swp2 up + + lldptool -T -i $swp1 -V APP $(dscp_map 10) >/dev/null + lldptool -T -i $swp2 -V APP $(dscp_map 20) >/dev/null + lldpad_wait $swp1 + lldpad_wait $swp2 +} + +switch_destroy() +{ + lldptool -T -i $swp2 -V APP -d $(dscp_map 20) >/dev/null + lldptool -T -i $swp1 -V APP -d $(dscp_map 10) >/dev/null + + # Give lldpad a chance to push down the changes. If the device is downed + # too soon, the updates will be left pending, but will have been struck + # off the lldpad's DB already, and we won't be able to tell. Then on + # next test iteration this would cause weirdness as newly-added APP + # rules conflict with the old ones, sometimes getting stuck in an + # "unknown" state. + sleep 5 + + ip link set dev $swp2 nomaster + ip link set dev $swp1 nomaster + ip link del dev br1 +} + +setup_prepare() +{ + h1=${NETIFS[p1]} + swp1=${NETIFS[p2]} + + swp2=${NETIFS[p3]} + h2=${NETIFS[p4]} + + vrf_prepare + + h1_create + h2_create + switch_create +} + +cleanup() +{ + pre_cleanup + + switch_destroy + h2_destroy + h1_destroy + + vrf_cleanup +} + +dscp_fetch_stats() +{ + local dev=$1; shift + local base=$1; shift + + for prio in {0..7}; do + local dscp=$((base + prio)) + local t=$(tc_rule_stats_get $dev $dscp) + echo "[$dscp]=$t " + done +} + +ping_ipv4() +{ + ping_test $h1 192.0.2.2 +} + +dscp_ping_test() +{ + local vrf_name=$1; shift + local sip=$1; shift + local dip=$1; shift + local prio=$1; shift + local dev_10=$1; shift + local dev_20=$1; shift + + local dscp_10=$(((prio + 10) << 2)) + local dscp_20=$(((prio + 20) << 2)) + + RET=0 + + local -A t0s + eval "t0s=($(dscp_fetch_stats $dev_10 10) + $(dscp_fetch_stats $dev_20 20))" + + ip vrf exec $vrf_name \ + ${PING} -Q $dscp_10 ${sip:+-I $sip} $dip \ + -c 10 -i 0.1 -w 2 &> /dev/null + + local -A t1s + eval "t1s=($(dscp_fetch_stats $dev_10 10) + $(dscp_fetch_stats $dev_20 20))" + + for key in ${!t0s[@]}; do + local expect + if ((key == dscp_10 || key == dscp_20)); then + expect=10 + else + expect=0 + fi + + local delta=$((t1s[key] - t0s[key])) + ((expect == delta)) + check_err $? "DSCP $key: Expected to capture $expect packets, got $delta." + done + + log_test "DSCP rewrite: $dscp_10-(prio $prio)-$dscp_20" +} + +test_dscp() +{ + for prio in {0..7}; do + dscp_ping_test v$h1 192.0.2.1 192.0.2.2 $prio $h1 $h2 + done +} + +trap cleanup EXIT + +setup_prepare +setup_wait + +tests_run + +exit $EXIT_STATUS -- cgit v1.2.3-58-ga151 From 40f98b9af943c3858958032ce1fb7bad449fac7b Mon Sep 17 00:00:00 2001 From: Xin Long <lucien.xin@gmail.com> Date: Fri, 27 Jul 2018 16:37:29 +0800 Subject: selftests: add a selftest for directed broadcast forwarding As Ido's suggestion, this patch is to add a selftest for directed broadcast forwarding with vrf. It does the assertion by checking the src IP of the echo-reply packet in ping_test_from. Signed-off-by: Xin Long <lucien.xin@gmail.com> Signed-off-by: David S. Miller <davem@davemloft.net> --- .../selftests/net/forwarding/router_broadcast.sh | 233 +++++++++++++++++++++ 1 file changed, 233 insertions(+) create mode 100755 tools/testing/selftests/net/forwarding/router_broadcast.sh (limited to 'tools') diff --git a/tools/testing/selftests/net/forwarding/router_broadcast.sh b/tools/testing/selftests/net/forwarding/router_broadcast.sh new file mode 100755 index 000000000000..7bd2ebb6e9de --- /dev/null +++ b/tools/testing/selftests/net/forwarding/router_broadcast.sh @@ -0,0 +1,233 @@ +#!/bin/bash +# SPDX-License-Identifier: GPL-2.0 + +ALL_TESTS="ping_ipv4" +NUM_NETIFS=6 +source lib.sh + +h1_create() +{ + vrf_create "vrf-h1" + ip link set dev $h1 master vrf-h1 + + ip link set dev vrf-h1 up + ip link set dev $h1 up + + ip address add 192.0.2.2/24 dev $h1 + + ip route add 198.51.100.0/24 vrf vrf-h1 nexthop via 192.0.2.1 + ip route add 198.51.200.0/24 vrf vrf-h1 nexthop via 192.0.2.1 +} + +h1_destroy() +{ + ip route del 198.51.200.0/24 vrf vrf-h1 + ip route del 198.51.100.0/24 vrf vrf-h1 + + ip address del 192.0.2.2/24 dev $h1 + + ip link set dev $h1 down + vrf_destroy "vrf-h1" +} + +h2_create() +{ + vrf_create "vrf-h2" + ip link set dev $h2 master vrf-h2 + + ip link set dev vrf-h2 up + ip link set dev $h2 up + + ip address add 198.51.100.2/24 dev $h2 + + ip route add 192.0.2.0/24 vrf vrf-h2 nexthop via 198.51.100.1 + ip route add 198.51.200.0/24 vrf vrf-h2 nexthop via 198.51.100.1 +} + +h2_destroy() +{ + ip route del 198.51.200.0/24 vrf vrf-h2 + ip route del 192.0.2.0/24 vrf vrf-h2 + + ip address del 198.51.100.2/24 dev $h2 + + ip link set dev $h2 down + vrf_destroy "vrf-h2" +} + +h3_create() +{ + vrf_create "vrf-h3" + ip link set dev $h3 master vrf-h3 + + ip link set dev vrf-h3 up + ip link set dev $h3 up + + ip address add 198.51.200.2/24 dev $h3 + + ip route add 192.0.2.0/24 vrf vrf-h3 nexthop via 198.51.200.1 + ip route add 198.51.100.0/24 vrf vrf-h3 nexthop via 198.51.200.1 +} + +h3_destroy() +{ + ip route del 198.51.100.0/24 vrf vrf-h3 + ip route del 192.0.2.0/24 vrf vrf-h3 + + ip address del 198.51.200.2/24 dev $h3 + + ip link set dev $h3 down + vrf_destroy "vrf-h3" +} + +router_create() +{ + ip link set dev $rp1 up + ip link set dev $rp2 up + ip link set dev $rp3 up + + ip address add 192.0.2.1/24 dev $rp1 + + ip address add 198.51.100.1/24 dev $rp2 + ip address add 198.51.200.1/24 dev $rp3 +} + +router_destroy() +{ + ip address del 198.51.200.1/24 dev $rp3 + ip address del 198.51.100.1/24 dev $rp2 + + ip address del 192.0.2.1/24 dev $rp1 + + ip link set dev $rp3 down + ip link set dev $rp2 down + ip link set dev $rp1 down +} + +setup_prepare() +{ + h1=${NETIFS[p1]} + rp1=${NETIFS[p2]} + + rp2=${NETIFS[p3]} + h2=${NETIFS[p4]} + + rp3=${NETIFS[p5]} + h3=${NETIFS[p6]} + + vrf_prepare + + h1_create + h2_create + h3_create + + router_create + + forwarding_enable +} + +cleanup() +{ + pre_cleanup + + forwarding_restore + + router_destroy + + h3_destroy + h2_destroy + h1_destroy + + vrf_cleanup +} + +bc_forwarding_disable() +{ + sysctl_set net.ipv4.conf.all.bc_forwarding 0 + sysctl_set net.ipv4.conf.$rp1.bc_forwarding 0 +} + +bc_forwarding_enable() +{ + sysctl_set net.ipv4.conf.all.bc_forwarding 1 + sysctl_set net.ipv4.conf.$rp1.bc_forwarding 1 +} + +bc_forwarding_restore() +{ + sysctl_restore net.ipv4.conf.$rp1.bc_forwarding + sysctl_restore net.ipv4.conf.all.bc_forwarding +} + +ping_test_from() +{ + local oif=$1 + local dip=$2 + local from=$3 + local fail=${4:-0} + + RET=0 + + log_info "ping $dip, expected reply from $from" + ip vrf exec $(master_name_get $oif) \ + $PING -I $oif $dip -c 10 -i 0.1 -w 2 -b 2>&1 | grep $from &> /dev/null + check_err_fail $fail $? +} + +ping_ipv4() +{ + sysctl_set net.ipv4.icmp_echo_ignore_broadcasts 0 + + bc_forwarding_disable + log_info "bc_forwarding disabled on r1 =>" + ping_test_from $h1 198.51.100.255 192.0.2.1 + log_test "h1 -> net2: reply from r1 (not forwarding)" + ping_test_from $h1 198.51.200.255 192.0.2.1 + log_test "h1 -> net3: reply from r1 (not forwarding)" + ping_test_from $h1 192.0.2.255 192.0.2.1 + log_test "h1 -> net1: reply from r1 (not dropping)" + ping_test_from $h1 255.255.255.255 192.0.2.1 + log_test "h1 -> 255.255.255.255: reply from r1 (not forwarding)" + + ping_test_from $h2 192.0.2.255 198.51.100.1 + log_test "h2 -> net1: reply from r1 (not forwarding)" + ping_test_from $h2 198.51.200.255 198.51.100.1 + log_test "h2 -> net3: reply from r1 (not forwarding)" + ping_test_from $h2 198.51.100.255 198.51.100.1 + log_test "h2 -> net2: reply from r1 (not dropping)" + ping_test_from $h2 255.255.255.255 198.51.100.1 + log_test "h2 -> 255.255.255.255: reply from r1 (not forwarding)" + bc_forwarding_restore + + bc_forwarding_enable + log_info "bc_forwarding enabled on r1 =>" + ping_test_from $h1 198.51.100.255 198.51.100.2 + log_test "h1 -> net2: reply from h2 (forwarding)" + ping_test_from $h1 198.51.200.255 198.51.200.2 + log_test "h1 -> net3: reply from h3 (forwarding)" + ping_test_from $h1 192.0.2.255 192.0.2.1 1 + log_test "h1 -> net1: no reply (dropping)" + ping_test_from $h1 255.255.255.255 192.0.2.1 + log_test "h1 -> 255.255.255.255: reply from r1 (not forwarding)" + + ping_test_from $h2 192.0.2.255 192.0.2.2 + log_test "h2 -> net1: reply from h1 (forwarding)" + ping_test_from $h2 198.51.200.255 198.51.200.2 + log_test "h2 -> net3: reply from h3 (forwarding)" + ping_test_from $h2 198.51.100.255 198.51.100.1 1 + log_test "h2 -> net2: no reply (dropping)" + ping_test_from $h2 255.255.255.255 198.51.100.1 + log_test "h2 -> 255.255.255.255: reply from r1 (not forwarding)" + bc_forwarding_restore + + sysctl_restore net.ipv4.icmp_echo_ignore_broadcasts +} + +trap cleanup EXIT + +setup_prepare +setup_wait + +tests_run + +exit $EXIT_STATUS -- cgit v1.2.3-58-ga151 From eef6ab8b7d3223e2ecd02a02f17523e93faba709 Mon Sep 17 00:00:00 2001 From: Petr Machata <petrm@mellanox.com> Date: Sat, 28 Jul 2018 00:48:13 +0200 Subject: selftests: mlxsw: qos_dscp_bridge: Fix There are two problems in this test case: - When indexing in bash associative array, the subscript is interpreted as string, not as a variable name to be expanded. - The keys stored to t0s and t1s are not DSCP values, but priority + base (i.e. the logical DSCP value, not the full bitfield value). In combination these two bugs conspire to make the test just work, except it doesn't really test anything and always passes. Fix the above two problems in obvious manner. Signed-off-by: Petr Machata <petrm@mellanox.com> Signed-off-by: David S. Miller <davem@davemloft.net> --- tools/testing/selftests/drivers/net/mlxsw/qos_dscp_bridge.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'tools') diff --git a/tools/testing/selftests/drivers/net/mlxsw/qos_dscp_bridge.sh b/tools/testing/selftests/drivers/net/mlxsw/qos_dscp_bridge.sh index 418319f19108..cc527660a022 100755 --- a/tools/testing/selftests/drivers/net/mlxsw/qos_dscp_bridge.sh +++ b/tools/testing/selftests/drivers/net/mlxsw/qos_dscp_bridge.sh @@ -217,13 +217,13 @@ dscp_ping_test() for key in ${!t0s[@]}; do local expect - if ((key == dscp_10 || key == dscp_20)); then + if ((key == prio+10 || key == prio+20)); then expect=10 else expect=0 fi - local delta=$((t1s[key] - t0s[key])) + local delta=$((t1s[$key] - t0s[$key])) ((expect == delta)) check_err $? "DSCP $key: Expected to capture $expect packets, got $delta." done -- cgit v1.2.3-58-ga151 From e094574f9bf806fb89a285c0a0263a46a1d536f9 Mon Sep 17 00:00:00 2001 From: Petr Machata <petrm@mellanox.com> Date: Mon, 30 Jul 2018 16:39:46 +0200 Subject: selftests: forwarding: lib: Add require_command() The logic for testing whether a certain command is available is used several times in the current code base. The tests in follow-up patches add more requirements like that. Therefore extract the logic into a named function, require_command(), that can be used directly from lib.sh as well as from any test that wishes to declare dependence on some command. Signed-off-by: Petr Machata <petrm@mellanox.com> Signed-off-by: David S. Miller <davem@davemloft.net> --- tools/testing/selftests/net/forwarding/lib.sh | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) (limited to 'tools') diff --git a/tools/testing/selftests/net/forwarding/lib.sh b/tools/testing/selftests/net/forwarding/lib.sh index 158d59ffee40..81e36157bf16 100644 --- a/tools/testing/selftests/net/forwarding/lib.sh +++ b/tools/testing/selftests/net/forwarding/lib.sh @@ -62,15 +62,18 @@ if [[ "$CHECK_TC" = "yes" ]]; then check_tc_version fi -if [[ ! -x "$(command -v jq)" ]]; then - echo "SKIP: jq not installed" - exit 1 -fi +require_command() +{ + local cmd=$1; shift -if [[ ! -x "$(command -v $MZ)" ]]; then - echo "SKIP: $MZ not installed" - exit 1 -fi + if [[ ! -x "$(command -v "$cmd")" ]]; then + echo "SKIP: $cmd not installed" + exit 1 + fi +} + +require_command jq +require_command $MZ if [[ ! -v NUM_NETIFS ]]; then echo "SKIP: importer does not define \"NUM_NETIFS\"" -- cgit v1.2.3-58-ga151 From 9d9e6bde3df4ec6af0cc7dac328f8d8555aa36f9 Mon Sep 17 00:00:00 2001 From: Petr Machata <petrm@mellanox.com> Date: Mon, 30 Jul 2018 16:39:52 +0200 Subject: selftests: forwarding: lib: Support team devices Add team_create() and team_destroy() to manage team netdevices. Signed-off-by: Petr Machata <petrm@mellanox.com> Signed-off-by: David S. Miller <davem@davemloft.net> --- tools/testing/selftests/net/forwarding/lib.sh | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) (limited to 'tools') diff --git a/tools/testing/selftests/net/forwarding/lib.sh b/tools/testing/selftests/net/forwarding/lib.sh index 81e36157bf16..3a4eba4f0d08 100644 --- a/tools/testing/selftests/net/forwarding/lib.sh +++ b/tools/testing/selftests/net/forwarding/lib.sh @@ -8,6 +8,7 @@ PING=${PING:=ping} PING6=${PING6:=ping6} MZ=${MZ:=mausezahn} +TEAMD=${TEAMD:=teamd} WAIT_TIME=${WAIT_TIME:=5} PAUSE_ON_FAIL=${PAUSE_ON_FAIL:=no} PAUSE_ON_CLEANUP=${PAUSE_ON_CLEANUP:=no} @@ -425,6 +426,28 @@ vlan_destroy() ip link del dev $name } +team_create() +{ + local if_name=$1; shift + local mode=$1; shift + + require_command $TEAMD + $TEAMD -t $if_name -d -c '{"runner": {"name": "'$mode'"}}' + for slave in "$@"; do + ip link set dev $slave down + ip link set dev $slave master $if_name + ip link set dev $slave up + done + ip link set dev $if_name up +} + +team_destroy() +{ + local if_name=$1; shift + + $TEAMD -t $if_name -k +} + master_name_get() { local if_name=$1 -- cgit v1.2.3-58-ga151 From ca70a5623823841c179cf792b549d63b5fbe32ab Mon Sep 17 00:00:00 2001 From: Petr Machata <petrm@mellanox.com> Date: Mon, 30 Jul 2018 16:39:57 +0200 Subject: selftests: forwarding: Introduce $ARPING Instead of relying on "arping" being installed everywhere under that name, introduce a variable $ARPING like the other tools do. Convert an existing test, mirror_gre_vlan_bridge_1q.sh to require_command $ARPING and then invoke arping through the variable. Signed-off-by: Petr Machata <petrm@mellanox.com> Signed-off-by: David S. Miller <davem@davemloft.net> --- tools/testing/selftests/net/forwarding/lib.sh | 1 + tools/testing/selftests/net/forwarding/mirror_gre_vlan_bridge_1q.sh | 6 ++++-- 2 files changed, 5 insertions(+), 2 deletions(-) (limited to 'tools') diff --git a/tools/testing/selftests/net/forwarding/lib.sh b/tools/testing/selftests/net/forwarding/lib.sh index 3a4eba4f0d08..843a6715924f 100644 --- a/tools/testing/selftests/net/forwarding/lib.sh +++ b/tools/testing/selftests/net/forwarding/lib.sh @@ -8,6 +8,7 @@ PING=${PING:=ping} PING6=${PING6:=ping6} MZ=${MZ:=mausezahn} +ARPING=${ARPING:=arping} TEAMD=${TEAMD:=teamd} WAIT_TIME=${WAIT_TIME:=5} PAUSE_ON_FAIL=${PAUSE_ON_FAIL:=no} diff --git a/tools/testing/selftests/net/forwarding/mirror_gre_vlan_bridge_1q.sh b/tools/testing/selftests/net/forwarding/mirror_gre_vlan_bridge_1q.sh index d3e75bb6a2d8..204b25f13934 100755 --- a/tools/testing/selftests/net/forwarding/mirror_gre_vlan_bridge_1q.sh +++ b/tools/testing/selftests/net/forwarding/mirror_gre_vlan_bridge_1q.sh @@ -28,6 +28,8 @@ source mirror_lib.sh source mirror_gre_lib.sh source mirror_gre_topo_lib.sh +require_command $ARPING + setup_prepare() { h1=${NETIFS[p1]} @@ -149,7 +151,7 @@ test_span_gre_forbidden_egress() bridge vlan add dev $swp3 vid 555 # Re-prime FDB - arping -I br1.555 192.0.2.130 -fqc 1 + $ARPING -I br1.555 192.0.2.130 -fqc 1 sleep 1 quick_test_span_gre_dir $tundev ingress @@ -223,7 +225,7 @@ test_span_gre_fdb_roaming() bridge fdb del dev $swp2 $h3mac vlan 555 master # Re-prime FDB - arping -I br1.555 192.0.2.130 -fqc 1 + $ARPING -I br1.555 192.0.2.130 -fqc 1 sleep 1 quick_test_span_gre_dir $tundev ingress -- cgit v1.2.3-58-ga151 From a9b33b2001b6e2b2a0df266977472db497a5d1ca Mon Sep 17 00:00:00 2001 From: Petr Machata <petrm@mellanox.com> Date: Mon, 30 Jul 2018 16:40:02 +0200 Subject: selftests: forwarding: Test mirror-to-gretap w/ UL team Test for "tc action mirred egress mirror" that mirrors to gretap when the underlay route points at a VLAN-aware bridge (802.1q), and the traffic egresses the bridge through a team device. Test upping and downing individual team device slaves and verify the traffic flows as expected. Signed-off-by: Petr Machata <petrm@mellanox.com> Signed-off-by: David S. Miller <davem@davemloft.net> --- .../net/forwarding/mirror_gre_bridge_1q_lag.sh | 283 +++++++++++++++++++++ 1 file changed, 283 insertions(+) create mode 100755 tools/testing/selftests/net/forwarding/mirror_gre_bridge_1q_lag.sh (limited to 'tools') diff --git a/tools/testing/selftests/net/forwarding/mirror_gre_bridge_1q_lag.sh b/tools/testing/selftests/net/forwarding/mirror_gre_bridge_1q_lag.sh new file mode 100755 index 000000000000..61844caf671e --- /dev/null +++ b/tools/testing/selftests/net/forwarding/mirror_gre_bridge_1q_lag.sh @@ -0,0 +1,283 @@ +#!/bin/bash +# SPDX-License-Identifier: GPL-2.0 + +# Test for "tc action mirred egress mirror" when the underlay route points at a +# bridge device with vlan filtering (802.1q), and the egress device is a team +# device. +# +# +----------------------+ +----------------------+ +# | H1 | | H2 | +# | + $h1.333 | | $h1.555 + | +# | | 192.0.2.1/28 | | 192.0.2.18/28 | | +# +-----|----------------+ +----------------|-----+ +# | $h1 | +# +--------------------------------+------------------------------+ +# | +# +--------------------------------------|------------------------------------+ +# | SW o---> mirror | +# | | | +# | +--------------------------------+------------------------------+ | +# | | $swp1 | | +# | + $swp1.333 $swp1.555 + | +# | 192.0.2.2/28 192.0.2.17/28 | +# | | +# | +-----------------------------------------------------------------------+ | +# | | BR1 (802.1q) | | +# | | + lag (team) 192.0.2.129/28 | | +# | | / \ 2001:db8:2::1/64 | | +# | +---/---\---------------------------------------------------------------+ | +# | / \ ^ | +# | | \ + gt4 (gretap) | | +# | | \ loc=192.0.2.129 | | +# | | \ rem=192.0.2.130 -+ | +# | | \ ttl=100 | +# | | \ tos=inherit | +# | | \ | +# | | \_________________________________ | +# | | \ | +# | + $swp3 + $swp4 | +# +---|------------------------------------------------|----------------------+ +# | | +# +---|----------------------+ +---|----------------------+ +# | + $h3 H3 | | + $h4 H4 | +# | 192.0.2.130/28 | | 192.0.2.130/28 | +# | 2001:db8:2::2/64 | | 2001:db8:2::2/64 | +# +--------------------------+ +--------------------------+ + +ALL_TESTS=" + test_mirror_gretap_first + test_mirror_gretap_second +" + +NUM_NETIFS=6 +source lib.sh +source mirror_lib.sh +source mirror_gre_lib.sh + +require_command $ARPING + +vlan_host_create() +{ + local if_name=$1; shift + local vid=$1; shift + local vrf_name=$1; shift + local ips=("${@}") + + vrf_create $vrf_name + ip link set dev $vrf_name up + vlan_create $if_name $vid $vrf_name "${ips[@]}" +} + +vlan_host_destroy() +{ + local if_name=$1; shift + local vid=$1; shift + local vrf_name=$1; shift + + vlan_destroy $if_name $vid + ip link set dev $vrf_name down + vrf_destroy $vrf_name +} + +h1_create() +{ + vlan_host_create $h1 333 vrf-h1 192.0.2.1/28 + ip -4 route add 192.0.2.16/28 vrf vrf-h1 nexthop via 192.0.2.2 +} + +h1_destroy() +{ + ip -4 route del 192.0.2.16/28 vrf vrf-h1 + vlan_host_destroy $h1 333 vrf-h1 +} + +h2_create() +{ + vlan_host_create $h1 555 vrf-h2 192.0.2.18/28 + ip -4 route add 192.0.2.0/28 vrf vrf-h2 nexthop via 192.0.2.17 +} + +h2_destroy() +{ + ip -4 route del 192.0.2.0/28 vrf vrf-h2 + vlan_host_destroy $h1 555 vrf-h2 +} + +h3_create() +{ + simple_if_init $h3 192.0.2.130/28 + tc qdisc add dev $h3 clsact +} + +h3_destroy() +{ + tc qdisc del dev $h3 clsact + simple_if_fini $h3 192.0.2.130/28 +} + +h4_create() +{ + simple_if_init $h4 192.0.2.130/28 + tc qdisc add dev $h4 clsact +} + +h4_destroy() +{ + tc qdisc del dev $h4 clsact + simple_if_fini $h4 192.0.2.130/28 +} + +switch_create() +{ + ip link set dev $swp1 up + tc qdisc add dev $swp1 clsact + vlan_create $swp1 333 "" 192.0.2.2/28 + vlan_create $swp1 555 "" 192.0.2.17/28 + + tunnel_create gt4 gretap 192.0.2.129 192.0.2.130 \ + ttl 100 tos inherit + + ip link set dev $swp3 up + ip link set dev $swp4 up + + ip link add name br1 type bridge vlan_filtering 1 + ip link set dev br1 up + __addr_add_del br1 add 192.0.2.129/32 + ip -4 route add 192.0.2.130/32 dev br1 + + team_create lag loadbalance $swp3 $swp4 + ip link set dev lag master br1 +} + +switch_destroy() +{ + ip link set dev lag nomaster + team_destroy lag + + ip -4 route del 192.0.2.130/32 dev br1 + __addr_add_del br1 del 192.0.2.129/32 + ip link set dev br1 down + ip link del dev br1 + + ip link set dev $swp4 down + ip link set dev $swp3 down + + tunnel_destroy gt4 + + vlan_destroy $swp1 555 + vlan_destroy $swp1 333 + tc qdisc del dev $swp1 clsact + ip link set dev $swp1 down +} + +setup_prepare() +{ + h1=${NETIFS[p1]} + swp1=${NETIFS[p2]} + + swp3=${NETIFS[p3]} + h3=${NETIFS[p4]} + + swp4=${NETIFS[p5]} + h4=${NETIFS[p6]} + + vrf_prepare + + ip link set dev $h1 up + h1_create + h2_create + h3_create + h4_create + switch_create + + trap_install $h3 ingress + trap_install $h4 ingress +} + +cleanup() +{ + pre_cleanup + + trap_uninstall $h4 ingress + trap_uninstall $h3 ingress + + switch_destroy + h4_destroy + h3_destroy + h2_destroy + h1_destroy + ip link set dev $h1 down + + vrf_cleanup +} + +test_lag_slave() +{ + local host_dev=$1; shift + local up_dev=$1; shift + local down_dev=$1; shift + local what=$1; shift + + RET=0 + + mirror_install $swp1 ingress gt4 \ + "proto 802.1q flower vlan_id 333 $tcflags" + + # Test connectivity through $up_dev when $down_dev is set down. + ip link set dev $down_dev down + setup_wait_dev $up_dev + setup_wait_dev $host_dev + $ARPING -I br1 192.0.2.130 -qfc 1 + sleep 2 + mirror_test vrf-h1 192.0.2.1 192.0.2.18 $host_dev 1 10 + + # Test lack of connectivity when both slaves are down. + ip link set dev $up_dev down + sleep 2 + mirror_test vrf-h1 192.0.2.1 192.0.2.18 $h3 1 0 + mirror_test vrf-h1 192.0.2.1 192.0.2.18 $h4 1 0 + + ip link set dev $up_dev up + ip link set dev $down_dev up + mirror_uninstall $swp1 ingress + + log_test "$what ($tcflags)" +} + +test_mirror_gretap_first() +{ + test_lag_slave $h3 $swp3 $swp4 "mirror to gretap: LAG first slave" +} + +test_mirror_gretap_second() +{ + test_lag_slave $h4 $swp4 $swp3 "mirror to gretap: LAG second slave" +} + +test_all() +{ + slow_path_trap_install $swp1 ingress + slow_path_trap_install $swp1 egress + + tests_run + + slow_path_trap_uninstall $swp1 egress + slow_path_trap_uninstall $swp1 ingress +} + +trap cleanup EXIT + +setup_prepare +setup_wait + +tcflags="skip_hw" +test_all + +if ! tc_offload_check; then + echo "WARN: Could not test offloaded functionality" +else + tcflags="skip_sw" + test_all +fi + +exit $EXIT_STATUS -- cgit v1.2.3-58-ga151 From 541c6ce30f6738a05a869606f50c14dea89d12e2 Mon Sep 17 00:00:00 2001 From: Petr Machata <petrm@mellanox.com> Date: Mon, 30 Jul 2018 16:40:07 +0200 Subject: selftests: forwarding: Test mirror-to-gretap w/ UL team LACP This tests mirror-to-gretap when an underlay packet path includes a team device which is not in loadbalance mode, but in LACP mode. The test manipulates LAG membership to achieve changes in txability, thus making sure that a driver that offloads mirror-to-gretap doesn't just consider upness of a device. Signed-off-by: Petr Machata <petrm@mellanox.com> Signed-off-by: David S. Miller <davem@davemloft.net> --- .../net/forwarding/mirror_gre_lag_lacp.sh | 285 +++++++++++++++++++++ 1 file changed, 285 insertions(+) create mode 100755 tools/testing/selftests/net/forwarding/mirror_gre_lag_lacp.sh (limited to 'tools') diff --git a/tools/testing/selftests/net/forwarding/mirror_gre_lag_lacp.sh b/tools/testing/selftests/net/forwarding/mirror_gre_lag_lacp.sh new file mode 100755 index 000000000000..9edf4cb104a8 --- /dev/null +++ b/tools/testing/selftests/net/forwarding/mirror_gre_lag_lacp.sh @@ -0,0 +1,285 @@ +#!/bin/bash +# SPDX-License-Identifier: GPL-2.0 + +# Test for "tc action mirred egress mirror" when the underlay route points at a +# team device. +# +# +----------------------+ +----------------------+ +# | H1 | | H2 | +# | + $h1.333 | | $h1.555 + | +# | | 192.0.2.1/28 | | 192.0.2.18/28 | | +# +----|-----------------+ +----------------|-----+ +# | $h1 | +# +---------------------------------+------------------------------+ +# | +# +--------------------------------------|------------------------------------+ +# | SW o---> mirror | +# | | | +# | +----------------------------------+------------------------------+ | +# | | $swp1 | | +# | + $swp1.333 $swp1.555 + | +# | 192.0.2.2/28 192.0.2.17/28 | +# | | +# | | +# | + gt4 (gretap) ,-> + lag1 (team) | +# | loc=192.0.2.129 | | 192.0.2.129/28 | +# | rem=192.0.2.130 --' | | +# | ttl=100 | | +# | tos=inherit | | +# | _____________________|______________________ | +# | / \ | +# | / \ | +# | + $swp3 + $swp4 | +# +---|------------------------------------------------|----------------------+ +# | | +# +---|------------------------------------------------|----------------------+ +# | + $h3 + $h4 H3 | +# | \ / | +# | \____________________________________________/ | +# | | | +# | + lag2 (team) | +# | 192.0.2.130/28 | +# | | +# +---------------------------------------------------------------------------+ + +ALL_TESTS=" + test_mirror_gretap_first + test_mirror_gretap_second +" + +NUM_NETIFS=6 +source lib.sh +source mirror_lib.sh +source mirror_gre_lib.sh + +require_command $ARPING + +vlan_host_create() +{ + local if_name=$1; shift + local vid=$1; shift + local vrf_name=$1; shift + local ips=("${@}") + + vrf_create $vrf_name + ip link set dev $vrf_name up + vlan_create $if_name $vid $vrf_name "${ips[@]}" +} + +vlan_host_destroy() +{ + local if_name=$1; shift + local vid=$1; shift + local vrf_name=$1; shift + + vlan_destroy $if_name $vid + ip link set dev $vrf_name down + vrf_destroy $vrf_name +} + +h1_create() +{ + vlan_host_create $h1 333 vrf-h1 192.0.2.1/28 + ip -4 route add 192.0.2.16/28 vrf vrf-h1 nexthop via 192.0.2.2 +} + +h1_destroy() +{ + ip -4 route del 192.0.2.16/28 vrf vrf-h1 + vlan_host_destroy $h1 333 vrf-h1 +} + +h2_create() +{ + vlan_host_create $h1 555 vrf-h2 192.0.2.18/28 + ip -4 route add 192.0.2.0/28 vrf vrf-h2 nexthop via 192.0.2.17 +} + +h2_destroy() +{ + ip -4 route del 192.0.2.0/28 vrf vrf-h2 + vlan_host_destroy $h1 555 vrf-h2 +} + +h3_create_team() +{ + team_create lag2 lacp $h3 $h4 + __simple_if_init lag2 vrf-h3 192.0.2.130/32 + ip -4 route add vrf vrf-h3 192.0.2.129/32 dev lag2 +} + +h3_destroy_team() +{ + ip -4 route del vrf vrf-h3 192.0.2.129/32 dev lag2 + __simple_if_fini lag2 192.0.2.130/32 + team_destroy lag2 + + ip link set dev $h3 down + ip link set dev $h4 down +} + +h3_create() +{ + vrf_create vrf-h3 + ip link set dev vrf-h3 up + tc qdisc add dev $h3 clsact + tc qdisc add dev $h4 clsact + h3_create_team +} + +h3_destroy() +{ + h3_destroy_team + tc qdisc del dev $h4 clsact + tc qdisc del dev $h3 clsact + ip link set dev vrf-h3 down + vrf_destroy vrf-h3 +} + +switch_create() +{ + ip link set dev $swp1 up + tc qdisc add dev $swp1 clsact + vlan_create $swp1 333 "" 192.0.2.2/28 + vlan_create $swp1 555 "" 192.0.2.17/28 + + tunnel_create gt4 gretap 192.0.2.129 192.0.2.130 \ + ttl 100 tos inherit + + ip link set dev $swp3 up + ip link set dev $swp4 up + team_create lag1 lacp $swp3 $swp4 + __addr_add_del lag1 add 192.0.2.129/32 + ip -4 route add 192.0.2.130/32 dev lag1 +} + +switch_destroy() +{ + ip -4 route del 192.0.2.130/32 dev lag1 + __addr_add_del lag1 del 192.0.2.129/32 + team_destroy lag1 + + ip link set dev $swp4 down + ip link set dev $swp3 down + + tunnel_destroy gt4 + + vlan_destroy $swp1 555 + vlan_destroy $swp1 333 + tc qdisc del dev $swp1 clsact + ip link set dev $swp1 down +} + +setup_prepare() +{ + h1=${NETIFS[p1]} + swp1=${NETIFS[p2]} + + swp3=${NETIFS[p3]} + h3=${NETIFS[p4]} + + swp4=${NETIFS[p5]} + h4=${NETIFS[p6]} + + vrf_prepare + + ip link set dev $h1 up + h1_create + h2_create + h3_create + switch_create + + trap_install $h3 ingress + trap_install $h4 ingress +} + +cleanup() +{ + pre_cleanup + + trap_uninstall $h4 ingress + trap_uninstall $h3 ingress + + switch_destroy + h3_destroy + h2_destroy + h1_destroy + ip link set dev $h1 down + + vrf_cleanup +} + +test_lag_slave() +{ + local up_dev=$1; shift + local down_dev=$1; shift + local what=$1; shift + + RET=0 + + mirror_install $swp1 ingress gt4 \ + "proto 802.1q flower vlan_id 333 $tcflags" + + # Move $down_dev away from the team. That will prompt change in + # txability of the connected device, without changing its upness. The + # driver should notice the txability change and move the traffic to the + # other slave. + ip link set dev $down_dev nomaster + sleep 2 + mirror_test vrf-h1 192.0.2.1 192.0.2.18 $up_dev 1 10 + + # Test lack of connectivity when neither slave is txable. + ip link set dev $up_dev nomaster + sleep 2 + mirror_test vrf-h1 192.0.2.1 192.0.2.18 $h3 1 0 + mirror_test vrf-h1 192.0.2.1 192.0.2.18 $h4 1 0 + mirror_uninstall $swp1 ingress + + # Recreate H3's team device, because mlxsw, which this test is + # predominantly mean to test, requires a bottom-up construction and + # doesn't allow enslavement to a device that already has an upper. + h3_destroy_team + h3_create_team + # Wait for ${h,swp}{3,4}. + setup_wait + + log_test "$what ($tcflags)" +} + +test_mirror_gretap_first() +{ + test_lag_slave $h3 $h4 "mirror to gretap: LAG first slave" +} + +test_mirror_gretap_second() +{ + test_lag_slave $h4 $h3 "mirror to gretap: LAG second slave" +} + +test_all() +{ + slow_path_trap_install $swp1 ingress + slow_path_trap_install $swp1 egress + + tests_run + + slow_path_trap_uninstall $swp1 egress + slow_path_trap_uninstall $swp1 ingress +} + +trap cleanup EXIT + +setup_prepare +setup_wait + +tcflags="skip_hw" +test_all + +if ! tc_offload_check; then + echo "WARN: Could not test offloaded functionality" +else + tcflags="skip_sw" + test_all +fi + +exit $EXIT_STATUS -- cgit v1.2.3-58-ga151 From 1ce6a9fc154935d9db771173ecde03fa9b42df4a Mon Sep 17 00:00:00 2001 From: Thomas Richter <tmricht@linux.ibm.com> Date: Mon, 30 Jul 2018 10:53:23 +0200 Subject: bpf: fix build error in libbpf with EXTRA_CFLAGS="-Wp, -D_FORTIFY_SOURCE=2 -O2" MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Commit 531b014e7a2f ("tools: bpf: make use of reallocarray") causes a compiler error when building the perf tool in the linux-next tree. Compile file tools/lib/bpf/libbpf.c on a FEDORA 28 installation with gcc compiler version: gcc (GCC) 8.0.1 20180324 (Red Hat 8.0.1-0.20) shows this error message: [root@p23lp27] # make V=1 EXTRA_CFLAGS="-Wp,-D_FORTIFY_SOURCE=2 -O2" [...] make -f /home6/tmricht/linux-next/tools/build/Makefile.build dir=./util/scripting-engines obj=libperf libbpf.c: In function ‘bpf_object__elf_collect’: libbpf.c:811:15: error: ignoring return value of ‘strerror_r’, declared with attribute warn_unused_result [-Werror=unused-result] strerror_r(-err, errmsg, sizeof(errmsg)); ^ cc1: all warnings being treated as errors mv: cannot stat './.libbpf.o.tmp': No such file or directory /home6/tmricht/linux-next/tools/build/Makefile.build:96: recipe for target 'libbpf.o' failed Replace all occurrences of strerror() by calls to strerror_r(). To keep the compiler quiet also use the return value from strerror_r() otherwise a 'variable set but not use' warning which is treated as error terminates the compile. Fixes: 531b014e7a2f ("tools: bpf: make use of reallocarray") Suggested-by: Jakub Kicinski <jakub.kicinski@netronome.com> Suggested-by: Daniel Borkmann <daniel@iogearbox.net> Signed-off-by: Thomas Richter <tmricht@linux.ibm.com> Reviewed-by: Hendrik Brueckner <brueckner@linux.ibm.com> Reviewed-by: Jakub Kicinski <jakub.kicinski@netronome.com> Signed-off-by: Daniel Borkmann <daniel@iogearbox.net> --- tools/lib/bpf/libbpf.c | 42 +++++++++++++++++++++++++++++------------- 1 file changed, 29 insertions(+), 13 deletions(-) (limited to 'tools') diff --git a/tools/lib/bpf/libbpf.c b/tools/lib/bpf/libbpf.c index 857d3d16968e..79fc7ed6995a 100644 --- a/tools/lib/bpf/libbpf.c +++ b/tools/lib/bpf/libbpf.c @@ -467,8 +467,10 @@ static int bpf_object__elf_init(struct bpf_object *obj) } else { obj->efile.fd = open(obj->path, O_RDONLY); if (obj->efile.fd < 0) { - pr_warning("failed to open %s: %s\n", obj->path, - strerror(errno)); + char errmsg[STRERR_BUFSIZE]; + char *cp = strerror_r(errno, errmsg, sizeof(errmsg)); + + pr_warning("failed to open %s: %s\n", obj->path, cp); return -errno; } @@ -807,10 +809,11 @@ static int bpf_object__elf_collect(struct bpf_object *obj) data->d_size, name, idx); if (err) { char errmsg[STRERR_BUFSIZE]; + char *cp = strerror_r(-err, errmsg, + sizeof(errmsg)); - strerror_r(-err, errmsg, sizeof(errmsg)); pr_warning("failed to alloc program %s (%s): %s", - name, obj->path, errmsg); + name, obj->path, cp); } } else if (sh.sh_type == SHT_REL) { void *reloc = obj->efile.reloc; @@ -1104,6 +1107,7 @@ bpf_object__create_maps(struct bpf_object *obj) for (i = 0; i < obj->nr_maps; i++) { struct bpf_map *map = &obj->maps[i]; struct bpf_map_def *def = &map->def; + char *cp, errmsg[STRERR_BUFSIZE]; int *pfd = &map->fd; if (map->fd >= 0) { @@ -1131,8 +1135,9 @@ bpf_object__create_maps(struct bpf_object *obj) *pfd = bpf_create_map_xattr(&create_attr); if (*pfd < 0 && create_attr.btf_key_type_id) { + cp = strerror_r(errno, errmsg, sizeof(errmsg)); pr_warning("Error in bpf_create_map_xattr(%s):%s(%d). Retrying without BTF.\n", - map->name, strerror(errno), errno); + map->name, cp, errno); create_attr.btf_fd = 0; create_attr.btf_key_type_id = 0; create_attr.btf_value_type_id = 0; @@ -1145,9 +1150,9 @@ bpf_object__create_maps(struct bpf_object *obj) size_t j; err = *pfd; + cp = strerror_r(errno, errmsg, sizeof(errmsg)); pr_warning("failed to create map (name: '%s'): %s\n", - map->name, - strerror(errno)); + map->name, cp); for (j = 0; j < i; j++) zclose(obj->maps[j].fd); return err; @@ -1299,6 +1304,7 @@ load_program(enum bpf_prog_type type, enum bpf_attach_type expected_attach_type, char *license, u32 kern_version, int *pfd, int prog_ifindex) { struct bpf_load_program_attr load_attr; + char *cp, errmsg[STRERR_BUFSIZE]; char *log_buf; int ret; @@ -1328,7 +1334,8 @@ load_program(enum bpf_prog_type type, enum bpf_attach_type expected_attach_type, } ret = -LIBBPF_ERRNO__LOAD; - pr_warning("load bpf program failed: %s\n", strerror(errno)); + cp = strerror_r(errno, errmsg, sizeof(errmsg)); + pr_warning("load bpf program failed: %s\n", cp); if (log_buf && log_buf[0] != '\0') { ret = -LIBBPF_ERRNO__VERIFY; @@ -1627,6 +1634,7 @@ out: static int check_path(const char *path) { + char *cp, errmsg[STRERR_BUFSIZE]; struct statfs st_fs; char *dname, *dir; int err = 0; @@ -1640,7 +1648,8 @@ static int check_path(const char *path) dir = dirname(dname); if (statfs(dir, &st_fs)) { - pr_warning("failed to statfs %s: %s\n", dir, strerror(errno)); + cp = strerror_r(errno, errmsg, sizeof(errmsg)); + pr_warning("failed to statfs %s: %s\n", dir, cp); err = -errno; } free(dname); @@ -1656,6 +1665,7 @@ static int check_path(const char *path) int bpf_program__pin_instance(struct bpf_program *prog, const char *path, int instance) { + char *cp, errmsg[STRERR_BUFSIZE]; int err; err = check_path(path); @@ -1674,7 +1684,8 @@ int bpf_program__pin_instance(struct bpf_program *prog, const char *path, } if (bpf_obj_pin(prog->instances.fds[instance], path)) { - pr_warning("failed to pin program: %s\n", strerror(errno)); + cp = strerror_r(errno, errmsg, sizeof(errmsg)); + pr_warning("failed to pin program: %s\n", cp); return -errno; } pr_debug("pinned program '%s'\n", path); @@ -1684,13 +1695,16 @@ int bpf_program__pin_instance(struct bpf_program *prog, const char *path, static int make_dir(const char *path) { + char *cp, errmsg[STRERR_BUFSIZE]; int err = 0; if (mkdir(path, 0700) && errno != EEXIST) err = -errno; - if (err) - pr_warning("failed to mkdir %s: %s\n", path, strerror(-err)); + if (err) { + cp = strerror_r(-err, errmsg, sizeof(errmsg)); + pr_warning("failed to mkdir %s: %s\n", path, cp); + } return err; } @@ -1737,6 +1751,7 @@ int bpf_program__pin(struct bpf_program *prog, const char *path) int bpf_map__pin(struct bpf_map *map, const char *path) { + char *cp, errmsg[STRERR_BUFSIZE]; int err; err = check_path(path); @@ -1749,7 +1764,8 @@ int bpf_map__pin(struct bpf_map *map, const char *path) } if (bpf_obj_pin(map->fd, path)) { - pr_warning("failed to pin map: %s\n", strerror(errno)); + cp = strerror_r(errno, errmsg, sizeof(errmsg)); + pr_warning("failed to pin map: %s\n", cp); return -errno; } -- cgit v1.2.3-58-ga151 From a40b712e4c4d09f6fa234872384da9705443d4e9 Mon Sep 17 00:00:00 2001 From: Andrey Ignatov <rdna@fb.com> Date: Mon, 30 Jul 2018 17:42:29 -0700 Subject: bpf: Sync bpf.h to tools/ Sync bpf_get_socket_cookie() related bpf UAPI changes to tools/. Signed-off-by: Andrey Ignatov <rdna@fb.com> Acked-by: Yonghong Song <yhs@fb.com> Signed-off-by: Daniel Borkmann <daniel@iogearbox.net> --- tools/include/uapi/linux/bpf.h | 14 ++++++++++++++ 1 file changed, 14 insertions(+) (limited to 'tools') diff --git a/tools/include/uapi/linux/bpf.h b/tools/include/uapi/linux/bpf.h index 870113916cac..0ebaaf7f3568 100644 --- a/tools/include/uapi/linux/bpf.h +++ b/tools/include/uapi/linux/bpf.h @@ -1371,6 +1371,20 @@ union bpf_attr { * A 8-byte long non-decreasing number on success, or 0 if the * socket field is missing inside *skb*. * + * u64 bpf_get_socket_cookie(struct bpf_sock_addr *ctx) + * Description + * Equivalent to bpf_get_socket_cookie() helper that accepts + * *skb*, but gets socket from **struct bpf_sock_addr** contex. + * Return + * A 8-byte long non-decreasing number. + * + * u64 bpf_get_socket_cookie(struct bpf_sock_ops *ctx) + * Description + * Equivalent to bpf_get_socket_cookie() helper that accepts + * *skb*, but gets socket from **struct bpf_sock_ops** contex. + * Return + * A 8-byte long non-decreasing number. + * * u32 bpf_get_socket_uid(struct sk_buff *skb) * Return * The owner UID of the socket associated to *skb*. If the socket -- cgit v1.2.3-58-ga151 From 0289a2cca0a5b75a0167243429d9163ec3fdf279 Mon Sep 17 00:00:00 2001 From: Andrey Ignatov <rdna@fb.com> Date: Mon, 30 Jul 2018 17:42:30 -0700 Subject: selftests/bpf: Add bpf_get_socket_cookie to bpf_helpers.h Add missing helper to bpf_helpers.h that is used in tests and samples. Signed-off-by: Andrey Ignatov <rdna@fb.com> Acked-by: Yonghong Song <yhs@fb.com> Signed-off-by: Daniel Borkmann <daniel@iogearbox.net> --- tools/testing/selftests/bpf/bpf_helpers.h | 2 ++ 1 file changed, 2 insertions(+) (limited to 'tools') diff --git a/tools/testing/selftests/bpf/bpf_helpers.h b/tools/testing/selftests/bpf/bpf_helpers.h index f2f28b6c8915..19a424483f6e 100644 --- a/tools/testing/selftests/bpf/bpf_helpers.h +++ b/tools/testing/selftests/bpf/bpf_helpers.h @@ -65,6 +65,8 @@ static int (*bpf_xdp_adjust_head)(void *ctx, int offset) = (void *) BPF_FUNC_xdp_adjust_head; static int (*bpf_xdp_adjust_meta)(void *ctx, int offset) = (void *) BPF_FUNC_xdp_adjust_meta; +static int (*bpf_get_socket_cookie)(void *ctx) = + (void *) BPF_FUNC_get_socket_cookie; static int (*bpf_setsockopt)(void *ctx, int level, int optname, void *optval, int optlen) = (void *) BPF_FUNC_setsockopt; -- cgit v1.2.3-58-ga151 From 194db0d95802fb48d03034fb6bfead1235de3450 Mon Sep 17 00:00:00 2001 From: Andrey Ignatov <rdna@fb.com> Date: Mon, 30 Jul 2018 17:42:31 -0700 Subject: selftests/bpf: Test for get_socket_cookie Add test to use get_socket_cookie() from BPF programs of types BPF_PROG_TYPE_SOCK_OPS and BPF_PROG_TYPE_CGROUP_SOCK_ADDR. The test attaches two programs to cgroup, runs TCP server and client in the cgroup and checks that two operations are done properly on client socket when user calls connect(2): 1. In BPF_CGROUP_INET6_CONNECT socket cookie is used as the key to write new value in a map for client socket. 2. In BPF_CGROUP_SOCK_OPS (BPF_SOCK_OPS_TCP_CONNECT_CB callback) the value written in "1." is found by socket cookie, since it's the same socket, and updated. Finally the test verifies the value in the map. Signed-off-by: Andrey Ignatov <rdna@fb.com> Acked-by: Yonghong Song <yhs@fb.com> Signed-off-by: Daniel Borkmann <daniel@iogearbox.net> --- tools/testing/selftests/bpf/Makefile | 6 +- tools/testing/selftests/bpf/socket_cookie_prog.c | 60 ++++++ tools/testing/selftests/bpf/test_socket_cookie.c | 225 +++++++++++++++++++++++ 3 files changed, 289 insertions(+), 2 deletions(-) create mode 100644 tools/testing/selftests/bpf/socket_cookie_prog.c create mode 100644 tools/testing/selftests/bpf/test_socket_cookie.c (limited to 'tools') diff --git a/tools/testing/selftests/bpf/Makefile b/tools/testing/selftests/bpf/Makefile index 478bf1bcbbf5..1b28277998e2 100644 --- a/tools/testing/selftests/bpf/Makefile +++ b/tools/testing/selftests/bpf/Makefile @@ -22,7 +22,8 @@ $(TEST_CUSTOM_PROGS): $(OUTPUT)/%: %.c # Order correspond to 'make run_tests' order TEST_GEN_PROGS = test_verifier test_tag test_maps test_lru_map test_lpm_map test_progs \ test_align test_verifier_log test_dev_cgroup test_tcpbpf_user \ - test_sock test_btf test_sockmap test_lirc_mode2_user get_cgroup_id_user + test_sock test_btf test_sockmap test_lirc_mode2_user get_cgroup_id_user \ + test_socket_cookie TEST_GEN_FILES = test_pkt_access.o test_xdp.o test_l4lb.o test_tcp_estats.o test_obj_id.o \ test_pkt_md_access.o test_xdp_redirect.o test_xdp_meta.o sockmap_parse_prog.o \ @@ -33,7 +34,7 @@ TEST_GEN_FILES = test_pkt_access.o test_xdp.o test_l4lb.o test_tcp_estats.o test test_btf_haskv.o test_btf_nokv.o test_sockmap_kern.o test_tunnel_kern.o \ test_get_stack_rawtp.o test_sockmap_kern.o test_sockhash_kern.o \ test_lwt_seg6local.o sendmsg4_prog.o sendmsg6_prog.o test_lirc_mode2_kern.o \ - get_cgroup_id_kern.o + get_cgroup_id_kern.o socket_cookie_prog.o # Order correspond to 'make run_tests' order TEST_PROGS := test_kmod.sh \ @@ -60,6 +61,7 @@ $(TEST_GEN_PROGS_EXTENDED): $(OUTPUT)/libbpf.a $(OUTPUT)/test_dev_cgroup: cgroup_helpers.c $(OUTPUT)/test_sock: cgroup_helpers.c $(OUTPUT)/test_sock_addr: cgroup_helpers.c +$(OUTPUT)/test_socket_cookie: cgroup_helpers.c $(OUTPUT)/test_sockmap: cgroup_helpers.c $(OUTPUT)/test_tcpbpf_user: cgroup_helpers.c $(OUTPUT)/test_progs: trace_helpers.c diff --git a/tools/testing/selftests/bpf/socket_cookie_prog.c b/tools/testing/selftests/bpf/socket_cookie_prog.c new file mode 100644 index 000000000000..9ff8ac4b0bf6 --- /dev/null +++ b/tools/testing/selftests/bpf/socket_cookie_prog.c @@ -0,0 +1,60 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright (c) 2018 Facebook + +#include <linux/bpf.h> +#include <sys/socket.h> + +#include "bpf_helpers.h" +#include "bpf_endian.h" + +struct bpf_map_def SEC("maps") socket_cookies = { + .type = BPF_MAP_TYPE_HASH, + .key_size = sizeof(__u64), + .value_size = sizeof(__u32), + .max_entries = 1 << 8, +}; + +SEC("cgroup/connect6") +int set_cookie(struct bpf_sock_addr *ctx) +{ + __u32 cookie_value = 0xFF; + __u64 cookie_key; + + if (ctx->family != AF_INET6 || ctx->user_family != AF_INET6) + return 1; + + cookie_key = bpf_get_socket_cookie(ctx); + if (bpf_map_update_elem(&socket_cookies, &cookie_key, &cookie_value, 0)) + return 0; + + return 1; +} + +SEC("sockops") +int update_cookie(struct bpf_sock_ops *ctx) +{ + __u32 new_cookie_value; + __u32 *cookie_value; + __u64 cookie_key; + + if (ctx->family != AF_INET6) + return 1; + + if (ctx->op != BPF_SOCK_OPS_TCP_CONNECT_CB) + return 1; + + cookie_key = bpf_get_socket_cookie(ctx); + + cookie_value = bpf_map_lookup_elem(&socket_cookies, &cookie_key); + if (!cookie_value) + return 1; + + new_cookie_value = (ctx->local_port << 8) | *cookie_value; + bpf_map_update_elem(&socket_cookies, &cookie_key, &new_cookie_value, 0); + + return 1; +} + +int _version SEC("version") = 1; + +char _license[] SEC("license") = "GPL"; diff --git a/tools/testing/selftests/bpf/test_socket_cookie.c b/tools/testing/selftests/bpf/test_socket_cookie.c new file mode 100644 index 000000000000..68e108e4687a --- /dev/null +++ b/tools/testing/selftests/bpf/test_socket_cookie.c @@ -0,0 +1,225 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright (c) 2018 Facebook + +#include <string.h> +#include <unistd.h> + +#include <arpa/inet.h> +#include <netinet/in.h> +#include <sys/types.h> +#include <sys/socket.h> + +#include <bpf/bpf.h> +#include <bpf/libbpf.h> + +#include "bpf_rlimit.h" +#include "cgroup_helpers.h" + +#define CG_PATH "/foo" +#define SOCKET_COOKIE_PROG "./socket_cookie_prog.o" + +static int start_server(void) +{ + struct sockaddr_in6 addr; + int fd; + + fd = socket(AF_INET6, SOCK_STREAM, 0); + if (fd == -1) { + log_err("Failed to create server socket"); + goto out; + } + + memset(&addr, 0, sizeof(addr)); + addr.sin6_family = AF_INET6; + addr.sin6_addr = in6addr_loopback; + addr.sin6_port = 0; + + if (bind(fd, (const struct sockaddr *)&addr, sizeof(addr)) == -1) { + log_err("Failed to bind server socket"); + goto close_out; + } + + if (listen(fd, 128) == -1) { + log_err("Failed to listen on server socket"); + goto close_out; + } + + goto out; + +close_out: + close(fd); + fd = -1; +out: + return fd; +} + +static int connect_to_server(int server_fd) +{ + struct sockaddr_storage addr; + socklen_t len = sizeof(addr); + int fd; + + fd = socket(AF_INET6, SOCK_STREAM, 0); + if (fd == -1) { + log_err("Failed to create client socket"); + goto out; + } + + if (getsockname(server_fd, (struct sockaddr *)&addr, &len)) { + log_err("Failed to get server addr"); + goto close_out; + } + + if (connect(fd, (const struct sockaddr *)&addr, len) == -1) { + log_err("Fail to connect to server"); + goto close_out; + } + + goto out; + +close_out: + close(fd); + fd = -1; +out: + return fd; +} + +static int validate_map(struct bpf_map *map, int client_fd) +{ + __u32 cookie_expected_value; + struct sockaddr_in6 addr; + socklen_t len = sizeof(addr); + __u32 cookie_value; + __u64 cookie_key; + int err = 0; + int map_fd; + + if (!map) { + log_err("Map not found in BPF object"); + goto err; + } + + map_fd = bpf_map__fd(map); + + err = bpf_map_get_next_key(map_fd, NULL, &cookie_key); + if (err) { + log_err("Can't get cookie key from map"); + goto out; + } + + err = bpf_map_lookup_elem(map_fd, &cookie_key, &cookie_value); + if (err) { + log_err("Can't get cookie value from map"); + goto out; + } + + err = getsockname(client_fd, (struct sockaddr *)&addr, &len); + if (err) { + log_err("Can't get client local addr"); + goto out; + } + + cookie_expected_value = (ntohs(addr.sin6_port) << 8) | 0xFF; + if (cookie_value != cookie_expected_value) { + log_err("Unexpected value in map: %x != %x", cookie_value, + cookie_expected_value); + goto err; + } + + goto out; +err: + err = -1; +out: + return err; +} + +static int run_test(int cgfd) +{ + enum bpf_attach_type attach_type; + struct bpf_prog_load_attr attr; + struct bpf_program *prog; + struct bpf_object *pobj; + const char *prog_name; + int server_fd = -1; + int client_fd = -1; + int prog_fd = -1; + int err = 0; + + memset(&attr, 0, sizeof(attr)); + attr.file = SOCKET_COOKIE_PROG; + attr.prog_type = BPF_PROG_TYPE_UNSPEC; + + err = bpf_prog_load_xattr(&attr, &pobj, &prog_fd); + if (err) { + log_err("Failed to load %s", attr.file); + goto out; + } + + bpf_object__for_each_program(prog, pobj) { + prog_name = bpf_program__title(prog, /*needs_copy*/ false); + + if (strcmp(prog_name, "cgroup/connect6") == 0) { + attach_type = BPF_CGROUP_INET6_CONNECT; + } else if (strcmp(prog_name, "sockops") == 0) { + attach_type = BPF_CGROUP_SOCK_OPS; + } else { + log_err("Unexpected prog: %s", prog_name); + goto err; + } + + err = bpf_prog_attach(bpf_program__fd(prog), cgfd, attach_type, + BPF_F_ALLOW_OVERRIDE); + if (err) { + log_err("Failed to attach prog %s", prog_name); + goto out; + } + } + + server_fd = start_server(); + if (server_fd == -1) + goto err; + + client_fd = connect_to_server(server_fd); + if (client_fd == -1) + goto err; + + if (validate_map(bpf_map__next(NULL, pobj), client_fd)) + goto err; + + goto out; +err: + err = -1; +out: + close(client_fd); + close(server_fd); + bpf_object__close(pobj); + printf("%s\n", err ? "FAILED" : "PASSED"); + return err; +} + +int main(int argc, char **argv) +{ + int cgfd = -1; + int err = 0; + + if (setup_cgroup_environment()) + goto err; + + cgfd = create_and_get_cgroup(CG_PATH); + if (!cgfd) + goto err; + + if (join_cgroup(CG_PATH)) + goto err; + + if (run_test(cgfd)) + goto err; + + goto out; +err: + err = -1; +out: + close(cgfd); + cleanup_cgroup_environment(); + return err; +} -- cgit v1.2.3-58-ga151 From fbeb1603bf4e9baa82da8f794de42949d0fe5e25 Mon Sep 17 00:00:00 2001 From: Arthur Fabre <afabre@cloudflare.com> Date: Tue, 31 Jul 2018 18:17:22 +0100 Subject: bpf: verifier: MOV64 don't mark dst reg unbounded When check_alu_op() handles a BPF_MOV64 between two registers, it calls check_reg_arg(DST_OP) on the dst register, marking it as unbounded. If the src and dst register are the same, this marks the src as unbounded, which can lead to unexpected errors for further checks that rely on bounds info. For example: BPF_MOV64_IMM(BPF_REG_2, 0), BPF_MOV64_REG(BPF_REG_2, BPF_REG_2), BPF_ALU64_REG(BPF_ADD, BPF_REG_1, BPF_REG_2), BPF_MOV64_IMM(BPF_REG_0, 0), BPF_EXIT_INSN(), Results in: "math between ctx pointer and register with unbounded min value is not allowed" check_alu_op() now uses check_reg_arg(DST_OP_NO_MARK), and MOVs that need to mark the dst register (MOVIMM, MOV32) do so. Added a test case for MOV64 dst == src, and dst != src. Signed-off-by: Arthur Fabre <afabre@cloudflare.com> Acked-by: Edward Cree <ecree@solarflare.com> Signed-off-by: Daniel Borkmann <daniel@iogearbox.net> --- kernel/bpf/verifier.c | 6 ++++-- tools/testing/selftests/bpf/test_verifier.c | 26 ++++++++++++++++++++++++++ 2 files changed, 30 insertions(+), 2 deletions(-) (limited to 'tools') diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c index 25e47c195874..e948303a0ea8 100644 --- a/kernel/bpf/verifier.c +++ b/kernel/bpf/verifier.c @@ -3238,8 +3238,8 @@ static int check_alu_op(struct bpf_verifier_env *env, struct bpf_insn *insn) } } - /* check dest operand */ - err = check_reg_arg(env, insn->dst_reg, DST_OP); + /* check dest operand, mark as required later */ + err = check_reg_arg(env, insn->dst_reg, DST_OP_NO_MARK); if (err) return err; @@ -3265,6 +3265,8 @@ static int check_alu_op(struct bpf_verifier_env *env, struct bpf_insn *insn) /* case: R = imm * remember the value we stored into this reg */ + /* clear any state __mark_reg_known doesn't set */ + mark_reg_unknown(env, regs, insn->dst_reg); regs[insn->dst_reg].type = SCALAR_VALUE; if (BPF_CLASS(insn->code) == BPF_ALU64) { __mark_reg_known(regs + insn->dst_reg, diff --git a/tools/testing/selftests/bpf/test_verifier.c b/tools/testing/selftests/bpf/test_verifier.c index f5f7bcc96046..c582afba9d1f 100644 --- a/tools/testing/selftests/bpf/test_verifier.c +++ b/tools/testing/selftests/bpf/test_verifier.c @@ -12332,6 +12332,32 @@ static struct bpf_test tests[] = { .result = REJECT, .errstr = "variable ctx access var_off=(0x0; 0x4)", }, + { + "mov64 src == dst", + .insns = { + BPF_MOV64_IMM(BPF_REG_2, 0), + BPF_MOV64_REG(BPF_REG_2, BPF_REG_2), + // Check bounds are OK + BPF_ALU64_REG(BPF_ADD, BPF_REG_1, BPF_REG_2), + BPF_MOV64_IMM(BPF_REG_0, 0), + BPF_EXIT_INSN(), + }, + .prog_type = BPF_PROG_TYPE_SCHED_CLS, + .result = ACCEPT, + }, + { + "mov64 src != dst", + .insns = { + BPF_MOV64_IMM(BPF_REG_3, 0), + BPF_MOV64_REG(BPF_REG_2, BPF_REG_3), + // Check bounds are OK + BPF_ALU64_REG(BPF_ADD, BPF_REG_1, BPF_REG_2), + BPF_MOV64_IMM(BPF_REG_0, 0), + BPF_EXIT_INSN(), + }, + .prog_type = BPF_PROG_TYPE_SCHED_CLS, + .result = ACCEPT, + }, }; static int probe_filter_length(const struct bpf_insn *fp) -- cgit v1.2.3-58-ga151 From 989133bf7ff3fe6ebcb9c1e7b5e13bebac175d35 Mon Sep 17 00:00:00 2001 From: Petr Machata <petrm@mellanox.com> Date: Wed, 1 Aug 2018 00:38:59 +0200 Subject: selftests: forwarding: Move lldpad waiting to lib.sh The function lldpad_wait() will be useful for a test added by a following patch. Likewise would the "sleep 5" with its extensive comment. Therefore move lldpad_wait() to lib.sh in order to allow reuse. Rename it to lldpad_app_wait_set() to recognize that what this is intended to wait on are the pending APP sets. For the sleeping, add a function lldpad_app_wait_del(). That will serve to hold the related explanatory comment (which edit for clarity), and as a token in the caller to identify the sites where this sort of waiting takes place. That will serve when/if a better way to handle this business is found. Signed-off-by: Petr Machata <petrm@mellanox.com> Reviewed-by: Ido Schimmel <idosch@mellanox.com> Signed-off-by: David S. Miller <davem@davemloft.net> --- .../selftests/drivers/net/mlxsw/qos_dscp_bridge.sh | 23 +++------------------- tools/testing/selftests/net/forwarding/lib.sh | 21 ++++++++++++++++++++ 2 files changed, 24 insertions(+), 20 deletions(-) (limited to 'tools') diff --git a/tools/testing/selftests/drivers/net/mlxsw/qos_dscp_bridge.sh b/tools/testing/selftests/drivers/net/mlxsw/qos_dscp_bridge.sh index cc527660a022..9e875ee8dc1c 100755 --- a/tools/testing/selftests/drivers/net/mlxsw/qos_dscp_bridge.sh +++ b/tools/testing/selftests/drivers/net/mlxsw/qos_dscp_bridge.sh @@ -103,16 +103,6 @@ dscp_map() done } -lldpad_wait() -{ - local dev=$1; shift - - while lldptool -t -i $dev -V APP -c app | grep -q pending; do - echo "$dev: waiting for lldpad to push pending APP updates" - sleep 5 - done -} - switch_create() { ip link add name br1 type bridge vlan_filtering 1 @@ -124,22 +114,15 @@ switch_create() lldptool -T -i $swp1 -V APP $(dscp_map 10) >/dev/null lldptool -T -i $swp2 -V APP $(dscp_map 20) >/dev/null - lldpad_wait $swp1 - lldpad_wait $swp2 + lldpad_app_wait_set $swp1 + lldpad_app_wait_set $swp2 } switch_destroy() { lldptool -T -i $swp2 -V APP -d $(dscp_map 20) >/dev/null lldptool -T -i $swp1 -V APP -d $(dscp_map 10) >/dev/null - - # Give lldpad a chance to push down the changes. If the device is downed - # too soon, the updates will be left pending, but will have been struck - # off the lldpad's DB already, and we won't be able to tell. Then on - # next test iteration this would cause weirdness as newly-added APP - # rules conflict with the old ones, sometimes getting stuck in an - # "unknown" state. - sleep 5 + lldpad_app_wait_del ip link set dev $swp2 nomaster ip link set dev $swp1 nomaster diff --git a/tools/testing/selftests/net/forwarding/lib.sh b/tools/testing/selftests/net/forwarding/lib.sh index 843a6715924f..90af5cd23417 100644 --- a/tools/testing/selftests/net/forwarding/lib.sh +++ b/tools/testing/selftests/net/forwarding/lib.sh @@ -247,6 +247,27 @@ setup_wait() sleep $WAIT_TIME } +lldpad_app_wait_set() +{ + local dev=$1; shift + + while lldptool -t -i $dev -V APP -c app | grep -q pending; do + echo "$dev: waiting for lldpad to push pending APP updates" + sleep 5 + done +} + +lldpad_app_wait_del() +{ + # Give lldpad a chance to push down the changes. If the device is downed + # too soon, the updates will be left pending. However, they will have + # been struck off the lldpad's DB already, so we won't be able to tell + # they are pending. Then on next test iteration this would cause + # weirdness as newly-added APP rules conflict with the old ones, + # sometimes getting stuck in an "unknown" state. + sleep 5 +} + pre_cleanup() { if [ "${PAUSE_ON_CLEANUP}" = "yes" ]; then -- cgit v1.2.3-58-ga151 From cf60869814bcdfbeec02e92ce9e6b38562707edd Mon Sep 17 00:00:00 2001 From: Petr Machata <petrm@mellanox.com> Date: Wed, 1 Aug 2018 00:39:25 +0200 Subject: selftests: forwarding: Move DSCP capture to lib.sh dscp_capture_install() and dscp_capture_uninstall() are going to be useful for a test added by a following patch, move them therefore to lib.sh together with related helpers. While doing so, change the rule preference from mere DSCP value to DSCP+100 in order to support adding captures of packets with DSCP of 0. Signed-off-by: Petr Machata <petrm@mellanox.com> Reviewed-by: Ido Schimmel <idosch@mellanox.com> Signed-off-by: David S. Miller <davem@davemloft.net> --- .../selftests/drivers/net/mlxsw/qos_dscp_bridge.sh | 42 ---------------------- tools/testing/selftests/net/forwarding/lib.sh | 42 ++++++++++++++++++++++ 2 files changed, 42 insertions(+), 42 deletions(-) (limited to 'tools') diff --git a/tools/testing/selftests/drivers/net/mlxsw/qos_dscp_bridge.sh b/tools/testing/selftests/drivers/net/mlxsw/qos_dscp_bridge.sh index 9e875ee8dc1c..1ca631d5aaba 100755 --- a/tools/testing/selftests/drivers/net/mlxsw/qos_dscp_bridge.sh +++ b/tools/testing/selftests/drivers/net/mlxsw/qos_dscp_bridge.sh @@ -34,36 +34,6 @@ lib_dir=$(dirname $0)/../../../net/forwarding NUM_NETIFS=4 source $lib_dir/lib.sh -__dscp_capture_add_del() -{ - local add_del=$1; shift - local dev=$1; shift - local base=$1; shift - local dscp; - - for prio in {0..7}; do - dscp=$((base + prio)) - __icmp_capture_add_del $add_del $dscp "" $dev \ - "ip_tos $((dscp << 2))" - done -} - -dscp_capture_install() -{ - local dev=$1; shift - local base=$1; shift - - __dscp_capture_add_del add $dev $base -} - -dscp_capture_uninstall() -{ - local dev=$1; shift - local base=$1; shift - - __dscp_capture_add_del del $dev $base -} - h1_create() { local dscp; @@ -155,18 +125,6 @@ cleanup() vrf_cleanup } -dscp_fetch_stats() -{ - local dev=$1; shift - local base=$1; shift - - for prio in {0..7}; do - local dscp=$((base + prio)) - local t=$(tc_rule_stats_get $dev $dscp) - echo "[$dscp]=$t " - done -} - ping_ipv4() { ping_test $h1 192.0.2.2 diff --git a/tools/testing/selftests/net/forwarding/lib.sh b/tools/testing/selftests/net/forwarding/lib.sh index 90af5cd23417..ca53b539aa2d 100644 --- a/tools/testing/selftests/net/forwarding/lib.sh +++ b/tools/testing/selftests/net/forwarding/lib.sh @@ -653,6 +653,48 @@ vlan_capture_uninstall() __vlan_capture_add_del del 100 "$@" } +__dscp_capture_add_del() +{ + local add_del=$1; shift + local dev=$1; shift + local base=$1; shift + local dscp; + + for prio in {0..7}; do + dscp=$((base + prio)) + __icmp_capture_add_del $add_del $((dscp + 100)) "" $dev \ + "skip_hw ip_tos $((dscp << 2))" + done +} + +dscp_capture_install() +{ + local dev=$1; shift + local base=$1; shift + + __dscp_capture_add_del add $dev $base +} + +dscp_capture_uninstall() +{ + local dev=$1; shift + local base=$1; shift + + __dscp_capture_add_del del $dev $base +} + +dscp_fetch_stats() +{ + local dev=$1; shift + local base=$1; shift + + for prio in {0..7}; do + local dscp=$((base + prio)) + local t=$(tc_rule_stats_get $dev $((dscp + 100))) + echo "[$dscp]=$t " + done +} + matchall_sink_create() { local dev=$1; shift -- cgit v1.2.3-58-ga151 From 9bae0451b7dcca54db376d2ea2cb8d9fc763a683 Mon Sep 17 00:00:00 2001 From: Petr Machata <petrm@mellanox.com> Date: Wed, 1 Aug 2018 00:39:29 +0200 Subject: selftests: mlxsw: Add test for ip_forward_update_priority Verify that with that sysctl turned off, DSCP prioritization and rewrite works the same way as in qos_dscp_bridge test. However when the sysctl is charged, there should be a reprioritization after routing stage, which will be observed by a different DSCP rewrite on egress. Signed-off-by: Petr Machata <petrm@mellanox.com> Reviewed-by: Ido Schimmel <idosch@mellanox.com> Signed-off-by: David S. Miller <davem@davemloft.net> --- .../selftests/drivers/net/mlxsw/qos_dscp_router.sh | 233 +++++++++++++++++++++ 1 file changed, 233 insertions(+) create mode 100755 tools/testing/selftests/drivers/net/mlxsw/qos_dscp_router.sh (limited to 'tools') diff --git a/tools/testing/selftests/drivers/net/mlxsw/qos_dscp_router.sh b/tools/testing/selftests/drivers/net/mlxsw/qos_dscp_router.sh new file mode 100755 index 000000000000..281d90766e12 --- /dev/null +++ b/tools/testing/selftests/drivers/net/mlxsw/qos_dscp_router.sh @@ -0,0 +1,233 @@ +#!/bin/bash +# SPDX-License-Identifier: GPL-2.0 + +# Test for DSCP prioritization in the router. +# +# With ip_forward_update_priority disabled, the packets are expected to keep +# their DSCP (which in this test uses only values 0..7) intact as they are +# forwarded by the switch. That is verified at $h2. ICMP responses are formed +# with the same DSCP as the requests, and likewise pass through the switch +# intact, which is verified at $h1. +# +# With ip_forward_update_priority enabled, router reprioritizes the packets +# according to the table in reprioritize(). Thus, say, DSCP 7 maps to priority +# 4, which on egress maps back to DSCP 4. The response packet then gets +# reprioritized to 6, getting DSCP 6 on egress. +# +# +----------------------+ +----------------------+ +# | H1 | | H2 | +# | + $h1 | | $h2 + | +# | | 192.0.2.1/28 | | 192.0.2.18/28 | | +# +----|-----------------+ +----------------|-----+ +# | | +# +----|----------------------------------------------------------------|-----+ +# | SW | | | +# | + $swp1 $swp2 + | +# | 192.0.2.2/28 192.0.2.17/28 | +# | APP=0,5,0 .. 7,5,7 APP=0,5,0 .. 7,5,7 | +# +---------------------------------------------------------------------------+ + +ALL_TESTS=" + ping_ipv4 + test_update + test_no_update +" + +lib_dir=$(dirname $0)/../../../net/forwarding + +NUM_NETIFS=4 +source $lib_dir/lib.sh + +reprioritize() +{ + local in=$1; shift + + # This is based on rt_tos2priority in include/net/route.h. Assuming 1:1 + # mapping between priorities and TOS, it yields a new priority for a + # packet with ingress priority of $in. + local -a reprio=(0 0 2 2 6 6 4 4) + + echo ${reprio[$in]} +} + +h1_create() +{ + local dscp; + + simple_if_init $h1 192.0.2.1/28 + tc qdisc add dev $h1 clsact + dscp_capture_install $h1 0 + ip route add vrf v$h1 192.0.2.16/28 via 192.0.2.2 +} + +h1_destroy() +{ + ip route del vrf v$h1 192.0.2.16/28 via 192.0.2.2 + dscp_capture_uninstall $h1 0 + tc qdisc del dev $h1 clsact + simple_if_fini $h1 192.0.2.1/28 +} + +h2_create() +{ + simple_if_init $h2 192.0.2.18/28 + tc qdisc add dev $h2 clsact + dscp_capture_install $h2 0 + ip route add vrf v$h2 192.0.2.0/28 via 192.0.2.17 +} + +h2_destroy() +{ + ip route del vrf v$h2 192.0.2.0/28 via 192.0.2.17 + dscp_capture_uninstall $h2 0 + tc qdisc del dev $h2 clsact + simple_if_fini $h2 192.0.2.18/28 +} + +dscp_map() +{ + local base=$1; shift + + for prio in {0..7}; do + echo app=$prio,5,$((base + prio)) + done +} + +switch_create() +{ + simple_if_init $swp1 192.0.2.2/28 + __simple_if_init $swp2 v$swp1 192.0.2.17/28 + + lldptool -T -i $swp1 -V APP $(dscp_map 0) >/dev/null + lldptool -T -i $swp2 -V APP $(dscp_map 0) >/dev/null + lldpad_app_wait_set $swp1 + lldpad_app_wait_set $swp2 +} + +switch_destroy() +{ + lldptool -T -i $swp2 -V APP -d $(dscp_map 0) >/dev/null + lldptool -T -i $swp1 -V APP -d $(dscp_map 0) >/dev/null + lldpad_app_wait_del + + __simple_if_fini $swp2 192.0.2.17/28 + simple_if_fini $swp1 192.0.2.2/28 +} + +setup_prepare() +{ + h1=${NETIFS[p1]} + swp1=${NETIFS[p2]} + + swp2=${NETIFS[p3]} + h2=${NETIFS[p4]} + + vrf_prepare + + sysctl_set net.ipv4.ip_forward_update_priority 1 + h1_create + h2_create + switch_create +} + +cleanup() +{ + pre_cleanup + + switch_destroy + h2_destroy + h1_destroy + sysctl_restore net.ipv4.ip_forward_update_priority + + vrf_cleanup +} + +ping_ipv4() +{ + ping_test $h1 192.0.2.18 +} + +dscp_ping_test() +{ + local vrf_name=$1; shift + local sip=$1; shift + local dip=$1; shift + local prio=$1; shift + local reprio=$1; shift + local dev1=$1; shift + local dev2=$1; shift + + local prio2=$($reprio $prio) # ICMP Request egress prio + local prio3=$($reprio $prio2) # ICMP Response egress prio + + local dscp=$((prio << 2)) # ICMP Request ingress DSCP + local dscp2=$((prio2 << 2)) # ICMP Request egress DSCP + local dscp3=$((prio3 << 2)) # ICMP Response egress DSCP + + RET=0 + + eval "local -A dev1_t0s=($(dscp_fetch_stats $dev1 0))" + eval "local -A dev2_t0s=($(dscp_fetch_stats $dev2 0))" + + ip vrf exec $vrf_name \ + ${PING} -Q $dscp ${sip:+-I $sip} $dip \ + -c 10 -i 0.1 -w 2 &> /dev/null + + eval "local -A dev1_t1s=($(dscp_fetch_stats $dev1 0))" + eval "local -A dev2_t1s=($(dscp_fetch_stats $dev2 0))" + + for i in {0..7}; do + local dscpi=$((i << 2)) + local expect2=0 + local expect3=0 + + if ((i == prio2)); then + expect2=10 + fi + if ((i == prio3)); then + expect3=10 + fi + + local delta=$((dev2_t1s[$i] - dev2_t0s[$i])) + ((expect2 == delta)) + check_err $? "DSCP $dscpi@$dev2: Expected to capture $expect2 packets, got $delta." + + delta=$((dev1_t1s[$i] - dev1_t0s[$i])) + ((expect3 == delta)) + check_err $? "DSCP $dscpi@$dev1: Expected to capture $expect3 packets, got $delta." + done + + log_test "DSCP rewrite: $dscp-(prio $prio2)-$dscp2-(prio $prio3)-$dscp3" +} + +__test_update() +{ + local update=$1; shift + local reprio=$1; shift + + sysctl_restore net.ipv4.ip_forward_update_priority + sysctl_set net.ipv4.ip_forward_update_priority $update + + for prio in {0..7}; do + dscp_ping_test v$h1 192.0.2.1 192.0.2.18 $prio $reprio $h1 $h2 + done +} + +test_update() +{ + __test_update 1 reprioritize +} + +test_no_update() +{ + __test_update 0 echo +} + +trap cleanup EXIT + +setup_prepare +setup_wait + +tests_run + +exit $EXIT_STATUS -- cgit v1.2.3-58-ga151 From c419cf52da77d0f0f01d4b75a75ae89406f0923f Mon Sep 17 00:00:00 2001 From: Roman Gushchin <guro@fb.com> Date: Thu, 2 Aug 2018 14:27:25 -0700 Subject: bpf: sync bpf.h to tools/ Sync cgroup storage related changes: 1) new BPF_MAP_TYPE_CGROUP_STORAGE map type 2) struct bpf_cgroup_sotrage_key definition 3) get_local_storage() helper Signed-off-by: Roman Gushchin <guro@fb.com> Cc: Alexei Starovoitov <ast@kernel.org> Cc: Daniel Borkmann <daniel@iogearbox.net> Acked-by: Martin KaFai Lau <kafai@fb.com> Signed-off-by: Daniel Borkmann <daniel@iogearbox.net> --- tools/include/uapi/linux/bpf.h | 27 ++++++++++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) (limited to 'tools') diff --git a/tools/include/uapi/linux/bpf.h b/tools/include/uapi/linux/bpf.h index 0ebaaf7f3568..dd5758dc35d3 100644 --- a/tools/include/uapi/linux/bpf.h +++ b/tools/include/uapi/linux/bpf.h @@ -75,6 +75,11 @@ struct bpf_lpm_trie_key { __u8 data[0]; /* Arbitrary size */ }; +struct bpf_cgroup_storage_key { + __u64 cgroup_inode_id; /* cgroup inode id */ + __u32 attach_type; /* program attach type */ +}; + /* BPF syscall commands, see bpf(2) man-page for details. */ enum bpf_cmd { BPF_MAP_CREATE, @@ -120,6 +125,7 @@ enum bpf_map_type { BPF_MAP_TYPE_CPUMAP, BPF_MAP_TYPE_XSKMAP, BPF_MAP_TYPE_SOCKHASH, + BPF_MAP_TYPE_CGROUP_STORAGE, }; enum bpf_prog_type { @@ -2089,6 +2095,24 @@ union bpf_attr { * Return * A 64-bit integer containing the current cgroup id based * on the cgroup within which the current task is running. + * + * void* get_local_storage(void *map, u64 flags) + * Description + * Get the pointer to the local storage area. + * The type and the size of the local storage is defined + * by the *map* argument. + * The *flags* meaning is specific for each map type, + * and has to be 0 for cgroup local storage. + * + * Depending on the bpf program type, a local storage area + * can be shared between multiple instances of the bpf program, + * running simultaneously. + * + * A user should care about the synchronization by himself. + * For example, by using the BPF_STX_XADD instruction to alter + * the shared data. + * Return + * Pointer to the local storage area. */ #define __BPF_FUNC_MAPPER(FN) \ FN(unspec), \ @@ -2171,7 +2195,8 @@ union bpf_attr { FN(rc_repeat), \ FN(rc_keydown), \ FN(skb_cgroup_id), \ - FN(get_current_cgroup_id), + FN(get_current_cgroup_id), \ + FN(get_local_storage), /* integer value in 'imm' field of BPF_CALL instruction selects which helper * function eBPF program intends to call -- cgit v1.2.3-58-ga151 From 34a6bbb8131b75daeb3e9be026d39d6463762baf Mon Sep 17 00:00:00 2001 From: Roman Gushchin <guro@fb.com> Date: Thu, 2 Aug 2018 14:27:26 -0700 Subject: bpftool: add support for CGROUP_STORAGE maps Add BPF_MAP_TYPE_CGROUP_STORAGE maps to the list of maps types which bpftool recognizes. Signed-off-by: Roman Gushchin <guro@fb.com> Cc: Alexei Starovoitov <ast@kernel.org> Cc: Daniel Borkmann <daniel@iogearbox.net> Acked-by: Jakub Kicinski <jakub.kicinski@netronome.com> Acked-by: Martin KaFai Lau <kafai@fb.com> Signed-off-by: Daniel Borkmann <daniel@iogearbox.net> --- tools/bpf/bpftool/map.c | 1 + 1 file changed, 1 insertion(+) (limited to 'tools') diff --git a/tools/bpf/bpftool/map.c b/tools/bpf/bpftool/map.c index 0ee3ba479d87..2dd1f8d9cc2d 100644 --- a/tools/bpf/bpftool/map.c +++ b/tools/bpf/bpftool/map.c @@ -68,6 +68,7 @@ static const char * const map_type_name[] = { [BPF_MAP_TYPE_SOCKMAP] = "sockmap", [BPF_MAP_TYPE_CPUMAP] = "cpumap", [BPF_MAP_TYPE_SOCKHASH] = "sockhash", + [BPF_MAP_TYPE_CGROUP_STORAGE] = "cgroup_storage", }; static bool map_is_per_cpu(__u32 type) -- cgit v1.2.3-58-ga151 From d4c9f573537506530019c505c8b6097a7d7a5fab Mon Sep 17 00:00:00 2001 From: Roman Gushchin <guro@fb.com> Date: Thu, 2 Aug 2018 14:27:28 -0700 Subject: selftests/bpf: add verifier cgroup storage tests Add the following verifier tests to cover the cgroup storage functionality: 1) valid access to the cgroup storage 2) invalid access: use regular hashmap instead of cgroup storage map 3) invalid access: use invalid map fd 4) invalid access: try access memory after the cgroup storage 5) invalid access: try access memory before the cgroup storage 6) invalid access: call get_local_storage() with non-zero flags For tests 2)-6) check returned error strings. Expected output: $ ./test_verifier #0/u add+sub+mul OK #0/p add+sub+mul OK #1/u DIV32 by 0, zero check 1 OK ... #280/p valid cgroup storage access OK #281/p invalid cgroup storage access 1 OK #282/p invalid cgroup storage access 2 OK #283/p invalid per-cgroup storage access 3 OK #284/p invalid cgroup storage access 4 OK #285/p invalid cgroup storage access 5 OK ... #649/p pass modified ctx pointer to helper, 2 OK #650/p pass modified ctx pointer to helper, 3 OK Summary: 901 PASSED, 0 SKIPPED, 0 FAILED Signed-off-by: Roman Gushchin <guro@fb.com> Cc: Alexei Starovoitov <ast@kernel.org> Cc: Daniel Borkmann <daniel@iogearbox.net> Acked-by: Martin KaFai Lau <kafai@fb.com> Signed-off-by: Daniel Borkmann <daniel@iogearbox.net> --- tools/testing/selftests/bpf/bpf_helpers.h | 2 + tools/testing/selftests/bpf/test_verifier.c | 140 +++++++++++++++++++++++++++- 2 files changed, 141 insertions(+), 1 deletion(-) (limited to 'tools') diff --git a/tools/testing/selftests/bpf/bpf_helpers.h b/tools/testing/selftests/bpf/bpf_helpers.h index 19a424483f6e..cb9fcfbc9307 100644 --- a/tools/testing/selftests/bpf/bpf_helpers.h +++ b/tools/testing/selftests/bpf/bpf_helpers.h @@ -135,6 +135,8 @@ static int (*bpf_rc_keydown)(void *ctx, unsigned int protocol, (void *) BPF_FUNC_rc_keydown; static unsigned long long (*bpf_get_current_cgroup_id)(void) = (void *) BPF_FUNC_get_current_cgroup_id; +static void *(*bpf_get_local_storage)(void *map, unsigned long long flags) = + (void *) BPF_FUNC_get_local_storage; /* llvm builtin functions that eBPF C program may use to * emit BPF_LD_ABS and BPF_LD_IND instructions diff --git a/tools/testing/selftests/bpf/test_verifier.c b/tools/testing/selftests/bpf/test_verifier.c index c582afba9d1f..4b5e03c25204 100644 --- a/tools/testing/selftests/bpf/test_verifier.c +++ b/tools/testing/selftests/bpf/test_verifier.c @@ -50,7 +50,7 @@ #define MAX_INSNS BPF_MAXINSNS #define MAX_FIXUPS 8 -#define MAX_NR_MAPS 7 +#define MAX_NR_MAPS 8 #define POINTER_VALUE 0xcafe4all #define TEST_DATA_LEN 64 @@ -70,6 +70,7 @@ struct bpf_test { int fixup_prog1[MAX_FIXUPS]; int fixup_prog2[MAX_FIXUPS]; int fixup_map_in_map[MAX_FIXUPS]; + int fixup_cgroup_storage[MAX_FIXUPS]; const char *errstr; const char *errstr_unpriv; uint32_t retval; @@ -4630,6 +4631,121 @@ static struct bpf_test tests[] = { .result = REJECT, .flags = F_NEEDS_EFFICIENT_UNALIGNED_ACCESS, }, + { + "valid cgroup storage access", + .insns = { + BPF_MOV64_IMM(BPF_REG_2, 0), + BPF_LD_MAP_FD(BPF_REG_1, 0), + BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, + BPF_FUNC_get_local_storage), + BPF_LDX_MEM(BPF_W, BPF_REG_1, BPF_REG_0, 0), + BPF_MOV64_REG(BPF_REG_0, BPF_REG_1), + BPF_ALU64_IMM(BPF_AND, BPF_REG_0, 1), + BPF_EXIT_INSN(), + }, + .fixup_cgroup_storage = { 1 }, + .result = ACCEPT, + .prog_type = BPF_PROG_TYPE_CGROUP_SKB, + }, + { + "invalid cgroup storage access 1", + .insns = { + BPF_MOV64_IMM(BPF_REG_2, 0), + BPF_LD_MAP_FD(BPF_REG_1, 0), + BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, + BPF_FUNC_get_local_storage), + BPF_LDX_MEM(BPF_W, BPF_REG_1, BPF_REG_0, 0), + BPF_MOV64_REG(BPF_REG_0, BPF_REG_1), + BPF_ALU64_IMM(BPF_AND, BPF_REG_0, 1), + BPF_EXIT_INSN(), + }, + .fixup_map1 = { 1 }, + .result = REJECT, + .errstr = "cannot pass map_type 1 into func bpf_get_local_storage", + .prog_type = BPF_PROG_TYPE_CGROUP_SKB, + }, + { + "invalid cgroup storage access 2", + .insns = { + BPF_MOV64_IMM(BPF_REG_2, 0), + BPF_LD_MAP_FD(BPF_REG_1, 1), + BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, + BPF_FUNC_get_local_storage), + BPF_ALU64_IMM(BPF_AND, BPF_REG_0, 1), + BPF_EXIT_INSN(), + }, + .result = REJECT, + .errstr = "fd 1 is not pointing to valid bpf_map", + .prog_type = BPF_PROG_TYPE_CGROUP_SKB, + }, + { + "invalid per-cgroup storage access 3", + .insns = { + BPF_MOV64_IMM(BPF_REG_2, 0), + BPF_LD_MAP_FD(BPF_REG_1, 0), + BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, + BPF_FUNC_get_local_storage), + BPF_LDX_MEM(BPF_W, BPF_REG_1, BPF_REG_0, 256), + BPF_ALU64_IMM(BPF_ADD, BPF_REG_1, 1), + BPF_MOV64_IMM(BPF_REG_0, 0), + BPF_EXIT_INSN(), + }, + .fixup_cgroup_storage = { 1 }, + .result = REJECT, + .errstr = "invalid access to map value, value_size=64 off=256 size=4", + .prog_type = BPF_PROG_TYPE_CGROUP_SKB, + }, + { + "invalid cgroup storage access 4", + .insns = { + BPF_MOV64_IMM(BPF_REG_2, 0), + BPF_LD_MAP_FD(BPF_REG_1, 0), + BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, + BPF_FUNC_get_local_storage), + BPF_LDX_MEM(BPF_W, BPF_REG_1, BPF_REG_0, -2), + BPF_MOV64_REG(BPF_REG_0, BPF_REG_1), + BPF_ALU64_IMM(BPF_ADD, BPF_REG_1, 1), + BPF_EXIT_INSN(), + }, + .fixup_cgroup_storage = { 1 }, + .result = REJECT, + .errstr = "invalid access to map value, value_size=64 off=-2 size=4", + .prog_type = BPF_PROG_TYPE_CGROUP_SKB, + }, + { + "invalid cgroup storage access 5", + .insns = { + BPF_MOV64_IMM(BPF_REG_2, 7), + BPF_LD_MAP_FD(BPF_REG_1, 0), + BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, + BPF_FUNC_get_local_storage), + BPF_LDX_MEM(BPF_W, BPF_REG_1, BPF_REG_0, 0), + BPF_MOV64_REG(BPF_REG_0, BPF_REG_1), + BPF_ALU64_IMM(BPF_AND, BPF_REG_0, 1), + BPF_EXIT_INSN(), + }, + .fixup_cgroup_storage = { 1 }, + .result = REJECT, + .errstr = "get_local_storage() doesn't support non-zero flags", + .prog_type = BPF_PROG_TYPE_CGROUP_SKB, + }, + { + "invalid cgroup storage access 6", + .insns = { + BPF_MOV64_REG(BPF_REG_2, BPF_REG_1), + BPF_LD_MAP_FD(BPF_REG_1, 0), + BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, + BPF_FUNC_get_local_storage), + BPF_LDX_MEM(BPF_W, BPF_REG_1, BPF_REG_0, 0), + BPF_MOV64_REG(BPF_REG_0, BPF_REG_1), + BPF_ALU64_IMM(BPF_AND, BPF_REG_0, 1), + BPF_EXIT_INSN(), + }, + .fixup_cgroup_storage = { 1 }, + .result = REJECT, + .errstr = "get_local_storage() doesn't support non-zero flags", + .prog_type = BPF_PROG_TYPE_CGROUP_SKB, + }, { "multiple registers share map_lookup_elem result", .insns = { @@ -12462,6 +12578,19 @@ static int create_map_in_map(void) return outer_map_fd; } +static int create_cgroup_storage(void) +{ + int fd; + + fd = bpf_create_map(BPF_MAP_TYPE_CGROUP_STORAGE, + sizeof(struct bpf_cgroup_storage_key), + TEST_DATA_LEN, 0, 0); + if (fd < 0) + printf("Failed to create array '%s'!\n", strerror(errno)); + + return fd; +} + static char bpf_vlog[UINT_MAX >> 8]; static void do_test_fixup(struct bpf_test *test, struct bpf_insn *prog, @@ -12474,6 +12603,7 @@ static void do_test_fixup(struct bpf_test *test, struct bpf_insn *prog, int *fixup_prog1 = test->fixup_prog1; int *fixup_prog2 = test->fixup_prog2; int *fixup_map_in_map = test->fixup_map_in_map; + int *fixup_cgroup_storage = test->fixup_cgroup_storage; if (test->fill_helper) test->fill_helper(test); @@ -12541,6 +12671,14 @@ static void do_test_fixup(struct bpf_test *test, struct bpf_insn *prog, fixup_map_in_map++; } while (*fixup_map_in_map); } + + if (*fixup_cgroup_storage) { + map_fds[7] = create_cgroup_storage(); + do { + prog[*fixup_cgroup_storage].imm = map_fds[7]; + fixup_cgroup_storage++; + } while (*fixup_cgroup_storage); + } } static void do_test_single(struct bpf_test *test, bool unpriv, -- cgit v1.2.3-58-ga151 From 68cfa3ac6b8db2782300ad0d699da27aaa2ac9fb Mon Sep 17 00:00:00 2001 From: Roman Gushchin <guro@fb.com> Date: Thu, 2 Aug 2018 14:27:29 -0700 Subject: selftests/bpf: add a cgroup storage test Implement a test to cover the cgroup storage functionality. The test implements a bpf program which drops every second packet by using the cgroup storage as a persistent storage. The test also use the userspace API to check the data in the cgroup storage, alter it, and check that the loaded and attached bpf program sees the update. Expected output: $ ./test_cgroup_storage test_cgroup_storage:PASS Signed-off-by: Roman Gushchin <guro@fb.com> Cc: Alexei Starovoitov <ast@kernel.org> Cc: Daniel Borkmann <daniel@iogearbox.net> Acked-by: Martin KaFai Lau <kafai@fb.com> Signed-off-by: Daniel Borkmann <daniel@iogearbox.net> --- tools/testing/selftests/bpf/Makefile | 3 +- tools/testing/selftests/bpf/test_cgroup_storage.c | 130 ++++++++++++++++++++++ 2 files changed, 132 insertions(+), 1 deletion(-) create mode 100644 tools/testing/selftests/bpf/test_cgroup_storage.c (limited to 'tools') diff --git a/tools/testing/selftests/bpf/Makefile b/tools/testing/selftests/bpf/Makefile index 1b28277998e2..ad241ddba350 100644 --- a/tools/testing/selftests/bpf/Makefile +++ b/tools/testing/selftests/bpf/Makefile @@ -23,7 +23,7 @@ $(TEST_CUSTOM_PROGS): $(OUTPUT)/%: %.c TEST_GEN_PROGS = test_verifier test_tag test_maps test_lru_map test_lpm_map test_progs \ test_align test_verifier_log test_dev_cgroup test_tcpbpf_user \ test_sock test_btf test_sockmap test_lirc_mode2_user get_cgroup_id_user \ - test_socket_cookie + test_socket_cookie test_cgroup_storage TEST_GEN_FILES = test_pkt_access.o test_xdp.o test_l4lb.o test_tcp_estats.o test_obj_id.o \ test_pkt_md_access.o test_xdp_redirect.o test_xdp_meta.o sockmap_parse_prog.o \ @@ -66,6 +66,7 @@ $(OUTPUT)/test_sockmap: cgroup_helpers.c $(OUTPUT)/test_tcpbpf_user: cgroup_helpers.c $(OUTPUT)/test_progs: trace_helpers.c $(OUTPUT)/get_cgroup_id_user: cgroup_helpers.c +$(OUTPUT)/test_cgroup_storage: cgroup_helpers.c .PHONY: force diff --git a/tools/testing/selftests/bpf/test_cgroup_storage.c b/tools/testing/selftests/bpf/test_cgroup_storage.c new file mode 100644 index 000000000000..dc83fb2d3f27 --- /dev/null +++ b/tools/testing/selftests/bpf/test_cgroup_storage.c @@ -0,0 +1,130 @@ +// SPDX-License-Identifier: GPL-2.0 +#include <assert.h> +#include <bpf/bpf.h> +#include <linux/filter.h> +#include <stdio.h> +#include <stdlib.h> + +#include "cgroup_helpers.h" + +char bpf_log_buf[BPF_LOG_BUF_SIZE]; + +#define TEST_CGROUP "/test-bpf-cgroup-storage-buf/" + +int main(int argc, char **argv) +{ + struct bpf_insn prog[] = { + BPF_LD_MAP_FD(BPF_REG_1, 0), /* map fd */ + BPF_MOV64_IMM(BPF_REG_2, 0), /* flags, not used */ + BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, + BPF_FUNC_get_local_storage), + BPF_MOV64_IMM(BPF_REG_1, 1), + BPF_STX_XADD(BPF_DW, BPF_REG_0, BPF_REG_1, 0), + BPF_LDX_MEM(BPF_W, BPF_REG_1, BPF_REG_0, 0), + BPF_ALU64_IMM(BPF_AND, BPF_REG_1, 0x1), + BPF_MOV64_REG(BPF_REG_0, BPF_REG_1), + BPF_EXIT_INSN(), + }; + size_t insns_cnt = sizeof(prog) / sizeof(struct bpf_insn); + int error = EXIT_FAILURE; + int map_fd, prog_fd, cgroup_fd; + struct bpf_cgroup_storage_key key; + unsigned long long value; + + map_fd = bpf_create_map(BPF_MAP_TYPE_CGROUP_STORAGE, sizeof(key), + sizeof(value), 0, 0); + if (map_fd < 0) { + printf("Failed to create map: %s\n", strerror(errno)); + goto out; + } + + prog[0].imm = map_fd; + prog_fd = bpf_load_program(BPF_PROG_TYPE_CGROUP_SKB, + prog, insns_cnt, "GPL", 0, + bpf_log_buf, BPF_LOG_BUF_SIZE); + if (prog_fd < 0) { + printf("Failed to load bpf program: %s\n", bpf_log_buf); + goto out; + } + + if (setup_cgroup_environment()) { + printf("Failed to setup cgroup environment\n"); + goto err; + } + + /* Create a cgroup, get fd, and join it */ + cgroup_fd = create_and_get_cgroup(TEST_CGROUP); + if (!cgroup_fd) { + printf("Failed to create test cgroup\n"); + goto err; + } + + if (join_cgroup(TEST_CGROUP)) { + printf("Failed to join cgroup\n"); + goto err; + } + + /* Attach the bpf program */ + if (bpf_prog_attach(prog_fd, cgroup_fd, BPF_CGROUP_INET_EGRESS, 0)) { + printf("Failed to attach bpf program\n"); + goto err; + } + + if (bpf_map_get_next_key(map_fd, NULL, &key)) { + printf("Failed to get the first key in cgroup storage\n"); + goto err; + } + + if (bpf_map_lookup_elem(map_fd, &key, &value)) { + printf("Failed to lookup cgroup storage\n"); + goto err; + } + + /* Every second packet should be dropped */ + assert(system("ping localhost -c 1 -W 1 -q > /dev/null") == 0); + assert(system("ping localhost -c 1 -W 1 -q > /dev/null")); + assert(system("ping localhost -c 1 -W 1 -q > /dev/null") == 0); + + /* Check the counter in the cgroup local storage */ + if (bpf_map_lookup_elem(map_fd, &key, &value)) { + printf("Failed to lookup cgroup storage\n"); + goto err; + } + + if (value != 3) { + printf("Unexpected data in the cgroup storage: %llu\n", value); + goto err; + } + + /* Bump the counter in the cgroup local storage */ + value++; + if (bpf_map_update_elem(map_fd, &key, &value, 0)) { + printf("Failed to update the data in the cgroup storage\n"); + goto err; + } + + /* Every second packet should be dropped */ + assert(system("ping localhost -c 1 -W 1 -q > /dev/null") == 0); + assert(system("ping localhost -c 1 -W 1 -q > /dev/null")); + assert(system("ping localhost -c 1 -W 1 -q > /dev/null") == 0); + + /* Check the final value of the counter in the cgroup local storage */ + if (bpf_map_lookup_elem(map_fd, &key, &value)) { + printf("Failed to lookup the cgroup storage\n"); + goto err; + } + + if (value != 7) { + printf("Unexpected data in the cgroup storage: %llu\n", value); + goto err; + } + + error = 0; + printf("test_cgroup_storage:PASS\n"); + +err: + cleanup_cgroup_environment(); + +out: + return error; +} -- cgit v1.2.3-58-ga151 From 6534770d6f176093b50896961107b2d545ef38f0 Mon Sep 17 00:00:00 2001 From: Jakub Kicinski <jakub.kicinski@netronome.com> Date: Thu, 2 Aug 2018 19:30:27 -0700 Subject: tools: bpf: fix BTF code added twice to different trees commit 38d5d3b3d5db ("bpf: Introduce BPF_ANNOTATE_KV_PAIR") added to the bpf and net trees what commit 92b57121ca79 ("bpf: btf: export btf types and name by offset from lib") has already added to bpf-next/net-next, but in slightly different location. Remove the duplicates (to fix build of libbpf). Signed-off-by: Jakub Kicinski <jakub.kicinski@netronome.com> Signed-off-by: David S. Miller <davem@davemloft.net> --- tools/lib/bpf/btf.c | 17 ----------------- tools/lib/bpf/btf.h | 1 - 2 files changed, 18 deletions(-) (limited to 'tools') diff --git a/tools/lib/bpf/btf.c b/tools/lib/bpf/btf.c index 1622a309f169..09ecf8162f7a 100644 --- a/tools/lib/bpf/btf.c +++ b/tools/lib/bpf/btf.c @@ -37,14 +37,6 @@ struct btf { int fd; }; -static const char *btf_name_by_offset(const struct btf *btf, __u32 offset) -{ - if (offset < btf->hdr->str_len) - return &btf->strings[offset]; - else - return NULL; -} - static int btf_add_type(struct btf *btf, struct btf_type *t) { if (btf->types_size - btf->nr_types < 2) { @@ -401,12 +393,3 @@ const char *btf__name_by_offset(const struct btf *btf, __u32 offset) else return NULL; } - -const struct btf_type *btf__type_by_id(const struct btf *btf, - __u32 type_id) -{ - if (type_id > btf->nr_types) - return NULL; - - return btf->types[type_id]; -} diff --git a/tools/lib/bpf/btf.h b/tools/lib/bpf/btf.h index dd8a86eab8ca..43c658ccfc2b 100644 --- a/tools/lib/bpf/btf.h +++ b/tools/lib/bpf/btf.h @@ -22,6 +22,5 @@ __s64 btf__resolve_size(const struct btf *btf, __u32 type_id); int btf__resolve_type(const struct btf *btf, __u32 type_id); int btf__fd(const struct btf *btf); const char *btf__name_by_offset(const struct btf *btf, __u32 offset); -const struct btf_type *btf__type_by_id(const struct btf *btf, __u32 type_id); #endif -- cgit v1.2.3-58-ga151 From 0069fb854364da79fd99236ea620affc8e1152d5 Mon Sep 17 00:00:00 2001 From: Roman Gushchin <guro@fb.com> Date: Thu, 2 Aug 2018 15:47:10 -0700 Subject: selftests/bpf: fix a typo in map in map test Commit fbeb1603bf4e ("bpf: verifier: MOV64 don't mark dst reg unbounded") revealed a typo in commit fb30d4b71214 ("bpf: Add tests for map-in-map"): BPF_MOV64_REG(BPF_REG_0, 0) was used instead of BPF_MOV64_IMM(BPF_REG_0, 0). I've noticed the problem by running bpf kselftests. Fixes: fb30d4b71214 ("bpf: Add tests for map-in-map") Signed-off-by: Roman Gushchin <guro@fb.com> Cc: Martin KaFai Lau <kafai@fb.com> Cc: Arthur Fabre <afabre@cloudflare.com> Cc: Daniel Borkmann <daniel@iogearbox.net> Cc: Alexei Starovoitov <ast@kernel.org> Acked-by: Martin KaFai Lau <kafai@fb.com> Signed-off-by: Daniel Borkmann <daniel@iogearbox.net> --- tools/testing/selftests/bpf/test_verifier.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'tools') diff --git a/tools/testing/selftests/bpf/test_verifier.c b/tools/testing/selftests/bpf/test_verifier.c index 4b5e03c25204..ac281ee771dd 100644 --- a/tools/testing/selftests/bpf/test_verifier.c +++ b/tools/testing/selftests/bpf/test_verifier.c @@ -7113,7 +7113,7 @@ static struct bpf_test tests[] = { BPF_MOV64_REG(BPF_REG_1, BPF_REG_0), BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_map_lookup_elem), - BPF_MOV64_REG(BPF_REG_0, 0), + BPF_MOV64_IMM(BPF_REG_0, 0), BPF_EXIT_INSN(), }, .fixup_map_in_map = { 3 }, @@ -7136,7 +7136,7 @@ static struct bpf_test tests[] = { BPF_ALU64_IMM(BPF_ADD, BPF_REG_1, 8), BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_map_lookup_elem), - BPF_MOV64_REG(BPF_REG_0, 0), + BPF_MOV64_IMM(BPF_REG_0, 0), BPF_EXIT_INSN(), }, .fixup_map_in_map = { 3 }, @@ -7158,7 +7158,7 @@ static struct bpf_test tests[] = { BPF_MOV64_REG(BPF_REG_1, BPF_REG_0), BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_map_lookup_elem), - BPF_MOV64_REG(BPF_REG_0, 0), + BPF_MOV64_IMM(BPF_REG_0, 0), BPF_EXIT_INSN(), }, .fixup_map_in_map = { 3 }, -- cgit v1.2.3-58-ga151 From 64f61cddf1934277e9fbb77d3d67308ffbfd4fa3 Mon Sep 17 00:00:00 2001 From: Vlad Buslov <vladbu@mellanox.com> Date: Sun, 5 Aug 2018 22:35:56 +0300 Subject: tc-testing: fix ip address in u32 test Fix expected ip address to actually match configured ip address. Fix test to expect single matched filter. Signed-off-by: Vlad Buslov <vladbu@mellanox.com> Acked-by: Jamal Hadi Salim <jhs@mojatatu.com> Signed-off-by: David S. Miller <davem@davemloft.net> --- tools/testing/selftests/tc-testing/tc-tests/filters/tests.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'tools') diff --git a/tools/testing/selftests/tc-testing/tc-tests/filters/tests.json b/tools/testing/selftests/tc-testing/tc-tests/filters/tests.json index 5fa02d86b35f..99a5ffca1088 100644 --- a/tools/testing/selftests/tc-testing/tc-tests/filters/tests.json +++ b/tools/testing/selftests/tc-testing/tc-tests/filters/tests.json @@ -12,8 +12,8 @@ "cmdUnderTest": "$TC filter add dev $DEV1 parent ffff: protocol ip prio 1 u32 match ip src 127.0.0.1/32 flowid 1:1 action ok", "expExitCode": "0", "verifyCmd": "$TC filter show dev $DEV1 parent ffff:", - "matchPattern": "match 7f000002/ffffffff at 12", - "matchCount": "0", + "matchPattern": "match 7f000001/ffffffff at 12", + "matchCount": "1", "teardown": [ "$TC qdisc del dev $DEV1 ingress" ] -- cgit v1.2.3-58-ga151 From 0c62f8a820b7fdeacf5ad9f9e24b53043d372c97 Mon Sep 17 00:00:00 2001 From: Vlad Buslov <vladbu@mellanox.com> Date: Sun, 5 Aug 2018 22:36:25 +0300 Subject: tc-testing: flush gact actions on test teardown Test 6fb4 creates one mirred and one pipe action, but only flushes mirred on teardown. Leaking pipe action causes failures in other tests. Add additional teardown command to also flush gact actions. Signed-off-by: Vlad Buslov <vladbu@mellanox.com> Acked-by: Jamal Hadi Salim <jhs@mojatatu.com> Signed-off-by: David S. Miller <davem@davemloft.net> --- tools/testing/selftests/tc-testing/tc-tests/actions/mirred.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'tools') 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 6e4edfae1799..db49fd0f8445 100644 --- a/tools/testing/selftests/tc-testing/tc-tests/actions/mirred.json +++ b/tools/testing/selftests/tc-testing/tc-tests/actions/mirred.json @@ -44,7 +44,8 @@ "matchPattern": "action order [0-9]*: mirred \\(Egress Redirect to device lo\\).*index 2 ref", "matchCount": "1", "teardown": [ - "$TC actions flush action mirred" + "$TC actions flush action mirred", + "$TC actions flush action gact" ] }, { -- cgit v1.2.3-58-ga151 From 757a9a39d483ae415a712388c33d4042a98b751f Mon Sep 17 00:00:00 2001 From: Vlad Buslov <vladbu@mellanox.com> Date: Sun, 5 Aug 2018 22:36:44 +0300 Subject: tc-testing: remove duplicate spaces in connmark match patterns Match patterns for some connmark tests contain duplicate whitespace that is not present in actual tc output. This causes tests to fail because they can't match required action, even when it was successfully created. Fixes: 1dad0f9ffff7 ("tc-testing: add connmark action tests") Signed-off-by: Vlad Buslov <vladbu@mellanox.com> Acked-by: Jamal Hadi Salim <jhs@mojatatu.com> Signed-off-by: David S. Miller <davem@davemloft.net> --- .../tc-testing/tc-tests/actions/connmark.json | 24 +++++++++++----------- 1 file changed, 12 insertions(+), 12 deletions(-) (limited to 'tools') diff --git a/tools/testing/selftests/tc-testing/tc-tests/actions/connmark.json b/tools/testing/selftests/tc-testing/tc-tests/actions/connmark.json index 70952bd98ff9..13147a1f5731 100644 --- a/tools/testing/selftests/tc-testing/tc-tests/actions/connmark.json +++ b/tools/testing/selftests/tc-testing/tc-tests/actions/connmark.json @@ -17,7 +17,7 @@ "cmdUnderTest": "$TC actions add action connmark", "expExitCode": "0", "verifyCmd": "$TC actions list action connmark", - "matchPattern": "action order [0-9]+: connmark zone 0 pipe", + "matchPattern": "action order [0-9]+: connmark zone 0 pipe", "matchCount": "1", "teardown": [ "$TC actions flush action connmark" @@ -41,7 +41,7 @@ "cmdUnderTest": "$TC actions add action connmark pass index 1", "expExitCode": "0", "verifyCmd": "$TC actions get action connmark index 1", - "matchPattern": "action order [0-9]+: connmark zone 0 pass.*index 1 ref", + "matchPattern": "action order [0-9]+: connmark zone 0 pass.*index 1 ref", "matchCount": "1", "teardown": [ "$TC actions flush action connmark" @@ -65,7 +65,7 @@ "cmdUnderTest": "$TC actions add action connmark drop index 100", "expExitCode": "0", "verifyCmd": "$TC actions get action connmark index 100", - "matchPattern": "action order [0-9]+: connmark zone 0 drop.*index 100 ref", + "matchPattern": "action order [0-9]+: connmark zone 0 drop.*index 100 ref", "matchCount": "1", "teardown": [ "$TC actions flush action connmark" @@ -89,7 +89,7 @@ "cmdUnderTest": "$TC actions add action connmark pipe index 455", "expExitCode": "0", "verifyCmd": "$TC actions get action connmark index 455", - "matchPattern": "action order [0-9]+: connmark zone 0 pipe.*index 455 ref", + "matchPattern": "action order [0-9]+: connmark zone 0 pipe.*index 455 ref", "matchCount": "1", "teardown": [ "$TC actions flush action connmark" @@ -113,7 +113,7 @@ "cmdUnderTest": "$TC actions add action connmark reclassify index 7", "expExitCode": "0", "verifyCmd": "$TC actions list action connmark", - "matchPattern": "action order [0-9]+: connmark zone 0 reclassify.*index 7 ref", + "matchPattern": "action order [0-9]+: connmark zone 0 reclassify.*index 7 ref", "matchCount": "1", "teardown": [ "$TC actions flush action connmark" @@ -137,7 +137,7 @@ "cmdUnderTest": "$TC actions add action connmark continue index 17", "expExitCode": "0", "verifyCmd": "$TC actions list action connmark", - "matchPattern": "action order [0-9]+: connmark zone 0 continue.*index 17 ref", + "matchPattern": "action order [0-9]+: connmark zone 0 continue.*index 17 ref", "matchCount": "1", "teardown": [ "$TC actions flush action connmark" @@ -161,7 +161,7 @@ "cmdUnderTest": "$TC actions add action connmark jump 10 index 17", "expExitCode": "0", "verifyCmd": "$TC actions list action connmark", - "matchPattern": "action order [0-9]+: connmark zone 0 jump 10.*index 17 ref", + "matchPattern": "action order [0-9]+: connmark zone 0 jump 10.*index 17 ref", "matchCount": "1", "teardown": [ "$TC actions flush action connmark" @@ -185,7 +185,7 @@ "cmdUnderTest": "$TC actions add action connmark zone 100 pipe index 1", "expExitCode": "0", "verifyCmd": "$TC actions get action connmark index 1", - "matchPattern": "action order [0-9]+: connmark zone 100 pipe.*index 1 ref", + "matchPattern": "action order [0-9]+: connmark zone 100 pipe.*index 1 ref", "matchCount": "1", "teardown": [ "$TC actions flush action connmark" @@ -209,7 +209,7 @@ "cmdUnderTest": "$TC actions add action connmark zone 65536 reclassify index 21", "expExitCode": "255", "verifyCmd": "$TC actions get action connmark index 1", - "matchPattern": "action order [0-9]+: connmark zone 65536 reclassify.*index 21 ref", + "matchPattern": "action order [0-9]+: connmark zone 65536 reclassify.*index 21 ref", "matchCount": "0", "teardown": [ "$TC actions flush action connmark" @@ -233,7 +233,7 @@ "cmdUnderTest": "$TC actions add action connmark zone 655 unsupp_arg pass index 2", "expExitCode": "255", "verifyCmd": "$TC actions get action connmark index 2", - "matchPattern": "action order [0-9]+: connmark zone 655 unsupp_arg pass.*index 2 ref", + "matchPattern": "action order [0-9]+: connmark zone 655 unsupp_arg pass.*index 2 ref", "matchCount": "0", "teardown": [ "$TC actions flush action connmark" @@ -258,7 +258,7 @@ "cmdUnderTest": "$TC actions replace action connmark zone 555 reclassify index 555", "expExitCode": "0", "verifyCmd": "$TC actions get action connmark index 555", - "matchPattern": "action order [0-9]+: connmark zone 555 reclassify.*index 555 ref", + "matchPattern": "action order [0-9]+: connmark zone 555 reclassify.*index 555 ref", "matchCount": "1", "teardown": [ "$TC actions flush action connmark" @@ -282,7 +282,7 @@ "cmdUnderTest": "$TC actions add action connmark zone 555 pipe index 5 cookie aabbccddeeff112233445566778800a1", "expExitCode": "0", "verifyCmd": "$TC actions get action connmark index 5", - "matchPattern": "action order [0-9]+: connmark zone 555 pipe.*index 5 ref.*cookie aabbccddeeff112233445566778800a1", + "matchPattern": "action order [0-9]+: connmark zone 555 pipe.*index 5 ref.*cookie aabbccddeeff112233445566778800a1", "matchCount": "1", "teardown": [ "$TC actions flush action connmark" -- cgit v1.2.3-58-ga151 From 981467033a37d916649647fa3afe1fe99bba1817 Mon Sep 17 00:00:00 2001 From: Vlad Buslov <vladbu@mellanox.com> Date: Sun, 5 Aug 2018 22:37:09 +0300 Subject: tc-testing: remove duplicate spaces in skbedit match patterns Match patterns for some skbedit tests contain duplicate whitespace that is not present in actual tc output. This causes tests to fail because they can't match required action, even when it was successfully created. Signed-off-by: Vlad Buslov <vladbu@mellanox.com> Acked-by: Jamal Hadi Salim <jhs@mojatatu.com> Signed-off-by: David S. Miller <davem@davemloft.net> --- .../tc-testing/tc-tests/actions/skbedit.json | 26 +++++++++++----------- 1 file changed, 13 insertions(+), 13 deletions(-) (limited to 'tools') 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 37ecc2716fee..5aaf593b914a 100644 --- a/tools/testing/selftests/tc-testing/tc-tests/actions/skbedit.json +++ b/tools/testing/selftests/tc-testing/tc-tests/actions/skbedit.json @@ -17,7 +17,7 @@ "cmdUnderTest": "$TC actions add action skbedit mark 1", "expExitCode": "0", "verifyCmd": "$TC actions list action skbedit", - "matchPattern": "action order [0-9]*: skbedit mark 1", + "matchPattern": "action order [0-9]*: skbedit mark 1", "matchCount": "1", "teardown": [ "$TC actions flush action skbedit" @@ -65,7 +65,7 @@ "cmdUnderTest": "$TC actions add action skbedit prio 99", "expExitCode": "0", "verifyCmd": "$TC actions list action skbedit", - "matchPattern": "action order [0-9]*: skbedit priority :99", + "matchPattern": "action order [0-9]*: skbedit priority :99", "matchCount": "1", "teardown": [ "$TC actions flush action skbedit" @@ -113,7 +113,7 @@ "cmdUnderTest": "$TC actions add action skbedit queue_mapping 909", "expExitCode": "0", "verifyCmd": "$TC actions list action skbedit", - "matchPattern": "action order [0-9]*: skbedit queue_mapping 909", + "matchPattern": "action order [0-9]*: skbedit queue_mapping 909", "matchCount": "1", "teardown": [ "$TC actions flush action skbedit" @@ -161,7 +161,7 @@ "cmdUnderTest": "$TC actions add action skbedit ptype host", "expExitCode": "0", "verifyCmd": "$TC actions list action skbedit", - "matchPattern": "action order [0-9]*: skbedit ptype host", + "matchPattern": "action order [0-9]*: skbedit ptype host", "matchCount": "1", "teardown": [ "$TC actions flush action skbedit" @@ -185,7 +185,7 @@ "cmdUnderTest": "$TC actions add action skbedit ptype otherhost", "expExitCode": "0", "verifyCmd": "$TC actions list action skbedit", - "matchPattern": "action order [0-9]*: skbedit ptype otherhost", + "matchPattern": "action order [0-9]*: skbedit ptype otherhost", "matchCount": "1", "teardown": [ "$TC actions flush action skbedit" @@ -233,7 +233,7 @@ "cmdUnderTest": "$TC actions add action skbedit ptype host pipe index 11", "expExitCode": "0", "verifyCmd": "$TC actions get action skbedit index 11", - "matchPattern": "action order [0-9]*: skbedit ptype host pipe.*index 11 ref", + "matchPattern": "action order [0-9]*: skbedit ptype host pipe.*index 11 ref", "matchCount": "1", "teardown": [ "$TC actions flush action skbedit" @@ -257,7 +257,7 @@ "cmdUnderTest": "$TC actions add action skbedit mark 56789 reclassify index 90", "expExitCode": "0", "verifyCmd": "$TC actions get action skbedit index 90", - "matchPattern": "action order [0-9]*: skbedit mark 56789 reclassify.*index 90 ref", + "matchPattern": "action order [0-9]*: skbedit mark 56789 reclassify.*index 90 ref", "matchCount": "1", "teardown": [ "$TC actions flush action skbedit" @@ -281,7 +281,7 @@ "cmdUnderTest": "$TC actions add action skbedit queue_mapping 3 pass index 271", "expExitCode": "0", "verifyCmd": "$TC actions get action skbedit index 271", - "matchPattern": "action order [0-9]*: skbedit queue_mapping 3 pass.*index 271 ref", + "matchPattern": "action order [0-9]*: skbedit queue_mapping 3 pass.*index 271 ref", "matchCount": "1", "teardown": [ "$TC actions flush action skbedit" @@ -305,7 +305,7 @@ "cmdUnderTest": "$TC actions add action skbedit queue_mapping 3 drop index 271", "expExitCode": "0", "verifyCmd": "$TC actions get action skbedit index 271", - "matchPattern": "action order [0-9]*: skbedit queue_mapping 3 drop.*index 271 ref", + "matchPattern": "action order [0-9]*: skbedit queue_mapping 3 drop.*index 271 ref", "matchCount": "1", "teardown": [ "$TC actions flush action skbedit" @@ -329,7 +329,7 @@ "cmdUnderTest": "$TC actions add action skbedit priority 8 jump 9 index 2", "expExitCode": "0", "verifyCmd": "$TC actions get action skbedit index 2", - "matchPattern": "action order [0-9]*: skbedit priority :8 jump 9.*index 2 ref", + "matchPattern": "action order [0-9]*: skbedit priority :8 jump 9.*index 2 ref", "matchCount": "1", "teardown": [ "$TC actions flush action skbedit" @@ -353,7 +353,7 @@ "cmdUnderTest": "$TC actions add action skbedit priority 16 continue index 32", "expExitCode": "0", "verifyCmd": "$TC actions get action skbedit index 32", - "matchPattern": "action order [0-9]*: skbedit priority :16 continue.*index 32 ref", + "matchPattern": "action order [0-9]*: skbedit priority :16 continue.*index 32 ref", "matchCount": "1", "teardown": [ "$TC actions flush action skbedit" @@ -377,7 +377,7 @@ "cmdUnderTest": "$TC actions add action skbedit priority 16 continue index 32 cookie deadbeef", "expExitCode": "0", "verifyCmd": "$TC actions get action skbedit index 32", - "matchPattern": "action order [0-9]*: skbedit priority :16 continue.*index 32 ref.*cookie deadbeef", + "matchPattern": "action order [0-9]*: skbedit priority :16 continue.*index 32 ref.*cookie deadbeef", "matchCount": "1", "teardown": [ "$TC actions flush action skbedit" @@ -405,7 +405,7 @@ "cmdUnderTest": "$TC actions list action skbedit", "expExitCode": "0", "verifyCmd": "$TC actions list action skbedit", - "matchPattern": "action order [0-9]*: skbedit", + "matchPattern": "action order [0-9]*: skbedit", "matchCount": "4", "teardown": [ "$TC actions flush action skbedit" -- cgit v1.2.3-58-ga151 From 736ac8146404560215e7aaf4e28fc2d8746a72d1 Mon Sep 17 00:00:00 2001 From: Keara Leibovitz <kleib@mojatatu.com> Date: Tue, 7 Aug 2018 15:18:43 -0400 Subject: tc-tests: initial version of nat action unit tests Initial set of nat action unit tests. Signed-off-by: Keara Leibovitz <kleib@mojatatu.com> Signed-off-by: David S. Miller <davem@davemloft.net> --- .../selftests/tc-testing/tc-tests/actions/nat.json | 593 +++++++++++++++++++++ 1 file changed, 593 insertions(+) create mode 100644 tools/testing/selftests/tc-testing/tc-tests/actions/nat.json (limited to 'tools') diff --git a/tools/testing/selftests/tc-testing/tc-tests/actions/nat.json b/tools/testing/selftests/tc-testing/tc-tests/actions/nat.json new file mode 100644 index 000000000000..0080dc2fd41c --- /dev/null +++ b/tools/testing/selftests/tc-testing/tc-tests/actions/nat.json @@ -0,0 +1,593 @@ +[ + { + "id": "7565", + "name": "Add nat action on ingress with default control action", + "category": [ + "actions", + "nat" + ], + "setup": [ + [ + "$TC actions flush action nat", + 0, + 1, + 255 + ] + ], + "cmdUnderTest": "$TC actions add action nat ingress 192.168.1.1 200.200.200.1", + "expExitCode": "0", + "verifyCmd": "$TC actions ls action nat", + "matchPattern": "action order [0-9]+: nat ingress 192.168.1.1/32 200.200.200.1 pass", + "matchCount": "1", + "teardown": [ + "$TC actions flush action nat" + ] + }, + { + "id": "fd79", + "name": "Add nat action on ingress with pipe control action", + "category": [ + "actions", + "nat" + ], + "setup": [ + [ + "$TC actions flush action nat", + 0, + 1, + 255 + ] + ], + "cmdUnderTest": "$TC actions add action nat ingress 1.1.1.1 2.2.2.1 pipe index 77", + "expExitCode": "0", + "verifyCmd": "$TC actions get action nat index 77", + "matchPattern": "action order [0-9]+: nat ingress 1.1.1.1/32 2.2.2.1 pipe.*index 77 ref", + "matchCount": "1", + "teardown": [ + "$TC actions flush action nat" + ] + }, + { + "id": "eab9", + "name": "Add nat action on ingress with continue control action", + "category": [ + "actions", + "nat" + ], + "setup": [ + [ + "$TC actions flush action nat", + 0, + 1, + 255 + ] + ], + "cmdUnderTest": "$TC actions add action nat ingress 192.168.10.10 192.168.20.20 continue index 1000", + "expExitCode": "0", + "verifyCmd": "$TC actions get action nat index 1000", + "matchPattern": "action order [0-9]+: nat ingress 192.168.10.10/32 192.168.20.20 continue.*index 1000 ref", + "matchCount": "1", + "teardown": [ + "$TC actions flush action nat" + ] + }, + { + "id": "c53a", + "name": "Add nat action on ingress with reclassify control action", + "category": [ + "actions", + "nat" + ], + "setup": [ + [ + "$TC actions flush action nat", + 0, + 1, + 255 + ] + ], + "cmdUnderTest": "$TC actions add action nat ingress 192.168.10.10 192.168.20.20 reclassify index 1000", + "expExitCode": "0", + "verifyCmd": "$TC actions get action nat index 1000", + "matchPattern": "action order [0-9]+: nat ingress 192.168.10.10/32 192.168.20.20 reclassify.*index 1000 ref", + "matchCount": "1", + "teardown": [ + "$TC actions flush action nat" + ] + }, + { + "id": "76c9", + "name": "Add nat action on ingress with jump control action", + "category": [ + "actions", + "nat" + ], + "setup": [ + [ + "$TC actions flush action nat", + 0, + 1, + 255 + ] + ], + "cmdUnderTest": "$TC actions add action nat ingress 12.18.10.10 12.18.20.20 jump 10 index 22", + "expExitCode": "0", + "verifyCmd": "$TC actions get action nat index 22", + "matchPattern": "action order [0-9]+: nat ingress 12.18.10.10/32 12.18.20.20 jump 10.*index 22 ref", + "matchCount": "1", + "teardown": [ + "$TC actions flush action nat" + ] + }, + { + "id": "24c6", + "name": "Add nat action on ingress with drop control action", + "category": [ + "actions", + "nat" + ], + "setup": [ + [ + "$TC actions flush action nat", + 0, + 1, + 255 + ] + ], + "cmdUnderTest": "$TC actions add action nat ingress 1.18.1.1 1.18.2.2 drop index 722", + "expExitCode": "0", + "verifyCmd": "$TC actions get action nat index 722", + "matchPattern": "action order [0-9]+: nat ingress 1.18.1.1/32 1.18.2.2 drop.*index 722 ref", + "matchCount": "1", + "teardown": [ + "$TC actions flush action nat" + ] + }, + { + "id": "2120", + "name": "Add nat action on ingress with maximum index value", + "category": [ + "actions", + "nat" + ], + "setup": [ + [ + "$TC actions flush action nat", + 0, + 1, + 255 + ] + ], + "cmdUnderTest": "$TC actions add action nat ingress 1.18.1.1 1.18.2.2 index 4294967295", + "expExitCode": "0", + "verifyCmd": "$TC actions get action nat index 4294967295", + "matchPattern": "action order [0-9]+: nat ingress 1.18.1.1/32 1.18.2.2 pass.*index 4294967295 ref", + "matchCount": "1", + "teardown": [ + "$TC actions flush action nat" + ] + }, + { + "id": "3e9d", + "name": "Add nat action on ingress with invalid index value", + "category": [ + "actions", + "nat" + ], + "setup": [ + [ + "$TC actions flush action nat", + 0, + 1, + 255 + ] + ], + "cmdUnderTest": "$TC actions add action nat ingress 1.18.1.1 1.18.2.2 index 4294967295555", + "expExitCode": "255", + "verifyCmd": "$TC actions get action nat index 4294967295555", + "matchPattern": "action order [0-9]+: nat ingress 1.18.1.1/32 1.18.2.2 pass.*index 4294967295555 ref", + "matchCount": "0", + "teardown": [ + [ + "$TC actions flush action nat", + 0, + 1, + 255 + ] + ] + }, + { + "id": "f6c9", + "name": "Add nat action on ingress with invalid IP address", + "category": [ + "actions", + "nat" + ], + "setup": [ + [ + "$TC actions flush action nat", + 0, + 1, + 255 + ] + ], + "cmdUnderTest": "$TC actions add action nat ingress 1.1.1.1 1.1888.2.2 index 7", + "expExitCode": "255", + "verifyCmd": "$TC actions get action nat index 7", + "matchPattern": "action order [0-9]+: nat ingress 1.1.1.1/32 1.1888.2.2 pass.*index 7 ref", + "matchCount": "0", + "teardown": [ + [ + "$TC actions flush action nat", + 0, + 1, + 255 + ] + ] + }, + { + "id": "be25", + "name": "Add nat action on ingress with invalid argument", + "category": [ + "actions", + "nat" + ], + "setup": [ + [ + "$TC actions flush action nat", + 0, + 1, + 255 + ] + ], + "cmdUnderTest": "$TC actions add action nat ingress 1.1.1.1 1.18.2.2 another_arg index 12", + "expExitCode": "255", + "verifyCmd": "$TC actions get action nat index 12", + "matchPattern": "action order [0-9]+: nat ingress 1.1.1.1/32 1.18.2.2 pass.*another_arg.*index 12 ref", + "matchCount": "0", + "teardown": [ + [ + "$TC actions flush action nat", + 0, + 1, + 255 + ] + ] + }, + { + "id": "a7bd", + "name": "Add nat action on ingress with DEFAULT IP address", + "category": [ + "actions", + "nat" + ], + "setup": [ + [ + "$TC actions flush action nat", + 0, + 1, + 255 + ] + ], + "cmdUnderTest": "$TC actions add action nat ingress default 10.10.10.1 index 12", + "expExitCode": "0", + "verifyCmd": "$TC actions get action nat index 12", + "matchPattern": "action order [0-9]+: nat ingress 0.0.0.0/32 10.10.10.1 pass.*index 12 ref", + "matchCount": "1", + "teardown": [ + "$TC actions flush action nat" + ] + }, + { + "id": "ee1e", + "name": "Add nat action on ingress with ANY IP address", + "category": [ + "actions", + "nat" + ], + "setup": [ + [ + "$TC actions flush action nat", + 0, + 1, + 255 + ] + ], + "cmdUnderTest": "$TC actions add action nat ingress any 10.10.10.1 index 12", + "expExitCode": "0", + "verifyCmd": "$TC actions get action nat index 12", + "matchPattern": "action order [0-9]+: nat ingress 0.0.0.0/32 10.10.10.1 pass.*index 12 ref", + "matchCount": "1", + "teardown": [ + "$TC actions flush action nat" + ] + }, + { + "id": "1de8", + "name": "Add nat action on ingress with ALL IP address", + "category": [ + "actions", + "nat" + ], + "setup": [ + [ + "$TC actions flush action nat", + 0, + 1, + 255 + ] + ], + "cmdUnderTest": "$TC actions add action nat ingress all 10.10.10.1 index 12", + "expExitCode": "0", + "verifyCmd": "$TC actions get action nat index 12", + "matchPattern": "action order [0-9]+: nat ingress 0.0.0.0/32 10.10.10.1 pass.*index 12 ref", + "matchCount": "1", + "teardown": [ + "$TC actions flush action nat" + ] + }, + { + "id": "8dba", + "name": "Add nat action on egress with default control action", + "category": [ + "actions", + "nat" + ], + "setup": [ + [ + "$TC actions flush action nat", + 0, + 1, + 255 + ] + ], + "cmdUnderTest": "$TC actions add action nat egress 10.10.10.1 20.20.20.1", + "expExitCode": "0", + "verifyCmd": "$TC actions ls action nat", + "matchPattern": "action order [0-9]+: nat egress 10.10.10.1/32 20.20.20.1 pass", + "matchCount": "1", + "teardown": [ + "$TC actions flush action nat" + ] + }, + { + "id": "19a7", + "name": "Add nat action on egress with pipe control action", + "category": [ + "actions", + "nat" + ], + "setup": [ + [ + "$TC actions flush action nat", + 0, + 1, + 255 + ] + ], + "cmdUnderTest": "$TC actions add action nat egress 10.10.10.1 20.20.20.1 pipe", + "expExitCode": "0", + "verifyCmd": "$TC actions ls action nat", + "matchPattern": "action order [0-9]+: nat egress 10.10.10.1/32 20.20.20.1 pipe", + "matchCount": "1", + "teardown": [ + "$TC actions flush action nat" + ] + }, + { + "id": "f1d9", + "name": "Add nat action on egress with continue control action", + "category": [ + "actions", + "nat" + ], + "setup": [ + [ + "$TC actions flush action nat", + 0, + 1, + 255 + ] + ], + "cmdUnderTest": "$TC actions add action nat egress 10.10.10.1 20.20.20.1 continue", + "expExitCode": "0", + "verifyCmd": "$TC actions ls action nat", + "matchPattern": "action order [0-9]+: nat egress 10.10.10.1/32 20.20.20.1 continue", + "matchCount": "1", + "teardown": [ + "$TC actions flush action nat" + ] + }, + { + "id": "6d4a", + "name": "Add nat action on egress with reclassify control action", + "category": [ + "actions", + "nat" + ], + "setup": [ + [ + "$TC actions flush action nat", + 0, + 1, + 255 + ] + ], + "cmdUnderTest": "$TC actions add action nat egress 10.10.10.1 20.20.20.1 reclassify", + "expExitCode": "0", + "verifyCmd": "$TC actions ls action nat", + "matchPattern": "action order [0-9]+: nat egress 10.10.10.1/32 20.20.20.1 reclassify", + "matchCount": "1", + "teardown": [ + "$TC actions flush action nat" + ] + }, + { + "id": "b313", + "name": "Add nat action on egress with jump control action", + "category": [ + "actions", + "nat" + ], + "setup": [ + [ + "$TC actions flush action nat", + 0, + 1, + 255 + ] + ], + "cmdUnderTest": "$TC actions add action nat egress 10.10.10.1 20.20.20.1 jump 777", + "expExitCode": "0", + "verifyCmd": "$TC actions ls action nat", + "matchPattern": "action order [0-9]+: nat egress 10.10.10.1/32 20.20.20.1 jump 777", + "matchCount": "1", + "teardown": [ + "$TC actions flush action nat" + ] + }, + { + "id": "d9fc", + "name": "Add nat action on egress with drop control action", + "category": [ + "actions", + "nat" + ], + "setup": [ + [ + "$TC actions flush action nat", + 0, + 1, + 255 + ] + ], + "cmdUnderTest": "$TC actions add action nat egress 10.10.10.1 20.20.20.1 drop", + "expExitCode": "0", + "verifyCmd": "$TC actions ls action nat", + "matchPattern": "action order [0-9]+: nat egress 10.10.10.1/32 20.20.20.1 drop", + "matchCount": "1", + "teardown": [ + "$TC actions flush action nat" + ] + }, + { + "id": "a895", + "name": "Add nat action on egress with DEFAULT IP address", + "category": [ + "actions", + "nat" + ], + "setup": [ + [ + "$TC actions flush action nat", + 0, + 1, + 255 + ] + ], + "cmdUnderTest": "$TC actions add action nat egress default 20.20.20.1 pipe index 10", + "expExitCode": "0", + "verifyCmd": "$TC actions get action nat index 10", + "matchPattern": "action order [0-9]+: nat egress 0.0.0.0/32 20.20.20.1 pipe.*index 10 ref", + "matchCount": "1", + "teardown": [ + "$TC actions flush action nat" + ] + }, + { + "id": "2572", + "name": "Add nat action on egress with ANY IP address", + "category": [ + "actions", + "nat" + ], + "setup": [ + [ + "$TC actions flush action nat", + 0, + 1, + 255 + ] + ], + "cmdUnderTest": "$TC actions add action nat egress any 20.20.20.1 pipe index 10", + "expExitCode": "0", + "verifyCmd": "$TC actions get action nat index 10", + "matchPattern": "action order [0-9]+: nat egress 0.0.0.0/32 20.20.20.1 pipe.*index 10 ref", + "matchCount": "1", + "teardown": [ + "$TC actions flush action nat" + ] + }, + { + "id": "37f3", + "name": "Add nat action on egress with ALL IP address", + "category": [ + "actions", + "nat" + ], + "setup": [ + [ + "$TC actions flush action nat", + 0, + 1, + 255 + ] + ], + "cmdUnderTest": "$TC actions add action nat egress all 20.20.20.1 pipe index 10", + "expExitCode": "0", + "verifyCmd": "$TC actions get action nat index 10", + "matchPattern": "action order [0-9]+: nat egress 0.0.0.0/32 20.20.20.1 pipe.*index 10 ref", + "matchCount": "1", + "teardown": [ + "$TC actions flush action nat" + ] + }, + { + "id": "6054", + "name": "Add nat action on egress with cookie", + "category": [ + "actions", + "nat" + ], + "setup": [ + [ + "$TC actions flush action nat", + 0, + 1, + 255 + ] + ], + "cmdUnderTest": "$TC actions add action nat egress all 20.20.20.1 pipe index 10 cookie aa1bc2d3eeff112233445566778800a1", + "expExitCode": "0", + "verifyCmd": "$TC actions get action nat index 10", + "matchPattern": "action order [0-9]+: nat egress 0.0.0.0/32 20.20.20.1 pipe.*index 10 ref.*cookie aa1bc2d3eeff112233445566778800a1", + "matchCount": "1", + "teardown": [ + "$TC actions flush action nat" + ] + }, + { + "id": "79d6", + "name": "Add nat action on ingress with cookie", + "category": [ + "actions", + "nat" + ], + "setup": [ + [ + "$TC actions flush action nat", + 0, + 1, + 255 + ] + ], + "cmdUnderTest": "$TC actions add action nat ingress 192.168.1.1 10.10.10.1 reclassify index 1 cookie 112233445566778899aabbccddeeff11", + "expExitCode": "0", + "verifyCmd": "$TC actions get action nat index 1", + "matchPattern": "action order [0-9]+: nat ingress 192.168.1.1/32 10.10.10.1 reclassify.*index 1 ref.*cookie 112233445566778899aabbccddeeff11", + "matchCount": "1", + "teardown": [ + "$TC actions flush action nat" + ] + } +] -- cgit v1.2.3-58-ga151 From 541ad323db3a692a80c276785a15790ba52da24b Mon Sep 17 00:00:00 2001 From: Nir Dotan <nird@mellanox.com> Date: Tue, 7 Aug 2018 19:41:55 +0300 Subject: selftests: forwarding: gre_multipath: Update next-hop statistics match criteria gre_multipath test was using egress vlan_id matching on flows, for the purpose of collecting next-hops statistics, later to be compared against configured weights. As matching on vlan_id on egress direction is not supported on all HW devices, change the match criteria to use destination IP. Signed-off-by: Nir Dotan <nird@mellanox.com> Acked-by: Petr Machata <petrm@mellanox.com> Signed-off-by: David S. Miller <davem@davemloft.net> --- tools/testing/selftests/net/forwarding/gre_multipath.sh | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'tools') diff --git a/tools/testing/selftests/net/forwarding/gre_multipath.sh b/tools/testing/selftests/net/forwarding/gre_multipath.sh index 3b1e047f7617..cca2baa03fb8 100755 --- a/tools/testing/selftests/net/forwarding/gre_multipath.sh +++ b/tools/testing/selftests/net/forwarding/gre_multipath.sh @@ -95,10 +95,10 @@ sw1_create() nexthop dev g1b tc qdisc add dev $ul1 clsact - tc filter add dev $ul1 egress pref 111 prot 802.1q \ - flower vlan_id 111 action pass - tc filter add dev $ul1 egress pref 222 prot 802.1q \ - flower vlan_id 222 action pass + tc filter add dev $ul1 egress pref 111 prot ipv4 \ + flower dst_ip 192.0.2.66 action pass + tc filter add dev $ul1 egress pref 222 prot ipv4 \ + flower dst_ip 192.0.2.82 action pass } sw1_destroy() -- cgit v1.2.3-58-ga151 From 3dd91570564679e13c4755cc5cdf2a535e3e1e34 Mon Sep 17 00:00:00 2001 From: Ido Schimmel <idosch@mellanox.com> Date: Thu, 9 Aug 2018 11:59:10 +0300 Subject: selftests: mlxsw: Add TC flower test for Spectrum-2 Signed-off-by: Ido Schimmel <idosch@mellanox.com> Reviewed-by: Jiri Pirko <jiri@mellanox.com> Signed-off-by: David S. Miller <davem@davemloft.net> --- .../drivers/net/mlxsw/spectrum-2/tc_flower.sh | 366 +++++++++++++++++++++ 1 file changed, 366 insertions(+) create mode 100755 tools/testing/selftests/drivers/net/mlxsw/spectrum-2/tc_flower.sh (limited to 'tools') diff --git a/tools/testing/selftests/drivers/net/mlxsw/spectrum-2/tc_flower.sh b/tools/testing/selftests/drivers/net/mlxsw/spectrum-2/tc_flower.sh new file mode 100755 index 000000000000..3b75180f455d --- /dev/null +++ b/tools/testing/selftests/drivers/net/mlxsw/spectrum-2/tc_flower.sh @@ -0,0 +1,366 @@ +#!/bin/bash +# SPDX-License-Identifier: GPL-2.0 + +# This test is for checking the A-TCAM and C-TCAM operation in Spectrum-2. +# It tries to exercise as many code paths in the eRP state machine as +# possible. + +lib_dir=$(dirname $0)/../../../../net/forwarding + +ALL_TESTS="single_mask_test identical_filters_test two_masks_test \ + multiple_masks_test ctcam_edge_cases_test" +NUM_NETIFS=2 +source $lib_dir/tc_common.sh +source $lib_dir/lib.sh + +tcflags="skip_hw" + +h1_create() +{ + simple_if_init $h1 192.0.2.1/24 198.51.100.1/24 +} + +h1_destroy() +{ + simple_if_fini $h1 192.0.2.1/24 198.51.100.1/24 +} + +h2_create() +{ + simple_if_init $h2 192.0.2.2/24 198.51.100.2/24 + tc qdisc add dev $h2 clsact +} + +h2_destroy() +{ + tc qdisc del dev $h2 clsact + simple_if_fini $h2 192.0.2.2/24 198.51.100.2/24 +} + +single_mask_test() +{ + # When only a single mask is required, the device uses the master + # mask and not the eRP table. Verify that under this mode the right + # filter is matched + + RET=0 + + tc filter add dev $h2 ingress protocol ip pref 1 handle 101 flower \ + $tcflags dst_ip 192.0.2.2 action drop + + $MZ $h1 -c 1 -p 64 -a $h1mac -b $h2mac -A 192.0.2.1 -B 192.0.2.2 \ + -t ip -q + + tc_check_packets "dev $h2 ingress" 101 1 + check_err $? "Single filter - did not match" + + tc filter add dev $h2 ingress protocol ip pref 2 handle 102 flower \ + $tcflags dst_ip 198.51.100.2 action drop + + $MZ $h1 -c 1 -p 64 -a $h1mac -b $h2mac -A 192.0.2.1 -B 192.0.2.2 \ + -t ip -q + + tc_check_packets "dev $h2 ingress" 101 2 + check_err $? "Two filters - did not match highest priority" + + $MZ $h1 -c 1 -p 64 -a $h1mac -b $h2mac -A 198.51.100.1 -B 198.51.100.2 \ + -t ip -q + + tc_check_packets "dev $h2 ingress" 102 1 + check_err $? "Two filters - did not match lowest priority" + + tc filter del dev $h2 ingress protocol ip pref 1 handle 101 flower + + $MZ $h1 -c 1 -p 64 -a $h1mac -b $h2mac -A 198.51.100.1 -B 198.51.100.2 \ + -t ip -q + + tc_check_packets "dev $h2 ingress" 102 2 + check_err $? "Single filter - did not match after delete" + + tc filter del dev $h2 ingress protocol ip pref 2 handle 102 flower + + log_test "single mask test ($tcflags)" +} + +identical_filters_test() +{ + # When two filters that only differ in their priority are used, + # one needs to be inserted into the C-TCAM. This test verifies + # that filters are correctly spilled to C-TCAM and that the right + # filter is matched + + RET=0 + + tc filter add dev $h2 ingress protocol ip pref 1 handle 101 flower \ + $tcflags dst_ip 192.0.2.2 action drop + tc filter add dev $h2 ingress protocol ip pref 2 handle 102 flower \ + $tcflags dst_ip 192.0.2.2 action drop + + $MZ $h1 -c 1 -p 64 -a $h1mac -b $h2mac -A 192.0.2.1 -B 192.0.2.2 \ + -t ip -q + + tc_check_packets "dev $h2 ingress" 101 1 + check_err $? "Did not match A-TCAM filter" + + tc filter del dev $h2 ingress protocol ip pref 1 handle 101 flower + + $MZ $h1 -c 1 -p 64 -a $h1mac -b $h2mac -A 192.0.2.1 -B 192.0.2.2 \ + -t ip -q + + tc_check_packets "dev $h2 ingress" 102 1 + check_err $? "Did not match C-TCAM filter after A-TCAM delete" + + tc filter add dev $h2 ingress protocol ip pref 3 handle 103 flower \ + $tcflags dst_ip 192.0.2.2 action drop + + $MZ $h1 -c 1 -p 64 -a $h1mac -b $h2mac -A 192.0.2.1 -B 192.0.2.2 \ + -t ip -q + + tc_check_packets "dev $h2 ingress" 102 2 + check_err $? "Did not match C-TCAM filter after A-TCAM add" + + tc filter del dev $h2 ingress protocol ip pref 2 handle 102 flower + + $MZ $h1 -c 1 -p 64 -a $h1mac -b $h2mac -A 192.0.2.1 -B 192.0.2.2 \ + -t ip -q + + tc_check_packets "dev $h2 ingress" 103 1 + check_err $? "Did not match A-TCAM filter after C-TCAM delete" + + tc filter del dev $h2 ingress protocol ip pref 3 handle 103 flower + + log_test "identical filters test ($tcflags)" +} + +two_masks_test() +{ + # When more than one mask is required, the eRP table is used. This + # test verifies that the eRP table is correctly allocated and used + + RET=0 + + tc filter add dev $h2 ingress protocol ip pref 1 handle 101 flower \ + $tcflags dst_ip 192.0.2.2 action drop + tc filter add dev $h2 ingress protocol ip pref 3 handle 103 flower \ + $tcflags dst_ip 192.0.0.0/16 action drop + + $MZ $h1 -c 1 -p 64 -a $h1mac -b $h2mac -A 192.0.2.1 -B 192.0.2.2 \ + -t ip -q + + tc_check_packets "dev $h2 ingress" 101 1 + check_err $? "Two filters - did not match highest priority" + + tc filter del dev $h2 ingress protocol ip pref 1 handle 101 flower + + $MZ $h1 -c 1 -p 64 -a $h1mac -b $h2mac -A 192.0.2.1 -B 192.0.2.2 \ + -t ip -q + + tc_check_packets "dev $h2 ingress" 103 1 + check_err $? "Single filter - did not match" + + tc filter add dev $h2 ingress protocol ip pref 2 handle 102 flower \ + $tcflags dst_ip 192.0.2.0/24 action drop + + $MZ $h1 -c 1 -p 64 -a $h1mac -b $h2mac -A 192.0.2.1 -B 192.0.2.2 \ + -t ip -q + + tc_check_packets "dev $h2 ingress" 102 1 + check_err $? "Two filters - did not match highest priority after add" + + tc filter del dev $h2 ingress protocol ip pref 3 handle 103 flower + tc filter del dev $h2 ingress protocol ip pref 2 handle 102 flower + + log_test "two masks test ($tcflags)" +} + +multiple_masks_test() +{ + # The number of masks in a region is limited. Once the maximum + # number of masks has been reached filters that require new + # masks are spilled to the C-TCAM. This test verifies that + # spillage is performed correctly and that the right filter is + # matched + + local index + + RET=0 + + NUM_MASKS=32 + BASE_INDEX=100 + + for i in $(eval echo {1..$NUM_MASKS}); do + index=$((BASE_INDEX - i)) + + tc filter add dev $h2 ingress protocol ip pref $index \ + handle $index \ + flower $tcflags dst_ip 192.0.2.2/${i} src_ip 192.0.2.1 \ + action drop + + $MZ $h1 -c 1 -p 64 -a $h1mac -b $h2mac -A 192.0.2.1 \ + -B 192.0.2.2 -t ip -q + + tc_check_packets "dev $h2 ingress" $index 1 + check_err $? "$i filters - did not match highest priority (add)" + done + + for i in $(eval echo {$NUM_MASKS..1}); do + index=$((BASE_INDEX - i)) + + $MZ $h1 -c 1 -p 64 -a $h1mac -b $h2mac -A 192.0.2.1 \ + -B 192.0.2.2 -t ip -q + + tc_check_packets "dev $h2 ingress" $index 2 + check_err $? "$i filters - did not match highest priority (del)" + + tc filter del dev $h2 ingress protocol ip pref $index \ + handle $index flower + done + + log_test "multiple masks test ($tcflags)" +} + +ctcam_two_atcam_masks_test() +{ + RET=0 + + # First case: C-TCAM is disabled when there are two A-TCAM masks. + # We push a filter into the C-TCAM by using two identical filters + # as in identical_filters_test() + + # Filter goes into A-TCAM + tc filter add dev $h2 ingress protocol ip pref 1 handle 101 flower \ + $tcflags dst_ip 192.0.2.2 action drop + # Filter goes into C-TCAM + tc filter add dev $h2 ingress protocol ip pref 2 handle 102 flower \ + $tcflags dst_ip 192.0.2.2 action drop + # Filter goes into A-TCAM + tc filter add dev $h2 ingress protocol ip pref 3 handle 103 flower \ + $tcflags dst_ip 192.0.2.0/24 action drop + + $MZ $h1 -c 1 -p 64 -a $h1mac -b $h2mac -A 192.0.2.1 -B 192.0.2.2 \ + -t ip -q + + tc_check_packets "dev $h2 ingress" 101 1 + check_err $? "Did not match A-TCAM filter" + + # Delete both A-TCAM and C-TCAM filters and make sure the remaining + # A-TCAM filter still works + tc filter del dev $h2 ingress protocol ip pref 2 handle 102 flower + tc filter del dev $h2 ingress protocol ip pref 1 handle 101 flower + + $MZ $h1 -c 1 -p 64 -a $h1mac -b $h2mac -A 192.0.2.1 -B 192.0.2.2 \ + -t ip -q + + tc_check_packets "dev $h2 ingress" 103 1 + check_err $? "Did not match A-TCAM filter" + + tc filter del dev $h2 ingress protocol ip pref 3 handle 103 flower + + log_test "ctcam with two atcam masks test ($tcflags)" +} + +ctcam_one_atcam_mask_test() +{ + RET=0 + + # Second case: C-TCAM is disabled when there is one A-TCAM mask. + # The test is similar to identical_filters_test() + + # Filter goes into A-TCAM + tc filter add dev $h2 ingress protocol ip pref 2 handle 102 flower \ + $tcflags dst_ip 192.0.2.2 action drop + # Filter goes into C-TCAM + tc filter add dev $h2 ingress protocol ip pref 1 handle 101 flower \ + $tcflags dst_ip 192.0.2.2 action drop + + $MZ $h1 -c 1 -p 64 -a $h1mac -b $h2mac -A 192.0.2.1 -B 192.0.2.2 \ + -t ip -q + + tc_check_packets "dev $h2 ingress" 101 1 + check_err $? "Did not match C-TCAM filter" + + tc filter del dev $h2 ingress protocol ip pref 1 handle 101 flower + + $MZ $h1 -c 1 -p 64 -a $h1mac -b $h2mac -A 192.0.2.1 -B 192.0.2.2 \ + -t ip -q + + tc_check_packets "dev $h2 ingress" 102 1 + check_err $? "Did not match A-TCAM filter" + + tc filter del dev $h2 ingress protocol ip pref 2 handle 102 flower + + log_test "ctcam with one atcam mask test ($tcflags)" +} + +ctcam_no_atcam_masks_test() +{ + RET=0 + + # Third case: C-TCAM is disabled when there are no A-TCAM masks + # This test exercises the code path that transitions the eRP table + # to its initial state after deleting the last C-TCAM mask + + # Filter goes into A-TCAM + tc filter add dev $h2 ingress protocol ip pref 1 handle 101 flower \ + $tcflags dst_ip 192.0.2.2 action drop + # Filter goes into C-TCAM + tc filter add dev $h2 ingress protocol ip pref 2 handle 102 flower \ + $tcflags dst_ip 192.0.2.2 action drop + + tc filter del dev $h2 ingress protocol ip pref 1 handle 101 flower + tc filter del dev $h2 ingress protocol ip pref 2 handle 102 flower + + log_test "ctcam with no atcam masks test ($tcflags)" +} + +ctcam_edge_cases_test() +{ + # When the C-TCAM is disabled after deleting the last C-TCAM + # mask, we want to make sure the eRP state machine is put in + # the correct state + + ctcam_two_atcam_masks_test + ctcam_one_atcam_mask_test + ctcam_no_atcam_masks_test +} + +setup_prepare() +{ + h1=${NETIFS[p1]} + h2=${NETIFS[p2]} + h1mac=$(mac_get $h1) + h2mac=$(mac_get $h2) + + vrf_prepare + + h1_create + h2_create +} + +cleanup() +{ + pre_cleanup + + h2_destroy + h1_destroy + + vrf_cleanup +} + +trap cleanup EXIT + +setup_prepare +setup_wait + +tests_run + +if ! tc_offload_check; then + check_err 1 "Could not test offloaded functionality" + log_test "mlxsw-specific tests for tc flower" + exit +else + tcflags="skip_sw" + tests_run +fi + +exit $EXIT_STATUS -- cgit v1.2.3-58-ga151 From af2a81dab44758de0b94679615ea75e8ee30aace Mon Sep 17 00:00:00 2001 From: Yonghong Song <yhs@fb.com> Date: Thu, 9 Aug 2018 08:55:21 -0700 Subject: tools/bpf: add bpffs pretty print btf test for hash/lru_hash maps Pretty print tests for hash/lru_hash maps are added in test_btf.c. The btf type blob is the same as pretty print array map test. The test result: $ mount -t bpf bpf /sys/fs/bpf $ ./test_btf -p BTF pretty print array......OK BTF pretty print hash......OK BTF pretty print lru hash......OK PASS:3 SKIP:0 FAIL:0 Signed-off-by: Yonghong Song <yhs@fb.com> Signed-off-by: Daniel Borkmann <daniel@iogearbox.net> --- tools/testing/selftests/bpf/test_btf.c | 87 ++++++++++++++++++++++++++++------ 1 file changed, 72 insertions(+), 15 deletions(-) (limited to 'tools') diff --git a/tools/testing/selftests/bpf/test_btf.c b/tools/testing/selftests/bpf/test_btf.c index ffdd27737c9e..7fa8c800c540 100644 --- a/tools/testing/selftests/bpf/test_btf.c +++ b/tools/testing/selftests/bpf/test_btf.c @@ -131,6 +131,8 @@ struct btf_raw_test { __u32 max_entries; bool btf_load_err; bool map_create_err; + bool ordered_map; + bool lossless_map; int hdr_len_delta; int type_off_delta; int str_off_delta; @@ -2093,8 +2095,7 @@ struct pprint_mapv { } aenum; }; -static struct btf_raw_test pprint_test = { - .descr = "BTF pretty print test #1", +static struct btf_raw_test pprint_test_template = { .raw_types = { /* unsighed char */ /* [1] */ BTF_TYPE_INT_ENC(NAME_TBD, 0, 0, 8, 1), @@ -2146,8 +2147,6 @@ static struct btf_raw_test pprint_test = { }, .str_sec = "\0unsigned char\0unsigned short\0unsigned int\0int\0unsigned long long\0uint8_t\0uint16_t\0uint32_t\0int32_t\0uint64_t\0ui64\0ui8a\0ENUM_ZERO\0ENUM_ONE\0ENUM_TWO\0ENUM_THREE\0pprint_mapv\0ui32\0ui16\0si32\0unused_bits2a\0bits28\0unused_bits2b\0aenum", .str_sec_size = sizeof("\0unsigned char\0unsigned short\0unsigned int\0int\0unsigned long long\0uint8_t\0uint16_t\0uint32_t\0int32_t\0uint64_t\0ui64\0ui8a\0ENUM_ZERO\0ENUM_ONE\0ENUM_TWO\0ENUM_THREE\0pprint_mapv\0ui32\0ui16\0si32\0unused_bits2a\0bits28\0unused_bits2b\0aenum"), - .map_type = BPF_MAP_TYPE_ARRAY, - .map_name = "pprint_test", .key_size = sizeof(unsigned int), .value_size = sizeof(struct pprint_mapv), .key_type_id = 3, /* unsigned int */ @@ -2155,6 +2154,40 @@ static struct btf_raw_test pprint_test = { .max_entries = 128 * 1024, }; +static struct btf_pprint_test_meta { + const char *descr; + enum bpf_map_type map_type; + const char *map_name; + bool ordered_map; + bool lossless_map; +} pprint_tests_meta[] = { +{ + .descr = "BTF pretty print array", + .map_type = BPF_MAP_TYPE_ARRAY, + .map_name = "pprint_test_array", + .ordered_map = true, + .lossless_map = true, +}, + +{ + .descr = "BTF pretty print hash", + .map_type = BPF_MAP_TYPE_HASH, + .map_name = "pprint_test_hash", + .ordered_map = false, + .lossless_map = true, +}, + +{ + .descr = "BTF pretty print lru hash", + .map_type = BPF_MAP_TYPE_LRU_HASH, + .map_name = "pprint_test_lru_hash", + .ordered_map = false, + .lossless_map = false, +}, + +}; + + static void set_pprint_mapv(struct pprint_mapv *v, uint32_t i) { v->ui32 = i; @@ -2166,10 +2199,12 @@ static void set_pprint_mapv(struct pprint_mapv *v, uint32_t i) v->aenum = i & 0x03; } -static int test_pprint(void) +static int do_test_pprint(void) { - const struct btf_raw_test *test = &pprint_test; + const struct btf_raw_test *test = &pprint_test_template; struct bpf_create_map_attr create_attr = {}; + unsigned int key, nr_read_elems; + bool ordered_map, lossless_map; int map_fd = -1, btf_fd = -1; struct pprint_mapv mapv = {}; unsigned int raw_btf_size; @@ -2178,7 +2213,6 @@ static int test_pprint(void) char pin_path[255]; size_t line_len = 0; char *line = NULL; - unsigned int key; uint8_t *raw_btf; ssize_t nread; int err, ret; @@ -2251,14 +2285,18 @@ static int test_pprint(void) goto done; } - key = 0; + nr_read_elems = 0; + ordered_map = test->ordered_map; + lossless_map = test->lossless_map; do { ssize_t nexpected_line; + unsigned int next_key; - set_pprint_mapv(&mapv, key); + next_key = ordered_map ? nr_read_elems : atoi(line); + set_pprint_mapv(&mapv, next_key); nexpected_line = snprintf(expected_line, sizeof(expected_line), "%u: {%u,0,%d,0x%x,0x%x,0x%x,{%lu|[%u,%u,%u,%u,%u,%u,%u,%u]},%s}\n", - key, + next_key, mapv.ui32, mapv.si32, mapv.unused_bits2a, mapv.bits28, mapv.unused_bits2b, mapv.ui64, @@ -2281,11 +2319,12 @@ static int test_pprint(void) } nread = getline(&line, &line_len, pin_file); - } while (++key < test->max_entries && nread > 0); + } while (++nr_read_elems < test->max_entries && nread > 0); - if (CHECK(key < test->max_entries, - "Unexpected EOF. key:%u test->max_entries:%u", - key, test->max_entries)) { + if (lossless_map && + CHECK(nr_read_elems < test->max_entries, + "Unexpected EOF. nr_read_elems:%u test->max_entries:%u", + nr_read_elems, test->max_entries)) { err = -1; goto done; } @@ -2314,6 +2353,24 @@ done: return err; } +static int test_pprint(void) +{ + unsigned int i; + int err = 0; + + for (i = 0; i < ARRAY_SIZE(pprint_tests_meta); i++) { + pprint_test_template.descr = pprint_tests_meta[i].descr; + pprint_test_template.map_type = pprint_tests_meta[i].map_type; + pprint_test_template.map_name = pprint_tests_meta[i].map_name; + pprint_test_template.ordered_map = pprint_tests_meta[i].ordered_map; + pprint_test_template.lossless_map = pprint_tests_meta[i].lossless_map; + + err |= count_result(do_test_pprint()); + } + + return err; +} + static void usage(const char *cmd) { fprintf(stderr, "Usage: %s [-l] [[-r test_num (1 - %zu)] | [-g test_num (1 - %zu)] | [-f test_num (1 - %zu)] | [-p]]\n", @@ -2409,7 +2466,7 @@ int main(int argc, char **argv) err |= test_file(); if (args.pprint_test) - err |= count_result(test_pprint()); + err |= test_pprint(); if (args.raw_test || args.get_info_test || args.file_test || args.pprint_test) -- cgit v1.2.3-58-ga151 From aa5f0c96cc7b96a678779055b2ff4cd9dabd8ba7 Mon Sep 17 00:00:00 2001 From: Martin KaFai Lau <kafai@fb.com> Date: Wed, 8 Aug 2018 01:01:27 -0700 Subject: bpf: Refactor ARRAY_SIZE macro to bpf_util.h This patch refactors the ARRAY_SIZE macro to bpf_util.h. Signed-off-by: Martin KaFai Lau <kafai@fb.com> Acked-by: Alexei Starovoitov <ast@kernel.org> Signed-off-by: Daniel Borkmann <daniel@iogearbox.net> --- tools/testing/selftests/bpf/bpf_util.h | 4 ++++ tools/testing/selftests/bpf/test_align.c | 5 +---- tools/testing/selftests/bpf/test_btf.c | 5 +---- tools/testing/selftests/bpf/test_sock.c | 5 +---- tools/testing/selftests/bpf/test_sock_addr.c | 5 +---- tools/testing/selftests/bpf/test_verifier.c | 5 +---- 6 files changed, 9 insertions(+), 20 deletions(-) (limited to 'tools') diff --git a/tools/testing/selftests/bpf/bpf_util.h b/tools/testing/selftests/bpf/bpf_util.h index d0811b3d6a6f..315a44fa32af 100644 --- a/tools/testing/selftests/bpf/bpf_util.h +++ b/tools/testing/selftests/bpf/bpf_util.h @@ -44,4 +44,8 @@ static inline unsigned int bpf_num_possible_cpus(void) name[bpf_num_possible_cpus()] #define bpf_percpu(name, cpu) name[(cpu)].v +#ifndef ARRAY_SIZE +# define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0])) +#endif + #endif /* __BPF_UTIL__ */ diff --git a/tools/testing/selftests/bpf/test_align.c b/tools/testing/selftests/bpf/test_align.c index 6b1b302310fe..5f377ec53f2f 100644 --- a/tools/testing/selftests/bpf/test_align.c +++ b/tools/testing/selftests/bpf/test_align.c @@ -18,10 +18,7 @@ #include "../../../include/linux/filter.h" #include "bpf_rlimit.h" - -#ifndef ARRAY_SIZE -# define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0])) -#endif +#include "bpf_util.h" #define MAX_INSNS 512 #define MAX_MATCHES 16 diff --git a/tools/testing/selftests/bpf/test_btf.c b/tools/testing/selftests/bpf/test_btf.c index 7fa8c800c540..6b5cfeb7a9cc 100644 --- a/tools/testing/selftests/bpf/test_btf.c +++ b/tools/testing/selftests/bpf/test_btf.c @@ -19,6 +19,7 @@ #include <bpf/btf.h> #include "bpf_rlimit.h" +#include "bpf_util.h" static uint32_t pass_cnt; static uint32_t error_cnt; @@ -93,10 +94,6 @@ static int __base_pr(const char *format, ...) #define MAX_NR_RAW_TYPES 1024 #define BTF_LOG_BUF_SIZE 65535 -#ifndef ARRAY_SIZE -# define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0])) -#endif - static struct args { unsigned int raw_test_num; unsigned int file_test_num; diff --git a/tools/testing/selftests/bpf/test_sock.c b/tools/testing/selftests/bpf/test_sock.c index f4d99fabc56d..b8ebe2f58074 100644 --- a/tools/testing/selftests/bpf/test_sock.c +++ b/tools/testing/selftests/bpf/test_sock.c @@ -14,10 +14,7 @@ #include "cgroup_helpers.h" #include "bpf_rlimit.h" - -#ifndef ARRAY_SIZE -# define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0])) -#endif +#include "bpf_util.h" #define CG_PATH "/foo" #define MAX_INSNS 512 diff --git a/tools/testing/selftests/bpf/test_sock_addr.c b/tools/testing/selftests/bpf/test_sock_addr.c index 2e45c92d1111..aeeb76a54d63 100644 --- a/tools/testing/selftests/bpf/test_sock_addr.c +++ b/tools/testing/selftests/bpf/test_sock_addr.c @@ -20,15 +20,12 @@ #include "cgroup_helpers.h" #include "bpf_rlimit.h" +#include "bpf_util.h" #ifndef ENOTSUPP # define ENOTSUPP 524 #endif -#ifndef ARRAY_SIZE -# define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0])) -#endif - #define CG_PATH "/foo" #define CONNECT4_PROG_PATH "./connect4_prog.o" #define CONNECT6_PROG_PATH "./connect6_prog.o" diff --git a/tools/testing/selftests/bpf/test_verifier.c b/tools/testing/selftests/bpf/test_verifier.c index 452cf5c6c784..67c412d19c09 100644 --- a/tools/testing/selftests/bpf/test_verifier.c +++ b/tools/testing/selftests/bpf/test_verifier.c @@ -42,12 +42,9 @@ #endif #include "bpf_rlimit.h" #include "bpf_rand.h" +#include "bpf_util.h" #include "../../../include/linux/filter.h" -#ifndef ARRAY_SIZE -# define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0])) -#endif - #define MAX_INSNS BPF_MAXINSNS #define MAX_FIXUPS 8 #define MAX_NR_MAPS 8 -- cgit v1.2.3-58-ga151 From 3bd43a8c91cba0493e507ed7baefa9b5613c28a9 Mon Sep 17 00:00:00 2001 From: Martin KaFai Lau <kafai@fb.com> Date: Wed, 8 Aug 2018 01:01:29 -0700 Subject: bpf: Sync bpf.h uapi to tools/ This patch sync include/uapi/linux/bpf.h to tools/include/uapi/linux/ Signed-off-by: Martin KaFai Lau <kafai@fb.com> Acked-by: Alexei Starovoitov <ast@kernel.org> Signed-off-by: Daniel Borkmann <daniel@iogearbox.net> --- tools/include/uapi/linux/bpf.h | 37 ++++++++++++++++++++++++++++++++++++- 1 file changed, 36 insertions(+), 1 deletion(-) (limited to 'tools') diff --git a/tools/include/uapi/linux/bpf.h b/tools/include/uapi/linux/bpf.h index dd5758dc35d3..3102a2a23c31 100644 --- a/tools/include/uapi/linux/bpf.h +++ b/tools/include/uapi/linux/bpf.h @@ -126,6 +126,7 @@ enum bpf_map_type { BPF_MAP_TYPE_XSKMAP, BPF_MAP_TYPE_SOCKHASH, BPF_MAP_TYPE_CGROUP_STORAGE, + BPF_MAP_TYPE_REUSEPORT_SOCKARRAY, }; enum bpf_prog_type { @@ -150,6 +151,7 @@ enum bpf_prog_type { BPF_PROG_TYPE_CGROUP_SOCK_ADDR, BPF_PROG_TYPE_LWT_SEG6LOCAL, BPF_PROG_TYPE_LIRC_MODE2, + BPF_PROG_TYPE_SK_REUSEPORT, }; enum bpf_attach_type { @@ -2113,6 +2115,14 @@ union bpf_attr { * the shared data. * Return * Pointer to the local storage area. + * + * int bpf_sk_select_reuseport(struct sk_reuseport_md *reuse, struct bpf_map *map, void *key, u64 flags) + * Description + * Select a SO_REUSEPORT sk from a BPF_MAP_TYPE_REUSEPORT_ARRAY map + * It checks the selected sk is matching the incoming + * request in the skb. + * Return + * 0 on success, or a negative error in case of failure. */ #define __BPF_FUNC_MAPPER(FN) \ FN(unspec), \ @@ -2196,7 +2206,8 @@ union bpf_attr { FN(rc_keydown), \ FN(skb_cgroup_id), \ FN(get_current_cgroup_id), \ - FN(get_local_storage), + FN(get_local_storage), \ + FN(sk_select_reuseport), /* integer value in 'imm' field of BPF_CALL instruction selects which helper * function eBPF program intends to call @@ -2413,6 +2424,30 @@ struct sk_msg_md { __u32 local_port; /* stored in host byte order */ }; +struct sk_reuseport_md { + /* + * Start of directly accessible data. It begins from + * the tcp/udp header. + */ + void *data; + void *data_end; /* End of directly accessible data */ + /* + * Total length of packet (starting from the tcp/udp header). + * Note that the directly accessible bytes (data_end - data) + * could be less than this "len". Those bytes could be + * indirectly read by a helper "bpf_skb_load_bytes()". + */ + __u32 len; + /* + * Eth protocol in the mac header (network byte order). e.g. + * ETH_P_IP(0x0800) and ETH_P_IPV6(0x86DD) + */ + __u32 eth_protocol; + __u32 ip_protocol; /* IP protocol. e.g. IPPROTO_TCP, IPPROTO_UDP */ + __u32 bind_inany; /* Is sock bound to an INANY address? */ + __u32 hash; /* A hash of the packet 4 tuples */ +}; + #define BPF_TAG_SIZE 8 struct bpf_prog_info { -- cgit v1.2.3-58-ga151 From 6bc8529c414f931ce0acef3099b015cf2f5c4291 Mon Sep 17 00:00:00 2001 From: Martin KaFai Lau <kafai@fb.com> Date: Wed, 8 Aug 2018 01:01:30 -0700 Subject: bpf: test BPF_MAP_TYPE_REUSEPORT_SOCKARRAY This patch adds tests for the new BPF_MAP_TYPE_REUSEPORT_SOCKARRAY. Signed-off-by: Martin KaFai Lau <kafai@fb.com> Acked-by: Alexei Starovoitov <ast@kernel.org> Signed-off-by: Daniel Borkmann <daniel@iogearbox.net> --- tools/lib/bpf/libbpf.c | 1 + tools/testing/selftests/bpf/test_maps.c | 262 +++++++++++++++++++++++++++++++- 2 files changed, 262 insertions(+), 1 deletion(-) (limited to 'tools') diff --git a/tools/lib/bpf/libbpf.c b/tools/lib/bpf/libbpf.c index 40211b51427a..2abd0f112627 100644 --- a/tools/lib/bpf/libbpf.c +++ b/tools/lib/bpf/libbpf.c @@ -1501,6 +1501,7 @@ static bool bpf_prog_type__needs_kver(enum bpf_prog_type type) case BPF_PROG_TYPE_SK_MSG: case BPF_PROG_TYPE_CGROUP_SOCK_ADDR: case BPF_PROG_TYPE_LIRC_MODE2: + case BPF_PROG_TYPE_SK_REUSEPORT: return false; case BPF_PROG_TYPE_UNSPEC: case BPF_PROG_TYPE_KPROBE: diff --git a/tools/testing/selftests/bpf/test_maps.c b/tools/testing/selftests/bpf/test_maps.c index 6c253343a6f9..4b7c74f5faa7 100644 --- a/tools/testing/selftests/bpf/test_maps.c +++ b/tools/testing/selftests/bpf/test_maps.c @@ -17,7 +17,8 @@ #include <stdlib.h> #include <sys/wait.h> - +#include <sys/socket.h> +#include <netinet/in.h> #include <linux/bpf.h> #include <bpf/bpf.h> @@ -26,8 +27,21 @@ #include "bpf_util.h" #include "bpf_rlimit.h" +#ifndef ENOTSUPP +#define ENOTSUPP 524 +#endif + static int map_flags; +#define CHECK(condition, tag, format...) ({ \ + int __ret = !!(condition); \ + if (__ret) { \ + printf("%s(%d):FAIL:%s ", __func__, __LINE__, tag); \ + printf(format); \ + exit(-1); \ + } \ +}) + static void test_hashmap(int task, void *data) { long long key, next_key, first_key, value; @@ -1150,6 +1164,250 @@ static void test_map_wronly(void) assert(bpf_map_get_next_key(fd, &key, &value) == -1 && errno == EPERM); } +static void prepare_reuseport_grp(int type, int map_fd, + __s64 *fds64, __u64 *sk_cookies, + unsigned int n) +{ + socklen_t optlen, addrlen; + struct sockaddr_in6 s6; + const __u32 index0 = 0; + const int optval = 1; + unsigned int i; + u64 sk_cookie; + __s64 fd64; + int err; + + s6.sin6_family = AF_INET6; + s6.sin6_addr = in6addr_any; + s6.sin6_port = 0; + addrlen = sizeof(s6); + optlen = sizeof(sk_cookie); + + for (i = 0; i < n; i++) { + fd64 = socket(AF_INET6, type, 0); + CHECK(fd64 == -1, "socket()", + "sock_type:%d fd64:%lld errno:%d\n", + type, fd64, errno); + + err = setsockopt(fd64, SOL_SOCKET, SO_REUSEPORT, + &optval, sizeof(optval)); + CHECK(err == -1, "setsockopt(SO_REUSEEPORT)", + "err:%d errno:%d\n", err, errno); + + /* reuseport_array does not allow unbound sk */ + err = bpf_map_update_elem(map_fd, &index0, &fd64, + BPF_ANY); + CHECK(err != -1 || errno != EINVAL, + "reuseport array update unbound sk", + "sock_type:%d err:%d errno:%d\n", + type, err, errno); + + err = bind(fd64, (struct sockaddr *)&s6, sizeof(s6)); + CHECK(err == -1, "bind()", + "sock_type:%d err:%d errno:%d\n", type, err, errno); + + if (i == 0) { + err = getsockname(fd64, (struct sockaddr *)&s6, + &addrlen); + CHECK(err == -1, "getsockname()", + "sock_type:%d err:%d errno:%d\n", + type, err, errno); + } + + err = getsockopt(fd64, SOL_SOCKET, SO_COOKIE, &sk_cookie, + &optlen); + CHECK(err == -1, "getsockopt(SO_COOKIE)", + "sock_type:%d err:%d errno:%d\n", type, err, errno); + + if (type == SOCK_STREAM) { + /* + * reuseport_array does not allow + * non-listening tcp sk. + */ + err = bpf_map_update_elem(map_fd, &index0, &fd64, + BPF_ANY); + CHECK(err != -1 || errno != EINVAL, + "reuseport array update non-listening sk", + "sock_type:%d err:%d errno:%d\n", + type, err, errno); + err = listen(fd64, 0); + CHECK(err == -1, "listen()", + "sock_type:%d, err:%d errno:%d\n", + type, err, errno); + } + + fds64[i] = fd64; + sk_cookies[i] = sk_cookie; + } +} + +static void test_reuseport_array(void) +{ +#define REUSEPORT_FD_IDX(err, last) ({ (err) ? last : !last; }) + + const __u32 array_size = 4, index0 = 0, index3 = 3; + int types[2] = { SOCK_STREAM, SOCK_DGRAM }, type; + __u64 grpa_cookies[2], sk_cookie, map_cookie; + __s64 grpa_fds64[2] = { -1, -1 }, fd64 = -1; + const __u32 bad_index = array_size; + int map_fd, err, t, f; + __u32 fds_idx = 0; + int fd; + + map_fd = bpf_create_map(BPF_MAP_TYPE_REUSEPORT_SOCKARRAY, + sizeof(__u32), sizeof(__u64), array_size, 0); + CHECK(map_fd == -1, "reuseport array create", + "map_fd:%d, errno:%d\n", map_fd, errno); + + /* Test lookup/update/delete with invalid index */ + err = bpf_map_delete_elem(map_fd, &bad_index); + CHECK(err != -1 || errno != E2BIG, "reuseport array del >=max_entries", + "err:%d errno:%d\n", err, errno); + + err = bpf_map_update_elem(map_fd, &bad_index, &fd64, BPF_ANY); + CHECK(err != -1 || errno != E2BIG, + "reuseport array update >=max_entries", + "err:%d errno:%d\n", err, errno); + + err = bpf_map_lookup_elem(map_fd, &bad_index, &map_cookie); + CHECK(err != -1 || errno != ENOENT, + "reuseport array update >=max_entries", + "err:%d errno:%d\n", err, errno); + + /* Test lookup/delete non existence elem */ + err = bpf_map_lookup_elem(map_fd, &index3, &map_cookie); + CHECK(err != -1 || errno != ENOENT, + "reuseport array lookup not-exist elem", + "err:%d errno:%d\n", err, errno); + err = bpf_map_delete_elem(map_fd, &index3); + CHECK(err != -1 || errno != ENOENT, + "reuseport array del not-exist elem", + "err:%d errno:%d\n", err, errno); + + for (t = 0; t < ARRAY_SIZE(types); t++) { + type = types[t]; + + prepare_reuseport_grp(type, map_fd, grpa_fds64, + grpa_cookies, ARRAY_SIZE(grpa_fds64)); + + /* Test BPF_* update flags */ + /* BPF_EXIST failure case */ + err = bpf_map_update_elem(map_fd, &index3, &grpa_fds64[fds_idx], + BPF_EXIST); + CHECK(err != -1 || errno != ENOENT, + "reuseport array update empty elem BPF_EXIST", + "sock_type:%d err:%d errno:%d\n", + type, err, errno); + fds_idx = REUSEPORT_FD_IDX(err, fds_idx); + + /* BPF_NOEXIST success case */ + err = bpf_map_update_elem(map_fd, &index3, &grpa_fds64[fds_idx], + BPF_NOEXIST); + CHECK(err == -1, + "reuseport array update empty elem BPF_NOEXIST", + "sock_type:%d err:%d errno:%d\n", + type, err, errno); + fds_idx = REUSEPORT_FD_IDX(err, fds_idx); + + /* BPF_EXIST success case. */ + err = bpf_map_update_elem(map_fd, &index3, &grpa_fds64[fds_idx], + BPF_EXIST); + CHECK(err == -1, + "reuseport array update same elem BPF_EXIST", + "sock_type:%d err:%d errno:%d\n", type, err, errno); + fds_idx = REUSEPORT_FD_IDX(err, fds_idx); + + /* BPF_NOEXIST failure case */ + err = bpf_map_update_elem(map_fd, &index3, &grpa_fds64[fds_idx], + BPF_NOEXIST); + CHECK(err != -1 || errno != EEXIST, + "reuseport array update non-empty elem BPF_NOEXIST", + "sock_type:%d err:%d errno:%d\n", + type, err, errno); + fds_idx = REUSEPORT_FD_IDX(err, fds_idx); + + /* BPF_ANY case (always succeed) */ + err = bpf_map_update_elem(map_fd, &index3, &grpa_fds64[fds_idx], + BPF_ANY); + CHECK(err == -1, + "reuseport array update same sk with BPF_ANY", + "sock_type:%d err:%d errno:%d\n", type, err, errno); + + fd64 = grpa_fds64[fds_idx]; + sk_cookie = grpa_cookies[fds_idx]; + + /* The same sk cannot be added to reuseport_array twice */ + err = bpf_map_update_elem(map_fd, &index3, &fd64, BPF_ANY); + CHECK(err != -1 || errno != EBUSY, + "reuseport array update same sk with same index", + "sock_type:%d err:%d errno:%d\n", + type, err, errno); + + err = bpf_map_update_elem(map_fd, &index0, &fd64, BPF_ANY); + CHECK(err != -1 || errno != EBUSY, + "reuseport array update same sk with different index", + "sock_type:%d err:%d errno:%d\n", + type, err, errno); + + /* Test delete elem */ + err = bpf_map_delete_elem(map_fd, &index3); + CHECK(err == -1, "reuseport array delete sk", + "sock_type:%d err:%d errno:%d\n", + type, err, errno); + + /* Add it back with BPF_NOEXIST */ + err = bpf_map_update_elem(map_fd, &index3, &fd64, BPF_NOEXIST); + CHECK(err == -1, + "reuseport array re-add with BPF_NOEXIST after del", + "sock_type:%d err:%d errno:%d\n", type, err, errno); + + /* Test cookie */ + err = bpf_map_lookup_elem(map_fd, &index3, &map_cookie); + CHECK(err == -1 || sk_cookie != map_cookie, + "reuseport array lookup re-added sk", + "sock_type:%d err:%d errno:%d sk_cookie:0x%llx map_cookie:0x%llxn", + type, err, errno, sk_cookie, map_cookie); + + /* Test elem removed by close() */ + for (f = 0; f < ARRAY_SIZE(grpa_fds64); f++) + close(grpa_fds64[f]); + err = bpf_map_lookup_elem(map_fd, &index3, &map_cookie); + CHECK(err != -1 || errno != ENOENT, + "reuseport array lookup after close()", + "sock_type:%d err:%d errno:%d\n", + type, err, errno); + } + + /* Test SOCK_RAW */ + fd64 = socket(AF_INET6, SOCK_RAW, IPPROTO_UDP); + CHECK(fd64 == -1, "socket(SOCK_RAW)", "err:%d errno:%d\n", + err, errno); + err = bpf_map_update_elem(map_fd, &index3, &fd64, BPF_NOEXIST); + CHECK(err != -1 || errno != ENOTSUPP, "reuseport array update SOCK_RAW", + "err:%d errno:%d\n", err, errno); + close(fd64); + + /* Close the 64 bit value map */ + close(map_fd); + + /* Test 32 bit fd */ + map_fd = bpf_create_map(BPF_MAP_TYPE_REUSEPORT_SOCKARRAY, + sizeof(__u32), sizeof(__u32), array_size, 0); + CHECK(map_fd == -1, "reuseport array create", + "map_fd:%d, errno:%d\n", map_fd, errno); + prepare_reuseport_grp(SOCK_STREAM, map_fd, &fd64, &sk_cookie, 1); + fd = fd64; + err = bpf_map_update_elem(map_fd, &index3, &fd, BPF_NOEXIST); + CHECK(err == -1, "reuseport array update 32 bit fd", + "err:%d errno:%d\n", err, errno); + err = bpf_map_lookup_elem(map_fd, &index3, &map_cookie); + CHECK(err != -1 || errno != ENOSPC, + "reuseport array lookup 32 bit fd", + "err:%d errno:%d\n", err, errno); + close(fd); + close(map_fd); +} + static void run_all_tests(void) { test_hashmap(0, NULL); @@ -1170,6 +1428,8 @@ static void run_all_tests(void) test_map_rdonly(); test_map_wronly(); + + test_reuseport_array(); } int main(void) -- cgit v1.2.3-58-ga151 From 91134d849a0e8fbc70b8607d280e0d325dcaf7bb Mon Sep 17 00:00:00 2001 From: Martin KaFai Lau <kafai@fb.com> Date: Wed, 8 Aug 2018 01:01:31 -0700 Subject: bpf: Test BPF_PROG_TYPE_SK_REUSEPORT This patch add tests for the new BPF_PROG_TYPE_SK_REUSEPORT. The tests cover: - IPv4/IPv6 + TCP/UDP - TCP syncookie - TCP fastopen - Cases when the bpf_sk_select_reuseport() returning errors - Cases when the bpf prog returns SK_DROP - Values from sk_reuseport_md - outer_map => reuseport_array The test depends on commit 3eee1f75f2b9 ("bpf: fix bpf_skb_load_bytes_relative pkt length check") Signed-off-by: Martin KaFai Lau <kafai@fb.com> Acked-by: Alexei Starovoitov <ast@kernel.org> Signed-off-by: Daniel Borkmann <daniel@iogearbox.net> --- tools/lib/bpf/bpf.c | 1 + tools/lib/bpf/bpf.h | 1 + tools/testing/selftests/bpf/Makefile | 4 +- tools/testing/selftests/bpf/bpf_helpers.h | 4 + .../testing/selftests/bpf/test_select_reuseport.c | 688 +++++++++++++++++++++ .../selftests/bpf/test_select_reuseport_common.h | 36 ++ .../selftests/bpf/test_select_reuseport_kern.c | 180 ++++++ 7 files changed, 912 insertions(+), 2 deletions(-) create mode 100644 tools/testing/selftests/bpf/test_select_reuseport.c create mode 100644 tools/testing/selftests/bpf/test_select_reuseport_common.h create mode 100644 tools/testing/selftests/bpf/test_select_reuseport_kern.c (limited to 'tools') diff --git a/tools/lib/bpf/bpf.c b/tools/lib/bpf/bpf.c index 9ddc89dae962..60aa4ca8b2c5 100644 --- a/tools/lib/bpf/bpf.c +++ b/tools/lib/bpf/bpf.c @@ -92,6 +92,7 @@ int bpf_create_map_xattr(const struct bpf_create_map_attr *create_attr) attr.btf_key_type_id = create_attr->btf_key_type_id; attr.btf_value_type_id = create_attr->btf_value_type_id; attr.map_ifindex = create_attr->map_ifindex; + attr.inner_map_fd = create_attr->inner_map_fd; return sys_bpf(BPF_MAP_CREATE, &attr, sizeof(attr)); } diff --git a/tools/lib/bpf/bpf.h b/tools/lib/bpf/bpf.h index 0639a30a457d..6f38164b2618 100644 --- a/tools/lib/bpf/bpf.h +++ b/tools/lib/bpf/bpf.h @@ -39,6 +39,7 @@ struct bpf_create_map_attr { __u32 btf_key_type_id; __u32 btf_value_type_id; __u32 map_ifindex; + __u32 inner_map_fd; }; int bpf_create_map_xattr(const struct bpf_create_map_attr *create_attr); diff --git a/tools/testing/selftests/bpf/Makefile b/tools/testing/selftests/bpf/Makefile index 17a7a5818ee1..daed162043c2 100644 --- a/tools/testing/selftests/bpf/Makefile +++ b/tools/testing/selftests/bpf/Makefile @@ -23,7 +23,7 @@ $(TEST_CUSTOM_PROGS): $(OUTPUT)/%: %.c TEST_GEN_PROGS = test_verifier test_tag test_maps test_lru_map test_lpm_map test_progs \ test_align test_verifier_log test_dev_cgroup test_tcpbpf_user \ test_sock test_btf test_sockmap test_lirc_mode2_user get_cgroup_id_user \ - test_socket_cookie test_cgroup_storage + test_socket_cookie test_cgroup_storage test_select_reuseport TEST_GEN_FILES = test_pkt_access.o test_xdp.o test_l4lb.o test_tcp_estats.o test_obj_id.o \ test_pkt_md_access.o test_xdp_redirect.o test_xdp_meta.o sockmap_parse_prog.o \ @@ -34,7 +34,7 @@ TEST_GEN_FILES = test_pkt_access.o test_xdp.o test_l4lb.o test_tcp_estats.o test test_btf_haskv.o test_btf_nokv.o test_sockmap_kern.o test_tunnel_kern.o \ test_get_stack_rawtp.o test_sockmap_kern.o test_sockhash_kern.o \ test_lwt_seg6local.o sendmsg4_prog.o sendmsg6_prog.o test_lirc_mode2_kern.o \ - get_cgroup_id_kern.o socket_cookie_prog.o + get_cgroup_id_kern.o socket_cookie_prog.o test_select_reuseport_kern.o # Order correspond to 'make run_tests' order TEST_PROGS := test_kmod.sh \ diff --git a/tools/testing/selftests/bpf/bpf_helpers.h b/tools/testing/selftests/bpf/bpf_helpers.h index 9ba1c72d7cf5..5c32266c2c38 100644 --- a/tools/testing/selftests/bpf/bpf_helpers.h +++ b/tools/testing/selftests/bpf/bpf_helpers.h @@ -111,6 +111,8 @@ static int (*bpf_xdp_adjust_tail)(void *ctx, int offset) = static int (*bpf_skb_get_xfrm_state)(void *ctx, int index, void *state, int size, int flags) = (void *) BPF_FUNC_skb_get_xfrm_state; +static int (*bpf_sk_select_reuseport)(void *ctx, void *map, void *key, __u32 flags) = + (void *) BPF_FUNC_sk_select_reuseport; static int (*bpf_get_stack)(void *ctx, void *buf, int size, int flags) = (void *) BPF_FUNC_get_stack; static int (*bpf_fib_lookup)(void *ctx, struct bpf_fib_lookup *params, @@ -173,6 +175,8 @@ struct bpf_map_def { static int (*bpf_skb_load_bytes)(void *ctx, int off, void *to, int len) = (void *) BPF_FUNC_skb_load_bytes; +static int (*bpf_skb_load_bytes_relative)(void *ctx, int off, void *to, int len, __u32 start_header) = + (void *) BPF_FUNC_skb_load_bytes_relative; static int (*bpf_skb_store_bytes)(void *ctx, int off, void *from, int len, int flags) = (void *) BPF_FUNC_skb_store_bytes; static int (*bpf_l3_csum_replace)(void *ctx, int off, int from, int to, int flags) = diff --git a/tools/testing/selftests/bpf/test_select_reuseport.c b/tools/testing/selftests/bpf/test_select_reuseport.c new file mode 100644 index 000000000000..75646d9b34aa --- /dev/null +++ b/tools/testing/selftests/bpf/test_select_reuseport.c @@ -0,0 +1,688 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Copyright (c) 2018 Facebook */ + +#include <stdlib.h> +#include <unistd.h> +#include <stdbool.h> +#include <string.h> +#include <errno.h> +#include <assert.h> +#include <fcntl.h> +#include <linux/bpf.h> +#include <linux/err.h> +#include <linux/types.h> +#include <linux/if_ether.h> +#include <sys/types.h> +#include <sys/epoll.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <bpf/bpf.h> +#include <bpf/libbpf.h> +#include "bpf_rlimit.h" +#include "bpf_util.h" +#include "test_select_reuseport_common.h" + +#define MIN_TCPHDR_LEN 20 +#define UDPHDR_LEN 8 + +#define TCP_SYNCOOKIE_SYSCTL "/proc/sys/net/ipv4/tcp_syncookies" +#define TCP_FO_SYSCTL "/proc/sys/net/ipv4/tcp_fastopen" +#define REUSEPORT_ARRAY_SIZE 32 + +static int result_map, tmp_index_ovr_map, linum_map, data_check_map; +static enum result expected_results[NR_RESULTS]; +static int sk_fds[REUSEPORT_ARRAY_SIZE]; +static int reuseport_array, outer_map; +static int select_by_skb_data_prog; +static int saved_tcp_syncookie; +static struct bpf_object *obj; +static int saved_tcp_fo; +static __u32 index_zero; +static int epfd; + +static union sa46 { + struct sockaddr_in6 v6; + struct sockaddr_in v4; + sa_family_t family; +} srv_sa; + +#define CHECK(condition, tag, format...) ({ \ + int __ret = !!(condition); \ + if (__ret) { \ + printf("%s(%d):FAIL:%s ", __func__, __LINE__, tag); \ + printf(format); \ + exit(-1); \ + } \ +}) + +static void create_maps(void) +{ + struct bpf_create_map_attr attr = {}; + + /* Creating reuseport_array */ + attr.name = "reuseport_array"; + attr.map_type = BPF_MAP_TYPE_REUSEPORT_SOCKARRAY; + attr.key_size = sizeof(__u32); + attr.value_size = sizeof(__u32); + attr.max_entries = REUSEPORT_ARRAY_SIZE; + + reuseport_array = bpf_create_map_xattr(&attr); + CHECK(reuseport_array == -1, "creating reuseport_array", + "reuseport_array:%d errno:%d\n", reuseport_array, errno); + + /* Creating outer_map */ + attr.name = "outer_map"; + attr.map_type = BPF_MAP_TYPE_ARRAY_OF_MAPS; + attr.key_size = sizeof(__u32); + attr.value_size = sizeof(__u32); + attr.max_entries = 1; + attr.inner_map_fd = reuseport_array; + outer_map = bpf_create_map_xattr(&attr); + CHECK(outer_map == -1, "creating outer_map", + "outer_map:%d errno:%d\n", outer_map, errno); +} + +static void prepare_bpf_obj(void) +{ + struct bpf_program *prog; + struct bpf_map *map; + int err; + struct bpf_object_open_attr attr = { + .file = "test_select_reuseport_kern.o", + .prog_type = BPF_PROG_TYPE_SK_REUSEPORT, + }; + + obj = bpf_object__open_xattr(&attr); + CHECK(IS_ERR_OR_NULL(obj), "open test_select_reuseport_kern.o", + "obj:%p PTR_ERR(obj):%ld\n", obj, PTR_ERR(obj)); + + prog = bpf_program__next(NULL, obj); + CHECK(!prog, "get first bpf_program", "!prog\n"); + bpf_program__set_type(prog, attr.prog_type); + + map = bpf_object__find_map_by_name(obj, "outer_map"); + CHECK(!map, "find outer_map", "!map\n"); + err = bpf_map__reuse_fd(map, outer_map); + CHECK(err, "reuse outer_map", "err:%d\n", err); + + err = bpf_object__load(obj); + CHECK(err, "load bpf_object", "err:%d\n", err); + + select_by_skb_data_prog = bpf_program__fd(prog); + CHECK(select_by_skb_data_prog == -1, "get prog fd", + "select_by_skb_data_prog:%d\n", select_by_skb_data_prog); + + map = bpf_object__find_map_by_name(obj, "result_map"); + CHECK(!map, "find result_map", "!map\n"); + result_map = bpf_map__fd(map); + CHECK(result_map == -1, "get result_map fd", + "result_map:%d\n", result_map); + + map = bpf_object__find_map_by_name(obj, "tmp_index_ovr_map"); + CHECK(!map, "find tmp_index_ovr_map", "!map\n"); + tmp_index_ovr_map = bpf_map__fd(map); + CHECK(tmp_index_ovr_map == -1, "get tmp_index_ovr_map fd", + "tmp_index_ovr_map:%d\n", tmp_index_ovr_map); + + map = bpf_object__find_map_by_name(obj, "linum_map"); + CHECK(!map, "find linum_map", "!map\n"); + linum_map = bpf_map__fd(map); + CHECK(linum_map == -1, "get linum_map fd", + "linum_map:%d\n", linum_map); + + map = bpf_object__find_map_by_name(obj, "data_check_map"); + CHECK(!map, "find data_check_map", "!map\n"); + data_check_map = bpf_map__fd(map); + CHECK(data_check_map == -1, "get data_check_map fd", + "data_check_map:%d\n", data_check_map); +} + +static void sa46_init_loopback(union sa46 *sa, sa_family_t family) +{ + memset(sa, 0, sizeof(*sa)); + sa->family = family; + if (sa->family == AF_INET6) + sa->v6.sin6_addr = in6addr_loopback; + else + sa->v4.sin_addr.s_addr = htonl(INADDR_LOOPBACK); +} + +static void sa46_init_inany(union sa46 *sa, sa_family_t family) +{ + memset(sa, 0, sizeof(*sa)); + sa->family = family; + if (sa->family == AF_INET6) + sa->v6.sin6_addr = in6addr_any; + else + sa->v4.sin_addr.s_addr = INADDR_ANY; +} + +static int read_int_sysctl(const char *sysctl) +{ + char buf[16]; + int fd, ret; + + fd = open(sysctl, 0); + CHECK(fd == -1, "open(sysctl)", "sysctl:%s fd:%d errno:%d\n", + sysctl, fd, errno); + + ret = read(fd, buf, sizeof(buf)); + CHECK(ret <= 0, "read(sysctl)", "sysctl:%s ret:%d errno:%d\n", + sysctl, ret, errno); + close(fd); + + return atoi(buf); +} + +static void write_int_sysctl(const char *sysctl, int v) +{ + int fd, ret, size; + char buf[16]; + + fd = open(sysctl, O_RDWR); + CHECK(fd == -1, "open(sysctl)", "sysctl:%s fd:%d errno:%d\n", + sysctl, fd, errno); + + size = snprintf(buf, sizeof(buf), "%d", v); + ret = write(fd, buf, size); + CHECK(ret != size, "write(sysctl)", + "sysctl:%s ret:%d size:%d errno:%d\n", sysctl, ret, size, errno); + close(fd); +} + +static void restore_sysctls(void) +{ + write_int_sysctl(TCP_FO_SYSCTL, saved_tcp_fo); + write_int_sysctl(TCP_SYNCOOKIE_SYSCTL, saved_tcp_syncookie); +} + +static void enable_fastopen(void) +{ + int fo; + + fo = read_int_sysctl(TCP_FO_SYSCTL); + write_int_sysctl(TCP_FO_SYSCTL, fo | 7); +} + +static void enable_syncookie(void) +{ + write_int_sysctl(TCP_SYNCOOKIE_SYSCTL, 2); +} + +static void disable_syncookie(void) +{ + write_int_sysctl(TCP_SYNCOOKIE_SYSCTL, 0); +} + +static __u32 get_linum(void) +{ + __u32 linum; + int err; + + err = bpf_map_lookup_elem(linum_map, &index_zero, &linum); + CHECK(err == -1, "lookup_elem(linum_map)", "err:%d errno:%d\n", + err, errno); + + return linum; +} + +static void check_data(int type, sa_family_t family, const struct cmd *cmd, + int cli_fd) +{ + struct data_check expected = {}, result; + union sa46 cli_sa; + socklen_t addrlen; + int err; + + addrlen = sizeof(cli_sa); + err = getsockname(cli_fd, (struct sockaddr *)&cli_sa, + &addrlen); + CHECK(err == -1, "getsockname(cli_fd)", "err:%d errno:%d\n", + err, errno); + + err = bpf_map_lookup_elem(data_check_map, &index_zero, &result); + CHECK(err == -1, "lookup_elem(data_check_map)", "err:%d errno:%d\n", + err, errno); + + if (type == SOCK_STREAM) { + expected.len = MIN_TCPHDR_LEN; + expected.ip_protocol = IPPROTO_TCP; + } else { + expected.len = UDPHDR_LEN; + expected.ip_protocol = IPPROTO_UDP; + } + + if (family == AF_INET6) { + expected.eth_protocol = htons(ETH_P_IPV6); + expected.bind_inany = !srv_sa.v6.sin6_addr.s6_addr32[3] && + !srv_sa.v6.sin6_addr.s6_addr32[2] && + !srv_sa.v6.sin6_addr.s6_addr32[1] && + !srv_sa.v6.sin6_addr.s6_addr32[0]; + + memcpy(&expected.skb_addrs[0], cli_sa.v6.sin6_addr.s6_addr32, + sizeof(cli_sa.v6.sin6_addr)); + memcpy(&expected.skb_addrs[4], &in6addr_loopback, + sizeof(in6addr_loopback)); + expected.skb_ports[0] = cli_sa.v6.sin6_port; + expected.skb_ports[1] = srv_sa.v6.sin6_port; + } else { + expected.eth_protocol = htons(ETH_P_IP); + expected.bind_inany = !srv_sa.v4.sin_addr.s_addr; + + expected.skb_addrs[0] = cli_sa.v4.sin_addr.s_addr; + expected.skb_addrs[1] = htonl(INADDR_LOOPBACK); + expected.skb_ports[0] = cli_sa.v4.sin_port; + expected.skb_ports[1] = srv_sa.v4.sin_port; + } + + if (memcmp(&result, &expected, offsetof(struct data_check, + equal_check_end))) { + printf("unexpected data_check\n"); + printf(" result: (0x%x, %u, %u)\n", + result.eth_protocol, result.ip_protocol, + result.bind_inany); + printf("expected: (0x%x, %u, %u)\n", + expected.eth_protocol, expected.ip_protocol, + expected.bind_inany); + CHECK(1, "data_check result != expected", + "bpf_prog_linum:%u\n", get_linum()); + } + + CHECK(!result.hash, "data_check result.hash empty", + "result.hash:%u", result.hash); + + expected.len += cmd ? sizeof(*cmd) : 0; + if (type == SOCK_STREAM) + CHECK(expected.len > result.len, "expected.len > result.len", + "expected.len:%u result.len:%u bpf_prog_linum:%u\n", + expected.len, result.len, get_linum()); + else + CHECK(expected.len != result.len, "expected.len != result.len", + "expected.len:%u result.len:%u bpf_prog_linum:%u\n", + expected.len, result.len, get_linum()); +} + +static void check_results(void) +{ + __u32 results[NR_RESULTS]; + __u32 i, broken = 0; + int err; + + for (i = 0; i < NR_RESULTS; i++) { + err = bpf_map_lookup_elem(result_map, &i, &results[i]); + CHECK(err == -1, "lookup_elem(result_map)", + "i:%u err:%d errno:%d\n", i, err, errno); + } + + for (i = 0; i < NR_RESULTS; i++) { + if (results[i] != expected_results[i]) { + broken = i; + break; + } + } + + if (i == NR_RESULTS) + return; + + printf("unexpected result\n"); + printf(" result: ["); + printf("%u", results[0]); + for (i = 1; i < NR_RESULTS; i++) + printf(", %u", results[i]); + printf("]\n"); + + printf("expected: ["); + printf("%u", expected_results[0]); + for (i = 1; i < NR_RESULTS; i++) + printf(", %u", expected_results[i]); + printf("]\n"); + + CHECK(expected_results[broken] != results[broken], + "unexpected result", + "expected_results[%u] != results[%u] bpf_prog_linum:%u\n", + broken, broken, get_linum()); +} + +static int send_data(int type, sa_family_t family, void *data, size_t len, + enum result expected) +{ + union sa46 cli_sa; + int fd, err; + + fd = socket(family, type, 0); + CHECK(fd == -1, "socket()", "fd:%d errno:%d\n", fd, errno); + + sa46_init_loopback(&cli_sa, family); + err = bind(fd, (struct sockaddr *)&cli_sa, sizeof(cli_sa)); + CHECK(fd == -1, "bind(cli_sa)", "err:%d errno:%d\n", err, errno); + + err = sendto(fd, data, len, MSG_FASTOPEN, (struct sockaddr *)&srv_sa, + sizeof(srv_sa)); + CHECK(err != len && expected >= PASS, + "sendto()", "family:%u err:%d errno:%d expected:%d\n", + family, err, errno, expected); + + return fd; +} + +static void do_test(int type, sa_family_t family, struct cmd *cmd, + enum result expected) +{ + int nev, srv_fd, cli_fd; + struct epoll_event ev; + struct cmd rcv_cmd; + ssize_t nread; + + cli_fd = send_data(type, family, cmd, cmd ? sizeof(*cmd) : 0, + expected); + nev = epoll_wait(epfd, &ev, 1, expected >= PASS ? 5 : 0); + CHECK((nev <= 0 && expected >= PASS) || + (nev > 0 && expected < PASS), + "nev <> expected", + "nev:%d expected:%d type:%d family:%d data:(%d, %d)\n", + nev, expected, type, family, + cmd ? cmd->reuseport_index : -1, + cmd ? cmd->pass_on_failure : -1); + check_results(); + check_data(type, family, cmd, cli_fd); + + if (expected < PASS) + return; + + CHECK(expected != PASS_ERR_SK_SELECT_REUSEPORT && + cmd->reuseport_index != ev.data.u32, + "check cmd->reuseport_index", + "cmd:(%u, %u) ev.data.u32:%u\n", + cmd->pass_on_failure, cmd->reuseport_index, ev.data.u32); + + srv_fd = sk_fds[ev.data.u32]; + if (type == SOCK_STREAM) { + int new_fd = accept(srv_fd, NULL, 0); + + CHECK(new_fd == -1, "accept(srv_fd)", + "ev.data.u32:%u new_fd:%d errno:%d\n", + ev.data.u32, new_fd, errno); + + nread = recv(new_fd, &rcv_cmd, sizeof(rcv_cmd), MSG_DONTWAIT); + CHECK(nread != sizeof(rcv_cmd), + "recv(new_fd)", + "ev.data.u32:%u nread:%zd sizeof(rcv_cmd):%zu errno:%d\n", + ev.data.u32, nread, sizeof(rcv_cmd), errno); + + close(new_fd); + } else { + nread = recv(srv_fd, &rcv_cmd, sizeof(rcv_cmd), MSG_DONTWAIT); + CHECK(nread != sizeof(rcv_cmd), + "recv(sk_fds)", + "ev.data.u32:%u nread:%zd sizeof(rcv_cmd):%zu errno:%d\n", + ev.data.u32, nread, sizeof(rcv_cmd), errno); + } + + close(cli_fd); +} + +static void test_err_inner_map(int type, sa_family_t family) +{ + struct cmd cmd = { + .reuseport_index = 0, + .pass_on_failure = 0, + }; + + printf("%s: ", __func__); + expected_results[DROP_ERR_INNER_MAP]++; + do_test(type, family, &cmd, DROP_ERR_INNER_MAP); + printf("OK\n"); +} + +static void test_err_skb_data(int type, sa_family_t family) +{ + printf("%s: ", __func__); + expected_results[DROP_ERR_SKB_DATA]++; + do_test(type, family, NULL, DROP_ERR_SKB_DATA); + printf("OK\n"); +} + +static void test_err_sk_select_port(int type, sa_family_t family) +{ + struct cmd cmd = { + .reuseport_index = REUSEPORT_ARRAY_SIZE, + .pass_on_failure = 0, + }; + + printf("%s: ", __func__); + expected_results[DROP_ERR_SK_SELECT_REUSEPORT]++; + do_test(type, family, &cmd, DROP_ERR_SK_SELECT_REUSEPORT); + printf("OK\n"); +} + +static void test_pass(int type, sa_family_t family) +{ + struct cmd cmd; + int i; + + printf("%s: ", __func__); + cmd.pass_on_failure = 0; + for (i = 0; i < REUSEPORT_ARRAY_SIZE; i++) { + expected_results[PASS]++; + cmd.reuseport_index = i; + do_test(type, family, &cmd, PASS); + } + printf("OK\n"); +} + +static void test_syncookie(int type, sa_family_t family) +{ + int err, tmp_index = 1; + struct cmd cmd = { + .reuseport_index = 0, + .pass_on_failure = 0, + }; + + if (type != SOCK_STREAM) + return; + + printf("%s: ", __func__); + /* + * +1 for TCP-SYN and + * +1 for the TCP-ACK (ack the syncookie) + */ + expected_results[PASS] += 2; + enable_syncookie(); + /* + * Simulate TCP-SYN and TCP-ACK are handled by two different sk: + * TCP-SYN: select sk_fds[tmp_index = 1] tmp_index is from the + * tmp_index_ovr_map + * TCP-ACK: select sk_fds[reuseport_index = 0] reuseport_index + * is from the cmd.reuseport_index + */ + err = bpf_map_update_elem(tmp_index_ovr_map, &index_zero, + &tmp_index, BPF_ANY); + CHECK(err == -1, "update_elem(tmp_index_ovr_map, 0, 1)", + "err:%d errno:%d\n", err, errno); + do_test(type, family, &cmd, PASS); + err = bpf_map_lookup_elem(tmp_index_ovr_map, &index_zero, + &tmp_index); + CHECK(err == -1 || tmp_index != -1, + "lookup_elem(tmp_index_ovr_map)", + "err:%d errno:%d tmp_index:%d\n", + err, errno, tmp_index); + disable_syncookie(); + printf("OK\n"); +} + +static void test_pass_on_err(int type, sa_family_t family) +{ + struct cmd cmd = { + .reuseport_index = REUSEPORT_ARRAY_SIZE, + .pass_on_failure = 1, + }; + + printf("%s: ", __func__); + expected_results[PASS_ERR_SK_SELECT_REUSEPORT] += 1; + do_test(type, family, &cmd, PASS_ERR_SK_SELECT_REUSEPORT); + printf("OK\n"); +} + +static void prepare_sk_fds(int type, sa_family_t family, bool inany) +{ + const int first = REUSEPORT_ARRAY_SIZE - 1; + int i, err, optval = 1; + struct epoll_event ev; + socklen_t addrlen; + + if (inany) + sa46_init_inany(&srv_sa, family); + else + sa46_init_loopback(&srv_sa, family); + addrlen = sizeof(srv_sa); + + /* + * The sk_fds[] is filled from the back such that the order + * is exactly opposite to the (struct sock_reuseport *)reuse->socks[]. + */ + for (i = first; i >= 0; i--) { + sk_fds[i] = socket(family, type, 0); + CHECK(sk_fds[i] == -1, "socket()", "sk_fds[%d]:%d errno:%d\n", + i, sk_fds[i], errno); + err = setsockopt(sk_fds[i], SOL_SOCKET, SO_REUSEPORT, + &optval, sizeof(optval)); + CHECK(err == -1, "setsockopt(SO_REUSEPORT)", + "sk_fds[%d] err:%d errno:%d\n", + i, err, errno); + + if (i == first) { + err = setsockopt(sk_fds[i], SOL_SOCKET, + SO_ATTACH_REUSEPORT_EBPF, + &select_by_skb_data_prog, + sizeof(select_by_skb_data_prog)); + CHECK(err == -1, "setsockopt(SO_ATTACH_REUEPORT_EBPF)", + "err:%d errno:%d\n", err, errno); + } + + err = bind(sk_fds[i], (struct sockaddr *)&srv_sa, addrlen); + CHECK(err == -1, "bind()", "sk_fds[%d] err:%d errno:%d\n", + i, err, errno); + + if (type == SOCK_STREAM) { + err = listen(sk_fds[i], 10); + CHECK(err == -1, "listen()", + "sk_fds[%d] err:%d errno:%d\n", + i, err, errno); + } + + err = bpf_map_update_elem(reuseport_array, &i, &sk_fds[i], + BPF_NOEXIST); + CHECK(err == -1, "update_elem(reuseport_array)", + "sk_fds[%d] err:%d errno:%d\n", i, err, errno); + + if (i == first) { + socklen_t addrlen = sizeof(srv_sa); + + err = getsockname(sk_fds[i], (struct sockaddr *)&srv_sa, + &addrlen); + CHECK(err == -1, "getsockname()", + "sk_fds[%d] err:%d errno:%d\n", i, err, errno); + } + } + + epfd = epoll_create(1); + CHECK(epfd == -1, "epoll_create(1)", + "epfd:%d errno:%d\n", epfd, errno); + + ev.events = EPOLLIN; + for (i = 0; i < REUSEPORT_ARRAY_SIZE; i++) { + ev.data.u32 = i; + err = epoll_ctl(epfd, EPOLL_CTL_ADD, sk_fds[i], &ev); + CHECK(err, "epoll_ctl(EPOLL_CTL_ADD)", "sk_fds[%d]\n", i); + } +} + +static void setup_per_test(int type, unsigned short family, bool inany) +{ + int ovr = -1, err; + + prepare_sk_fds(type, family, inany); + err = bpf_map_update_elem(tmp_index_ovr_map, &index_zero, &ovr, + BPF_ANY); + CHECK(err == -1, "update_elem(tmp_index_ovr_map, 0, -1)", + "err:%d errno:%d\n", err, errno); +} + +static void cleanup_per_test(void) +{ + int i, err; + + for (i = 0; i < REUSEPORT_ARRAY_SIZE; i++) + close(sk_fds[i]); + close(epfd); + + err = bpf_map_delete_elem(outer_map, &index_zero); + CHECK(err == -1, "delete_elem(outer_map)", + "err:%d errno:%d\n", err, errno); +} + +static void cleanup(void) +{ + close(outer_map); + close(reuseport_array); + bpf_object__close(obj); +} + +static void test_all(void) +{ + /* Extra SOCK_STREAM to test bind_inany==true */ + const int types[] = { SOCK_STREAM, SOCK_DGRAM, SOCK_STREAM }; + const char * const type_strings[] = { "TCP", "UDP", "TCP" }; + const char * const family_strings[] = { "IPv6", "IPv4" }; + const unsigned short families[] = { AF_INET6, AF_INET }; + const bool bind_inany[] = { false, false, true }; + int t, f, err; + + for (f = 0; f < ARRAY_SIZE(families); f++) { + unsigned short family = families[f]; + + for (t = 0; t < ARRAY_SIZE(types); t++) { + bool inany = bind_inany[t]; + int type = types[t]; + + printf("######## %s/%s %s ########\n", + family_strings[f], type_strings[t], + inany ? " INANY " : "LOOPBACK"); + + setup_per_test(type, family, inany); + + test_err_inner_map(type, family); + + /* Install reuseport_array to the outer_map */ + err = bpf_map_update_elem(outer_map, &index_zero, + &reuseport_array, BPF_ANY); + CHECK(err == -1, "update_elem(outer_map)", + "err:%d errno:%d\n", err, errno); + + test_err_skb_data(type, family); + test_err_sk_select_port(type, family); + test_pass(type, family); + test_syncookie(type, family); + test_pass_on_err(type, family); + + cleanup_per_test(); + printf("\n"); + } + } +} + +int main(int argc, const char **argv) +{ + create_maps(); + prepare_bpf_obj(); + saved_tcp_fo = read_int_sysctl(TCP_FO_SYSCTL); + saved_tcp_syncookie = read_int_sysctl(TCP_SYNCOOKIE_SYSCTL); + enable_fastopen(); + disable_syncookie(); + atexit(restore_sysctls); + + test_all(); + + cleanup(); + return 0; +} diff --git a/tools/testing/selftests/bpf/test_select_reuseport_common.h b/tools/testing/selftests/bpf/test_select_reuseport_common.h new file mode 100644 index 000000000000..08eb2a9f145f --- /dev/null +++ b/tools/testing/selftests/bpf/test_select_reuseport_common.h @@ -0,0 +1,36 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Copyright (c) 2018 Facebook */ + +#ifndef __TEST_SELECT_REUSEPORT_COMMON_H +#define __TEST_SELECT_REUSEPORT_COMMON_H + +#include <linux/types.h> + +enum result { + DROP_ERR_INNER_MAP, + DROP_ERR_SKB_DATA, + DROP_ERR_SK_SELECT_REUSEPORT, + DROP_MISC, + PASS, + PASS_ERR_SK_SELECT_REUSEPORT, + NR_RESULTS, +}; + +struct cmd { + __u32 reuseport_index; + __u32 pass_on_failure; +}; + +struct data_check { + __u32 ip_protocol; + __u32 skb_addrs[8]; + __u16 skb_ports[2]; + __u16 eth_protocol; + __u8 bind_inany; + __u8 equal_check_end[0]; + + __u32 len; + __u32 hash; +}; + +#endif diff --git a/tools/testing/selftests/bpf/test_select_reuseport_kern.c b/tools/testing/selftests/bpf/test_select_reuseport_kern.c new file mode 100644 index 000000000000..5b54ec637ada --- /dev/null +++ b/tools/testing/selftests/bpf/test_select_reuseport_kern.c @@ -0,0 +1,180 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Copyright (c) 2018 Facebook */ + +#include <stdlib.h> +#include <linux/in.h> +#include <linux/ip.h> +#include <linux/ipv6.h> +#include <linux/tcp.h> +#include <linux/udp.h> +#include <linux/bpf.h> +#include <linux/types.h> +#include <linux/if_ether.h> + +#include "bpf_endian.h" +#include "bpf_helpers.h" +#include "test_select_reuseport_common.h" + +int _version SEC("version") = 1; + +#ifndef offsetof +#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER) +#endif + +struct bpf_map_def SEC("maps") outer_map = { + .type = BPF_MAP_TYPE_ARRAY_OF_MAPS, + .key_size = sizeof(__u32), + .value_size = sizeof(__u32), + .max_entries = 1, +}; + +struct bpf_map_def SEC("maps") result_map = { + .type = BPF_MAP_TYPE_ARRAY, + .key_size = sizeof(__u32), + .value_size = sizeof(__u32), + .max_entries = NR_RESULTS, +}; + +struct bpf_map_def SEC("maps") tmp_index_ovr_map = { + .type = BPF_MAP_TYPE_ARRAY, + .key_size = sizeof(__u32), + .value_size = sizeof(int), + .max_entries = 1, +}; + +struct bpf_map_def SEC("maps") linum_map = { + .type = BPF_MAP_TYPE_ARRAY, + .key_size = sizeof(__u32), + .value_size = sizeof(__u32), + .max_entries = 1, +}; + +struct bpf_map_def SEC("maps") data_check_map = { + .type = BPF_MAP_TYPE_ARRAY, + .key_size = sizeof(__u32), + .value_size = sizeof(struct data_check), + .max_entries = 1, +}; + +#define GOTO_DONE(_result) ({ \ + result = (_result); \ + linum = __LINE__; \ + goto done; \ +}) + +SEC("select_by_skb_data") +int _select_by_skb_data(struct sk_reuseport_md *reuse_md) +{ + __u32 linum, index = 0, flags = 0, index_zero = 0; + __u32 *result_cnt, *linum_value; + struct data_check data_check = {}; + struct cmd *cmd, cmd_copy; + void *data, *data_end; + void *reuseport_array; + enum result result; + int *index_ovr; + int err; + + data = reuse_md->data; + data_end = reuse_md->data_end; + data_check.len = reuse_md->len; + data_check.eth_protocol = reuse_md->eth_protocol; + data_check.ip_protocol = reuse_md->ip_protocol; + data_check.hash = reuse_md->hash; + data_check.bind_inany = reuse_md->bind_inany; + if (data_check.eth_protocol == bpf_htons(ETH_P_IP)) { + if (bpf_skb_load_bytes_relative(reuse_md, + offsetof(struct iphdr, saddr), + data_check.skb_addrs, 8, + BPF_HDR_START_NET)) + GOTO_DONE(DROP_MISC); + } else { + if (bpf_skb_load_bytes_relative(reuse_md, + offsetof(struct ipv6hdr, saddr), + data_check.skb_addrs, 32, + BPF_HDR_START_NET)) + GOTO_DONE(DROP_MISC); + } + + /* + * The ip_protocol could be a compile time decision + * if the bpf_prog.o is dedicated to either TCP or + * UDP. + * + * Otherwise, reuse_md->ip_protocol or + * the protocol field in the iphdr can be used. + */ + if (data_check.ip_protocol == IPPROTO_TCP) { + struct tcphdr *th = data; + + if (th + 1 > data_end) + GOTO_DONE(DROP_MISC); + + data_check.skb_ports[0] = th->source; + data_check.skb_ports[1] = th->dest; + + if ((th->doff << 2) + sizeof(*cmd) > data_check.len) + GOTO_DONE(DROP_ERR_SKB_DATA); + if (bpf_skb_load_bytes(reuse_md, th->doff << 2, &cmd_copy, + sizeof(cmd_copy))) + GOTO_DONE(DROP_MISC); + cmd = &cmd_copy; + } else if (data_check.ip_protocol == IPPROTO_UDP) { + struct udphdr *uh = data; + + if (uh + 1 > data_end) + GOTO_DONE(DROP_MISC); + + data_check.skb_ports[0] = uh->source; + data_check.skb_ports[1] = uh->dest; + + if (sizeof(struct udphdr) + sizeof(*cmd) > data_check.len) + GOTO_DONE(DROP_ERR_SKB_DATA); + if (data + sizeof(struct udphdr) + sizeof(*cmd) > data_end) { + if (bpf_skb_load_bytes(reuse_md, sizeof(struct udphdr), + &cmd_copy, sizeof(cmd_copy))) + GOTO_DONE(DROP_MISC); + cmd = &cmd_copy; + } else { + cmd = data + sizeof(struct udphdr); + } + } else { + GOTO_DONE(DROP_MISC); + } + + reuseport_array = bpf_map_lookup_elem(&outer_map, &index_zero); + if (!reuseport_array) + GOTO_DONE(DROP_ERR_INNER_MAP); + + index = cmd->reuseport_index; + index_ovr = bpf_map_lookup_elem(&tmp_index_ovr_map, &index_zero); + if (!index_ovr) + GOTO_DONE(DROP_MISC); + + if (*index_ovr != -1) { + index = *index_ovr; + *index_ovr = -1; + } + err = bpf_sk_select_reuseport(reuse_md, reuseport_array, &index, + flags); + if (!err) + GOTO_DONE(PASS); + + if (cmd->pass_on_failure) + GOTO_DONE(PASS_ERR_SK_SELECT_REUSEPORT); + else + GOTO_DONE(DROP_ERR_SK_SELECT_REUSEPORT); + +done: + result_cnt = bpf_map_lookup_elem(&result_map, &result); + if (!result_cnt) + return SK_DROP; + + bpf_map_update_elem(&linum_map, &index_zero, &linum, BPF_ANY); + bpf_map_update_elem(&data_check_map, &index_zero, &data_check, BPF_ANY); + + (*result_cnt)++; + return result < PASS ? SK_DROP : SK_PASS; +} + +char _license[] SEC("license") = "GPL"; -- cgit v1.2.3-58-ga151 From b70f1f3af47f4a21a25678f9ee587ed7986d62f8 Mon Sep 17 00:00:00 2001 From: Keara Leibovitz <kleib@mojatatu.com> Date: Fri, 10 Aug 2018 10:09:41 -0400 Subject: tc: Update README and add config Updated README. Added config file that contains the minimum required features enabled to run the tests currently present in the kernel. This must be updated when new unittests are created and require their own modules. Signed-off-by: Keara Leibovitz <kleib@mojatatu.com> Signed-off-by: David S. Miller <davem@davemloft.net> --- tools/testing/selftests/tc-testing/README | 16 +++++++---- tools/testing/selftests/tc-testing/config | 48 +++++++++++++++++++++++++++++++ 2 files changed, 59 insertions(+), 5 deletions(-) create mode 100644 tools/testing/selftests/tc-testing/config (limited to 'tools') diff --git a/tools/testing/selftests/tc-testing/README b/tools/testing/selftests/tc-testing/README index 3a0336782d2d..49a6f8c3fdae 100644 --- a/tools/testing/selftests/tc-testing/README +++ b/tools/testing/selftests/tc-testing/README @@ -17,6 +17,10 @@ REQUIREMENTS * The kernel must have veth support available, as a veth pair is created prior to running the tests. +* The kernel must have the appropriate infrastructure enabled to run all tdc + unit tests. See the config file in this directory for minimum required + features. As new tests will be added, config options list will be updated. + * All tc-related features being tested must be built in or available as modules. To check what is required in current setup run: ./tdc.py -c @@ -109,8 +113,8 @@ COMMAND LINE ARGUMENTS Run tdc.py -h to see the full list of available arguments. usage: tdc.py [-h] [-p PATH] [-D DIR [DIR ...]] [-f FILE [FILE ...]] - [-c [CATG [CATG ...]]] [-e ID [ID ...]] [-l] [-s] [-i] [-v] - [-d DEVICE] [-n NS] [-V] + [-c [CATG [CATG ...]]] [-e ID [ID ...]] [-l] [-s] [-i] [-v] [-N] + [-d DEVICE] [-P] [-n] [-V] Linux TC unit tests @@ -118,8 +122,10 @@ optional arguments: -h, --help show this help message and exit -p PATH, --path PATH The full path to the tc executable to use -v, --verbose Show the commands that are being run + -N, --notap Suppress tap results for command under test -d DEVICE, --device DEVICE Execute the test case in flower category + -P, --pause Pause execution just before post-suite stage selection: select which test cases: files plus directories; filtered by categories @@ -146,10 +152,10 @@ action: -i, --id Generate ID numbers for new test cases netns: - options for nsPlugin(run commands in net namespace) + options for nsPlugin (run commands in net namespace) - -n NS, --namespace NS - Run commands in namespace NS + -n, --namespace + Run commands in namespace as specified in tdc_config.py valgrind: options for valgrindPlugin (run command under test under Valgrind) diff --git a/tools/testing/selftests/tc-testing/config b/tools/testing/selftests/tc-testing/config new file mode 100644 index 000000000000..203302065458 --- /dev/null +++ b/tools/testing/selftests/tc-testing/config @@ -0,0 +1,48 @@ +CONFIG_NET_SCHED=y + +# +# Queueing/Scheduling +# +CONFIG_NET_SCH_PRIO=m +CONFIG_NET_SCH_INGRESS=m + +# +# Classification +# +CONFIG_NET_CLS=y +CONFIG_NET_CLS_FW=m +CONFIG_NET_CLS_U32=m +CONFIG_CLS_U32_PERF=y +CONFIG_CLS_U32_MARK=y +CONFIG_NET_EMATCH=y +CONFIG_NET_EMATCH_STACK=32 +CONFIG_NET_EMATCH_CMP=m +CONFIG_NET_EMATCH_NBYTE=m +CONFIG_NET_EMATCH_U32=m +CONFIG_NET_EMATCH_META=m +CONFIG_NET_EMATCH_TEXT=m +CONFIG_NET_EMATCH_IPSET=m +CONFIG_NET_EMATCH_IPT=m +CONFIG_NET_CLS_ACT=y +CONFIG_NET_ACT_POLICE=m +CONFIG_NET_ACT_GACT=m +CONFIG_GACT_PROB=y +CONFIG_NET_ACT_MIRRED=m +CONFIG_NET_ACT_SAMPLE=m +CONFIG_NET_ACT_IPT=m +CONFIG_NET_ACT_NAT=m +CONFIG_NET_ACT_PEDIT=m +CONFIG_NET_ACT_SIMP=m +CONFIG_NET_ACT_SKBEDIT=m +CONFIG_NET_ACT_CSUM=m +CONFIG_NET_ACT_VLAN=m +CONFIG_NET_ACT_BPF=m +CONFIG_NET_ACT_CONNMARK=m +CONFIG_NET_ACT_SKBMOD=m +CONFIG_NET_ACT_IFE=m +CONFIG_NET_ACT_TUNNEL_KEY=m +CONFIG_NET_IFE_SKBMARK=m +CONFIG_NET_IFE_SKBPRIO=m +CONFIG_NET_IFE_SKBTCINDEX=m +CONFIG_NET_CLS_IND=y +CONFIG_NET_SCH_FIFO=y -- cgit v1.2.3-58-ga151 From 539764d07b49c5b322cc065d275f65df275e4991 Mon Sep 17 00:00:00 2001 From: Andrey Ignatov <rdna@fb.com> Date: Sun, 12 Aug 2018 10:49:28 -0700 Subject: bpf: Sync bpf.h to tools/ Sync skb_ancestor_cgroup_id() related bpf UAPI changes to tools/. Signed-off-by: Andrey Ignatov <rdna@fb.com> Signed-off-by: Daniel Borkmann <daniel@iogearbox.net> --- tools/include/uapi/linux/bpf.h | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) (limited to 'tools') diff --git a/tools/include/uapi/linux/bpf.h b/tools/include/uapi/linux/bpf.h index 3102a2a23c31..66917a4eba27 100644 --- a/tools/include/uapi/linux/bpf.h +++ b/tools/include/uapi/linux/bpf.h @@ -2093,6 +2093,24 @@ union bpf_attr { * Return * The id is returned or 0 in case the id could not be retrieved. * + * u64 bpf_skb_ancestor_cgroup_id(struct sk_buff *skb, int ancestor_level) + * Description + * Return id of cgroup v2 that is ancestor of cgroup associated + * with the *skb* at the *ancestor_level*. The root cgroup is at + * *ancestor_level* zero and each step down the hierarchy + * increments the level. If *ancestor_level* == level of cgroup + * associated with *skb*, then return value will be same as that + * of **bpf_skb_cgroup_id**\ (). + * + * The helper is useful to implement policies based on cgroups + * that are upper in hierarchy than immediate cgroup associated + * with *skb*. + * + * The format of returned id and helper limitations are same as in + * **bpf_skb_cgroup_id**\ (). + * Return + * The id is returned or 0 in case the id could not be retrieved. + * * u64 bpf_get_current_cgroup_id(void) * Return * A 64-bit integer containing the current cgroup id based @@ -2207,7 +2225,8 @@ union bpf_attr { FN(skb_cgroup_id), \ FN(get_current_cgroup_id), \ FN(get_local_storage), \ - FN(sk_select_reuseport), + FN(sk_select_reuseport), \ + FN(skb_ancestor_cgroup_id), /* integer value in 'imm' field of BPF_CALL instruction selects which helper * function eBPF program intends to call -- cgit v1.2.3-58-ga151 From 02f6ac7456a0d77360cb47b8d2ed4366b883096d Mon Sep 17 00:00:00 2001 From: Andrey Ignatov <rdna@fb.com> Date: Sun, 12 Aug 2018 10:49:29 -0700 Subject: selftests/bpf: Add cgroup id helpers to bpf_helpers.h Add bpf_skb_cgroup_id and bpf_skb_ancestor_cgroup_id helpers to bpf_helpers.h to use them in tests and samples. Signed-off-by: Andrey Ignatov <rdna@fb.com> Signed-off-by: Daniel Borkmann <daniel@iogearbox.net> --- tools/testing/selftests/bpf/bpf_helpers.h | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'tools') diff --git a/tools/testing/selftests/bpf/bpf_helpers.h b/tools/testing/selftests/bpf/bpf_helpers.h index 5c32266c2c38..e4be7730222d 100644 --- a/tools/testing/selftests/bpf/bpf_helpers.h +++ b/tools/testing/selftests/bpf/bpf_helpers.h @@ -139,6 +139,10 @@ static unsigned long long (*bpf_get_current_cgroup_id)(void) = (void *) BPF_FUNC_get_current_cgroup_id; static void *(*bpf_get_local_storage)(void *map, unsigned long long flags) = (void *) BPF_FUNC_get_local_storage; +static unsigned long long (*bpf_skb_cgroup_id)(void *ctx) = + (void *) BPF_FUNC_skb_cgroup_id; +static unsigned long long (*bpf_skb_ancestor_cgroup_id)(void *ctx, int level) = + (void *) BPF_FUNC_skb_ancestor_cgroup_id; /* llvm builtin functions that eBPF C program may use to * emit BPF_LD_ABS and BPF_LD_IND instructions -- cgit v1.2.3-58-ga151 From 5ecd8c22739b9a5f6d6431234decd912aa3f48ad Mon Sep 17 00:00:00 2001 From: Andrey Ignatov <rdna@fb.com> Date: Sun, 12 Aug 2018 10:49:30 -0700 Subject: selftests/bpf: Selftest for bpf_skb_ancestor_cgroup_id Add selftests for bpf_skb_ancestor_cgroup_id helper. test_skb_cgroup_id.sh prepares testing interface and adds tc qdisc and filter for it using BPF object compiled from test_skb_cgroup_id_kern.c program. BPF program in test_skb_cgroup_id_kern.c gets ancestor cgroup id using the new helper at different levels of cgroup hierarchy that skb belongs to, including root level and non-existing level, and saves it to the map where the key is the level of corresponding cgroup and the value is its id. To trigger BPF program, user space program test_skb_cgroup_id_user is run. It adds itself into testing cgroup and sends UDP datagram to link-local multicast address of testing interface. Then it reads cgroup ids saved in kernel for different levels from the BPF map and compares them with those in user space. They must be equal for every level of ancestry. Example of run: # ./test_skb_cgroup_id.sh Wait for testing link-local IP to become available ... OK Note: 8 bytes struct bpf_elf_map fixup performed due to size mismatch! [PASS] Signed-off-by: Andrey Ignatov <rdna@fb.com> Signed-off-by: Daniel Borkmann <daniel@iogearbox.net> --- tools/testing/selftests/bpf/Makefile | 9 +- tools/testing/selftests/bpf/test_skb_cgroup_id.sh | 62 +++++++ .../selftests/bpf/test_skb_cgroup_id_kern.c | 47 ++++++ .../selftests/bpf/test_skb_cgroup_id_user.c | 187 +++++++++++++++++++++ 4 files changed, 302 insertions(+), 3 deletions(-) create mode 100755 tools/testing/selftests/bpf/test_skb_cgroup_id.sh create mode 100644 tools/testing/selftests/bpf/test_skb_cgroup_id_kern.c create mode 100644 tools/testing/selftests/bpf/test_skb_cgroup_id_user.c (limited to 'tools') diff --git a/tools/testing/selftests/bpf/Makefile b/tools/testing/selftests/bpf/Makefile index daed162043c2..fff7fb1285fc 100644 --- a/tools/testing/selftests/bpf/Makefile +++ b/tools/testing/selftests/bpf/Makefile @@ -34,7 +34,8 @@ TEST_GEN_FILES = test_pkt_access.o test_xdp.o test_l4lb.o test_tcp_estats.o test test_btf_haskv.o test_btf_nokv.o test_sockmap_kern.o test_tunnel_kern.o \ test_get_stack_rawtp.o test_sockmap_kern.o test_sockhash_kern.o \ test_lwt_seg6local.o sendmsg4_prog.o sendmsg6_prog.o test_lirc_mode2_kern.o \ - get_cgroup_id_kern.o socket_cookie_prog.o test_select_reuseport_kern.o + get_cgroup_id_kern.o socket_cookie_prog.o test_select_reuseport_kern.o \ + test_skb_cgroup_id_kern.o # Order correspond to 'make run_tests' order TEST_PROGS := test_kmod.sh \ @@ -45,10 +46,11 @@ TEST_PROGS := test_kmod.sh \ test_sock_addr.sh \ test_tunnel.sh \ test_lwt_seg6local.sh \ - test_lirc_mode2.sh + test_lirc_mode2.sh \ + test_skb_cgroup_id.sh # Compile but not part of 'make run_tests' -TEST_GEN_PROGS_EXTENDED = test_libbpf_open test_sock_addr +TEST_GEN_PROGS_EXTENDED = test_libbpf_open test_sock_addr test_skb_cgroup_id_user include ../lib.mk @@ -59,6 +61,7 @@ $(TEST_GEN_PROGS): $(BPFOBJ) $(TEST_GEN_PROGS_EXTENDED): $(OUTPUT)/libbpf.a $(OUTPUT)/test_dev_cgroup: cgroup_helpers.c +$(OUTPUT)/test_skb_cgroup_id_user: cgroup_helpers.c $(OUTPUT)/test_sock: cgroup_helpers.c $(OUTPUT)/test_sock_addr: cgroup_helpers.c $(OUTPUT)/test_socket_cookie: cgroup_helpers.c diff --git a/tools/testing/selftests/bpf/test_skb_cgroup_id.sh b/tools/testing/selftests/bpf/test_skb_cgroup_id.sh new file mode 100755 index 000000000000..42544a969abc --- /dev/null +++ b/tools/testing/selftests/bpf/test_skb_cgroup_id.sh @@ -0,0 +1,62 @@ +#!/bin/sh +# SPDX-License-Identifier: GPL-2.0 +# Copyright (c) 2018 Facebook + +set -eu + +wait_for_ip() +{ + local _i + echo -n "Wait for testing link-local IP to become available " + for _i in $(seq ${MAX_PING_TRIES}); do + echo -n "." + if ping -6 -q -c 1 -W 1 ff02::1%${TEST_IF} >/dev/null 2>&1; then + echo " OK" + return + fi + sleep 1 + done + echo 1>&2 "ERROR: Timeout waiting for test IP to become available." + exit 1 +} + +setup() +{ + # Create testing interfaces not to interfere with current environment. + ip link add dev ${TEST_IF} type veth peer name ${TEST_IF_PEER} + ip link set ${TEST_IF} up + ip link set ${TEST_IF_PEER} up + + wait_for_ip + + tc qdisc add dev ${TEST_IF} clsact + tc filter add dev ${TEST_IF} egress bpf obj ${BPF_PROG_OBJ} \ + sec ${BPF_PROG_SECTION} da + + BPF_PROG_ID=$(tc filter show dev ${TEST_IF} egress | \ + awk '/ id / {sub(/.* id /, "", $0); print($1)}') +} + +cleanup() +{ + ip link del ${TEST_IF} 2>/dev/null || : + ip link del ${TEST_IF_PEER} 2>/dev/null || : +} + +main() +{ + trap cleanup EXIT 2 3 6 15 + setup + ${PROG} ${TEST_IF} ${BPF_PROG_ID} +} + +DIR=$(dirname $0) +TEST_IF="test_cgid_1" +TEST_IF_PEER="test_cgid_2" +MAX_PING_TRIES=5 +BPF_PROG_OBJ="${DIR}/test_skb_cgroup_id_kern.o" +BPF_PROG_SECTION="cgroup_id_logger" +BPF_PROG_ID=0 +PROG="${DIR}/test_skb_cgroup_id_user" + +main diff --git a/tools/testing/selftests/bpf/test_skb_cgroup_id_kern.c b/tools/testing/selftests/bpf/test_skb_cgroup_id_kern.c new file mode 100644 index 000000000000..68cf9829f5a7 --- /dev/null +++ b/tools/testing/selftests/bpf/test_skb_cgroup_id_kern.c @@ -0,0 +1,47 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright (c) 2018 Facebook + +#include <linux/bpf.h> +#include <linux/pkt_cls.h> + +#include <string.h> + +#include "bpf_helpers.h" + +#define NUM_CGROUP_LEVELS 4 + +struct bpf_map_def SEC("maps") cgroup_ids = { + .type = BPF_MAP_TYPE_ARRAY, + .key_size = sizeof(__u32), + .value_size = sizeof(__u64), + .max_entries = NUM_CGROUP_LEVELS, +}; + +static __always_inline void log_nth_level(struct __sk_buff *skb, __u32 level) +{ + __u64 id; + + /* [1] &level passed to external function that may change it, it's + * incompatible with loop unroll. + */ + id = bpf_skb_ancestor_cgroup_id(skb, level); + bpf_map_update_elem(&cgroup_ids, &level, &id, 0); +} + +SEC("cgroup_id_logger") +int log_cgroup_id(struct __sk_buff *skb) +{ + /* Loop unroll can't be used here due to [1]. Unrolling manually. + * Number of calls should be in sync with NUM_CGROUP_LEVELS. + */ + log_nth_level(skb, 0); + log_nth_level(skb, 1); + log_nth_level(skb, 2); + log_nth_level(skb, 3); + + return TC_ACT_OK; +} + +int _version SEC("version") = 1; + +char _license[] SEC("license") = "GPL"; diff --git a/tools/testing/selftests/bpf/test_skb_cgroup_id_user.c b/tools/testing/selftests/bpf/test_skb_cgroup_id_user.c new file mode 100644 index 000000000000..c121cc59f314 --- /dev/null +++ b/tools/testing/selftests/bpf/test_skb_cgroup_id_user.c @@ -0,0 +1,187 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright (c) 2018 Facebook + +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include <arpa/inet.h> +#include <net/if.h> +#include <netinet/in.h> +#include <sys/socket.h> +#include <sys/types.h> + + +#include <bpf/bpf.h> +#include <bpf/libbpf.h> + +#include "bpf_rlimit.h" +#include "cgroup_helpers.h" + +#define CGROUP_PATH "/skb_cgroup_test" +#define NUM_CGROUP_LEVELS 4 + +/* RFC 4291, Section 2.7.1 */ +#define LINKLOCAL_MULTICAST "ff02::1" + +static int mk_dst_addr(const char *ip, const char *iface, + struct sockaddr_in6 *dst) +{ + memset(dst, 0, sizeof(*dst)); + + dst->sin6_family = AF_INET6; + dst->sin6_port = htons(1025); + + if (inet_pton(AF_INET6, ip, &dst->sin6_addr) != 1) { + log_err("Invalid IPv6: %s", ip); + return -1; + } + + dst->sin6_scope_id = if_nametoindex(iface); + if (!dst->sin6_scope_id) { + log_err("Failed to get index of iface: %s", iface); + return -1; + } + + return 0; +} + +static int send_packet(const char *iface) +{ + struct sockaddr_in6 dst; + char msg[] = "msg"; + int err = 0; + int fd = -1; + + if (mk_dst_addr(LINKLOCAL_MULTICAST, iface, &dst)) + goto err; + + fd = socket(AF_INET6, SOCK_DGRAM, 0); + if (fd == -1) { + log_err("Failed to create UDP socket"); + goto err; + } + + if (sendto(fd, &msg, sizeof(msg), 0, (const struct sockaddr *)&dst, + sizeof(dst)) == -1) { + log_err("Failed to send datagram"); + goto err; + } + + goto out; +err: + err = -1; +out: + if (fd >= 0) + close(fd); + return err; +} + +int get_map_fd_by_prog_id(int prog_id) +{ + struct bpf_prog_info info = {}; + __u32 info_len = sizeof(info); + __u32 map_ids[1]; + int prog_fd = -1; + int map_fd = -1; + + prog_fd = bpf_prog_get_fd_by_id(prog_id); + if (prog_fd < 0) { + log_err("Failed to get fd by prog id %d", prog_id); + goto err; + } + + info.nr_map_ids = 1; + info.map_ids = (__u64) (unsigned long) map_ids; + + if (bpf_obj_get_info_by_fd(prog_fd, &info, &info_len)) { + log_err("Failed to get info by prog fd %d", prog_fd); + goto err; + } + + if (!info.nr_map_ids) { + log_err("No maps found for prog fd %d", prog_fd); + goto err; + } + + map_fd = bpf_map_get_fd_by_id(map_ids[0]); + if (map_fd < 0) + log_err("Failed to get fd by map id %d", map_ids[0]); +err: + if (prog_fd >= 0) + close(prog_fd); + return map_fd; +} + +int check_ancestor_cgroup_ids(int prog_id) +{ + __u64 actual_ids[NUM_CGROUP_LEVELS], expected_ids[NUM_CGROUP_LEVELS]; + __u32 level; + int err = 0; + int map_fd; + + expected_ids[0] = 0x100000001; /* root cgroup */ + expected_ids[1] = get_cgroup_id(""); + expected_ids[2] = get_cgroup_id(CGROUP_PATH); + expected_ids[3] = 0; /* non-existent cgroup */ + + map_fd = get_map_fd_by_prog_id(prog_id); + if (map_fd < 0) + goto err; + + for (level = 0; level < NUM_CGROUP_LEVELS; ++level) { + if (bpf_map_lookup_elem(map_fd, &level, &actual_ids[level])) { + log_err("Failed to lookup key %d", level); + goto err; + } + if (actual_ids[level] != expected_ids[level]) { + log_err("%llx (actual) != %llx (expected), level: %u\n", + actual_ids[level], expected_ids[level], level); + goto err; + } + } + + goto out; +err: + err = -1; +out: + if (map_fd >= 0) + close(map_fd); + return err; +} + +int main(int argc, char **argv) +{ + int cgfd = -1; + int err = 0; + + if (argc < 3) { + fprintf(stderr, "Usage: %s iface prog_id\n", argv[0]); + exit(EXIT_FAILURE); + } + + if (setup_cgroup_environment()) + goto err; + + cgfd = create_and_get_cgroup(CGROUP_PATH); + if (!cgfd) + goto err; + + if (join_cgroup(CGROUP_PATH)) + goto err; + + if (send_packet(argv[1])) + goto err; + + if (check_ancestor_cgroup_ids(atoi(argv[2]))) + goto err; + + goto out; +err: + err = -1; +out: + close(cgfd); + cleanup_cgroup_environment(); + printf("[%s]\n", err ? "FAIL" : "PASS"); + return err; +} -- cgit v1.2.3-58-ga151 From 26a1ccc6c117be8e33e0410fce8c5298b0015b99 Mon Sep 17 00:00:00 2001 From: Colin Ian King <colin.king@canonical.com> Date: Mon, 13 Aug 2018 15:00:32 +0100 Subject: bpf: test: fix spelling mistake "REUSEEPORT" -> "REUSEPORT" Trivial fix to spelling mistake in error message Signed-off-by: Colin Ian King <colin.king@canonical.com> Acked-by: Alexei Starovoitov <ast@kernel.org> Signed-off-by: David S. Miller <davem@davemloft.net> --- tools/testing/selftests/bpf/test_maps.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'tools') diff --git a/tools/testing/selftests/bpf/test_maps.c b/tools/testing/selftests/bpf/test_maps.c index 4b7c74f5faa7..6f54f84144a0 100644 --- a/tools/testing/selftests/bpf/test_maps.c +++ b/tools/testing/selftests/bpf/test_maps.c @@ -1191,7 +1191,7 @@ static void prepare_reuseport_grp(int type, int map_fd, err = setsockopt(fd64, SOL_SOCKET, SO_REUSEPORT, &optval, sizeof(optval)); - CHECK(err == -1, "setsockopt(SO_REUSEEPORT)", + CHECK(err == -1, "setsockopt(SO_REUSEPORT)", "err:%d errno:%d\n", err, errno); /* reuseport_array does not allow unbound sk */ -- cgit v1.2.3-58-ga151