]> git.proxmox.com Git - pve-network.git/blame - PVE/Network/SDN/VxlanPlugin.pm
make sdn controller plugin generic
[pve-network.git] / PVE / Network / SDN / VxlanPlugin.pm
CommitLineData
3ee45e4c 1package PVE::Network::SDN::VxlanPlugin;
7e720d4d
AD
2
3use strict;
4use warnings;
86d22462 5use PVE::Network::SDN::Plugin;
c692cbfa 6use PVE::Tools qw($IPV4RE);
e3dca233 7use PVE::INotify;
7e720d4d 8
86d22462 9use base('PVE::Network::SDN::Plugin');
7e720d4d 10
6bffe819
AD
11PVE::JSONSchema::register_format('pve-sdn-vxlanrange', \&pve_verify_sdn_vxlanrange);
12sub pve_verify_sdn_vxlanrange {
7e720d4d
AD
13 my ($vxlanstr) = @_;
14
86d22462 15 PVE::Network::SDN::Plugin::parse_tag_number_or_range($vxlanstr, '16777216');
7e720d4d
AD
16
17 return $vxlanstr;
18}
19
c692cbfa
AD
20PVE::JSONSchema::register_format('ipv4-multicast', \&parse_ipv4_multicast);
21sub parse_ipv4_multicast {
22 my ($ipv4, $noerr) = @_;
23
24 if ($ipv4 !~ m/^(?:$IPV4RE)$/) {
25 return undef if $noerr;
26 die "value does not look like a valid multicast IPv4 address\n";
27 }
28
29 if ($ipv4 =~ m/^(\d+)\.\d+.\d+.\d+/) {
30 if($1 < 224 || $1 > 239) {
31 return undef if $noerr;
32 die "value does not look like a valid multicast IPv4 address\n";
33 }
34 }
35
36 return $ipv4;
37}
38
7e720d4d 39sub type {
3ee45e4c 40 return 'vxlan';
7e720d4d
AD
41}
42
8fb1ee7f
AD
43sub plugindata {
44 return {
45 role => 'transport',
46 };
47}
48
7e720d4d
AD
49sub properties {
50 return {
51 'vxlan-allowed' => {
6bffe819 52 type => 'string', format => 'pve-sdn-vxlanrange',
7e720d4d
AD
53 description => "Allowed vlan range",
54 },
55 'multicast-address' => {
56 description => "Multicast address.",
c692cbfa 57 type => 'string', format => 'ipv4-multicast'
7e720d4d 58 },
3ee45e4c
AD
59 'unicast-address' => {
60 description => "Unicast peers address ip list.",
fcfca9ef 61 type => 'string', format => 'ip-list'
3ee45e4c 62 },
126fc68c
AD
63 'vrf' => {
64 description => "vrf name.",
7d35eaf5 65 type => 'string', #fixme: format
126fc68c
AD
66 },
67 'vrf-vxlan' => {
68 type => 'integer',
69 description => "l3vni.",
70 },
8fb1ee7f 71 'controller' => {
87d8b623
AD
72 type => 'string',
73 description => "Frr router name",
74 },
7e720d4d
AD
75 };
76}
77
78sub options {
79
80 return {
85533e98 81 'uplink-id' => { optional => 0 },
3ee45e4c
AD
82 'multicast-address' => { optional => 1 },
83 'unicast-address' => { optional => 1 },
7e720d4d 84 'vxlan-allowed' => { optional => 1 },
126fc68c
AD
85 'vrf' => { optional => 1 },
86 'vrf-vxlan' => { optional => 1 },
8fb1ee7f 87 'controller' => { optional => 1 },
7e720d4d
AD
88 };
89}
90
91# Plugin implementation
6bffe819 92sub generate_sdn_config {
93dea3aa 93 my ($class, $plugin_config, $zoneid, $vnetid, $vnet, $uplinks, $config) = @_;
7e720d4d
AD
94
95 my $tag = $vnet->{tag};
dc7e431e 96 my $alias = $vnet->{alias};
5b31292c
AD
97 my $ipv4 = $vnet->{ipv4};
98 my $ipv6 = $vnet->{ipv6};
99 my $mac = $vnet->{mac};
7e720d4d 100 my $multicastaddress = $plugin_config->{'multicast-address'};
3ee45e4c
AD
101 my @unicastaddress = split(',', $plugin_config->{'unicast-address'}) if $plugin_config->{'unicast-address'};
102
7e720d4d
AD
103 my $uplink = $plugin_config->{'uplink-id'};
104 my $vxlanallowed = $plugin_config->{'vxlan-allowed'};
126fc68c
AD
105 my $vrf = $plugin_config->{'vrf'};
106 my $vrfvxlan = $plugin_config->{'vrf-vxlan'};
7e720d4d
AD
107
108 die "missing vxlan tag" if !$tag;
3ee45e4c
AD
109 my $iface = "uplink$uplink";
110 my $ifaceip = "";
111
112 if($uplinks->{$uplink}->{name}) {
113 $iface = $uplinks->{$uplink}->{name};
114 $ifaceip = PVE::Network::SDN::Plugin::get_first_local_ipv4_from_interface($iface);
115 }
c1ae8486
AD
116
117 my $mtu = 1450;
118 $mtu = $uplinks->{$uplink}->{mtu} - 50 if $uplinks->{$uplink}->{mtu};
119 $mtu = $vnet->{mtu} if $vnet->{mtu};
7e720d4d 120
93dea3aa
AD
121 #vxlan interface
122 my @iface_config = ();
123 push @iface_config, "vxlan-id $tag";
3ee45e4c
AD
124
125 if($multicastaddress) {
93dea3aa
AD
126 push @iface_config, "vxlan-svcnodeip $multicastaddress";
127 push @iface_config, "vxlan-physdev $iface";
3ee45e4c
AD
128 } elsif (@unicastaddress) {
129
130 foreach my $address (@unicastaddress) {
131 next if $address eq $ifaceip;
93dea3aa 132 push @iface_config, "vxlan_remoteip $address";
3ee45e4c
AD
133 }
134 } else {
93dea3aa
AD
135 push @iface_config, "vxlan-local-tunnelip $ifaceip" if $ifaceip;
136 push @iface_config, "bridge-learning off";
137 push @iface_config, "bridge-arp-nd-suppress on";
3ee45e4c
AD
138 }
139
93dea3aa 140 push @iface_config, "mtu $mtu" if $mtu;
87d8b623 141 push(@{$config->{"vxlan$vnetid"}}, @iface_config) if !$config->{"vxlan$vnetid"};
93dea3aa
AD
142
143 #vnet bridge
144 @iface_config = ();
145 push @iface_config, "address $ipv4" if $ipv4;
146 push @iface_config, "address $ipv6" if $ipv6;
147 push @iface_config, "hwaddress $mac" if $mac;
148 push @iface_config, "bridge_ports vxlan$vnetid";
149 push @iface_config, "bridge_stp off";
150 push @iface_config, "bridge_fd 0";
151 push @iface_config, "mtu $mtu" if $mtu;
152 push @iface_config, "alias $alias" if $alias;
79939ff7
AD
153 push @iface_config, "ip-forward on" if $ipv4;
154 push @iface_config, "ip6-forward on" if $ipv6;
155 push @iface_config, "arp-accept on" if $ipv4||$ipv6;
93dea3aa 156 push @iface_config, "vrf $vrf" if $vrf;
87d8b623 157 push(@{$config->{$vnetid}}, @iface_config) if !$config->{$vnetid};
126fc68c
AD
158
159 if ($vrf) {
93dea3aa
AD
160 #vrf intreface
161 @iface_config = ();
162 push @iface_config, "vrf-table auto";
87d8b623 163 push(@{$config->{$vrf}}, @iface_config) if !$config->{$vrf};
126fc68c
AD
164
165 if ($vrfvxlan) {
93dea3aa
AD
166 #l3vni vxlan interface
167 my $iface_vxlan = "vxlan$vrf";
168 @iface_config = ();
169 push @iface_config, "vxlan-id $vrfvxlan";
170 push @iface_config, "vxlan-local-tunnelip $ifaceip" if $ifaceip;
171 push @iface_config, "bridge-learning off";
172 push @iface_config, "bridge-arp-nd-suppress on";
173 push @iface_config, "mtu $mtu" if $mtu;
87d8b623 174 push(@{$config->{$iface_vxlan}}, @iface_config) if !$config->{$iface_vxlan};
93dea3aa
AD
175
176 #l3vni bridge
126fc68c 177 my $brvrf = "br$vrf";
93dea3aa
AD
178 @iface_config = ();
179 push @iface_config, "bridge-ports $iface_vxlan";
180 push @iface_config, "bridge_stp off";
181 push @iface_config, "bridge_fd 0";
182 push @iface_config, "mtu $mtu" if $mtu;
183 push @iface_config, "vrf $vrf";
87d8b623 184 push(@{$config->{$brvrf}}, @iface_config) if !$config->{$brvrf};
126fc68c
AD
185 }
186 }
7e720d4d
AD
187
188 return $config;
189}
190
8fb1ee7f 191sub generate_controller_config {
074d270b 192 my ($class, $plugin_config, $router, $id, $uplinks, $config) = @_;
87d8b623
AD
193
194 my $vrf = $plugin_config->{'vrf'};
195 my $vrfvxlan = $plugin_config->{'vrf-vxlan'};
074d270b
AD
196 my $asn = $router->{asn};
197 my $gatewaynodes = $router->{'gateway-nodes'};
87d8b623 198
074d270b 199 return if !$vrf || !$vrfvxlan || !$asn;
87d8b623
AD
200
201 #vrf
202 my @router_config = ();
203 push @router_config, "vni $vrfvxlan";
8fb1ee7f 204 push(@{$config->{frr}->{vrf}->{"$vrf"}}, @router_config);
87d8b623 205
87d8b623 206 @router_config = ();
e3dca233
AD
207
208 my $is_gateway = undef;
209 my $local_node = PVE::INotify::nodename();
210
211 foreach my $gatewaynode (PVE::Tools::split_list($gatewaynodes)) {
212 $is_gateway = 1 if $gatewaynode eq $local_node;
213 }
214
215 if ($is_gateway) {
216
217 @router_config = ();
218 #import /32 routes of evpn network from vrf1 to default vrf (for packet return)
219 #frr 7.1 tag is bugged -> works fine with 7.1 stable branch(20190829-02-g6ba76bbc1)
220 #https://github.com/FRRouting/frr/issues/4905
17854295 221 push @router_config, "import vrf $vrf";
8fb1ee7f
AD
222 push(@{$config->{frr}->{router}->{"bgp $asn"}->{"address-family"}->{"ipv4 unicast"}}, @router_config);
223 push(@{$config->{frr}->{router}->{"bgp $asn"}->{"address-family"}->{"ipv6 unicast"}}, @router_config);
e3dca233
AD
224
225 @router_config = ();
6c8d2382
AD
226 #redistribute connected to be able to route to local vms on the gateway
227 push @router_config, "redistribute connected";
8fb1ee7f
AD
228 push(@{$config->{frr}->{router}->{"bgp $asn vrf $vrf"}->{"address-family"}->{"ipv4 unicast"}}, @router_config);
229 push(@{$config->{frr}->{router}->{"bgp $asn vrf $vrf"}->{"address-family"}->{"ipv6 unicast"}}, @router_config);
e3dca233 230
6c8d2382 231 @router_config = ();
e3dca233 232 #add default originate to announce 0.0.0.0/0 type5 route in evpn
17854295 233 push @router_config, "default-originate ipv4";
074d270b 234 push @router_config, "default-originate ipv6";
8fb1ee7f 235 push(@{$config->{frr}->{router}->{"bgp $asn vrf $vrf"}->{"address-family"}->{"l2vpn evpn"}}, @router_config);
e3dca233 236 }
87d8b623
AD
237
238 return $config;
239}
240
fe0c6b9e 241sub on_delete_hook {
6bffe819 242 my ($class, $transportid, $sdn_cfg) = @_;
fe0c6b9e
AD
243
244 # verify that no vnet are associated to this transport
6bffe819
AD
245 foreach my $id (keys %{$sdn_cfg->{ids}}) {
246 my $sdn = $sdn_cfg->{ids}->{$id};
7d35eaf5 247 die "transport $transportid is used by vnet $id"
6bffe819 248 if ($sdn->{type} eq 'vnet' && defined($sdn->{transportzone}) && $sdn->{transportzone} eq $transportid);
a8ad2789 249 }
fe0c6b9e
AD
250}
251
e8d5906e 252sub on_update_hook {
6bffe819 253 my ($class, $transportid, $sdn_cfg) = @_;
c723980e 254
6bffe819 255 my $transport = $sdn_cfg->{ids}->{$transportid};
e8d5906e
AD
256
257 # verify that vxlan-allowed don't conflict with another vxlan-allowed transport
258
7d35eaf5 259 # verify that vxlan-allowed is matching currently vnet tag in this transport
c723980e
AD
260 my $vxlanallowed = $transport->{'vxlan-allowed'};
261 if ($vxlanallowed) {
6bffe819
AD
262 foreach my $id (keys %{$sdn_cfg->{ids}}) {
263 my $sdn = $sdn_cfg->{ids}->{$id};
264 if ($sdn->{type} eq 'vnet' && defined($sdn->{tag})) {
265 if(defined($sdn->{transportzone}) && $sdn->{transportzone} eq $transportid) {
266 my $tag = $sdn->{tag};
c723980e 267 eval {
86d22462 268 PVE::Network::SDN::Plugin::parse_tag_number_or_range($vxlanallowed, '16777216', $tag);
c723980e
AD
269 };
270 if($@) {
271 die "vnet $id - vlan $tag is not allowed in transport $transportid";
272 }
273 }
274 }
275 }
276 }
5bda8607
AD
277
278 # verify that router exist
279 if (defined($sdn_cfg->{ids}->{$transportid}->{router})) {
280 my $router = $sdn_cfg->{ids}->{$transportid}->{router};
281 if (!defined($sdn_cfg->{ids}->{$router})) {
282 die "router $router don't exist";
283 } else {
284 die "$router is not a router type" if $sdn_cfg->{ids}->{$router}->{type} ne 'frr';
285 }
286
287 #vrf && vrf-vxlan need to be defined with router
288 my $vrf = $sdn_cfg->{ids}->{$transportid}->{vrf};
289 if (!defined($vrf)) {
290 die "missing vrf option";
291 } else {
292 # verify that vrf is not already declared in another transport
293 foreach my $id (keys %{$sdn_cfg->{ids}}) {
294 next if $id eq $transportid;
295 die "vrf $vrf is already declared in $id"
296 if (defined($sdn_cfg->{ids}->{$id}->{vrf}) && $sdn_cfg->{ids}->{$id}->{vrf} eq $vrf);
297 }
298 }
299
300 my $vrfvxlan = $sdn_cfg->{ids}->{$transportid}->{'vrf-vxlan'};
301 if (!defined($vrfvxlan)) {
302 die "missing vrf-vxlan option";
303 } else {
304 # verify that vrf-vxlan is not already declared in another transport
305 foreach my $id (keys %{$sdn_cfg->{ids}}) {
306 next if $id eq $transportid;
307 die "vrf-vxlan $vrfvxlan is already declared in $id"
308 if (defined($sdn_cfg->{ids}->{$id}->{'vrf-vxlan'}) && $sdn_cfg->{ids}->{$id}->{'vrf-vxlan'} eq $vrfvxlan);
309 }
310 }
7d35eaf5 311 }
e8d5906e
AD
312}
313
7e720d4d
AD
3141;
315
316