linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH v3] ipv6: add `force_forwarding` sysctl to enable per-interface forwarding
@ 2025-07-02  7:46 Gabriel Goller
  2025-07-02 10:05 ` Nicolas Dichtel
  2025-07-02 14:34 ` Jakub Kicinski
  0 siblings, 2 replies; 13+ messages in thread
From: Gabriel Goller @ 2025-07-02  7:46 UTC (permalink / raw)
  To: Nicolas Dichtel, David S. Miller, Eric Dumazet, Jakub Kicinski,
	Paolo Abeni, Simon Horman, Jonathan Corbet, David Ahern
  Cc: netdev, linux-doc, linux-kernel

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.<interface>.forwarding` configures the interface
Host/Router configuration.

Introduce a new sysctl flag `force_forwarding`, which can be set on every
interface. The ip6_forwarding function will then check if the global
forwarding flag OR the force_forwarding flag is active and forward the
packet.

To preserver backwards-compatibility reset the flag (on all interfaces)
to 0 if the net.ipv6.conf.all.forwarding flag is set to 0.

[0]: https://www.kernel.org/doc/Documentation/networking/ip-sysctl.txt

Signed-off-by: Gabriel Goller <g.goller@proxmox.com>
---

Changelog:
v2 -> v3:
    * remove forwarding=0 setting force_forwarding=0 globally.
    * add min and max (0 and 1) value to sysctl.
v1 -> v2:
    * rename from `do_forwarding` to `force_forwarding`.
    * add global `force_forwarding` flag which will enable
      `force_forwarding` on every interface like the
      `ipv4.all.forwarding` flag.
    * `forwarding`=0 will disable global and per-interface
      `force_forwarding`.
    * export option as NETCONFA_FORCE_FORWARDING.

 Documentation/networking/ip-sysctl.rst |  5 ++
 include/linux/ipv6.h                   |  1 +
 include/uapi/linux/ipv6.h              |  1 +
 include/uapi/linux/netconf.h           |  1 +
 include/uapi/linux/sysctl.h            |  1 +
 net/ipv6/addrconf.c                    | 90 ++++++++++++++++++++++++++
 net/ipv6/ip6_output.c                  |  3 +-
 7 files changed, 101 insertions(+), 1 deletion(-)

diff --git a/Documentation/networking/ip-sysctl.rst b/Documentation/networking/ip-sysctl.rst
index 0f1251cce314..f709aed44cde 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.
 
+force_forwarding - BOOLEAN
+	Enable forwarding on this interface only -- regardless of the setting on
+	``conf/all/forwarding``. When setting ``conf.all.forwarding`` to 0,
+	the `force_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..5380107e466c 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;
+	__s32		force_forwarding;
 	__cacheline_group_end(ipv6_devconf_read_txrx);
 
 	__s32		accept_ra;
diff --git a/include/uapi/linux/ipv6.h b/include/uapi/linux/ipv6.h
index cf592d7b630f..d4d3ae774b26 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_FORCE_FORWARDING,
 	DEVCONF_MAX
 };
 
diff --git a/include/uapi/linux/netconf.h b/include/uapi/linux/netconf.h
index fac4edd55379..1c8c84d65ae3 100644
--- a/include/uapi/linux/netconf.h
+++ b/include/uapi/linux/netconf.h
@@ -19,6 +19,7 @@ enum {
 	NETCONFA_IGNORE_ROUTES_WITH_LINKDOWN,
 	NETCONFA_INPUT,
 	NETCONFA_BC_FORWARDING,
+	NETCONFA_FORCE_FORWARDING,
 	__NETCONFA_MAX
 };
 #define NETCONFA_MAX	(__NETCONFA_MAX - 1)
diff --git a/include/uapi/linux/sysctl.h b/include/uapi/linux/sysctl.h
index 8981f00204db..63d1464cb71c 100644
--- a/include/uapi/linux/sysctl.h
+++ b/include/uapi/linux/sysctl.h
@@ -573,6 +573,7 @@ enum {
 	NET_IPV6_ACCEPT_RA_FROM_LOCAL=26,
 	NET_IPV6_ACCEPT_RA_RT_INFO_MIN_PLEN=27,
 	NET_IPV6_RA_DEFRTR_METRIC=28,
+	NET_IPV6_FORCE_FORWARDING=29,
 	__NET_IPV6_MAX
 };
 
diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c
index ba2ec7c870cc..cca78f75cf0c 100644
--- a/net/ipv6/addrconf.c
+++ b/net/ipv6/addrconf.c
@@ -239,6 +239,7 @@ static struct ipv6_devconf ipv6_devconf __read_mostly = {
 	.ndisc_evict_nocarrier	= 1,
 	.ra_honor_pio_life	= 0,
 	.ra_honor_pio_pflag	= 0,
+	.force_forwarding	= 0,
 };
 
 static struct ipv6_devconf ipv6_devconf_dflt __read_mostly = {
@@ -303,6 +304,7 @@ static struct ipv6_devconf ipv6_devconf_dflt __read_mostly = {
 	.ndisc_evict_nocarrier	= 1,
 	.ra_honor_pio_life	= 0,
 	.ra_honor_pio_pflag	= 0,
+	.force_forwarding	= 0,
 };
 
 /* 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 = __in6_dev_get_rtnl_net(dev);
 		if (idev) {
 			int changed = (!idev->cnf.forwarding) ^ (!newf);
+			/*
+			 * With the introduction of force_forwarding, we need to be backwards
+			 * compatible, so that means we need to set the force_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 == 0)
+				WRITE_ONCE(idev->cnf.force_forwarding, newf);
 
 			WRITE_ONCE(idev->cnf.forwarding, newf);
 			if (changed)
@@ -5719,6 +5730,7 @@ static void ipv6_store_devconf(const struct ipv6_devconf *cnf,
 	array[DEVCONF_ACCEPT_UNTRACKED_NA] =
 		READ_ONCE(cnf->accept_untracked_na);
 	array[DEVCONF_ACCEPT_RA_MIN_LFT] = READ_ONCE(cnf->accept_ra_min_lft);
+	array[DEVCONF_FORCE_FORWARDING] = READ_ONCE(cnf->force_forwarding);
 }
 
 static inline size_t inet6_ifla6_size(void)
@@ -6747,6 +6759,77 @@ static int addrconf_sysctl_disable_policy(const struct ctl_table *ctl, int write
 	return ret;
 }
 
+/* called with RTNL locked */
+static void addrconf_force_forward_change(struct net *net, __s32 newf)
+{
+	struct net_device *dev;
+	struct inet6_dev *idev;
+
+	for_each_netdev(net, dev) {
+		idev = __in6_dev_get_rtnl_net(dev);
+		if (idev) {
+			int changed = (!idev->cnf.force_forwarding) ^ (!newf);
+
+			WRITE_ONCE(idev->cnf.force_forwarding, newf);
+			if (changed) {
+				inet6_netconf_notify_devconf(dev_net(dev), RTM_NEWNETCONF,
+							     NETCONFA_FORCE_FORWARDING,
+							     dev->ifindex, &idev->cnf);
+			}
+		}
+	}
+}
+
+static int addrconf_sysctl_force_forwarding(const struct ctl_table *ctl, int write,
+					    void *buffer, size_t *lenp, loff_t *ppos)
+{
+	int *valp = ctl->data;
+	int ret;
+	int old, new;
+
+	// get extra params from table
+	struct inet6_dev *idev = ctl->extra1;
+	struct net *net = ctl->extra2;
+
+	// copy table and change extra params to min/max so we can use proc_douintvec_minmax
+	struct ctl_table lctl;
+
+	lctl = *ctl;
+	lctl.extra1 = SYSCTL_ZERO;
+	lctl.extra2 = SYSCTL_ONE;
+
+	old = *valp;
+	ret = proc_douintvec_minmax(&lctl, write, buffer, lenp, ppos);
+	new = *valp;
+
+	if (write && old != new) {
+		if (!rtnl_net_trylock(net))
+			return restart_syscall();
+
+		if (valp == &net->ipv6.devconf_dflt->force_forwarding) {
+			inet6_netconf_notify_devconf(net, RTM_NEWNETCONF,
+						     NETCONFA_FORCE_FORWARDING,
+						     NETCONFA_IFINDEX_DEFAULT,
+						     net->ipv6.devconf_dflt);
+		} else if (valp == &net->ipv6.devconf_all->force_forwarding) {
+			inet6_netconf_notify_devconf(net, RTM_NEWNETCONF,
+						     NETCONFA_FORCE_FORWARDING,
+						     NETCONFA_IFINDEX_ALL,
+						     net->ipv6.devconf_all);
+
+			addrconf_force_forward_change(net, new);
+		} else {
+			inet6_netconf_notify_devconf(net, RTM_NEWNETCONF,
+						     NETCONFA_FORCE_FORWARDING,
+						     idev->dev->ifindex,
+						     &idev->cnf);
+		}
+		rtnl_net_unlock(net);
+	}
+
+	return ret;
+}
+
 static int minus_one = -1;
 static const int two_five_five = 255;
 static u32 ioam6_if_id_max = U16_MAX;
@@ -7217,6 +7300,13 @@ static const struct ctl_table addrconf_sysctl[] = {
 		.extra1		= SYSCTL_ZERO,
 		.extra2		= SYSCTL_TWO,
 	},
+	{
+		.procname	= "force_forwarding",
+		.data		= &ipv6_devconf.force_forwarding,
+		.maxlen		= sizeof(int),
+		.mode		= 0644,
+		.proc_handler	= addrconf_sysctl_force_forwarding,
+	},
 };
 
 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..c15ed4197416 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;
 
 	idev = __in6_dev_get_safely(dev_get_by_index_rcu(net, IP6CB(skb)->iif));
-	if (READ_ONCE(net->ipv6.devconf_all->forwarding) == 0)
+	if ((idev && READ_ONCE(idev->cnf.force_forwarding) == 0) &&
+	    READ_ONCE(net->ipv6.devconf_all->forwarding) == 0)
 		goto error;
 
 	if (skb->pkt_type != PACKET_HOST)
-- 
2.39.5



