Feature/tunnel wireguard for FRR#3569
Conversation
ipspace
left a comment
There was a problem hiding this comment.
It looks like you (or your AI friend) copied the utils/tunnel.py code, resulting in duplicate functionality. Please fix that.
And yes, I know you'll tell me something about AFs, but we can deal with that.
| def _interface_has_af(intf: Box, af: str) -> bool: | ||
| return af in intf and isinstance(intf[af],str) | ||
|
|
||
| def _interface_matches_constraints( |
There was a problem hiding this comment.
And why exactly did you reinvent the utility function? There's a reason I put it into utils, not into tunnel.gre
There was a problem hiding this comment.
I instructed AI to implement a Wireguard tunnel plugin similar to the GRE tunnel plugin - it took this a bit too literally
| we derive a deterministic address that the initial config script assigns when | ||
| the interface is created. | ||
| ''' | ||
| if intf.get('tunnel.af') != 'ipv6' or 'ipv6' not in intf: |
There was a problem hiding this comment.
If the wireguard tunnels are not dual-stacked (I know I could check), then you have to throw an error, not just check the AF, shrug, and return. If they are dual-stacked, then I don't understand why you're checking tunnel.af (which impacts the underlay interface selection)
|
PR now uses the common |
|
I would suggest you use the new utility functions from #3576, which might make your code cleaner (and with less duplicate code). Also, the new source selection function should give you all the information you need to figure out AF and MTU. |
8a275f9 to
e4d2c21
Compare
ok, updated:
with logic in |
ipspace
left a comment
There was a problem hiding this comment.
Please stop "fixing" imaginary problems, hard-coding stuff where include files work, and changing things for no documented reason.
There are probably other things that have to be fixed; I had enough.
| fi | ||
| {% elif netlab_mgmt_vrf|default(True) %} | ||
| {% else %} | ||
| {% if netlab_linux_packages|default({}) %} |
There was a problem hiding this comment.
Why would you install packages only in FRR container but not in FRR VM?
There was a problem hiding this comment.
Both clab and libvirt providers pass the integration tests, what led you to believe this would only apply to FRR container?
There was a problem hiding this comment.
Stupid me. We install Linux packages unconditionally in libvirt path and check whether netlab_linux_packages is defined in clab path. Maybe we should unify them once this gets merged?
There was a problem hiding this comment.
It's the asymmetric redundant if condition that may have confused you, I'll make it the same
| if [ ! -e /sys/devices/virtual/net/{{ i.ifname }} ]; then | ||
| {% if i.type in ['lag','bond'] %} | ||
| {{ create_bond_dev(i,node_provider) }} | ||
| {% elif i.tunnel.mode|default('') == 'gre' %} |
There was a problem hiding this comment.
No. Why are you "fixing" a problem that does not exist, and why are you doing that in an unrelated PR?
There was a problem hiding this comment.
As requested, I moved device creation to the initial template (including 'tunnel' similar to 'bond'). This in turn broke the GRE plugin, because now tunnel devices got created twice
I can refactor things and use the per-plugin include pattern instead
There was a problem hiding this comment.
How about creating tunnels in the custom config templates? Why do you think you have to create it during the initial device configuration? FRR seems patient...
| dev {{ i.tunnel._source.ifname }} ttl 255 | ||
| {% else %} | ||
| ip link add {{ i.ifname }} type dummy | ||
| ip link add {{ i.ifname }} type {{ 'wireguard' if i.tunnel.mode|default('') == 'wireguard' else 'dummy' }} |
There was a problem hiding this comment.
No. We have a nice solution in #3566. Why do you think it makes sense to hard-code "wireguard" in main FRR initial template just to fix your immediate challenge?
| ip link set dev {{ intf.ifname }} up | ||
| {% endfor %} | ||
| exit 0 | ||
| # Empty file, GRE tunnels are created during initial FRR device configuration |
There was a problem hiding this comment.
Please stop fixing imaginary problems
|
|
||
| ip link set dev "${ifname}" mtu "${mtu}" up | ||
|
|
||
| wait_wg_peer "${ifname}" "${peer_key}" |
There was a problem hiding this comment.
How do you expect this to work between a FRR VM and a FRR container? Also, what happens if the two containers are configured in different batches?
|
|
||
| yield ndata,intf | ||
|
|
||
| def tunnel_source( |
There was a problem hiding this comment.
For the Wireguard plugin I needed to customize each tunnel beyond what the generic code does.
There are many ways to do that, I could simply iterate again too. I thought this might be a nice way to do it - but if you don't like it, we can change it
There was a problem hiding this comment.
Premature optimization at its best... Let's add a generator, and a stub function, with all the accompanying docstrings, just so we don't need an extra for loop 🤦♂️ in code that will be executed once every other blue moon.
Why not? It's either there or in the device quirks.
So we'll modify core logic to address the needs of one device for one plugin? Well, no. Even if it were more common, it's still applicable only to Linux and friends (FRR, BIRD). |
|
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>
…fig. Tunnel netdevs were created in the first loop but skipped the down/sysctl/up cycle that enables accept_ra on host interfaces. Co-authored-by: Cursor <cursoragent@cursor.com>
Defer link-local assignment until after the IPv6 sysctl loop and only bring loopback interfaces up during netdev creation so tunnel down/up does not drop addresses needed for OSPFv3. Co-authored-by: Cursor <cursoragent@cursor.com>
Match the VyOS initial-config pattern for FRR, restore shared tunnel_source in _p2p, and drop WireGuard peer handshake waiting that breaks partial deploys. Co-authored-by: Cursor <cursoragent@cursor.com>
Map tunnel.gre-style config names to slash-separated plugin directories so frr.initial.j2 templates are found during initial device configuration. Co-authored-by: Cursor <cursoragent@cursor.com>
Derive _ipv6_link_local in wireguard_intf_defaults instead of an FRR device quirk, and iterate node_iflist directly in post_transform. Co-authored-by: Cursor <cursoragent@cursor.com>
Inline tunnel AF derivation and scope af_active to the allowed_ips default block. Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Restore GRE from dev and fold WireGuard netdev creation into frr.j2, removing the frr.initial.j2 indirection from initial device config. Co-authored-by: Cursor <cursoragent@cursor.com>
d3fe3e1 to
8cc58f6
Compare
Keep tunnel-specific IPv6 link-local assignment in the WireGuard plugin template instead of the shared initial device configuration. Co-authored-by: Cursor <cursoragent@cursor.com>
Run linux/packages.j2 on containerlab nodes again so per-node packages like wireguard-tools are installed during initial config. Co-authored-by: Cursor <cursoragent@cursor.com>
Use a single interface loop with inlined wg set commands instead of a separate setup_wg_tunnel wrapper function. Co-authored-by: Cursor <cursoragent@cursor.com>
Assume vrf_table and rule_pref are set whenever tunnel.vrf is configured. Co-authored-by: Cursor <cursoragent@cursor.com>
Set peer_ip from the tunnel AF first, then build peer_endpoint from IP and port. Co-authored-by: Cursor <cursoragent@cursor.com>
Compute rule_pref only when tunnel.vrf is set and inline the fwmark conditional. Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Move destination variables below VRF underlay setup and use intf.tunnel.af directly instead of a separate af alias. Co-authored-by: Cursor <cursoragent@cursor.com>
tunnel_destination can run after interface defaults even when earlier steps reported validation errors. Co-authored-by: Cursor <cursoragent@cursor.com>
|
Perfection is achieved, not when there is nothing more to add, but when there is nothing left to take away... |
ipspace
left a comment
There was a problem hiding this comment.
Please try taking out the LLA hack. The current setup works for me without adding manual LLA to the tunnel interface.
Drop the deterministic fe80:: derivation and restore routing.py to dev. Co-authored-by: Cursor <cursoragent@cursor.com>
|
|
||
| for node in node_iflist: | ||
| ndata = topology.nodes[node] | ||
| for intf in _tunnel.interfaces(ndata,'wireguard'): |
There was a problem hiding this comment.
You could have used node_iflist[node] here, but it doesn't matter.
ipspace
left a comment
There was a problem hiding this comment.
Works like a charm ;) Thank you!
Tested:
Notes:
add_linux_packagesis a candidate for general util method