public inbox for pve-devel@lists.proxmox.com
 help / color / mirror / Atom feed
From: Hannes Laimer <h.laimer@proxmox.com>
To: pve-devel@lists.proxmox.com
Subject: [PATCH pve-network v3 5/9] sdn: evpn: add IPv6 RA / SLAAC support
Date: Tue, 23 Jun 2026 14:56:22 +0200	[thread overview]
Message-ID: <20260623125626.1195681-6-h.laimer@proxmox.com> (raw)
In-Reply-To: <20260623125626.1195681-1-h.laimer@proxmox.com>

Configure IPv6 Router Advertisements on EVPN vnets so guests can
autoconfigure addresses via SLAAC and pick up DNS / DHCP hints.

The configuration is split between the VNet and the subnet, matching
the protocol's per-RA / per-prefix layers. Defaults are picked so
the typical SLAAC case needs no per-subnet configuration.

The API rejects each option where it does not apply: the RA toggle
outside EVPN zones, per-prefix overrides on IPv4 subnets, and
explicitly enabling the autonomous flag on non-/64 prefixes.

Signed-off-by: Hannes Laimer <h.laimer@proxmox.com>
---

Notes:
    v3:
     - accept valid IPv6 subnets again, non-/64 and SNAT no longer rejected
     - reject autonomous only when explicitly set on a non-/64
     - validate preferred not above valid, cap both at u32 max
     - name the offending field in validation errors
     - gate RA via a supports_ipv6_ra predicate, breaks the load cycle
     - reject a router lifetime below the RA interval
     - read subnets once from the running snapshot, no pending leak
     - eval guard RA generation, gate it on zone node membership
     - register ipv6-ra-rdnss in encode_value

 src/PVE/Network/SDN.pm                        |  1 +
 src/PVE/Network/SDN/Controllers.pm            | 21 ++++-
 src/PVE/Network/SDN/Controllers/EvpnPlugin.pm | 22 ++++-
 src/PVE/Network/SDN/Controllers/Plugin.pm     |  2 +-
 src/PVE/Network/SDN/SubnetPlugin.pm           | 68 +++++++++++++++
 src/PVE/Network/SDN/VnetPlugin.pm             | 85 +++++++++++++++++++
 src/PVE/Network/SDN/Zones/EvpnPlugin.pm       | 14 ++-
 src/PVE/Network/SDN/Zones/Plugin.pm           |  6 ++
 .../evpn/slaac/expected_controller_config     | 53 ++++++++++++
 .../zones/evpn/slaac/expected_sdn_interfaces  | 43 ++++++++++
 src/test/zones/evpn/slaac/interfaces          |  7 ++
 src/test/zones/evpn/slaac/sdn_config          | 41 +++++++++
 12 files changed, 354 insertions(+), 9 deletions(-)
 create mode 100644 src/test/zones/evpn/slaac/expected_controller_config
 create mode 100644 src/test/zones/evpn/slaac/expected_sdn_interfaces
 create mode 100644 src/test/zones/evpn/slaac/interfaces
 create mode 100644 src/test/zones/evpn/slaac/sdn_config

