ipv6: When forwarding count rx stats on the orig netdev
authorStephen Suryaputra <ssuryaextr@gmail.com>
Thu, 14 Oct 2021 13:08:45 +0000 (09:08 -0400)
committerJakub Kicinski <kuba@kernel.org>
Fri, 15 Oct 2021 22:32:04 +0000 (15:32 -0700)
Commit bdb7cc643fc9 ("ipv6: Count interface receive statistics on the
ingress netdev") does not work when ip6_forward() executes on the skbs
with vrf-enslaved netdev. Use IP6CB(skb)->iif to get to the right one.

Add a selftest script to verify.

Fixes: bdb7cc643fc9 ("ipv6: Count interface receive statistics on the ingress netdev")
Signed-off-by: Stephen Suryaputra <ssuryaextr@gmail.com>
Reviewed-by: David Ahern <dsahern@kernel.org>
Link: https://lore.kernel.org/r/20211014130845.410602-1-ssuryaextr@gmail.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
net/ipv6/ip6_output.c
tools/testing/selftests/net/forwarding/Makefile
tools/testing/selftests/net/forwarding/forwarding.config.sample
tools/testing/selftests/net/forwarding/ip6_forward_instats_vrf.sh [new file with mode: 0755]
tools/testing/selftests/net/forwarding/lib.sh

index 12f985f..2f044a4 100644 (file)
@@ -464,13 +464,14 @@ static bool ip6_pkt_too_big(const struct sk_buff *skb, unsigned int mtu)
 
 int ip6_forward(struct sk_buff *skb)
 {
-       struct inet6_dev *idev = __in6_dev_get_safely(skb->dev);
        struct dst_entry *dst = skb_dst(skb);
        struct ipv6hdr *hdr = ipv6_hdr(skb);
        struct inet6_skb_parm *opt = IP6CB(skb);
        struct net *net = dev_net(dst->dev);
+       struct inet6_dev *idev;
        u32 mtu;
 
+       idev = __in6_dev_get_safely(dev_get_by_index_rcu(net, IP6CB(skb)->iif));
        if (net->ipv6.devconf_all->forwarding == 0)
                goto error;
 
index d97bd68..72ee644 100644 (file)
@@ -9,6 +9,7 @@ TEST_PROGS = bridge_igmp.sh \
        gre_inner_v4_multipath.sh \
        gre_inner_v6_multipath.sh \
        gre_multipath.sh \
+       ip6_forward_instats_vrf.sh \
        ip6gre_inner_v4_multipath.sh \
        ip6gre_inner_v6_multipath.sh \
        ipip_flat_gre_key.sh \
index b802c14..e5e2fbe 100644 (file)
@@ -39,3 +39,5 @@ NETIF_CREATE=yes
 # Timeout (in seconds) before ping exits regardless of how many packets have
 # been sent or received
 PING_TIMEOUT=5
+# IPv6 traceroute utility name.
+TROUTE6=traceroute6
diff --git a/tools/testing/selftests/net/forwarding/ip6_forward_instats_vrf.sh b/tools/testing/selftests/net/forwarding/ip6_forward_instats_vrf.sh
new file mode 100755 (executable)
index 0000000..9f5b3e2
--- /dev/null
@@ -0,0 +1,172 @@
+#!/bin/bash
+# SPDX-License-Identifier: GPL-2.0
+
+# Test ipv6 stats on the incoming if when forwarding with VRF
+
+ALL_TESTS="
+       ipv6_ping
+       ipv6_in_too_big_err
+       ipv6_in_hdr_err
+       ipv6_in_addr_err
+       ipv6_in_discard
+"
+
+NUM_NETIFS=4
+source lib.sh
+
+h1_create()
+{
+       simple_if_init $h1 2001:1:1::2/64
+       ip -6 route add vrf v$h1 2001:1:2::/64 via 2001:1:1::1
+}
+
+h1_destroy()
+{
+       ip -6 route del vrf v$h1 2001:1:2::/64 via 2001:1:1::1
+       simple_if_fini $h1 2001:1:1::2/64
+}
+
+router_create()
+{
+       vrf_create router
+       __simple_if_init $rtr1 router 2001:1:1::1/64
+       __simple_if_init $rtr2 router 2001:1:2::1/64
+       mtu_set $rtr2 1280
+}
+
+router_destroy()
+{
+       mtu_restore $rtr2
+       __simple_if_fini $rtr2 2001:1:2::1/64
+       __simple_if_fini $rtr1 2001:1:1::1/64
+       vrf_destroy router
+}
+
+h2_create()
+{
+       simple_if_init $h2 2001:1:2::2/64
+       ip -6 route add vrf v$h2 2001:1:1::/64 via 2001:1:2::1
+       mtu_set $h2 1280
+}
+
+h2_destroy()
+{
+       mtu_restore $h2
+       ip -6 route del vrf v$h2 2001:1:1::/64 via 2001:1:2::1
+       simple_if_fini $h2 2001:1:2::2/64
+}
+
+setup_prepare()
+{
+       h1=${NETIFS[p1]}
+       rtr1=${NETIFS[p2]}
+
+       rtr2=${NETIFS[p3]}
+       h2=${NETIFS[p4]}
+
+       vrf_prepare
+       h1_create
+       router_create
+       h2_create
+
+       forwarding_enable
+}
+
+cleanup()
+{
+       pre_cleanup
+
+       forwarding_restore
+
+       h2_destroy
+       router_destroy
+       h1_destroy
+       vrf_cleanup
+}
+
+ipv6_in_too_big_err()
+{
+       RET=0
+
+       local t0=$(ipv6_stats_get $rtr1 Ip6InTooBigErrors)
+       local vrf_name=$(master_name_get $h1)
+
+       # Send too big packets
+       ip vrf exec $vrf_name \
+               $PING6 -s 1300 2001:1:2::2 -c 1 -w $PING_TIMEOUT &> /dev/null
+
+       local t1=$(ipv6_stats_get $rtr1 Ip6InTooBigErrors)
+       test "$((t1 - t0))" -ne 0
+       check_err $?
+       log_test "Ip6InTooBigErrors"
+}
+
+ipv6_in_hdr_err()
+{
+       RET=0
+
+       local t0=$(ipv6_stats_get $rtr1 Ip6InHdrErrors)
+       local vrf_name=$(master_name_get $h1)
+
+       # Send packets with hop limit 1, easiest with traceroute6 as some ping6
+       # doesn't allow hop limit to be specified
+       ip vrf exec $vrf_name \
+               $TROUTE6 2001:1:2::2 &> /dev/null
+
+       local t1=$(ipv6_stats_get $rtr1 Ip6InHdrErrors)
+       test "$((t1 - t0))" -ne 0
+       check_err $?
+       log_test "Ip6InHdrErrors"
+}
+
+ipv6_in_addr_err()
+{
+       RET=0
+
+       local t0=$(ipv6_stats_get $rtr1 Ip6InAddrErrors)
+       local vrf_name=$(master_name_get $h1)
+
+       # Disable forwarding temporary while sending the packet
+       sysctl -qw net.ipv6.conf.all.forwarding=0
+       ip vrf exec $vrf_name \
+               $PING6 2001:1:2::2 -c 1 -w $PING_TIMEOUT &> /dev/null
+       sysctl -qw net.ipv6.conf.all.forwarding=1
+
+       local t1=$(ipv6_stats_get $rtr1 Ip6InAddrErrors)
+       test "$((t1 - t0))" -ne 0
+       check_err $?
+       log_test "Ip6InAddrErrors"
+}
+
+ipv6_in_discard()
+{
+       RET=0
+
+       local t0=$(ipv6_stats_get $rtr1 Ip6InDiscards)
+       local vrf_name=$(master_name_get $h1)
+
+       # Add a policy to discard
+       ip xfrm policy add dst 2001:1:2::2/128 dir fwd action block
+       ip vrf exec $vrf_name \
+               $PING6 2001:1:2::2 -c 1 -w $PING_TIMEOUT &> /dev/null
+       ip xfrm policy del dst 2001:1:2::2/128 dir fwd
+
+       local t1=$(ipv6_stats_get $rtr1 Ip6InDiscards)
+       test "$((t1 - t0))" -ne 0
+       check_err $?
+       log_test "Ip6InDiscards"
+}
+ipv6_ping()
+{
+       RET=0
+
+       ping6_test $h1 2001:1:2::2
+}
+
+trap cleanup EXIT
+
+setup_prepare
+setup_wait
+tests_run
+
+exit $EXIT_STATUS
index e7fc5c3..92087d4 100644 (file)
@@ -751,6 +751,14 @@ qdisc_parent_stats_get()
            | jq '.[] | select(.parent == "'"$parent"'") | '"$selector"
 }
 
+ipv6_stats_get()
+{
+       local dev=$1; shift
+       local stat=$1; shift
+
+       cat /proc/net/dev_snmp6/$dev | grep "^$stat" | cut -f2
+}
+
 humanize()
 {
        local speed=$1; shift