1

netfilter pull request 24-06-19

-----BEGIN PGP SIGNATURE-----
 
 iQIzBAABCgAdFiEEN9lkrMBJgcdVAPub1V2XiooUIOQFAmZzCt0ACgkQ1V2XiooU
 IOT/KxAAnSw4peWDTEh1MGagzvUtWFx+S7PZM5g+G/1FtILieMhgDdarVh0T41WT
 MnbfzYbluXy0Xh1RjLRiPvfLPfqyprOYBHUw5o2qCuqFJheWCLyWKwxxQEXXA56t
 gQSkLT4h7kdzdxGAC44Bwx3/cv+S1gfl/sZQ3ku5VmYiAAJ3EoFyxWY3kVUxyqOL
 PAkMxnHrfrNf6XgQB7s8pdZqxkTCyrDTiK0jeZ5aH7hY2eM7rJKQLribMsY02PT6
 rVX6mjqixSiKMz1NLWVKx7hwV5JXVF8iu3SNPsZGzbNfN0jChoHk+XvUUWBHW8dD
 M2PCfGPsHZhuc/yb7Osa5jfB6qVOHlp7QcUkQAUtE6Wh/MohpV1MGpIxwGQcHIDu
 lii7B7v8D1Cl+eWAICG1V1F2v2EL3WyBcMso3gFQxzLAyVF6D8NVvsgmNYfrsk3t
 kOOdhnNcY0s/ZhJh8GKM/qigisYzt04+56swoc1oLXHbdyeHhMdrqCoU9q8e7Fro
 yTaigsZYqWifmfThEYGtN1mtsw0VirencN1oSeyZJEnAM7WusyFLcrmg8rmjTbJ7
 +FOwIw7H68Omx+RFJhpOquZWbFUexeAOA3I9VScC4DOvL4yZ51JbDJ82pUzXOqB3
 RT0wF3gt8k+NqFSgAFaaJEcdRA3Qc3EyAb1MkdleJPOoxVPebsw=
 =hdot
 -----END PGP SIGNATURE-----

Merge tag 'nf-24-06-19' of git://git.kernel.org/pub/scm/linux/kernel/git/netfilter/nf

Pablo Neira Ayuso says:

====================
Netfilter fixes for net

The following patchset contains Netfilter fixes for net:

Patch #1 fixes the suspicious RCU usage warning that resulted from the
	 recent fix for the race between namespace cleanup and gc in
	 ipset left out checking the pernet exit phase when calling
	 rcu_dereference_protected(), from Jozsef Kadlecsik.

Patch #2 fixes incorrect input and output netdevice in SRv6 prerouting
	 hooks, from Jianguo Wu.

Patch #3 moves nf_hooks_lwtunnel sysctl toggle to the netfilter core.
	 The connection tracking system is loaded on-demand, this
	 ensures availability of this knob regardless.

Patch #4-#5 adds selftests for SRv6 netfilter hooks also from Jianguo Wu.

netfilter pull request 24-06-19

* tag 'nf-24-06-19' of git://git.kernel.org/pub/scm/linux/kernel/git/netfilter/nf:
  selftests: add selftest for the SRv6 End.DX6 behavior with netfilter
  selftests: add selftest for the SRv6 End.DX4 behavior with netfilter
  netfilter: move the sysctl nf_hooks_lwtunnel into the netfilter core
  seg6: fix parameter passing when calling NF_HOOK() in End.DX4 and End.DX6 behaviors
  netfilter: ipset: Fix suspicious rcu_dereference_protected()
====================

Link: https://lore.kernel.org/r/20240619170537.2846-1-pablo@netfilter.org
Signed-off-by: Paolo Abeni <pabeni@redhat.com>
This commit is contained in:
Paolo Abeni 2024-06-20 11:21:52 +02:00
commit 0f74d0cda9
11 changed files with 776 additions and 26 deletions

View File

