]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
ip6_tunnel: enable to change proto of fb tunnels
authorNicolas Dichtel <nicolas.dichtel@6wind.com>
Mon, 30 Jun 2025 14:54:54 +0000 (16:54 +0200)
committerJakub Kicinski <kuba@kernel.org>
Wed, 2 Jul 2025 00:40:57 +0000 (17:40 -0700)
This is possible via the ioctl API:
> ip -6 tunnel change ip6tnl0 mode any

Let's align the netlink API:
> ip link set ip6tnl0 type ip6tnl mode any

Signed-off-by: Nicolas Dichtel <nicolas.dichtel@6wind.com>
Link: https://patch.msgid.link/20250630145602.1027220-1-nicolas.dichtel@6wind.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
net/ipv6/ip6_tunnel.c

index a885bb5c98ea8e1e9f8cb5844207767d1fa11c56..436e077061d1471ea01352bcc2042ba27788b264 100644 (file)
@@ -1562,11 +1562,22 @@ static void ip6_tnl_update(struct ip6_tnl *t, struct __ip6_tnl_parm *p)
        netdev_state_change(t->dev);
 }
 
-static void ip6_tnl0_update(struct ip6_tnl *t, struct __ip6_tnl_parm *p)
+static int ip6_tnl0_update(struct ip6_tnl *t, struct __ip6_tnl_parm *p,
+                          bool strict)
 {
-       /* for default tnl0 device allow to change only the proto */
+       /* For the default ip6tnl0 device, allow changing only the protocol
+        * (the IP6_TNL_F_CAP_PER_PACKET flag is set on ip6tnl0, and all other
+        * parameters are 0).
+        */
+       if (strict &&
+           (!ipv6_addr_any(&p->laddr) || !ipv6_addr_any(&p->raddr) ||
+            p->flags != t->parms.flags || p->hop_limit || p->encap_limit ||
+            p->flowinfo || p->link || p->fwmark || p->collect_md))
+               return -EINVAL;
+
        t->parms.proto = p->proto;
        netdev_state_change(t->dev);
+       return 0;
 }
 
 static void
@@ -1680,7 +1691,7 @@ ip6_tnl_siocdevprivate(struct net_device *dev, struct ifreq *ifr,
                        } else
                                t = netdev_priv(dev);
                        if (dev == ip6n->fb_tnl_dev)
-                               ip6_tnl0_update(t, &p1);
+                               ip6_tnl0_update(t, &p1, false);
                        else
                                ip6_tnl_update(t, &p1);
                }
@@ -2053,8 +2064,28 @@ static int ip6_tnl_changelink(struct net_device *dev, struct nlattr *tb[],
        struct ip6_tnl_net *ip6n = net_generic(net, ip6_tnl_net_id);
        struct ip_tunnel_encap ipencap;
 
-       if (dev == ip6n->fb_tnl_dev)
-               return -EINVAL;
+       if (dev == ip6n->fb_tnl_dev) {
+               if (ip_tunnel_netlink_encap_parms(data, &ipencap)) {
+                       /* iproute2 always sets TUNNEL_ENCAP_FLAG_CSUM6, so
+                        * let's ignore this flag.
+                        */
+                       ipencap.flags &= ~TUNNEL_ENCAP_FLAG_CSUM6;
+                       if (memchr_inv(&ipencap, 0, sizeof(ipencap))) {
+                               NL_SET_ERR_MSG(extack,
+                                              "Only protocol can be changed for fallback tunnel, not encap params");
+                               return -EINVAL;
+                       }
+               }
+
+               ip6_tnl_netlink_parms(data, &p);
+               if (ip6_tnl0_update(t, &p, true) < 0) {
+                       NL_SET_ERR_MSG(extack,
+                                      "Only protocol can be changed for fallback tunnel");
+                       return -EINVAL;
+               }
+
+               return 0;
+       }
 
        if (ip_tunnel_netlink_encap_parms(data, &ipencap)) {
                int err = ip6_tnl_encap_setup(t, &ipencap);