]> git.proxmox.com Git - pve-network.git/blobdiff - PVE/Network/SDN/VxlanPlugin.pm
vxlan: add gateway-nodes option
[pve-network.git] / PVE / Network / SDN / VxlanPlugin.pm
index c93dbc45520bd242a2f041d0e98b2c6722dc9ee4..ec1729c6d50c31fe7a33275d3cf3da0090d98f74 100644 (file)
@@ -4,6 +4,8 @@ use strict;
 use warnings;
 use PVE::Network::SDN::Plugin;
 use PVE::Tools;
+use PVE::INotify;
+use PVE::JSONSchema qw(get_standard_option);
 
 use base('PVE::Network::SDN::Plugin');
 
@@ -28,20 +30,25 @@ sub properties {
         },
         'multicast-address' => {
             description => "Multicast address.",
-            type => 'string',  #fixme: format 
+            type => 'string',  #fixme: format
         },
        'unicast-address' => {
            description => "Unicast peers address ip list.",
-           type => 'string',  #fixme: format 
+           type => 'string',  #fixme: format
        },
        'vrf' => {
            description => "vrf name.",
-           type => 'string',  #fixme: format 
+           type => 'string',  #fixme: format
        },
        'vrf-vxlan' => {
            type => 'integer',
            description => "l3vni.",
        },
+       'router' => {
+           type => 'string',
+           description => "Frr router name",
+       },
+       'gateway-nodes' => get_standard_option('pve-node-list'),
     };
 }
 