^ permalink raw reply related	[flat|nested] 13+ messages in thread

* Re: [PATCH v3] ipv6: add `force_forwarding` sysctl to enable per-interface forwarding
  2025-07-02  7:46 [PATCH v3] ipv6: add `force_forwarding` sysctl to enable per-interface forwarding Gabriel Goller
@ 2025-07-02 10:05 ` Nicolas Dichtel
  2025-07-02 22:26   ` Randy Dunlap
  2025-07-03 11:04   ` Gabriel Goller
  2025-07-02 14:34 ` Jakub Kicinski
  1 sibling, 2 replies; 13+ messages in thread
From: Nicolas Dichtel @ 2025-07-02 10:05 UTC (permalink / raw)
  To: Gabriel Goller, David S. Miller, Eric Dumazet, Jakub Kicinski,
	Paolo Abeni, Simon Horman, Jonathan Corbet, David Ahern
  Cc: netdev, linux-doc, linux-kernel

Le 02/07/2025 à 09:46, Gabriel Goller a écrit :
> 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.<interface>.forwarding` configures the interface
> Host/Router configuration.
> 
> Introduce a new sysctl flag `force_forwarding`, which can be set on every
> interface. The ip6_forwarding function will then check if the global
> forwarding flag OR the force_forwarding flag is active and forward the
> packet.
> 
> To preserver backwards-compatibility reset the flag (on all interfaces)
> to 0 if the net.ipv6.conf.all.forwarding flag is set to 0.
> 
> [0]: https://www.kernel.org/doc/Documentation/networking/ip-sysctl.txt
> 
> Signed-off-by: Gabriel Goller <g.goller@proxmox.com>
> ---
> 
Please, wait 24 hours before reposting.
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/Documentation/process/maintainer-netdev.rst#n419


[snip]

> @@ -6747,6 +6759,77 @@ static int addrconf_sysctl_disable_policy(const struct ctl_table *ctl, int write
>  	return ret;
>  }
>  
> +/* called with RTNL locked */
Instead of a comment ...

> +static void addrconf_force_forward_change(struct net *net, __s32 newf)
> +{
> +	struct net_device *dev;
> +	struct inet6_dev *idev;
> +
... put

	ASSERT_RTNL();

> +	for_each_netdev(net, dev) {
> +		idev = __in6_dev_get_rtnl_net(dev);
> +		if (idev) {
> +			int changed = (!idev->cnf.force_forwarding) ^ (!newf);
> +
> +			WRITE_ONCE(idev->cnf.force_forwarding, newf);
> +			if (changed) {
> +				inet6_netconf_notify_devconf(dev_net(dev), RTM_NEWNETCONF,
> +							     NETCONFA_FORCE_FORWARDING,
> +							     dev->ifindex, &idev->cnf);
> +			}
> +		}
> +	}
> +}
> +
> +static int addrconf_sysctl_force_forwarding(const struct ctl_table *ctl, int write,
> +					    void *buffer, size_t *lenp, loff_t *ppos)
> +{
> +	int *valp = ctl->data;
> +	int ret;
> +	int old, new;
> +
> +	// get extra params from table
/* */ for comment
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/Documentation/process/coding-style.rst#n598

> +	struct inet6_dev *idev = ctl->extra1;
> +	struct net *net = ctl->extra2;
Reverse x-mas tree for the variables declaration
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/Documentation/process/maintainer-netdev.rst#n368

> +
> +	// copy table and change extra params to min/max so we can use proc_douintvec_minmax
> +	struct ctl_table lctl;
> +
> +	lctl = *ctl;
> +	lctl.extra1 = SYSCTL_ZERO;
> +	lctl.extra2 = SYSCTL_ONE;
> +
> +	old = *valp;
> +	ret = proc_douintvec_minmax(&lctl, write, buffer, lenp, ppos);
> +	new = *valp;
I probably missed something. The new value is written in lctl. When is it
written in ctl?

> +
> +	if (write && old != new) {
> +		if (!rtnl_net_trylock(net))
> +			return restart_syscall();
> +
> +		if (valp == &net->ipv6.devconf_dflt->force_forwarding) {
> +			inet6_netconf_notify_devconf(net, RTM_NEWNETCONF,
> +						     NETCONFA_FORCE_FORWARDING,
> +						     NETCONFA_IFINDEX_DEFAULT,
> +						     net->ipv6.devconf_dflt);
> +		} else if (valp == &net->ipv6.devconf_all->force_forwarding) {
> +			inet6_netconf_notify_devconf(net, RTM_NEWNETCONF,
> +						     NETCONFA_FORCE_FORWARDING,
> +						     NETCONFA_IFINDEX_ALL,
> +						     net->ipv6.devconf_all);
> +
> +			addrconf_force_forward_change(net, new);
> +		} else {
> +			inet6_netconf_notify_devconf(net, RTM_NEWNETCONF,
> +						     NETCONFA_FORCE_FORWARDING,
> +						     idev->dev->ifindex,
> +						     &idev->cnf);
> +		}
> +		rtnl_net_unlock(net);
> +	}
> +
> +	return ret;
> +}
> +
>  static int minus_one = -1;
>  static const int two_five_five = 255;
>  static u32 ioam6_if_id_max = U16_MAX;

^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [PATCH v3] ipv6: add `force_forwarding` sysctl to enable per-interface forwarding
  2025-07-02  7:46 [PATCH v3] ipv6: add `force_forwarding` sysctl to enable per-interface forwarding Gabriel Goller
  2025-07-02 10:05 ` Nicolas Dichtel
