|
| 1 | +From: Christian Breunig <christian@breunig.cc> |
| 2 | +Date: Mon, 14 May 2026 13:11:00 +0200 |
| 3 | +Subject: [PATCH] net/l2tp: allow unmanaged tunnel setup without route to peer |
| 4 | + |
| 5 | +Kernel-created L2TPv3 tunnels (genetlink L2TP_CMD_TUNNEL_CREATE without |
| 6 | +L2TP_ATTR_FD) used udp_sock_create() and kernel_connect(), which invoke |
| 7 | +__ip4_datagram_connect() / __ip6_datagram_connect(). Those paths insist on |
| 8 | +a successful FIB lookup at connect time. If no route to the configured |
| 9 | +remote existed yet, tunnel and interface creation failed. |
| 10 | + |
| 11 | +The data path already resolves routes on transmit (e.g. __ip_queue_xmit(), |
| 12 | +inet6_csk_route_socket()). This change defers requiring a route until |
| 13 | +packets are sent. |
| 14 | + |
| 15 | +Details: |
| 16 | +- UDP encapsulation: bind with udp_sock_create() after zeroing peer_udp_port, |
| 17 | + then l2tp_udp_sk_set_peer() sets daddr/dport and socket "connected" state |
| 18 | + without caching sk_dst from connect. |
| 19 | +- IPv4 L2TP/IP (l2tp_ip): on -ENETUNREACH / -EHOSTUNREACH from |
| 20 | + __ip4_datagram_connect(), l2tp_ip_connect_deferred() installs peer and |
| 21 | + bind-table updates without a connect-time route. |
| 22 | +- IPv6 L2TP/IP (l2tp_ip6): same for __ip6_datagram_connect(), including |
| 23 | + IPv4-mapped peers and scope / bound-device checks aligned with the normal |
| 24 | + connect path. |
| 25 | + |
| 26 | +Forwarding still only happens once the FIB can reach the peer. Until then |
| 27 | +outgoing packets follow the existing no-route drop path. |
| 28 | + |
| 29 | +Assisted-by: Cursor:claude-4.8-opus |
| 30 | +Signed-off-by: Christian Breunig <christian@breunig.cc> |
| 31 | + |
| 32 | +--- |
| 33 | +diff --git i/net/l2tp/l2tp_core.c w/net/l2tp/l2tp_core.c |
| 34 | +index 9156a937334a..6fd9c1d89533 100644 |
| 35 | +--- i/net/l2tp/l2tp_core.c |
| 36 | ++++ w/net/l2tp/l2tp_core.c |
| 37 | +@@ -55,6 +55,8 @@ |
| 38 | + #include <net/inet_ecn.h> |
| 39 | + #include <net/ip6_route.h> |
| 40 | + #include <net/ip6_checksum.h> |
| 41 | ++#include <net/sock_reuseport.h> |
| 42 | ++#include <net/transp_v6.h> |
| 43 | + |
| 44 | + #include <asm/byteorder.h> |
| 45 | + #include <linux/atomic.h> |
| 46 | +@@ -1453,6 +1455,67 @@ static void l2tp_tunnel_del_work(struct work_struct *work) |
| 47 | + * These sockets are freed when the namespace exits using the pernet |
| 48 | + * exit hook. |
| 49 | + */ |
| 50 | ++ |
| 51 | ++static int l2tp_udp_sk_set_peer(struct socket *sock, struct udp_port_cfg *cfg) |
| 52 | ++{ |
| 53 | ++ struct sock *sk = sock->sk; |
| 54 | ++ struct inet_sock *inet; |
| 55 | ++ |
| 56 | ++ lock_sock(sk); |
| 57 | ++ inet = inet_sk(sk); |
| 58 | ++ sk_dst_reset(sk); |
| 59 | ++ |
| 60 | ++#if IS_ENABLED(CONFIG_IPV6) |
| 61 | ++ if (cfg->family == AF_INET6) { |
| 62 | ++ struct ipv6_pinfo *np = inet6_sk(sk); |
| 63 | ++ |
| 64 | ++ sk->sk_v6_daddr = cfg->peer_ip6; |
| 65 | ++ np->flow_label = 0; |
| 66 | ++ inet->inet_dport = cfg->peer_udp_port; |
| 67 | ++ } else |
| 68 | ++#endif |
| 69 | ++ { |
| 70 | ++ inet->inet_daddr = cfg->peer_ip.s_addr; |
| 71 | ++ inet->inet_dport = cfg->peer_udp_port; |
| 72 | ++ } |
| 73 | ++ |
| 74 | ++ reuseport_has_conns_set(sk); |
| 75 | ++ sk->sk_state = TCP_ESTABLISHED; |
| 76 | ++ sk_set_txhash(sk); |
| 77 | ++ atomic_set(&inet->inet_id, get_random_u16()); |
| 78 | ++ release_sock(sk); |
| 79 | ++ return 0; |
| 80 | ++} |
| 81 | ++ |
| 82 | ++static int l2tp_tunnel_udp_sock_create(struct net *net, struct udp_port_cfg *cfg, |
| 83 | ++ struct socket **sockp) |
| 84 | ++{ |
| 85 | ++ struct udp_port_cfg cfg_bind = *cfg; |
| 86 | ++ int err; |
| 87 | ++ struct socket *sock; |
| 88 | ++ |
| 89 | ++ cfg_bind.peer_udp_port = 0; |
| 90 | ++ |
| 91 | ++ err = udp_sock_create(net, &cfg_bind, &sock); |
| 92 | ++ if (err < 0) |
| 93 | ++ return err; |
| 94 | ++ |
| 95 | ++ if (!cfg->peer_udp_port) { |
| 96 | ++ *sockp = sock; |
| 97 | ++ return 0; |
| 98 | ++ } |
| 99 | ++ |
| 100 | ++ err = l2tp_udp_sk_set_peer(sock, cfg); |
| 101 | ++ if (err < 0) { |
| 102 | ++ kernel_sock_shutdown(sock, SHUT_RDWR); |
| 103 | ++ sock_release(sock); |
| 104 | ++ return err; |
| 105 | ++ } |
| 106 | ++ |
| 107 | ++ *sockp = sock; |
| 108 | ++ return 0; |
| 109 | ++} |
| 110 | ++ |
| 111 | + static int l2tp_tunnel_sock_create(struct net *net, |
| 112 | + u32 tunnel_id, |
| 113 | + u32 peer_tunnel_id, |
| 114 | +@@ -1490,7 +1553,7 @@ static int l2tp_tunnel_sock_create(struct net *net, |
| 115 | + udp_conf.local_udp_port = htons(cfg->local_udp_port); |
| 116 | + udp_conf.peer_udp_port = htons(cfg->peer_udp_port); |
| 117 | + |
| 118 | +- err = udp_sock_create(net, &udp_conf, &sock); |
| 119 | ++ err = l2tp_tunnel_udp_sock_create(net, &udp_conf, &sock); |
| 120 | + if (err < 0) |
| 121 | + goto out; |
| 122 | + |
| 123 | +diff --git i/net/l2tp/l2tp_ip.c w/net/l2tp/l2tp_ip.c |
| 124 | +index 29795d2839e8..d63b00f09421 100644 |
| 125 | +--- i/net/l2tp/l2tp_ip.c |
| 126 | ++++ w/net/l2tp/l2tp_ip.c |
| 127 | +@@ -24,6 +24,7 @@ |
| 128 | + #include <net/xfrm.h> |
| 129 | + #include <net/net_namespace.h> |
| 130 | + #include <net/netns/generic.h> |
| 131 | ++#include <net/sock_reuseport.h> |
| 132 | + |
| 133 | + #include "l2tp_core.h" |
| 134 | + |
| 135 | +@@ -328,6 +329,37 @@ static int l2tp_ip_bind(struct sock *sk, struct sockaddr *uaddr, int addr_len) |
| 136 | + return ret; |
| 137 | + } |
| 138 | + |
| 139 | ++/* Install peer address without capturing a route. Outgoing packets resolve |
| 140 | ++ * the path in __ip_queue_xmit(); this matches on-demand forwarding once the |
| 141 | ++ * FIB can reach the remote. |
| 142 | ++ */ |
| 143 | ++static void __l2tp_ip4_sk_set_peer(struct sock *sk, __be32 daddr, __be16 dport) |
| 144 | ++{ |
| 145 | ++ struct inet_sock *inet = inet_sk(sk); |
| 146 | ++ |
| 147 | ++ sk_dst_reset(sk); |
| 148 | ++ inet->inet_daddr = daddr; |
| 149 | ++ inet->inet_dport = dport; |
| 150 | ++ reuseport_has_conns_set(sk); |
| 151 | ++ sk->sk_state = TCP_ESTABLISHED; |
| 152 | ++ sk_set_txhash(sk); |
| 153 | ++ atomic_set(&inet->inet_id, get_random_u16()); |
| 154 | ++} |
| 155 | ++ |
| 156 | ++static int l2tp_ip_connect_deferred(struct sock *sk, struct sockaddr_l2tpip *lsa) |
| 157 | ++{ |
| 158 | ++ struct l2tp_ip_net *pn = l2tp_ip_pernet(sock_net(sk)); |
| 159 | ++ |
| 160 | ++ __l2tp_ip4_sk_set_peer(sk, lsa->l2tp_addr.s_addr, lsa->l2tp_unused); |
| 161 | ++ l2tp_ip_sk(sk)->peer_conn_id = lsa->l2tp_conn_id; |
| 162 | ++ |
| 163 | ++ write_lock_bh(&pn->l2tp_ip_lock); |
| 164 | ++ hlist_del_init(&sk->sk_bind_node); |
| 165 | ++ sk_add_bind_node(sk, &pn->l2tp_ip_bind_table); |
| 166 | ++ write_unlock_bh(&pn->l2tp_ip_lock); |
| 167 | ++ return 0; |
| 168 | ++} |
| 169 | ++ |
| 170 | + static int l2tp_ip_connect(struct sock *sk, struct sockaddr *uaddr, int addr_len) |
| 171 | + { |
| 172 | + struct sockaddr_l2tpip *lsa = (struct sockaddr_l2tpip *)uaddr; |
| 173 | +@@ -349,6 +381,11 @@ static int l2tp_ip_connect(struct sock *sk, struct sockaddr *uaddr, int addr_len |
| 174 | + } |
| 175 | + |
| 176 | + rc = __ip4_datagram_connect(sk, uaddr, addr_len); |
| 177 | ++ if (rc == -ENETUNREACH || rc == -EHOSTUNREACH) { |
| 178 | ++ rc = l2tp_ip_connect_deferred(sk, lsa); |
| 179 | ++ goto out_sk; |
| 180 | ++ } |
| 181 | ++ |
| 182 | + if (rc < 0) |
| 183 | + goto out_sk; |
| 184 | + |
| 185 | +diff --git i/net/l2tp/l2tp_ip6.c w/net/l2tp/l2tp_ip6.c |
| 186 | +index ea232f338dcb..610e666c0a58 100644 |
| 187 | +--- i/net/l2tp/l2tp_ip6.c |
| 188 | ++++ w/net/l2tp/l2tp_ip6.c |
| 189 | +@@ -28,6 +28,8 @@ |
| 190 | + #include <net/transp_v6.h> |
| 191 | + #include <net/addrconf.h> |
| 192 | + #include <net/ip6_route.h> |
| 193 | ++#include <net/l3mdev.h> |
| 194 | ++#include <net/sock_reuseport.h> |
| 195 | + |
| 196 | + #include "l2tp_core.h" |
| 197 | + |
| 198 | +@@ -383,6 +385,116 @@ static int l2tp_ip6_bind(struct sock *sk, struct sockaddr *uaddr, int addr_len) |
| 199 | + return err; |
| 200 | + } |
| 201 | + |
| 202 | ++static bool l2tp_ipv6_mapped_addr_any(const struct in6_addr *a) |
| 203 | ++{ |
| 204 | ++ return ipv6_addr_v4mapped(a) && a->s6_addr32[3] == 0; |
| 205 | ++} |
| 206 | ++ |
| 207 | ++/* Peer address without a cached dst; outbound path resolves the route. */ |
| 208 | ++static void __l2tp_ip6_sk_set_peer(struct sock *sk, const struct in6_addr *daddr, |
| 209 | ++ __be16 dport, __be32 flowlabel) |
| 210 | ++{ |
| 211 | ++ struct inet_sock *inet = inet_sk(sk); |
| 212 | ++ struct ipv6_pinfo *np = inet6_sk(sk); |
| 213 | ++ |
| 214 | ++ sk_dst_reset(sk); |
| 215 | ++ sk->sk_v6_daddr = *daddr; |
| 216 | ++ np->flow_label = flowlabel; |
| 217 | ++ inet->inet_dport = dport; |
| 218 | ++ reuseport_has_conns_set(sk); |
| 219 | ++ sk->sk_state = TCP_ESTABLISHED; |
| 220 | ++ sk_set_txhash(sk); |
| 221 | ++} |
| 222 | ++ |
| 223 | ++static int __l2tp_ip6_connect_deferred_mapped(struct sock *sk, |
| 224 | ++ struct sockaddr_l2tpip6 *lsa, |
| 225 | ++ struct sockaddr_in6 *usin) |
| 226 | ++{ |
| 227 | ++ struct inet_sock *inet = inet_sk(sk); |
| 228 | ++ struct ipv6_pinfo *np = inet6_sk(sk); |
| 229 | ++ struct sockaddr_in sin; |
| 230 | ++ struct l2tp_ip6_net *pn; |
| 231 | ++ int err; |
| 232 | ++ |
| 233 | ++ memset(&sin, 0, sizeof(sin)); |
| 234 | ++ sin.sin_family = AF_INET; |
| 235 | ++ sin.sin_addr.s_addr = usin->sin6_addr.s6_addr32[3]; |
| 236 | ++ sin.sin_port = usin->sin6_port; |
| 237 | ++ |
| 238 | ++ err = __ip4_datagram_connect(sk, (struct sockaddr *)&sin, sizeof(sin)); |
| 239 | ++ if (err == -ENETUNREACH || err == -EHOSTUNREACH) { |
| 240 | ++ sk_dst_reset(sk); |
| 241 | ++ inet->inet_daddr = sin.sin_addr.s_addr; |
| 242 | ++ inet->inet_dport = sin.sin_port; |
| 243 | ++ reuseport_has_conns_set(sk); |
| 244 | ++ sk->sk_state = TCP_ESTABLISHED; |
| 245 | ++ sk_set_txhash(sk); |
| 246 | ++ atomic_set(&inet->inet_id, get_random_u16()); |
| 247 | ++ } else if (err) { |
| 248 | ++ return err; |
| 249 | ++ } |
| 250 | ++ |
| 251 | ++ ipv6_addr_set_v4mapped(inet->inet_daddr, &sk->sk_v6_daddr); |
| 252 | ++ if (ipv6_addr_any(&np->saddr) || l2tp_ipv6_mapped_addr_any(&np->saddr)) |
| 253 | ++ ipv6_addr_set_v4mapped(inet->inet_saddr, &np->saddr); |
| 254 | ++ if (ipv6_addr_any(&sk->sk_v6_rcv_saddr) || |
| 255 | ++ l2tp_ipv6_mapped_addr_any(&sk->sk_v6_rcv_saddr)) { |
| 256 | ++ ipv6_addr_set_v4mapped(inet->inet_rcv_saddr, &sk->sk_v6_rcv_saddr); |
| 257 | ++ if (sk->sk_prot->rehash) |
| 258 | ++ sk->sk_prot->rehash(sk); |
| 259 | ++ } |
| 260 | ++ |
| 261 | ++ l2tp_ip6_sk(sk)->peer_conn_id = lsa->l2tp_conn_id; |
| 262 | ++ pn = l2tp_ip6_pernet(sock_net(sk)); |
| 263 | ++ write_lock_bh(&pn->l2tp_ip6_lock); |
| 264 | ++ hlist_del_init(&sk->sk_bind_node); |
| 265 | ++ sk_add_bind_node(sk, &pn->l2tp_ip6_bind_table); |
| 266 | ++ write_unlock_bh(&pn->l2tp_ip6_lock); |
| 267 | ++ return 0; |
| 268 | ++} |
| 269 | ++ |
| 270 | ++static int l2tp_ip6_connect_deferred(struct sock *sk, struct sockaddr_l2tpip6 *lsa, |
| 271 | ++ struct sockaddr_in6 *usin, |
| 272 | ++ int addr_len) |
| 273 | ++{ |
| 274 | ++ struct ipv6_pinfo *np = inet6_sk(sk); |
| 275 | ++ struct l2tp_ip6_net *pn; |
| 276 | ++ int addr_type = ipv6_addr_type(&usin->sin6_addr); |
| 277 | ++ const struct in6_addr *daddr = &usin->sin6_addr; |
| 278 | ++ __be32 fl6_flowlabel = 0; |
| 279 | ++ |
| 280 | ++ if (addr_type & IPV6_ADDR_MAPPED) |
| 281 | ++ return __l2tp_ip6_connect_deferred_mapped(sk, lsa, usin); |
| 282 | ++ |
| 283 | ++ if (inet6_test_bit(SNDFLOW, sk)) |
| 284 | ++ fl6_flowlabel = usin->sin6_flowinfo & IPV6_FLOWINFO_MASK; |
| 285 | ++ |
| 286 | ++ if (__ipv6_addr_needs_scope_id(addr_type)) { |
| 287 | ++ if (addr_len >= sizeof(struct sockaddr_in6) && |
| 288 | ++ usin->sin6_scope_id) { |
| 289 | ++ if (!sk_dev_equal_l3scope(sk, usin->sin6_scope_id)) |
| 290 | ++ return -EINVAL; |
| 291 | ++ WRITE_ONCE(sk->sk_bound_dev_if, usin->sin6_scope_id); |
| 292 | ++ } |
| 293 | ++ |
| 294 | ++ if (!sk->sk_bound_dev_if && (addr_type & IPV6_ADDR_MULTICAST)) |
| 295 | ++ WRITE_ONCE(sk->sk_bound_dev_if, READ_ONCE(np->mcast_oif)); |
| 296 | ++ |
| 297 | ++ if (!sk->sk_bound_dev_if) |
| 298 | ++ return -EINVAL; |
| 299 | ++ } |
| 300 | ++ |
| 301 | ++ __l2tp_ip6_sk_set_peer(sk, daddr, usin->sin6_port, fl6_flowlabel); |
| 302 | ++ l2tp_ip6_sk(sk)->peer_conn_id = lsa->l2tp_conn_id; |
| 303 | ++ |
| 304 | ++ pn = l2tp_ip6_pernet(sock_net(sk)); |
| 305 | ++ write_lock_bh(&pn->l2tp_ip6_lock); |
| 306 | ++ hlist_del_init(&sk->sk_bind_node); |
| 307 | ++ sk_add_bind_node(sk, &pn->l2tp_ip6_bind_table); |
| 308 | ++ write_unlock_bh(&pn->l2tp_ip6_lock); |
| 309 | ++ return 0; |
| 310 | ++} |
| 311 | ++ |
| 312 | + static int l2tp_ip6_connect(struct sock *sk, struct sockaddr *uaddr, |
| 313 | + int addr_len) |
| 314 | + { |
| 315 | +@@ -418,6 +530,11 @@ static int l2tp_ip6_connect(struct sock *sk, struct sockaddr *uaddr, |
| 316 | + } |
| 317 | + |
| 318 | + rc = __ip6_datagram_connect(sk, uaddr, addr_len); |
| 319 | ++ if (rc == -ENETUNREACH || rc == -EHOSTUNREACH) { |
| 320 | ++ rc = l2tp_ip6_connect_deferred(sk, lsa, usin, addr_len); |
| 321 | ++ goto out_sk; |
| 322 | ++ } |
| 323 | ++ |
| 324 | + if (rc < 0) |
| 325 | + goto out_sk; |
| 326 | + |
0 commit comments