@@ -54,15 +61,20 @@ sub options {
         'vxlan-allowed' => { optional => 1 },
         'vrf' => { optional => 1 },
         'vrf-vxlan' => { optional => 1 },
+        'router' => { optional => 1 },
+        'gateway-nodes' => { optional => 1 },
     };
 }
 
 # Plugin implementation
 sub generate_sdn_config {
-    my ($class, $plugin_config, $zoneid, $vnetid, $vnet, $uplinks) = @_;
+    my ($class, $plugin_config, $zoneid, $vnetid, $vnet, $uplinks, $config) = @_;
 
     my $tag = $vnet->{tag};
     my $alias = $vnet->{alias};
+    my $ipv4 = $vnet->{ipv4};
+    my $ipv6 = $vnet->{ipv6};
+    my $mac = $vnet->{mac};
     my $multicastaddress = $plugin_config->{'multicast-address'};
     my @unicastaddress = split(',', $plugin_config->{'unicast-address'}) if $plugin_config->{'unicast-address'};
 
@@ -84,77 +96,130 @@ sub generate_sdn_config {
     $mtu = $uplinks->{$uplink}->{mtu} - 50 if $uplinks->{$uplink}->{mtu};
     $mtu = $vnet->{mtu} if $vnet->{mtu};
 
-    my $config = "\n";
-    $config .= "auto vxlan$vnetid\n";
-    $config .= "iface vxlan$vnetid inet manual\n";
-    $config .= "       vxlan-id $tag\n";
+    #vxlan interface
+    my @iface_config = ();
+    push @iface_config, "vxlan-id $tag";
 
     if($multicastaddress) {
-       $config .= "       vxlan-svcnodeip $multicastaddress\n";
-       $config .= "       vxlan-physdev $iface\n";
+       push @iface_config, "vxlan-svcnodeip $multicastaddress";
+       push @iface_config, "vxlan-physdev $iface";
     } elsif (@unicastaddress) {
 
        foreach my $address (@unicastaddress) {
            next if $address eq $ifaceip;
-           $config .= "       vxlan_remoteip $address\n";
+           push @iface_config, "vxlan_remoteip $address";
        }
     } else {
-       $config .= "       vxlan-local-tunnelip $ifaceip\n" if $ifaceip;
-       $config .= "       bridge-learning off\n";
-       $config .= "       bridge-arp-nd-suppress on\n";
+       push @iface_config, "vxlan-local-tunnelip $ifaceip" if $ifaceip;
+       push @iface_config, "bridge-learning off";
+       push @iface_config, "bridge-arp-nd-suppress on";
     }
 
-    $config .= "       mtu $mtu\n" if $mtu;
-    $config .= "\n";
-    $config .= "auto $vnetid\n";
-    $config .= "iface $vnetid inet manual\n";
-    $config .= "       bridge_ports vxlan$vnetid\n";
-    $config .= "       bridge_stp off\n";
-    $config .= "       bridge_fd 0\n";
-    $config .= "       mtu $mtu\n" if $mtu;
-    $config .= "       alias $alias\n" if $alias;
-    $config .= "       vrf $vrf\n" if $vrf;
+    push @iface_config, "mtu $mtu" if $mtu;
+    push(@{$config->{"vxlan$vnetid"}}, @iface_config) if !$config->{"vxlan$vnetid"};
+
+    #vnet bridge
+    @iface_config = ();
+    push @iface_config, "address $ipv4" if $ipv4;
+    push @iface_config, "address $ipv6" if $ipv6;
+    push @iface_config, "hwaddress $mac" if $mac;
+    push @iface_config, "bridge_ports vxlan$vnetid";
+    push @iface_config, "bridge_stp off";
+    push @iface_config, "bridge_fd 0";
+    push @iface_config, "mtu $mtu" if $mtu;
+    push @iface_config, "alias $alias" if $alias;
+    push @iface_config, "vrf $vrf" if $vrf;
+    push(@{$config->{$vnetid}}, @iface_config) if !$config->{$vnetid};
 
     if ($vrf) {
-       $config .= "\n";
-       $config .= "auto $vrf\n";
-       $config .= "iface $vrf\n";
-       $config .= "       vrf-table auto\n";
+       #vrf intreface
+       @iface_config = ();
+       push @iface_config, "vrf-table auto";
+       push(@{$config->{$vrf}}, @iface_config) if !$config->{$vrf};
 
        if ($vrfvxlan) {
-
-           my $vxlanvrf = "vxlan$vrf";
+           #l3vni vxlan interface
+           my $iface_vxlan = "vxlan$vrf";
+           @iface_config = ();
+           push @iface_config, "vxlan-id $vrfvxlan";
+           push @iface_config, "vxlan-local-tunnelip $ifaceip" if $ifaceip;
+           push @iface_config, "bridge-learning off";
+           push @iface_config, "bridge-arp-nd-suppress on";
+           push @iface_config, "mtu $mtu" if $mtu;
+           push(@{$config->{$iface_vxlan}}, @iface_config) if !$config->{$iface_vxlan};
+
+           #l3vni bridge
            my $brvrf = "br$vrf";
-
-           $config .= "\n";
-           $config .= "auto $vxlanvrf\n";
-           $config .= "iface $vxlanvrf\n";
-           $config .= "        vxlan-id $vrfvxlan\n";
-           $config .= "        vxlan-local-tunnelip $ifaceip\n" if $ifaceip;
-           $config .= "        bridge-learning off\n";
-           $config .= "        bridge-arp-nd-suppress on\n";
-           $config .= "        mtu $mtu\n" if $mtu;
-
-           $config .= "\n";
-           $config .= "auto $brvrf\n";
-           $config .= "        bridge-ports $vxlanvrf\n";
-           $config .= "        bridge_stp off\n";
-           $config .= "        bridge_fd 0\n";
-           $config .= "        mtu $mtu\n" if $mtu;
-           $config .= "        vrf $vrf\n";
+           @iface_config = ();
+           push @iface_config, "bridge-ports $iface_vxlan";
+           push @iface_config, "bridge_stp off";
+           push @iface_config, "bridge_fd 0";
+           push @iface_config, "mtu $mtu" if $mtu;
+           push @iface_config, "vrf $vrf";
+           push(@{$config->{$brvrf}}, @iface_config) if !$config->{$brvrf};
        }
     }
 
     return $config;
 }
 
+sub generate_frr_config {
+    my ($class, $plugin_config, $asn, $id, $uplinks, $config) = @_;
+
+    my $vrf = $plugin_config->{'vrf'};
+    my $vrfvxlan = $plugin_config->{'vrf-vxlan'};
+    my $gatewaynodes = $plugin_config->{'gateway-nodes'};
+
+    return if !$vrf || !$vrfvxlan;
+
+    #vrf
+    my @router_config = ();
+    push @router_config, "vni $vrfvxlan";
+    push @router_config, "exit-vrf";
+    push(@{$config->{vrf}->{"vrf $vrf"}}, @router_config);
+
+
+    @router_config = ();
+
+    my $is_gateway = undef;
+    my $local_node = PVE::INotify::nodename();
+
+    foreach my $gatewaynode (PVE::Tools::split_list($gatewaynodes)) {
+       $is_gateway = 1 if $gatewaynode eq $local_node;
+    }
+
+    if ($is_gateway) {
+
+       @router_config = ();
+       #import /32 routes of evpn network from vrf1 to default vrf (for packet return)
+       #frr 7.1 tag is bugged -> works fine with 7.1 stable branch(20190829-02-g6ba76bbc1)
+       #https://github.com/FRRouting/frr/issues/4905
+       push @router_config, "!";
+       push @router_config, "address-family ipv4 unicast";
+       push @router_config, " import vrf $vrf";
+       push @router_config, "exit-address-family";
+       push(@{$config->{router}->{"router bgp $asn"}}, @router_config);
+
+       @router_config = ();
+
+       #add default originate to announce 0.0.0.0/0 type5 route in evpn
+       push @router_config, "!";
+       push @router_config, "address-family l2vpn evpn";
+       push @router_config, " default-originate ipv4";
+       push @router_config, "exit-address-family";
+       push(@{$config->{router}->{"router bgp $asn vrf $vrf"}}, @router_config);
+    }
+
+    return $config;
+}
+
 sub on_delete_hook {
     my ($class, $transportid, $sdn_cfg) = @_;
 
     # verify that no vnet are associated to this transport
     foreach my $id (keys %{$sdn_cfg->{ids}}) {
        my $sdn = $sdn_cfg->{ids}->{$id};
-       die "transport $transportid is used by vnet $id" 
+       die "transport $transportid is used by vnet $id"
            if ($sdn->{type} eq 'vnet' && defined($sdn->{transportzone}) && $sdn->{transportzone} eq $transportid);
     }
 }
@@ -166,7 +231,7 @@ sub on_update_hook {
 
     # verify that vxlan-allowed don't conflict with another vxlan-allowed transport
 
-    # verify that vxlan-allowed is matching currently vnet tag in this transport  
+    # verify that vxlan-allowed is matching currently vnet tag in this transport
     my $vxlanallowed = $transport->{'vxlan-allowed'};
     if ($vxlanallowed) {
        foreach my $id (keys %{$sdn_cfg->{ids}}) {
@@ -184,6 +249,41 @@ sub on_update_hook {
            }
        }
     }
+
+    # verify that router exist
+    if (defined($sdn_cfg->{ids}->{$transportid}->{router})) {
+       my $router = $sdn_cfg->{ids}->{$transportid}->{router};
+       if (!defined($sdn_cfg->{ids}->{$router})) {
+           die "router $router don't exist";
+       } else {
+           die "$router is not a router type" if $sdn_cfg->{ids}->{$router}->{type} ne 'frr';
+       }
+
+       #vrf && vrf-vxlan need to be defined with router
+       my $vrf = $sdn_cfg->{ids}->{$transportid}->{vrf};
+       if (!defined($vrf)) {
+           die "missing vrf option";
+       } else {
+           # verify that vrf is not already declared in another transport
+           foreach my $id (keys %{$sdn_cfg->{ids}}) {
+               next if $id eq $transportid;
+               die "vrf $vrf is already declared in $id"
+                       if (defined($sdn_cfg->{ids}->{$id}->{vrf}) && $sdn_cfg->{ids}->{$id}->{vrf} eq $vrf);
+           }
+       }
+
+       my $vrfvxlan = $sdn_cfg->{ids}->{$transportid}->{'vrf-vxlan'};
+       if (!defined($vrfvxlan)) {
+           die "missing vrf-vxlan option";
+       } else {
+           # verify that vrf-vxlan is not already declared in another transport
+           foreach my $id (keys %{$sdn_cfg->{ids}}) {
+               next if $id eq $transportid;
+               die "vrf-vxlan $vrfvxlan is already declared in $id"
+                       if (defined($sdn_cfg->{ids}->{$id}->{'vrf-vxlan'}) && $sdn_cfg->{ids}->{$id}->{'vrf-vxlan'} eq $vrfvxlan);
+           }
+       }
+    }
 }
 
 1;