X-Git-Url: https://git.proxmox.com/?a=blobdiff_plain;f=PVE%2FNetwork%2FSDN%2FControllers%2FEvpnPlugin.pm;h=22480d41547ebd2d8c60ccef83fcd0d7bc73b38a;hb=916488cccaa2d592b8701b32fc9c1fd82677fbfd;hp=6927921ebac455fdea527ce9b69289d711f9410a;hpb=b634e5772a1ce053e59330e8959358c28f7de772;p=pve-network.git diff --git a/PVE/Network/SDN/Controllers/EvpnPlugin.pm b/PVE/Network/SDN/Controllers/EvpnPlugin.pm index 6927921..22480d4 100644 --- a/PVE/Network/SDN/Controllers/EvpnPlugin.pm +++ b/PVE/Network/SDN/Controllers/EvpnPlugin.pm @@ -22,6 +22,8 @@ sub properties { asn => { type => 'integer', description => "autonomous system number", + minimum => 0, + maximum => 4294967296 }, peers => { description => "peers address list.", @@ -97,11 +99,17 @@ sub generate_controller_config { # address-family l2vpn @controller_config = (); + push @controller_config, "neighbor VTEP route-map MAP_VTEP_IN in"; + push @controller_config, "neighbor VTEP route-map MAP_VTEP_OUT out"; push @controller_config, "neighbor VTEP activate"; push @controller_config, "advertise-all-vni"; push @controller_config, "autort as $autortas" if $autortas; push(@{$bgp->{"address-family"}->{"l2vpn evpn"}}, @controller_config); + my $routemap = { rule => undef, action => "permit" }; + push(@{$config->{frr_routemap}->{'MAP_VTEP_IN'}}, $routemap ); + push(@{$config->{frr_routemap}->{'MAP_VTEP_OUT'}}, $routemap ); + return $config; } @@ -113,8 +121,13 @@ sub generate_controller_zone_config { my $vrf = "vrf_$id"; my $vrfvxlan = $plugin_config->{'vrf-vxlan'}; my $exitnodes = $plugin_config->{'exitnodes'}; + my $exitnodes_primary = $plugin_config->{'exitnodes-primary'}; + my $advertisesubnets = $plugin_config->{'advertise-subnets'}; + my $exitnodes_local_routing = $plugin_config->{'exitnodes-local-routing'}; + my $rt_import = [PVE::Tools::split_list($plugin_config->{'rt-import'})] if $plugin_config->{'rt-import'}; my $asn = $controller->{asn}; + my @peers = PVE::Tools::split_list($controller->{'peers'}) if $controller->{'peers'}; my $ebgp = undef; my $loopback = undef; my $autortas = undef; @@ -128,6 +141,8 @@ sub generate_controller_zone_config { return if !$vrf || !$vrfvxlan || !$asn; + my ($ifaceip, $interface) = PVE::Network::SDN::Zones::Plugin::find_local_ip_interface_peers(\@peers, $loopback); + # vrf my @controller_config = (); push @controller_config, "vni $vrfvxlan"; @@ -135,7 +150,7 @@ sub generate_controller_zone_config { #main vrf router @controller_config = (); - push @controller_config, "no bgp ebgp-requires-policy" if $ebgp; + push @controller_config, "bgp router-id $ifaceip"; # push @controller_config, "!"; push(@{$config->{frr}->{router}->{"bgp $asn vrf $vrf"}->{""}}, @controller_config); @@ -148,28 +163,90 @@ sub generate_controller_zone_config { if ($is_gateway) { + if(!$exitnodes_primary || $exitnodes_primary eq $local_node) { + #filter default type5 route coming from other exit nodes on primary node or both nodes if no primary is defined. + my $routemap_config = (); + push @{$routemap_config}, "match evpn route-type prefix"; + my $routemap = { rule => $routemap_config, action => "deny" }; + unshift(@{$config->{frr_routemap}->{'MAP_VTEP_IN'}}, $routemap); + } elsif ($exitnodes_primary ne $local_node) { + my $routemap_config = (); + push @{$routemap_config}, "match evpn vni $vrfvxlan"; + push @{$routemap_config}, "match evpn route-type prefix"; + push @{$routemap_config}, "set metric 200"; + my $routemap = { rule => $routemap_config, action => "permit" }; + unshift(@{$config->{frr_routemap}->{'MAP_VTEP_OUT'}}, $routemap); + } + + + if (!$exitnodes_local_routing) { + @controller_config = (); + #import /32 routes of evpn network from vrf1 to default vrf (for packet return) + push @controller_config, "import vrf $vrf"; + push(@{$config->{frr}->{router}->{"bgp $asn"}->{"address-family"}->{"ipv4 unicast"}}, @controller_config); + push(@{$config->{frr}->{router}->{"bgp $asn"}->{"address-family"}->{"ipv6 unicast"}}, @controller_config); + + @controller_config = (); + #redistribute connected to be able to route to local vms on the gateway + push @controller_config, "redistribute connected"; + push(@{$config->{frr}->{router}->{"bgp $asn vrf $vrf"}->{"address-family"}->{"ipv4 unicast"}}, @controller_config); + push(@{$config->{frr}->{router}->{"bgp $asn vrf $vrf"}->{"address-family"}->{"ipv6 unicast"}}, @controller_config); + } + @controller_config = (); - #import /32 routes of evpn network from vrf1 to default vrf (for packet return) - push @controller_config, "import vrf $vrf"; - push(@{$config->{frr}->{router}->{"bgp $asn"}->{"address-family"}->{"ipv4 unicast"}}, @controller_config); - push(@{$config->{frr}->{router}->{"bgp $asn"}->{"address-family"}->{"ipv6 unicast"}}, @controller_config); + #add default originate to announce 0.0.0.0/0 type5 route in evpn + push @controller_config, "default-originate ipv4"; + push @controller_config, "default-originate ipv6"; + push(@{$config->{frr}->{router}->{"bgp $asn vrf $vrf"}->{"address-family"}->{"l2vpn evpn"}}, @controller_config); + } elsif ($advertisesubnets) { @controller_config = (); - #redistribute connected to be able to route to local vms on the gateway + #redistribute connected networks push @controller_config, "redistribute connected"; push(@{$config->{frr}->{router}->{"bgp $asn vrf $vrf"}->{"address-family"}->{"ipv4 unicast"}}, @controller_config); push(@{$config->{frr}->{router}->{"bgp $asn vrf $vrf"}->{"address-family"}->{"ipv6 unicast"}}, @controller_config); @controller_config = (); - #add default originate to announce 0.0.0.0/0 type5 route in evpn - push @controller_config, "default-originate ipv4"; - push @controller_config, "default-originate ipv6"; + #advertise connected networks type5 route in evpn + push @controller_config, "advertise ipv4 unicast"; + push @controller_config, "advertise ipv6 unicast"; + push(@{$config->{frr}->{router}->{"bgp $asn vrf $vrf"}->{"address-family"}->{"l2vpn evpn"}}, @controller_config); + } + + if($rt_import) { + @controller_config = (); + foreach my $rt (sort @{$rt_import}) { + push @controller_config, "route-target import $rt"; + } push(@{$config->{frr}->{router}->{"bgp $asn vrf $vrf"}->{"address-family"}->{"l2vpn evpn"}}, @controller_config); } return $config; } +sub generate_controller_vnet_config { + my ($class, $plugin_config, $controller, $zone, $zoneid, $vnetid, $config) = @_; + + 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); + my @controller_config = (); + foreach my $subnetid (sort keys %{$subnets}) { + my $subnet = $subnets->{$subnetid}; + my $cidr = $subnet->{cidr}; + push @controller_config, "ip route $cidr 10.255.255.2 xvrf_$zoneid"; + } + push(@{$config->{frr}->{''}}, @controller_config); +} + sub on_delete_hook { my ($class, $controllerid, $zone_cfg) = @_; @@ -280,22 +357,41 @@ sub generate_frr_recurse{ } } -sub write_controller_config { +sub generate_frr_routemap { + my ($final_config, $routemaps) = @_; + + foreach my $id (sort keys %$routemaps) { + + my $routemap = $routemaps->{$id}; + my $order = 0; + foreach my $seq (@$routemap) { + $order++; + next if !defined($seq->{action}); + my @config = (); + push @config, "!"; + push @config, "route-map $id $seq->{action} $order"; + my $rule = $seq->{rule}; + push @config, map { " $_" } @$rule; + push @{$final_config}, @config; + } + } +} +sub generate_controller_rawconfig { my ($class, $plugin_config, $config) = @_; my $nodename = PVE::INotify::nodename(); my $final_config = []; - push @{$final_config}, "log syslog informational"; - push @{$final_config}, "ip forwarding"; - push @{$final_config}, "ipv6 forwarding"; + push @{$final_config}, "frr version 8.0.1"; push @{$final_config}, "frr defaults datacenter"; - push @{$final_config}, "service integrated-vtysh-config"; push @{$final_config}, "hostname $nodename"; + push @{$final_config}, "log syslog informational"; + push @{$final_config}, "service integrated-vtysh-config"; push @{$final_config}, "!"; if (-e "/etc/frr/frr.conf.local") { generate_frr_recurse($final_config, $config->{frr}->{vrf}, "vrf", 1); + generate_frr_routemap($final_config, $config->{frr_routemap}); push @{$final_config}, "!"; my $local_conf = file_get_contents("/etc/frr/frr.conf.local"); @@ -303,6 +399,7 @@ sub write_controller_config { push @{$final_config}, $local_conf; } else { generate_frr_recurse($final_config, $config->{frr}, undef, 0); + generate_frr_routemap($final_config, $config->{frr_routemap}); } push @{$final_config}, "!"; @@ -311,6 +408,14 @@ sub write_controller_config { my $rawconfig = join("\n", @{$final_config}); + return if !$rawconfig; + return $rawconfig; +} + +sub write_controller_config { + my ($class, $plugin_config, $config) = @_; + + my $rawconfig = $class->generate_controller_rawconfig($plugin_config, $config); return if !$rawconfig; return if !-d "/etc/frr"; @@ -336,7 +441,13 @@ sub reload_controller { }; if (-e $conf_file && -e $bin_path) { - run_command([$bin_path, '--stdout', '--reload', $conf_file], outfunc => {}, errfunc => $err); + eval { + run_command([$bin_path, '--stdout', '--reload', $conf_file], outfunc => {}, errfunc => $err); + }; + if ($@) { + warn "frr reload command fail. Restarting frr."; + eval { run_command(['systemctl', 'restart', 'frr']); }; + } } }