From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from firstgate.proxmox.com (firstgate.proxmox.com [212.224.123.68]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits)) (No client certificate requested) by lists.proxmox.com (Postfix) with ESMTPS id DA2D966E8F for ; Sun, 8 Nov 2020 15:20:28 +0100 (CET) Received: from firstgate.proxmox.com (localhost [127.0.0.1]) by firstgate.proxmox.com (Proxmox) with ESMTP id 2B004EC98 for ; Sun, 8 Nov 2020 15:20:16 +0100 (CET) Received: from kvmformation1.odiso.net (globalOdiso.M6Lille.odiso.net [89.248.211.242]) by firstgate.proxmox.com (Proxmox) with ESMTP id 92D80E8F3 for ; Sun, 8 Nov 2020 15:19:48 +0100 (CET) Received: by kvmformation1.odiso.net (Postfix, from userid 0) id 86BC271386C; Sun, 8 Nov 2020 15:19:42 +0100 (CET) From: Alexandre Derumier To: pve-devel@lists.proxmox.com Date: Sun, 8 Nov 2020 15:19:32 +0100 Message-Id: <20201108141940.1028443-31-aderumier@odiso.com> X-Mailer: git-send-email 2.20.1 In-Reply-To: <20201108141940.1028443-1-aderumier@odiso.com> References: <20201108141940.1028443-1-aderumier@odiso.com> MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-SPAM-LEVEL: Spam detection results: 1 AWL -0.201 Adjusted score from AWL reputation of From: address HEADER_FROM_DIFFERENT_DOMAINS 0.25 From and EnvelopeFrom 2nd level mail domains are different KAM_DMARC_STATUS 0.01 Test Rule for DKIM or SPF Failure with Strict Alignment KAM_LAZY_DOMAIN_SECURITY 1 Sending domain does not have any anti-forgery methods KHOP_HELO_FCRDNS 0.276 Relay HELO differs from its IP's reverse DNS NO_DNS_FOR_FROM 0.379 Envelope sender has no MX or A DNS records SPF_HELO_NONE 0.001 SPF: HELO does not publish an SPF Record SPF_NONE 0.001 SPF: sender does not publish an SPF Record URIBL_BLOCKED 0.001 ADMINISTRATOR NOTICE: The query to URIBL was blocked. See http://wiki.apache.org/spamassassin/DnsBlocklists#dnsbl-block for more information. [vnetplugin.pm, simpleplugin.pm, evpnplugin.pm, zones.pm, subnets.pm, netboxplugin.pm, subnetplugin.pm, vnets.pm, phpipamplugin.pm, plugin.pm, powerdnsplugin.pm, pveplugin.pm] Subject: [pve-devel] [PATCH pve-network 30/38] subnets/ipam: allow same subnet on different zones X-BeenThere: pve-devel@lists.proxmox.com X-Mailman-Version: 2.1.29 Precedence: list List-Id: Proxmox VE development discussion List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Sun, 08 Nov 2020 14:20:28 -0000 --- PVE/API2/Network/SDN/Subnets.pm | 13 ++-- PVE/API2/Network/SDN/Vnets.pm | 14 ++-- PVE/API2/Network/SDN/Zones.pm | 11 +++ PVE/Network/SDN/Dns/PowerdnsPlugin.pm | 6 +- PVE/Network/SDN/Ipams/NetboxPlugin.pm | 18 ++--- PVE/Network/SDN/Ipams/PVEPlugin.pm | 102 ++++++++++++++++--------- PVE/Network/SDN/Ipams/PhpIpamPlugin.pm | 20 ++--- PVE/Network/SDN/Ipams/Plugin.pm | 6 +- PVE/Network/SDN/SubnetPlugin.pm | 26 +++++-- PVE/Network/SDN/Subnets.pm | 28 ++++--- PVE/Network/SDN/VnetPlugin.pm | 31 ++++---- PVE/Network/SDN/Vnets.pm | 18 +++-- PVE/Network/SDN/Zones/EvpnPlugin.pm | 3 +- PVE/Network/SDN/Zones/SimplePlugin.pm | 5 +- 14 files changed, 188 insertions(+), 113 deletions(-) diff --git a/PVE/API2/Network/SDN/Subnets.pm b/PVE/API2/Network/SDN/Subnets.pm index 5ea4fc4..0fb9420 100644 --- a/PVE/API2/Network/SDN/Subnets.pm +++ b/PVE/API2/Network/SDN/Subnets.pm @@ -28,7 +28,6 @@ my $api_sdn_subnets_config = sub { my $scfg = dclone(PVE::Network::SDN::Subnets::sdn_subnets_config($cfg, $id)); $scfg->{subnet} = $id; - $scfg->{cidr} = $id =~ s/-/\//r; $scfg->{digest} = $cfg->{digest}; return $scfg; @@ -169,7 +168,6 @@ __PACKAGE__->register_method ({ my $type = extract_param($param, 'type'); my $cidr = extract_param($param, 'subnet'); - my $id = $cidr =~ s/\//-/r; # create /etc/pve/sdn directory PVE::Cluster::check_cfs_quorum(); @@ -184,7 +182,9 @@ __PACKAGE__->register_method ({ my $vnet = $param->{vnet}; my $zoneid = $vnet_cfg->{ids}->{$vnet}->{zone}; my $zone = $zone_cfg->{ids}->{$zoneid}; - + my $id = $cidr =~ s/\//-/r; + $id = "$zoneid-$id"; + my $opts = PVE::Network::SDN::SubnetPlugin->check_config($id, $param, 1, 1); my $scfg = undef; @@ -193,7 +193,9 @@ __PACKAGE__->register_method ({ } $cfg->{ids}->{$id} = $opts; - PVE::Network::SDN::SubnetPlugin->on_update_hook($zone, $id, $opts); + + my $subnet = PVE::Network::SDN::Subnets::sdn_subnets_config($cfg, $id); + PVE::Network::SDN::SubnetPlugin->on_update_hook($zone, $id, $subnet); PVE::Network::SDN::Subnets::write_config($cfg); @@ -238,7 +240,8 @@ __PACKAGE__->register_method ({ raise_param_exc({ ipam => "you can't change ipam"}) if $opts->{ipam} && $scfg->{ipam} && $opts->{ipam} ne $scfg->{ipam}; - PVE::Network::SDN::SubnetPlugin->on_update_hook($zone, $id, $opts, $scfg); + my $subnet = PVE::Network::SDN::Subnets::sdn_subnets_config($cfg, $id); + PVE::Network::SDN::SubnetPlugin->on_update_hook($zone, $id, $subnet); PVE::Network::SDN::Subnets::write_config($cfg); diff --git a/PVE/API2/Network/SDN/Vnets.pm b/PVE/API2/Network/SDN/Vnets.pm index 6ff61c5..3f99f58 100644 --- a/PVE/API2/Network/SDN/Vnets.pm +++ b/PVE/API2/Network/SDN/Vnets.pm @@ -19,6 +19,7 @@ use PVE::API2::Network::SDN::Subnets; use Storable qw(dclone); use PVE::JSONSchema qw(get_standard_option); use PVE::RPCEnvironment; +use PVE::Exception qw(raise raise_param_exc); use PVE::RESTHandler; @@ -195,9 +196,7 @@ __PACKAGE__->register_method ({ my $plugin = PVE::Network::SDN::Zones::Plugin->lookup($plugin_config->{type}); $plugin->vnet_update_hook($cfg->{ids}->{$id}); - my $subnet_cfg = PVE::Network::SDN::Subnets::config(); - - PVE::Network::SDN::VnetPlugin->on_update_hook($id, $cfg, $subnet_cfg); + PVE::Network::SDN::VnetPlugin->on_update_hook($id, $cfg); PVE::Network::SDN::Vnets::write_config($cfg); @@ -228,7 +227,12 @@ __PACKAGE__->register_method ({ PVE::SectionConfig::assert_if_modified($cfg, $digest); + my $opts = PVE::Network::SDN::VnetPlugin->check_config($id, $param, 0, 1); + raise_param_exc({ zone => "missing zone"}) if !$opts->{zone}; + my $subnets = PVE::Network::SDN::Vnets::get_subnets($id); + raise_param_exc({ zone => "can't change zone if subnets exists"}) if($subnets && $opts->{zone} ne $cfg->{ids}->{$id}->{zone}); + $cfg->{ids}->{$id} = $opts; my $zone_cfg = PVE::Network::SDN::Zones::config(); @@ -237,9 +241,7 @@ __PACKAGE__->register_method ({ my $plugin = PVE::Network::SDN::Zones::Plugin->lookup($plugin_config->{type}); $plugin->vnet_update_hook($cfg->{ids}->{$id}); - my $subnet_cfg = PVE::Network::SDN::Subnets::config(); - - PVE::Network::SDN::VnetPlugin->on_update_hook($id, $cfg, $subnet_cfg); + PVE::Network::SDN::VnetPlugin->on_update_hook($id, $cfg); PVE::Network::SDN::Vnets::write_config($cfg); diff --git a/PVE/API2/Network/SDN/Zones.pm b/PVE/API2/Network/SDN/Zones.pm index 54f087d..5ae577b 100644 --- a/PVE/API2/Network/SDN/Zones.pm +++ b/PVE/API2/Network/SDN/Zones.pm @@ -9,6 +9,7 @@ use PVE::Cluster qw(cfs_read_file cfs_write_file); use PVE::Network::SDN; use PVE::Network::SDN::Vnets; use PVE::Network::SDN::Zones; +use PVE::Network::SDN::Subnets; use PVE::Network::SDN::Dns; use PVE::Network::SDN::Zones::Plugin; use PVE::Network::SDN::Zones::VlanPlugin; @@ -263,6 +264,16 @@ __PACKAGE__->register_method ({ my $plugin = PVE::Network::SDN::Zones::Plugin->lookup($scfg->{type}); my $opts = $plugin->check_config($id, $param, 0, 1); + if($opts->{ipam} ne $scfg->{ipam}) { + + #don't allow ipam change if subnet are defined + my $subnets_cfg = PVE::Network::SDN::Subnets::config(); + foreach my $subnetid (sort keys %{$subnets_cfg->{ids}}) { + my $subnet = PVE::Network::SDN::Subnets::sdn_subnets_config($subnets_cfg, $subnetid); + raise_param_exc({ ipam => "can't change ipam if subnet if already defined for this zone"}) if $subnet->{zone} eq $id; + } + } + foreach my $k (%$opts) { $scfg->{$k} = $opts->{$k}; } diff --git a/PVE/Network/SDN/Dns/PowerdnsPlugin.pm b/PVE/Network/SDN/Dns/PowerdnsPlugin.pm index 5b98e87..b00432e 100644 --- a/PVE/Network/SDN/Dns/PowerdnsPlugin.pm +++ b/PVE/Network/SDN/Dns/PowerdnsPlugin.pm @@ -186,11 +186,11 @@ sub verify_zone { } sub get_reversedns_zone { - my ($class, $plugin_config, $subnetid, $ip) = @_; + my ($class, $plugin_config, $subnetid, $subnet, $ip) = @_; - my ($network, $mask) = split(/-/, $subnetid); + my $cidr = $subnet->{cidr}; + my $mask = $subnet->{mask}; - my $cidr = "$ip/$mask"; my $zone = ""; if (Net::IP::ip_is_ipv4($ip)) { diff --git a/PVE/Network/SDN/Ipams/NetboxPlugin.pm b/PVE/Network/SDN/Ipams/NetboxPlugin.pm index c25f451..8695b7d 100644 --- a/PVE/Network/SDN/Ipams/NetboxPlugin.pm +++ b/PVE/Network/SDN/Ipams/NetboxPlugin.pm @@ -30,7 +30,7 @@ sub options { sub add_subnet { my ($class, $plugin_config, $subnetid, $subnet) = @_; - my $cidr = $subnetid =~ s/-/\//r; + my $cidr = $subnet->{cidr}; my $gateway = $subnet->{gateway}; my $url = $plugin_config->{url}; my $token = $plugin_config->{token}; @@ -40,13 +40,11 @@ sub add_subnet { #create subnet if (!$internalid) { - my ($network, $mask) = split(/-/, $subnetid); my $params = { prefix => $cidr }; eval { my $result = PVE::Network::SDN::Ipams::Plugin::api_request("POST", "$url/ipam/prefixes/", $headers, $params); - $subnet->{ipamid} = $result->{id} if defined($result->{id}); }; if ($@) { die "error add subnet to ipam: $@"; @@ -58,7 +56,7 @@ sub add_subnet { sub del_subnet { my ($class, $plugin_config, $subnetid, $subnet) = @_; - my $cidr = $subnetid =~ s/-/\//r; + my $cidr = $subnet->{cidr}; my $url = $plugin_config->{url}; my $token = $plugin_config->{token}; my $gateway = $subnet->{gateway}; @@ -66,9 +64,8 @@ sub del_subnet { my $internalid = get_prefix_id($url, $cidr, $headers); return if !$internalid; - #fixme: check that prefix is empty exluding gateway, before delete - PVE::Network::SDN::Ipams::NetboxPlugin::del_ip($class, $plugin_config, $subnetid, $gateway) if $gateway; + return; #fixme: check that prefix is empty exluding gateway, before delete eval { PVE::Network::SDN::Ipams::Plugin::api_request("DELETE", "$url/ipam/prefixes/$internalid/", $headers); @@ -80,9 +77,9 @@ sub del_subnet { } sub add_ip { - my ($class, $plugin_config, $subnetid, $ip, $is_gateway) = @_; + my ($class, $plugin_config, $subnetid, $subnet, $ip, $is_gateway) = @_; - my ($network, $mask) = split(/-/, $subnetid); + my $mask = $subnet->{mask}; my $url = $plugin_config->{url}; my $token = $plugin_config->{token}; my $section = $plugin_config->{section}; @@ -102,7 +99,8 @@ sub add_ip { sub add_next_freeip { my ($class, $plugin_config, $subnetid, $subnet) = @_; - my $cidr = $subnetid =~ s/-/\//r; + my $cidr = $subnet->{cidr}; + my $url = $plugin_config->{url}; my $token = $plugin_config->{token}; my $headers = ['Content-Type' => 'application/json; charset=UTF-8', 'Authorization' => "token $token"]; @@ -125,7 +123,7 @@ sub add_next_freeip { } sub del_ip { - my ($class, $plugin_config, $subnetid, $ip) = @_; + my ($class, $plugin_config, $subnetid, $subnet, $ip) = @_; return if !$ip; diff --git a/PVE/Network/SDN/Ipams/PVEPlugin.pm b/PVE/Network/SDN/Ipams/PVEPlugin.pm index a3ad3d6..601ad26 100644 --- a/PVE/Network/SDN/Ipams/PVEPlugin.pm +++ b/PVE/Network/SDN/Ipams/PVEPlugin.pm @@ -7,6 +7,7 @@ use PVE::Cluster qw(cfs_read_file cfs_write_file cfs_register_file cfs_lock_file use PVE::Tools; use JSON; use NetAddr::IP; +use Net::IP; use Digest::SHA; use base('PVE::Network::SDN::Ipams::Plugin'); @@ -33,15 +34,22 @@ sub options { sub add_subnet { my ($class, $plugin_config, $subnetid, $subnet) = @_; - my $cidr = $subnetid =~ s/-/\//r; + my $cidr = $subnet->{cidr}; + my $zone = $subnet->{zone}; my $gateway = $subnet->{gateway}; + cfs_lock_file($ipamdb_file, undef, sub { - my $config = read_db(); - #create subnet - if (!defined($config->{subnets}->{$cidr})) { - $config->{subnets}->{$cidr}->{ips} = {}; - write_db($config); + my $db = {}; + $db = read_db(); + + $db->{zones}->{$zone} = {} if !$db->{zones}->{$zone}; + my $zonedb = $db->{zones}->{$zone}; + + if(!$zonedb->{subnets}->{$cidr}) { + #create subnet + $zonedb->{subnets}->{$cidr}->{ips} = {}; + write_db($db); } }); die "$@" if $@; @@ -50,14 +58,19 @@ sub add_subnet { sub del_subnet { my ($class, $plugin_config, $subnetid, $subnet) = @_; - my $cidr = $subnetid =~ s/-/\//r; + my $cidr = $subnet->{cidr}; + my $zone = $subnet->{zone}; cfs_lock_file($ipamdb_file, undef, sub { my $db = read_db(); - my $ips = $db->{subnets}->{$cidr}->{ips}; + die "zone $zone don't exist in ipam db" if !$db->{zones}->{$zone}; + my $dbzone = $db->{zones}->{$zone}; + die "subnet $cidr don't exist in ipam db" if !$dbzone->{subnets}->{$cidr}; + my $dbsubnet = $dbzone->{subnets}->{$cidr}; + my $ips = $dbsubnet->{ips}; die "can't delete subnet $cidr , not empty" if keys %{$ips} > 0; - delete $db->{subnets}->{$cidr}; + delete $dbzone->{subnets}->{$cidr}; write_db($db); }); die "$@" if $@; @@ -65,19 +78,22 @@ sub del_subnet { } sub add_ip { - my ($class, $plugin_config, $subnetid, $ip, $is_gateway) = @_; + my ($class, $plugin_config, $subnetid, $subnet, $ip, $is_gateway) = @_; - my $cidr = $subnetid =~ s/-/\//r; + my $cidr = $subnet->{cidr}; + my $zone = $subnet->{zone}; cfs_lock_file($ipamdb_file, undef, sub { my $db = read_db(); - my $s = $db->{subnets}->{$cidr}; - die "ip $ip already exist" if defined($s->{ips}->{$ip}); + die "zone $zone don't exist in ipam db" if !$db->{zones}->{$zone}; + my $dbzone = $db->{zones}->{$zone}; + die "subnet $cidr don't exist in ipam db" if !$dbzone->{subnets}->{$cidr}; + my $dbsubnet = $dbzone->{subnets}->{$cidr}; - #verify that ip is valid for this subnet - $s->{ips}->{$ip} = 1; + die "ip $ip already exist" if defined($dbsubnet->{ips}->{$ip}); + $dbsubnet->{ips}->{$ip} = 1; write_db($db); }); die "$@" if $@; @@ -86,49 +102,65 @@ sub add_ip { sub add_next_freeip { my ($class, $plugin_config, $subnetid, $subnet) = @_; - my $cidr = $subnetid =~ s/-/\//r; + my $cidr = $subnet->{cidr}; + my $network = $subnet->{network}; + my $zone = $subnet->{zone}; + my $mask = $subnet->{mask}; my $freeip = undef; cfs_lock_file($ipamdb_file, undef, sub { my $db = read_db(); - my $s = $db->{subnets}->{$cidr}; - my $iplist = new NetAddr::IP($cidr); - my $broadcast = $iplist->broadcast(); - - while(1) { - $iplist++; - last if $iplist eq $broadcast; - my $ip = $iplist->addr(); - next if defined($s->{ips}->{$ip}); - $freeip = $ip; - last; + die "zone $zone don't exist in ipam db" if !$db->{zones}->{$zone}; + my $dbzone = $db->{zones}->{$zone}; + die "subnet $cidr don't exist in ipam db" if !$dbzone->{subnets}->{$cidr}; + my $dbsubnet = $dbzone->{subnets}->{$cidr}; + + if (Net::IP::ip_is_ipv4($network) && $mask == 32) { + die "can't find free ip in subnet $cidr" if defined($dbsubnet->{ips}->{$network}); + $freeip = $network; + } else { + + my $iplist = new NetAddr::IP($cidr); + my $broadcast = $iplist->broadcast(); + + while(1) { + $iplist++; + last if $iplist eq $broadcast; + my $ip = $iplist->addr(); + next if defined($dbsubnet->{ips}->{$ip}); + $freeip = $ip; + last; + } } die "can't find free ip in subnet $cidr" if !$freeip; - $s->{ips}->{$freeip} = 1; + $dbsubnet->{ips}->{$freeip} = 1; write_db($db); }); die "$@" if $@; - my ($network, $mask) = split(/-/, $subnetid); return "$freeip/$mask"; } sub del_ip { - my ($class, $plugin_config, $subnetid, $ip) = @_; + my ($class, $plugin_config, $subnetid, $subnet, $ip) = @_; - my $cidr = $subnetid =~ s/-/\//r; + my $cidr = $subnet->{cidr}; + my $zone = $subnet->{zone}; cfs_lock_file($ipamdb_file, undef, sub { my $db = read_db(); - my $s = $db->{subnets}->{$cidr}; - return if !$ip; + die "zone $zone don't exist in ipam db" if !$db->{zones}->{$zone}; + my $dbzone = $db->{zones}->{$zone}; + die "subnet $cidr don't exist in ipam db" if !$dbzone->{subnets}->{$cidr}; + my $dbsubnet = $dbzone->{subnets}->{$cidr}; + + die "ip $ip does not exist in pam" if !defined($dbsubnet->{ips}->{$ip}); + delete $dbsubnet->{ips}->{$ip}; - die "ip $ip does not exist in pam" if !defined($s->{ips}->{$ip}); - delete $s->{ips}->{$ip}; write_db($db); }); die "$@" if $@; diff --git a/PVE/Network/SDN/Ipams/PhpIpamPlugin.pm b/PVE/Network/SDN/Ipams/PhpIpamPlugin.pm index d7ba3ed..324f1b2 100644 --- a/PVE/Network/SDN/Ipams/PhpIpamPlugin.pm +++ b/PVE/Network/SDN/Ipams/PhpIpamPlugin.pm @@ -40,7 +40,10 @@ sub options { sub add_subnet { my ($class, $plugin_config, $subnetid, $subnet) = @_; - my $cidr = $subnetid =~ s/-/\//r; + my $cidr = $subnet->{cidr}; + my $network = $subnet->{network}; + my $mask = $subnet->{mask}; + my $gateway = $subnet->{gateway}; my $url = $plugin_config->{url}; my $token = $plugin_config->{token}; @@ -52,7 +55,6 @@ sub add_subnet { #create subnet if (!$internalid) { - my ($network, $mask) = split(/-/, $subnetid); my $params = { subnet => $network, mask => $mask, @@ -72,7 +74,7 @@ sub add_subnet { sub del_subnet { my ($class, $plugin_config, $subnetid, $subnet) = @_; - my $cidr = $subnetid =~ s/-/\//r; + my $cidr = $subnet->{cidr}; my $url = $plugin_config->{url}; my $token = $plugin_config->{token}; my $section = $plugin_config->{section}; @@ -81,7 +83,7 @@ sub del_subnet { my $internalid = get_internalid($url, $cidr, $headers); return if !$internalid; - #fixme: check that prefix is empty exluding gateway, before delete + return; #fixme: check that prefix is empty exluding gateway, before delete eval { PVE::Network::SDN::Ipams::Plugin::api_request("DELETE", "$url/subnets/$internalid", $headers); @@ -93,9 +95,9 @@ sub del_subnet { } sub add_ip { - my ($class, $plugin_config, $subnetid, $ip, $is_gateway) = @_; + my ($class, $plugin_config, $subnetid, $subnet, $ip, $is_gateway) = @_; - my $cidr = $subnetid =~ s/-/\//r; + my $cidr = $subnet->{cidr}; my $url = $plugin_config->{url}; my $token = $plugin_config->{token}; my $section = $plugin_config->{section}; @@ -120,7 +122,8 @@ sub add_ip { sub add_next_freeip { my ($class, $plugin_config, $subnetid, $subnet, $internalid, $hostname) = @_; - my $cidr = $subnetid =~ s/-/\//r; + my $cidr = $subnet->{cidr}; + my $mask = $subnet->{mask}; my $url = $plugin_config->{url}; my $token = $plugin_config->{token}; my $section = $plugin_config->{section}; @@ -140,12 +143,11 @@ sub add_next_freeip { die "can't find free ip in subnet $cidr: $@"; } - my ($network, $mask) = split(/-/, $subnetid); return "$ip/$mask"; } sub del_ip { - my ($class, $plugin_config, $subnetid, $ip) = @_; + my ($class, $plugin_config, $subnetid, $subnet, $ip) = @_; return if !$ip; diff --git a/PVE/Network/SDN/Ipams/Plugin.pm b/PVE/Network/SDN/Ipams/Plugin.pm index 683346c..a2ade3b 100644 --- a/PVE/Network/SDN/Ipams/Plugin.pm +++ b/PVE/Network/SDN/Ipams/Plugin.pm @@ -75,16 +75,16 @@ sub del_subnet { } sub add_ip { - my ($class, $plugin_config, $subnetid, $subnet, $internalid, $ip, $hostname, $is_gateway) = @_; + my ($class, $plugin_config, $subnetid, $subnet, $ip, $is_gateway) = @_; } sub add_next_freeip { - my ($class, $plugin_config) = @_; + my ($class, $plugin_config, $subnetid, $subnet) = @_; } sub del_ip { - my ($class, $plugin_config, $subnetid, $ip) = @_; + my ($class, $plugin_config, $subnetid, $subnet, $ip) = @_; } diff --git a/PVE/Network/SDN/SubnetPlugin.pm b/PVE/Network/SDN/SubnetPlugin.pm index a5d03f6..1444262 100644 --- a/PVE/Network/SDN/SubnetPlugin.pm +++ b/PVE/Network/SDN/SubnetPlugin.pm @@ -10,6 +10,7 @@ use PVE::Exception qw(raise raise_param_exc); use Net::Subnet qw(subnet_matcher); use PVE::Network::SDN::Vnets; use PVE::Network::SDN::Ipams; +use Net::IP; PVE::Cluster::cfs_register_file('sdn/subnets.cfg', sub { __PACKAGE__->parse_config(@_); }, @@ -25,7 +26,13 @@ PVE::JSONSchema::register_format('pve-sdn-subnet-id', \&parse_sdn_subnet_id); sub parse_sdn_subnet_id { my ($id, $noerr) = @_; - my $cidr = $id =~ s/-/\//r; + my $cidr = ""; + if($id =~ /\//) { + $cidr = $id; + } else { + my ($zone, $ip, $mask) = split(/-/, $id); + $cidr = "$ip/$mask"; + } if (!(PVE::JSONSchema::pve_verify_cidrv4($cidr, 1) || PVE::JSONSchema::pve_verify_cidrv6($cidr, 1))) @@ -91,7 +98,9 @@ sub options { sub on_update_hook { my ($class, $zone, $subnetid, $subnet, $old_subnet) = @_; - my $cidr = $subnetid =~ s/-/\//r; + my $cidr = $subnet->{cidr}; + my $mask = $subnet->{mask}; + my $subnet_matcher = subnet_matcher($cidr); my $vnetid = $subnet->{vnet}; @@ -109,9 +118,11 @@ sub on_update_hook { raise_param_exc({ vnet => "you can't add a subnet on a vlanaware vnet"}) if $vnet->{vlanaware}; } - my ($ip, $mask) = split(/\//, $cidr); + my $pointopoint = 1 if Net::IP::ip_is_ipv4($gateway) && $mask == 32; + #for /32 pointopoint, we allow gateway outside the subnet - raise_param_exc({ gateway => "$gateway is not in subnet $subnetid"}) if $gateway && !$subnet_matcher->($gateway) && $mask != 32; + raise_param_exc({ gateway => "$gateway is not in subnet $cidr"}) if $gateway && !$subnet_matcher->($gateway) && !$pointopoint; + if ($ipam) { my $ipam_cfg = PVE::Network::SDN::Ipams::config(); @@ -119,7 +130,10 @@ sub on_update_hook { my $plugin = PVE::Network::SDN::Ipams::Plugin->lookup($plugin_config->{type}); $plugin->add_subnet($plugin_config, $subnetid, $subnet); - #delete on removal + #don't register gateway for pointopoint + return if $pointopoint; + + #delete gateway on removal if (!defined($gateway) && $old_gateway) { eval { PVE::Network::SDN::Subnets::del_ip($zone, $subnetid, $old_subnet, $old_gateway); @@ -130,7 +144,7 @@ sub on_update_hook { PVE::Network::SDN::Subnets::add_ip($zone, $subnetid, $subnet, $gateway); } - #delete old ip after update + #delete old gateway after update if($gateway && $old_gateway && $gateway ne $old_gateway) { eval { PVE::Network::SDN::Subnets::del_ip($zone, $subnetid, $old_subnet, $old_gateway); diff --git a/PVE/Network/SDN/Subnets.pm b/PVE/Network/SDN/Subnets.pm index 50130d5..bd1eb36 100644 --- a/PVE/Network/SDN/Subnets.pm +++ b/PVE/Network/SDN/Subnets.pm @@ -21,6 +21,14 @@ sub sdn_subnets_config { my $scfg = $cfg->{ids}->{$id}; die "sdn subnet '$id' does not exist\n" if (!$noerr && !$scfg); + if($scfg) { + my ($zone, $network, $mask) = split(/-/, $id); + $scfg->{cidr} = "$network/$mask"; + $scfg->{zone} = $zone; + $scfg->{network} = $network; + $scfg->{mask} = $mask; + } + return $scfg; } @@ -64,13 +72,15 @@ sub get_subnet { } sub find_ip_subnet { - my ($ip, $subnets) = @_; + my ($ip, $mask, $subnets) = @_; my $subnet = undef; my $subnetid = undef; foreach my $id (sort keys %{$subnets}) { - my $cidr = $id =~ s/-/\//r; + + next if $mask ne $subnets->{$id}->{mask}; + my $cidr = $subnets->{$id}->{cidr}; my $subnet_matcher = subnet_matcher($cidr); next if !$subnet_matcher->($ip); $subnet = $subnets->{$id}; @@ -94,14 +104,14 @@ my $verify_dns_zone = sub { }; my $get_reversedns_zone = sub { - my ($subnetid, $dns, $ip) = @_; + my ($subnetid, $subnet, $dns, $ip) = @_; return if !$subnetid || !$dns || !$ip; my $dns_cfg = PVE::Network::SDN::Dns::config(); my $plugin_config = $dns_cfg->{ids}->{$dns}; my $plugin = PVE::Network::SDN::Dns::Plugin->lookup($plugin_config->{type}); - $plugin->get_reversedns_zone($plugin_config, $subnetid, $ip); + $plugin->get_reversedns_zone($plugin_config, $subnetid, $subnet, $ip); }; my $add_dns_record = sub { @@ -181,7 +191,7 @@ sub next_free_ip { } eval { - my $reversednszone = &$get_reversedns_zone($subnetid, $reversedns, $ip); + my $reversednszone = &$get_reversedns_zone($subnetid, $subnet, $reversedns, $ip); #add dns &$add_dns_record($dnszone, $dns, $hostname, $dnszoneprefix, $ip); @@ -208,7 +218,7 @@ sub add_ip { my $dns = $zone->{dns}; my $dnszone = $zone->{dnszone}; my $reversedns = $zone->{reversedns}; - my $reversednszone = &$get_reversedns_zone($subnetid, $reversedns, $ip); + my $reversednszone = &$get_reversedns_zone($subnetid, $subnet, $reversedns, $ip); my $dnszoneprefix = $subnet->{dnszoneprefix}; #verify dns zones before ipam @@ -220,7 +230,7 @@ sub add_ip { my $plugin_config = $ipam_cfg->{ids}->{$ipamid}; my $plugin = PVE::Network::SDN::Ipams::Plugin->lookup($plugin_config->{type}); eval { - $plugin->add_ip($plugin_config, $subnetid, $ip); + $plugin->add_ip($plugin_config, $subnetid, $subnet, $ip); }; die $@ if $@; } @@ -250,7 +260,7 @@ sub del_ip { my $dns = $zone->{dns}; my $dnszone = $zone->{dnszone}; my $reversedns = $zone->{reversedns}; - my $reversednszone = &$get_reversedns_zone($subnetid, $reversedns, $ip); + my $reversednszone = &$get_reversedns_zone($subnetid, $subnet, $reversedns, $ip); my $dnszoneprefix = $subnet->{dnszoneprefix}; &$verify_dns_zone($dnszone, $dns); @@ -260,7 +270,7 @@ sub del_ip { my $ipam_cfg = PVE::Network::SDN::Ipams::config(); my $plugin_config = $ipam_cfg->{ids}->{$ipamid}; my $plugin = PVE::Network::SDN::Ipams::Plugin->lookup($plugin_config->{type}); - $plugin->del_ip($plugin_config, $subnetid, $ip); + $plugin->del_ip($plugin_config, $subnetid, $subnet, $ip); } eval { diff --git a/PVE/Network/SDN/VnetPlugin.pm b/PVE/Network/SDN/VnetPlugin.pm index 8481f0d..518d2dd 100644 --- a/PVE/Network/SDN/VnetPlugin.pm +++ b/PVE/Network/SDN/VnetPlugin.pm @@ -91,29 +91,28 @@ sub on_delete_hook { #verify if subnets are associated my $subnets = PVE::Network::SDN::Vnets::get_subnets($vnetid); - my @subnetlist = (); - foreach my $subnetid (sort keys %{$subnets}) { - push @subnetlist, $subnetid; - } - raise_param_exc({ vnet => "Vnet is attached to following subnets:". join(',', @subnetlist)}) if @subnetlist > 0; + raise_param_exc({ vnet => "Can't delete vnet if subnets exists"}) if $subnets; } sub on_update_hook { - my ($class, $vnetid, $vnet_cfg, $subnet_cfg) = @_; + my ($class, $vnetid, $vnet_cfg) = @_; + + my $vnet = $vnet_cfg->{ids}->{$vnetid}; + my $tag = $vnet->{tag}; + my $vlanaware = $vnet->{vlanaware}; + + #don't allow vlanaware change if subnets are defined + if($vnet->{vlanaware}) { + my $subnets = PVE::Network::SDN::Vnets::get_subnets($vnetid); + raise_param_exc({ vlanaware => "vlanaware vnet is not compatible with subnets"}) if $subnets; + } - #fixme : don't allow change zone if subnets are defined - #fixme : don't vlanaware change if subnets are defined -# my $subnets = PVE::Network::SDN::Vnets::get_subnets($vnetid); - # verify that tag is not already defined in another vnet - if (defined($vnet_cfg->{ids}->{$vnetid}->{tag})) { - my $tag = $vnet_cfg->{ids}->{$vnetid}->{tag}; + if (defined($tag)) { foreach my $id (keys %{$vnet_cfg->{ids}}) { next if $id eq $vnetid; - my $vnet = $vnet_cfg->{ids}->{$id}; - if ($vnet->{type} eq 'vnet' && defined($vnet->{tag})) { - raise_param_exc({ tag => "tag $tag already exist in vnet $id"}) if $tag eq $vnet->{tag}; - } + my $othervnettag = $vnet_cfg->{ids}->{$id}->{tag}; + raise_param_exc({ tag => "tag $tag already exist in vnet $id"}) if $othervnettag && $tag eq $othervnettag; } } } diff --git a/PVE/Network/SDN/Vnets.pm b/PVE/Network/SDN/Vnets.pm index d08db51..6d11003 100644 --- a/PVE/Network/SDN/Vnets.pm +++ b/PVE/Network/SDN/Vnets.pm @@ -66,10 +66,10 @@ sub get_vnet { sub get_subnets { my ($vnetid) = @_; - my $subnets = {}; + my $subnets = undef; my $subnets_cfg = PVE::Network::SDN::Subnets::config(); foreach my $subnetid (sort keys %{$subnets_cfg->{ids}}) { - my $subnet = $subnets_cfg->{ids}->{$subnetid}; + my $subnet = PVE::Network::SDN::Subnets::sdn_subnets_config($subnets_cfg, $subnetid); next if !$subnet->{vnet} || $subnet->{vnet} ne $vnetid; $subnets->{$subnetid} = $subnet; } @@ -77,7 +77,7 @@ sub get_subnets { } -sub get_next_free_ip { +sub get_next_free_cidr { my ($vnetid, $hostname, $ipversion) = @_; my $vnet = PVE::Network::SDN::Vnets::get_vnet($vnetid); @@ -91,7 +91,7 @@ sub get_next_free_ip { foreach my $subnetid (sort keys %{$subnets}) { my $subnet = $subnets->{$subnetid}; - my ($network, $mask) = split(/-/, $subnetid); + my $network = $subnet->{network}; next if $ipversion != Net::IP::ip_get_version($network); $subnetcount++; @@ -108,7 +108,7 @@ sub get_next_free_ip { return $ip; } -sub add_ip { +sub add_cidr { my ($vnetid, $cidr, $hostname) = @_; my $subnets = PVE::Network::SDN::Vnets::get_subnets($vnetid, 1); @@ -117,12 +117,13 @@ sub add_ip { my $zone = PVE::Network::SDN::Zones::get_zone($zoneid); my ($ip, $mask) = split(/\//, $cidr); - my ($subnetid, $subnet) = PVE::Network::SDN::Subnets::find_ip_subnet($ip, $subnets); + die "ip address is not in cidr format" if !$mask; + my ($subnetid, $subnet) = PVE::Network::SDN::Subnets::find_ip_subnet($ip, $mask, $subnets); PVE::Network::SDN::Subnets::add_ip($zone, $subnetid, $subnet, $ip, $hostname); } -sub del_ip { +sub del_cidr { my ($vnetid, $cidr, $hostname) = @_; my $subnets = PVE::Network::SDN::Vnets::get_subnets($vnetid, 1); @@ -131,7 +132,8 @@ sub del_ip { my $zone = PVE::Network::SDN::Zones::get_zone($zoneid); my ($ip, $mask) = split(/\//, $cidr); - my ($subnetid, $subnet) = PVE::Network::SDN::Subnets::find_ip_subnet($ip, $subnets); + die "ip address is not in cidr format" if !$mask; + my ($subnetid, $subnet) = PVE::Network::SDN::Subnets::find_ip_subnet($ip, $mask, $subnets); PVE::Network::SDN::Subnets::del_ip($zone, $subnetid, $subnet, $ip, $hostname); } diff --git a/PVE/Network/SDN/Zones/EvpnPlugin.pm b/PVE/Network/SDN/Zones/EvpnPlugin.pm index 723b3cc..62ab817 100644 --- a/PVE/Network/SDN/Zones/EvpnPlugin.pm +++ b/PVE/Network/SDN/Zones/EvpnPlugin.pm @@ -86,7 +86,8 @@ sub generate_sdn_config { my $subnets = PVE::Network::SDN::Vnets::get_subnets($vnetid, 1); foreach my $subnetid (sort keys %{$subnets}) { my $subnet = $subnets->{$subnetid}; - my $cidr = $subnetid =~ s/-/\//r; + my $cidr = $subnet->{cidr}; + my $gateway = $subnet->{gateway}; if ($gateway) { push @iface_config, "address $gateway" if !defined($address->{$gateway}); diff --git a/PVE/Network/SDN/Zones/SimplePlugin.pm b/PVE/Network/SDN/Zones/SimplePlugin.pm index fe0f20f..5294485 100644 --- a/PVE/Network/SDN/Zones/SimplePlugin.pm +++ b/PVE/Network/SDN/Zones/SimplePlugin.pm @@ -60,14 +60,15 @@ sub generate_sdn_config { my $subnets = PVE::Network::SDN::Vnets::get_subnets($vnetid, 1); foreach my $subnetid (sort keys %{$subnets}) { my $subnet = $subnets->{$subnetid}; - my $cidr = $subnetid =~ s/-/\//r; + my $cidr = $subnet->{cidr}; + my $mask = $subnet->{mask}; + my $gateway = $subnet->{gateway}; if ($gateway) { push @iface_config, "address $gateway" if !defined($address->{$gateway}); $address->{$gateway} = 1; } #add route for /32 pointtopoint - my ($ip, $mask) = split(/\//, $cidr); push @iface_config, "up ip route add $cidr dev $vnetid" if $mask == 32; if ($subnet->{snat}) { #find outgoing interface -- 2.20.1