@ -15,6 +15,9 @@ struct netns_nf {
const struct nf_logger __rcu *nf_loggers[NFPROTO_NUMPROTO]; const struct nf_logger __rcu *nf_loggers[NFPROTO_NUMPROTO];
#ifdef CONFIG_SYSCTL #ifdef CONFIG_SYSCTL
struct ctl_table_header *nf_log_dir_header; struct ctl_table_header *nf_log_dir_header;
#ifdef CONFIG_LWTUNNEL
struct ctl_table_header *nf_lwtnl_dir_header;
#endif
#endif #endif
struct nf_hook_entries __rcu *hooks_ipv4[NF_INET_NUMHOOKS]; struct nf_hook_entries __rcu *hooks_ipv4[NF_INET_NUMHOOKS];
struct nf_hook_entries __rcu *hooks_ipv6[NF_INET_NUMHOOKS]; struct nf_hook_entries __rcu *hooks_ipv6[NF_INET_NUMHOOKS];

View File

@ -941,8 +941,8 @@ static int input_action_end_dx6(struct sk_buff *skb,
if (static_branch_unlikely(&nf_hooks_lwtunnel_enabled)) if (static_branch_unlikely(&nf_hooks_lwtunnel_enabled))
return NF_HOOK(NFPROTO_IPV6, NF_INET_PRE_ROUTING, return NF_HOOK(NFPROTO_IPV6, NF_INET_PRE_ROUTING,
dev_net(skb->dev), NULL, skb, NULL, dev_net(skb->dev), NULL, skb, skb->dev,
skb_dst(skb)->dev, input_action_end_dx6_finish); NULL, input_action_end_dx6_finish);
return input_action_end_dx6_finish(dev_net(skb->dev), NULL, skb); return input_action_end_dx6_finish(dev_net(skb->dev), NULL, skb);
drop: drop:
@ -991,8 +991,8 @@ static int input_action_end_dx4(struct sk_buff *skb,
if (static_branch_unlikely(&nf_hooks_lwtunnel_enabled)) if (static_branch_unlikely(&nf_hooks_lwtunnel_enabled))
return NF_HOOK(NFPROTO_IPV4, NF_INET_PRE_ROUTING, return NF_HOOK(NFPROTO_IPV4, NF_INET_PRE_ROUTING,
dev_net(skb->dev), NULL, skb, NULL, dev_net(skb->dev), NULL, skb, skb->dev,
skb_dst(skb)->dev, input_action_end_dx4_finish); NULL, input_action_end_dx4_finish);
return input_action_end_dx4_finish(dev_net(skb->dev), NULL, skb); return input_action_end_dx4_finish(dev_net(skb->dev), NULL, skb);
drop: drop:

View File

@ -815,12 +815,21 @@ int __init netfilter_init(void)
if (ret < 0) if (ret < 0)
goto err; goto err;
#ifdef CONFIG_LWTUNNEL
ret = netfilter_lwtunnel_init();
if (ret < 0)
goto err_lwtunnel_pernet;
#endif
ret = netfilter_log_init(); ret = netfilter_log_init();
if (ret < 0) if (ret < 0)
goto err_pernet; goto err_log_pernet;
return 0; return 0;
err_pernet: err_log_pernet:
#ifdef CONFIG_LWTUNNEL
netfilter_lwtunnel_fini();
err_lwtunnel_pernet:
#endif
unregister_pernet_subsys(&netfilter_net_ops); unregister_pernet_subsys(&netfilter_net_ops);
err: err:
return ret; return ret;

View File

@ -53,12 +53,13 @@ MODULE_DESCRIPTION("core IP set support");
MODULE_ALIAS_NFNL_SUBSYS(NFNL_SUBSYS_IPSET); MODULE_ALIAS_NFNL_SUBSYS(NFNL_SUBSYS_IPSET);
/* When the nfnl mutex or ip_set_ref_lock is held: */ /* When the nfnl mutex or ip_set_ref_lock is held: */
#define ip_set_dereference(p) \ #define ip_set_dereference(inst) \
rcu_dereference_protected(p, \ rcu_dereference_protected((inst)->ip_set_list, \
lockdep_nfnl_is_held(NFNL_SUBSYS_IPSET) || \ lockdep_nfnl_is_held(NFNL_SUBSYS_IPSET) || \
lockdep_is_held(&ip_set_ref_lock)) lockdep_is_held(&ip_set_ref_lock) || \
(inst)->is_deleted)
#define ip_set(inst, id) \ #define ip_set(inst, id) \
ip_set_dereference((inst)->ip_set_list)[id] ip_set_dereference(inst)[id]
#define ip_set_ref_netlink(inst,id) \ #define ip_set_ref_netlink(inst,id) \
rcu_dereference_raw((inst)->ip_set_list)[id] rcu_dereference_raw((inst)->ip_set_list)[id]
#define ip_set_dereference_nfnl(p) \ #define ip_set_dereference_nfnl(p) \
@ -1133,7 +1134,7 @@ static int ip_set_create(struct sk_buff *skb, const struct nfnl_info *info,
if (!list) if (!list)
goto cleanup; goto cleanup;
/* nfnl mutex is held, both lists are valid */ /* nfnl mutex is held, both lists are valid */
tmp = ip_set_dereference(inst->ip_set_list); tmp = ip_set_dereference(inst);
memcpy(list, tmp, sizeof(struct ip_set *) * inst->ip_set_max); memcpy(list, tmp, sizeof(struct ip_set *) * inst->ip_set_max);
rcu_assign_pointer(inst->ip_set_list, list); rcu_assign_pointer(inst->ip_set_list, list);
/* Make sure all current packets have passed through */ /* Make sure all current packets have passed through */

View File

@ -22,9 +22,6 @@
#include <net/netfilter/nf_conntrack_acct.h> #include <net/netfilter/nf_conntrack_acct.h>
#include <net/netfilter/nf_conntrack_zones.h> #include <net/netfilter/nf_conntrack_zones.h>
#include <net/netfilter/nf_conntrack_timestamp.h> #include <net/netfilter/nf_conntrack_timestamp.h>
#ifdef CONFIG_LWTUNNEL
#include <net/netfilter/nf_hooks_lwtunnel.h>
#endif
#include <linux/rculist_nulls.h> #include <linux/rculist_nulls.h>
static bool enable_hooks __read_mostly; static bool enable_hooks __read_mostly;
@ -612,9 +609,6 @@ enum nf_ct_sysctl_index {
NF_SYSCTL_CT_PROTO_TIMEOUT_GRE, NF_SYSCTL_CT_PROTO_TIMEOUT_GRE,
NF_SYSCTL_CT_PROTO_TIMEOUT_GRE_STREAM, NF_SYSCTL_CT_PROTO_TIMEOUT_GRE_STREAM,
#endif #endif
#ifdef CONFIG_LWTUNNEL
NF_SYSCTL_CT_LWTUNNEL,
#endif
NF_SYSCTL_CT_LAST_SYSCTL, NF_SYSCTL_CT_LAST_SYSCTL,
}; };
@ -946,15 +940,6 @@ static struct ctl_table nf_ct_sysctl_table[] = {
.proc_handler = proc_dointvec_jiffies, .proc_handler = proc_dointvec_jiffies,
}, },
#endif #endif
#ifdef CONFIG_LWTUNNEL
[NF_SYSCTL_CT_LWTUNNEL] = {
.procname = "nf_hooks_lwtunnel",
.data = NULL,
.maxlen = sizeof(int),
.mode = 0644,
.proc_handler = nf_hooks_lwtunnel_sysctl_handler,
},
#endif
}; };
static struct ctl_table nf_ct_netfilter_table[] = { static struct ctl_table nf_ct_netfilter_table[] = {

View File

@ -3,6 +3,9 @@
#include <linux/sysctl.h> #include <linux/sysctl.h>
#include <net/lwtunnel.h> #include <net/lwtunnel.h>
#include <net/netfilter/nf_hooks_lwtunnel.h> #include <net/netfilter/nf_hooks_lwtunnel.h>
#include <linux/netfilter.h>
#include "nf_internals.h"
static inline int nf_hooks_lwtunnel_get(void) static inline int nf_hooks_lwtunnel_get(void)
{ {
@ -50,4 +53,68 @@ int nf_hooks_lwtunnel_sysctl_handler(struct ctl_table *table, int write,
return ret; return ret;
} }
EXPORT_SYMBOL_GPL(nf_hooks_lwtunnel_sysctl_handler); EXPORT_SYMBOL_GPL(nf_hooks_lwtunnel_sysctl_handler);
static struct ctl_table nf_lwtunnel_sysctl_table[] = {
{
.procname = "nf_hooks_lwtunnel",
.data = NULL,
.maxlen = sizeof(int),
.mode = 0644,
.proc_handler = nf_hooks_lwtunnel_sysctl_handler,
},
};
static int __net_init nf_lwtunnel_net_init(struct net *net)
{
struct ctl_table_header *hdr;
struct ctl_table *table;
table = nf_lwtunnel_sysctl_table;
if (!net_eq(net, &init_net)) {
table = kmemdup(nf_lwtunnel_sysctl_table,
sizeof(nf_lwtunnel_sysctl_table),
GFP_KERNEL);
if (!table)
goto err_alloc;
}
hdr = register_net_sysctl_sz(net, "net/netfilter", table,
ARRAY_SIZE(nf_lwtunnel_sysctl_table));
if (!hdr)
goto err_reg;
net->nf.nf_lwtnl_dir_header = hdr;
return 0;
err_reg:
if (!net_eq(net, &init_net))
kfree(table);
err_alloc:
return -ENOMEM;
}
static void __net_exit nf_lwtunnel_net_exit(struct net *net)
{
const struct ctl_table *table;
table = net->nf.nf_lwtnl_dir_header->ctl_table_arg;
unregister_net_sysctl_table(net->nf.nf_lwtnl_dir_header);
if (!net_eq(net, &init_net))
kfree(table);
}
static struct pernet_operations nf_lwtunnel_net_ops = {
.init = nf_lwtunnel_net_init,
.exit = nf_lwtunnel_net_exit,
};
int __init netfilter_lwtunnel_init(void)
{
return register_pernet_subsys(&nf_lwtunnel_net_ops);
}
void netfilter_lwtunnel_fini(void)
{
unregister_pernet_subsys(&nf_lwtunnel_net_ops);
}
#endif /* CONFIG_SYSCTL */ #endif /* CONFIG_SYSCTL */

View File

@ -29,6 +29,12 @@ void nf_queue_nf_hook_drop(struct net *net);
/* nf_log.c */ /* nf_log.c */
int __init netfilter_log_init(void); int __init netfilter_log_init(void);
#ifdef CONFIG_LWTUNNEL
/* nf_hooks_lwtunnel.c */
int __init netfilter_lwtunnel_init(void);
void netfilter_lwtunnel_fini(void);
#endif
/* core.c */ /* core.c */
void nf_hook_entries_delete_raw(struct nf_hook_entries __rcu **pp, void nf_hook_entries_delete_raw(struct nf_hook_entries __rcu **pp,
const struct nf_hook_ops *reg); const struct nf_hook_ops *reg);

View File

@ -43,6 +43,8 @@ TEST_PROGS += srv6_hl2encap_red_l2vpn_test.sh
TEST_PROGS += srv6_end_next_csid_l3vpn_test.sh TEST_PROGS += srv6_end_next_csid_l3vpn_test.sh
TEST_PROGS += srv6_end_x_next_csid_l3vpn_test.sh TEST_PROGS += srv6_end_x_next_csid_l3vpn_test.sh
TEST_PROGS += srv6_end_flavors_test.sh TEST_PROGS += srv6_end_flavors_test.sh
TEST_PROGS += srv6_end_dx4_netfilter_test.sh
TEST_PROGS += srv6_end_dx6_netfilter_test.sh
TEST_PROGS += vrf_strict_mode_test.sh TEST_PROGS += vrf_strict_mode_test.sh
TEST_PROGS += arp_ndisc_evict_nocarrier.sh TEST_PROGS += arp_ndisc_evict_nocarrier.sh
TEST_PROGS += ndisc_unsolicited_na_test.sh TEST_PROGS += ndisc_unsolicited_na_test.sh

View File

@ -101,3 +101,5 @@ CONFIG_NETFILTER_XT_MATCH_POLICY=m
CONFIG_CRYPTO_ARIA=y CONFIG_CRYPTO_ARIA=y
CONFIG_XFRM_INTERFACE=m CONFIG_XFRM_INTERFACE=m
CONFIG_XFRM_USER=m CONFIG_XFRM_USER=m
CONFIG_IP_NF_MATCH_RPFILTER=m
CONFIG_IP6_NF_MATCH_RPFILTER=m

View File

@ -0,0 +1,335 @@
#!/bin/bash
# SPDX-License-Identifier: GPL-2.0
#
# author: Jianguo Wu <wujianguo@chinatelecom.cn>
#
# Mostly copied from tools/testing/selftests/net/srv6_end_dt4_l3vpn_test.sh.
#
# This script is designed for testing the support of netfilter hooks for
# SRv6 End.DX4 behavior.
#
# Hereafter a network diagram is shown, where one tenants (named 100) offer
# IPv4 L3 VPN services allowing hosts to communicate with each other across
# an IPv6 network.
#
# Routers rt-1 and rt-2 implement IPv4 L3 VPN services leveraging the SRv6
# architecture. The key components for such VPNs are: a) SRv6 Encap behavior,
# b) SRv6 End.DX4 behavior.
#
# To explain how an IPv4 L3 VPN based on SRv6 works, let us briefly consider an
# example where, within the same domain of tenant 100, the host hs-1 pings
# the host hs-2.
#
# First of all, L2 reachability of the host hs-2 is taken into account by
# the router rt-1 which acts as an arp proxy.
#
# When the host hs-1 sends an IPv4 packet destined to hs-2, the router rt-1
# receives the packet on the internal veth-t100 interface, rt-1 contains the
# SRv6 Encap route for encapsulating the IPv4 packet in a IPv6 plus the Segment
# Routing Header (SRH) packet. This packet is sent through the (IPv6) core
# network up to the router rt-2 that receives it on veth0 interface.
#
# The rt-2 router uses the 'localsid' routing table to process incoming
# IPv6+SRH packets which belong to the VPN of the tenant 100. For each of these
# packets, the SRv6 End.DX4 behavior removes the outer IPv6+SRH headers and
# routs the packet to the specified nexthop. Afterwards, the packet is sent to
# the host hs-2 through the veth-t100 interface.
#
# The ping response follows the same processing but this time the role of rt-1
# and rt-2 are swapped.
#
# And when net.netfilter.nf_hooks_lwtunnel is set to 1 in rt-1 or rt-2, and a
# rpfilter iptables rule is added, SRv6 packets will go through netfilter PREROUTING
# hooks.
#
#
# +-------------------+ +-------------------+
# | | | |
# | hs-1 netns | | hs-2 netns |
# | | | |
# | +-------------+ | | +-------------+ |
# | | veth0 | | | | veth0 | |
# | | 10.0.0.1/24 | | | | 10.0.0.2/24 | |
# | +-------------+ | | +-------------+ |
# | . | | . |
# +-------------------+ +-------------------+
# . .
# . .
# . .
# +-----------------------------------+ +-----------------------------------+
# | . | | . |
# | +---------------+ | | +---------------- |
# | | veth-t100 | | | | veth-t100 | |
# | | 10.0.0.11/24 | +----------+ | | +----------+ | 10.0.0.22/24 | |
# | +-------+-------+ | route | | | | route | +-------+-------- |
# | | table | | | | table | |
# | +----------+ | | +----------+ |
# | +--------------+ | | +--------------+ |
# | | veth0 | | | | veth0 | |
# | | 2001:11::1/64 |.|...|.| 2001:11::2/64 | |
# | +--------------+ | | +--------------+ |
# | | | |
# | rt-1 netns | | rt-2 netns |
# | | | |
# +-----------------------------------+ +-----------------------------------+
#
# ~~~~~~~~~~~~~~~~~~~~~~~~~
# | Network configuration |
# ~~~~~~~~~~~~~~~~~~~~~~~~~
#
# rt-1: localsid table
# +----------------------------------------------------------------+
# |SID |Action |
# +----------------------------------------------------------------+
# |fc00:21:100::6004|apply SRv6 End.DX4 nh4 10.0.0.1 dev veth-t100 |
# +----------------------------------------------------------------+
#
# rt-1: route table
# +---------------------------------------------------+
# |host |Action |
# +---------------------------------------------------+
# |10.0.0.2 |apply seg6 encap segs fc00:12:100::6004|
# +---------------------------------------------------+
# |10.0.0.0/24|forward to dev veth_t100 |
# +---------------------------------------------------+
#
#
# rt-2: localsid table
# +---------------------------------------------------------------+
# |SID |Action |
# +---------------------------------------------------------------+
# |fc00:12:100::6004|apply SRv6 End.DX4 nh4 10.0.0.2 dev veth-t100|
# +---------------------------------------------------------------+
#
# rt-2: route table
# +---------------------------------------------------+
# |host |Action |
# +---------------------------------------------------+
# |10.0.0.1 |apply seg6 encap segs fc00:21:100::6004|
# +---------------------------------------------------+
# |10.0.0.0/24|forward to dev veth_t100 |
# +---------------------------------------------------+
#
# Kselftest framework requirement - SKIP code is 4.
ksft_skip=4
readonly IPv6_RT_NETWORK=2001:11
readonly IPv4_HS_NETWORK=10.0.0
readonly SID_LOCATOR=fc00
PING_TIMEOUT_SEC=4
ret=0
PAUSE_ON_FAIL=${PAUSE_ON_FAIL:=no}
log_test()
{
local rc=$1
local expected=$2
local msg="$3"
if [ ${rc} -eq ${expected} ]; then
nsuccess=$((nsuccess+1))
printf "\n TEST: %-60s [ OK ]\n" "${msg}"
else
ret=1
nfail=$((nfail+1))
printf "\n TEST: %-60s [FAIL]\n" "${msg}"
if [ "${PAUSE_ON_FAIL}" = "yes" ]; then
echo
echo "hit enter to continue, 'q' to quit"
read a
[ "$a" = "q" ] && exit 1
fi
fi
}
print_log_test_results()
{
if [ "$TESTS" != "none" ]; then
printf "\nTests passed: %3d\n" ${nsuccess}
printf "Tests failed: %3d\n" ${nfail}
fi
}
log_section()
{
echo
echo "################################################################################"
echo "TEST SECTION: $*"
echo "################################################################################"
}
cleanup()
{
ip link del veth-rt-1 2>/dev/null || true
ip link del veth-rt-2 2>/dev/null || true
# destroy routers rt-* and hosts hs-*
for ns in $(ip netns show | grep -E 'rt-*|hs-*'); do
ip netns del ${ns} || true
done
}
# Setup the basic networking for the routers
setup_rt_networking()
{
local rt=$1
local nsname=rt-${rt}
ip netns add ${nsname}
ip netns exec ${nsname} sysctl -wq net.ipv6.conf.all.accept_dad=0
ip netns exec ${nsname} sysctl -wq net.ipv6.conf.default.accept_dad=0
ip link set veth-rt-${rt} netns ${nsname}
ip -netns ${nsname} link set veth-rt-${rt} name veth0
ip -netns ${nsname} addr add ${IPv6_RT_NETWORK}::${rt}/64 dev veth0 nodad
ip -netns ${nsname} link set veth0 up
ip -netns ${nsname} link set lo up
ip netns exec ${nsname} sysctl -wq net.ipv4.ip_forward=1
ip netns exec ${nsname} sysctl -wq net.ipv6.conf.all.forwarding=1
}
setup_rt_netfilter()
{
local rt=$1
local nsname=rt-${rt}
ip netns exec ${nsname} sysctl -wq net.netfilter.nf_hooks_lwtunnel=1
ip netns exec ${nsname} iptables -t raw -A PREROUTING -m rpfilter --invert -j DROP
}
setup_hs()
{
local hs=$1
local rt=$2
local tid=$3
local hsname=hs-${hs}
local rtname=rt-${rt}
local rtveth=veth-t${tid}
# set the networking for the host
ip netns add ${hsname}
ip -netns ${hsname} link add veth0 type veth peer name ${rtveth}
ip -netns ${hsname} link set ${rtveth} netns ${rtname}
ip -netns ${hsname} addr add ${IPv4_HS_NETWORK}.${hs}/24 dev veth0
ip -netns ${hsname} link set veth0 up
ip -netns ${hsname} link set lo up
ip -netns ${rtname} addr add ${IPv4_HS_NETWORK}.${rt}${hs}/24 dev ${rtveth}
ip -netns ${rtname} link set ${rtveth} up
ip netns exec ${rtname} sysctl -wq net.ipv4.conf.${rtveth}.proxy_arp=1
}
setup_vpn_config()
{
local hssrc=$1
local rtsrc=$2
local hsdst=$3
local rtdst=$4
local tid=$5
local hssrc_name=hs-t${tid}-${hssrc}
local hsdst_name=hs-t${tid}-${hsdst}
local rtsrc_name=rt-${rtsrc}
local rtdst_name=rt-${rtdst}
local vpn_sid=${SID_LOCATOR}:${hssrc}${hsdst}:${tid}::6004
# set the encap route for encapsulating packets which arrive from the
# host hssrc and destined to the access router rtsrc.
ip -netns ${rtsrc_name} -4 route add ${IPv4_HS_NETWORK}.${hsdst}/32 \
encap seg6 mode encap segs ${vpn_sid} dev veth0
ip -netns ${rtsrc_name} -6 route add ${vpn_sid}/128 \
via 2001:11::${rtdst} dev veth0
# set the decap route for decapsulating packets which arrive from
# the rtdst router and destined to the hsdst host.
ip -netns ${rtdst_name} -6 route add ${vpn_sid}/128 \
encap seg6local action End.DX4 nh4 ${IPv4_HS_NETWORK}.${hsdst} dev veth-t${tid}
}
setup()
{
ip link add veth-rt-1 type veth peer name veth-rt-2
# setup the networking for router rt-1 and router rt-2
setup_rt_networking 1
setup_rt_networking 2
# setup two hosts for the tenant 100.
# - host hs-1 is directly connected to the router rt-1;
# - host hs-2 is directly connected to the router rt-2.
setup_hs 1 1 100
setup_hs 2 2 100
# setup the IPv4 L3 VPN which connects the host hs-1 and host hs-2.
setup_vpn_config 1 1 2 2 100 #args: src_host src_router dst_host dst_router tenant
setup_vpn_config 2 2 1 1 100
}
check_hs_connectivity()
{
local hssrc=$1
local hsdst=$2
local tid=$3
ip netns exec hs-${hssrc} ping -c 1 -W ${PING_TIMEOUT_SEC} \
${IPv4_HS_NETWORK}.${hsdst} >/dev/null 2>&1
}
check_and_log_hs_connectivity()
{
local hssrc=$1
local hsdst=$2
local tid=$3
check_hs_connectivity ${hssrc} ${hsdst} ${tid}
log_test $? 0 "Hosts connectivity: hs-${hssrc} -> hs-${hsdst} (tenant ${tid})"
}
host_tests()
{
log_section "SRv6 VPN connectivity test among hosts in the same tenant"
check_and_log_hs_connectivity 1 2 100
check_and_log_hs_connectivity 2 1 100
}
router_netfilter_tests()
{
log_section "SRv6 VPN connectivity test with netfilter enabled in routers"
setup_rt_netfilter 1
setup_rt_netfilter 2
check_and_log_hs_connectivity 1 2 100
check_and_log_hs_connectivity 2 1 100
}
if [ "$(id -u)" -ne 0 ];then
echo "SKIP: Need root privileges"
exit $ksft_skip
fi
if [ ! -x "$(command -v ip)" ]; then
echo "SKIP: Could not run test without ip tool"
exit $ksft_skip
fi
cleanup &>/dev/null
setup
host_tests
router_netfilter_tests
print_log_test_results
cleanup &>/dev/null
exit ${ret}

View File

@ -0,0 +1,340 @@
#!/bin/bash
# SPDX-License-Identifier: GPL-2.0
#
# author: Jianguo Wu <wujianguo@chinatelecom.cn>
#
# Mostly copied from tools/testing/selftests/net/srv6_end_dt6_l3vpn_test.sh.
#
# This script is designed for testing the support of netfilter hooks for
# SRv6 End.DX4 behavior.
#
# Hereafter a network diagram is shown, where one tenants (named 100) offer
# IPv6 L3 VPN services allowing hosts to communicate with each other across
# an IPv6 network.
#
# Routers rt-1 and rt-2 implement IPv6 L3 VPN services leveraging the SRv6
# architecture. The key components for such VPNs are: a) SRv6 Encap behavior,
# b) SRv6 End.DX4 behavior.
#
# To explain how an IPv6 L3 VPN based on SRv6 works, let us briefly consider an
# example where, within the same domain of tenant 100, the host hs-1 pings
# the host hs-2.
#
# First of all, L2 reachability of the host hs-2 is taken into account by
# the router rt-1 which acts as an arp proxy.
#
# When the host hs-1 sends an IPv6 packet destined to hs-2, the router rt-1
# receives the packet on the internal veth-t100 interface, rt-1 contains the
# SRv6 Encap route for encapsulating the IPv6 packet in a IPv6 plus the Segment
# Routing Header (SRH) packet. This packet is sent through the (IPv6) core
# network up to the router rt-2 that receives it on veth0 interface.
#
# The rt-2 router uses the 'localsid' routing table to process incoming
# IPv6+SRH packets which belong to the VPN of the tenant 100. For each of these
# packets, the SRv6 End.DX4 behavior removes the outer IPv6+SRH headers and
# routs the packet to the specified nexthop. Afterwards, the packet is sent to
# the host hs-2 through the veth-t100 interface.
#
# The ping response follows the same processing but this time the role of rt-1
# and rt-2 are swapped.
#
# And when net.netfilter.nf_hooks_lwtunnel is set to 1 in rt-1 or rt-2, and a
# rpfilter iptables rule is added, SRv6 packets will go through netfilter PREROUTING
# hooks.
#
#
# +-------------------+ +-------------------+
# | | | |
# | hs-1 netns | | hs-2 netns |
# | | | |
# | +-------------+ | | +-------------+ |
# | | veth0 | | | | veth0 | |
# | | cafe::1/64 | | | | cafe::2/64 | |
# | +-------------+ | | +-------------+ |
# | . | | . |
# +-------------------+ +-------------------+
# . .
# . .
# . .
# +-----------------------------------+ +-----------------------------------+
# | . | | . |
# | +---------------+ | | +---------------- |
# | | veth-t100 | | | | veth-t100 | |
# | | cafe::11/64 | +----------+ | | +----------+ | cafe::22/64 | |
# | +-------+-------+ | route | | | | route | +-------+-------- |
# | | table | | | | table | |
# | +----------+ | | +----------+ |
# | +--------------+ | | +--------------+ |
# | | veth0 | | | | veth0 | |
# | | 2001:11::1/64 |.|...|.| 2001:11::2/64 | |
# | +--------------+ | | +--------------+ |
# | | | |
# | rt-1 netns | | rt-2 netns |
# | | | |
# +-----------------------------------+ +-----------------------------------+
#
# ~~~~~~~~~~~~~~~~~~~~~~~~~
# | Network configuration |
# ~~~~~~~~~~~~~~~~~~~~~~~~~
#
# rt-1: localsid table
# +----------------------------------------------------------------+
# |SID |Action |
# +----------------------------------------------------------------+
# |fc00:21:100::6004|apply SRv6 End.DX6 nh6 cafe::1 dev veth-t100 |
# +----------------------------------------------------------------+
#
# rt-1: route table
# +---------------------------------------------------+
# |host |Action |
# +---------------------------------------------------+
# |cafe::2 |apply seg6 encap segs fc00:12:100::6004|
# +---------------------------------------------------+
# |cafe::/64 |forward to dev veth_t100 |
# +---------------------------------------------------+
#
#
# rt-2: localsid table
# +---------------------------------------------------------------+
# |SID |Action |
# +---------------------------------------------------------------+
# |fc00:12:100::6004|apply SRv6 End.DX6 nh6 cafe::2 dev veth-t100 |
# +---------------------------------------------------------------+
#
# rt-2: route table
# +---------------------------------------------------+
# |host |Action |
# +---------------------------------------------------+
# |cafe::1 |apply seg6 encap segs fc00:21:100::6004|
# +---------------------------------------------------+
# |cafe::/64 |forward to dev veth_t100 |
# +---------------------------------------------------+
#
# Kselftest framework requirement - SKIP code is 4.
ksft_skip=4
readonly IPv6_RT_NETWORK=2001:11
readonly IPv6_HS_NETWORK=cafe
readonly SID_LOCATOR=fc00
PING_TIMEOUT_SEC=4
ret=0
PAUSE_ON_FAIL=${PAUSE_ON_FAIL:=no}
log_test()
{
local rc=$1
local expected=$2
local msg="$3"
if [ ${rc} -eq ${expected} ]; then
nsuccess=$((nsuccess+1))
printf "\n TEST: %-60s [ OK ]\n" "${msg}"
else
ret=1
nfail=$((nfail+1))
printf "\n TEST: %-60s [FAIL]\n" "${msg}"
if [ "${PAUSE_ON_FAIL}" = "yes" ]; then
echo
echo "hit enter to continue, 'q' to quit"
read a
[ "$a" = "q" ] && exit 1
fi
fi
}
print_log_test_results()
{
if [ "$TESTS" != "none" ]; then
printf "\nTests passed: %3d\n" ${nsuccess}
printf "Tests failed: %3d\n" ${nfail}
fi
}
log_section()
{
echo
echo "################################################################################"
echo "TEST SECTION: $*"
echo "################################################################################"
}
cleanup()
{
ip link del veth-rt-1 2>/dev/null || true
ip link del veth-rt-2 2>/dev/null || true
# destroy routers rt-* and hosts hs-*
for ns in $(ip netns show | grep -E 'rt-*|hs-*'); do
ip netns del ${ns} || true
done
}
# Setup the basic networking for the routers
setup_rt_networking()
{
local rt=$1
local nsname=rt-${rt}
ip netns add ${nsname}
ip netns exec ${nsname} sysctl -wq net.ipv6.conf.all.accept_dad=0
ip netns exec ${nsname} sysctl -wq net.ipv6.conf.default.accept_dad=0
ip link set veth-rt-${rt} netns ${nsname}
ip -netns ${nsname} link set veth-rt-${rt} name veth0
ip -netns ${nsname} addr add ${IPv6_RT_NETWORK}::${rt}/64 dev veth0 nodad
ip -netns ${nsname} link set veth0 up
ip -netns ${nsname} link set lo up
ip netns exec ${nsname} sysctl -wq net.ipv6.conf.all.forwarding=1
}
setup_rt_netfilter()
{
local rt=$1
local nsname=rt-${rt}
ip netns exec ${nsname} sysctl -wq net.netfilter.nf_hooks_lwtunnel=1
ip netns exec ${nsname} ip6tables -t raw -A PREROUTING -m rpfilter --invert -j DROP
}
setup_hs()
{
local hs=$1
local rt=$2
local tid=$3
local hsname=hs-${hs}
local rtname=rt-${rt}
local rtveth=veth-t${tid}
# set the networking for the host
ip netns add ${hsname}
ip -netns ${hsname} link add veth0 type veth peer name ${rtveth}
ip -netns ${hsname} link set ${rtveth} netns ${rtname}
ip -netns ${hsname} addr add ${IPv6_HS_NETWORK}::${hs}/64 dev veth0 nodad
ip -netns ${hsname} link set veth0 up
ip -netns ${hsname} link set lo up
ip -netns ${rtname} addr add ${IPv6_HS_NETWORK}::${rt}${hs}/64 dev ${rtveth}
ip -netns ${rtname} link set ${rtveth} up
ip netns exec ${rtname} sysctl -wq net.ipv6.conf.all.accept_dad=0
ip netns exec ${rtname} sysctl -wq net.ipv6.conf.default.accept_dad=0
ip netns exec ${rtname} sysctl -wq net.ipv6.conf.${rtveth}.proxy_ndp=1
}
setup_vpn_config()
{
local hssrc=$1
local rtsrc=$2
local hsdst=$3
local rtdst=$4
local tid=$5
local hssrc_name=hs-t${tid}-${hssrc}
local hsdst_name=hs-t${tid}-${hsdst}
local rtsrc_name=rt-${rtsrc}
local rtdst_name=rt-${rtdst}
local rtveth=veth-t${tid}
local vpn_sid=${SID_LOCATOR}:${hssrc}${hsdst}:${tid}::6004
ip -netns ${rtsrc_name} -6 neigh add proxy ${IPv6_HS_NETWORK}::${hsdst} dev ${rtveth}
# set the encap route for encapsulating packets which arrive from the
# host hssrc and destined to the access router rtsrc.
ip -netns ${rtsrc_name} -6 route add ${IPv6_HS_NETWORK}::${hsdst}/128 \
encap seg6 mode encap segs ${vpn_sid} dev veth0
ip -netns ${rtsrc_name} -6 route add ${vpn_sid}/128 \
via 2001:11::${rtdst} dev veth0
# set the decap route for decapsulating packets which arrive from
# the rtdst router and destined to the hsdst host.
ip -netns ${rtdst_name} -6 route add ${vpn_sid}/128 \
encap seg6local action End.DX6 nh6 ${IPv6_HS_NETWORK}::${hsdst} dev veth-t${tid}
}
setup()
{
ip link add veth-rt-1 type veth peer name veth-rt-2
# setup the networking for router rt-1 and router rt-2
setup_rt_networking 1
setup_rt_networking 2
# setup two hosts for the tenant 100.
# - host hs-1 is directly connected to the router rt-1;
# - host hs-2 is directly connected to the router rt-2.
setup_hs 1 1 100
setup_hs 2 2 100
# setup the IPv4 L3 VPN which connects the host hs-1 and host hs-2.
setup_vpn_config 1 1 2 2 100 #args: src_host src_router dst_host dst_router tenant
setup_vpn_config 2 2 1 1 100
}
check_hs_connectivity()
{
local hssrc=$1
local hsdst=$2
local tid=$3
ip netns exec hs-${hssrc} ping -6 -c 1 -W ${PING_TIMEOUT_SEC} \
${IPv6_HS_NETWORK}::${hsdst} >/dev/null 2>&1
}
check_and_log_hs_connectivity()
{
local hssrc=$1
local hsdst=$2
local tid=$3
check_hs_connectivity ${hssrc} ${hsdst} ${tid}
log_test $? 0 "Hosts connectivity: hs-${hssrc} -> hs-${hsdst} (tenant ${tid})"
}
host_tests()
{
log_section "SRv6 VPN connectivity test among hosts in the same tenant"
check_and_log_hs_connectivity 1 2 100
check_and_log_hs_connectivity 2 1 100
}
router_netfilter_tests()
{
log_section "SRv6 VPN connectivity test with netfilter enabled in routers"
setup_rt_netfilter 1
setup_rt_netfilter 2
check_and_log_hs_connectivity 1 2 100
check_and_log_hs_connectivity 2 1 100
}
if [ "$(id -u)" -ne 0 ];then
echo "SKIP: Need root privileges"
exit $ksft_skip
fi
if [ ! -x "$(command -v ip)" ]; then
echo "SKIP: Could not run test without ip tool"
exit $ksft_skip
fi
cleanup &>/dev/null
setup
host_tests
router_netfilter_tests
print_log_test_results
cleanup &>/dev/null
exit ${ret}