]> git.proxmox.com Git - pve-network.git/blob - PVE/Network/SDN/VxlanPlugin.pm
18ed62945bd91561e60f933d9157b7ddb910a829
[pve-network.git] / PVE / Network / SDN / VxlanPlugin.pm
1 package PVE::Network::SDN::VxlanPlugin;
2
3 use strict;
4 use warnings;
5 use PVE::Network::SDN::Plugin;
6 use PVE::Tools;
7
8 use base('PVE::Network::SDN::Plugin');
9
10 PVE::JSONSchema::register_format('pve-sdn-vxlanrange', \&pve_verify_sdn_vxlanrange);
11 sub pve_verify_sdn_vxlanrange {
12 my ($vxlanstr) = @_;
13
14 PVE::Network::SDN::Plugin::parse_tag_number_or_range($vxlanstr, '16777216');
15
16 return $vxlanstr;
17 }
18
19 sub type {
20 return 'vxlan';
21 }
22
23 sub properties {
24 return {
25 'vxlan-allowed' => {
26 type => 'string', format => 'pve-sdn-vxlanrange',
27 description => "Allowed vlan range",
28 },
29 'multicast-address' => {
30 description => "Multicast address.",
31 type => 'string', #fixme: format
32 },
33 'unicast-address' => {
34 description => "Unicast peers address ip list.",
35 type => 'string', #fixme: format
36 },
37 'vrf' => {
38 description => "vrf name.",
39 type => 'string', #fixme: format
40 },
41 'vrf-vxlan' => {
42 type => 'integer',
43 description => "l3vni.",
44 },
45 'router' => {
46 type => 'string',
47 description => "Frr router name",
48 },
49 };
50 }
51
52 sub options {
53
54 return {
55 'uplink-id' => { optional => 0 },
56 'multicast-address' => { optional => 1 },
57 'unicast-address' => { optional => 1 },
58 'vxlan-allowed' => { optional => 1 },
59 'vrf' => { optional => 1 },
60 'vrf-vxlan' => { optional => 1 },
61 'router' => { optional => 1 },
62 };
63 }
64
65 # Plugin implementation
66 sub generate_sdn_config {
67 my ($class, $plugin_config, $zoneid, $vnetid, $vnet, $uplinks, $config) = @_;
68
69 my $tag = $vnet->{tag};
70 my $alias = $vnet->{alias};
71 my $ipv4 = $vnet->{ipv4};
72 my $ipv6 = $vnet->{ipv6};
73 my $mac = $vnet->{mac};
74 my $multicastaddress = $plugin_config->{'multicast-address'};
75 my @unicastaddress = split(',', $plugin_config->{'unicast-address'}) if $plugin_config->{'unicast-address'};
76
77 my $uplink = $plugin_config->{'uplink-id'};
78 my $vxlanallowed = $plugin_config->{'vxlan-allowed'};
79 my $vrf = $plugin_config->{'vrf'};
80 my $vrfvxlan = $plugin_config->{'vrf-vxlan'};
81
82 die "missing vxlan tag" if !$tag;
83 my $iface = "uplink$uplink";
84 my $ifaceip = "";
85
86 if($uplinks->{$uplink}->{name}) {
87 $iface = $uplinks->{$uplink}->{name};
88 $ifaceip = PVE::Network::SDN::Plugin::get_first_local_ipv4_from_interface($iface);
89 }
90
91 my $mtu = 1450;
92 $mtu = $uplinks->{$uplink}->{mtu} - 50 if $uplinks->{$uplink}->{mtu};
93 $mtu = $vnet->{mtu} if $vnet->{mtu};
94
95 #vxlan interface
96 my @iface_config = ();
97 push @iface_config, "vxlan-id $tag";
98
99 if($multicastaddress) {
100 push @iface_config, "vxlan-svcnodeip $multicastaddress";
101 push @iface_config, "vxlan-physdev $iface";
102 } elsif (@unicastaddress) {
103
104 foreach my $address (@unicastaddress) {
105 next if $address eq $ifaceip;
106 push @iface_config, "vxlan_remoteip $address";
107 }
108 } else {
109 push @iface_config, "vxlan-local-tunnelip $ifaceip" if $ifaceip;
110 push @iface_config, "bridge-learning off";
111 push @iface_config, "bridge-arp-nd-suppress on";
112 }
113
114 push @iface_config, "mtu $mtu" if $mtu;
115 push(@{$config->{"vxlan$vnetid"}}, @iface_config) if !$config->{"vxlan$vnetid"};
116
117 #vnet bridge
118 @iface_config = ();
119 push @iface_config, "address $ipv4" if $ipv4;
120 push @iface_config, "address $ipv6" if $ipv6;
121 push @iface_config, "hwaddress $mac" if $mac;
122 push @iface_config, "bridge_ports vxlan$vnetid";
123 push @iface_config, "bridge_stp off";
124 push @iface_config, "bridge_fd 0";
125 push @iface_config, "mtu $mtu" if $mtu;
126 push @iface_config, "alias $alias" if $alias;
127 push @iface_config, "vrf $vrf" if $vrf;
128 push(@{$config->{$vnetid}}, @iface_config) if !$config->{$vnetid};
129
130 if ($vrf) {
131 #vrf intreface
132 @iface_config = ();
133 push @iface_config, "vrf-table auto";
134 push(@{$config->{$vrf}}, @iface_config) if !$config->{$vrf};
135
136 if ($vrfvxlan) {
137 #l3vni vxlan interface
138 my $iface_vxlan = "vxlan$vrf";
139 @iface_config = ();
140 push @iface_config, "vxlan-id $vrfvxlan";
141 push @iface_config, "vxlan-local-tunnelip $ifaceip" if $ifaceip;
142 push @iface_config, "bridge-learning off";
143 push @iface_config, "bridge-arp-nd-suppress on";
144 push @iface_config, "mtu $mtu" if $mtu;
145 push(@{$config->{$iface_vxlan}}, @iface_config) if !$config->{$iface_vxlan};
146
147 #l3vni bridge
148 my $brvrf = "br$vrf";
149 @iface_config = ();
150 push @iface_config, "bridge-ports $iface_vxlan";
151 push @iface_config, "bridge_stp off";
152 push @iface_config, "bridge_fd 0";
153 push @iface_config, "mtu $mtu" if $mtu;
154 push @iface_config, "vrf $vrf";
155 push(@{$config->{$brvrf}}, @iface_config) if !$config->{$brvrf};
156 }
157 }
158
159 return $config;
160 }
161
162 sub generate_frr_config {
163 my ($class, $plugin_config, $asn, $id, $uplinks, $config) = @_;
164
165 my $vrf = $plugin_config->{'vrf'};
166 my $vrfvxlan = $plugin_config->{'vrf-vxlan'};
167 return if !$vrf || !$vrfvxlan;
168
169 my $uplink = $plugin_config->{'uplink-id'};
170
171 my $iface = "uplink$uplink";
172 my $ifaceip = "";
173
174 if($uplinks->{$uplink}->{name}) {
175 $iface = $uplinks->{$uplink}->{name};
176 $ifaceip = PVE::Network::SDN::Plugin::get_first_local_ipv4_from_interface($iface);
177 }
178
179 #vrf
180 my @router_config = ();
181 push @router_config, "vni $vrfvxlan";
182 push @router_config, "exit-vrf";
183 push(@{$config->{vrf}->{"vrf $vrf"}}, @router_config);
184
185
186 #vrf router
187 @router_config = ();
188 push @router_config, "bgp router-id $ifaceip";
189 push @router_config, "!";
190 push @router_config, "address-family ipv4 unicast";
191 push @router_config, " redistribute connected";
192 push @router_config, "exit-address-family";
193 push @router_config, "!";
194 push @router_config, "address-family l2vpn evpn";
195 push @router_config, " advertise ipv4 unicast";
196 push @router_config, "exit-address-family";
197 push(@{$config->{router}->{"router bgp $asn vrf $vrf"}}, @router_config);
198
199 return $config;
200 }
201
202 sub on_delete_hook {
203 my ($class, $transportid, $sdn_cfg) = @_;
204
205 # verify that no vnet are associated to this transport
206 foreach my $id (keys %{$sdn_cfg->{ids}}) {
207 my $sdn = $sdn_cfg->{ids}->{$id};
208 die "transport $transportid is used by vnet $id"
209 if ($sdn->{type} eq 'vnet' && defined($sdn->{transportzone}) && $sdn->{transportzone} eq $transportid);
210 }
211 }
212
213 sub on_update_hook {
214 my ($class, $transportid, $sdn_cfg) = @_;
215
216 my $transport = $sdn_cfg->{ids}->{$transportid};
217
218 # verify that vxlan-allowed don't conflict with another vxlan-allowed transport
219
220 # verify that vxlan-allowed is matching currently vnet tag in this transport
221 my $vxlanallowed = $transport->{'vxlan-allowed'};
222 if ($vxlanallowed) {
223 foreach my $id (keys %{$sdn_cfg->{ids}}) {
224 my $sdn = $sdn_cfg->{ids}->{$id};
225 if ($sdn->{type} eq 'vnet' && defined($sdn->{tag})) {
226 if(defined($sdn->{transportzone}) && $sdn->{transportzone} eq $transportid) {
227 my $tag = $sdn->{tag};
228 eval {
229 PVE::Network::SDN::Plugin::parse_tag_number_or_range($vxlanallowed, '16777216', $tag);
230 };
231 if($@) {
232 die "vnet $id - vlan $tag is not allowed in transport $transportid";
233 }
234 }
235 }
236 }
237 }
238
239 # verify that router exist
240 if (defined($sdn_cfg->{ids}->{$transportid}->{router})) {
241 my $router = $sdn_cfg->{ids}->{$transportid}->{router};
242 if (!defined($sdn_cfg->{ids}->{$router})) {
243 die "router $router don't exist";
244 } else {
245 die "$router is not a router type" if $sdn_cfg->{ids}->{$router}->{type} ne 'frr';
246 }
247
248 #vrf && vrf-vxlan need to be defined with router
249 my $vrf = $sdn_cfg->{ids}->{$transportid}->{vrf};
250 if (!defined($vrf)) {
251 die "missing vrf option";
252 } else {
253 # verify that vrf is not already declared in another transport
254 foreach my $id (keys %{$sdn_cfg->{ids}}) {
255 next if $id eq $transportid;
256 die "vrf $vrf is already declared in $id"
257 if (defined($sdn_cfg->{ids}->{$id}->{vrf}) && $sdn_cfg->{ids}->{$id}->{vrf} eq $vrf);
258 }
259 }
260
261 my $vrfvxlan = $sdn_cfg->{ids}->{$transportid}->{'vrf-vxlan'};
262 if (!defined($vrfvxlan)) {
263 die "missing vrf-vxlan option";
264 } else {
265 # verify that vrf-vxlan is not already declared in another transport
266 foreach my $id (keys %{$sdn_cfg->{ids}}) {
267 next if $id eq $transportid;
268 die "vrf-vxlan $vrfvxlan is already declared in $id"
269 if (defined($sdn_cfg->{ids}->{$id}->{'vrf-vxlan'}) && $sdn_cfg->{ids}->{$id}->{'vrf-vxlan'} eq $vrfvxlan);
270 }
271 }
272 }
273 }
274
275 1;
276
277