diff --git a/src/PVE/Network/SDN.pm b/src/PVE/Network/SDN.pm
index 33a3cf3..8f30fe7 100644
--- a/src/PVE/Network/SDN.pm
+++ b/src/PVE/Network/SDN.pm
@@ -516,6 +516,7 @@ sub encode_value {
         || $key eq 'allowed_ips'
         || $key eq 'secondary-controllers'
         || $key eq 'redistribute'
+        || $key eq 'ipv6-ra-rdnss'
     ) {
         if (ref($value) eq 'HASH') {
             return join(',', sort keys(%$value));
diff --git a/src/PVE/Network/SDN/Controllers.pm b/src/PVE/Network/SDN/Controllers.pm
index 4336b86..5a4cb93 100644
--- a/src/PVE/Network/SDN/Controllers.pm
+++ b/src/PVE/Network/SDN/Controllers.pm
@@ -8,6 +8,7 @@ use JSON;
 use PVE::Tools qw(extract_param dir_glob_regex run_command);
 use PVE::Cluster qw(cfs_read_file cfs_write_file cfs_lock_file);
 
+use PVE::Network::SDN::Subnets;
 use PVE::Network::SDN::Vnets;
 use PVE::Network::SDN::Zones;
 
@@ -153,6 +154,18 @@ sub generate_frr_config {
         }
     }
 
+    # Bucket subnets by vnet once, from the same config snapshot the rest of the FRR
+    # config is generated from: the committed running config on apply, the compiled
+    # pending config for the dry-run diff. This keeps frr.conf consistent with the
+    # generated /etc/network/interfaces.d/sdn.
+    my $subnets_by_vnet = {};
+    my $subnet_cfg = $sdn_config->{subnets} // { ids => {} };
+    for my $subnetid (sort keys %{ $subnet_cfg->{ids} }) {
+        my $subnet = PVE::Network::SDN::Subnets::sdn_subnets_config($subnet_cfg, $subnetid);
+        next if !$subnet->{vnet};
+        $subnets_by_vnet->{ $subnet->{vnet} }->{$subnetid} = $subnet;
+    }
+
     foreach my $id (sort keys %{ $vnet_cfg->{ids} }) {
         my $plugin_config = $vnet_cfg->{ids}->{$id};
         my $zoneid = $plugin_config->{zone};
@@ -169,7 +182,13 @@ sub generate_frr_config {
             my $controller_plugin =
                 PVE::Network::SDN::Controllers::Plugin->lookup($controller->{type});
             $controller_plugin->generate_vnet_frr_config(
-                $plugin_config, $controller, $zone, $zoneid, $id, $frr_config,
+                $plugin_config,
+                $controller,
+                $zone,
+                $zoneid,
+                $id,
+                $frr_config,
+                $subnets_by_vnet->{$id} // {},
             );
 
             if (!$zone->{'rt-import'}) {
diff --git a/src/PVE/Network/SDN/Controllers/EvpnPlugin.pm b/src/PVE/Network/SDN/Controllers/EvpnPlugin.pm
index 4220cb6..b9dcb9f 100644
--- a/src/PVE/Network/SDN/Controllers/EvpnPlugin.pm
+++ b/src/PVE/Network/SDN/Controllers/EvpnPlugin.pm
@@ -11,6 +11,7 @@ use PVE::RESTEnvironment qw(log_warn);
 use PVE::Network::SDN::Controllers::Plugin;
 use PVE::Network::SDN::Zones::Plugin;
 use PVE::Network::SDN::Fabrics;
+use PVE::RS::SDN::IPv6RA;
 use Net::IP;
 
 use base('PVE::Network::SDN::Controllers::Plugin');
@@ -584,19 +585,34 @@ sub generate_zone_frr_config {
 }
 
 sub generate_vnet_frr_config {
-    my ($class, $plugin_config, $controller, $zone, $zoneid, $vnetid, $config) = @_;
+    my ($class, $plugin_config, $controller, $zone, $zoneid, $vnetid, $config, $subnets) = @_;
+
+    $subnets //= {};
+
+    my $local_node = PVE::INotify::nodename();
+
+    # Only emit ND/RA config on nodes that are part of the zone, mirroring the node
+    # filtering of the bridge generation in the zones plugin.
+    my $is_zone_member = !defined($zone->{nodes}) || $zone->{nodes}->{$local_node};
+
+    if ($plugin_config->{'ipv6-ra'} && $is_zone_member) {
+        my $nd_iface =
+            eval { PVE::RS::SDN::IPv6RA::build_interface($vnetid, $plugin_config, $subnets); };
+        if (my $err = $@) {
+            log_warn("vnet $vnetid: could not generate IPv6 RA configuration: $err");
+        }
+        $config->{frr}->{nd_interfaces}->{$vnetid} = $nd_iface if $nd_iface;
+    }
 
     my $exitnodes = $zone->{'exitnodes'};
     my $exitnodes_local_routing = $zone->{'exitnodes-local-routing'};
 
     return if !$exitnodes_local_routing;
 
-    my $local_node = PVE::INotify::nodename();
     my $is_gateway = $exitnodes->{$local_node};
 
     return if !$is_gateway;
 
-    my $subnets = PVE::Network::SDN::Vnets::get_subnets($vnetid, 1);
     $config->{frr}->{ip_routes} //= [];
     foreach my $subnetid (sort keys %{$subnets}) {
         my $subnet = $subnets->{$subnetid};
diff --git a/src/PVE/Network/SDN/Controllers/Plugin.pm b/src/PVE/Network/SDN/Controllers/Plugin.pm
index 6da3eec..45870ee 100644
--- a/src/PVE/Network/SDN/Controllers/Plugin.pm
+++ b/src/PVE/Network/SDN/Controllers/Plugin.pm
@@ -93,7 +93,7 @@ sub generate_zone_frr_config {
 }
 
 sub generate_vnet_frr_config {
-    my ($class, $plugin_config, $controller, $zoneid, $vnetid, $config) = @_;
+    my ($class, $plugin_config, $controller, $zone, $zoneid, $vnetid, $config, $subnets) = @_;
 
 }
 
diff --git a/src/PVE/Network/SDN/SubnetPlugin.pm b/src/PVE/Network/SDN/SubnetPlugin.pm
index e2a0e50..078fed7 100644
--- a/src/PVE/Network/SDN/SubnetPlugin.pm
+++ b/src/PVE/Network/SDN/SubnetPlugin.pm
@@ -177,6 +177,39 @@ sub properties {
             description => 'IP address for the DNS server',
             optional => 1,
         },
+        # Per-prefix Router Advertisement overrides. Only meaningful for IPv6 subnets
+        # whose vnet has `ipv6-ra` enabled, silently ignored otherwise.
+        'nd-prefix-autonomous' => {
+            type => 'boolean',
+            description =>
+                "Set the autonomous (A) flag for this prefix in RAs, enabling SLAAC for"
+                . " hosts on this subnet. SLAAC requires a /64 prefix, so this defaults to"
+                . " enabled on /64 subnets and disabled (and not enablable) otherwise.",
+            optional => 1,
+        },
+        'nd-prefix-on-link' => {
+            type => 'boolean',
+            description =>
+                "Set the on-link (L) flag for this prefix in RAs (default: on-link).",
+            optional => 1,
+            default => 1,
+        },
+        'nd-prefix-valid-lifetime' => {
+            type => 'integer',
+            description => "Valid lifetime for this prefix in RAs, in seconds.",
+            minimum => 0,
+            maximum => 4294967295,
+            default => 2592000,
+            optional => 1,
+        },
+        'nd-prefix-preferred-lifetime' => {
+            type => 'integer',
+            description => "Preferred lifetime for this prefix in RAs, in seconds.",
+            minimum => 0,
+            maximum => 4294967295,
+            default => 604800,
+            optional => 1,
+        },
     };
 }
 
@@ -189,6 +222,10 @@ sub options {
         dnszoneprefix => { optional => 1 },
         'dhcp-range' => { optional => 1 },
         'dhcp-dns-server' => { optional => 1 },
+        'nd-prefix-autonomous' => { optional => 1 },
+        'nd-prefix-on-link' => { optional => 1 },
+        'nd-prefix-valid-lifetime' => { optional => 1 },
+        'nd-prefix-preferred-lifetime' => { optional => 1 },
     };
 }
 
@@ -206,6 +243,37 @@ sub on_update_hook {
     my $dns = $zone->{dns};
     my $dnszone = $zone->{dnszone};
     my $reversedns = $zone->{reversedns};
+    my $is_ipv6 = PVE::JSONSchema::pve_verify_cidrv6($cidr, 1);
+
+    # Per-prefix RA overrides only apply on IPv6 subnets. Reject explicit overrides on
+    # IPv4 subnets so the user notices their config has no effect.
+    if (!$is_ipv6) {
+        for my $opt (
+            qw(nd-prefix-autonomous nd-prefix-on-link nd-prefix-valid-lifetime nd-prefix-preferred-lifetime)
+        ) {
+            raise_param_exc({ $opt => "only valid on IPv6 subnets" })
+                if defined($subnet->{$opt});
+        }
+    }
+
+    # SLAAC requires a /64 prefix (RFC 4862), so the autonomous flag defaults to enabled
+    # only on /64 subnets. Explicitly enabling it elsewhere can never work, reject it.
+    if ($is_ipv6 && $mask != 64 && $subnet->{'nd-prefix-autonomous'}) {
+        raise_param_exc(
+            { 'nd-prefix-autonomous' => "autonomous (SLAAC) flag requires a /64 prefix" });
+    }
+
+    # The preferred lifetime must not exceed the valid lifetime (RFC 4861), FRR rejects
+    # such prefix lines at config load. Mixed explicit/default cases are clamped when the
+    # FRR config is built.
+    my $nd_valid = $subnet->{'nd-prefix-valid-lifetime'};
+    my $nd_preferred = $subnet->{'nd-prefix-preferred-lifetime'};
+    if (defined($nd_valid) && defined($nd_preferred) && $nd_preferred > $nd_valid) {
+        raise_param_exc({
+            'nd-prefix-preferred-lifetime' =>
+                "preferred lifetime must not be greater than the valid lifetime ($nd_valid)",
+        });
+    }
 
     my $mac = undef;
 
diff --git a/src/PVE/Network/SDN/VnetPlugin.pm b/src/PVE/Network/SDN/VnetPlugin.pm
index e041575..2479d7c 100644
--- a/src/PVE/Network/SDN/VnetPlugin.pm
+++ b/src/PVE/Network/SDN/VnetPlugin.pm
@@ -83,6 +83,57 @@ sub properties {
                 "If true, sets the isolated property for all interfaces on the bridge of this VNet.",
             optional => 1,
         },
+        # IPv6 Router Advertisement settings (EVPN zones only). The master toggle gates
+        # the rest. Per-prefix flags live on the subnet (nd-prefix-*).
+        'ipv6-ra' => {
+            type => 'boolean',
+            description =>
+                "Emit IPv6 Router Advertisements on this VNet's bridge. Requires the VNet to belong"
+                . " to an EVPN zone with at least one IPv6 subnet.",
+            optional => 1,
+        },
+        'ipv6-ra-managed' => {
+            type => 'boolean',
+            description => "Set the managed-address (M) flag in emitted RAs.",
+            optional => 1,
+        },
+        'ipv6-ra-other' => {
+            type => 'boolean',
+            description => "Set the other-configuration (O) flag in emitted RAs.",
+            optional => 1,
+        },
+        'ipv6-ra-rdnss' => {
+            type => 'array',
+            description => "RDNSS (Recursive DNS Server) addresses advertised in RAs.",
+            optional => 1,
+            items => {
+                type => 'string',
+                format => 'ipv6',
+            },
+        },
+        'ipv6-ra-router-lifetime' => {
+            type => 'integer',
+            description =>
+                "Router lifetime advertised in RAs (seconds). 0 tells hosts not to use this"
+                . " router as a default gateway.",
+            optional => 1,
+            minimum => 0,
+            maximum => 9000,
+        },
+        'ipv6-ra-interval' => {
+            type => 'integer',
+            description => "Maximum interval between unsolicited RAs (seconds).",
+            optional => 1,
+            minimum => 4,
+            maximum => 1800,
+        },
+        'ipv6-ra-mtu' => {
+            type => 'integer',
+            description => "MTU advertised in RAs.",
+            optional => 1,
+            minimum => 1280,
+            maximum => 65535,
+        },
     };
 }
 
@@ -93,6 +144,13 @@ sub options {
         alias => { optional => 1 },
         vlanaware => { optional => 1 },
         'isolate-ports' => { optional => 1 },
+        'ipv6-ra' => { optional => 1 },
+        'ipv6-ra-managed' => { optional => 1 },
+        'ipv6-ra-other' => { optional => 1 },
+        'ipv6-ra-rdnss' => { optional => 1 },
+        'ipv6-ra-router-lifetime' => { optional => 1 },
+        'ipv6-ra-interval' => { optional => 1 },
+        'ipv6-ra-mtu' => { optional => 1 },
     };
 }
 
@@ -117,6 +175,33 @@ sub on_update_hook {
         raise_param_exc({ vlanaware => "vlanaware vnet is not compatible with subnets" })
             if $subnets;
     }
+
+    # RA settings are only consumed by zones whose plugin emits RAs (currently EVPN).
+    # Reject them elsewhere so the user notices their config has no effect. Loaded at
+    # runtime to avoid a compile-time module cycle (Zones -> Vnets -> VnetPlugin).
+    if (my @ra_opts = sort grep { /^ipv6-ra/ } keys %$vnet) {
+        require PVE::Network::SDN::Zones;
+        require PVE::Network::SDN::Zones::Plugin;
+
+        my $zone_cfg = PVE::Network::SDN::Zones::config();
+        my $zone = $zone_cfg->{ids}->{ $vnet->{zone} };
+        my $zone_plugin =
+            $zone ? eval { PVE::Network::SDN::Zones::Plugin->lookup($zone->{type}) } : undef;
+
+        raise_param_exc({ $ra_opts[0] => "IPv6 RA options are only supported on EVPN zones" })
+            if !$zone_plugin || !$zone_plugin->supports_ipv6_ra();
+    }
+
+    # RFC 4861: a non-zero router lifetime must be at least the maximum RA interval
+    # (FRR default: 600s), FRR rejects the lifetime at config load otherwise.
+    my $ra_lifetime = $vnet->{'ipv6-ra-router-lifetime'};
+    my $ra_interval = $vnet->{'ipv6-ra-interval'} // 600;
+    if (defined($ra_lifetime) && $ra_lifetime != 0 && $ra_lifetime < $ra_interval) {
+        raise_param_exc({
+            'ipv6-ra-router-lifetime' =>
+                "router lifetime must be 0 or at least the RA interval ($ra_interval seconds)",
+        });
+    }
 }
 
 1;
