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 { | |
1262519c AD |
22 | 'bgp-multipath-as-path-relax' => { |
23 | type => 'boolean', | |
24 | optional => 1, | |
25 | }, | |
f23633dc AD |
26 | ebgp => { |
27 | type => 'boolean', | |
28 | optional => 1, | |
29 | description => "Enable ebgp. (remote-as external)", | |
30 | }, | |
4083537b AD |
31 | 'ebgp-multihop' => { |
32 | type => 'integer', | |
33 | optional => 1, | |
34 | }, | |
f23633dc AD |
35 | loopback => { |
36 | description => "source loopback interface.", | |
37 | type => 'string' | |
38 | }, | |
39 | node => get_standard_option('pve-node'), | |
40 | }; | |
41 | } | |
42 | ||
43 | sub options { | |
44 | return { | |
45 | 'node' => { optional => 0 }, | |
46 | 'asn' => { optional => 0 }, | |
47 | 'peers' => { optional => 0 }, | |
1262519c | 48 | 'bgp-multipath-as-path-relax' => { optional => 1 }, |
f23633dc | 49 | 'ebgp' => { optional => 1 }, |
4083537b | 50 | 'ebgp-multihop' => { optional => 1 }, |
f23633dc AD |
51 | 'loopback' => { optional => 1 }, |
52 | }; | |
53 | } | |
54 | ||
55 | # Plugin implementation | |
56 | sub generate_controller_config { | |
57 | my ($class, $plugin_config, $controller, $id, $uplinks, $config) = @_; | |
58 | ||
59 | my @peers; | |
60 | @peers = PVE::Tools::split_list($plugin_config->{'peers'}) if $plugin_config->{'peers'}; | |
61 | ||
62 | my $asn = $plugin_config->{asn}; | |
63 | my $ebgp = $plugin_config->{ebgp}; | |
4083537b | 64 | my $ebgp_multihop = $plugin_config->{'ebgp-multihop'}; |
f23633dc | 65 | my $loopback = $plugin_config->{loopback}; |
1262519c AD |
66 | my $multipath_relax = $plugin_config->{'bgp-multipath-as-path-relax'}; |
67 | ||
f23633dc AD |
68 | my $local_node = PVE::INotify::nodename(); |
69 | ||
70 | ||
71 | return if !$asn; | |
72 | return if $local_node ne $plugin_config->{node}; | |
73 | ||
74 | my $bgp = $config->{frr}->{router}->{"bgp $asn"} //= {}; | |
75 | ||
76 | my ($ifaceip, $interface) = PVE::Network::SDN::Zones::Plugin::find_local_ip_interface_peers(\@peers, $loopback); | |
77 | ||
78 | my $remoteas = $ebgp ? "external" : $asn; | |
79 | ||
80 | #global options | |
81 | my @controller_config = ( | |
82 | "bgp router-id $ifaceip", | |
83 | "no bgp default ipv4-unicast", | |
84 | "coalesce-time 1000", | |
85 | "bgp network import-check" | |
86 | ); | |
87 | ||
88 | push(@{$bgp->{""}}, @controller_config) if keys %{$bgp} == 0; | |
89 | ||
90 | @controller_config = (); | |
91 | if($ebgp) { | |
92 | push @controller_config, "no bgp ebgp-requires-policy"; | |
93 | push @controller_config, "bgp disable-ebgp-connected-route-check" if $loopback; | |
94 | } | |
95 | ||
1262519c AD |
96 | push @controller_config, "bgp bestpath as-path multipath-relax" if $multipath_relax; |
97 | ||
f23633dc AD |
98 | #BGP neighbors |
99 | if(@peers) { | |
100 | push @controller_config, "neighbor BGP peer-group"; | |
101 | push @controller_config, "neighbor BGP remote-as $remoteas"; | |
102 | push @controller_config, "neighbor BGP bfd"; | |
4083537b | 103 | push @controller_config, "neighbor BGP ebgp-multihop $ebgp_multihop" if $ebgp && $ebgp_multihop; |
f23633dc AD |
104 | } |
105 | ||
106 | # BGP peers | |
107 | foreach my $address (@peers) { | |
108 | push @controller_config, "neighbor $address peer-group BGP"; | |
109 | } | |
110 | push(@{$bgp->{""}}, @controller_config); | |
111 | ||
112 | # address-family unicast | |
113 | if (@peers) { | |
114 | my $ipversion = Net::IP::ip_is_ipv6($ifaceip) ? "ipv6" : "ipv4"; | |
115 | my $mask = Net::IP::ip_is_ipv6($ifaceip) ? "/128" : "32"; | |
116 | ||
117 | push(@{$bgp->{"address-family"}->{"$ipversion unicast"}}, "network $ifaceip/$mask") if $loopback; | |
118 | push(@{$bgp->{"address-family"}->{"$ipversion unicast"}}, "neighbor BGP activate"); | |
119 | push(@{$bgp->{"address-family"}->{"$ipversion unicast"}}, "neighbor BGP soft-reconfiguration inbound"); | |
120 | } | |
121 | ||
bbf4e4b1 AD |
122 | if ($loopback) { |
123 | push(@{$config->{frr}->{''}}, "ip prefix-list loopbacks_ips seq 10 permit 0.0.0.0/0 le 32"); | |
124 | push(@{$config->{frr}->{''}}, "ip protocol bgp route-map correct_src"); | |
125 | push(@{$config->{frr}->{'route-map'}->{'correct_src permit 1'}}, "match ip address prefix-list loopbacks_ips"); | |
126 | push(@{$config->{frr}->{'route-map'}->{'correct_src permit 1'}}, "set src $ifaceip"); | |
127 | } | |
128 | ||
f23633dc AD |
129 | return $config; |
130 | } | |
131 | ||
132 | sub generate_controller_zone_config { | |
133 | my ($class, $plugin_config, $controller, $controller_cfg, $id, $uplinks, $config) = @_; | |
134 | ||
135 | } | |
136 | ||
137 | sub on_delete_hook { | |
138 | my ($class, $controllerid, $zone_cfg) = @_; | |
139 | ||
140 | # verify that zone is associated to this controller | |
141 | foreach my $id (keys %{$zone_cfg->{ids}}) { | |
142 | my $zone = $zone_cfg->{ids}->{$id}; | |
143 | die "controller $controllerid is used by $id" | |
144 | if (defined($zone->{controller}) && $zone->{controller} eq $controllerid); | |
145 | } | |
146 | } | |
147 | ||
148 | sub on_update_hook { | |
149 | my ($class, $controllerid, $controller_cfg) = @_; | |
150 | ||
151 | # we can only have 1 bgp controller by node | |
152 | my $local_node = PVE::INotify::nodename(); | |
153 | my $controllernb = 0; | |
154 | foreach my $id (keys %{$controller_cfg->{ids}}) { | |
155 | next if $id eq $controllerid; | |
156 | my $controller = $controller_cfg->{ids}->{$id}; | |
157 | next if $controller->{type} ne "bgp"; | |
158 | next if $controller->{node} ne $local_node; | |
159 | $controllernb++; | |
160 | die "only 1 bgp controller can be defined" if $controllernb > 1; | |
161 | } | |
162 | } | |
163 | ||
9cef13e9 AD |
164 | sub generate_controller_rawconfig { |
165 | my ($class, $plugin_config, $config) = @_; | |
166 | return ""; | |
167 | } | |
168 | ||
f23633dc AD |
169 | sub write_controller_config { |
170 | my ($class, $plugin_config, $config) = @_; | |
171 | return; | |
172 | } | |
173 | ||
174 | sub reload_controller { | |
175 | my ($class) = @_; | |
176 | return; | |
177 | } | |
178 | ||
179 | 1; | |
180 | ||
181 |