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 8F4799A2DF for ; Fri, 17 Nov 2023 12:40:27 +0100 (CET) Received: from firstgate.proxmox.com (localhost [127.0.0.1]) by firstgate.proxmox.com (Proxmox) with ESMTP id B23A430C23 for ; Fri, 17 Nov 2023 12:40:24 +0100 (CET) Received: from lana.proxmox.com (unknown [94.136.29.99]) by firstgate.proxmox.com (Proxmox) with ESMTP for ; Fri, 17 Nov 2023 12:40:19 +0100 (CET) Received: by lana.proxmox.com (Postfix, from userid 10043) id 3022E2C33C2; Fri, 17 Nov 2023 12:40:18 +0100 (CET) From: Stefan Hanreich To: pve-devel@lists.proxmox.com Date: Fri, 17 Nov 2023 12:39:44 +0100 Message-Id: <20231117114011.834002-7-s.hanreich@proxmox.com> X-Mailer: git-send-email 2.39.2 In-Reply-To: <20231117114011.834002-1-s.hanreich@proxmox.com> References: <20231117114011.834002-1-s.hanreich@proxmox.com> MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-SPAM-LEVEL: Spam detection results: 0 AWL -0.473 Adjusted score from AWL reputation of From: address BAYES_00 -1.9 Bayes spam probability is 0 to 1% DMARC_MISSING 0.1 Missing DMARC policy 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 RDNS_NONE 0.793 Delivered to internal network by a host with no rDNS 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 T_SCC_BODY_TEXT_LINE -0.01 - Subject: [pve-devel] [PATCH v4 pve-network 06/33] subnet: vnet: refactor IPAM related methods 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: Fri, 17 Nov 2023 11:40:27 -0000 Those methods are used by the DHCP plugins to attain the next free IP address for a given DHCP range, as well as delete all entries with a certain MAC address. Co-Authored-By: Alexandre Derumier Signed-off-by: Stefan Hanreich --- src/PVE/Network/SDN/SubnetPlugin.pm | 3 +- src/PVE/Network/SDN/Subnets.pm | 50 ++++++++----- src/PVE/Network/SDN/Vnets.pm | 105 +++++++++++++++++----------- 3 files changed, 101 insertions(+), 57 deletions(-) diff --git a/src/PVE/Network/SDN/SubnetPlugin.pm b/src/PVE/Network/SDN/SubnetPlugin.pm index a4adae8..88933f5 100644 --- a/src/PVE/Network/SDN/SubnetPlugin.pm +++ b/src/PVE/Network/SDN/SubnetPlugin.pm @@ -172,8 +172,7 @@ sub on_update_hook { } if(!$old_gateway || $gateway && $gateway ne $old_gateway) { my $hostname = "$vnetid-gw"; - my $description = "gateway"; - PVE::Network::SDN::Subnets::add_ip($zone, $subnetid, $subnet, $gateway, $hostname, $mac, $description, 1); + PVE::Network::SDN::Subnets::add_ip($zone, $subnetid, $subnet, $gateway, $hostname, $mac, undef, 1); } #delete old gateway after update diff --git a/src/PVE/Network/SDN/Subnets.pm b/src/PVE/Network/SDN/Subnets.pm index 6e74de1..b05b3d9 100644 --- a/src/PVE/Network/SDN/Subnets.pm +++ b/src/PVE/Network/SDN/Subnets.pm @@ -98,14 +98,12 @@ sub get_subnet { } sub find_ip_subnet { - my ($ip, $mask, $subnets) = @_; + my ($ip, $subnets) = @_; my $subnet = undef; my $subnetid = undef; foreach my $id (sort keys %{$subnets}) { - - next if $mask ne $subnets->{$id}->{mask}; my $cidr = $subnets->{$id}->{cidr}; my $subnet_matcher = subnet_matcher($cidr); next if !$subnet_matcher->($ip); @@ -207,12 +205,11 @@ sub del_subnet { $plugin->del_subnet($plugin_config, $subnetid, $subnet); } -sub next_free_ip { - my ($zone, $subnetid, $subnet, $hostname, $mac, $description, $skipdns) = @_; +sub add_next_free_ip { + my ($zone, $subnetid, $subnet, $hostname, $mac, $vmid, $skipdns, $dhcprange) = @_; my $cidr = undef; my $ip = undef; - $description = '' if !$description; my $ipamid = $zone->{ipam}; my $dns = $zone->{dns}; @@ -230,10 +227,28 @@ sub next_free_ip { my $plugin_config = $ipam_cfg->{ids}->{$ipamid}; my $plugin = PVE::Network::SDN::Ipams::Plugin->lookup($plugin_config->{type}); eval { - $cidr = $plugin->add_next_freeip($plugin_config, $subnetid, $subnet, $hostname, $mac, $description); - ($ip, undef) = split(/\//, $cidr); + if ($dhcprange) { + my $data = { + mac => $mac, + hostname => $hostname, + vmid => $vmid, + }; + + my $dhcp_ranges = PVE::Network::SDN::Subnets::get_dhcp_ranges($subnet); + + foreach my $range (@$dhcp_ranges) { + $ip = $plugin->add_range_next_freeip($plugin_config, $subnet, $range, $data); + next if !$ip; + } + } else { + $ip = $plugin->add_next_freeip($plugin_config, $subnetid, $subnet, $hostname, $mac, $vmid); + } }; + die $@ if $@; + + eval { PVE::Network::SDN::Ipams::add_cache_mac_ip($mac, $ip); }; + warn $@ if $@; } eval { @@ -250,15 +265,15 @@ sub next_free_ip { #rollback my $err = $@; eval { - PVE::Network::SDN::Subnets::del_ip($zone, $subnetid, $subnet, $ip, $hostname) + PVE::Network::SDN::Subnets::del_ip($zone, $subnetid, $subnet, $ip, $hostname, $mac) }; die $err; } - return $cidr; + return $ip; } sub add_ip { - my ($zone, $subnetid, $subnet, $ip, $hostname, $mac, $description, $is_gateway, $skipdns) = @_; + my ($zone, $subnetid, $subnet, $ip, $hostname, $mac, $vmid, $is_gateway, $skipdns) = @_; return if !$subnet || !$ip; @@ -287,7 +302,7 @@ sub add_ip { my $plugin = PVE::Network::SDN::Ipams::Plugin->lookup($plugin_config->{type}); eval { - $plugin->add_ip($plugin_config, $subnetid, $subnet, $ip, $hostname, $mac, $description, $is_gateway); + $plugin->add_ip($plugin_config, $subnetid, $subnet, $ip, $hostname, $mac, $vmid, $is_gateway); }; die $@ if $@; } @@ -304,14 +319,14 @@ sub add_ip { #rollback my $err = $@; eval { - PVE::Network::SDN::Subnets::del_ip($zone, $subnetid, $subnet, $ip, $hostname) + PVE::Network::SDN::Subnets::del_ip($zone, $subnetid, $subnet, $ip, $hostname, $mac) }; die $err; } } sub update_ip { - my ($zone, $subnetid, $subnet, $ip, $hostname, $oldhostname, $mac, $description, $skipdns) = @_; + my ($zone, $subnetid, $subnet, $ip, $hostname, $oldhostname, $mac, $vmid, $skipdns) = @_; return if !$subnet || !$ip; @@ -338,7 +353,7 @@ sub update_ip { my $plugin_config = $ipam_cfg->{ids}->{$ipamid}; my $plugin = PVE::Network::SDN::Ipams::Plugin->lookup($plugin_config->{type}); eval { - $plugin->update_ip($plugin_config, $subnetid, $subnet, $ip, $hostname, $mac, $description); + $plugin->update_ip($plugin_config, $subnetid, $subnet, $ip, $hostname, $mac, $vmid); }; die $@ if $@; } @@ -358,7 +373,7 @@ sub update_ip { } sub del_ip { - my ($zone, $subnetid, $subnet, $ip, $hostname, $skipdns) = @_; + my ($zone, $subnetid, $subnet, $ip, $hostname, $mac, $skipdns) = @_; return if !$subnet || !$ip; @@ -383,6 +398,9 @@ sub del_ip { my $plugin_config = $ipam_cfg->{ids}->{$ipamid}; my $plugin = PVE::Network::SDN::Ipams::Plugin->lookup($plugin_config->{type}); $plugin->del_ip($plugin_config, $subnetid, $subnet, $ip); + + eval { PVE::Network::SDN::Ipams::del_cache_mac_ip($mac, $ip); }; + warn $@ if $@; } eval { diff --git a/src/PVE/Network/SDN/Vnets.pm b/src/PVE/Network/SDN/Vnets.pm index 39bdda0..4b3276e 100644 --- a/src/PVE/Network/SDN/Vnets.pm +++ b/src/PVE/Network/SDN/Vnets.pm @@ -80,81 +80,108 @@ sub get_subnets { return $subnets; } -sub get_subnet_from_vnet_cidr { - my ($vnetid, $cidr) = @_; +sub get_subnet_from_vnet_ip { + my ($vnetid, $ip) = @_; my $subnets = PVE::Network::SDN::Vnets::get_subnets($vnetid, 1); my $vnet = PVE::Network::SDN::Vnets::get_vnet($vnetid); my $zoneid = $vnet->{zone}; my $zone = PVE::Network::SDN::Zones::get_zone($zoneid); - my ($ip, $mask) = split(/\//, $cidr); - die "ip address is not in cidr format" if !$mask; - - my ($subnetid, $subnet) = PVE::Network::SDN::Subnets::find_ip_subnet($ip, $mask, $subnets); + my ($subnetid, $subnet) = PVE::Network::SDN::Subnets::find_ip_subnet($ip, $subnets); return ($zone, $subnetid, $subnet, $ip); } -sub get_next_free_cidr { - my ($vnetid, $hostname, $mac, $description, $ipversion, $skipdns) = @_; +sub add_next_free_cidr { + my ($vnetid, $hostname, $mac, $vmid, $skipdns, $dhcprange) = @_; my $vnet = PVE::Network::SDN::Vnets::get_vnet($vnetid); + return if !$vnet; + my $zoneid = $vnet->{zone}; my $zone = PVE::Network::SDN::Zones::get_zone($zoneid); - return if !$zone->{ipam}; + return if !$zone->{ipam} || !$zone->{dhcp}; - $ipversion = 4 if !$ipversion; my $subnets = PVE::Network::SDN::Vnets::get_subnets($vnetid, 1); - my $ip = undef; - my $subnetcount = 0; - - foreach my $subnetid (sort keys %{$subnets}) { - my $subnet = $subnets->{$subnetid}; - my $network = $subnet->{network}; - - next if $ipversion != Net::IP::ip_get_version($network); - $subnetcount++; - eval { - $ip = PVE::Network::SDN::Subnets::next_free_ip($zone, $subnetid, $subnet, $hostname, $mac, $description, $skipdns); - }; - warn $@ if $@; - last if $ip; + my $ips = {}; + + my @ipversions = qw/ 4 6 /; + for my $ipversion (@ipversions) { + my $ip = undef; + my $subnetcount = 0; + foreach my $subnetid (sort keys %{$subnets}) { + my $subnet = $subnets->{$subnetid}; + my $network = $subnet->{network}; + + next if Net::IP::ip_get_version($network) != $ipversion || $ips->{$ipversion}; + $subnetcount++; + + eval { + $ip = PVE::Network::SDN::Subnets::add_next_free_ip($zone, $subnetid, $subnet, $hostname, $mac, $vmid, $skipdns, $dhcprange); + }; + die $@ if $@; + + if ($ip) { + $ips->{$ipversion} = $ip; + last; + } + } + die "can't find any free ip" if !$ip && $subnetcount > 0; } - die "can't find any free ip" if !$ip && $subnetcount > 0; - - return $ip; } -sub add_cidr { - my ($vnetid, $cidr, $hostname, $mac, $description, $skipdns) = @_; +sub add_ip { + my ($vnetid, $ip, $hostname, $mac, $vmid, $skipdns) = @_; return if !$vnetid; - my ($zone, $subnetid, $subnet, $ip) = PVE::Network::SDN::Vnets::get_subnet_from_vnet_cidr($vnetid, $cidr); - PVE::Network::SDN::Subnets::add_ip($zone, $subnetid, $subnet, $ip, $hostname, $mac, $description, undef, $skipdns); + my ($zone, $subnetid, $subnet) = PVE::Network::SDN::Vnets::get_subnet_from_vnet_ip($vnetid, $ip); + PVE::Network::SDN::Subnets::add_ip($zone, $subnetid, $subnet, $ip, $hostname, $mac, $vmid, undef, $skipdns); } -sub update_cidr { - my ($vnetid, $cidr, $hostname, $oldhostname, $mac, $description, $skipdns) = @_; +sub update_ip { + my ($vnetid, $ip, $hostname, $oldhostname, $mac, $vmid, $skipdns) = @_; return if !$vnetid; - my ($zone, $subnetid, $subnet, $ip) = PVE::Network::SDN::Vnets::get_subnet_from_vnet_cidr($vnetid, $cidr); - PVE::Network::SDN::Subnets::update_ip($zone, $subnetid, $subnet, $ip, $hostname, $oldhostname, $mac, $description, $skipdns); + my ($zone, $subnetid, $subnet) = PVE::Network::SDN::Vnets::get_subnet_from_vnet_ip($vnetid, $ip); + PVE::Network::SDN::Subnets::update_ip($zone, $subnetid, $subnet, $ip, $hostname, $oldhostname, $mac, $vmid, $skipdns); } -sub del_cidr { - my ($vnetid, $cidr, $hostname, $skipdns) = @_; +sub del_ip { + my ($vnetid, $ip, $hostname, $mac, $skipdns) = @_; return if !$vnetid; - my ($zone, $subnetid, $subnet, $ip) = PVE::Network::SDN::Vnets::get_subnet_from_vnet_cidr($vnetid, $cidr); - PVE::Network::SDN::Subnets::del_ip($zone, $subnetid, $subnet, $ip, $hostname, $skipdns); + my ($zone, $subnetid, $subnet) = PVE::Network::SDN::Vnets::get_subnet_from_vnet_ip($vnetid, $ip); + PVE::Network::SDN::Subnets::del_ip($zone, $subnetid, $subnet, $ip, $hostname, $mac, $skipdns); +} + +sub get_ips_from_mac { + my ($vnetid, $mac) = @_; + + my $vnet = PVE::Network::SDN::Vnets::get_vnet($vnetid); + return if !$vnet; + + my $zoneid = $vnet->{zone}; + my $zone = PVE::Network::SDN::Zones::get_zone($zoneid); + + return if !$zone->{ipam} || !$zone->{dhcp}; + + return PVE::Network::SDN::Ipams::get_ips_from_mac($mac, $zoneid, $zone); } +sub del_ips_from_mac { + my ($vnetid, $mac, $hostname) = @_; + my ($ip4, $ip6) = PVE::Network::SDN::Vnets::get_ips_from_mac($vnetid, $mac); + PVE::Network::SDN::Vnets::del_ip($vnetid, $ip4, $hostname, $mac) if $ip4; + PVE::Network::SDN::Vnets::del_ip($vnetid, $ip6, $hostname, $mac) if $ip6; + + return ($ip4, $ip6); +} 1; -- 2.39.2