Skip to content

Kernel: T8605: net/l2tp: allow unmanaged tunnel setup without route to peer - #1189

Merged
dmbaturin merged 1 commit into
vyos:rollingfrom
c-po:l2tpv3
Jun 26, 2026
Merged

Kernel: T8605: net/l2tp: allow unmanaged tunnel setup without route to peer#1189
dmbaturin merged 1 commit into
vyos:rollingfrom
c-po:l2tpv3

Conversation

@c-po

@c-po c-po commented May 14, 2026

Copy link
Copy Markdown
Member

Change summary

Kernel-created L2TPv3 tunnels (genetlink L2TP_CMD_TUNNEL_CREATE without L2TP_ATTR_FD) used udp_sock_create() and kernel_connect(), which invoke __ip4_datagram_connect()/__ip6_datagram_connect(). Those paths insist on a successful FIB lookup at connect time. If no route to the configured remote existed yet, tunnel and interface creation failed.

The data path already resolves routes on transmit (e.g. __ip_queue_xmit(), inet6_csk_route_socket()). This change defers requiring a route until packets are sent.

Details:

  • UDP encapsulation: bind with udp_sock_create() after zeroing peer_udp_port, then l2tp_udp_sk_set_peer() sets daddr/dport and socket "connected" state without caching sk_dst from connect.
  • IPv4 L2TP/IP (l2tp_ip): on -ENETUNREACH/-EHOSTUNREACH from __ip4_datagram_connect(), l2tp_ip_connect_deferred() installs peer and bind-table updates without a connect-time route.
  • IPv6 L2TP/IP (l2tp_ip6): same for __ip6_datagram_connect(), including IPv4-mapped peers and scope / bound-device checks aligned with the normal connect path.

Forwarding still only happens once the FIB can reach the peer. Until then outgoing packets follow the existing no-route drop path.

Types of changes

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Code style update (formatting, renaming)
  • Refactoring (no functional changes)
  • Migration from an old Vyatta component to vyos-1x, please link to related PR inside obsoleted component
  • Other (please describe):

Related Task(s)

Related PR(s)

How to test / Smoketest result

set interfaces ethernet eth0 address '10.10.10.1/24'
set interfaces loopback lo address '192.168.1.11/32'
set interfaces bridge br0 member interface eth3
set interfaces bridge br0 member interface l2tpeth333
set interfaces l2tpv3 l2tpeth333 encapsulation 'ip'
set interfaces l2tpv3 l2tpeth333 peer-session-id '33'
set interfaces l2tpv3 l2tpeth333 peer-tunnel-id '33'
set interfaces l2tpv3 l2tpeth333 remote '198.51.100.100'
set interfaces l2tpv3 l2tpeth333 session-id '33'
set interfaces l2tpv3 l2tpeth333 source-address '192.168.1.11'
set interfaces l2tpv3 l2tpeth333 tunnel-id '33'

Checklist:

  • I have read the CONTRIBUTING document
  • I have linked this PR to one or more Phabricator Task(s)
  • My commit headlines contain a valid Task id
  • My change requires a change to the documentation
  • I have updated the documentation accordingly

…o peer

Kernel-created L2TPv3 tunnels (genetlink L2TP_CMD_TUNNEL_CREATE without
L2TP_ATTR_FD) used udp_sock_create() and kernel_connect(), which invoke
__ip4_datagram_connect() / __ip6_datagram_connect(). Those paths insist on
a successful FIB lookup at connect time. If no route to the configured
remote existed yet, tunnel and interface creation failed.

The data path already resolves routes on transmit (e.g. __ip_queue_xmit(),
inet6_csk_route_socket()). This change defers requiring a route until
packets are sent.

Details:
- UDP encapsulation: bind with udp_sock_create() after zeroing peer_udp_port,
  then l2tp_udp_sk_set_peer() sets daddr/dport and socket "connected" state
  without caching sk_dst from connect.
- IPv4 L2TP/IP (l2tp_ip): on -ENETUNREACH / -EHOSTUNREACH from
  __ip4_datagram_connect(), l2tp_ip_connect_deferred() installs peer and
  bind-table updates without a connect-time route.
- IPv6 L2TP/IP (l2tp_ip6): same for __ip6_datagram_connect(), including
  IPv4-mapped peers and scope / bound-device checks aligned with the normal
  connect path.

Forwarding still only happens once the FIB can reach the peer. Until then
outgoing packets follow the existing no-route drop path.

Assisted-by: Cursor:claude-4.6-opus
Signed-off-by: Christian Breunig <christian@breunig.cc>
@github-actions

