Le 03/07/2025 à 18:01, 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. > > Add a short selftest that checks if a packet gets forwarded with and > without `force_forwarding`. > > [0]: https://www.kernel.org/doc/Documentation/networking/ip-sysctl.txt > > Signed-off-by: Gabriel Goller <g.goller@xxxxxxxxxxx> > --- > [snip] > @@ -6747,6 +6759,78 @@ static int addrconf_sysctl_disable_policy(const struct ctl_table *ctl, int write > return ret; > } > > +static void addrconf_force_forward_change(struct net *net, __s32 newf) > +{ > + ASSERT_RTNL(); > + struct net_device *dev; > + struct inet6_dev *idev; > + ASSERT_RTNL() is always put after variables declaration. > + 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) > +{ > + struct inet6_dev *idev = ctl->extra1; > + struct net *net = ctl->extra2; > + int *valp = ctl->data; > + loff_t pos = *ppos; > + int new_val = *valp; > + int old_val = *valp; > + int ret; > + > + struct ctl_table tmp_ctl = *ctl; This declaration should be put with other declarations. > + > + tmp_ctl.extra1 = SYSCTL_ZERO; > + tmp_ctl.extra2 = SYSCTL_ONE; > + tmp_ctl.data = &new_val; > + > + ret = proc_douintvec_minmax(&tmp_ctl, write, buffer, lenp, ppos); > + > + if (write && old_val != new_val) { > + 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_val); > + } else { > + inet6_netconf_notify_devconf(net, RTM_NEWNETCONF, > + NETCONFA_FORCE_FORWARDING, > + idev->dev->ifindex, > + &idev->cnf); > + } > + rtnl_net_unlock(net); > + } > + > + if (write) > + WRITE_ONCE(*valp, new_val); Why not putting this in the above block? And maybe under the rtnl_lock to avoid race if two users change the value at the same time. Nicolas