Feature/tunnel wireguard for FRR#3569
Conversation
Install wireguard-tools during initial configuration on clab FRR nodes before the management VRF is created, and add integration tests for global and transport-VRF underlay scenarios. Co-authored-by: Cursor <cursoragent@cursor.com>
Add shared tunnel utilities, OSPFv3 link-local assignment, VRF routing rules for WireGuard handshakes, and integration tests for IPv6 transport. Co-authored-by: Cursor <cursoragent@cursor.com>
Use tunnel-AF-specific fwmark rules so IPv6 transport handshakes work over VRF underlays instead of peer-specific policy routing. Co-authored-by: Cursor <cursoragent@cursor.com>
Move WireGuard netdev creation out of the plugin's custom config (which runs last) and into the initial FRR config, so the interface exists before FRR and the routing modules are configured. A generic _linux_device_type interface attribute tells the initial script which kind of netdev to create, and the deterministic IPv6 link-local (_ipv6_link_local) needed for OSPFv3 is assigned at the same time (kernel address auto-generation is impossible for the ARPHRD_NONE WireGuard device). Drop the optional cryptography fallback; key generation now relies solely on wireguard-tools, and the integration tests use explicit static keys. Docs updated accordingly. Co-authored-by: Cursor <cursoragent@cursor.com>
The global WireGuard listening socket could not receive encrypted packets arriving on a VRF underlay interface for IPv6 tunnels, so handshakes never completed. net.ipv4.udp_l3mdev_accept governs l3mdev socket matching for both IPv4 and IPv6 UDP (there is no net.ipv6 counterpart), but it was only set in the IPv4 branch. Set it unconditionally and collapse the per-AF policy-rule handling into a single command. Co-authored-by: Cursor <cursoragent@cursor.com>
Default tunnel.allowed_ips to ::/0 for IPv6 tunnels (0.0.0.0/0 for IPv4) so IPv6 tunnels work without setting it explicitly. Add an Allowed IPs section explaining WireGuard cryptokey routing and its inbound/outbound semantics, and trim the examples to the plain and VRF/IPv6 cases. Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Set _default for tunnel.af, tunnel.mtu, and tunnel.persistent_keepalive in defaults.yml instead of hard-coding them in post_transform. Only the allowed_ips default stays in code because it depends on the resolved af. Co-authored-by: Cursor <cursoragent@cursor.com>
Replace hardcoded default routes with a dedicated core address pool so the static routes reference the pool rather than 0.0.0.0/0 and ::/0. Co-authored-by: Cursor <cursoragent@cursor.com>
Schema _default values for tunnel.af, mtu, and persistent_keepalive are materialized onto the per-node interface entries during link validation, before link->interface propagation. They then win over an explicit link-level override (e.g. tunnel.af: ipv6) during the interface merge, breaking the tunnel source lookup. Resolve these defaults in post_transform where the interface data is already merged. Also iterate node_iflist with .items() instead of re-indexing by key. Co-authored-by: Cursor <cursoragent@cursor.com>
Infer tunnel.af from the underlay source interface (IPv4 when available, IPv6 for IPv6-only underlays) so IPv6-only links no longer need an explicit tunnel.af. Derive the tunnel interface link-local address from the low 64 bits (interface identifier) of the overlay address via a new routing.get_ipv6_link_local helper, guaranteeing distinct link-locals on both tunnel endpoints. Drop the now-redundant tunnel.af/allowed_ips from the IPv6 integration tests and update the docs. Co-authored-by: Cursor <cursoragent@cursor.com>
Drop the redundant tunnel.mtu schema attribute and reuse the standard interface mtu attribute. When not set explicitly, derive the WireGuard interface MTU from the underlay source interface MTU minus the AF-specific encapsulation overhead (80 bytes for IPv6, 60 bytes for IPv4), so it scales with jumbo-frame underlays. Add underlay MTU settings and a 1500-byte ping validation to the tunnel integration tests, and update the docs. Co-authored-by: Cursor <cursoragent@cursor.com>
… _source_intf Move the persistent_keepalive default back to the attribute schema and rename the internal tunnel._source to tunnel._source_intf to match the _source_intf convention used elsewhere in the codebase. Co-authored-by: Cursor <cursoragent@cursor.com>
Use the node's default MTU (node setting, lab default, or device default) before the hardcoded 1500 when the underlay source interface has no explicit MTU. Co-authored-by: Cursor <cursoragent@cursor.com>
intf.mtu and tunnel.af are always resolved during transformation, so the template default() filters are unnecessary. Co-authored-by: Cursor <cursoragent@cursor.com>
Reword the add_linux_packages docstring: netlab_linux_packages is set as a per-node host var (not device-wide) so wireguard-tools is installed only on nodes with tunnels, starting from the device defaults since the host var replaces the group var. Co-authored-by: Cursor <cursoragent@cursor.com>
Declare features.tunnel.wireguard (with VRF support) so nodes using the default none device pass the tunnel feature check. Co-authored-by: Cursor <cursoragent@cursor.com>
| 'or configure tunnel.private_key and tunnel.public_key)', | ||
| module='tunnel.wireguard') | ||
|
|
||
| def generate_keypair() -> tuple[str, str]: |
There was a problem hiding this comment.
Do we need to use the wg-tools callout's in the generation of keys if they dont exist?
As its rather simple to just let the python handle this with, which i think you partially had in one of your earlier commits?
import base64
from cryptography.hazmat.primitives.asymmetric.x25519 import X25519PrivateKey
from cryptography.hazmat.primitives import serialization
There was a problem hiding this comment.
We could, my thinking was to let WireGuard tools generate the keys for WireGuard tunnels so it's likely to work, but I'm not married to them
There was a problem hiding this comment.
Benefit of it not using those tools is the plugin can be more easily used for other OSs that also might not have the wg-tools or similar package. (thinking of OpenBSD/RouterOS or similar that do have wg implementations)
Plus this appears to be the only use of calling external program from within this python code.
Tested:
Notes:
add_linux_packagesis a candidate for general util method