github-actions Bot commented May 14, 2026

Copy link
Copy Markdown

👍
No issues in PR Title / Commit Title

@coderabbitai

coderabbitai Bot commented May 14, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Repository YAML (base), Central YAML (inherited), Organization UI (inherited)

Review profile: CHILL

Plan: Pro

Run ID: 3f86ba99-af15-4679-a22a-a6225ea912ff

📥 Commits

Reviewing files that changed from the base of the PR and between 62894a3 and 8c4d602.

📒 Files selected for processing (1)
  • scripts/package-build/linux-kernel/patches/kernel/0004-l2tp-defer-route-at-tunnel-create.patch
🔗 Linked repositories identified

CodeRabbit considers these linked repositories for cross-repo context during reviews:

  • ansible/ansible (manual)
🚧 Files skipped from review as they are similar to previous changes (1)
  • scripts/package-build/linux-kernel/patches/kernel/0004-l2tp-defer-route-at-tunnel-create.patch
📜 Recent review details
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: Mergify Merge Protections
  • GitHub Check: Summary
🧰 Additional context used
🔍 Remote MCP Context7

Relevant facts found

  • L2TP netlink tunnel creation: when L2TP_CMD_TUNNEL_CREATE omits L2TP_ATTR_FD, the kernel creates the tunnel socket itself using the supplied IP/UDP attributes (i.e., unmanaged tunnel behavior).
  • L2TP supports UDP and IP encapsulation; tunnel sockets for UDP (IPv4) are created with socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP) and for IP-encapsulation with socket(AF_INET6, SOCK_DGRAM, IPPROTO_L2TP).
  • The kernel L2TP documentation describes the netlink attributes used when the kernel creates its own socket (IP/UDP source/destination, UDP ports, checksum options) — matching the PR’s scope of changing internal socket-creation/connect behavior rather than netlink API surface.

Sources searched (Context7 / Linux kernel docs): results for L2TP tunnel socket creation, L2TP netlink API and attributes, and examples for UDP/IP tunnel socket creation.


📝 Walkthrough

Summary by CodeRabbit

Release Notes

  • Bug Fixes
    • Fixed L2TPv3 tunnel creation to support scenarios where network routes to the peer are not immediately available, enabling tunnel setup to proceed with route establishment deferred.

Walkthrough

Defers L2TP tunnel peer route validation from creation to transmit time by splitting UDP socket creation and adding deferred connect/install paths for IPv4 and IPv6 (including IPv4-mapped peers), plus bind-table registration on deferred install.

Changes

L2TP Route Deferral

Layer / File(s) Summary
Core UDP socket deferred-bind helpers
scripts/package-build/linux-kernel/patches/kernel/0004-l2tp-defer-route-at-tunnel-create.patch (lines 1–123)
l2tp_core.c adds helpers to create UDP sockets with the peer port cleared so the peer daddr/dport can be installed later; updates l2tp_tunnel_sock_create() to call the helper and performs sk_dst_reset plus sk_state/txhash/inet_id updates.
IPv4 L2TP/IP deferred connect with bind-table update
scripts/package-build/linux-kernel/patches/kernel/0004-l2tp-defer-route-at-tunnel-create.patch (lines 124–185)
l2tp_ip.c treats __ip4_datagram_connect() returning -ENETUNREACH/-EHOSTUNREACH as deferred: set peer daddr/dport, call sk_dst_reset, set peer connection id, update socket state/txhash/inet_id, and re-register the socket into the pernet bind-table under lock instead of failing.
IPv6 L2TP/IP deferred connect with IPv4-mapped peer handling
scripts/package-build/linux-kernel/patches/kernel/0004-l2tp-defer-route-at-tunnel-create.patch (lines 186–326)
l2tp_ip6.c defers IPv6 peer installation on -ENETUNREACH/-EHOSTUNREACH: sk_dst_reset, set IPv6 peer daddr/dport/flowlabel, update socket state, and register in IPv6 bind-table; for IPv4-mapped peers, attempt an IPv4 datagram connect for reachability then restore IPv4-mapped IPv6 state before registering.
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately describes the main change: allowing L2TPv3 tunnel creation without requiring a pre-existing FIB route to the peer.
Description check ✅ Passed The description provides comprehensive context on the problem (connect-time route requirement), solution approach (deferred route resolution), and implementation details across UDP, IPv4, and IPv6 paths.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
✨ Simplify code
  • Create PR with simplified code

Comment @coderabbitai help to get the list of available commands and usage tips.

@github-actions github-actions Bot added the current VyOS rolling release label May 14, 2026

