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(-)
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 `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 <g.goller@proxmox.com>
---
* 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/networking/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.
+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);
__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
};
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=26,
NET_IPV6_ACCEPT_RA_RT_INFO_MIN_PLEN=27,
NET_IPV6_RA_DEFRTR_METRIC=28,
+ NET_IPV6_DO_FORWARDING=29,
__NET_IPV6_MAX
};
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 = {
.ndisc_evict_nocarrier = 1,
.ra_honor_pio_life = 0,
.ra_honor_pio_pflag = 0,
+ .do_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,
+ .do_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 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 == 0)
+ WRITE_ONCE(idev->cnf.do_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_DO_FORWARDING] = READ_ONCE(cnf->do_forwarding);
}
static inline size_t inet6_ifla6_size(void)
@@ -7217,6 +7229,15 @@ static const struct ctl_table addrconf_sysctl[] = {
.extra1 = SYSCTL_ZERO,
.extra2 = SYSCTL_TWO,
},
+ {
+ .procname = "do_forwarding",
+ .data = &ipv6_devconf.do_forwarding,
+ .maxlen = sizeof(u8),
+ .mode = 0644,
+ .proc_handler = proc_dou8vec_minmax,
+ .extra1 = SYSCTL_ZERO,
+ .extra2 = SYSCTL_ONE,
+ },
};
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;
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.do_forwarding) == 0) &&
+ READ_ONCE(net->ipv6.devconf_all->forwarding) == 0)
goto error;
if (skb->pkt_type != PACKET_HOST)
--
2.39.5
Le 25/06/2025 à 16:26, 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 `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 <g.goller@proxmox.com> Please, export this sysctl via a NETCONFA_DO_FORWARDING attribute also. > --- > > * 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. What about force_forwarding? > * 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! It seems correct to me. > * Thanks for the help! Maybe you could align ipv6.all.do_forwarding on ipv4.all.forwarding, ie setting all existing ipv6.*.do_forwarding. https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/net/ipv4/devinet.c#n2423 Regards, Nicolas
On 26.06.2025 16:51, Nicolas Dichtel wrote: >Le 25/06/2025 à 16:26, 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 `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 <g.goller@proxmox.com> >Please, export this sysctl via a NETCONFA_DO_FORWARDING attribute also. Yep, will do. >> --- >> >> * 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. >What about force_forwarding? I Agree! >> * 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! >It seems correct to me. > >> * Thanks for the help! >Maybe you could align ipv6.all.do_forwarding on ipv4.all.forwarding, ie setting >all existing ipv6.*.do_forwarding. >https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/net/ipv4/devinet.c#n2423 Also done! >Regards, >Nicolas Sent a new patch just now, thanks for reviewing!
Le 27/06/2025 à 16:47, Gabriel Goller a écrit : [snip] > > Sent a new patch just now, thanks for reviewing! > FWIW, I didn't see any new patch from you. Regards, Nicolas
On 30.06.2025 16:53, Nicolas Dichtel wrote: >Le 27/06/2025 à 16:47, Gabriel Goller a écrit : >[snip] >> >> Sent a new patch just now, thanks for reviewing! >> >FWIW, I didn't see any new patch from you. It's out now, forgot to actually send it -_- >Regards, >Nicolas
© 2016 - 2025 Red Hat, Inc.