@ 2025-07-02 14:34 ` Jakub Kicinski
  2025-07-02 15:14   ` Nicolas Dichtel
  1 sibling, 1 reply; 13+ messages in thread
From: Jakub Kicinski @ 2025-07-02 14:34 UTC (permalink / raw)
  To: Gabriel Goller, Nicolas Dichtel
  Cc: David S. Miller, Eric Dumazet, Paolo Abeni, Simon Horman,
	Jonathan Corbet, David Ahern, netdev, linux-doc, linux-kernel

On Wed,  2 Jul 2025 09:46:18 +0200 Gabriel Goller wrote:
> 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.<interface>.forwarding` configures the interface
> Host/Router configuration.
> 
> Introduce a new sysctl flag `force_forwarding`, which can be set on every
> interface. The ip6_forwarding function will then check if the global
> forwarding flag OR the force_forwarding flag is active and forward the
> packet.

Should we invert the polarity? It appears that the condition below only
let's this setting _disable_ forwarding. IMO calling it "force" suggests
to the user that it will force it to be enabled.

Nicolas, how do you feel about asking for a selftest here? 
The functionality is fairly trivial from datapath PoV, but feels odd 
to merge uAPI these days without a selftest..

^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [PATCH v3] ipv6: add `force_forwarding` sysctl to enable per-interface forwarding
  2025-07-02 14:34 ` Jakub Kicinski
@ 2025-07-02 15:14   ` Nicolas Dichtel
  2025-07-02 16:10     ` Jakub Kicinski
  0 siblings, 1 reply; 13+ messages in thread
From: Nicolas Dichtel @ 2025-07-02 15:14 UTC (permalink / raw)
  To: Jakub Kicinski, Gabriel Goller
  Cc: David S. Miller, Eric Dumazet, Paolo Abeni, Simon Horman,
	Jonathan Corbet, David Ahern, netdev, linux-doc, linux-kernel

Le 02/07/2025 à 16:34, Jakub Kicinski a écrit :
> On Wed,  2 Jul 2025 09:46:18 +0200 Gabriel Goller wrote:
>> 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.<interface>.forwarding` configures the interface
>> Host/Router configuration.
>>
>> Introduce a new sysctl flag `force_forwarding`, which can be set on every
>> interface. The ip6_forwarding function will then check if the global
>> forwarding flag OR the force_forwarding flag is active and forward the
>> packet.
> 
> Should we invert the polarity? It appears that the condition below only
> let's this setting _disable_ forwarding. IMO calling it "force" suggests
> to the user that it will force it to be enabled.
Not sure to follow you. When force_forwarding is set to 1 the forwarding is
always enabled.

sysctl | all.forwarding | iface.force_forwarding | packet processing from iface
       |      0         |           0            |        no forward
       |      0         |           1            |         forward
       |      1         |           0            |         forward
       |      1         |           1            |         forward

> 
> Nicolas, how do you feel about asking for a selftest here? 
> The functionality is fairly trivial from datapath PoV, but feels odd 
> to merge uAPI these days without a selftest..
No problem, let's do it right.

^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [PATCH v3] ipv6: add `force_forwarding` sysctl to enable per-interface forwarding
  2025-07-02 15:14   ` Nicolas Dichtel
@ 2025-07-02 16:10     ` Jakub Kicinski
  2025-07-02 16:17       ` Nicolas Dichtel
  2025-07-03 11:05       ` Gabriel Goller
  0 siblings, 2 replies; 13+ messages in thread
From: Jakub Kicinski @ 2025-07-02 16:10 UTC (permalink / raw)
  To: Nicolas Dichtel
  Cc: Gabriel Goller, David S. Miller, Eric Dumazet, Paolo Abeni,
	Simon Horman, Jonathan Corbet, David Ahern, netdev, linux-doc,
	linux-kernel

On Wed, 2 Jul 2025 17:14:42 +0200 Nicolas Dichtel wrote:
> > Should we invert the polarity? It appears that the condition below only
> > let's this setting _disable_ forwarding. IMO calling it "force" suggests
> > to the user that it will force it to be enabled.  
> Not sure to follow you. When force_forwarding is set to 1 the forwarding is
> always enabled.
> 
> sysctl | all.forwarding | iface.force_forwarding | packet processing from iface
>        |      0         |           0            |        no forward
>        |      0         |           1            |         forward
>        |      1         |           0            |         forward
>        |      1         |           1            |         forward

Ugh, I can't read comparisons to zero.
Let's switch to more sane logic:

	if (idev && !READ_ONCE(idev->cnf.force_forwarding) &&
	    !READ_ONCE(net->ipv6.devconf_all->forwarding))

^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [PATCH v3] ipv6: add `force_forwarding` sysctl to enable per-interface forwarding
  2025-07-02 16:10     ` Jakub Kicinski
