From nobody Wed Oct 8 17:34:02 2025 Received: from proxmox-new.maurer-it.com (proxmox-new.maurer-it.com [94.136.29.106]) (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 E603D266562; Wed, 25 Jun 2025 14:26:23 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=94.136.29.106 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1750861587; cv=none; b=CcTudH51m9v575RaZ96YUCSJg9LEV9MMdCZMwHjKnZqz5pGsJayogAA3xr2PECSVry+MldgKmUtANEqqt9fAusetysfKAPD0B6gziTddSC85E4E3VF36l+fDSNRy7pqt2/nr0xvOnBIolZMRQEw2zFOjarO+xtm341wfX5K8dfY= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1750861587; c=relaxed/simple; bh=/9VFx4V/R12ObC+QTUIcUs4r3Yji2iXrxnaQH+uihB0=; h=From:To:Cc:Subject:Date:Message-Id:MIME-Version; b=ePMwJ0m/MuyvrYatfeHUtMwYIlQiIzuMcDgEA0BDqPZA3eGcsL1cZtCh4sqH0v7WT2CGIGfS8cvRkEf7Th5croliUGVjruG2oQ+OnukXSliaG5+V5CbtnbnoiOE4rL+QdFc3ynyP/sFbycdMd0vbhcIiWoLiOVQvTB12RYNroxk= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=proxmox.com; spf=pass smtp.mailfrom=proxmox.com; arc=none smtp.client-ip=94.136.29.106 Authentication-Results: smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=proxmox.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=proxmox.com Received: from proxmox-new.maurer-it.com (localhost.localdomain [127.0.0.1]) by proxmox-new.maurer-it.com (Proxmox) with ESMTP id 8B9074685B; Wed, 25 Jun 2025 16:26:15 +0200 (CEST) From: Gabriel Goller To: "David S. Miller" , Eric Dumazet , Jakub Kicinski , Paolo Abeni , Simon Horman , Jonathan Corbet , David Ahern Cc: Nicolas Dichtel , netdev@vger.kernel.org, linux-doc@vger.kernel.org, linux-kernel@vger.kernel.org Subject: [PATCH] ipv6: add `do_forwarding` sysctl to enable per-interface forwarding Date: Wed, 25 Jun 2025 16:26:06 +0200 Message-Id: <20250625142607.828873-1-g.goller@proxmox.com> X-Mailer: git-send-email 2.39.5 Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" It is currently impossible to enable ipv6 forwarding on a per-interface basis like in ipv4. To enable forwarding on an ipv6 interface we need to enable it on all interfaces and disable it on the other interfaces using a netfilter rule. This is especially cumbersome if you have lots of interface and only want to enable forwarding on a few. According to the sysctl docs [0] the `net.ipv6.conf.all.forwarding` enables forwarding for all interfaces, while the interface-specific `net.ipv6.conf..forwarding` configures the interface Host/Router configuration. Introduce a new sysctl flag `do_forwarding`, which can be set on every interface. The ip6_forwarding function will then check if the global forwarding flag OR the do_forwarding flag is active and forward the packet. To preserver backwards-compatibility also reset the flag on all interfaces when setting the global forwarding flag to 0. [0]: https://www.kernel.org/doc/Documentation/networking/ip-sysctl.txt Signed-off-by: Gabriel Goller --- * I don't have any hard feelings about the naming, Nicolas Dichtel proposed `fwd_per_iface` but I think `do_forwarding` is a better fit. * I'm also not sure about the reset when setting the global forwarding flag; don't know if I did that right. Feedback is welcome! * Thanks for the help! Documentation/networking/ip-sysctl.rst | 5 +++++ include/linux/ipv6.h | 1 + include/uapi/linux/ipv6.h | 1 + include/uapi/linux/sysctl.h | 1 + net/ipv6/addrconf.c | 21 +++++++++++++++++++++ net/ipv6/ip6_output.c | 3 ++- 6 files changed, 31 insertions(+), 1 deletion(-) diff --git a/Documentation/networking/ip-sysctl.rst b/Documentation/network= ing/ip-sysctl.rst index 0f1251cce314..fa966a710e21 100644 --- a/Documentation/networking/ip-sysctl.rst +++ b/Documentation/networking/ip-sysctl.rst @@ -2292,6 +2292,11 @@ conf/all/forwarding - BOOLEAN proxy_ndp - BOOLEAN Do proxy ndp. =20 +do_forwarding - BOOLEAN + Enable forwarding on this interface only -- regardless of the setting on + ``conf/all/forwarding``. When setting ``conf.all.forwarding`` to 0, + the `do_forwarding` flag will be reset on all interfaces. + fwmark_reflect - BOOLEAN Controls the fwmark of kernel-generated IPv6 reply packets that are not associated with a socket for example, TCP RSTs or ICMPv6 echo replies). diff --git a/include/linux/ipv6.h b/include/linux/ipv6.h index 5aeeed22f35b..74d7cfbb8f83 100644 --- a/include/linux/ipv6.h +++ b/include/linux/ipv6.h @@ -19,6 +19,7 @@ struct ipv6_devconf { __s32 forwarding; __s32 disable_policy; __s32 proxy_ndp; + __u8 do_forwarding; __cacheline_group_end(ipv6_devconf_read_txrx); =20 __s32 accept_ra; diff --git a/include/uapi/linux/ipv6.h b/include/uapi/linux/ipv6.h index cf592d7b630f..66147838bb83 100644 --- a/include/uapi/linux/ipv6.h +++ b/include/uapi/linux/ipv6.h @@ -199,6 +199,7 @@ enum { DEVCONF_NDISC_EVICT_NOCARRIER, DEVCONF_ACCEPT_UNTRACKED_NA, DEVCONF_ACCEPT_RA_MIN_LFT, + DEVCONF_DO_FORWARDING, DEVCONF_MAX }; =20 diff --git a/include/uapi/linux/sysctl.h b/include/uapi/linux/sysctl.h index 8981f00204db..d540689910ec 100644 --- a/include/uapi/linux/sysctl.h +++ b/include/uapi/linux/sysctl.h @@ -573,6 +573,7 @@ enum { NET_IPV6_ACCEPT_RA_FROM_LOCAL=3D26, NET_IPV6_ACCEPT_RA_RT_INFO_MIN_PLEN=3D27, NET_IPV6_RA_DEFRTR_METRIC=3D28, + NET_IPV6_DO_FORWARDING=3D29, __NET_IPV6_MAX }; =20 diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c index ba2ec7c870cc..2f0c68428f63 100644 --- a/net/ipv6/addrconf.c +++ b/net/ipv6/addrconf.c @@ -239,6 +239,7 @@ static struct ipv6_devconf ipv6_devconf __read_mostly = =3D { .ndisc_evict_nocarrier =3D 1, .ra_honor_pio_life =3D 0, .ra_honor_pio_pflag =3D 0, + .do_forwarding =3D 0, }; =20 static struct ipv6_devconf ipv6_devconf_dflt __read_mostly =3D { @@ -303,6 +304,7 @@ static struct ipv6_devconf ipv6_devconf_dflt __read_mos= tly =3D { .ndisc_evict_nocarrier =3D 1, .ra_honor_pio_life =3D 0, .ra_honor_pio_pflag =3D 0, + .do_forwarding =3D 0, }; =20 /* Check if link is ready: is it up and is a valid qdisc available */ @@ -857,6 +859,15 @@ static void addrconf_forward_change(struct net *net, _= _s32 newf) idev =3D __in6_dev_get_rtnl_net(dev); if (idev) { int changed =3D (!idev->cnf.forwarding) ^ (!newf); + /* + * With the introduction of do_forwarding, we need to be backwards + * compatible, so that means we need to set the do_forwarding flag + * on every interface to 0 if net.ipv6.conf.all.forwarding is set to 0. + * This allows the global forwarding flag to disable forwarding for + * all interfaces. + */ + if (newf =3D=3D 0) + WRITE_ONCE(idev->cnf.do_forwarding, newf); =20 WRITE_ONCE(idev->cnf.forwarding, newf); if (changed) @@ -5719,6 +5730,7 @@ static void ipv6_store_devconf(const struct ipv6_devc= onf *cnf, array[DEVCONF_ACCEPT_UNTRACKED_NA] =3D READ_ONCE(cnf->accept_untracked_na); array[DEVCONF_ACCEPT_RA_MIN_LFT] =3D READ_ONCE(cnf->accept_ra_min_lft); + array[DEVCONF_DO_FORWARDING] =3D READ_ONCE(cnf->do_forwarding); } =20 static inline size_t inet6_ifla6_size(void) @@ -7217,6 +7229,15 @@ static const struct ctl_table addrconf_sysctl[] =3D { .extra1 =3D SYSCTL_ZERO, .extra2 =3D SYSCTL_TWO, }, + { + .procname =3D "do_forwarding", + .data =3D &ipv6_devconf.do_forwarding, + .maxlen =3D sizeof(u8), + .mode =3D 0644, + .proc_handler =3D proc_dou8vec_minmax, + .extra1 =3D SYSCTL_ZERO, + .extra2 =3D SYSCTL_ONE, + }, }; =20 static int __addrconf_sysctl_register(struct net *net, char *dev_name, diff --git a/net/ipv6/ip6_output.c b/net/ipv6/ip6_output.c index 7bd29a9ff0db..a75bbf54157e 100644 --- a/net/ipv6/ip6_output.c +++ b/net/ipv6/ip6_output.c @@ -509,7 +509,8 @@ int ip6_forward(struct sk_buff *skb) u32 mtu; =20 idev =3D __in6_dev_get_safely(dev_get_by_index_rcu(net, IP6CB(skb)->iif)); - if (READ_ONCE(net->ipv6.devconf_all->forwarding) =3D=3D 0) + if ((idev && READ_ONCE(idev->cnf.do_forwarding) =3D=3D 0) && + READ_ONCE(net->ipv6.devconf_all->forwarding) =3D=3D 0) goto error; =20 if (skb->pkt_type !=3D PACKET_HOST) --=20 2.39.5