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');
},
'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'),
};
}
'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'};
$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);
}
}
# 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}}) {
}
}
}
+
+ # 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;