diff --git a/src/PVE/Network/SDN/Zones/EvpnPlugin.pm b/src/PVE/Network/SDN/Zones/EvpnPlugin.pm
index dfbd7e9..d8bfa80 100644
--- a/src/PVE/Network/SDN/Zones/EvpnPlugin.pm
+++ b/src/PVE/Network/SDN/Zones/EvpnPlugin.pm
@@ -266,12 +266,14 @@ sub generate_sdn_config {
             $enable_forward_v4 = 1 if $gateway;
         }
 
-        if ($subnet->{snat}) {
+        if ($subnet->{snat} && $is_evpn_gateway) {
 
-            #find outgoing interface
+            #find outgoing interface, but don't fail the whole vnet on a node that
+            #cannot route the check address (e.g. no IPv6 connectivity)
             my ($outip, $outiface) =
-                PVE::Network::SDN::Zones::Plugin::get_local_route_ip($checkrouteip);
-            if ($outip && $outiface && $is_evpn_gateway) {
+                eval { PVE::Network::SDN::Zones::Plugin::get_local_route_ip($checkrouteip) };
+            warn "vnet $vnetid: not generating SNAT rules for $cidr: $@" if $@;
+            if ($outip && $outiface) {
                 #use snat, faster than masquerade
                 push @iface_config,
                     "post-up $iptables -t nat -A POSTROUTING -s '$cidr' -o $outiface -j SNAT --to-source $outip";
@@ -431,5 +433,9 @@ sub vnet_update_hook {
     }
 }
 
+sub supports_ipv6_ra {
+    return 1;
+}
+
 1;
 
diff --git a/src/PVE/Network/SDN/Zones/Plugin.pm b/src/PVE/Network/SDN/Zones/Plugin.pm
index 74a3384..a0adfe9 100644
--- a/src/PVE/Network/SDN/Zones/Plugin.pm
+++ b/src/PVE/Network/SDN/Zones/Plugin.pm
@@ -163,6 +163,12 @@ sub vnet_update_hook {
     # do nothing by default
 }
 
+# Whether vnets in this zone type can emit IPv6 Router Advertisements (ipv6-ra* vnet
+# options). Zone plugins that generate RA configuration override this.
+sub supports_ipv6_ra {
+    return 0;
+}
+
 #helpers
 sub parse_tag_number_or_range {
     my ($str, $max, $tag) = @_;
diff --git a/src/test/zones/evpn/slaac/expected_controller_config b/src/test/zones/evpn/slaac/expected_controller_config
new file mode 100644
index 0000000..7d59591
--- /dev/null
+++ b/src/test/zones/evpn/slaac/expected_controller_config
@@ -0,0 +1,53 @@
+frr version 10.6.1
+frr defaults datacenter
+hostname localhost
+log syslog informational
+service integrated-vtysh-config
+!
+vrf vrf_myzone
+ vni 1000
+exit-vrf
+!
+router bgp 65000
+ bgp router-id 192.168.0.1
+ no bgp hard-administrative-reset
+ no bgp default ipv4-unicast
+ coalesce-time 1000
+ no bgp graceful-restart notification
+ neighbor VTEP peer-group
+ neighbor VTEP remote-as 65000
+ neighbor VTEP bfd
+ neighbor 192.168.0.2 peer-group VTEP
+ neighbor 192.168.0.3 peer-group VTEP
+ !
+ address-family l2vpn evpn
+  neighbor VTEP activate
+  neighbor VTEP route-map MAP_VTEP_IN in
+  neighbor VTEP route-map MAP_VTEP_OUT out
+  advertise-all-vni
+ exit-address-family
+exit
+!
+router bgp 65000 vrf vrf_myzone
+ bgp router-id 192.168.0.1
+ no bgp hard-administrative-reset
+ no bgp graceful-restart notification
+exit
+!
+route-map MAP_VTEP_IN permit 1
+exit
+!
+route-map MAP_VTEP_OUT permit 1
+exit
+!
+interface myvnet
+ no ipv6 nd suppress-ra
+ ipv6 nd managed-config-flag
+ ipv6 nd other-config-flag
+ ipv6 nd rdnss 2a08:2142:302:3::53
+ ipv6 nd prefix 2a08:2142:302:3::/64 7200 3600
+ ipv6 nd prefix fd00:1::/64 2592000 604800 no-autoconfig
+exit
+!
+line vty
+!
diff --git a/src/test/zones/evpn/slaac/expected_sdn_interfaces b/src/test/zones/evpn/slaac/expected_sdn_interfaces
new file mode 100644
index 0000000..2e479a3
--- /dev/null
+++ b/src/test/zones/evpn/slaac/expected_sdn_interfaces
@@ -0,0 +1,43 @@
+#version:1
+
+auto myvnet
+iface myvnet
+	address 2a08:2142:302:3::1/64
+	address fd00:1::1/64
+	hwaddress A2:1D:CB:1A:C0:8B
+	bridge_ports vxlan_myvnet
+	bridge_stp off
+	bridge_fd 0
+	mtu 1450
+	ip6-forward on
+	arp-accept on
+	vrf vrf_myzone
+
+auto vrf_myzone
+iface vrf_myzone
+	vrf-table auto
+	post-up ip route add vrf vrf_myzone unreachable default metric 4278198272
+
+auto vrfbr_myzone
+iface vrfbr_myzone
+	bridge-ports vrfvx_myzone
+	bridge_stp off
+	bridge_fd 0
+	mtu 1450
+	vrf vrf_myzone
+
+auto vrfvx_myzone
+iface vrfvx_myzone
+	vxlan-id 1000
+	vxlan-local-tunnelip 192.168.0.1
+	bridge-learning off
+	bridge-arp-nd-suppress on
+	mtu 1450
+
+auto vxlan_myvnet
+iface vxlan_myvnet
+	vxlan-id 100
+	vxlan-local-tunnelip 192.168.0.1
+	bridge-learning off
+	bridge-arp-nd-suppress on
+	mtu 1450
diff --git a/src/test/zones/evpn/slaac/interfaces b/src/test/zones/evpn/slaac/interfaces
new file mode 100644
index 0000000..66bb826
--- /dev/null
+++ b/src/test/zones/evpn/slaac/interfaces
@@ -0,0 +1,7 @@
+auto vmbr0
+iface vmbr0 inet static
+	address 192.168.0.1/24
+	gateway 192.168.0.254
+        bridge-ports eth0
+        bridge-stp off
+        bridge-fd 0
diff --git a/src/test/zones/evpn/slaac/sdn_config b/src/test/zones/evpn/slaac/sdn_config
new file mode 100644
index 0000000..e6294e3
--- /dev/null
+++ b/src/test/zones/evpn/slaac/sdn_config
@@ -0,0 +1,41 @@
+{
+  version => 1,
+  vnets   => {
+               ids => {
+                        myvnet => {
+                                    tag => "100",
+                                    type => "vnet",
+                                    zone => "myzone",
+                                    'ipv6-ra' => 1,
+                                    'ipv6-ra-managed' => 1,
+                                    'ipv6-ra-other' => 1,
+                                    'ipv6-ra-rdnss' => ['2a08:2142:302:3::53'],
+                                  },
+                      },
+             },
+
+  zones   => {
+               ids => { myzone => { ipam => "pve", type => "evpn", controller => "evpnctl", 'vrf-vxlan' => 1000, 'mac' => 'A2:1D:CB:1A:C0:8B' } },
+             },
+  controllers  => {
+               ids => { evpnctl => { type => "evpn", 'peers' => '192.168.0.1,192.168.0.2,192.168.0.3', asn => "65000" } },
+             },
+
+  subnets => {
+              ids => {
+                        'myzone-2a08:2142:302:3::-64' => {
+                                                           'type' => 'subnet',
+                                                           'vnet' => 'myvnet',
+                                                           'gateway' => '2a08:2142:302:3::1',
+                                                           'nd-prefix-valid-lifetime' => 7200,
+                                                           'nd-prefix-preferred-lifetime' => 3600,
+                                                         },
+                        'myzone-fd00:1::-64' => {
+                                                  'type' => 'subnet',
+                                                  'vnet' => 'myvnet',
+                                                  'gateway' => 'fd00:1::1',
+                                                  'nd-prefix-autonomous' => 0,
+                                                },
+                     }
+             }
+}
-- 
2.47.3





  parent reply	other threads:[~2026-06-23 12:58 UTC|newest]

Thread overview: 10+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-06-23 12:56 [PATCH docs/manager/network/proxmox{-perl-rs,-ve-rs} v3 0/9] add IPv6 RA / SLAAC support to EVPN zones Hannes Laimer
2026-06-23 12:56 ` [PATCH proxmox-perl-rs v3 1/9] pve-rs: sdn: add IPv6 RA builder binding Hannes Laimer
2026-06-23 12:56 ` [PATCH proxmox-ve-rs v3 2/9] frr: add IPv6 router advertisement support Hannes Laimer
2026-06-23 12:56 ` [PATCH proxmox-ve-rs v3 3/9] ve-config: add per-vnet IPv6 RA configuration Hannes Laimer
2026-06-23 12:56 ` [PATCH pve-manager v3 4/9] ui: sdn: add IPv6 RA / SLAAC support Hannes Laimer
2026-06-23 12:56 ` Hannes Laimer [this message]
2026-06-23 12:56 ` [PATCH pve-network v3 6/9] sdn: evpn: derive IP version from CIDR for gateway-less subnets Hannes Laimer
2026-06-23 12:56 ` [PATCH pve-network v3 7/9] sdn: evpn: accept untracked IPv6 NA on EVPN vnet bridges Hannes Laimer
2026-06-23 12:56 ` [PATCH pve-network v3 8/9] api: vnet: include zone-type in vnet list Hannes Laimer
2026-06-23 12:56 ` [PATCH pve-docs v3 9/9] sdn: add IPv6 RA / SLAAC section Hannes Laimer

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20260623125626.1195681-6-h.laimer@proxmox.com \
    --to=h.laimer@proxmox.com \
    --cc=pve-devel@lists.proxmox.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
Service provided by Proxmox Server Solutions GmbH | Privacy | Legal