@sever-sever sever-sever left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add a patch to the L2TP tunnel interfaces that lack routes to the peer to avoid fails.

@sever-sever
sever-sever requested review from dmbaturin and hedrok May 15, 2026 09:18
@alexk37

alexk37 commented May 15, 2026

Copy link
Copy Markdown
Contributor

Fix is verified in both rolling and sagitta:

Config from the original report (loopback source, remote behind a static route, l2tpv3 in bridge) commits cleanly in a single commit on RouterA(fixed build), survives reboot, and forwards real L2 traffic end-to-end:

  • Tunnel comes up u/u on both ends.
  • ClientA (172.16.0.1/24) <-> ClientB (172.16.0.2/24) ping succeeds across br0 → l2tpeth333 → IP-proto-115 → l2tpeth333 → br0.
  • tcpdump -ni eth0 proto 115 confirms bidirectional L2TPv3-over-IP between 192.168.1.11 <->198.51.100.100.
  • After reboot the config loads without Configuration error and data plane is functional with no manual intervention.

Topology:
ClientA -- RouterA(fixed) == L2TPv3/IP == RouterB -- ClientB

tcpdump:

vyos@vyos:~$ sudo tcpdump -ni eth0 proto 115 -c 4
tcpdump: verbose output suppressed, use -v[v]... for full protocol decode
listening on eth0, link-type EN10MB (Ethernet), snapshot length 262144 bytes
09:36:46.267960 IP 192.168.1.11 > 198.51.100.100:  ip-proto-115 106
09:36:46.270663 IP 198.51.100.100 > 192.168.1.11:  ip-proto-115 106
09:36:47.269683 IP 192.168.1.11 > 198.51.100.100:  ip-proto-115 106
09:36:47.271268 IP 198.51.100.100 > 192.168.1.11:  ip-proto-115 106
4 packets captured
4 packets received by filter
0 packets dropped by kernel

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In
`@scripts/package-build/linux-kernel/patches/kernel/0004-l2tp-defer-route-at-tunnel-create.patch`:
- Around line 60-77: The new branch for AF_INET6 bypasses the IPv6 connect-time
validation from kernel_connect()/udp_sock_create6()/__ip6_datagram_connect(), so
revert to invoking the IPv6 connect path (call kernel_connect() on AF_INET6
sockets) or re-run the same validation checks for scope-less link-local
addresses and IPv4-mapped peers before marking the socket connected;
specifically, for the AF_INET6 branch that now sets sk->sk_v6_daddr and
inet6_sk()->flow_label, perform the same checks/logic as
__ip6_datagram_connect()/udp_sock_create6() (or call kernel_connect()) to ensure
interface-binding, scope validation and mapped-address handling are applied
prior to calling reuseport_has_conns_set(sk), sk_set_txhash(sk) and
atomic_set(&inet->inet_id, ...).
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Central YAML (base), Organization UI (inherited)

Review profile: CHILL

Plan: Pro Plus

Run ID: 91d15920-9d91-4eb7-9132-8745658a81ab

📥 Commits

Reviewing files that changed from the base of the PR and between 2395e4f and 428af84.

📒 Files selected for processing (1)
  • scripts/package-build/linux-kernel/patches/kernel/0004-l2tp-defer-route-at-tunnel-create.patch
📜 Review details
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
  • GitHub Check: codeql-analysis-call / Analyze (python)
  • GitHub Check: Mergify Merge Protections
  • GitHub Check: Summary
🔇 Additional comments (1)
scripts/package-build/linux-kernel/patches/kernel/0004-l2tp-defer-route-at-tunnel-create.patch (1)

82-109: LGTM!

Also applies to: 118-119, 139-168, 176-196, 217-325, 333-355

@c-po
c-po marked this pull request as draft May 17, 2026 08:05
@mergify mergify Bot added rolling and removed current VyOS rolling release labels Jun 2, 2026
@c-po
c-po marked this pull request as ready for review June 12, 2026 20:04

@dmbaturin dmbaturin left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The issue is certainly real. The logic seems fine to me, although I'm not fully qualified to judge kernel code. I trust @alexk37's testing.

@dmbaturin
dmbaturin merged commit b284c29 into vyos:rolling Jun 26, 2026
9 of 10 checks passed
@vyos-bot vyos-bot Bot added mirror-initiated This PR initiated for mirror sync workflow mirror-completed and removed mirror-initiated This PR initiated for mirror sync workflow labels Jun 26, 2026
@c-po
c-po deleted the l2tpv3 branch July 2, 2026 05:11
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Development

Successfully merging this pull request may close these issues.

4 participants