From patchwork Mon May 6 08:00:40 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Antony Antony X-Patchwork-Id: 13655035 Received: from a.mx.secunet.com (a.mx.secunet.com [62.96.220.36]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id D7A35140E40; Mon, 6 May 2024 08:00:52 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=62.96.220.36 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1714982455; cv=none; b=svXmpL6/NU5mZ2A98QXhoVg3aSTiU+R5tq+GaGJNJEF9XBE5Nn8OZ4S5cMb/n3SM5MXpI6QCDSfQJvcys8SuVpdIBEgoZcX/WuHgicRt/B1ODLxVLe9XLewWJypCFhNbiHjBo9Ta/H5mFA1YycNapDCig5Bvv3v2HK0CjsjiYSw= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1714982455; c=relaxed/simple; bh=KUz72Jz3RA6f8g5ODmR/AHRMyPwA/99eRXklFzCM1AY=; h=Date:From:To:CC:Subject:Message-ID:References:MIME-Version: Content-Type:Content-Disposition:In-Reply-To; b=RVTBZ3WwazoItNAztBt+/Q4i3qO9cmVGY0Mtdv3eBopaDkAYmzmXnTclOq6X7aQ+h+0UJv/d3zjzI+oqQkr0dvHLHuYnaH9H1w8xagyLceJoo20lpvo/24Qd62Q2fEPJ1nDDqZ+MNexPbqHiT+g/WCmCtBgRDaPWBEDuIYf8Xu8= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=secunet.com; spf=pass smtp.mailfrom=secunet.com; dkim=pass (2048-bit key) header.d=secunet.com header.i=@secunet.com header.b=EmtqO/ZV; arc=none smtp.client-ip=62.96.220.36 Authentication-Results: smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=secunet.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=secunet.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=secunet.com header.i=@secunet.com header.b="EmtqO/ZV" Received: from localhost (localhost [127.0.0.1]) by a.mx.secunet.com (Postfix) with ESMTP id CBE2C2074A; Mon, 6 May 2024 10:00:50 +0200 (CEST) X-Virus-Scanned: by secunet Received: from a.mx.secunet.com ([127.0.0.1]) by localhost (a.mx.secunet.com [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id gBfQ50x6JiAh; Mon, 6 May 2024 10:00:47 +0200 (CEST) Received: from mailout2.secunet.com (mailout2.secunet.com [62.96.220.49]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by a.mx.secunet.com (Postfix) with ESMTPS id EA0C1201A0; Mon, 6 May 2024 10:00:46 +0200 (CEST) DKIM-Filter: OpenDKIM Filter v2.11.0 a.mx.secunet.com EA0C1201A0 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=secunet.com; s=202301; t=1714982447; bh=lSEHGladzuqmKPIEerNtXlNa4toMQIllV18os6y7qLI=; h=Date:From:To:CC:Subject:Reply-To:References:In-Reply-To:From; b=EmtqO/ZVDFhXRRJ+b6bM3vLsVQxQc6CcTS+Bv6I274LYlaD7cuKJSdAlScY4e93kU RoTZLBy+n4+MeE299do3jfMEIcvPc1r7SWAtQ/zx0QgEx3IioRBI0yV4HmQwb51MvW X2U4Ff9trH1gQMZRcHvsNt4GoWLvmDVNC+vC7CWOQRLO9UDozr3xWeXxX51BkuQD9O 8rXNRvPnBfV1U72lxAmO4pu8ahVbHQJPgJuvAgYNmApWkyTWopRGLVrugXGul5Ym4I 75sGDAF+WGyQFpA0bGrtEiSq06TODf7faNgFqKtZ04RdS7FlNOMHHosF6ZllEet89L U5APMMYBtvWVg== Received: from cas-essen-02.secunet.de (unknown [10.53.40.202]) by mailout2.secunet.com (Postfix) with ESMTP id DD09C80004A; Mon, 6 May 2024 10:00:46 +0200 (CEST) Received: from mbx-essen-01.secunet.de (10.53.40.197) by cas-essen-02.secunet.de (10.53.40.202) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256) id 15.1.2507.39; Mon, 6 May 2024 10:00:46 +0200 Received: from moon.secunet.de (172.18.149.1) by mbx-essen-01.secunet.de (10.53.40.197) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256) id 15.1.2507.39; Mon, 6 May 2024 10:00:46 +0200 Date: Mon, 6 May 2024 10:00:40 +0200 From: Antony Antony To: Jakub Kicinski , Steffen Klassert , , CC: "David S. Miller" , David Ahern , Eric Dumazet , Paolo Abeni , "Herbert Xu" , Antony Antony , Shuah Khan , Subject: [PATCH net-next v3 1/2] xfrm: fix source address in icmp error generation from IPsec gateway Message-ID: <14c03971406de4effc9cf2ef21cf3921bd8b662d.1714982035.git.antony.antony@secunet.com> Reply-To: References: Precedence: bulk X-Mailing-List: linux-kselftest@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Disposition: inline In-Reply-To: Precedence: first-class Priority: normal Organization: secunet X-ClientProxiedBy: cas-essen-01.secunet.de (10.53.40.201) To mbx-essen-01.secunet.de (10.53.40.197) X-EXCLAIMER-MD-CONFIG: 2c86f778-e09b-4440-8b15-867914633a10 When enabling support for XFRM lookup using reverse ICMP payload, we have identified an issue where the source address of the IPv4, e.g., "Destination Host Unreachable" message, is incorrect. The error message received by the sender appears to originate from a non-existing/unreachable host. IPv6 seems to behave correctly; respond with an existing source address from the gateway. Here is example of incorrect source address for ICMP error response. When sending a ping to an unreachable host, the sender would receive an ICMP unreachable response with a fake source address. Rather the address of the host that generated ICMP Unreachable message. This is confusing and incorrect. A fllow up commit adds this example as a test case. Example: ping -W 9 -w 5 -c 1 10.1.4.3 PING 10.1.4.3 (10.1.4.3) 56(84) bytes of data. From 10.1.4.3 icmp_seq=1 Destination Host Unreachable Notice : packet has the source address of the ICMP "Unreachable host!" This issue can be traced back to commit 415b3334a21a ("icmp: Fix regression in nexthop resolution during replies.") which introduced a change that copied the source address from the ICMP payload. This commit would force to use source address from the gatway/host. The ICMP error message source address correctly set from the host. After fixing: ping -W 5 -c 1 10.1.4.3 PING 10.1.4.3 (10.1.4.3) 56(84) bytes of data. From 10.1.3.2 icmp_seq=1 Destination Host Unreachable Here is an snippt to reporduce the issue. export AB="10.1" for i in 1 2 3 4 5; do h="host${i}" ip netns add ${h} ip -netns ${h} link set lo up ip netns exec ${h} sysctl -wq net.ipv4.ip_forward=1 if [ $i -lt 5 ]; then ip -netns ${h} link add eth0 type veth peer name eth10${i} ip -netns ${h} addr add "${AB}.${i}.1/24" dev eth0 ip -netns ${h} link set up dev eth0 fi done for i in 1 2 3 4 5; do h="host${i}" p=$((i - 1)) ph="host${p}" # connect to previous host if [ $i -gt 1 ]; then ip -netns ${ph} link set eth10${p} netns ${h} ip -netns ${h} link set eth10${p} name eth1 ip -netns ${h} link set up dev eth1 ip -netns ${h} addr add "${AB}.${p}.2/24" dev eth1 fi # add forward routes for k in $(seq ${i} $((5 - 1))); do ip -netns ${h} route 2>/dev/null | (grep "${AB}.${k}.0" 2>/dev/null) || \ ip -netns ${h} route add "${AB}.${k}.0/24" via "${AB}.${i}.2" 2>/dev/nul done # add reverse routes for k in $(seq 1 $((i - 2))); do ip -netns ${h} route 2>/dev/null | grep "${AB}.${k}.0" 2>/dev/null || \ ip -netns ${h} route add "${AB}.${k}.0/24" via "${AB}.${p}.1" 2>/dev/nul done done ip netns exec host1 ping -q -W 2 -w 1 -c 1 10.1.4.2 2>&1>/dev/null && echo "success 10.1.4.2 reachable" || echo "ERROR" ip netns exec host1 ping -W 9 -w 5 -c 1 10.1.4.3 || echo "note the source address of unreachble of gateway" ip -netns host1 route flush cache ip netns exec host3 nft add table inet filter ip netns exec host3 nft add chain inet filter FORWARD { type filter hook forward priority filter\; policy drop \; } ip netns exec host3 nft add rule inet filter FORWARD counter ip protocol icmp drop ip netns exec host3 nft add rule inet filter FORWARD counter ip protocol esp accept ip netns exec host3 nft add rule inet filter FORWARD counter drop ip -netns host2 xfrm policy add src 10.1.1.0/24 dst 10.1.4.0/24 dir out \ flag icmp tmpl src 10.1.2.1 dst 10.1.3.2 proto esp reqid 1 mode tunnel ip -netns host2 xfrm policy add src 10.1.4.0/24 dst 10.1.1.0/24 dir in \ tmpl src 10.1.3.2 dst 10.1.2.1 proto esp reqid 2 mode tunnel ip -netns host2 xfrm policy add src 10.1.4.0/24 dst 10.1.1.0/24 dir fwd \ flag icmp tmpl src 10.1.3.2 dst 10.1.2.1 proto esp reqid 2 mode tunnel ip -netns host2 xfrm state add src 10.1.2.1 dst 10.1.3.2 proto esp spi 1 \ reqid 1 replay-window 1 mode tunnel aead 'rfc4106(gcm(aes))' \ 0x1111111111111111111111111111111111111111 96 \ sel src 10.1.1.0/24 dst 10.1.4.0/24 ip -netns host2 xfrm state add src 10.1.3.2 dst 10.1.2.1 proto esp spi 2 \ flag icmp reqid 2 replay-window 10 mode tunnel aead 'rfc4106(gcm(aes))' \ 0x2222222222222222222222222222222222222222 96 ip -netns host4 xfrm policy add src 10.1.4.0/24 dst 10.1.1.0/24 dir out \ flag icmp tmpl src 10.1.3.2 dst 10.1.2.1 proto esp reqid 1 mode tunnel ip -netns host4 xfrm policy add src 10.1.1.0/24 dst 10.1.4.0/24 dir in \ tmpl src 10.1.2.1 dst 10.1.3.2 proto esp reqid 2 mode tunnel ip -netns host4 xfrm policy add src 10.1.1.0/24 dst 10.1.4.0/24 dir fwd \ flag icmp tmpl src 10.1.2.1 dst 10.1.3.2 proto esp reqid 2 mode tunnel ip -netns host4 xfrm state add src 10.1.3.2 dst 10.1.2.1 proto esp spi 2 \ reqid 1 replay-window 1 mode tunnel aead 'rfc4106(gcm(aes))' \ 0x2222222222222222222222222222222222222222 96 ip -netns host4 xfrm state add src 10.1.2.1 dst 10.1.3.2 proto esp spi 1 \ reqid 2 replay-window 20 flag icmp mode tunnel aead 'rfc4106(gcm(aes))' \ 0x1111111111111111111111111111111111111111 96 \ sel src 10.1.1.0/24 dst 10.1.4.0/24 ip netns exec host1 ping -W 5 -c 1 10.1.4.2 2>&1 > /dev/null && echo "" ip netns exec host1 ping -W 5 -c 1 10.1.4.3 || echo "note source address of gateway 10.1.3.2" Again before the fix ping -W 5 -c 1 10.1.4.3 From 10.1.4.3 icmp_seq=1 Destination Host Unreachable After the fix From 10.1.3.2 icmp_seq=1 Destination Host Unreachable Signed-off-by: Antony Antony Acked-by: Tobias Brunner --- net/ipv4/icmp.c | 1 - 1 file changed, 1 deletion(-) diff --git a/net/ipv4/icmp.c b/net/ipv4/icmp.c index 207482d30dc7..317ad5165408 100644 --- a/net/ipv4/icmp.c +++ b/net/ipv4/icmp.c @@ -558,7 +558,6 @@ static struct rtable *icmp_route_lookup(struct net *net, rt2 = dst_rtable(dst2); if (!IS_ERR(dst2)) { dst_release(&rt->dst); - memcpy(fl4, &fl4_dec, sizeof(*fl4)); rt = rt2; } else if (PTR_ERR(dst2) == -EPERM) { if (rt) From patchwork Mon May 6 08:05:54 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Antony Antony X-Patchwork-Id: 13655086 Received: from a.mx.secunet.com (a.mx.secunet.com [62.96.220.36]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 1BE1674416; Mon, 6 May 2024 08:06:06 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=62.96.220.36 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1714982769; cv=none; b=HMj+Jc36LVsAzMlZ258yQKo4FcEDhqR2AyrXoGmloTxPxwwHiTlrQGbi1lmQJxAuNnz8crfSsLBA9DBfK5KRBfPD36pIDL9hwh4vxNbPHGogtZjyW5PHdYBjWow+uEJvbMAD9ruHQA5effCnWooaB3jK84gyk1Lk8Ud+PViKZas= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1714982769; c=relaxed/simple; bh=bzLlwEre0RIDnWnC7+IGyQQ9XZGI9MMiGD8uW4JQetE=; h=Date:From:To:CC:Subject:Message-ID:References:MIME-Version: Content-Type:Content-Disposition:In-Reply-To; b=YgU4fMNTY3ShDtcOp+rHMdebdFBWkdyxe0hQKDFtQoY++swu9tAEa437UMmkyN7E2jIJ2iVhHKpzu2J6yCeMg019vQbfns7/FzrQ8B0jGNo6czYU/GLHOhgZOaHeDonPqaCdKe17LkxO3w2sn9ka0L3i2uDgHSq2mpLO9HLm3zs= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=secunet.com; spf=pass smtp.mailfrom=secunet.com; dkim=pass (2048-bit key) header.d=secunet.com header.i=@secunet.com header.b=dTuiRCbO; arc=none smtp.client-ip=62.96.220.36 Authentication-Results: smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=secunet.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=secunet.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=secunet.com header.i=@secunet.com header.b="dTuiRCbO" Received: from localhost (localhost [127.0.0.1]) by a.mx.secunet.com (Postfix) with ESMTP id 34F5B2069C; Mon, 6 May 2024 10:06:05 +0200 (CEST) X-Virus-Scanned: by secunet Received: from a.mx.secunet.com ([127.0.0.1]) by localhost (a.mx.secunet.com [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id S5M-pHPe3fjL; Mon, 6 May 2024 10:06:02 +0200 (CEST) Received: from mailout2.secunet.com (mailout2.secunet.com [62.96.220.49]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by a.mx.secunet.com (Postfix) with ESMTPS id C8C76201A0; Mon, 6 May 2024 10:06:02 +0200 (CEST) DKIM-Filter: OpenDKIM Filter v2.11.0 a.mx.secunet.com C8C76201A0 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=secunet.com; s=202301; t=1714982762; bh=q4EFU43GFG61k+aE4BiqZJzQ0kBzFrzsWwOVhv9fg8k=; h=Date:From:To:CC:Subject:Reply-To:References:In-Reply-To:From; b=dTuiRCbOQTyBnw2xnDTyHwVOSX2KjF2BBnFhUAI82XbDs86Ip9P6yR7gh7O8nP4IE Rz33Mp/imC2EpSLei1CiERmvK9VHxcpoxo0m3kx1OPYE2MPIC42qyyVm5ypXEoiOFY YKhKtst0HzXrt1FUx+n00lsYmiGavmfA8FSu32BBDxfkU1G5NZKBchbgrSxTw/568T +PIaVD9XfazDuOOceB6/NaKPGsRemLUhghnNrGhRSGsfM/7twJB5YR59Bi89gI276S IoR1sM5AZ2yByqXQ7Dy4AdbjVDuIzTO9Cgfa5EWwFlZr2xODLVGPXYU7ynQcmSHrpI 8mU7N0QUeGLPA== Received: from cas-essen-02.secunet.de (unknown [10.53.40.202]) by mailout2.secunet.com (Postfix) with ESMTP id BB96D80004A; Mon, 6 May 2024 10:06:02 +0200 (CEST) Received: from mbx-essen-01.secunet.de (10.53.40.197) by cas-essen-02.secunet.de (10.53.40.202) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256) id 15.1.2507.39; Mon, 6 May 2024 10:06:02 +0200 Received: from moon.secunet.de (172.18.149.1) by mbx-essen-01.secunet.de (10.53.40.197) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256) id 15.1.2507.39; Mon, 6 May 2024 10:06:02 +0200 Date: Mon, 6 May 2024 10:05:54 +0200 From: Antony Antony To: Jakub Kicinski , Steffen Klassert , , CC: "David S. Miller" , David Ahern , Eric Dumazet , Paolo Abeni , "Herbert Xu" , Antony Antony , Shuah Khan , Subject: [PATCH net-next v3 2/2] selftests/net: add ICMP unreachable over IPsec tunnel Message-ID: <053f57d79058138d09a0e606c0500a40cb78596d.1714982035.git.antony.antony@secunet.com> Reply-To: References: Precedence: bulk X-Mailing-List: linux-kselftest@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Disposition: inline In-Reply-To: Precedence: first-class Priority: normal Organization: secunet X-ClientProxiedBy: cas-essen-02.secunet.de (10.53.40.202) To mbx-essen-01.secunet.de (10.53.40.197) X-EXCLAIMER-MD-CONFIG: 2c86f778-e09b-4440-8b15-867914633a10 Add IPsec tunnel, aka xfrm state, tests with ICMP flags enabled. IPv4 and IPv6, unreachable tests over xfrm/IPsec tunnels, xfrm SA with "flag icmp" set. Signed-off-by: Antony Antony --- v2->v3: fix the IFS whitespace. It got mangled --- tools/testing/selftests/net/Makefile | 1 + tools/testing/selftests/net/xfrm_state.sh | 624 ++++++++++++++++++++++ 2 files changed, 625 insertions(+) create mode 100755 tools/testing/selftests/net/xfrm_state.sh diff --git a/tools/testing/selftests/net/Makefile b/tools/testing/selftests/net/Makefile index 5befca249452..7d96b3e411b7 100644 --- a/tools/testing/selftests/net/Makefile +++ b/tools/testing/selftests/net/Makefile @@ -53,6 +53,7 @@ TEST_PROGS += bind_bhash.sh TEST_PROGS += ip_local_port_range.sh TEST_PROGS += rps_default_mask.sh TEST_PROGS += big_tcp.sh +TEST_PROGS += xfrm_state.sh TEST_PROGS_EXTENDED := toeplitz_client.sh toeplitz.sh TEST_GEN_FILES = socket nettest TEST_GEN_FILES += psock_fanout psock_tpacket msg_zerocopy reuseport_addr_any diff --git a/tools/testing/selftests/net/xfrm_state.sh b/tools/testing/selftests/net/xfrm_state.sh new file mode 100755 index 000000000000..26eac013abcf --- /dev/null +++ b/tools/testing/selftests/net/xfrm_state.sh @@ -0,0 +1,624 @@ +#!/bin/bash -u +# SPDX-License-Identifier: GPL-2.0 +# +# Checks for xfrm/ESP/IPsec tunnel. +# - The unreachable tests are for icmp error handling. +# As specified in IETF RFC 4301 section 6. +# +# See "test=" below for the implemented tests. +# +# Network topology default +# 10.1.c.d or IPv6 fc00:c::d/64 +# 1.1 1.2 2.1 2.2 3.1 3.2 4.1 4.2 5.1 5.2 6.1 6.2 +# eth0 eth1 eth0 eth1 eth0 eth1 eth0 eth1 eth0 eth1 eth0 eth1 +# a -------- r1 -------- s1 -------- r2 -------- s2 -------- r3 -------- b +# a, b = Alice and Bob hosts without IPsec. +# r1, r2, r3 routers without IPsec +# s1, s2, IPsec gateways/routers that setup tunnel(s). +# +# Network topology: x for IPsec gateway that generate ICMP response. +# 10.1.c.d or IPv6 fc00:c::d/64 +# 1.1 1.2 2.1 2.2 3.1 3.2 4.1 4.2 5.1 5.2 +# eth0 eth1 eth0 eth1 eth0 eth1 eth0 eth1 eth0 eth1 +# a -------- r1 -------- s1 -------- r2 -------- s2 -------- b +# + +source lib.sh + +PAUSE_ON_FAIL=no +VERBOSE=${VERBOSE:-0} +TRACING=0 + +# Name Description +tests=" + unreachable_ipv4 IPv4 unreachable from router r3 + unreachable_ipv4 IPv6 unreachable from router r3 + unreachable_gw_ipv4 IPv4 unreachable from IPsec gateway s2 + unreachable_gw_ipv6 IPv6 unreachable from IPsec gateway s2 + mtu_ipv4_r2 IPv4 MTU exceeded from ESP router r2 + mtu_ipv6_r2 IPv6 MTU exceeded from ESP router r2 + mtu_ipv4_r3 IPv4 MTU exceeded router r3 + mtu_ipv6_r3 IPv6 MTU exceeded router r3" + +ns_set="a r1 s1 r2 s2 r3 b" # Network topology default +imax=7 # number of namespaces in the test + +prefix4="10.1" +prefix6="fc00" + +run_cmd() { + cmd="$*" + + if [ "$VERBOSE" -gt 0 ]; then + printf " COMMAND: $cmd\n" + fi + + out="$($cmd 2>&1)" + rc=$? + if [ "$VERBOSE" -gt 1 -a -n "$out" ]; then + echo " $out" + echo + fi + return $rc +} + +run_test() { + ( + tname="$1" + tdesc="$2" + + + unset IFS + + fail="yes" + + # Since cleanup() relies on variables modified by this sub shell, it + # has to run in this context. + trap cleanup EXIT + + if [ "$VERBOSE" -gt 0 ]; then + printf "\n#####################################################################\n\n" + fi + + # if errexit was not set, set it and unset after test eval + errexit=0 + if [[ $- =~ "e" ]]; then + errexit=1 + else + set -e + fi + + eval test_${tname} + ret=$? + fail="no" + [ $errexit -eq 0 ] && set +e # hack until exception is fixed + + if [ $ret -eq 0 ]; then + printf "TEST: %-60s [ PASS ]\n" "${tdesc}" + elif [ $ret -eq 1 ]; then + printf "TEST: %-60s [FAIL]\n" "${tdesc}" + if [ "$VERBOSE" -eq 0 -o -n "${out}" -o -n "${out}" ]; then + echo "#####################################################################" + [ -n "${cmd}" ] && echo -e "${cmd}" + [ -n "${out}" ] && echo -e "${out}" + echo "#####################################################################" + fi + if [ "${PAUSE_ON_FAIL}" = "yes" ]; then + echo + echo "Pausing. Hit enter to continue" + read a + fi + err_flush + exit 1 + elif [ $ret -eq $ksft_skip ]; then + printf "TEST: %-60s [SKIP]\n" "${tdesc}" + err_flush + fi + + return $ret + ) + ret=$? + case $ret in + 0) + all_skipped=false + [ $exitcode -eq $ksft_skip ] && exitcode=0 + ;; + $ksft_skip) + [ $all_skipped = true ] && exitcode=$ksft_skip + ;; + *) + all_skipped=false + exitcode=1 + ;; + esac + + return $ret +} + +# Find the auto-generated name for this namespace +nsname() { + eval echo ns_$1 +} + +nscmd() { + eval echo "ip netns exec $1" +} + +setup_namespace() { + setup_ns NS_A + ns_a="ip netns exec ${NS_A}" +} + +setup_namespaces() { + local namespaces=""; + + NS_R1="" + NS_R2="" + NS_R3="" + for ns in ${ns_set}; do + n=$(nsname ${ns}) + n=$(echo $n | tr '[:lower:]' '[:upper:]') + namespaces="$namespaces ${n}" + done + + setup_ns $namespaces + + ns_active= #ordered list of namespaces for this test. + + [ -n NS_A ] && ns_a="ip netns exec ${NS_A}" && ns_active="${ns_active} $NS_A" + [ -n NS_R1 ] && ns_r1="ip netns exec ${NS_R1}" && ns_active="${ns_active} $NS_R1" + [ -n NS_S1 ] && ns_s1="ip netns exec ${NS_S1}" && ns_active="${ns_active} $NS_S1" + [ -n NS_R2 ] && ns_r2="ip netns exec ${NS_R2}" && ns_active="${ns_active} $NS_R2" + [ -n NS_S2 ] && ns_s2="ip netns exec ${NS_S2}" && ns_active="${ns_active} $NS_S2" + [ -n NS_R3 ] && ns_r3="ip netns exec ${NS_R3}" && ns_active="${ns_active} $NS_R3" + [ -n NS_B ] && ns_b="ip netns exec ${NS_B}" && ns_active="${ns_active} $NS_B" +} + +setup_addr_add() { + local ns_cmd=$(nscmd $1) + local ip0="$2" + local ip1="$3" + + if [ -n "${ip0}" ]; then + run_cmd ${ns_cmd} ip addr add ${ip0} dev eth0 + run_cmd ${ns_cmd} ip link set up eth0 + fi + if [ -n "${ip1}" ]; then + run_cmd ${ns_cmd} ip addr add ${ip1} dev eth1 + run_cmd ${ns_cmd} ip link set up eth1 + fi + run_cmd ${ns_cmd} sysctl -q net/ipv4/ip_forward=1 + run_cmd ${ns_cmd} sysctl -q net/ipv6/conf/all/forwarding=1 + + # Disable DAD, so that we don't have to wait to use the + # configured IPv6 addresses + run_cmd ${ns_cmd} sysctl -q net/ipv6/conf/default/accept_dad=0 +} + +route_add() { + local ns_cmd=$(nscmd $1) + local nhf=$2 + local nhr=$3 + local i=$4 + + if [ -n "${nhf}" ]; then + # add forward routes + for j in $(seq $((i + 1)) $imax); do + local route="${prefix}${s}${j}${S}0/${prefix_len}" + run_cmd ${ns_cmd} ip route replace "${route} via ${nhf}" + done + fi + + if [ -n "${nhr}" ]; then + # add reverse routes + for j in $(seq 1 $((i - 2))); do + local route="${prefix}${s}${j}${S}0/${prefix_len}" + run_cmd ${ns_cmd} ip route replace "${route} via ${nhr}" + done + fi +} + +veth_add() { + local ns_cmd=$(nscmd $1) + local tn="veth${2}1" + local ln=${3:-eth0} + run_cmd ${ns_cmd} ip link add ${ln} type veth peer name ${tn} +} + +setup_nft_add_icmp_filter() { + local ns_cmd=${ns_r2} + + run_cmd ${ns_cmd} nft add table inet filter + run_cmd ${ns_cmd} nft add chain inet filter FORWARD \ + { type filter hook forward priority filter\; policy drop \; } + run_cmd ${ns_cmd} nft add rule inet filter FORWARD counter ip protocol \ + icmp counter log drop + run_cmd ${ns_cmd} nft add rule inet filter FORWARD counter ip protocol esp \ + counter log accept +} + +setup_nft_add_icmpv6_filter() { + local ns_cmd=${ns_r2} + + run_cmd ${ns_cmd} nft add table inet filter + run_cmd ${ns_cmd} nft add chain inet filter FORWARD { type filter \ + hook forward priority filter\; policy drop \; } + run_cmd ${ns_cmd} nft add rule inet filter FORWARD ip6 nexthdr \ + ipv6-icmp icmpv6 type echo-request counter log drop + run_cmd ${ns_cmd} nft add rule inet filter FORWARD ip6 nexthdr esp \ + counter log accept + run_cmd ${ns_cmd} nft add rule inet filter FORWARD ip6 nexthdr \ + ipv6-icmp icmpv6 type {nd-neighbor-solicit,nd-neighbor-advert,\ + nd-router-solicit,nd-router-advert} counter log accept +} + +veth_mv() { + local ns=$1 + local nsp=$2 + local rn=${4:-eth1} + local tn="veth${3}1" + + run_cmd "$(nscmd ${nsp})" ip link set ${tn} netns ${ns} + run_cmd "$(nscmd ${ns})" ip link set ${tn} name ${rn} +} + +vm_set() { + s1_src=${src} + s1_dst=${dst} + s1_src_net=${src_net} + s1_dst_net=${dst_net} +} + +setup_vm_set_v4() { + src="10.1.3.1" + dst="10.1.4.2" + src_net="10.1.1.0/24" + dst_net="10.1.6.0/24" + + prefix=${prefix4} + prefix_len=24 + s="." + S="." + + vm_set +} + +setup_vm_set_v4x() { + ns_set="a r1 s1 r2 s2 b" # Network topology: x + imax=6 + prefix=${prefix4} + s="." + S="." + src="10.1.3.1" + dst="10.1.4.2" + src_net="10.1.1.0/24" + dst_net="10.1.5.0/24" + prefix_len=24 + + vm_set +} + +setup_vm_set_v6() { + imax=7 + prefix=${prefix6} + s=":" + S="::" + src="fc00:3::1" + dst="fc00:4::2" + src_net="fc00:1::0/64" + dst_net="fc00:6::0/64" + prefix_len=64 + + vm_set +} + +setup_vm_set_v6x() { + ns_set="a r1 s1 r2 s2 b" # Network topology: x + imax=6 + prefix=${prefix6} + s=":" + S="::" + src="fc00:3::1" + dst="fc00:4::2" + src_net="fc00:1::0/64" + dst_net="fc00:5::0/64" + prefix_len=64 + + vm_set +} + +setup_veths() { + i=1 + for ns in ${ns_active}; do + [ ${i} = ${imax} ] && continue + veth_add ${ns} ${i} + i=$((i + 1)) + done + + j=1 + for ns in ${ns_active}; do + if [ ${j} -eq 1 ]; then + p=${ns}; + pj=${j} + j=$((j + 1)) + continue + fi + veth_mv ${ns} "${p}" ${pj} + p=${ns} + pj=${j} + j=$((j + 1)) + done +} + +setup_routes() { + ip1="" + i=1 + for ns in ${ns_active}; do + # 10.1.C.1/24 + ip0="${prefix}${s}${i}${S}1/${prefix_len}" + [ "${ns}" = b ] && ip0="" + setup_addr_add ${ns} "${ip0}" "${ip1}" + # 10.1.C.2/24 + ip1="${prefix}${s}${i}${S}2/${prefix_len}" + i=$((i + 1)) + done + + i=1 + nhr="" + for ns in ${ns_active}; do + nhf="${prefix}${s}${i}${S}2" + [ "${ns}" = b ] && nhf="" + route_add ${ns} "${nhf}" "${nhr}" ${i} + nhr="${prefix}${s}${i}${S}1" + i=$((i + 1)) + done +} + +setup_xfrm() { + + run_cmd ${ns_s1} ip xfrm policy add src ${s1_src_net} dst ${s1_dst_net} dir out \ + tmpl src ${s1_src} dst ${s1_dst} proto esp reqid 1 mode tunnel + + # no "input" policies. we are only doing forwarding. + # run_cmd ${ns_s1} ip xfrm policy add src ${s1_dst_net} dst ${s1_src_net} dir in \ + # flag icmp tmpl src ${s1_dst} dst ${s1_src} proto esp reqid 2 mode tunnel + + run_cmd ${ns_s1} ip xfrm policy add src ${s1_dst_net} dst ${s1_src_net} dir fwd \ + flag icmp tmpl src ${s1_dst} dst ${s1_src} proto esp reqid 2 mode tunnel + + run_cmd ${ns_s1} ip xfrm state add src ${s1_src} dst ${s1_dst} proto esp spi 1 \ + reqid 1 mode tunnel aead 'rfc4106(gcm(aes))' \ + 0x1111111111111111111111111111111111111111 96 \ + sel src ${s1_src_net} dst ${s1_dst_net} + + run_cmd ${ns_s1} ip xfrm state add src ${s1_dst} dst ${s1_src} proto esp spi 2 \ + reqid 2 flag icmp replay-window 8 mode tunnel aead 'rfc4106(gcm(aes))' \ + 0x2222222222222222222222222222222222222222 96 \ + sel src ${s1_dst_net} dst ${s1_src_net} + + run_cmd ${ns_s2} ip xfrm policy add src ${s1_dst_net} dst ${s1_src_net} dir out \ + flag icmp tmpl src ${s1_dst} dst ${s1_src} proto esp reqid 2 mode tunnel + + run_cmd ${ns_s2} ip xfrm policy add src ${s1_src_net} dst ${s1_dst_net} dir fwd \ + tmpl src ${s1_src} dst ${s1_dst} proto esp reqid 1 mode tunnel + + run_cmd ${ns_s2} ip xfrm state add src ${s1_dst} dst ${s1_src} proto esp spi 2 \ + reqid 2 mode tunnel aead 'rfc4106(gcm(aes))' \ + 0x2222222222222222222222222222222222222222 96 \ + sel src ${s1_dst_net} dst ${s1_src_net} + + run_cmd ${ns_s2} ip xfrm state add src ${s1_src} dst ${s1_dst} proto esp spi 1 \ + reqid 1 flag icmp replay-window 8 mode tunnel aead 'rfc4106(gcm(aes))' \ + 0x1111111111111111111111111111111111111111 96 \ + sel src ${s1_src_net} dst ${s1_dst_net} +} + +setup() { + [ "$(id -u)" -ne 0 ] && echo " need to run as root" && return $ksft_skip + + for arg do + eval setup_${arg} || { echo " ${arg} not supported"; return 1; } + done +} + +trace() { + [ $TRACING -eq 0 ] && return + + for arg do + [ "${ns_cmd}" = "" ] && ns_cmd="${arg}" && continue + ns_cmd= + done + sleep 1 +} + +cleanup() { + if [ "${fail}" = "yes" -a -n "${desc}" ]; then + printf "TEST: %-60s [ FAIL ]\n" "${desc}" + [ -n "${cmd}" ] && echo -e "${cmd}\n" + [ -n "${out}" ] && echo -e "${out}\n" + fi + + cleanup_all_ns +} + +mtu() { + ns_cmd="${1}" + dev="${2}" + mtu="${3}" + + ${ns_cmd} ip link set dev ${dev} mtu ${mtu} +} + +mtu_parse() { + input="${1}" + + next=0 + for i in ${input}; do + [ ${next} -eq 1 -a "${i}" = "lock" ] && next=2 && continue + [ ${next} -eq 1 ] && echo "${i}" && return + [ ${next} -eq 2 ] && echo "lock ${i}" && return + [ "${i}" = "mtu" ] && next=1 + done +} + +link_get() { + ns_cmd="${1}" + name="${2}" + + ${ns_cmd} ip link show dev "${name}" +} + +link_get_mtu() { + ns_cmd="${1}" + name="${2}" + + mtu_parse "$(link_get "${ns_cmd}" ${name})" +} + +test_unreachable_ipv6() { + setup vm_set_v6 namespaces veths routes xfrm nft_add_icmpv6_filter || return $ksft_skip + run_cmd ${ns_a} ping -W 5 -w 4 -c 1 fc00:6::2 + run_cmd ${ns_a} ping -W 5 -w 4 -c 1 fc00:6::3 || true + rc=0 + echo -e "$out" | grep -q -E 'From fc00:5::2 icmp_seq.* Destination' || rc=1 + return ${rc} +} + +test_unreachable_gw_ipv6() { + setup vm_set_v6x namespaces veths routes xfrm nft_add_icmpv6_filter || return $ksft_skip + run_cmd ${ns_a} ping -W 5 -w 4 -c 1 fc00:5::2 + run_cmd ${ns_a} ping -W 5 -w 4 -c 1 fc00:5::3 || true + rc=0 + echo -e "$out" | grep -q -E 'From fc00:4::2 icmp_seq.* Destination' || rc=1 + return ${rc} +} + +test_unreachable_gw_ipv4() { + setup vm_set_v4x namespaces veths routes xfrm nft_add_icmp_filter || return $ksft_skip + run_cmd ${ns_a} ping -W 5 -w 4 -c 1 10.1.5.2 + run_cmd ${ns_a} ping -W 5 -w 4 -c 1 10.1.5.3 || true + rc=0 + echo -e "$out" | grep -q -E 'From 10.1.4.2 icmp_seq.* Destination' || rc=1 + return ${rc} +} + +test_unreachable_ipv4() { + setup vm_set_v4 namespaces veths routes xfrm nft_add_icmp_filter || return $ksft_skip + run_cmd ${ns_a} ping -W 5 -w 4 -c 1 10.1.6.2 + run_cmd ${ns_a} ping -W 5 -w 4 -c 1 10.1.6.3 || true + rc=0 + echo -e "$out" | grep -q -E 'From 10.1.5.2 icmp_seq.* Destination' || rc=1 + return ${rc} +} + +test_mtu_ipv4_r2() { + setup vm_set_v4 namespaces veths routes xfrm nft_add_icmp_filter || return $ksft_skip + run_cmd ${ns_a} ping -W 5 -w 4 -c 1 10.1.6.2 + run_cmd ${ns_r2} ip route replace 10.1.3.0/24 dev eth1 src 10.1.3.2 mtu 1300 + run_cmd ${ns_r2} ip route replace 10.1.4.0/24 dev eth0 src 10.1.4.1 mtu 1300 + run_cmd ${ns_a} ping -M do -s 1300 -W 5 -w 4 -c 1 10.1.6.2 || true + rc=0 + # note the error should be s1 not from r2 + echo -e "$out" | grep -q -E "From 10.1.2.2 icmp_seq=.* Frag needed and DF set" || rc=1 + return ${rc} +} + +test_mtu_ipv6_r2() { + setup vm_set_v6 namespaces veths routes xfrm nft_add_icmpv6_filter || return $ksft_skip + run_cmd ${ns_a} ping -W 5 -w 4 -c 1 fc00:6::2 + run_cmd ${ns_r2} ip -6 route replace fc00:3::/64 dev eth1 metric 256 src fc00:3::2 mtu 1300 + run_cmd ${ns_r2} ip -6 route replace fc00:4::/64 dev eth0 metric 256 src fc00:4::1 mtu 1300 + run_cmd ${ns_a} ping -M do -s 1300 -W 5 -w 4 -c 1 fc00:6::2 || true + rc=0 + # note the error should be s1 not from r2 + echo -e "$out" | grep -q -E "From fc00:2::2 icmp_seq=.* Packet too big: mtu=1230" || rc=1 + return ${rc} +} + +test_mtu_ipv4_r3() { + setup vm_set_v4 namespaces veths routes xfrm nft_add_icmp_filter || return $ksft_skip + run_cmd ${ns_a} ping -W 5 -w 4 -c 1 10.1.6.2 + run_cmd ${ns_r3} ip route replace 10.1.6.0/24 dev eth0 src 10.1.6.1 mtu 1300 + run_cmd ${ns_a} ping -M do -s 1350 -W 5 -w 4 -c 1 10.1.6.2 || true + rc=0 + echo -e "$out" | grep -q -E "From 10.1.5.2 icmp_seq=.* Frag needed and DF set \(mtu = 1300\)" || rc=1 + return ${rc} +} + +test_mtu_ipv6_r3() { + setup vm_set_v6 namespaces veths routes xfrm nft_add_icmpv6_filter || return $ksft_skip + run_cmd ${ns_a} ping -W 5 -w 4 -c 1 fc00:6::2 + run_cmd ${ns_r3} ip -6 route replace fc00:6::/64 dev eth1 metric 256 src fc00:6::1 mtu 1300 + run_cmd ${ns_a} ping -M do -s 1300 -W 5 -w 4 -c 1 fc00:6::2 || true + rc=0 + # note the error should be s1 not from r2 + echo -e "$out" | grep -q -E "From fc00:5::2 icmp_seq=.* Packet too big: mtu=1300" || rc=1 + return ${rc} +} + +################################################################################ +# +usage() { + echo + echo "$0 [OPTIONS] [TEST]..." + echo "If no TEST argument is given, all tests will be run." + echo + echo -e "\t-p Pause on fail" + echo -e "\t-v Verbose output. Show commands; -vv Show output also" + echo "Available tests${tests}" + exit 1 +} + +################################################################################ +# +exitcode=0 +desc=0 +all_skipped=true +out= +cmd= + +while getopts :pv o +do + case $o in + p) PAUSE_ON_FAIL=yes;; + v) VERBOSE=$(( VERBOSE + 1 ));; + *) usage;; + esac +done +shift $(($OPTIND-1)) + +IFS=" +" + +for arg do + # Check first that all requested tests are available before running any + command -v > /dev/null "test_${arg}" || { echo "=== Test ${arg} not found"; usage; } +done + +trap cleanup EXIT + +name="" +desc="" +fail="no" + +# end cleanup +cleanup + +for t in ${tests}; do + [ "${name}" = "" ] && name="${t}" && continue + [ "${desc}" = "" ] && desc="${t}" + + run_this=1 + for arg do + [ "${arg}" != "${arg#--*}" ] && continue + [ "${arg}" = "${name}" ] && run_this=1 && break + run_this=0 + done + if [ $run_this -eq 1 ]; then + run_test "${name}" "${desc}" + fi + name="" + desc="" +done + +exit ${exitcode}