@ 2025-07-02 16:17       ` Nicolas Dichtel
  2025-07-03 11:05       ` Gabriel Goller
  1 sibling, 0 replies; 13+ messages in thread
From: Nicolas Dichtel @ 2025-07-02 16:17 UTC (permalink / raw)
  To: Jakub Kicinski
  Cc: Gabriel Goller, David S. Miller, Eric Dumazet, Paolo Abeni,
	Simon Horman, Jonathan Corbet, David Ahern, netdev, linux-doc,
	linux-kernel

Le 02/07/2025 à 18:10, Jakub Kicinski a écrit :
> On Wed, 2 Jul 2025 17:14:42 +0200 Nicolas Dichtel wrote:
>>> Should we invert the polarity? It appears that the condition below only
>>> let's this setting _disable_ forwarding. IMO calling it "force" suggests
>>> to the user that it will force it to be enabled.  
>> Not sure to follow you. When force_forwarding is set to 1 the forwarding is
>> always enabled.
>>
>> sysctl | all.forwarding | iface.force_forwarding | packet processing from iface
>>        |      0         |           0            |        no forward
>>        |      0         |           1            |         forward
>>        |      1         |           0            |         forward
>>        |      1         |           1            |         forward
> 
> Ugh, I can't read comparisons to zero.
> Let's switch to more sane logic:
> 
> 	if (idev && !READ_ONCE(idev->cnf.force_forwarding) &&
> 	    !READ_ONCE(net->ipv6.devconf_all->forwarding))
+1

^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [PATCH v3] ipv6: add `force_forwarding` sysctl to enable per-interface forwarding
  2025-07-02 10:05 ` Nicolas Dichtel
@ 2025-07-02 22:26   ` Randy Dunlap
  2025-07-03  6:58     ` Nicolas Dichtel
  2025-07-03 11:04   ` Gabriel Goller
  1 sibling, 1 reply; 13+ messages in thread
From: Randy Dunlap @ 2025-07-02 22:26 UTC (permalink / raw)
  To: nicolas.dichtel, Gabriel Goller, David S. Miller, Eric Dumazet,
	Jakub Kicinski, Paolo Abeni, Simon Horman, Jonathan Corbet,
	David Ahern
  Cc: netdev, linux-doc, linux-kernel



On 7/2/25 3:05 AM, Nicolas Dichtel wrote:
> Le 02/07/2025 à 09:46, Gabriel Goller a écrit :
>> 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.<interface>.forwarding` configures the interface
>> Host/Router configuration.
>>
>> Introduce a new sysctl flag `force_forwarding`, which can be set on every
>> interface. The ip6_forwarding function will then check if the global
>> forwarding flag OR the force_forwarding flag is active and forward the
>> packet.
>>
>> To preserver backwards-compatibility reset the flag (on all interfaces)
>> to 0 if the net.ipv6.conf.all.forwarding flag is set to 0.
>>
>> [0]: https://www.kernel.org/doc/Documentation/networking/ip-sysctl.txt
>>
>> Signed-off-by: Gabriel Goller <g.goller@proxmox.com>
>> ---


[snip]

>> +static int addrconf_sysctl_force_forwarding(const struct ctl_table *ctl, int write,
>> +					    void *buffer, size_t *lenp, loff_t *ppos)
>> +{
>> +	int *valp = ctl->data;
>> +	int ret;
>> +	int old, new;
>> +
>> +	// get extra params from table
> /* */ for comment
> https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/Documentation/process/coding-style.rst#n598

Hm, lots there from the BK to git transfer in 2005, with a few updates by Mauro, Jakub, and myself.


More recently (2016!), Linus said this:
  https://lore.kernel.org/lkml/CA+55aFyQYJerovMsSoSKS7PessZBr4vNp-3QUUwhqk4A4_jcbg@mail.gmail.com/

which seems to allow for "//" style commenting. But yeah, it hasn't been added to
coding-style.rst.

>> +	struct inet6_dev *idev = ctl->extra1;
>> +	struct net *net = ctl->extra2;
> Reverse x-mas tree for the variables declaration
> https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/Documentation/process/maintainer-netdev.rst#n368

Shouldn't maintainer-netdev.rst contain something about netdev-style comment blocks?
(not that I'm offering since I think it's ugly)

-- 
~Randy


^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [PATCH v3] ipv6: add `force_forwarding` sysctl to enable per-interface forwarding
  2025-07-02 22:26   ` Randy Dunlap
@ 2025-07-03  6:58     ` Nicolas Dichtel
  2025-07-04  4:09       ` Randy Dunlap
  0 siblings, 1 reply; 13+ messages in thread
From: Nicolas Dichtel @ 2025-07-03  6:58 UTC (permalink / raw)
  To: Randy Dunlap, Gabriel Goller, David S. Miller, Eric Dumazet,
	Jakub Kicinski, Paolo Abeni, Simon Horman, Jonathan Corbet,
	David Ahern
  Cc: netdev, linux-doc, linux-kernel

Le 03/07/2025 à 00:26, Randy Dunlap a écrit :

[snip]

