]>
Commit | Line | Data |
---|---|---|
f23633dc AD |
1 | package PVE::Network::SDN::Controllers::BgpPlugin; |
2 | ||
3 | use strict; | |
4 | use warnings; | |
5 | ||
6 | use PVE::INotify; | |
7 | use PVE::JSONSchema qw(get_standard_option); | |
8 | use PVE::Tools qw(run_command file_set_contents file_get_contents); | |
9 | ||
10 | use PVE::Network::SDN::Controllers::Plugin; | |
11 | use PVE::Network::SDN::Zones::Plugin; | |
12 | use Net::IP; | |
13 | ||
14 | use base('PVE::Network::SDN::Controllers::Plugin'); | |
15 | ||
16 | sub type { | |
17 | return 'bgp'; | |
18 | } | |
19 | ||
20 | sub properties { | |
21 | return { | |
22 | ebgp => { | |
23 | type => 'boolean', | |
24 | optional => 1, | |
25 | description => "Enable ebgp. (remote-as external)", | |
26 | }, | |
27 | loopback => { | |
28 | description => "source loopback interface.", | |
29 | type => 'string' | |
30 | }, | |
31 | node => get_standard_option('pve-node'), | |
32 | }; | |
33 | } | |
34 | ||
35 | sub options { | |
36 | return { | |
37 | 'node' => { optional => 0 }, | |
38 | 'asn' => { optional => 0 }, | |
39 | 'peers' => { optional => 0 }, | |
40 | 'ebgp' => { optional => 1 }, | |
41 | 'loopback' => { optional => 1 }, | |
42 | }; | |
43 | } | |
44 | ||
45 | # Plugin implementation | |
46 | sub generate_controller_config { | |
47 | my ($class, $plugin_config, $controller, $id, $uplinks, $config) = @_; | |
48 | ||
49 | my @peers; | |
50 | @peers = PVE::Tools::split_list($plugin_config->{'peers'}) if $plugin_config->{'peers'}; | |
51 | ||
52 | my $asn = $plugin_config->{asn}; | |
53 | my $ebgp = $plugin_config->{ebgp}; | |
54 | my $loopback = $plugin_config->{loopback}; | |
55 | my $local_node = PVE::INotify::nodename(); | |
56 | ||
57 | ||
58 | return if !$asn; | |
59 | return if $local_node ne $plugin_config->{node}; | |
60 | ||
61 | my $bgp = $config->{frr}->{router}->{"bgp $asn"} //= {}; | |
62 | ||
63 | my ($ifaceip, $interface) = PVE::Network::SDN::Zones::Plugin::find_local_ip_interface_peers(\@peers, $loopback); | |
64 | ||
65 | my $remoteas = $ebgp ? "external" : $asn; | |
66 | ||
67 | #global options | |
68 | my @controller_config = ( | |
69 | "bgp router-id $ifaceip", | |
70 | "no bgp default ipv4-unicast", | |
71 | "coalesce-time 1000", | |
72 | "bgp network import-check" | |
73 | ); | |
74 | ||
75 | push(@{$bgp->{""}}, @controller_config) if keys %{$bgp} == 0; | |
76 | ||
77 | @controller_config = (); | |
78 | if($ebgp) { | |
79 | push @controller_config, "no bgp ebgp-requires-policy"; | |
80 | push @controller_config, "bgp disable-ebgp-connected-route-check" if $loopback; | |
81 | } | |
82 | ||
83 | #BGP neighbors | |
84 | if(@peers) { | |
85 | push @controller_config, "neighbor BGP peer-group"; | |
86 | push @controller_config, "neighbor BGP remote-as $remoteas"; | |
87 | push @controller_config, "neighbor BGP bfd"; | |
88 | } | |
89 | ||
90 | # BGP peers | |
91 | foreach my $address (@peers) { | |
92 | push @controller_config, "neighbor $address peer-group BGP"; | |
93 | } | |
94 | push(@{$bgp->{""}}, @controller_config); | |
95 | ||
96 | # address-family unicast | |
97 | if (@peers) { | |
98 | my $ipversion = Net::IP::ip_is_ipv6($ifaceip) ? "ipv6" : "ipv4"; | |
99 | my $mask = Net::IP::ip_is_ipv6($ifaceip) ? "/128" : "32"; | |
100 | ||
101 | push(@{$bgp->{"address-family"}->{"$ipversion unicast"}}, "network $ifaceip/$mask") if $loopback; | |
102 | push(@{$bgp->{"address-family"}->{"$ipversion unicast"}}, "neighbor BGP activate"); | |
103 | push(@{$bgp->{"address-family"}->{"$ipversion unicast"}}, "neighbor BGP soft-reconfiguration inbound"); | |
104 | } | |
105 | ||
106 | return $config; | |
107 | } | |
108 | ||
109 | sub generate_controller_zone_config { | |
110 | my ($class, $plugin_config, $controller, $controller_cfg, $id, $uplinks, $config) = @_; | |
111 | ||
112 | } | |
113 | ||
114 | sub on_delete_hook { | |
115 | my ($class, $controllerid, $zone_cfg) = @_; | |
116 | ||
117 | # verify that zone is associated to this controller | |
118 | foreach my $id (keys %{$zone_cfg->{ids}}) { | |
119 | my $zone = $zone_cfg->{ids}->{$id}; | |
120 | die "controller $controllerid is used by $id" | |
121 | if (defined($zone->{controller}) && $zone->{controller} eq $controllerid); | |
122 | } | |
123 | } | |
124 | ||
125 | sub on_update_hook { | |
126 | my ($class, $controllerid, $controller_cfg) = @_; | |
127 | ||
128 | # we can only have 1 bgp controller by node | |
129 | my $local_node = PVE::INotify::nodename(); | |
130 | my $controllernb = 0; | |
131 | foreach my $id (keys %{$controller_cfg->{ids}}) { | |
132 | next if $id eq $controllerid; | |
133 | my $controller = $controller_cfg->{ids}->{$id}; | |
134 | next if $controller->{type} ne "bgp"; | |
135 | next if $controller->{node} ne $local_node; | |
136 | $controllernb++; | |
137 | die "only 1 bgp controller can be defined" if $controllernb > 1; | |
138 | } | |
139 | } | |
140 | ||
141 | sub write_controller_config { | |
142 | my ($class, $plugin_config, $config) = @_; | |
143 | return; | |
144 | } | |
145 | ||
146 | sub reload_controller { | |
147 | my ($class) = @_; | |
148 | return; | |
149 | } | |
150 | ||
151 | 1; | |
152 | ||
153 |