>>> +static int addrconf_sysctl_force_forwarding(const struct ctl_table *ctl, int write,
>>> +					    void *buffer, size_t *lenp, loff_t *ppos)
>>> +{
>>> +	int *valp = ctl->data;
>>> +	int ret;
>>> +	int old, new;
>>> +
>>> +	// get extra params from table
>> /* */ for comment
>> https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/Documentation/process/coding-style.rst#n598
> 
> Hm, lots there from the BK to git transfer in 2005, with a few updates by Mauro, Jakub, and myself.
> 
> 
> More recently (2016!), Linus said this:
>   https://lore.kernel.org/lkml/CA+55aFyQYJerovMsSoSKS7PessZBr4vNp-3QUUwhqk4A4_jcbg@mail.gmail.com/
> 
> which seems to allow for "//" style commenting. But yeah, it hasn't been added to
> coding-style.rst.
I wasn't aware. I always seen '//' rejected.

> 
>>> +	struct inet6_dev *idev = ctl->extra1;
>>> +	struct net *net = ctl->extra2;
>> Reverse x-mas tree for the variables declaration
>> https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/Documentation/process/maintainer-netdev.rst#n368
> 
> Shouldn't maintainer-netdev.rst contain something about netdev-style comment blocks?
> (not that I'm offering since I think it's ugly)
> 
It has been removed:
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=82b8000c28b5

^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [PATCH v3] ipv6: add `force_forwarding` sysctl to enable per-interface forwarding
  2025-07-02 10:05 ` Nicolas Dichtel
  2025-07-02 22:26   ` Randy Dunlap
@ 2025-07-03 11:04   ` Gabriel Goller
  2025-07-03 14:04     ` Nicolas Dichtel
  1 sibling, 1 reply; 13+ messages in thread
From: Gabriel Goller @ 2025-07-03 11:04 UTC (permalink / raw)
  To: Nicolas Dichtel
  Cc: David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
	Simon Horman, Jonathan Corbet, David Ahern, netdev, linux-doc,
	linux-kernel

On 02.07.2025 12:05, Nicolas Dichtel wrote:
>Le 02/07/2025 à 09:46, Gabriel Goller a écrit :
>> 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.<interface>.forwarding` configures the interface
>> Host/Router configuration.
>>
>> Introduce a new sysctl flag `force_forwarding`, which can be set on every
>> interface. The ip6_forwarding function will then check if the global
>> forwarding flag OR the force_forwarding flag is active and forward the
>> packet.
>>
>> To preserver backwards-compatibility reset the flag (on all interfaces)
>> to 0 if the net.ipv6.conf.all.forwarding flag is set to 0.
>>
>> [0]: https://www.kernel.org/doc/Documentation/networking/ip-sysctl.txt
>>
>> Signed-off-by: Gabriel Goller <g.goller@proxmox.com>
>> ---
>>
>Please, wait 24 hours before reposting.
>https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/Documentation/process/maintainer-netdev.rst#n419

Ah, my bad, thought I posted v2 in the morning as well :(

>[snip]
>
>> @@ -6747,6 +6759,77 @@ static int addrconf_sysctl_disable_policy(const struct ctl_table *ctl, int write
>>  	return ret;
>>  }
>>
>> +/* called with RTNL locked */
>Instead of a comment ...
>
>> +static void addrconf_force_forward_change(struct net *net, __s32 newf)
>> +{
>> +	struct net_device *dev;
>> +	struct inet6_dev *idev;
>> +
>... put
>
>	ASSERT_RTNL();
>

Agree.

>> +	for_each_netdev(net, dev) {
>> +		idev = __in6_dev_get_rtnl_net(dev);
>> +		if (idev) {
>> +			int changed = (!idev->cnf.force_forwarding) ^ (!newf);
>> +
>> +			WRITE_ONCE(idev->cnf.force_forwarding, newf);
>> +			if (changed) {
>> +				inet6_netconf_notify_devconf(dev_net(dev), RTM_NEWNETCONF,
>> +							     NETCONFA_FORCE_FORWARDING,
>> +							     dev->ifindex, &idev->cnf);
>> +			}
>> +		}
>> +	}
>> +}
>> +
>> +static int addrconf_sysctl_force_forwarding(const struct ctl_table *ctl, int write,
>> +					    void *buffer, size_t *lenp, loff_t *ppos)
>> +{
>> +	int *valp = ctl->data;
>> +	int ret;
>> +	int old, new;
>> +
>> +	// get extra params from table
>/* */ for comment
>https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/Documentation/process/coding-style.rst#n598

NAK
(https://lore.kernel.org/lkml/CA+55aFyQYJerovMsSoSKS7PessZBr4vNp-3QUUwhqk4A4_jcbg@mail.gmail.com/#r)

But I'll capitalize the first word.

>> +	struct inet6_dev *idev = ctl->extra1;
>> +	struct net *net = ctl->extra2;
>Reverse x-mas tree for the variables declaration
>https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/Documentation/process/maintainer-netdev.rst#n368

Done.

>> +
>> +	// copy table and change extra params to min/max so we can use proc_douintvec_minmax
>> +	struct ctl_table lctl;
>> +
>> +	lctl = *ctl;
>> +	lctl.extra1 = SYSCTL_ZERO;
>> +	lctl.extra2 = SYSCTL_ONE;
>> +
>> +	old = *valp;
>> +	ret = proc_douintvec_minmax(&lctl, write, buffer, lenp, ppos);
>> +	new = *valp;
>I probably missed something. The new value is written in lctl. When is it
>written in ctl?

Ah, sorry there is something missing here.

This is supposed to look like this:

	struct inet6_dev *idev = ctl->extra1;
	struct net *net = ctl->extra2;
	int *valp = ctl->data;
	loff_t pos = *ppos;
	int new = *valp;
	int old = *valp;
	int ret;

	struct ctl_table lctl;

	lctl = *ctl;
	lctl.extra1 = SYSCTL_ZERO;
	lctl.extra2 = SYSCTL_ONE;
	lctl.data = &new;

	ret = proc_douintvec_minmax(&lctl, write, buffer, lenp, ppos);
	
	...

	if (write)
		WRITE_ONCE(*valp, new);
	if (ret)
		*ppos = pos;
	return ret;


>> +
>> +	if (write && old != new) {
>> +		if (!rtnl_net_trylock(net))
>> +			return restart_syscall();
>> +
>> +		if (valp == &net->ipv6.devconf_dflt->force_forwarding) {
>> +			inet6_netconf_notify_devconf(net, RTM_NEWNETCONF,
>> +						     NETCONFA_FORCE_FORWARDING,
>> +						     NETCONFA_IFINDEX_DEFAULT,
>> +						     net->ipv6.devconf_dflt);
>> +		} else if (valp == &net->ipv6.devconf_all->force_forwarding) {
>> +			inet6_netconf_notify_devconf(net, RTM_NEWNETCONF,
>> +						     NETCONFA_FORCE_FORWARDING,
>> +						     NETCONFA_IFINDEX_ALL,
>> +						     net->ipv6.devconf_all);
>> +
>> +			addrconf_force_forward_change(net, new);
>> +		} else {
>> +			inet6_netconf_notify_devconf(net, RTM_NEWNETCONF,
>> +						     NETCONFA_FORCE_FORWARDING,
>> +						     idev->dev->ifindex,
>> +						     &idev->cnf);
>> +		}
>> +		rtnl_net_unlock(net);
>> +	}
>> +
>> +	return ret;
>> +}
>> +
>>  static int minus_one = -1;
>>  static const int two_five_five = 255;
>>  static u32 ioam6_if_id_max = U16_MAX;

Thanks for the review!


^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [PATCH v3] ipv6: add `force_forwarding` sysctl to enable per-interface forwarding
  2025-07-02 16:10     ` Jakub Kicinski
  2025-07-02 16:17       ` Nicolas Dichtel
@ 2025-07-03 11:05       ` Gabriel Goller
  1 sibling, 0 replies; 13+ messages in thread
From: Gabriel Goller @ 2025-07-03 11:05 UTC (permalink / raw)
  To: Jakub Kicinski
  Cc: Nicolas Dichtel, David S. Miller, Eric Dumazet, Paolo Abeni,
	Simon Horman, Jonathan Corbet, David Ahern, netdev, linux-doc,
	linux-kernel

On 02.07.2025 09:10, Jakub Kicinski wrote:
>On Wed, 2 Jul 2025 17:14:42 +0200 Nicolas Dichtel wrote:
>> > Should we invert the polarity? It appears that the condition below only
>> > let's this setting _disable_ forwarding. IMO calling it "force" suggests
>> > to the user that it will force it to be enabled.
>> Not sure to follow you. When force_forwarding is set to 1 the forwarding is
>> always enabled.
>>
>> sysctl | all.forwarding | iface.force_forwarding | packet processing from iface
>>        |      0         |           0            |        no forward
>>        |      0         |           1            |         forward
>>        |      1         |           0            |         forward
>>        |      1         |           1            |         forward
>
>Ugh, I can't read comparisons to zero.
>Let's switch to more sane logic:
>
>	if (idev && !READ_ONCE(idev->cnf.force_forwarding) &&
>	    !READ_ONCE(net->ipv6.devconf_all->forwarding))

Agree!

Thanks for the review.


^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [PATCH v3] ipv6: add `force_forwarding` sysctl to enable per-interface forwarding
  2025-07-03 11:04   ` Gabriel Goller
@ 2025-07-03 14:04     ` Nicolas Dichtel
  2025-07-03 14:50       ` Gabriel Goller
  0 siblings, 1 reply; 13+ messages in thread
From: Nicolas Dichtel @ 2025-07-03 14:04 UTC (permalink / raw)
  To: David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
	Simon Horman, Jonathan Corbet, David Ahern, netdev, linux-doc,
	linux-kernel

Le 03/07/2025 à 13:04, Gabriel Goller a écrit :
[snip]
>>> +    // get extra params from table
>> /* */ for comment
>> https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/
>> Documentation/process/coding-style.rst#n598
> 
> NAK
> (https://lore.kernel.org/lkml/
> CA+55aFyQYJerovMsSoSKS7PessZBr4vNp-3QUUwhqk4A4_jcbg@mail.gmail.com/#r)

I will follow the netdev maintainers' guidelines.

If the doc I pointed to is wrong, please update it. It will be easier to find
than a 9-year-old email.


Regards,
Nicolas

^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [PATCH v3] ipv6: add `force_forwarding` sysctl to enable per-interface forwarding
  2025-07-03 14:04     ` Nicolas Dichtel
@ 2025-07-03 14:50       ` Gabriel Goller
  0 siblings, 0 replies; 13+ messages in thread
From: Gabriel Goller @ 2025-07-03 14:50 UTC (permalink / raw)
  To: Nicolas Dichtel
  Cc: David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
	Simon Horman, Jonathan Corbet, David Ahern, netdev, linux-doc,
	linux-kernel

On 03.07.2025 16:04, Nicolas Dichtel wrote:
>Le 03/07/2025 à 13:04, Gabriel Goller a écrit :
>[snip]
>>>> +    // get extra params from table
>>> /* */ for comment
>>> https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/
>>> Documentation/process/coding-style.rst#n598
>>
>> NAK
>> (https://lore.kernel.org/lkml/
>> CA+55aFyQYJerovMsSoSKS7PessZBr4vNp-3QUUwhqk4A4_jcbg@mail.gmail.com/#r)
>
>I will follow the netdev maintainers' guidelines.
>
>If the doc I pointed to is wrong, please update it. It will be easier to find
>than a 9-year-old email.

The netdev maintainers guideline doesn't contain anything about
comments. Also the coding-style doesn't prohibit single-line comments
with the `//` style.

The comments are kinda pointless though, I'll remove them in the next
version.

>Regards,
>Nicolas

Thanks


^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [PATCH v3] ipv6: add `force_forwarding` sysctl to enable per-interface forwarding
  2025-07-03  6:58     ` Nicolas Dichtel
@ 2025-07-04  4:09       ` Randy Dunlap
  0 siblings, 0 replies; 13+ messages in thread
From: Randy Dunlap @ 2025-07-04  4:09 UTC (permalink / raw)
  To: nicolas.dichtel, Nicolas Dichtel, Gabriel Goller, David S. Miller,
	Eric Dumazet, Jakub Kicinski, Paolo Abeni, Simon Horman,
	Jonathan Corbet, David Ahern
  Cc: netdev, linux-doc, linux-kernel

On July 2, 2025 11:58:16 PM PDT, Nicolas Dichtel <nicolas.dichtel@6wind.com> wrote:
>Le 03/07/2025 à 00:26, Randy Dunlap a écrit :
>
>[snip]
>
>>>> +static int addrconf_sysctl_force_forwarding(const struct ctl_table *ctl, int write,
>>>> +					    void *buffer, size_t *lenp, loff_t *ppos)
>>>> +{
>>>> +	int *valp = ctl->data;
>>>> +	int ret;
>>>> +	int old, new;
>>>> +
>>>> +	// get extra params from table
>>> /* */ for comment
>>> https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/Documentation/process/coding-style.rst#n598
>> 
>> Hm, lots there from the BK to git transfer in 2005, with a few updates by Mauro, Jakub, and myself.
>> 
>> 
>> More recently (2016!), Linus said this:
>>   https://lore.kernel.org/lkml/CA+55aFyQYJerovMsSoSKS7PessZBr4vNp-3QUUwhqk4A4_jcbg@mail.gmail.com/
>> 
>> which seems to allow for "//" style commenting. But yeah, it hasn't been added to
>> coding-style.rst.
>I wasn't aware. I always seen '//' rejected.
>
>> 
>>>> +	struct inet6_dev *idev = ctl->extra1;
>>>> +	struct net *net = ctl->extra2;
>>> Reverse x-mas tree for the variables declaration
>>> https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/Documentation/process/maintainer-netdev.rst#n368
>> 
>> Shouldn't maintainer-netdev.rst contain something about netdev-style comment blocks?
>> (not that I'm offering since I think it's ugly)
>> 
>It has been removed:
>https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=82b8000c28b5
>

Oh, thanks.  Sorry I missed that patch. 



~Randy

^ permalink raw reply	[flat|nested] 13+ messages in thread

end of thread, other threads:[~2025-07-04  4:09 UTC | newest]

Thread overview: 13+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2025-07-02  7:46 [PATCH v3] ipv6: add `force_forwarding` sysctl to enable per-interface forwarding Gabriel Goller
2025-07-02 10:05 ` Nicolas Dichtel
2025-07-02 22:26   ` Randy Dunlap
2025-07-03  6:58     ` Nicolas Dichtel
2025-07-04  4:09       ` Randy Dunlap
2025-07-03 11:04   ` Gabriel Goller
2025-07-03 14:04     ` Nicolas Dichtel
2025-07-03 14:50       ` Gabriel Goller
2025-07-02 14:34 ` Jakub Kicinski
2025-07-02 15:14   ` Nicolas Dichtel
2025-07-02 16:10     ` Jakub Kicinski
2025-07-02 16:17       ` Nicolas Dichtel
2025-07-03 11:05       ` Gabriel Goller

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).