public inbox for pve-devel@lists.proxmox.com
 help / color / mirror / Atom feed
* [pve-devel] [PATCH v4 cluster/network/manager/qemu-server/container/docs 00/33] Add support for DHCP servers to SDN
@ 2023-11-17 11:39 Stefan Hanreich
  2023-11-17 11:39 ` [pve-devel] [PATCH v4 pve-cluster 01/33] add priv/macs.db Stefan Hanreich
                   ` (35 more replies)
  0 siblings, 36 replies; 66+ messages in thread
From: Stefan Hanreich @ 2023-11-17 11:39 UTC (permalink / raw)
  To: pve-devel

This patch series adds support for automatically deploying dnsmasq as a DHCP
server to a simple SDN Zone. This series is now in a state where I think it is
okay to integrate into the respective repositories **except** for pve-container

You can (and should) check out the documentation, but here is a quick setup
guide for your convenienve:

You need to install dnsmasq (and disable it afterwards):

  apt install dnsmasq && systemctl disable --now dnsmasq


You can use the following example configuration for deploying a DHCP server in
a SDN subnet, you should also be able to recreate this configuration in the
web UI:


/etc/pve/sdn/zones.cfg:

  simple: DHCPNAT
          ipam pve
          dhcp dnsmasq


/etc/pve/sdn/vnets.cfg:

  vnet: dhcpnat
          zone DHCPNAT


/etc/pve/sdn/subnets.cfg:

  subnet: DHCPNAT-10.1.0.0-16
          vnet dhcpnat
          dhcp-dns-server 10.1.0.1
          dhcp-range start-address=10.1.0.100,end-address=10.1.0.200
          gateway 10.1.0.1
          snat 1

Don't forget to apply the new configuration!

For testing it can be helpful to monitor the following files (e.g. with watch)
to find out what is happening
  * /etc/dnsmasq.d/<zone_id>/ethers (on each node)
  * /etc/pve/priv/ipam.db
  * /etc/pve/priv/macs.db

Known Issues (working on fixes currently):
  * removing an entry with a duplicate MAC address removes all entries with
    MAC address
  * dnsmasq and IPv6 (and DHCP in general) do not really play well together,
    so using subnets with IPv6 configured is wonky
  * removing custom mappings fails due to a missing VMID
  * Changing IP addresses and MAC addresses can be buggy due to the way dnsmasq
    handles leases
  * DHCP DNS server setting is not available in the UI

Changes from v3 -> v4:
  * Improved API (permissions, validation, error handling)
  * UI polishing
  * added support for containers
  * countless small bug fixes

Changes from v2 -> v3:
  * Removed dhcp.cfg, DHCP server now get configured at the zone
  * added UI
  * added / updated API
  * DHCP acquires IPs at vNIC creation instead of VM start
  * DHCP releases IPs at vNIC removal instead of VM stop
  * improved dnsmasq configuration generation
  * added priv/macs.db for caching mac/IP mappings
  * refactored IPAM plugins
  * updated tests

Changes from v1 -> v2:
  * added hooks for handling DHCP when starting / stopping / .. VMs and CTs
  * Get an IP from IPAM and register that IP in the DHCP server
    (pve only for now)
  * remove lease-time, since it is now infinite and managed by the VM lifecycle
  * add hooks for setting & deleting DHCP mappings to DHCP plugins
  * modified interface of the abstract class to reflect new requirements
  * added helpers in existing SDN classes
  * simplified DHCP configuration settings



pve-cluster:

Alexandre Derumier (1):
  add priv/macs.db

 src/PVE/Cluster.pm  | 1 +
 src/pmxcfs/status.c | 1 +
 2 files changed, 2 insertions(+)


pve-network:

Alexandre Derumier (3):
  sdn: fix tests
  sdn: fix subnets && netbox ipam tests
  add add_dhcp_mapping

Stefan Hanreich (12):
  sdn: preparations for DHCP plugin
  subnet: add dhcp options
  sdn: zone: add dhcp option
  ipam: plugins: preparations for DHCP
  subnet: vnet: refactor IPAM related methods
  dhcp: add abstract class for DHCP plugins
  sdn: dhcp: add dnsmasq plugin
  sdn: dhcp: add helper for creating DHCP leases
  api: add endpoints for managing PVE IPAM
  api: subnet: add dhcp ranges
  api: zone: add dhcp option
  dhcp: regenerate config for DHCP plugins on applying configuration

 debian/control                                |   1 +
 src/PVE/API2/Network/SDN.pm                   |   6 +
 src/PVE/API2/Network/SDN/Ipam.pm              | 221 +++++++++++++++++
 src/PVE/API2/Network/SDN/Makefile             |   2 +-
 src/PVE/API2/Network/SDN/Subnets.pm           |   1 +
 src/PVE/API2/Network/SDN/Zones.pm             |   1 +
 src/PVE/Network/SDN.pm                        |   9 +-
 src/PVE/Network/SDN/Dhcp.pm                   | 118 +++++++++
 src/PVE/Network/SDN/Dhcp/Dnsmasq.pm           | 226 ++++++++++++++++++
 src/PVE/Network/SDN/Dhcp/Makefile             |   8 +
 src/PVE/Network/SDN/Dhcp/Plugin.pm            |  65 +++++
 src/PVE/Network/SDN/Ipams.pm                  |  80 ++++++-
 src/PVE/Network/SDN/Ipams/NetboxPlugin.pm     |  86 ++++++-
 src/PVE/Network/SDN/Ipams/PVEPlugin.pm        |  85 ++++++-
 src/PVE/Network/SDN/Ipams/PhpIpamPlugin.pm    |  29 +++
 src/PVE/Network/SDN/Ipams/Plugin.pm           |  19 +-
 src/PVE/Network/SDN/Makefile                  |   3 +-
 src/PVE/Network/SDN/SubnetPlugin.pm           |  32 ++-
 src/PVE/Network/SDN/Subnets.pm                |  98 +++++---
 src/PVE/Network/SDN/Vnets.pm                  | 147 ++++++++----
 src/PVE/Network/SDN/Zones.pm                  |  34 ++-
 src/PVE/Network/SDN/Zones/SimplePlugin.pm     |   7 +-
 src/test/ipams/netbox/expected.add_ip         |   2 +-
 .../ipams/netbox/expected.add_ip_notgateway   |   2 +-
 .../ipams/netbox/expected.add_next_freeip     |   2 +-
 src/test/ipams/netbox/expected.update_ip      |   2 +-
 src/test/run_test_subnets.pl                  |  16 +-
 src/test/run_test_vnets.pl                    |   4 +-
 28 files changed, 1181 insertions(+), 125 deletions(-)
 create mode 100644 src/PVE/API2/Network/SDN/Ipam.pm
 create mode 100644 src/PVE/Network/SDN/Dhcp.pm
 create mode 100644 src/PVE/Network/SDN/Dhcp/Dnsmasq.pm
 create mode 100644 src/PVE/Network/SDN/Dhcp/Makefile
 create mode 100644 src/PVE/Network/SDN/Dhcp/Plugin.pm


pve-manager:

Stefan Hanreich (4):
  sdn: regenerate DHCP config on reload
  sdn: add DHCP option to Zone dialogue
  sdn: subnet: add panel for editing dhcp ranges
  sdn: ipam: add ipam panel

 PVE/API2/Network.pm                  |   1 +
 www/css/ext6-pve.css                 |  22 ++-
 www/manager6/Makefile                |   2 +
 www/manager6/dc/Config.js            |  12 +-
 www/manager6/sdn/IpamEdit.js         |  78 ++++++++
 www/manager6/sdn/SubnetEdit.js       | 160 +++++++++++++++-
 www/manager6/sdn/zones/Base.js       |   6 +-
 www/manager6/sdn/zones/SimpleEdit.js |  10 +
 www/manager6/tree/DhcpTree.js        | 267 +++++++++++++++++++++++++++
 9 files changed, 547 insertions(+), 11 deletions(-)
 create mode 100644 www/manager6/sdn/IpamEdit.js
 create mode 100644 www/manager6/tree/DhcpTree.js


qemu-server:

Alexandre Derumier (6):
  vmnic add|remove : add|del ip in ipam
  vm_start : vm-network-scripts: add_dhcp_reservation
  api2: create|restore|clone: add_free_ip
  vm_destroy: delete ip from ipam
  nic hotplug: add_dhcp_mapping
  nic online bridge/vlan change: link disconnect/reconnect

 PVE/API2/Qemu.pm              |  6 +++
 PVE/QemuServer.pm             | 86 +++++++++++++++++++++++++++++++++++
 vm-network-scripts/pve-bridge |  2 +
 3 files changed, 94 insertions(+)


pve-container:

Alexandre Derumier (6):
  nic hotplug : add|del ips in ipam
  vm_destroy: remove ips from ipam for all interfaces
  vm_create|restore: create ips in ipam
  vm_clone : create ips in ipams
  vm_apply_pending: add|del ips from ipam for offline changes
  lxc-pve-prestart-hook : add_dhcp_mapping

 src/PVE/API2/LXC.pm       | 10 ++++++++-
 src/PVE/LXC.pm            | 46 +++++++++++++++++++++++++++++++++++++++
 src/PVE/LXC/Config.pm     | 27 +++++++++++++++++++++++
 src/lxc-pve-prestart-hook | 15 +++++++++++++
 4 files changed, 97 insertions(+), 1 deletion(-)


pve-docs:

Stefan Hanreich (1):
  sdn: dhcp: Add documentation for DHCP

 pvesdn.adoc | 122 ++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 122 insertions(+)


Summary over all repositories:
  47 files changed, 2043 insertions(+), 137 deletions(-)

-- 
murpp v0.4.0




^ permalink raw reply	[flat|nested] 66+ messages in thread

* [pve-devel] [PATCH v4 pve-cluster 01/33] add priv/macs.db
  2023-11-17 11:39 [pve-devel] [PATCH v4 cluster/network/manager/qemu-server/container/docs 00/33] Add support for DHCP servers to SDN Stefan Hanreich
@ 2023-11-17 11:39 ` Stefan Hanreich
  2023-11-17 13:54   ` [pve-devel] applied: " Thomas Lamprecht
  2023-11-17 11:39 ` [pve-devel] [PATCH v4 pve-network 02/33] sdn: preparations for DHCP plugin Stefan Hanreich
                   ` (34 subsequent siblings)
  35 siblings, 1 reply; 66+ messages in thread
From: Stefan Hanreich @ 2023-11-17 11:39 UTC (permalink / raw)
  To: pve-devel

From: Alexandre Derumier <aderumier@odiso.com>

use to cache mac-ip list association.

can be use by external ipam, firewall,etc for fast lookup

Signed-off-by: Alexandre Derumier <aderumier@odiso.com>
---
 src/PVE/Cluster.pm  | 1 +
 src/pmxcfs/status.c | 1 +
 2 files changed, 2 insertions(+)

diff --git a/src/PVE/Cluster.pm b/src/PVE/Cluster.pm
index cfa2583..80c4bc0 100644
--- a/src/PVE/Cluster.pm
+++ b/src/PVE/Cluster.pm
@@ -62,6 +62,7 @@ my $observed = {
     'priv/token.cfg' => 1,
     'priv/acme/plugins.cfg' => 1,
     'priv/ipam.db' => 1,
+    'priv/macs.db' => 1,
     '/qemu-server/' => 1,
     '/openvz/' => 1,
     '/lxc/' => 1,
diff --git a/src/pmxcfs/status.c b/src/pmxcfs/status.c
index c8094ac..078602e 100644
--- a/src/pmxcfs/status.c
+++ b/src/pmxcfs/status.c
@@ -89,6 +89,7 @@ static memdb_change_t memdb_change_array[] = {
 	{ .path = "priv/tfa.cfg" },
 	{ .path = "priv/token.cfg" },
 	{ .path = "priv/ipam.db" },
+	{ .path = "priv/macs.db" },
 	{ .path = "datacenter.cfg" },
 	{ .path = "vzdump.cron" },
 	{ .path = "vzdump.conf" },
-- 
2.39.2




^ permalink raw reply	[flat|nested] 66+ messages in thread

* [pve-devel] [PATCH v4 pve-network 02/33] sdn: preparations for DHCP plugin
  2023-11-17 11:39 [pve-devel] [PATCH v4 cluster/network/manager/qemu-server/container/docs 00/33] Add support for DHCP servers to SDN Stefan Hanreich
  2023-11-17 11:39 ` [pve-devel] [PATCH v4 pve-cluster 01/33] add priv/macs.db Stefan Hanreich
@ 2023-11-17 11:39 ` Stefan Hanreich
  2023-11-17 11:39 ` [pve-devel] [PATCH v4 pve-network 03/33] subnet: add dhcp options Stefan Hanreich
                   ` (33 subsequent siblings)
  35 siblings, 0 replies; 66+ messages in thread
From: Stefan Hanreich @ 2023-11-17 11:39 UTC (permalink / raw)
  To: pve-devel

Add the option to retrieve the running configuration instead of only
the pending configuration via the config methods. Refactor methods
using the running config to utilize the new parameter.

Add helper methods and return additional attributes from the getter
functions that are needed by the dhcp plugins.

Signed-off-by: Stefan Hanreich <s.hanreich@proxmox.com>
---
 src/PVE/Network/SDN/Subnets.pm | 25 +++++++++++++------------
 src/PVE/Network/SDN/Vnets.pm   | 27 +++++++++++++--------------
 src/PVE/Network/SDN/Zones.pm   | 34 +++++++++++++++++++++++++---------
 3 files changed, 51 insertions(+), 35 deletions(-)

diff --git a/src/PVE/Network/SDN/Subnets.pm b/src/PVE/Network/SDN/Subnets.pm
index 6bb42e5..f654d3a 100644
--- a/src/PVE/Network/SDN/Subnets.pm
+++ b/src/PVE/Network/SDN/Subnets.pm
@@ -23,7 +23,9 @@ sub sdn_subnets_config {
     my $scfg = $cfg->{ids}->{$id};
     die "sdn subnet '$id' does not exist\n" if (!$noerr && !$scfg);
 
-    if($scfg) {
+    if ($scfg) {
+	$scfg->{id} = $id;
+
 	my ($zone, $network, $mask) = split(/-/, $id);
 	$scfg->{cidr} = "$network/$mask";
 	$scfg->{zone} = $zone;
@@ -35,7 +37,14 @@ sub sdn_subnets_config {
 }
 
 sub config {
-    my $config = cfs_read_file("sdn/subnets.cfg");
+    my ($running) = @_;
+
+    if ($running) {
+	my $cfg = PVE::Network::SDN::running_config();
+	return $cfg->{subnets};
+    }
+
+    return cfs_read_file("sdn/subnets.cfg");
 }
 
 sub write_config {
@@ -61,16 +70,8 @@ sub complete_sdn_subnet {
 sub get_subnet {
     my ($subnetid, $running) = @_;
 
-    my $cfg = {};
-    if($running) {
-	my $cfg = PVE::Network::SDN::running_config();
-	$cfg = $cfg->{subnets};
-    } else {
-	$cfg = PVE::Network::SDN::Subnets::config();
-    }
-
-    my $subnet = PVE::Network::SDN::Subnets::sdn_subnets_config($cfg, $subnetid, 1);
-    return $subnet;
+    my $cfg = PVE::Network::SDN::Subnets::config($running);
+    return PVE::Network::SDN::Subnets::sdn_subnets_config($cfg, $subnetid, 1);
 }
 
 sub find_ip_subnet {
diff --git a/src/PVE/Network/SDN/Vnets.pm b/src/PVE/Network/SDN/Vnets.pm
index 1106c9f..39bdda0 100644
--- a/src/PVE/Network/SDN/Vnets.pm
+++ b/src/PVE/Network/SDN/Vnets.pm
@@ -26,6 +26,13 @@ sub sdn_vnets_config {
 }
 
 sub config {
+    my ($running) = @_;
+
+    if ($running) {
+	my $cfg = PVE::Network::SDN::running_config();
+	return $cfg->{vnets};
+    }
+
     return cfs_read_file("sdn/vnets.cfg");
 }
 
@@ -54,31 +61,23 @@ sub get_vnet {
 
     return if !$vnetid;
 
-    my $scfg = {};
-    if($running) {
-	my $cfg = PVE::Network::SDN::running_config();
-	$scfg = $cfg->{vnets};
-    } else {
-	$scfg = PVE::Network::SDN::Vnets::config();
-    }
-
-    my $vnet = PVE::Network::SDN::Vnets::sdn_vnets_config($scfg, $vnetid, 1);
-
-    return $vnet;
+    my $cfg = PVE::Network::SDN::Vnets::config($running);
+    return PVE::Network::SDN::Vnets::sdn_vnets_config($cfg, $vnetid, 1);
 }
 
 sub get_subnets {
-    my ($vnetid) = @_;
+    my ($vnetid, $running) = @_;
 
     my $subnets = undef;
-    my $subnets_cfg = PVE::Network::SDN::Subnets::config();
+    my $subnets_cfg = PVE::Network::SDN::Subnets::config($running);
+
     foreach my $subnetid (sort keys %{$subnets_cfg->{ids}}) {
 	my $subnet = PVE::Network::SDN::Subnets::sdn_subnets_config($subnets_cfg, $subnetid);
 	next if !$subnet->{vnet} || ($vnetid && $subnet->{vnet} ne $vnetid);
 	$subnets->{$subnetid} = $subnet;
     }
-    return $subnets;
 
+    return $subnets;
 }
 
 sub get_subnet_from_vnet_cidr {
diff --git a/src/PVE/Network/SDN/Zones.pm b/src/PVE/Network/SDN/Zones.pm
index 4ad4e4d..5bd3536 100644
--- a/src/PVE/Network/SDN/Zones.pm
+++ b/src/PVE/Network/SDN/Zones.pm
@@ -40,8 +40,14 @@ sub sdn_zones_config {
 }
 
 sub config {
-    my $config = cfs_read_file("sdn/zones.cfg");
-    return $config;
+    my ($running) = @_;
+
+    if ($running) {
+	my $cfg = PVE::Network::SDN::running_config();
+	return $cfg->{zones};
+    }
+
+    return cfs_read_file("sdn/zones.cfg");
 }
 
 sub get_plugin_config {
@@ -74,19 +80,29 @@ sub complete_sdn_zone {
 sub get_zone {
     my ($zoneid, $running) = @_;
 
-    my $cfg = {};
-    if($running) {
-        my $cfg = PVE::Network::SDN::running_config();
-        $cfg = $cfg->{vnets};
-    } else {
-        $cfg = PVE::Network::SDN::Zones::config();
-    }
+    my $cfg = PVE::Network::SDN::Zones::config($running);
 
     my $zone = PVE::Network::SDN::Zones::sdn_zones_config($cfg, $zoneid, 1);
 
     return $zone;
 }
 
+sub get_vnets {
+    my ($zoneid, $running) = @_;
+
+    return if !$zoneid;
+
+    my $vnets_config = PVE::Network::SDN::Vnets::config($running);
+    my $vnets = undef;
+
+    for my $vnetid (keys %{$vnets_config->{ids}}) {
+        my $vnet = PVE::Network::SDN::Vnets::sdn_vnets_config($vnets_config, $vnetid);
+        next if !$vnet->{zone} || $vnet->{zone} ne $zoneid;
+        $vnets->{$vnetid} = $vnet;
+    }
+
+    return $vnets;
+}
 
 sub generate_etc_network_config {
 
-- 
2.39.2




^ permalink raw reply	[flat|nested] 66+ messages in thread

* [pve-devel] [PATCH v4 pve-network 03/33] subnet: add dhcp options
  2023-11-17 11:39 [pve-devel] [PATCH v4 cluster/network/manager/qemu-server/container/docs 00/33] Add support for DHCP servers to SDN Stefan Hanreich
  2023-11-17 11:39 ` [pve-devel] [PATCH v4 pve-cluster 01/33] add priv/macs.db Stefan Hanreich
  2023-11-17 11:39 ` [pve-devel] [PATCH v4 pve-network 02/33] sdn: preparations for DHCP plugin Stefan Hanreich
@ 2023-11-17 11:39 ` Stefan Hanreich
  2023-11-17 11:39 ` [pve-devel] [PATCH v4 pve-network 04/33] sdn: zone: add dhcp option Stefan Hanreich
                   ` (32 subsequent siblings)
  35 siblings, 0 replies; 66+ messages in thread
From: Stefan Hanreich @ 2023-11-17 11:39 UTC (permalink / raw)
  To: pve-devel

Additionally add a helper function for parsing the DHCP ranges of a
subnet.

Signed-off-by: Stefan Hanreich <s.hanreich@proxmox.com>
---
 src/PVE/Network/SDN/SubnetPlugin.pm | 29 +++++++++++++++++++++++++++++
 src/PVE/Network/SDN/Subnets.pm      | 23 +++++++++++++++++++++++
 2 files changed, 52 insertions(+)

diff --git a/src/PVE/Network/SDN/SubnetPlugin.pm b/src/PVE/Network/SDN/SubnetPlugin.pm
index 15b370f..a4adae8 100644
--- a/src/PVE/Network/SDN/SubnetPlugin.pm
+++ b/src/PVE/Network/SDN/SubnetPlugin.pm
@@ -61,6 +61,19 @@ sub private {
     return $defaultData;
 }
 
+my $dhcp_range_fmt = {
+    'start-address' => {
+	type => 'ip',
+	description => 'Start address for the DHCP IP range',
+    },
+    'end-address' => {
+	type => 'ip',
+	description => 'End address for the DHCP IP range',
+    },
+};
+
+PVE::JSONSchema::register_format('pve-sdn-dhcp-range', $dhcp_range_fmt);
+
 sub properties {
     return {
         vnet => {
@@ -84,6 +97,20 @@ sub properties {
             type => 'string', format => 'dns-name',
             description => "dns domain zone prefix  ex: 'adm' -> <hostname>.adm.mydomain.com",
         },
+	'dhcp-range' => {
+	    type => 'array',
+	    description => 'A list of DHCP ranges for this subnet',
+	    optional => 1,
+	    items => {
+		type => 'string',
+		format => 'pve-sdn-dhcp-range',
+	    }
+	},
+	'dhcp-dns-server' => {
+	    type => 'ip',
+	    description => 'IP address for the DNS server',
+	    optional => 1,
+	},
     };
 }
 
@@ -94,6 +121,8 @@ sub options {
 #	routes => { optional => 1 },
 	snat => { optional => 1 },
 	dnszoneprefix => { optional => 1 },
+	'dhcp-range' => { optional => 1 },
+	'dhcp-dns-server' => { optional => 1 },
     };
 }
 
diff --git a/src/PVE/Network/SDN/Subnets.pm b/src/PVE/Network/SDN/Subnets.pm
index f654d3a..6e74de1 100644
--- a/src/PVE/Network/SDN/Subnets.pm
+++ b/src/PVE/Network/SDN/Subnets.pm
@@ -8,6 +8,7 @@ use Net::IP;
 use NetAddr::IP qw(:lower);
 
 use PVE::Cluster qw(cfs_read_file cfs_write_file cfs_lock_file);
+use PVE::JSONSchema qw(parse_property_string);
 use PVE::Network::SDN::Dns;
 use PVE::Network::SDN::Ipams;
 
@@ -36,6 +37,28 @@ sub sdn_subnets_config {
     return $scfg;
 }
 
+sub get_dhcp_ranges {
+    my ($subnet_config) = @_;
+
+    my @dhcp_ranges = ();
+
+    if ($subnet_config->{'dhcp-range'}) {
+	foreach my $element (@{$subnet_config->{'dhcp-range'}}) {
+	    my $dhcp_range = eval { parse_property_string('pve-sdn-dhcp-range', $element) };
+
+	    if ($@ || !$dhcp_range) {
+		warn "Unable to parse dhcp-range string: $element\n";
+		warn "$@\n" if $@;
+		next;
+	    }
+
+	    push @dhcp_ranges, $dhcp_range;
+	}
+    }
+
+    return \@dhcp_ranges;
+}
+
 sub config {
     my ($running) = @_;
 
-- 
2.39.2




^ permalink raw reply	[flat|nested] 66+ messages in thread

* [pve-devel] [PATCH v4 pve-network 04/33] sdn: zone: add dhcp option
  2023-11-17 11:39 [pve-devel] [PATCH v4 cluster/network/manager/qemu-server/container/docs 00/33] Add support for DHCP servers to SDN Stefan Hanreich
                   ` (2 preceding siblings ...)
  2023-11-17 11:39 ` [pve-devel] [PATCH v4 pve-network 03/33] subnet: add dhcp options Stefan Hanreich
@ 2023-11-17 11:39 ` Stefan Hanreich
  2023-11-17 11:39 ` [pve-devel] [PATCH v4 pve-network 05/33] ipam: plugins: preparations for DHCP Stefan Hanreich
                   ` (31 subsequent siblings)
  35 siblings, 0 replies; 66+ messages in thread
From: Stefan Hanreich @ 2023-11-17 11:39 UTC (permalink / raw)
  To: pve-devel

Co-Authored-By: Alexandre Derumier <aderumier@odiso.com>
Signed-off-by: Stefan Hanreich <s.hanreich@proxmox.com>
---
 src/PVE/Network/SDN/Zones/SimplePlugin.pm | 7 ++++++-
 1 file changed, 6 insertions(+), 1 deletion(-)

diff --git a/src/PVE/Network/SDN/Zones/SimplePlugin.pm b/src/PVE/Network/SDN/Zones/SimplePlugin.pm
index 4922903..f30278c 100644
--- a/src/PVE/Network/SDN/Zones/SimplePlugin.pm
+++ b/src/PVE/Network/SDN/Zones/SimplePlugin.pm
@@ -26,7 +26,11 @@ sub properties {
 	dnszone => {
 	    type => 'string', format => 'dns-name',
 	    description => "dns domain zone  ex: mydomain.com",
-	}
+	},
+	dhcp => {
+	    type => 'pve-configid',
+	    description => 'ID of the DHCP server responsible for managing this range',
+	},
     };
 }
 
@@ -38,6 +42,7 @@ sub options {
 	reversedns => { optional => 1 },
 	dnszone => { optional => 1 },
 	ipam => { optional => 1 },
+	dhcp => { optional => 1 },
     };
 }
 
-- 
2.39.2




^ permalink raw reply	[flat|nested] 66+ messages in thread

* [pve-devel] [PATCH v4 pve-network 05/33] ipam: plugins: preparations for DHCP
  2023-11-17 11:39 [pve-devel] [PATCH v4 cluster/network/manager/qemu-server/container/docs 00/33] Add support for DHCP servers to SDN Stefan Hanreich
                   ` (3 preceding siblings ...)
  2023-11-17 11:39 ` [pve-devel] [PATCH v4 pve-network 04/33] sdn: zone: add dhcp option Stefan Hanreich
@ 2023-11-17 11:39 ` Stefan Hanreich
  2023-11-17 11:39 ` [pve-devel] [PATCH v4 pve-network 06/33] subnet: vnet: refactor IPAM related methods Stefan Hanreich
                   ` (30 subsequent siblings)
  35 siblings, 0 replies; 66+ messages in thread
From: Stefan Hanreich @ 2023-11-17 11:39 UTC (permalink / raw)
  To: pve-devel

Adds a new file priv/macs.db for caching the queries to IPAM.

Additionally adds and imeplements methods to the IPAM plugins that
are required for the DHCP functionality.

Co-Authored-By: Alexandre Derumier <aderumier@odiso.com>
Signed-off-by: Stefan Hanreich <s.hanreich@proxmox.com>
---
 src/PVE/Network/SDN/Ipams.pm               | 80 +++++++++++++++++++-
 src/PVE/Network/SDN/Ipams/NetboxPlugin.pm  | 86 ++++++++++++++++++++--
 src/PVE/Network/SDN/Ipams/PVEPlugin.pm     | 85 +++++++++++++++++++--
 src/PVE/Network/SDN/Ipams/PhpIpamPlugin.pm | 29 ++++++++
 src/PVE/Network/SDN/Ipams/Plugin.pm        | 19 ++++-
 5 files changed, 281 insertions(+), 18 deletions(-)

diff --git a/src/PVE/Network/SDN/Ipams.pm b/src/PVE/Network/SDN/Ipams.pm
index e8a4b0b..926df90 100644
--- a/src/PVE/Network/SDN/Ipams.pm
+++ b/src/PVE/Network/SDN/Ipams.pm
@@ -4,9 +4,10 @@ use strict;
 use warnings;
 
 use JSON;
+use Net::IP;
 
 use PVE::Tools qw(extract_param dir_glob_regex run_command);
-use PVE::Cluster qw(cfs_read_file cfs_write_file cfs_lock_file);
+use PVE::Cluster qw(cfs_register_file cfs_read_file cfs_write_file cfs_lock_file);
 use PVE::Network;
 
 use PVE::Network::SDN::Ipams::PVEPlugin;
@@ -19,6 +20,64 @@ PVE::Network::SDN::Ipams::NetboxPlugin->register();
 PVE::Network::SDN::Ipams::PhpIpamPlugin->register();
 PVE::Network::SDN::Ipams::Plugin->init();
 
+my $macdb_filename = 'priv/macs.db';
+
+cfs_register_file($macdb_filename, \&json_reader, \&json_writer);
+
+sub json_reader {
+    my ($filename, $data) = @_;
+
+    return defined($data) && length($data) > 0 ? decode_json($data) : {};
+}
+
+sub json_writer {
+    my ($filename, $data) = @_;
+
+    return encode_json($data);
+}
+
+sub read_macdb {
+    my () = @_;
+
+    return cfs_read_file($macdb_filename);
+}
+
+sub write_macdb {
+    my ($data) = @_;
+
+    cfs_write_file($macdb_filename, $data);
+}
+
+sub add_cache_mac_ip {
+    my ($mac, $ip) = @_;
+
+    cfs_lock_file($macdb_filename, undef, sub {
+	my $db = read_macdb();
+	if (Net::IP::ip_is_ipv4($ip)) {
+	    $db->{macs}->{$mac}->{ip4} = $ip;
+	} else {
+	    $db->{macs}->{$mac}->{ip6} = $ip;
+	}
+	write_macdb($db);
+    });
+    warn "$@" if $@;
+}
+
+sub del_cache_mac_ip {
+    my ($mac, $ip) = @_;
+
+    cfs_lock_file($macdb_filename, undef, sub {
+	my $db = read_macdb();
+	if (Net::IP::ip_is_ipv4($ip)) {
+	    delete $db->{macs}->{$mac}->{ip4};
+	} else {
+	    delete $db->{macs}->{$mac}->{ip6};
+	}
+        delete $db->{macs}->{$mac} if !defined($db->{macs}->{$mac}->{ip4}) && !defined($db->{macs}->{$mac}->{ip6});
+	write_macdb($db);
+    });
+    warn "$@" if $@;
+}
 
 sub sdn_ipams_config {
     my ($cfg, $id, $noerr) = @_;
@@ -39,8 +98,8 @@ sub config {
 }
 
 sub get_plugin_config {
-    my ($vnet) = @_;
-    my $ipamid = $vnet->{ipam};
+    my ($zone) = @_;
+    my $ipamid = $zone->{ipam};
     my $ipam_cfg = PVE::Network::SDN::Ipams::config();
     return $ipam_cfg->{ids}->{$ipamid};
 }
@@ -65,5 +124,20 @@ sub complete_sdn_vnet {
     return  $cmdname eq 'add' ? [] : [ PVE::Network::SDN::Vnets::sdn_ipams_ids($cfg) ];
 }
 
+sub get_ips_from_mac {
+    my ($mac, $zoneid, $zone) = @_;
+
+    my $macdb = read_macdb();
+    return ($macdb->{macs}->{$mac}->{ip4}, $macdb->{macs}->{$mac}->{ip6}) if $macdb->{macs}->{$mac};
+
+    my $plugin_config = get_plugin_config($zone);
+    my $plugin = PVE::Network::SDN::Ipams::Plugin->lookup($plugin_config->{type});
+    ($macdb->{macs}->{$mac}->{ip4}, $macdb->{macs}->{$mac}->{ip6}) = $plugin->get_ips_from_mac($plugin_config, $mac, $zoneid);
+
+    write_macdb($macdb);
+
+    return ($macdb->{macs}->{$mac}->{ip4}, $macdb->{macs}->{$mac}->{ip6});
+}
+
 1;
 
diff --git a/src/PVE/Network/SDN/Ipams/NetboxPlugin.pm b/src/PVE/Network/SDN/Ipams/NetboxPlugin.pm
index f0e7168..91010bb 100644
--- a/src/PVE/Network/SDN/Ipams/NetboxPlugin.pm
+++ b/src/PVE/Network/SDN/Ipams/NetboxPlugin.pm
@@ -77,14 +77,20 @@ sub del_subnet {
 }
 
 sub add_ip {
-    my ($class, $plugin_config, $subnetid, $subnet, $ip, $hostname, $mac, $description, $is_gateway, $noerr) = @_;
+    my ($class, $plugin_config, $subnetid, $subnet, $ip, $hostname, $mac, $vmid, $is_gateway, $noerr) = @_;
 
     my $mask = $subnet->{mask};
     my $url = $plugin_config->{url};
     my $token = $plugin_config->{token};
     my $section = $plugin_config->{section};
     my $headers = ['Content-Type' => 'application/json; charset=UTF-8', 'Authorization' => "token $token"];
-    $description .= " mac:$mac" if $mac && $description;
+
+    my $description = undef;
+    if ($is_gateway) {
+	$description = 'gateway'
+    } elsif ($mac) {
+	$description = "mac:$mac";
+    }
 
     my $params = { address => "$ip/$mask", dns_name => $hostname, description => $description };
 
@@ -102,14 +108,20 @@ sub add_ip {
 }
 
 sub update_ip {
-    my ($class, $plugin_config, $subnetid, $subnet, $ip, $hostname, $mac, $description, $is_gateway, $noerr) = @_;
+    my ($class, $plugin_config, $subnetid, $subnet, $ip, $hostname, $mac, $vmid, $is_gateway, $noerr) = @_;
 
     my $mask = $subnet->{mask};
     my $url = $plugin_config->{url};
     my $token = $plugin_config->{token};
     my $section = $plugin_config->{section};
     my $headers = ['Content-Type' => 'application/json; charset=UTF-8', 'Authorization' => "token $token"];
-    $description .= " mac:$mac" if $mac && $description;
+
+    my $description = undef;
+    if ($is_gateway) {
+	$description = 'gateway'
+    } elsif ($mac) {
+	$description = "mac:$mac";
+    }
 
     my $params = { address => "$ip/$mask", dns_name => $hostname, description => $description };
 
@@ -125,7 +137,7 @@ sub update_ip {
 }
 
 sub add_next_freeip {
-    my ($class, $plugin_config, $subnetid, $subnet, $hostname, $mac, $description, $noerr) = @_;
+    my ($class, $plugin_config, $subnetid, $subnet, $hostname, $mac, $vmid, $noerr) = @_;
 
     my $cidr = $subnet->{cidr};
 
@@ -134,7 +146,8 @@ sub add_next_freeip {
     my $headers = ['Content-Type' => 'application/json; charset=UTF-8', 'Authorization' => "token $token"];
 
     my $internalid = get_prefix_id($url, $cidr, $headers);
-    $description .= " mac:$mac" if $mac && $description;
+
+    my $description = "mac:$mac" if $mac;
 
     my $params = { dns_name => $hostname, description => $description };
 
@@ -151,6 +164,33 @@ sub add_next_freeip {
     return $ip;
 }
 
+sub add_range_next_freeip {
+    my ($class, $plugin_config, $subnet, $range, $data, $noerr) = @_;
+
+    my $url = $plugin_config->{url};
+    my $token = $plugin_config->{token};
+    my $headers = ['Content-Type' => 'application/json; charset=UTF-8', 'Authorization' => "token $token"];
+
+    my $internalid = get_iprange_id($url, $range, $headers);
+    my $description = "mac:$data->{mac}" if $data->{mac};
+
+    my $params = { dns_name => $data->{hostname}, description => $description };
+
+    my $ip = undef;
+    eval {
+	my $result = PVE::Network::SDN::api_request("POST", "$url/ipam/ip-ranges/$internalid/available-ips/", $headers, $params);
+	$ip = $result->{address};
+	print "found ip free $ip in range $range->{'start-address'}-$range->{'end-address'}\n" if $ip;
+    };
+
+    if ($@) {
+	die "can't find free ip in range $range->{'start-address'}-$range->{'end-address'}: $@" if !$noerr;
+    }
+
+    return $ip;
+
+}
+
 sub del_ip {
     my ($class, $plugin_config, $subnetid, $subnet, $ip, $noerr) = @_;
 
@@ -171,6 +211,31 @@ sub del_ip {
     }
 }
 
+sub get_ips_from_mac {
+    my ($class, $plugin_config, $mac, $zoneid) = @_;
+
+    my $url = $plugin_config->{url};
+    my $token = $plugin_config->{token};
+    my $headers = ['Content-Type' => 'application/json; charset=UTF-8', 'Authorization' => "token $token"];
+
+    my $ip4 = undef;
+    my $ip6 = undef;
+
+    my $data = PVE::Network::SDN::api_request("GET", "$url/ipam/ip-addresses/?description__ic=$mac", $headers);
+    for my $ip (@{$data->{results}}) {
+	if ($ip->{family}->{value} == 4 && !$ip4) {
+	    ($ip4, undef) = split(/\//, $ip->{address});
+	}
+
+	if ($ip->{family}->{value} == 6 && !$ip6) {
+	    ($ip6, undef) = split(/\//, $ip->{address});
+	}
+    }
+
+    return ($ip4, $ip6);
+}
+
+
 sub verify_api {
     my ($class, $plugin_config) = @_;
 
@@ -204,6 +269,15 @@ sub get_prefix_id {
     return $internalid;
 }
 
+sub get_iprange_id {
+    my ($url, $range, $headers) = @_;
+
+    my $result = PVE::Network::SDN::api_request("GET", "$url/ipam/ip-ranges/?start_address=$range->{'start-address'}&end_address=$range->{'end-address'}", $headers);
+    my $data = @{$result->{results}}[0];
+    my $internalid = $data->{id};
+    return $internalid;
+}
+
 sub get_ip_id {
     my ($url, $ip, $headers) = @_;
     my $result = PVE::Network::SDN::api_request("GET", "$url/ipam/ip-addresses/?q=$ip", $headers);
diff --git a/src/PVE/Network/SDN/Ipams/PVEPlugin.pm b/src/PVE/Network/SDN/Ipams/PVEPlugin.pm
index 3e8ffc5..a5b4fe7 100644
--- a/src/PVE/Network/SDN/Ipams/PVEPlugin.pm
+++ b/src/PVE/Network/SDN/Ipams/PVEPlugin.pm
@@ -82,7 +82,7 @@ sub del_subnet {
 }
 
 sub add_ip {
-    my ($class, $plugin_config, $subnetid, $subnet, $ip, $hostname, $mac, $description, $is_gateway) = @_;
+    my ($class, $plugin_config, $subnetid, $subnet, $ip, $hostname, $mac, $vmid, $is_gateway) = @_;
 
     my $cidr = $subnet->{cidr};
     my $zone = $subnet->{zone};
@@ -96,8 +96,17 @@ sub add_ip {
 	die "subnet '$cidr' doesn't exist in IPAM DB\n" if !$dbsubnet;
 
 	die "IP '$ip' already exist\n" if (!$is_gateway && defined($dbsubnet->{ips}->{$ip})) || ($is_gateway && defined($dbsubnet->{ips}->{$ip}) && !defined($dbsubnet->{ips}->{$ip}->{gateway}));
-	$dbsubnet->{ips}->{$ip} = {};
-	$dbsubnet->{ips}->{$ip} = {gateway => 1} if $is_gateway;
+
+        my $data = {};
+	if ($is_gateway) {
+	    $data->{gateway} = 1;
+	} else {
+	    $data->{vmid} = $vmid if $vmid;
+	    $data->{hostname} = $hostname if $hostname;
+	    $data->{mac} = $mac if $mac;
+	}
+
+	$dbsubnet->{ips}->{$ip} = $data;
 
 	write_db($db);
     });
@@ -105,12 +114,12 @@ sub add_ip {
 }
 
 sub update_ip {
-    my ($class, $plugin_config, $subnetid, $subnet, $ip, $hostname, $mac, $description, $is_gateway) = @_;
+    my ($class, $plugin_config, $subnetid, $subnet, $ip, $hostname, $mac, $vmid, $is_gateway) = @_;
     return;
 }
 
 sub add_next_freeip {
-    my ($class, $plugin_config, $subnetid, $subnet, $hostname, $mac, $description) = @_;
+    my ($class, $plugin_config, $subnetid, $subnet, $hostname, $mac, $vmid, $noerr) = @_;
 
     my $cidr = $subnet->{cidr};
     my $network = $subnet->{network};
@@ -156,6 +165,39 @@ sub add_next_freeip {
     return "$freeip/$mask";
 }
 
+sub add_range_next_freeip {
+    my ($class, $plugin_config, $subnet, $range, $data, $noerr) = @_;
+
+    my $cidr = $subnet->{cidr};
+    my $zone = $subnet->{zone};
+
+    cfs_lock_file($ipamdb_file, undef, sub {
+	my $db = read_db();
+
+	my $dbzone = $db->{zones}->{$zone};
+	die "zone '$zone' doesn't exist in IPAM DB\n" if !$dbzone;
+
+	my $dbsubnet = $dbzone->{subnets}->{$cidr};
+	die "subnet '$cidr' doesn't exist in IPAM DB\n" if !$dbsubnet;
+
+	my $ip = new Net::IP ("$range->{'start-address'} - $range->{'end-address'}")
+	    or die "Invalid IP address(es) in Range!\n";
+	my $mac = $data->{mac};
+
+	do {
+	    my $ip_address = $ip->version() == 6 ? $ip->short() : $ip->ip();
+	    if (!$dbsubnet->{ips}->{$ip_address}) {
+		$dbsubnet->{ips}->{$ip_address} = $data;
+		write_db($db);
+
+		return $ip_address;
+	    }
+	} while (++$ip);
+
+	die "No free IP left in Range $range->{'start-address'}:$range->{'end-address'}}\n";
+    });
+}
+
 sub del_ip {
     my ($class, $plugin_config, $subnetid, $subnet, $ip) = @_;
 
@@ -172,12 +214,43 @@ sub del_ip {
 
 	die "IP '$ip' does not exist in IPAM DB\n" if !defined($dbsubnet->{ips}->{$ip});
 	delete $dbsubnet->{ips}->{$ip};
-
 	write_db($db);
     });
     die "$@" if $@;
 }
 
+sub get_ips_from_mac {
+    my ($class, $plugin_config, $mac, $zoneid) = @_;
+
+    #just in case, as this should already be cached in local macs.db
+
+    my $ip4 = undef;
+    my $ip6 = undef;
+
+    my $db = read_db();
+    die "zone $zoneid don't exist in ipam db" if !$db->{zones}->{$zoneid};
+    my $dbzone = $db->{zones}->{$zoneid};
+    my $subnets = $dbzone->{subnets};
+
+    for my $subnet ( keys %$subnets) {
+	next if Net::IP::ip_is_ipv4($subnet) && $ip4;
+	next if $ip6;
+	my $ips = $subnets->{$subnet}->{ips};
+	for my $ip (keys %$ips) {
+	    my $ipobject = $ips->{$ip};
+	    if ($ipobject->{mac} && $ipobject->{mac} eq $mac) {
+		if (Net::IP::ip_is_ipv4($ip)) {
+		    $ip4 = $ip;
+		} else {
+		    $ip6 = $ip;
+		}
+	    }
+	}
+	last if $ip4 && $ip6;
+    }
+    return ($ip4, $ip6);
+}
+
 #helpers
 
 sub read_db {
diff --git a/src/PVE/Network/SDN/Ipams/PhpIpamPlugin.pm b/src/PVE/Network/SDN/Ipams/PhpIpamPlugin.pm
index ad5286b..1b7b666 100644
--- a/src/PVE/Network/SDN/Ipams/PhpIpamPlugin.pm
+++ b/src/PVE/Network/SDN/Ipams/PhpIpamPlugin.pm
@@ -204,6 +204,35 @@ sub del_ip {
     }
 }
 
+sub get_ips_from_mac {
+    my ($class, $plugin_config, $mac, $zoneid) = @_;
+
+
+    my $url = $plugin_config->{url};
+    my $token = $plugin_config->{token};
+    my $headers = ['Content-Type' => 'application/json; charset=UTF-8', 'Token' => $token];
+
+    my $ip4 = undef;
+    my $ip6 = undef;
+
+    my $ips = PVE::Network::SDN::api_request("GET", "$url/addresses/search_mac/$mac", $headers);
+
+    #fixme
+    die "parsing of result not yet implemented";
+
+    for my $ip (@$ips) {
+#        if ($ip->{family}->{value} == 4 && !$ip4) {
+#            ($ip4, undef) = split(/\//, $ip->{address});
+#        }
+#
+#        if ($ip->{family}->{value} == 6 && !$ip6) {
+#            ($ip6, undef) = split(/\//, $ip->{address});
+#        }
+    }
+
+    return ($ip4, $ip6);
+}
+
 sub verify_api {
     my ($class, $plugin_config) = @_;
 
diff --git a/src/PVE/Network/SDN/Ipams/Plugin.pm b/src/PVE/Network/SDN/Ipams/Plugin.pm
index c96eeda..05d1416 100644
--- a/src/PVE/Network/SDN/Ipams/Plugin.pm
+++ b/src/PVE/Network/SDN/Ipams/Plugin.pm
@@ -79,13 +79,13 @@ sub del_subnet {
 }
 
 sub add_ip {
-    my ($class, $plugin_config, $subnetid, $subnet, $ip, $hostname, $mac, $description, $is_gateway, $noerr) = @_;
+    my ($class, $plugin_config, $subnetid, $subnet, $ip, $hostname, $mac, $vmid, $is_gateway, $noerr) = @_;
 
     die "please implement inside plugin";
 }
 
 sub update_ip {
-    my ($class, $plugin_config, $subnetid, $subnet, $ip, $hostname, $mac, $description, $is_gateway, $noerr) = @_;
+    my ($class, $plugin_config, $subnetid, $subnet, $ip, $hostname, $mac, $vmid, $is_gateway, $noerr) = @_;
     # only update ip attributes (mac,hostname,..). Don't change the ip addresses itself, as some ipam
     # don't allow ip address change without del/add
 
@@ -93,7 +93,14 @@ sub update_ip {
 }
 
 sub add_next_freeip {
-    my ($class, $plugin_config, $subnetid, $subnet, $hostname, $mac, $description, $noerr) = @_;
+    my ($class, $plugin_config, $subnetid, $subnet, $hostname, $mac, $vmid, $noerr) = @_;
+
+    die "please implement inside plugin";
+}
+
+
+sub add_range_next_freeip {
+    my ($class, $plugin_config, $subnet, $range, $data, $noerr) = @_;
 
     die "please implement inside plugin";
 }
@@ -104,6 +111,12 @@ sub del_ip {
     die "please implement inside plugin";
 }
 
+sub get_ips_from_mac {
+    my ($class, $plugin_config, $mac, $zone) = @_;
+
+    die "please implement inside plugin";
+}
+
 sub on_update_hook {
     my ($class, $plugin_config)  = @_;
 }
-- 
2.39.2




^ permalink raw reply	[flat|nested] 66+ messages in thread

* [pve-devel] [PATCH v4 pve-network 06/33] subnet: vnet: refactor IPAM related methods
  2023-11-17 11:39 [pve-devel] [PATCH v4 cluster/network/manager/qemu-server/container/docs 00/33] Add support for DHCP servers to SDN Stefan Hanreich
                   ` (4 preceding siblings ...)
  2023-11-17 11:39 ` [pve-devel] [PATCH v4 pve-network 05/33] ipam: plugins: preparations for DHCP Stefan Hanreich
@ 2023-11-17 11:39 ` Stefan Hanreich
  2023-11-17 14:13   ` Stefan Lendl
  2023-11-17 11:39 ` [pve-devel] [PATCH v4 pve-network 07/33] dhcp: add abstract class for DHCP plugins Stefan Hanreich
                   ` (29 subsequent siblings)
  35 siblings, 1 reply; 66+ messages in thread
From: Stefan Hanreich @ 2023-11-17 11:39 UTC (permalink / raw)
  To: pve-devel

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 <aderumier@odiso.com>
Signed-off-by: Stefan Hanreich <s.hanreich@proxmox.com>
---
 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




^ permalink raw reply	[flat|nested] 66+ messages in thread

* [pve-devel] [PATCH v4 pve-network 07/33] dhcp: add abstract class for DHCP plugins
  2023-11-17 11:39 [pve-devel] [PATCH v4 cluster/network/manager/qemu-server/container/docs 00/33] Add support for DHCP servers to SDN Stefan Hanreich
                   ` (5 preceding siblings ...)
  2023-11-17 11:39 ` [pve-devel] [PATCH v4 pve-network 06/33] subnet: vnet: refactor IPAM related methods Stefan Hanreich
@ 2023-11-17 11:39 ` Stefan Hanreich
  2023-11-17 11:39 ` [pve-devel] [PATCH v4 pve-network 08/33] sdn: dhcp: add dnsmasq plugin Stefan Hanreich
                   ` (28 subsequent siblings)
  35 siblings, 0 replies; 66+ messages in thread
From: Stefan Hanreich @ 2023-11-17 11:39 UTC (permalink / raw)
  To: pve-devel

This abstract class provides several hooks that should be called
during the config generation process, they expose the functionality
for the different configuration tasks required from the DHCP plugins.

add_ip_mapping
Adds a mapping from MAC address to an IP for a given DHCP server. The
DHCP server will then always assign the given IP address to the MAC.

del_ip_mapping
Deletes all mappings for a given MAC address for a given DHCP server.

before_regenerate
Should be called before the plugin does any configuration tasks. The
main usage for this hook is tearing down old instances.

after_regenerate
Should be called after the plugin has finished generating any
configuration. The main usage for this hook is to perform cleanup and
restart / reload services.

before_configure
Should be called before creating the configuration for a specific DHCP
instance, as defined in the dhcp.cfg. This can be used for performing
instance-specific setup.

after_configure
Should be called after the configuration for a specific DHCP instance,
as defined in the dhcp.cfg. This will mainly be used for enabling and
restarting / reloading a specific instance of a DHCP server.

configure_subnet
This function configures the settings for a specific subnet (that can
contain multiple DHCP ranges). This sets global settings for a
specific subnet such as DNS server or gateway.

configure_range
This configures a DHCP range that is available for a given Subnet.

Co-Authored-By: Alexandre Derumier <aderumier@odiso.com>
Signed-off-by: Stefan Hanreich <s.hanreich@proxmox.com>
---
 src/PVE/Network/SDN/Dhcp/Makefile  |  8 ++++
 src/PVE/Network/SDN/Dhcp/Plugin.pm | 65 ++++++++++++++++++++++++++++++
 src/PVE/Network/SDN/Makefile       |  1 +
 3 files changed, 74 insertions(+)
 create mode 100644 src/PVE/Network/SDN/Dhcp/Makefile
 create mode 100644 src/PVE/Network/SDN/Dhcp/Plugin.pm

diff --git a/src/PVE/Network/SDN/Dhcp/Makefile b/src/PVE/Network/SDN/Dhcp/Makefile
new file mode 100644
index 0000000..6546513
--- /dev/null
+++ b/src/PVE/Network/SDN/Dhcp/Makefile
@@ -0,0 +1,8 @@
+SOURCES=Plugin.pm Dnsmasq.pm
+
+
+PERL5DIR=${DESTDIR}/usr/share/perl5
+
+.PHONY: install
+install:
+	for i in ${SOURCES}; do install -D -m 0644 $$i ${PERL5DIR}/PVE/Network/SDN/Dhcp/$$i; done
diff --git a/src/PVE/Network/SDN/Dhcp/Plugin.pm b/src/PVE/Network/SDN/Dhcp/Plugin.pm
new file mode 100644
index 0000000..7b9e9b7
--- /dev/null
+++ b/src/PVE/Network/SDN/Dhcp/Plugin.pm
@@ -0,0 +1,65 @@
+package PVE::Network::SDN::Dhcp::Plugin;
+
+use strict;
+use warnings;
+
+use PVE::Cluster;
+use PVE::JSONSchema qw(get_standard_option);
+
+use base qw(PVE::SectionConfig);
+
+my $defaultData = {
+    propertyList => {
+       type => {
+           description => "Plugin type.",
+           format => 'pve-configid',
+           type => 'string',
+       },
+    },
+};
+
+sub private {
+    return $defaultData;
+}
+
+sub add_ip_mapping {
+    my ($class, $dhcp_config, $mac, $ip) = @_;
+    die 'implement in sub class';
+}
+
+sub del_ip_mapping {
+    my ($class, $dhcp_config, $mac) = @_;
+    die 'implement in sub class';
+}
+
+sub configure_range {
+    my ($class, $dhcp_config, $subnet_config, $range_config) = @_;
+    die 'implement in sub class';
+}
+
+sub configure_subnet {
+    my ($class, $dhcp_config, $subnet_config) = @_;
+    die 'implement in sub class';
+}
+
+sub before_configure {
+    my ($class, $dhcp_config) = @_;
+    die 'implement in sub class';
+}
+
+sub after_configure {
+    my ($class, $dhcp_config) = @_;
+    die 'implement in sub class';
+}
+
+sub before_regenerate {
+    my ($class) = @_;
+    die 'implement in sub class';
+}
+
+sub after_regenerate {
+    my ($class, $dhcp_config) = @_;
+    die 'implement in sub class';
+}
+
+1;
diff --git a/src/PVE/Network/SDN/Makefile b/src/PVE/Network/SDN/Makefile
index 92cfcd0..848f7d4 100644
--- a/src/PVE/Network/SDN/Makefile
+++ b/src/PVE/Network/SDN/Makefile
@@ -10,4 +10,5 @@ install:
 	make -C Zones install
 	make -C Ipams install
 	make -C Dns install
+	make -C Dhcp install
 
-- 
2.39.2




^ permalink raw reply	[flat|nested] 66+ messages in thread

* [pve-devel] [PATCH v4 pve-network 08/33] sdn: dhcp: add dnsmasq plugin
  2023-11-17 11:39 [pve-devel] [PATCH v4 cluster/network/manager/qemu-server/container/docs 00/33] Add support for DHCP servers to SDN Stefan Hanreich
                   ` (6 preceding siblings ...)
  2023-11-17 11:39 ` [pve-devel] [PATCH v4 pve-network 07/33] dhcp: add abstract class for DHCP plugins Stefan Hanreich
@ 2023-11-17 11:39 ` Stefan Hanreich
  2023-11-17 11:39 ` [pve-devel] [PATCH v4 pve-network 09/33] sdn: dhcp: add helper for creating DHCP leases Stefan Hanreich
                   ` (27 subsequent siblings)
  35 siblings, 0 replies; 66+ messages in thread
From: Stefan Hanreich @ 2023-11-17 11:39 UTC (permalink / raw)
  To: pve-devel

The plugin creates a dnsmasq@<zone> service that spawns a dnsmasq
instance that handles dhcp for that zone.

The configuration files for a dnsmasq instance lie within
/etc/dnsmasq.d/<zone>

The plugin generates the following configuration files:
* 00-default.conf
  Contains the default global configuration for dnsmasq. Disables DNS,
  enables some specific options for Windows, does some
  security-related configuration and makes dnsmasq bind only to the
  interfaces it is responsible for
* 10-<subnet>.conf
  Contains the default settings for a subnet. Sets dhcp options for
  DNS and gateway.
* 10-<subnet>.ranges.conf
  Contains the configuration of DHCP ranges for a subnet.
* ethers
  Contains the MAC address to IP mappings for a zone.

Every subnet gets assigned a tag in the dnsmasq configuration that is
equal to the subnet_id. This can be used to override / set additional
configuration options on a per-subnet basis.

Additionally it creates the file /etc/default/dnsmasq.<zone> that
provides default options for the dnsmasq service.

Leases are stored in /var/lib/misc/dnsmasq.<zone>.

Co-Authored-By: Alexandre Derumier <aderumier@odiso.com>
Signed-off-by: Stefan Hanreich <s.hanreich@proxmox.com>
---
 debian/control                      |   1 +
 src/PVE/Network/SDN/Dhcp/Dnsmasq.pm | 198 ++++++++++++++++++++++++++++
 2 files changed, 199 insertions(+)
 create mode 100644 src/PVE/Network/SDN/Dhcp/Dnsmasq.pm

diff --git a/debian/control b/debian/control
index 8b720c3..4424096 100644
--- a/debian/control
+++ b/debian/control
@@ -24,6 +24,7 @@ Depends: libpve-common-perl (>= 5.0-45),
          ${misc:Depends},
          ${perl:Depends},
 Recommends: frr-pythontools (>= 8.5.1~), ifupdown2
+Suggests: dnsmasq
 Description: Proxmox VE's SDN (Software Defined Network) stack
  This package contains the Software Defined Network (tech preview) for
  Proxmox VE.
diff --git a/src/PVE/Network/SDN/Dhcp/Dnsmasq.pm b/src/PVE/Network/SDN/Dhcp/Dnsmasq.pm
new file mode 100644
index 0000000..21a6ddd
--- /dev/null
+++ b/src/PVE/Network/SDN/Dhcp/Dnsmasq.pm
@@ -0,0 +1,198 @@
+package PVE::Network::SDN::Dhcp::Dnsmasq;
+
+use strict;
+use warnings;
+
+use base qw(PVE::Network::SDN::Dhcp::Plugin);
+
+use Net::IP qw(:PROC);
+use PVE::Tools qw(file_set_contents run_command lock_file);
+
+use File::Copy;
+
+my $DNSMASQ_CONFIG_ROOT = '/etc/dnsmasq.d';
+my $DNSMASQ_DEFAULT_ROOT = '/etc/default';
+my $DNSMASQ_LEASE_ROOT = '/var/lib/misc';
+
+sub type {
+    return 'dnsmasq';
+}
+
+sub del_ip_mapping {
+    my ($class, $dhcpid, $mac) = @_;
+
+    my $ethers_file = "$DNSMASQ_CONFIG_ROOT/$dhcpid/ethers";
+    my $ethers_tmp_file = "$ethers_file.tmp";
+
+    my $removeFn = sub {
+	open(my $in, '<', $ethers_file) or die "Could not open file '$ethers_file' $!\n";
+	open(my $out, '>', $ethers_tmp_file) or die "Could not open file '$ethers_tmp_file' $!\n";
+
+        while (my $line = <$in>) {
+	    next if $line =~ m/^$mac/;
+	    print $out $line;
+	}
+
+	close $in;
+	close $out;
+
+	move $ethers_tmp_file, $ethers_file;
+
+	chmod 0644, $ethers_file;
+    };
+
+    PVE::Tools::lock_file($ethers_file, 10, $removeFn);
+
+    if ($@) {
+	warn "Unable to remove $mac from the dnsmasq configuration: $@\n";
+	return;
+    }
+
+    my $service_name = "dnsmasq\@$dhcpid";
+    PVE::Tools::run_command(['systemctl', 'reload', $service_name]);
+}
+
+sub add_ip_mapping {
+    my ($class, $dhcpid, $mac, $ip) = @_;
+
+    my $ethers_file = "$DNSMASQ_CONFIG_ROOT/$dhcpid/ethers";
+    my $ethers_tmp_file = "$ethers_file.tmp";
+
+    my $appendFn = sub {
+	open(my $in, '<', $ethers_file) or die "Could not open file '$ethers_file' $!\n";
+	open(my $out, '>', $ethers_tmp_file) or die "Could not open file '$ethers_tmp_file' $!\n";
+
+        while (my $line = <$in>) {
+	    next if $line =~ m/^$mac/;
+	    print $out $line;
+	}
+
+	print $out "$mac,$ip\n";
+	close $in;
+	close $out;
+	move $ethers_tmp_file, $ethers_file;
+	chmod 0644, $ethers_file;
+    };
+
+    PVE::Tools::lock_file($ethers_file, 10, $appendFn);
+
+    if ($@) {
+	warn "Unable to add $mac/$ip to the dnsmasq configuration: $@\n";
+	return;
+    }
+
+    my $service_name = "dnsmasq\@$dhcpid";
+    PVE::Tools::run_command(['systemctl', 'reload', $service_name]);
+}
+
+sub configure_subnet {
+    my ($class, $dhcpid, $subnet_config) = @_;
+
+    die "No gateway defined for subnet $subnet_config->{id}"
+	if !$subnet_config->{gateway};
+
+    my $tag = $subnet_config->{id};
+
+    my @dnsmasq_config = (
+	"listen-address=$subnet_config->{gateway}",
+    );
+
+    my $option_string;
+    if (ip_is_ipv6($subnet_config->{network})) {
+	$option_string = 'option6';
+	push @dnsmasq_config, "enable-ra";
+    } else {
+	$option_string = 'option';
+	push @dnsmasq_config, "dhcp-option=tag:$tag,$option_string:router,$subnet_config->{gateway}";
+    }
+
+    push @dnsmasq_config, "dhcp-option=tag:$tag,$option_string:dns-server,$subnet_config->{'dhcp-dns-server'}"
+	if $subnet_config->{'dhcp-dns-server'};
+
+    PVE::Tools::file_set_contents(
+	"$DNSMASQ_CONFIG_ROOT/$dhcpid/10-$subnet_config->{id}.conf",
+	join("\n", @dnsmasq_config) . "\n"
+    );
+}
+
+sub configure_range {
+    my ($class, $dhcpid, $subnet_config, $range_config) = @_;
+
+    my $range_file = "$DNSMASQ_CONFIG_ROOT/$dhcpid/10-$subnet_config->{id}.ranges.conf",
+    my $tag = $subnet_config->{id};
+
+    open(my $fh, '>>', $range_file) or die "Could not open file '$range_file' $!\n";
+    print $fh "dhcp-range=set:$tag,$range_config->{'start-address'},$range_config->{'end-address'}\n";
+    close $fh;
+}
+
+sub before_configure {
+    my ($class, $dhcpid) = @_;
+
+    my $config_directory = "$DNSMASQ_CONFIG_ROOT/$dhcpid";
+
+    mkdir($config_directory, 755) if !-d $config_directory;
+
+    my $default_config = <<CFG;
+CONFIG_DIR='$config_directory,\*.conf'
+DNSMASQ_OPTS="--conf-file=/dev/null"
+CFG
+
+    PVE::Tools::file_set_contents(
+	"$DNSMASQ_DEFAULT_ROOT/dnsmasq.$dhcpid",
+	$default_config
+    );
+
+    my $default_dnsmasq_config = <<CFG;
+except-interface=lo
+bind-dynamic
+no-resolv
+no-hosts
+dhcp-leasefile=$DNSMASQ_LEASE_ROOT/dnsmasq.$dhcpid.leases
+dhcp-hostsfile=$config_directory/ethers
+dhcp-ignore=tag:!known
+
+# Send an empty WPAD option. This may be REQUIRED to get windows 7 to behave.
+dhcp-option=252,"\\n"
+
+# Send microsoft-specific option to tell windows to release the DHCP lease
+# when it shuts down. Note the "i" flag, to tell dnsmasq to send the
+# value as a four-byte integer - that's what microsoft wants.
+dhcp-option=vendor:MSFT,2,1i
+
+# If a DHCP client claims that its name is "wpad", ignore that.
+# This fixes a security hole. see CERT Vulnerability VU#598349
+dhcp-name-match=set:wpad-ignore,wpad
+dhcp-ignore-names=tag:wpad-ignore
+CFG
+
+    PVE::Tools::file_set_contents(
+	"$config_directory/00-default.conf",
+	$default_dnsmasq_config
+    );
+
+    unlink glob "$config_directory/10-*.conf";
+}
+
+sub after_configure {
+    my ($class, $dhcpid) = @_;
+
+    my $service_name = "dnsmasq\@$dhcpid";
+
+    PVE::Tools::run_command(['systemctl', 'enable', $service_name]);
+    PVE::Tools::run_command(['systemctl', 'restart', $service_name]);
+}
+
+sub before_regenerate {
+    my ($class) = @_;
+
+    PVE::Tools::run_command(['systemctl', 'stop', "dnsmasq@*"]);
+    PVE::Tools::run_command(['systemctl', 'disable', 'dnsmasq@']);
+}
+
+sub after_regenerate {
+    my ($class) = @_;
+    # noop
+}
+
+1;
-- 
2.39.2




^ permalink raw reply	[flat|nested] 66+ messages in thread

* [pve-devel] [PATCH v4 pve-network 09/33] sdn: dhcp: add helper for creating DHCP leases
  2023-11-17 11:39 [pve-devel] [PATCH v4 cluster/network/manager/qemu-server/container/docs 00/33] Add support for DHCP servers to SDN Stefan Hanreich
                   ` (7 preceding siblings ...)
  2023-11-17 11:39 ` [pve-devel] [PATCH v4 pve-network 08/33] sdn: dhcp: add dnsmasq plugin Stefan Hanreich
@ 2023-11-17 11:39 ` Stefan Hanreich
  2023-11-17 11:39 ` [pve-devel] [PATCH v4 pve-network 10/33] api: add endpoints for managing PVE IPAM Stefan Hanreich
                   ` (26 subsequent siblings)
  35 siblings, 0 replies; 66+ messages in thread
From: Stefan Hanreich @ 2023-11-17 11:39 UTC (permalink / raw)
  To: pve-devel

This helper can be used to create DHCP entries for a specific zone. It
is used by the API to create DHCP leases for VMs/CTs.

Co-Authored-By: Alexandre Derumier <aderumier@odiso.com>
Signed-off-by: Stefan Hanreich <s.hanreich@proxmox.com>
---
 src/PVE/Network/SDN/Dhcp.pm  | 115 +++++++++++++++++++++++++++++++++++
 src/PVE/Network/SDN/Makefile |   2 +-
 2 files changed, 116 insertions(+), 1 deletion(-)
 create mode 100644 src/PVE/Network/SDN/Dhcp.pm

diff --git a/src/PVE/Network/SDN/Dhcp.pm b/src/PVE/Network/SDN/Dhcp.pm
new file mode 100644
index 0000000..a05b441
--- /dev/null
+++ b/src/PVE/Network/SDN/Dhcp.pm
@@ -0,0 +1,115 @@
+package PVE::Network::SDN::Dhcp;
+
+use strict;
+use warnings;
+
+use PVE::Cluster qw(cfs_read_file);
+
+use PVE::Network::SDN;
+use PVE::Network::SDN::SubnetPlugin;
+use PVE::Network::SDN::Dhcp qw(config);
+use PVE::Network::SDN::Subnets qw(sdn_subnets_config config get_dhcp_ranges);
+use PVE::Network::SDN::Dhcp::Plugin;
+use PVE::Network::SDN::Dhcp::Dnsmasq;
+
+use PVE::INotify qw(nodename);
+
+PVE::Network::SDN::Dhcp::Plugin->init();
+
+PVE::Network::SDN::Dhcp::Dnsmasq->register();
+PVE::Network::SDN::Dhcp::Dnsmasq->init();
+
+sub add_mapping {
+    my ($vnetid, $mac, $ip4, $ip6) = @_;
+
+    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};
+
+    my $dhcp_plugin = PVE::Network::SDN::Dhcp::Plugin->lookup($zone->{dhcp});
+    $dhcp_plugin->add_ip_mapping($zoneid, $mac, $ip4) if $ip4;
+    $dhcp_plugin->add_ip_mapping($zoneid, $mac, $ip6) if $ip6;
+}
+
+sub remove_mapping {
+    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};
+
+    my $dhcp_plugin = PVE::Network::SDN::Dhcp::Plugin->lookup($zone->{dhcp});
+    $dhcp_plugin->del_ip_mapping($zoneid, $mac);
+}
+
+sub regenerate_config {
+    my ($reload) = @_;
+
+    my $cfg = PVE::Network::SDN::running_config();
+
+    my $zone_cfg = $cfg->{zones};
+    my $subnet_cfg = $cfg->{subnets};
+    return if !$zone_cfg && !$subnet_cfg;
+
+    my $nodename = PVE::INotify::nodename();
+
+    my $plugins = PVE::Network::SDN::Dhcp::Plugin->lookup_types();
+
+    foreach my $plugin_name (@$plugins) {
+	my $plugin = PVE::Network::SDN::Dhcp::Plugin->lookup($plugin_name);
+	eval { $plugin->before_regenerate() };
+	die "Could not run before_regenerate for DHCP plugin $plugin_name $@\n" if $@;
+    }
+
+    foreach my $zoneid (sort keys %{$zone_cfg->{ids}}) {
+        my $zone = $zone_cfg->{ids}->{$zoneid};
+        next if !$zone->{dhcp};
+
+	my $dhcp_plugin_name = $zone->{dhcp};
+	my $dhcp_plugin = PVE::Network::SDN::Dhcp::Plugin->lookup($dhcp_plugin_name);
+
+	die "Could not find DHCP plugin: $dhcp_plugin_name" if !$dhcp_plugin;
+
+	eval { $dhcp_plugin->before_configure($zoneid) };
+	die "Could not run before_configure for DHCP server $zoneid $@\n" if $@;
+
+
+	foreach my $subnet_id (keys %{$subnet_cfg->{ids}}) {
+	    my $subnet_config = PVE::Network::SDN::Subnets::sdn_subnets_config($subnet_cfg, $subnet_id);
+	    my $dhcp_ranges = PVE::Network::SDN::Subnets::get_dhcp_ranges($subnet_config);
+
+	    my ($zone, $subnet_network, $subnet_mask) = split(/-/, $subnet_id);
+	    next if $zone ne $zoneid;
+	    next if !$dhcp_ranges;
+
+	    eval { $dhcp_plugin->configure_subnet($zoneid, $subnet_config) };
+	    warn "Could not configure subnet $subnet_id: $@\n" if $@;
+
+	    foreach my $dhcp_range (@$dhcp_ranges) {
+		eval { $dhcp_plugin->configure_range($zoneid, $subnet_config, $dhcp_range) };
+		warn "Could not configure DHCP range for $subnet_id: $@\n" if $@;
+	    }
+	}
+
+	eval { $dhcp_plugin->after_configure($zoneid) };
+	warn "Could not run after_configure for DHCP server $zoneid $@\n" if $@;
+
+    }
+
+    foreach my $plugin_name (@$plugins) {
+	my $plugin = PVE::Network::SDN::Dhcp::Plugin->lookup($plugin_name);
+
+	eval { $plugin->after_regenerate() };
+	warn "Could not run after_regenerate for DHCP plugin $plugin_name $@\n" if $@;
+    }
+}
+
+1;
diff --git a/src/PVE/Network/SDN/Makefile b/src/PVE/Network/SDN/Makefile
index 848f7d4..3e6e5fb 100644
--- a/src/PVE/Network/SDN/Makefile
+++ b/src/PVE/Network/SDN/Makefile
@@ -1,4 +1,4 @@
-SOURCES=Vnets.pm VnetPlugin.pm Zones.pm Controllers.pm Subnets.pm SubnetPlugin.pm Ipams.pm Dns.pm
+SOURCES=Vnets.pm VnetPlugin.pm Zones.pm Controllers.pm Subnets.pm SubnetPlugin.pm Ipams.pm Dns.pm Dhcp.pm
 
 
 PERL5DIR=${DESTDIR}/usr/share/perl5
-- 
2.39.2




^ permalink raw reply	[flat|nested] 66+ messages in thread

* [pve-devel] [PATCH v4 pve-network 10/33] api: add endpoints for managing PVE IPAM
  2023-11-17 11:39 [pve-devel] [PATCH v4 cluster/network/manager/qemu-server/container/docs 00/33] Add support for DHCP servers to SDN Stefan Hanreich
                   ` (8 preceding siblings ...)
  2023-11-17 11:39 ` [pve-devel] [PATCH v4 pve-network 09/33] sdn: dhcp: add helper for creating DHCP leases Stefan Hanreich
@ 2023-11-17 11:39 ` Stefan Hanreich
  2023-11-18 16:27   ` Thomas Lamprecht
  2023-11-17 11:39 ` [pve-devel] [PATCH v4 pve-network 11/33] api: subnet: add dhcp ranges Stefan Hanreich
                   ` (25 subsequent siblings)
  35 siblings, 1 reply; 66+ messages in thread
From: Stefan Hanreich @ 2023-11-17 11:39 UTC (permalink / raw)
  To: pve-devel

Signed-off-by: Stefan Hanreich <s.hanreich@proxmox.com>
---
 src/PVE/API2/Network/SDN.pm       |   6 +
 src/PVE/API2/Network/SDN/Ipam.pm  | 221 ++++++++++++++++++++++++++++++
 src/PVE/API2/Network/SDN/Makefile |   2 +-
 3 files changed, 228 insertions(+), 1 deletion(-)
 create mode 100644 src/PVE/API2/Network/SDN/Ipam.pm

diff --git a/src/PVE/API2/Network/SDN.pm b/src/PVE/API2/Network/SDN.pm
index d216e48..551afcf 100644
--- a/src/PVE/API2/Network/SDN.pm
+++ b/src/PVE/API2/Network/SDN.pm
@@ -15,6 +15,7 @@ use PVE::Network::SDN;
 use PVE::API2::Network::SDN::Controllers;
 use PVE::API2::Network::SDN::Vnets;
 use PVE::API2::Network::SDN::Zones;
+use PVE::API2::Network::SDN::Ipam;
 use PVE::API2::Network::SDN::Ipams;
 use PVE::API2::Network::SDN::Dns;
 
@@ -35,6 +36,11 @@ __PACKAGE__->register_method ({
     path => 'controllers',
 });
 
+__PACKAGE__->register_method ({
+    subclass => "PVE::API2::Network::SDN::Ipam",
+    path => 'ipam',
+});
+
 __PACKAGE__->register_method ({
     subclass => "PVE::API2::Network::SDN::Ipams",
     path => 'ipams',
diff --git a/src/PVE/API2/Network/SDN/Ipam.pm b/src/PVE/API2/Network/SDN/Ipam.pm
new file mode 100644
index 0000000..e71ca7d
--- /dev/null
+++ b/src/PVE/API2/Network/SDN/Ipam.pm
@@ -0,0 +1,221 @@
+package PVE::API2::Network::SDN::Ipam;
+
+use strict;
+use warnings;
+
+use PVE::Tools qw(extract_param);
+use PVE::Cluster qw(cfs_read_file cfs_write_file);
+
+use PVE::Network::SDN;
+use PVE::Network::SDN::Dhcp;
+use PVE::Network::SDN::Vnets;
+use PVE::Network::SDN::Ipams::Plugin;
+
+use PVE::JSONSchema qw(get_standard_option);
+use PVE::RPCEnvironment;
+
+use PVE::RESTHandler;
+
+use base qw(PVE::RESTHandler);
+
+__PACKAGE__->register_method ({
+    name => 'ipamindex',
+    path => '',
+    method => 'GET',
+    description => 'List PVE IPAM Entries',
+    protected => 1,
+    permissions => {
+	description => "Only list entries where you have 'SDN.Audit' or 'SDN.Allocate' permissions on '/sdn/zones/<zone>/<vnet>'",
+	user => 'all',
+    },
+    parameters => {
+	additionalProperties => 0,
+    },
+    returns => {
+	type => 'array',
+    },
+    code => sub {
+	my ($param) = @_;
+
+	my $rpcenv = PVE::RPCEnvironment::get();
+	my $authuser = $rpcenv->get_user();
+	my $privs = [ 'SDN.Audit', 'SDN.Allocate' ];
+
+	my $ipam_plugin = PVE::Network::SDN::Ipams::Plugin->lookup('pve');
+	my $ipam_db = $ipam_plugin->read_db();
+
+	my $result = [];
+
+	for my $zone_id (keys %{$ipam_db->{zones}}) {
+	    my $zone_config = PVE::Network::SDN::Zones::get_zone($zone_id, 1);
+            next if !$zone_config || $zone_config->{ipam} ne 'pve' || !$zone_config->{dhcp};
+
+	    my $zone = $ipam_db->{zones}->{$zone_id};
+
+	    my $vnets = PVE::Network::SDN::Zones::get_vnets($zone_id, 1);
+
+	    for my $subnet_cidr (keys %{$zone->{subnets}}) {
+		my $subnet = $zone->{subnets}->{$subnet_cidr};
+		my $ip = new NetAddr::IP($subnet_cidr) or die 'Found invalid CIDR in IPAM';
+
+		my $vnet = undef;
+		for my $vnet_id (keys %$vnets) {
+		    eval {
+			my ($zone, $subnetid, $subnet_cfg, $ip) = PVE::Network::SDN::Vnets::get_subnet_from_vnet_ip(
+			    $vnet_id,
+			    $ip->addr,
+			);
+
+			$vnet = $subnet_cfg->{vnet};
+		    };
+
+		    last if $vnet;
+		}
+
+		next if !$vnet || !$rpcenv->check_any($authuser, "/sdn/zones/$zone_id/$vnet", $privs, 1);
+
+		for my $ip (keys %{$subnet->{ips}}) {
+		    my $entry = $subnet->{ips}->{$ip};
+		    $entry->{zone} = $zone_id;
+		    $entry->{subnet} = $subnet_cidr;
+		    $entry->{ip} = $ip;
+		    $entry->{vnet} = $vnet;
+
+		    push @$result, $entry;
+		}
+	    }
+	}
+
+	return $result;
+    },
+});
+
+__PACKAGE__->register_method ({
+    name => 'dhcpdelete',
+    path => '{zone}/{vnet}/{mac}',
+    method => 'DELETE',
+    description => 'Delete DHCP Mappings in a VNet for a MAC address',
+    protected => 1,
+    permissions => {
+	check => ['perm', '/sdn/zones/{zone}/{vnet}', [ 'SDN.Allocate' ]],
+    },
+    parameters => {
+	additionalProperties => 0,
+	properties => {
+	    zone => get_standard_option('pve-sdn-zone-id'),
+	    vnet => get_standard_option('pve-sdn-vnet-id'),
+	    mac => get_standard_option('mac-addr'),
+	},
+    },
+    returns => { type => 'null' },
+    code => sub {
+	my ($param) = @_;
+
+	my $vnet = extract_param($param, 'vnet');
+	my $mac = extract_param($param, 'mac');
+
+	PVE::Network::SDN::Dhcp::remove_mapping($vnet, $mac);
+
+	eval {
+	    PVE::Network::SDN::Vnets::del_ips_from_mac($vnet, $mac);
+	};
+	my $error = $@;
+
+	PVE::Network::SDN::Vnets::add_dhcp_mapping($vnet, $mac) if $error;
+	die "$error\n" if $error;
+
+	return undef;
+    },
+});
+
+__PACKAGE__->register_method ({
+    name => 'dhcpcreate',
+    path => '{zone}/{vnet}/{mac}',
+    method => 'POST',
+    description => 'Create DHCP Mapping',
+    protected => 1,
+    permissions => {
+	check => ['perm', '/sdn/zones/{zone}/{vnet}', [ 'SDN.Allocate' ]],
+    },
+    parameters => {
+	additionalProperties => 0,
+	properties => {
+	    zone => get_standard_option('pve-sdn-zone-id'),
+	    vnet => get_standard_option('pve-sdn-vnet-id'),
+	    mac => get_standard_option('mac-addr'),
+	    ip => {
+		type => 'string',
+		format => 'ip',
+		description => 'The IP address to associate with the given MAC address',
+	    },
+	},
+    },
+    returns => { type => 'null' },
+    code => sub {
+	my ($param) = @_;
+
+	my $vnet = extract_param($param, 'vnet');
+	my $mac = extract_param($param, 'mac');
+	my $ip = extract_param($param, 'ip');
+
+	PVE::Network::SDN::Vnets::add_ip($vnet, $ip, '', $mac, undef);
+	PVE::Network::SDN::Vnets::add_dhcp_mapping($vnet, $mac);
+
+	return undef;
+    },
+});
+__PACKAGE__->register_method ({
+    name => 'dhcpupdate',
+    path => '{zone}/{vnet}/{mac}',
+    method => 'PUT',
+    description => 'Update DHCP Mapping',
+    protected => 1,
+    permissions => {
+	check => ['perm', '/sdn/zones/{zone}/{vnet}', [ 'SDN.Allocate' ]],
+    },
+    parameters => {
+	additionalProperties => 0,
+	properties => {
+	    zone => get_standard_option('pve-sdn-zone-id'),
+	    vnet => get_standard_option('pve-sdn-vnet-id'),
+	    vmid => get_standard_option('pve-vmid', {
+		optional => 1,
+	    }),
+	    mac => get_standard_option('mac-addr'),
+	    ip => {
+		type => 'string',
+		format => 'ip',
+		description => 'The IP address to associate with the given MAC address',
+	    },
+	},
+    },
+    returns => { type => 'null' },
+    code => sub {
+	my ($param) = @_;
+
+	my $vnet = extract_param($param, 'vnet');
+	my $mac = extract_param($param, 'mac');
+	my $vmid = extract_param($param, 'vmid');
+	my $ip = extract_param($param, 'ip');
+
+	PVE::Network::SDN::Dhcp::remove_mapping($vnet, $mac);
+	my ($old_ip4, $old_ip6) = PVE::Network::SDN::Vnets::del_ips_from_mac($vnet, $mac, '');
+
+	eval {
+	    PVE::Network::SDN::Vnets::add_ip($vnet, $ip, '', $mac, $vmid);
+	};
+	my $error = $@;
+
+	if ($error) {
+	    PVE::Network::SDN::Vnets::add_ip($vnet, $old_ip4, '', $mac, $vmid) if $old_ip4;
+	    PVE::Network::SDN::Vnets::add_ip($vnet, $old_ip6, '', $mac, $vmid) if $old_ip6;
+	}
+
+	PVE::Network::SDN::Vnets::add_dhcp_mapping($vnet, $mac);
+
+	die "$error\n" if $error;
+	return undef;
+    },
+});
+
+1;
diff --git a/src/PVE/API2/Network/SDN/Makefile b/src/PVE/API2/Network/SDN/Makefile
index 3683fa4..2480c09 100644
--- a/src/PVE/API2/Network/SDN/Makefile
+++ b/src/PVE/API2/Network/SDN/Makefile
@@ -1,4 +1,4 @@
-SOURCES=Vnets.pm Zones.pm Controllers.pm Subnets.pm Ipams.pm Dns.pm
+SOURCES=Vnets.pm Zones.pm Controllers.pm Subnets.pm Ipams.pm Ipam.pm Dns.pm
 
 
 PERL5DIR=${DESTDIR}/usr/share/perl5
-- 
2.39.2




^ permalink raw reply	[flat|nested] 66+ messages in thread

* [pve-devel] [PATCH v4 pve-network 11/33] api: subnet: add dhcp ranges
  2023-11-17 11:39 [pve-devel] [PATCH v4 cluster/network/manager/qemu-server/container/docs 00/33] Add support for DHCP servers to SDN Stefan Hanreich
                   ` (9 preceding siblings ...)
  2023-11-17 11:39 ` [pve-devel] [PATCH v4 pve-network 10/33] api: add endpoints for managing PVE IPAM Stefan Hanreich
@ 2023-11-17 11:39 ` Stefan Hanreich
  2023-11-17 11:39 ` [pve-devel] [PATCH v4 pve-network 12/33] api: zone: add dhcp option Stefan Hanreich
                   ` (24 subsequent siblings)
  35 siblings, 0 replies; 66+ messages in thread
From: Stefan Hanreich @ 2023-11-17 11:39 UTC (permalink / raw)
  To: pve-devel

Signed-off-by: Stefan Hanreich <s.hanreich@proxmox.com>
---
 src/PVE/API2/Network/SDN/Subnets.pm | 1 +
 1 file changed, 1 insertion(+)

diff --git a/src/PVE/API2/Network/SDN/Subnets.pm b/src/PVE/API2/Network/SDN/Subnets.pm
index eb6b41b..c263cd5 100644
--- a/src/PVE/API2/Network/SDN/Subnets.pm
+++ b/src/PVE/API2/Network/SDN/Subnets.pm
@@ -29,6 +29,7 @@ my $api_sdn_subnets_config = sub {
     my $scfg = dclone(PVE::Network::SDN::Subnets::sdn_subnets_config($cfg, $id));
     $scfg->{subnet} = $id;
     $scfg->{digest} = $cfg->{digest};
+    $scfg->{'dhcp-range'} = PVE::Network::SDN::Subnets::get_dhcp_ranges($scfg);
 
     return $scfg;
 };
-- 
2.39.2




^ permalink raw reply	[flat|nested] 66+ messages in thread

* [pve-devel] [PATCH v4 pve-network 12/33] api: zone: add dhcp option
  2023-11-17 11:39 [pve-devel] [PATCH v4 cluster/network/manager/qemu-server/container/docs 00/33] Add support for DHCP servers to SDN Stefan Hanreich
                   ` (10 preceding siblings ...)
  2023-11-17 11:39 ` [pve-devel] [PATCH v4 pve-network 11/33] api: subnet: add dhcp ranges Stefan Hanreich
@ 2023-11-17 11:39 ` Stefan Hanreich
  2023-11-17 11:39 ` [pve-devel] [PATCH v4 pve-network 13/33] dhcp: regenerate config for DHCP plugins on applying configuration Stefan Hanreich
                   ` (23 subsequent siblings)
  35 siblings, 0 replies; 66+ messages in thread
From: Stefan Hanreich @ 2023-11-17 11:39 UTC (permalink / raw)
  To: pve-devel

Signed-off-by: Stefan Hanreich <s.hanreich@proxmox.com>
---
 src/PVE/API2/Network/SDN/Zones.pm | 1 +
 1 file changed, 1 insertion(+)

diff --git a/src/PVE/API2/Network/SDN/Zones.pm b/src/PVE/API2/Network/SDN/Zones.pm
index 4c8b7e1..1c3356e 100644
--- a/src/PVE/API2/Network/SDN/Zones.pm
+++ b/src/PVE/API2/Network/SDN/Zones.pm
@@ -99,6 +99,7 @@ __PACKAGE__->register_method ({
 			    reversedns => { type => 'string', optional => 1},
 			    dnszone => { type => 'string', optional => 1},
 			    ipam => { type => 'string', optional => 1},
+			    dhcp => { type => 'string', optional => 1},
 			    pending => { optional => 1},
 			    state => { type => 'string', optional => 1},
 			    nodes => { type => 'string', optional => 1},
-- 
2.39.2




^ permalink raw reply	[flat|nested] 66+ messages in thread

* [pve-devel] [PATCH v4 pve-network 13/33] dhcp: regenerate config for DHCP plugins on applying configuration
  2023-11-17 11:39 [pve-devel] [PATCH v4 cluster/network/manager/qemu-server/container/docs 00/33] Add support for DHCP servers to SDN Stefan Hanreich
                   ` (11 preceding siblings ...)
  2023-11-17 11:39 ` [pve-devel] [PATCH v4 pve-network 12/33] api: zone: add dhcp option Stefan Hanreich
@ 2023-11-17 11:39 ` Stefan Hanreich
  2023-11-17 11:39 ` [pve-devel] [PATCH v4 pve-network 14/33] sdn: fix tests Stefan Hanreich
                   ` (22 subsequent siblings)
  35 siblings, 0 replies; 66+ messages in thread
From: Stefan Hanreich @ 2023-11-17 11:39 UTC (permalink / raw)
  To: pve-devel

Signed-off-by: Stefan Hanreich <s.hanreich@proxmox.com>
---
 src/PVE/Network/SDN.pm | 9 ++++++++-
 1 file changed, 8 insertions(+), 1 deletion(-)

diff --git a/src/PVE/Network/SDN.pm b/src/PVE/Network/SDN.pm
index 057034f..c306527 100644
--- a/src/PVE/Network/SDN.pm
+++ b/src/PVE/Network/SDN.pm
@@ -12,6 +12,7 @@ use PVE::Network::SDN::Vnets;
 use PVE::Network::SDN::Zones;
 use PVE::Network::SDN::Controllers;
 use PVE::Network::SDN::Subnets;
+use PVE::Network::SDN::Dhcp;
 
 use PVE::Tools qw(extract_param dir_glob_regex run_command);
 use PVE::Cluster qw(cfs_read_file cfs_write_file cfs_lock_file);
@@ -155,7 +156,7 @@ sub commit_config {
     my $controllers = { ids => $controllers_cfg->{ids} };
     my $subnets = { ids => $subnets_cfg->{ids} };
 
-     $cfg = { version => $version, vnets => $vnets, zones => $zones, controllers => $controllers, subnets => $subnets };
+    $cfg = { version => $version, vnets => $vnets, zones => $zones, controllers => $controllers, subnets => $subnets };
 
     cfs_write_file($running_cfg, $cfg);
 }
@@ -231,6 +232,12 @@ sub generate_controller_config {
     PVE::Network::SDN::Controllers::reload_controller() if $reload;
 }
 
+sub generate_dhcp_config {
+    my ($reload) = @_;
+
+    PVE::Network::SDN::Dhcp::regenerate_config($reload);
+}
+
 sub encode_value {
     my ($type, $key, $value) = @_;
 
-- 
2.39.2




^ permalink raw reply	[flat|nested] 66+ messages in thread

* [pve-devel] [PATCH v4 pve-network 14/33] sdn: fix tests
  2023-11-17 11:39 [pve-devel] [PATCH v4 cluster/network/manager/qemu-server/container/docs 00/33] Add support for DHCP servers to SDN Stefan Hanreich
                   ` (12 preceding siblings ...)
  2023-11-17 11:39 ` [pve-devel] [PATCH v4 pve-network 13/33] dhcp: regenerate config for DHCP plugins on applying configuration Stefan Hanreich
@ 2023-11-17 11:39 ` Stefan Hanreich
  2023-11-17 11:39 ` [pve-devel] [PATCH v4 pve-network 15/33] sdn: fix subnets && netbox ipam tests Stefan Hanreich
                   ` (21 subsequent siblings)
  35 siblings, 0 replies; 66+ messages in thread
From: Stefan Hanreich @ 2023-11-17 11:39 UTC (permalink / raw)
  To: pve-devel

From: Alexandre Derumier <aderumier@odiso.com>

Signed-off-by: Stefan Hanreich <s.hanreich@proxmox.com>
---
 src/test/run_test_subnets.pl | 8 +++++++-
 src/test/run_test_vnets.pl   | 4 ++--
 2 files changed, 9 insertions(+), 3 deletions(-)

diff --git a/src/test/run_test_subnets.pl b/src/test/run_test_subnets.pl
index f6564e1..c98359a 100755
--- a/src/test/run_test_subnets.pl
+++ b/src/test/run_test_subnets.pl
@@ -109,6 +109,12 @@ foreach my $path (@plugins) {
 	    my $ipam_config = read_sdn_config ("$path/ipam_config");
 	    return $ipam_config;
 	},
+	add_cache_mac_ip => sub {
+	    return;
+	},
+	del_cache_mac_ip => sub {
+	    return;
+	}
     );
 
     ## add_subnet
@@ -192,7 +198,7 @@ foreach my $path (@plugins) {
     $expected = '{"zones":{"myzone":{"subnets":{"'.$subnet_cidr.'":{"ips":{"'.$ip.'":{"gateway":1},"'.$ipnextfree.'":{},"'.$ip2.'":{}}}}}}}';
 
     eval {
-	$ip3 = PVE::Network::SDN::Subnets::next_free_ip($zone, $subnetid, $subnet, $hostname, $mac, $description);
+	$ip3 = PVE::Network::SDN::Subnets::add_next_free_ip($zone, $subnetid, $subnet, $hostname, $mac, $description);
     };
 
     if ($@) {
diff --git a/src/test/run_test_vnets.pl b/src/test/run_test_vnets.pl
index 5aeb676..dc9da67 100755
--- a/src/test/run_test_vnets.pl
+++ b/src/test/run_test_vnets.pl
@@ -231,7 +231,7 @@ foreach my $path (@plugins) {
     $expected = $ipam ? $cidr3 : undef;
 
     eval {
-	$result = PVE::Network::SDN::Vnets::get_next_free_cidr($vnetid, $hostname, $mac, $description, $ipversion);
+	$result = PVE::Network::SDN::Vnets::add_next_free_cidr($vnetid, $hostname, $mac, $description);
     };
 
     if ($@) {
@@ -309,7 +309,7 @@ foreach my $path (@plugins) {
     $expected = $ipam ? $cidr1 : undef;
 
     eval {
-	$result = PVE::Network::SDN::Vnets::get_next_free_cidr($vnetid, $hostname, $mac, $description, $ipversion);
+	$result = PVE::Network::SDN::Vnets::add_next_free_cidr($vnetid, $hostname, $mac, $description);
     };
 
     if ($@) {
-- 
2.39.2




^ permalink raw reply	[flat|nested] 66+ messages in thread

* [pve-devel] [PATCH v4 pve-network 15/33] sdn: fix subnets && netbox ipam tests
  2023-11-17 11:39 [pve-devel] [PATCH v4 cluster/network/manager/qemu-server/container/docs 00/33] Add support for DHCP servers to SDN Stefan Hanreich
                   ` (13 preceding siblings ...)
  2023-11-17 11:39 ` [pve-devel] [PATCH v4 pve-network 14/33] sdn: fix tests Stefan Hanreich
@ 2023-11-17 11:39 ` Stefan Hanreich
  2023-11-17 11:39 ` [pve-devel] [PATCH v4 pve-network 16/33] add add_dhcp_mapping Stefan Hanreich
                   ` (20 subsequent siblings)
  35 siblings, 0 replies; 66+ messages in thread
From: Stefan Hanreich @ 2023-11-17 11:39 UTC (permalink / raw)
  To: pve-devel

From: Alexandre Derumier <aderumier@odiso.com>

Signed-off-by: Alexandre Derumier <aderumier@odiso.com>
---
 src/test/ipams/netbox/expected.add_ip            | 2 +-
 src/test/ipams/netbox/expected.add_ip_notgateway | 2 +-
 src/test/ipams/netbox/expected.add_next_freeip   | 2 +-
 src/test/ipams/netbox/expected.update_ip         | 2 +-
 src/test/run_test_subnets.pl                     | 8 ++++----
 5 files changed, 8 insertions(+), 8 deletions(-)

diff --git a/src/test/ipams/netbox/expected.add_ip b/src/test/ipams/netbox/expected.add_ip
index e9323ca..cd46c53 100644
--- a/src/test/ipams/netbox/expected.add_ip
+++ b/src/test/ipams/netbox/expected.add_ip
@@ -1,5 +1,5 @@
 bless( {
-                  '_content' => '{"address":"10.0.0.1/24","description":"mydescription mac:da:65:8f:18:9b:6f","dns_name":"myhostname"}',
+                  '_content' => '{"address":"10.0.0.1/24","description":"gateway","dns_name":"myhostname"}',
                   '_headers' => bless( {
                                          'authorization' => 'token 0123456789abcdef0123456789abcdef01234567',
                                          'content-type' => 'application/json; charset=UTF-8'
diff --git a/src/test/ipams/netbox/expected.add_ip_notgateway b/src/test/ipams/netbox/expected.add_ip_notgateway
index e9323ca..5ea43cd 100644
--- a/src/test/ipams/netbox/expected.add_ip_notgateway
+++ b/src/test/ipams/netbox/expected.add_ip_notgateway
@@ -1,5 +1,5 @@
 bless( {
-                  '_content' => '{"address":"10.0.0.1/24","description":"mydescription mac:da:65:8f:18:9b:6f","dns_name":"myhostname"}',
+                  '_content' => '{"address":"10.0.0.1/24","description":"mac:da:65:8f:18:9b:6f","dns_name":"myhostname"}',
                   '_headers' => bless( {
                                          'authorization' => 'token 0123456789abcdef0123456789abcdef01234567',
                                          'content-type' => 'application/json; charset=UTF-8'
diff --git a/src/test/ipams/netbox/expected.add_next_freeip b/src/test/ipams/netbox/expected.add_next_freeip
index 86840eb..fbbafbb 100644
--- a/src/test/ipams/netbox/expected.add_next_freeip
+++ b/src/test/ipams/netbox/expected.add_next_freeip
@@ -1,5 +1,5 @@
 bless( {
-                  '_content' => '{"description":"mydescription mac:da:65:8f:18:9b:6f","dns_name":"myhostname"}',
+                  '_content' => '{"description":"mac:da:65:8f:18:9b:6f","dns_name":"myhostname"}',
                   '_headers' => bless( {
                                          'authorization' => 'token 0123456789abcdef0123456789abcdef01234567',
                                          'content-type' => 'application/json; charset=UTF-8'
diff --git a/src/test/ipams/netbox/expected.update_ip b/src/test/ipams/netbox/expected.update_ip
index 5679e8d..d3bc1ad 100644
--- a/src/test/ipams/netbox/expected.update_ip
+++ b/src/test/ipams/netbox/expected.update_ip
@@ -1,5 +1,5 @@
 bless( {
-                  '_content' => '{"address":"10.0.0.1/24","description":"mydescription mac:da:65:8f:18:9b:6f","dns_name":"myhostname"}',
+                  '_content' => '{"address":"10.0.0.1/24","description":"gateway","dns_name":"myhostname"}',
                   '_headers' => bless( {
                                          'authorization' => 'token 0123456789abcdef0123456789abcdef01234567',
                                          'content-type' => 'application/json; charset=UTF-8'
diff --git a/src/test/run_test_subnets.pl b/src/test/run_test_subnets.pl
index c98359a..578b5c1 100755
--- a/src/test/run_test_subnets.pl
+++ b/src/test/run_test_subnets.pl
@@ -176,7 +176,7 @@ foreach my $path (@plugins) {
     $test = "add_second_ip $ip2";
     $name = "$testid $test";
     $result = undef;
-    $expected = '{"zones":{"myzone":{"subnets":{"'.$subnet_cidr.'":{"ips":{"'.$ip.'":{"gateway":1},"'.$ip2.'":{}}}}}}}';
+    $expected = '{"zones":{"myzone":{"subnets":{"'.$subnet_cidr.'":{"ips":{"'.$ip.'":{"gateway":1},"'.$ip2.'":{"hostname":"'.$hostname.'","ip":"'.$ip2.'","mac":"'.$mac.'"}}}}}}}';
 
     eval {
 	PVE::Network::SDN::Subnets::add_ip($zone, $subnetid, $subnet, $ip2, $hostname, $mac, $description);
@@ -195,7 +195,7 @@ foreach my $path (@plugins) {
     $test = "find_next_freeip ($ipnextfree)";
     $name = "$testid $test";
     $result = undef;
-    $expected = '{"zones":{"myzone":{"subnets":{"'.$subnet_cidr.'":{"ips":{"'.$ip.'":{"gateway":1},"'.$ipnextfree.'":{},"'.$ip2.'":{}}}}}}}';
+    $expected = '{"zones":{"myzone":{"subnets":{"'.$subnet_cidr.'":{"ips":{"'.$ip.'":{"gateway":1},"'.$ipnextfree.'":{},"'.$ip2.'":{"hostname":"'.$hostname.'","ip":"'.$ip2.'","mac":"'.$mac.'"}}}}}}}';
 
     eval {
 	$ip3 = PVE::Network::SDN::Subnets::add_next_free_ip($zone, $subnetid, $subnet, $hostname, $mac, $description);
@@ -212,7 +212,7 @@ foreach my $path (@plugins) {
     $test = "del_ip $ip";
     $name = "$testid $test";
     $result = undef;
-    $expected = '{"zones":{"myzone":{"subnets":{"'.$subnet_cidr.'":{"ips":{"'.$ipnextfree.'":{},"'.$ip2.'":{}}}}}}}';
+    $expected = '{"zones":{"myzone":{"subnets":{"'.$subnet_cidr.'":{"ips":{"'.$ipnextfree.'":{},"'.$ip2.'":{"hostname":"'.$hostname.'","ip":"'.$ip2.'","mac":"'.$mac.'"}}}}}}}';
 
     eval {
 	PVE::Network::SDN::Subnets::del_ip($zone, $subnetid, $subnet, $ip, $hostname);
@@ -264,7 +264,7 @@ foreach my $path (@plugins) {
 
     $name = "$testid $test";
     $result = undef;
-    $expected = '{"zones":{"myzone":{"subnets":{"'.$subnet_cidr.'":{"ips":{"'.$ipnextfree.'":{},"'.$ip2.'":{}}}}}}}';
+    $expected = '{"zones":{"myzone":{"subnets":{"'.$subnet_cidr.'":{"ips":{"'.$ipnextfree.'":{},"'.$ip2.'":{"hostname":"'.$hostname.'","ip":"'.$ip2.'","mac":"'.$mac.'"}}}}}}}';
 
     eval {
 	PVE::Network::SDN::Subnets::add_ip($zone, $subnetid, $subnet, $ip, $hostname, $mac, $description);
-- 
2.39.2




^ permalink raw reply	[flat|nested] 66+ messages in thread

* [pve-devel] [PATCH v4 pve-network 16/33] add add_dhcp_mapping
  2023-11-17 11:39 [pve-devel] [PATCH v4 cluster/network/manager/qemu-server/container/docs 00/33] Add support for DHCP servers to SDN Stefan Hanreich
                   ` (14 preceding siblings ...)
  2023-11-17 11:39 ` [pve-devel] [PATCH v4 pve-network 15/33] sdn: fix subnets && netbox ipam tests Stefan Hanreich
@ 2023-11-17 11:39 ` Stefan Hanreich
  2023-11-17 11:39 ` [pve-devel] [PATCH v4 pve-manager 17/33] sdn: regenerate DHCP config on reload Stefan Hanreich
                   ` (19 subsequent siblings)
  35 siblings, 0 replies; 66+ messages in thread
From: Stefan Hanreich @ 2023-11-17 11:39 UTC (permalink / raw)
  To: pve-devel

From: Alexandre Derumier <aderumier@odiso.com>

Signed-off-by: Alexandre Derumier <aderumier@odiso.com>
---
 src/PVE/Network/SDN/Dhcp.pm         |  9 ++++---
 src/PVE/Network/SDN/Dhcp/Dnsmasq.pm | 40 ++++++++++++++++++++++++-----
 src/PVE/Network/SDN/Dhcp/Plugin.pm  |  2 +-
 src/PVE/Network/SDN/Vnets.pm        | 15 +++++++++++
 4 files changed, 56 insertions(+), 10 deletions(-)

diff --git a/src/PVE/Network/SDN/Dhcp.pm b/src/PVE/Network/SDN/Dhcp.pm
index a05b441..fc33f08 100644
--- a/src/PVE/Network/SDN/Dhcp.pm
+++ b/src/PVE/Network/SDN/Dhcp.pm
@@ -8,6 +8,7 @@ use PVE::Cluster qw(cfs_read_file);
 use PVE::Network::SDN;
 use PVE::Network::SDN::SubnetPlugin;
 use PVE::Network::SDN::Dhcp qw(config);
+use PVE::Network::SDN::Ipams;
 use PVE::Network::SDN::Subnets qw(sdn_subnets_config config get_dhcp_ranges);
 use PVE::Network::SDN::Dhcp::Plugin;
 use PVE::Network::SDN::Dhcp::Dnsmasq;
@@ -30,9 +31,11 @@ sub add_mapping {
 
     return if !$zone->{ipam} || !$zone->{dhcp};
 
-    my $dhcp_plugin = PVE::Network::SDN::Dhcp::Plugin->lookup($zone->{dhcp});
-    $dhcp_plugin->add_ip_mapping($zoneid, $mac, $ip4) if $ip4;
-    $dhcp_plugin->add_ip_mapping($zoneid, $mac, $ip6) if $ip6;
+    my $dhcptype = $zone->{dhcp};
+
+    my $macdb = PVE::Network::SDN::Ipams::read_macdb();
+    my $dhcp_plugin = PVE::Network::SDN::Dhcp::Plugin->lookup($dhcptype);
+    $dhcp_plugin->add_ip_mapping($zoneid, $macdb, $mac, $ip4, $ip6)
 }
 
 sub remove_mapping {
diff --git a/src/PVE/Network/SDN/Dhcp/Dnsmasq.pm b/src/PVE/Network/SDN/Dhcp/Dnsmasq.pm
index 21a6ddd..c4b6bde 100644
--- a/src/PVE/Network/SDN/Dhcp/Dnsmasq.pm
+++ b/src/PVE/Network/SDN/Dhcp/Dnsmasq.pm
@@ -53,21 +53,49 @@ sub del_ip_mapping {
 }
 
 sub add_ip_mapping {
-    my ($class, $dhcpid, $mac, $ip) = @_;
+    my ($class, $dhcpid, $macdb, $mac, $ip4, $ip6) = @_;
 
     my $ethers_file = "$DNSMASQ_CONFIG_ROOT/$dhcpid/ethers";
     my $ethers_tmp_file = "$ethers_file.tmp";
 
+    my $change = undef;
+    my $match4 = undef;
+    my $match6 = undef;
+
     my $appendFn = sub {
 	open(my $in, '<', $ethers_file) or die "Could not open file '$ethers_file' $!\n";
 	open(my $out, '>', $ethers_tmp_file) or die "Could not open file '$ethers_tmp_file' $!\n";
 
         while (my $line = <$in>) {
-	    next if $line =~ m/^$mac/;
-	    print $out $line;
+	    chomp($line);
+	    my ($parsed_mac, $parsed_ip) = split(/,/, $line);
+	    #delete removed mac
+	    if (!defined($macdb->{macs}->{$parsed_mac})) {
+		$change = 1;
+		next;
+	    }
+
+	    #delete changed ip
+	    my $ipversion = Net::IP::ip_is_ipv4($parsed_ip) ? "ip4" : "ip6";
+	    if ($macdb->{macs}->{$parsed_mac}->{$ipversion} && $macdb->{macs}->{$parsed_mac}->{$ipversion} ne $parsed_ip) {
+		$change = 1;
+		next;
+	    }
+	    print $out "$parsed_mac,$parsed_ip\n";
+	    #check if mac/ip already exist
+	    $match4 = 1 if $parsed_mac eq $mac && $macdb->{macs}->{$mac}->{'ip4'} && $macdb->{macs}->{$mac}->{'ip4'} eq $ip4;
+	    $match6 = 1 if $parsed_mac eq $mac && $macdb->{macs}->{$mac}->{'ip6'} && $macdb->{macs}->{$mac}->{'ip6'} eq $ip6;
 	}
 
-	print $out "$mac,$ip\n";
+	if(!$match4 && $ip4) {
+	    print $out "$mac,$ip4\n";
+	    $change = 1;
+	}
+
+	if(!$match6 && $ip6) {
+	    print $out "$mac,$ip6\n";
+	    $change = 1;
+	}
 	close $in;
 	close $out;
 	move $ethers_tmp_file, $ethers_file;
@@ -77,12 +105,12 @@ sub add_ip_mapping {
     PVE::Tools::lock_file($ethers_file, 10, $appendFn);
 
     if ($@) {
-	warn "Unable to add $mac/$ip to the dnsmasq configuration: $@\n";
+	warn "Unable to add $mac to the dnsmasq configuration: $@\n";
 	return;
     }
 
     my $service_name = "dnsmasq\@$dhcpid";
-    PVE::Tools::run_command(['systemctl', 'reload', $service_name]);
+    PVE::Tools::run_command(['systemctl', 'reload', $service_name]) if $change;
 }
 
 sub configure_subnet {
diff --git a/src/PVE/Network/SDN/Dhcp/Plugin.pm b/src/PVE/Network/SDN/Dhcp/Plugin.pm
index 7b9e9b7..8d0f7ba 100644
--- a/src/PVE/Network/SDN/Dhcp/Plugin.pm
+++ b/src/PVE/Network/SDN/Dhcp/Plugin.pm
@@ -23,7 +23,7 @@ sub private {
 }
 
 sub add_ip_mapping {
-    my ($class, $dhcp_config, $mac, $ip) = @_;
+    my ($class, $dhcpid, $macdb, $mac, $ip4, $ip6) = @_;
     die 'implement in sub class';
 }
 
diff --git a/src/PVE/Network/SDN/Vnets.pm b/src/PVE/Network/SDN/Vnets.pm
index 4b3276e..09378ff 100644
--- a/src/PVE/Network/SDN/Vnets.pm
+++ b/src/PVE/Network/SDN/Vnets.pm
@@ -7,6 +7,7 @@ use Net::IP;
 
 use PVE::Cluster qw(cfs_read_file cfs_write_file cfs_lock_file);
 use PVE::Network::SDN;
+use PVE::Network::SDN::Dhcp;
 use PVE::Network::SDN::Subnets;
 use PVE::Network::SDN::Zones;
 
@@ -184,4 +185,18 @@ sub del_ips_from_mac {
     return ($ip4, $ip6);
 }
 
+sub add_dhcp_mapping {
+    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};
+
+    my ($ip4,$ip6) = PVE::Network::SDN::Vnets::get_ips_from_mac($vnetid, $mac);
+    PVE::Network::SDN::Dhcp::add_mapping($vnetid, $mac, $ip4, $ip6) if $ip4 || $ip6;
+}
+
 1;
-- 
2.39.2




^ permalink raw reply	[flat|nested] 66+ messages in thread

* [pve-devel] [PATCH v4 pve-manager 17/33] sdn: regenerate DHCP config on reload
  2023-11-17 11:39 [pve-devel] [PATCH v4 cluster/network/manager/qemu-server/container/docs 00/33] Add support for DHCP servers to SDN Stefan Hanreich
                   ` (15 preceding siblings ...)
  2023-11-17 11:39 ` [pve-devel] [PATCH v4 pve-network 16/33] add add_dhcp_mapping Stefan Hanreich
@ 2023-11-17 11:39 ` Stefan Hanreich
  2023-11-21 21:15   ` [pve-devel] applied: " Thomas Lamprecht
  2023-11-17 11:39 ` [pve-devel] [PATCH v4 pve-manager 18/33] sdn: add DHCP option to Zone dialogue Stefan Hanreich
                   ` (18 subsequent siblings)
  35 siblings, 1 reply; 66+ messages in thread
From: Stefan Hanreich @ 2023-11-17 11:39 UTC (permalink / raw)
  To: pve-devel

Signed-off-by: Stefan Hanreich <s.hanreich@proxmox.com>
---
 PVE/API2/Network.pm | 1 +
 1 file changed, 1 insertion(+)

diff --git a/PVE/API2/Network.pm b/PVE/API2/Network.pm
index 00d964a79..f39f04f52 100644
--- a/PVE/API2/Network.pm
+++ b/PVE/API2/Network.pm
@@ -660,6 +660,7 @@ __PACKAGE__->register_method({
 
 	    if ($have_sdn) {
 		PVE::Network::SDN::generate_zone_config();
+		PVE::Network::SDN::generate_dhcp_config();
 	    }
 
 	    my $err = sub {
-- 
2.39.2




^ permalink raw reply	[flat|nested] 66+ messages in thread

* [pve-devel] [PATCH v4 pve-manager 18/33] sdn: add DHCP option to Zone dialogue
  2023-11-17 11:39 [pve-devel] [PATCH v4 cluster/network/manager/qemu-server/container/docs 00/33] Add support for DHCP servers to SDN Stefan Hanreich
                   ` (16 preceding siblings ...)
  2023-11-17 11:39 ` [pve-devel] [PATCH v4 pve-manager 17/33] sdn: regenerate DHCP config on reload Stefan Hanreich
@ 2023-11-17 11:39 ` Stefan Hanreich
  2023-11-17 11:39 ` [pve-devel] [PATCH v4 pve-manager 19/33] sdn: subnet: add panel for editing dhcp ranges Stefan Hanreich
                   ` (17 subsequent siblings)
  35 siblings, 0 replies; 66+ messages in thread
From: Stefan Hanreich @ 2023-11-17 11:39 UTC (permalink / raw)
  To: pve-devel

Co-Authored-by: Stefan Lendl <s.lendl@proxmox.com>
Signed-off-by: Stefan Hanreich <s.hanreich@proxmox.com>
---
 www/manager6/sdn/zones/Base.js       |  6 ++++--
 www/manager6/sdn/zones/SimpleEdit.js | 10 ++++++++++
 2 files changed, 14 insertions(+), 2 deletions(-)

diff --git a/www/manager6/sdn/zones/Base.js b/www/manager6/sdn/zones/Base.js
index 602e4c16b..db9b47b18 100644
--- a/www/manager6/sdn/zones/Base.js
+++ b/www/manager6/sdn/zones/Base.js
@@ -55,7 +55,9 @@ Ext.define('PVE.panel.SDNZoneBase', {
 	    },
 	);
 
-	me.advancedItems = [
+	me.advancedItems = me.advancedItems ?? [];
+
+	me.advancedItems.unshift(
 	    {
 		xtype: 'pveSDNDnsSelector',
 		fieldLabel: gettext('DNS Server'),
@@ -77,7 +79,7 @@ Ext.define('PVE.panel.SDNZoneBase', {
 		fieldLabel: gettext('DNS Zone'),
 		allowBlank: true,
 	    },
-	];
+	);
 
 	me.callParent();
     },
diff --git a/www/manager6/sdn/zones/SimpleEdit.js b/www/manager6/sdn/zones/SimpleEdit.js
index cb7c34035..7a6f1d0d9 100644
--- a/www/manager6/sdn/zones/SimpleEdit.js
+++ b/www/manager6/sdn/zones/SimpleEdit.js
@@ -19,6 +19,16 @@ Ext.define('PVE.sdn.zones.SimpleInputPanel', {
 	var me = this;
 
         me.items = [];
+	me.advancedItems = [
+	    {
+		xtype: 'proxmoxcheckbox',
+		name: 'dhcp',
+		inputValue: 'dnsmasq',
+		uncheckedValue: undefined,
+		checked: false,
+		fieldLabel: gettext('automatic DHCP'),
+	    },
+	];
 
 	me.callParent();
     },
-- 
2.39.2




^ permalink raw reply	[flat|nested] 66+ messages in thread

* [pve-devel] [PATCH v4 pve-manager 19/33] sdn: subnet: add panel for editing dhcp ranges
  2023-11-17 11:39 [pve-devel] [PATCH v4 cluster/network/manager/qemu-server/container/docs 00/33] Add support for DHCP servers to SDN Stefan Hanreich
                   ` (17 preceding siblings ...)
  2023-11-17 11:39 ` [pve-devel] [PATCH v4 pve-manager 18/33] sdn: add DHCP option to Zone dialogue Stefan Hanreich
@ 2023-11-17 11:39 ` Stefan Hanreich
  2023-11-20 13:20   ` Dominik Csapak
  2023-11-17 11:39 ` [pve-devel] [PATCH v4 pve-manager 20/33] sdn: ipam: add ipam panel Stefan Hanreich
                   ` (16 subsequent siblings)
  35 siblings, 1 reply; 66+ messages in thread
From: Stefan Hanreich @ 2023-11-17 11:39 UTC (permalink / raw)
  To: pve-devel

Signed-off-by: Stefan Hanreich <s.hanreich@proxmox.com>
---
 www/manager6/Makefile          |   1 +
 www/manager6/sdn/SubnetEdit.js | 160 ++++++++++++++++++++++++++++++++-
 2 files changed, 160 insertions(+), 1 deletion(-)

diff --git a/www/manager6/Makefile b/www/manager6/Makefile
index dccd2ba1c..093452cd7 100644
--- a/www/manager6/Makefile
+++ b/www/manager6/Makefile
@@ -274,6 +274,7 @@ JSSRC= 							\
 	sdn/ZoneContentView.js				\
 	sdn/ZoneContentPanel.js				\
 	sdn/ZoneView.js					\
+	sdn/IpamEdit.js					\
 	sdn/OptionsPanel.js				\
 	sdn/controllers/Base.js				\
 	sdn/controllers/EvpnEdit.js			\
diff --git a/www/manager6/sdn/SubnetEdit.js b/www/manager6/sdn/SubnetEdit.js
index b9825d2a3..4fe16ab92 100644
--- a/www/manager6/sdn/SubnetEdit.js
+++ b/www/manager6/sdn/SubnetEdit.js
@@ -56,6 +56,147 @@ Ext.define('PVE.sdn.SubnetInputPanel', {
     ],
 });
 
+Ext.define('PVE.sdn.SubnetDhcpRangePanel', {
+    extend: 'Ext.form.FieldContainer',
+    mixins: ['Ext.form.field.Field'],
+
+    initComponent: function() {
+	let me = this;
+
+	me.callParent();
+	me.initField();
+    },
+
+    getValue: function() {
+	let me = this;
+	let store = me.lookup('grid').getStore();
+
+	let data = [];
+
+	store.getData()
+	    .each((item) =>
+		data.push(`start-address=${item.data['start-address']},end-address=${item.data['end-address']}`),
+	    );
+
+	return data;
+    },
+
+    getSubmitData: function() {
+	let me = this;
+
+	let data = {};
+	let value = me.getValue();
+
+	if (value.length) {
+	    data[me.getName()] = value;
+	}
+
+	return data;
+    },
+
+    setValue: function(dhcpRanges) {
+	let me = this;
+	let store = me.lookup('grid').getStore();
+	store.setData(dhcpRanges);
+    },
+
+    getErrors: function() {
+	let me = this;
+        let errors = [];
+
+	return errors;
+    },
+
+    controller: {
+	xclass: 'Ext.app.ViewController',
+
+	addRange: function() {
+	    let me = this;
+	    me.lookup('grid').getStore().add({});
+	},
+
+	removeRange: function(field) {
+	    let me = this;
+	    let record = field.getWidgetRecord();
+
+	    me.lookup('grid').getStore().remove(record);
+	},
+
+	onValueChange: function(field, value) {
+	    let me = this;
+	    let record = field.getWidgetRecord();
+	    let column = field.getWidgetColumn();
+
+	    record.set(column.dataIndex, value);
+	    record.commit();
+	},
+
+	control: {
+	    'grid button': {
+		click: 'removeRange',
+	    },
+	    'field': {
+		change: 'onValueChange',
+	    },
+	},
+    },
+
+    items: [
+	{
+	    xtype: 'grid',
+	    reference: 'grid',
+	    scrollable: true,
+	    store: {
+		fields: ['start-address', 'end-address'],
+	    },
+	    columns: [
+		{
+		    text: gettext('Start Address'),
+		    xtype: 'widgetcolumn',
+		    dataIndex: 'start-address',
+		    flex: 1,
+		    widget: {
+			xtype: 'textfield',
+			vtype: 'IP64Address',
+		    },
+		},
+		{
+		    text: gettext('End Address'),
+		    xtype: 'widgetcolumn',
+		    dataIndex: 'end-address',
+		    flex: 1,
+		    widget: {
+			xtype: 'textfield',
+			vtype: 'IP64Address',
+		    },
+		},
+		{
+		    xtype: 'widgetcolumn',
+		    width: 40,
+		    widget: {
+			xtype: 'button',
+			iconCls: 'fa fa-trash-o',
+		    },
+		},
+	    ],
+	},
+	{
+	    xtype: 'container',
+	    layout: {
+		type: 'hbox',
+	    },
+	    items: [
+		{
+		    xtype: 'button',
+		    text: gettext('Add'),
+		    iconCls: 'fa fa-plus-circle',
+		    handler: 'addRange',
+		},
+	    ],
+	},
+    ],
+});
+
 Ext.define('PVE.sdn.SubnetEdit', {
     extend: 'Proxmox.window.Edit',
 
@@ -67,6 +208,8 @@ Ext.define('PVE.sdn.SubnetEdit', {
 
     base_url: undefined,
 
+    bodyPadding: 0,
+
     initComponent: function() {
 	var me = this;
 
@@ -82,11 +225,22 @@ Ext.define('PVE.sdn.SubnetEdit', {
 
 	let ipanel = Ext.create('PVE.sdn.SubnetInputPanel', {
 	    isCreate: me.isCreate,
+	    title: gettext('General'),
+	});
+
+	let dhcpPanel = Ext.create('PVE.sdn.SubnetDhcpRangePanel', {
+	    isCreate: me.isCreate,
+	    title: gettext('DHCP Ranges'),
+	    name: 'dhcp-range',
 	});
 
 	Ext.apply(me, {
 	    items: [
-		ipanel,
+		{
+		    xtype: 'tabpanel',
+		    bodyPadding: 10,
+		    items: [ipanel, dhcpPanel],
+		},
 	    ],
 	});
 
@@ -97,6 +251,10 @@ Ext.define('PVE.sdn.SubnetEdit', {
 		success: function(response, options) {
 		    let values = response.result.data;
 		    ipanel.setValues(values);
+
+		    if (values['dhcp-range']) {
+			dhcpPanel.setValue(values['dhcp-range']);
+		    }
 		},
 	    });
 	}
-- 
2.39.2




^ permalink raw reply	[flat|nested] 66+ messages in thread

* [pve-devel] [PATCH v4 pve-manager 20/33] sdn: ipam: add ipam panel
  2023-11-17 11:39 [pve-devel] [PATCH v4 cluster/network/manager/qemu-server/container/docs 00/33] Add support for DHCP servers to SDN Stefan Hanreich
                   ` (18 preceding siblings ...)
  2023-11-17 11:39 ` [pve-devel] [PATCH v4 pve-manager 19/33] sdn: subnet: add panel for editing dhcp ranges Stefan Hanreich
@ 2023-11-17 11:39 ` Stefan Hanreich
  2023-11-17 15:04   ` DERUMIER, Alexandre
  2023-11-20 13:44   ` Dominik Csapak
  2023-11-17 11:39 ` [pve-devel] [PATCH v4 qemu-server 21/33] vmnic add|remove : add|del ip in ipam Stefan Hanreich
                   ` (15 subsequent siblings)
  35 siblings, 2 replies; 66+ messages in thread
From: Stefan Hanreich @ 2023-11-17 11:39 UTC (permalink / raw)
  To: pve-devel

Signed-off-by: Stefan Hanreich <s.hanreich@proxmox.com>
---
 www/css/ext6-pve.css          |  22 ++-
 www/manager6/Makefile         |   1 +
 www/manager6/dc/Config.js     |  12 +-
 www/manager6/sdn/IpamEdit.js  |  78 ++++++++++
 www/manager6/tree/DhcpTree.js | 267 ++++++++++++++++++++++++++++++++++
 5 files changed, 372 insertions(+), 8 deletions(-)
 create mode 100644 www/manager6/sdn/IpamEdit.js
 create mode 100644 www/manager6/tree/DhcpTree.js

diff --git a/www/css/ext6-pve.css b/www/css/ext6-pve.css
index e18b173f5..091855356 100644
--- a/www/css/ext6-pve.css
+++ b/www/css/ext6-pve.css
@@ -510,28 +510,38 @@ div.right-aligned {
     content: ' ';
 }
 
-.fa-sdn:before {
+.x-fa-sdn-treelist:before {
     width: 14px;
     height: 14px;
     position: absolute;
     left: 1px;
     top: 4px;
+}
+
+.fa-sdn:before {
     background-image:url(../images/icon-sdn.svg);
     background-size: 14px 14px;
     content: ' ';
 }
 
 .fa-network-wired:before {
-    width: 14px;
-    height: 14px;
-    position: absolute;
-    left: 1px;
-    top: 4px;
     background-image:url(../images/icon-fa-network-wired.svg);
     background-size: 14px 14px;
     content: ' ';
 }
 
+.x-fa-treepanel:before {
+    width: 16px;
+    height: 24px;
+    display: block;
+    background-repeat: no-repeat;
+    background-position: center;
+}
+
+.x-tree-icon-none {
+    display: none;
+}
+
 .x-treelist-row-over > * > .x-treelist-item-icon,
 .x-treelist-row-over > * > .x-treelist-item-text{
     color: #000;
diff --git a/www/manager6/Makefile b/www/manager6/Makefile
index 093452cd7..93b4ff155 100644
--- a/www/manager6/Makefile
+++ b/www/manager6/Makefile
@@ -108,6 +108,7 @@ JSSRC= 							\
 	tree/ResourceTree.js				\
 	tree/SnapshotTree.js				\
 	tree/ResourceMapTree.js				\
+	tree/DhcpTree.js				\
 	window/Backup.js				\
 	window/BackupConfig.js				\
 	window/BulkAction.js				\
diff --git a/www/manager6/dc/Config.js b/www/manager6/dc/Config.js
index 7d01da5fb..7c2b7b168 100644
--- a/www/manager6/dc/Config.js
+++ b/www/manager6/dc/Config.js
@@ -185,7 +185,7 @@ Ext.define('PVE.dc.Config', {
 		me.items.push({
 		    xtype: 'pveSDNStatus',
 		    title: gettext('SDN'),
-		    iconCls: 'fa fa-sdn',
+		    iconCls: 'fa fa-sdn x-fa-sdn-treelist',
 		    hidden: true,
 		    itemId: 'sdn',
 		    expandedOnInit: true,
@@ -203,7 +203,7 @@ Ext.define('PVE.dc.Config', {
 		    groups: ['sdn'],
 		    title: 'VNets',
 		    hidden: true,
-		    iconCls: 'fa fa-network-wired',
+		    iconCls: 'fa fa-network-wired x-fa-sdn-treelist',
 		    itemId: 'sdnvnet',
 		},
 		{
@@ -213,6 +213,14 @@ Ext.define('PVE.dc.Config', {
 		    hidden: true,
 		    iconCls: 'fa fa-gear',
 		    itemId: 'sdnoptions',
+		},
+		{
+		    xtype: 'pveDhcpTree',
+		    groups: ['sdn'],
+		    title: gettext('IPAM'),
+		    hidden: true,
+		    iconCls: 'fa fa-map-signs',
+		    itemId: 'sdnmappings',
 		});
 	    }
 
diff --git a/www/manager6/sdn/IpamEdit.js b/www/manager6/sdn/IpamEdit.js
new file mode 100644
index 000000000..18e22c592
--- /dev/null
+++ b/www/manager6/sdn/IpamEdit.js
@@ -0,0 +1,78 @@
+Ext.define('PVE.sdn.IpamEditInputPanel', {
+    extend: 'Proxmox.panel.InputPanel',
+    mixins: ['Proxmox.Mixin.CBind'],
+
+    isCreate: false,
+
+    onGetValues: function(values) {
+	let me = this;
+
+	if (!values.vmid) {
+	    delete values.vmid;
+	}
+
+	return values;
+    },
+
+    items: [
+	{
+	    xtype: 'pmxDisplayEditField',
+	    name: 'vmid',
+	    fieldLabel: gettext('VMID'),
+	    allowBlank: false,
+	    editable: false,
+	    cbind: {
+		hidden: '{isCreate}',
+	    },
+	},
+	{
+	    xtype: 'pmxDisplayEditField',
+	    name: 'mac',
+	    fieldLabel: gettext('MAC'),
+	    allowBlank: false,
+	    cbind: {
+		editable: '{isCreate}',
+	    },
+	},
+	{
+	    xtype: 'proxmoxtextfield',
+	    name: 'ip',
+	    fieldLabel: gettext('IP'),
+	    allowBlank: false,
+	},
+    ],
+});
+
+Ext.define('PVE.sdn.IpamEdit', {
+    extend: 'Proxmox.window.Edit',
+
+    subject: gettext('DHCP Mapping'),
+    width: 350,
+
+    isCreate: false,
+    mapping: {},
+
+    submitUrl: function(url, values) {
+	return `${url}/${values.zone}/${values.vnet}/${values.mac}`;
+    },
+
+    initComponent: function() {
+	var me = this;
+
+	me.method = me.isCreate ? 'POST' : 'PUT';
+
+	let ipanel = Ext.create('PVE.sdn.IpamEditInputPanel', {
+	    isCreate: me.isCreate,
+	});
+
+	Ext.apply(me, {
+	    items: [
+		ipanel,
+	    ],
+	});
+
+	me.callParent();
+
+	ipanel.setValues(me.mapping);
+    },
+});
diff --git a/www/manager6/tree/DhcpTree.js b/www/manager6/tree/DhcpTree.js
new file mode 100644
index 000000000..ca279c29a
--- /dev/null
+++ b/www/manager6/tree/DhcpTree.js
@@ -0,0 +1,267 @@
+Ext.define('PVE.sdn.DhcpTree', {
+    extend: 'Ext.tree.Panel',
+    xtype: 'pveDhcpTree',
+
+    layout: 'fit',
+    rootVisible: false,
+    animate: false,
+
+    store: {
+	sorters: ['ip', 'name'],
+    },
+
+    controller: {
+	xclass: 'Ext.app.ViewController',
+
+	reload: function() {
+	    let me = this;
+
+	    Proxmox.Utils.API2Request({
+		url: `/cluster/sdn/ipam`,
+		method: 'GET',
+		success: function(response, opts) {
+		    let root = {
+			name: '__root',
+			expanded: true,
+			children: [],
+		    };
+
+		    let zones = {};
+		    let vnets = {};
+		    let subnets = {};
+
+		    response.result.data.forEach((element) => {
+			element.leaf = true;
+
+			if (!(element.zone in zones)) {
+			    let zone = {
+				name: element.zone,
+				type: 'zone',
+				iconCls: 'fa fa-th',
+				expanded: true,
+				children: [],
+			    };
+
+			    zones[element.zone] = zone;
+			    root.children.push(zone);
+			}
+
+			if (!(element.vnet in vnets)) {
+			    let vnet = {
+				name: element.vnet,
+				zone: element.zone,
+				type: 'vnet',
+				iconCls: 'fa fa-network-wired x-fa-treepanel',
+				expanded: true,
+				children: [],
+			    };
+
+			    vnets[element.vnet] = vnet;
+			    zones[element.zone].children.push(vnet);
+			}
+
+			if (!(element.subnet in subnets)) {
+			    let subnet = {
+				name: element.subnet,
+				zone: element.zone,
+				vnet: element.vnet,
+				type: 'subnet',
+				iconCls: 'x-tree-icon-none',
+				expanded: true,
+				children: [],
+			    };
+
+			    subnets[element.subnet] = subnet;
+			    vnets[element.vnet].children.push(subnet);
+			}
+
+			element.type = 'mapping';
+			element.iconCls = 'x-tree-icon-none';
+			subnets[element.subnet].children.push(element);
+		    });
+
+		    me.getView().setRootNode(root);
+		},
+	    });
+	},
+
+	init: function(view) {
+	    let me = this;
+	    me.reload();
+	},
+
+	onDelete: function(table, rI, cI, item, e, { data }) {
+	    let me = this;
+	    let view = me.getView();
+
+	    Ext.Msg.show({
+		title: gettext('Confirm'),
+		icon: Ext.Msg.WARNING,
+		message: Ext.String.format(gettext('Are you sure you want to remove DHCP mapping {0}'), `${data.mac} / ${data.ip}`),
+		buttons: Ext.Msg.YESNO,
+		defaultFocus: 'no',
+		callback: function(btn) {
+		    if (btn !== 'yes') {
+		        return;
+		    }
+
+		    Proxmox.Utils.API2Request({
+			url: `/cluster/sdn/ipam/${data.zone}/${data.vnet}/${data.mac}`,
+			method: 'DELETE',
+			waitMsgTarget: view,
+			failure: function(response, opts) {
+			    Ext.Msg.alert(gettext('Error'), response.htmlStatus);
+			},
+			callback: me.reload.bind(me),
+		    });
+		},
+	    });
+	},
+
+	editAction: function(_grid, _rI, _cI, _item, _e, rec) {
+	    this.edit(rec);
+	},
+
+	editDblClick: function() {
+	    let me = this;
+
+	    let view = me.getView();
+	    let selection = view.getSelection();
+
+	    if (!selection || selection.length < 1) {
+		return;
+	    }
+
+	    me.edit(selection[0]);
+	},
+
+	edit: function(rec) {
+	    let me = this;
+
+	    if (rec.data.type === 'mapping' && !rec.data.gateway) {
+		me.openEditWindow(rec.data);
+	    }
+	},
+
+	openEditWindow: function(data) {
+	    let me = this;
+
+	    Ext.create('PVE.sdn.IpamEdit', {
+		autoShow: true,
+		mapping: data,
+		url: `/cluster/sdn/ipam`,
+		extraRequestParams: {
+		    vmid: data.vmid,
+		    mac: data.mac,
+		    zone: data.zone,
+		    vnet: data.vnet,
+		},
+		listeners: {
+		    destroy: () => me.reload(),
+		},
+	    });
+	},
+    },
+
+    listeners: {
+	itemdblclick: 'editDblClick',
+    },
+
+    tbar: [
+	{
+	    xtype: 'proxmoxButton',
+	    text: gettext('Reload'),
+	    handler: 'reload',
+	},
+    ],
+
+    columns: [
+	{
+	    xtype: 'treecolumn',
+	    text: gettext('Name / VMID'),
+	    dataIndex: 'name',
+	    width: 200,
+	    renderer: function(value, meta, record) {
+		if (record.get('gateway')) {
+		    return gettext('Gateway');
+		}
+
+		return record.get('name') ?? record.get('vmid') ?? ' ';
+	    },
+	},
+	{
+	    text: gettext('IP'),
+	    dataIndex: 'ip',
+	    width: 200,
+	},
+	{
+	    text: gettext('MAC'),
+	    dataIndex: 'mac',
+	    width: 200,
+	},
+	{
+	    text: gettext('Gateway'),
+	    dataIndex: 'gateway',
+	    width: 200,
+	},
+	{
+	    header: gettext('Actions'),
+	    xtype: 'actioncolumn',
+	    dataIndex: 'text',
+	    width: 150,
+	    items: [
+		{
+		    handler: function(table, rI, cI, item, e, { data }) {
+			let me = this;
+
+			Ext.create('PVE.sdn.IpamEdit', {
+			    autoShow: true,
+			    mapping: {},
+			    url: `/cluster/sdn/ipam`,
+			    isCreate: true,
+			    extraRequestParams: {
+				vnet: data.name,
+				zone: data.zone,
+			    },
+			    listeners: {
+				destroy: () => {
+				    me.up('pveDhcpTree').controller.reload();
+				},
+			    },
+			});
+		    },
+		    getTip: (v, m, rec) => gettext('Add'),
+		    getClass: (v, m, { data }) => {
+			if (data.type === 'vnet') {
+			    return 'fa fa-plus-square';
+			}
+
+			return 'pmx-hidden';
+		    },
+                },
+		{
+		    handler: 'editAction',
+		    getTip: (v, m, rec) => gettext('Edit'),
+		    getClass: (v, m, { data }) => {
+			if (data.type === 'mapping' && !data.gateway) {
+			    return 'fa fa-pencil fa-fw';
+			}
+
+			return 'pmx-hidden';
+		    },
+                },
+		{
+		    handler: 'onDelete',
+		    getTip: (v, m, rec) => gettext('Delete'),
+		    getClass: (v, m, { data }) => {
+			if (data.type === 'mapping' && !data.gateway) {
+			    return 'fa critical fa-trash-o';
+			}
+
+			return 'pmx-hidden';
+		    },
+                },
+	    ],
+	},
+    ],
+});
-- 
2.39.2




^ permalink raw reply	[flat|nested] 66+ messages in thread

* [pve-devel] [PATCH v4 qemu-server 21/33] vmnic add|remove : add|del ip in ipam
  2023-11-17 11:39 [pve-devel] [PATCH v4 cluster/network/manager/qemu-server/container/docs 00/33] Add support for DHCP servers to SDN Stefan Hanreich
                   ` (19 preceding siblings ...)
  2023-11-17 11:39 ` [pve-devel] [PATCH v4 pve-manager 20/33] sdn: ipam: add ipam panel Stefan Hanreich
@ 2023-11-17 11:39 ` Stefan Hanreich
  2023-11-21 13:53   ` [pve-devel] applied-series: " Wolfgang Bumiller
  2023-11-17 11:40 ` [pve-devel] [PATCH v4 qemu-server 22/33] vm_start : vm-network-scripts: add_dhcp_reservation Stefan Hanreich
                   ` (14 subsequent siblings)
  35 siblings, 1 reply; 66+ messages in thread
From: Stefan Hanreich @ 2023-11-17 11:39 UTC (permalink / raw)
  To: pve-devel

From: Alexandre Derumier <aderumier@odiso.com>

Co-Authored-by: Stefan Lendl <s.lendl@proxmox.com>
Signed-off-by: Stefan Hanreich <s.hanreich@proxmox.com>
Signed-off-by: Alexandre Derumier <aderumier@odiso.com>
---
 PVE/QemuServer.pm | 40 ++++++++++++++++++++++++++++++++++++++++
 1 file changed, 40 insertions(+)

diff --git a/PVE/QemuServer.pm b/PVE/QemuServer.pm
index c465fb6..5f15c66 100644
--- a/PVE/QemuServer.pm
+++ b/PVE/QemuServer.pm
@@ -64,6 +64,7 @@ use PVE::QemuServer::USB;
 my $have_sdn;
 eval {
     require PVE::Network::SDN::Zones;
+    require PVE::Network::SDN::Vnets;
     $have_sdn = 1;
 };
 
@@ -4998,6 +4999,10 @@ sub vmconfig_hotplug_pending {
 	    } elsif ($opt =~ m/^net(\d+)$/) {
 		die "skip\n" if !$hotplug_features->{network};
 		vm_deviceunplug($vmid, $conf, $opt);
+		if($have_sdn) {
+		    my $net = PVE::QemuServer::parse_net($conf->{$opt});
+		    PVE::Network::SDN::Vnets::del_ips_from_mac($net->{bridge}, $net->{macaddr}, $conf->{name});
+		}
 	    } elsif (is_valid_drivename($opt)) {
 		die "skip\n" if !$hotplug_features->{disk} || $opt =~ m/(ide|sata)(\d+)/;
 		vm_deviceunplug($vmid, $conf, $opt);
@@ -5203,6 +5208,12 @@ sub vmconfig_apply_pending {
 		die "internal error";
 	    } elsif (defined($conf->{$opt}) && is_valid_drivename($opt)) {
 		vmconfig_delete_or_detach_drive($vmid, $storecfg, $conf, $opt, $force);
+	    } elsif (defined($conf->{$opt}) && $opt =~ m/^net\d+$/) {
+		if($have_sdn) {
+		    my $net = PVE::QemuServer::parse_net($conf->{$opt});
+		    eval { PVE::Network::SDN::Vnets::del_ips_from_mac($net->{bridge}, $net->{macaddr}, $conf->{name}) };
+		    warn if $@;
+		}
 	    }
 	};
 	if (my $err = $@) {
@@ -5222,6 +5233,20 @@ sub vmconfig_apply_pending {
 	eval {
 	    if (defined($conf->{$opt}) && is_valid_drivename($opt)) {
 		vmconfig_register_unused_drive($storecfg, $vmid, $conf, parse_drive($opt, $conf->{$opt}))
+	    } elsif (defined($conf->{pending}->{$opt}) && $opt =~ m/^net\d+$/) {
+		if($have_sdn) {
+                    my $new_net = PVE::QemuServer::parse_net($conf->{pending}->{$opt});
+		    if ($conf->{$opt}){
+		        my $old_net = PVE::QemuServer::parse_net($conf->{$opt});
+
+			if ($old_net->{bridge} ne $new_net->{bridge} ||
+			    $old_net->{macaddr} ne $new_net->{macaddr}) {
+			    PVE::Network::SDN::Vnets::del_ips_from_mac($old_net->{bridge}, $old_net->{macaddr}, $conf->{name});
+			}
+		   }
+		   #fixme: reuse ip if mac change && same bridge
+		   PVE::Network::SDN::Vnets::add_next_free_cidr($new_net->{bridge}, $conf->{name}, $new_net->{macaddr}, $vmid, undef, 1);
+		}
 	    }
 	};
 	if (my $err = $@) {
@@ -5265,6 +5290,11 @@ sub vmconfig_update_net {
             # for non online change, we try to hot-unplug
 	    die "skip\n" if !$hotplug;
 	    vm_deviceunplug($vmid, $conf, $opt);
+
+	    if($have_sdn) {
+		PVE::Network::SDN::Vnets::del_ips_from_mac($oldnet->{bridge}, $oldnet->{macaddr}, $conf->{name});
+	    }
+
 	} else {
 
 	    die "internal error" if $opt !~ m/net(\d+)/;
@@ -5276,6 +5306,13 @@ sub vmconfig_update_net {
 		safe_num_ne($oldnet->{firewall}, $newnet->{firewall})) {
 		PVE::Network::tap_unplug($iface);
 
+		if (safe_string_ne($oldnet->{bridge}, $newnet->{bridge})) {
+		    if ($have_sdn) {
+			PVE::Network::SDN::Vnets::del_ips_from_mac($oldnet->{bridge}, $oldnet->{macaddr}, $conf->{name});
+			PVE::Network::SDN::Vnets::add_next_free_cidr($newnet->{bridge}, $conf->{name}, $newnet->{macaddr}, $vmid, undef, 1);
+		    }
+		}
+
 		if ($have_sdn) {
 		    PVE::Network::SDN::Zones::tap_plug($iface, $newnet->{bridge}, $newnet->{tag}, $newnet->{firewall}, $newnet->{trunks}, $newnet->{rate});
 		} else {
@@ -5296,6 +5333,9 @@ sub vmconfig_update_net {
     }
 
     if ($hotplug) {
+	if ($have_sdn) {
+	    PVE::Network::SDN::Vnets::add_next_free_cidr($newnet->{bridge}, $conf->{name}, $newnet->{macaddr}, $vmid, undef, 1);
+	}
 	vm_deviceplug($storecfg, $conf, $vmid, $opt, $newnet, $arch, $machine_type);
     } else {
 	die "skip\n";
-- 
2.39.2




^ permalink raw reply	[flat|nested] 66+ messages in thread

* [pve-devel] [PATCH v4 qemu-server 22/33] vm_start : vm-network-scripts: add_dhcp_reservation
  2023-11-17 11:39 [pve-devel] [PATCH v4 cluster/network/manager/qemu-server/container/docs 00/33] Add support for DHCP servers to SDN Stefan Hanreich
                   ` (20 preceding siblings ...)
  2023-11-17 11:39 ` [pve-devel] [PATCH v4 qemu-server 21/33] vmnic add|remove : add|del ip in ipam Stefan Hanreich
@ 2023-11-17 11:40 ` Stefan Hanreich
  2023-11-17 11:40 ` [pve-devel] [PATCH v4 qemu-server 23/33] api2: create|restore|clone: add_free_ip Stefan Hanreich
                   ` (13 subsequent siblings)
  35 siblings, 0 replies; 66+ messages in thread
From: Stefan Hanreich @ 2023-11-17 11:40 UTC (permalink / raw)
  To: pve-devel

From: Alexandre Derumier <aderumier@odiso.com>

Signed-off-by: Stefan Hanreich <s.hanreich@proxmox.com>
Signed-off-by: Alexandre Derumier <aderumier@odiso.com>
---
 vm-network-scripts/pve-bridge | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/vm-network-scripts/pve-bridge b/vm-network-scripts/pve-bridge
index d37ce33..e8f8798 100755
--- a/vm-network-scripts/pve-bridge
+++ b/vm-network-scripts/pve-bridge
@@ -10,6 +10,7 @@ use PVE::Network;
 my $have_sdn;
 eval {
     require PVE::Network::SDN::Zones;
+    require PVE::Network::SDN::Vnets;
     $have_sdn = 1;
 };
 
@@ -44,6 +45,7 @@ my $net = PVE::QemuServer::parse_net($netconf);
 die "unable to parse network config '$netid'\n" if !$net;
 
 if ($have_sdn) {
+    PVE::Network::SDN::Vnets::add_dhcp_mapping($net->{bridge}, $net->{macaddr});
     PVE::Network::SDN::Zones::tap_create($iface, $net->{bridge});
     PVE::Network::SDN::Zones::tap_plug($iface, $net->{bridge}, $net->{tag}, $net->{firewall}, $net->{trunks}, $net->{rate});
 } else {
-- 
2.39.2




^ permalink raw reply	[flat|nested] 66+ messages in thread

* [pve-devel] [PATCH v4 qemu-server 23/33] api2: create|restore|clone: add_free_ip
  2023-11-17 11:39 [pve-devel] [PATCH v4 cluster/network/manager/qemu-server/container/docs 00/33] Add support for DHCP servers to SDN Stefan Hanreich
                   ` (21 preceding siblings ...)
  2023-11-17 11:40 ` [pve-devel] [PATCH v4 qemu-server 22/33] vm_start : vm-network-scripts: add_dhcp_reservation Stefan Hanreich
@ 2023-11-17 11:40 ` Stefan Hanreich
  2023-11-17 11:40 ` [pve-devel] [PATCH v4 qemu-server 24/33] vm_destroy: delete ip from ipam Stefan Hanreich
                   ` (12 subsequent siblings)
  35 siblings, 0 replies; 66+ messages in thread
From: Stefan Hanreich @ 2023-11-17 11:40 UTC (permalink / raw)
  To: pve-devel

From: Alexandre Derumier <aderumier@odiso.com>

Co-Authored-by: Stefan Lendl <s.lendl@proxmox.com>
Signed-off-by: Stefan Hanreich <s.hanreich@proxmox.com>
Signed-off-by: Alexandre Derumier <aderumier@odiso.com>
---
 PVE/API2/Qemu.pm  |  6 ++++++
 PVE/QemuServer.pm | 15 +++++++++++++++
 2 files changed, 21 insertions(+)

diff --git a/PVE/API2/Qemu.pm b/PVE/API2/Qemu.pm
index 38bdaab..7ae2f58 100644
--- a/PVE/API2/Qemu.pm
+++ b/PVE/API2/Qemu.pm
@@ -991,6 +991,8 @@ __PACKAGE__->register_method({
 		    eval { PVE::QemuServer::template_create($vmid, $restored_conf) };
 		    warn $@ if $@;
 		}
+
+		PVE::QemuServer::create_ifaces_ipams_ips($restored_conf, $vmid) if $unique;
 	    };
 
 	    # ensure no old replication state are exists
@@ -1066,6 +1068,8 @@ __PACKAGE__->register_method({
 		}
 
 		PVE::AccessControl::add_vm_to_pool($vmid, $pool) if $pool;
+
+		PVE::QemuServer::create_ifaces_ipams_ips($conf, $vmid);
 	    };
 
 	    PVE::QemuConfig->lock_config_full($vmid, 1, $realcmd);
@@ -3763,6 +3767,8 @@ __PACKAGE__->register_method({
 
 		PVE::QemuConfig->write_config($newid, $newconf);
 
+		PVE::QemuServer::create_ifaces_ipams_ips($newconf, $newid);
+
 		if ($target) {
 		    # always deactivate volumes - avoid lvm LVs to be active on several nodes
 		    PVE::Storage::deactivate_volumes($storecfg, $vollist, $snapname) if !$running;
diff --git a/PVE/QemuServer.pm b/PVE/QemuServer.pm
index 5f15c66..b92743c 100644
--- a/PVE/QemuServer.pm
+++ b/PVE/QemuServer.pm
@@ -8628,4 +8628,19 @@ sub del_nets_bridge_fdb {
     }
 }
 
+sub create_ifaces_ipams_ips {
+    my ($conf, $vmid) = @_;
+
+    return if !$have_sdn;
+
+    foreach my $opt (keys %$conf) {
+        if ($opt =~ m/^net(\d+)$/) {
+            my $value = $conf->{$opt};
+            my $net = PVE::QemuServer::parse_net($value);
+            eval { PVE::Network::SDN::Vnets::add_next_free_cidr($net->{bridge}, $conf->{name}, $net->{macaddr}, $vmid, undef, 1) };
+            warn $@ if $@;
+        }
+    }
+}
+
 1;
-- 
2.39.2




^ permalink raw reply	[flat|nested] 66+ messages in thread

* [pve-devel] [PATCH v4 qemu-server 24/33] vm_destroy: delete ip from ipam
  2023-11-17 11:39 [pve-devel] [PATCH v4 cluster/network/manager/qemu-server/container/docs 00/33] Add support for DHCP servers to SDN Stefan Hanreich
                   ` (22 preceding siblings ...)
  2023-11-17 11:40 ` [pve-devel] [PATCH v4 qemu-server 23/33] api2: create|restore|clone: add_free_ip Stefan Hanreich
@ 2023-11-17 11:40 ` Stefan Hanreich
  2023-11-17 11:40 ` [pve-devel] [PATCH v4 qemu-server 25/33] nic hotplug: add_dhcp_mapping Stefan Hanreich
                   ` (11 subsequent siblings)
  35 siblings, 0 replies; 66+ messages in thread
From: Stefan Hanreich @ 2023-11-17 11:40 UTC (permalink / raw)
  To: pve-devel

From: Alexandre Derumier <aderumier@odiso.com>

Co-Authored-By: Stefan Hanreich <s.hanreich@proxmox.com>
Signed-off-by: Stefan Hanreich <s.hanreich@proxmox.com>
Signed-off-by: Alexandre Derumier <aderumier@odiso.com>
---
 PVE/QemuServer.pm | 17 +++++++++++++++++
 1 file changed, 17 insertions(+)

diff --git a/PVE/QemuServer.pm b/PVE/QemuServer.pm
index b92743c..b4cb741 100644
--- a/PVE/QemuServer.pm
+++ b/PVE/QemuServer.pm
@@ -2341,6 +2341,9 @@ sub destroy_vm {
 	});
     }
 
+    eval { delete_ifaces_ipams_ips($conf, $vmid)};
+    warn $@ if $@;
+
     if (defined $replacement_conf) {
 	PVE::QemuConfig->write_config($vmid, $replacement_conf);
     } else {
@@ -8643,4 +8646,18 @@ sub create_ifaces_ipams_ips {
     }
 }
 
+sub delete_ifaces_ipams_ips {
+    my ($conf, $vmid) = @_;
+
+    return if !$have_sdn;
+
+    foreach my $opt (keys %$conf) {
+	if ($opt =~ m/^net(\d+)$/) {
+	    my $net = PVE::QemuServer::parse_net($conf->{$opt});
+	    eval { PVE::Network::SDN::Vnets::del_ips_from_mac($net->{bridge}, $net->{macaddr}, $conf->{name}) };
+	    warn $@ if $@;
+	}
+    }
+}
+
 1;
-- 
2.39.2




^ permalink raw reply	[flat|nested] 66+ messages in thread

* [pve-devel] [PATCH v4 qemu-server 25/33] nic hotplug: add_dhcp_mapping
  2023-11-17 11:39 [pve-devel] [PATCH v4 cluster/network/manager/qemu-server/container/docs 00/33] Add support for DHCP servers to SDN Stefan Hanreich
                   ` (23 preceding siblings ...)
  2023-11-17 11:40 ` [pve-devel] [PATCH v4 qemu-server 24/33] vm_destroy: delete ip from ipam Stefan Hanreich
@ 2023-11-17 11:40 ` Stefan Hanreich
  2023-11-17 11:40 ` [pve-devel] [PATCH v4 qemu-server 26/33] nic online bridge/vlan change: link disconnect/reconnect Stefan Hanreich
                   ` (10 subsequent siblings)
  35 siblings, 0 replies; 66+ messages in thread
From: Stefan Hanreich @ 2023-11-17 11:40 UTC (permalink / raw)
  To: pve-devel

From: Alexandre Derumier <aderumier@odiso.com>

Signed-off-by: Alexandre Derumier <aderumier@odiso.com>
---
 PVE/QemuServer.pm | 1 +
 1 file changed, 1 insertion(+)

diff --git a/PVE/QemuServer.pm b/PVE/QemuServer.pm
index b4cb741..5e158b3 100644
--- a/PVE/QemuServer.pm
+++ b/PVE/QemuServer.pm
@@ -5339,6 +5339,7 @@ sub vmconfig_update_net {
 	if ($have_sdn) {
 	    PVE::Network::SDN::Vnets::add_next_free_cidr($newnet->{bridge}, $conf->{name}, $newnet->{macaddr}, $vmid, undef, 1);
 	}
+	PVE::Network::SDN::Vnets::add_dhcp_mapping($newnet->{bridge}, $newnet->{macaddr});
 	vm_deviceplug($storecfg, $conf, $vmid, $opt, $newnet, $arch, $machine_type);
     } else {
 	die "skip\n";
-- 
2.39.2




^ permalink raw reply	[flat|nested] 66+ messages in thread

* [pve-devel] [PATCH v4 qemu-server 26/33] nic online bridge/vlan change: link disconnect/reconnect
  2023-11-17 11:39 [pve-devel] [PATCH v4 cluster/network/manager/qemu-server/container/docs 00/33] Add support for DHCP servers to SDN Stefan Hanreich
                   ` (24 preceding siblings ...)
  2023-11-17 11:40 ` [pve-devel] [PATCH v4 qemu-server 25/33] nic hotplug: add_dhcp_mapping Stefan Hanreich
@ 2023-11-17 11:40 ` Stefan Hanreich
  2023-11-17 11:40 ` [pve-devel] [PATCH v4 pve-container 27/33] nic hotplug : add|del ips in ipam Stefan Hanreich
                   ` (9 subsequent siblings)
  35 siblings, 0 replies; 66+ messages in thread
From: Stefan Hanreich @ 2023-11-17 11:40 UTC (permalink / raw)
  To: pve-devel

From: Alexandre Derumier <aderumier@odiso.com>

We want to notify guest of the change, so it can resubmit dhcp request,
or send gratuitous arp,...

Signed-off-by: Alexandre Derumier <aderumier@odiso.com>
---
 PVE/QemuServer.pm | 13 +++++++++++++
 1 file changed, 13 insertions(+)

diff --git a/PVE/QemuServer.pm b/PVE/QemuServer.pm
index 5e158b3..e87df9d 100644
--- a/PVE/QemuServer.pm
+++ b/PVE/QemuServer.pm
@@ -5309,6 +5309,12 @@ sub vmconfig_update_net {
 		safe_num_ne($oldnet->{firewall}, $newnet->{firewall})) {
 		PVE::Network::tap_unplug($iface);
 
+		#set link_down in guest if bridge or vlan change to notify guest (dhcp renew for example)
+		if (safe_string_ne($oldnet->{bridge}, $newnet->{bridge}) ||
+		    safe_num_ne($oldnet->{tag}, $newnet->{tag})) {
+		    qemu_set_link_status($vmid, $opt, 0);
+		}
+
 		if (safe_string_ne($oldnet->{bridge}, $newnet->{bridge})) {
 		    if ($have_sdn) {
 			PVE::Network::SDN::Vnets::del_ips_from_mac($oldnet->{bridge}, $oldnet->{macaddr}, $conf->{name});
@@ -5321,6 +5327,13 @@ sub vmconfig_update_net {
 		} else {
 		    PVE::Network::tap_plug($iface, $newnet->{bridge}, $newnet->{tag}, $newnet->{firewall}, $newnet->{trunks}, $newnet->{rate});
 		}
+
+		#set link_up in guest if bridge or vlan change to notify guest (dhcp renew for example)
+		if (safe_string_ne($oldnet->{bridge}, $newnet->{bridge}) ||
+		    safe_num_ne($oldnet->{tag}, $newnet->{tag})) {
+		    qemu_set_link_status($vmid, $opt, 1);
+		}
+
 	    } elsif (safe_num_ne($oldnet->{rate}, $newnet->{rate})) {
 		# Rate can be applied on its own but any change above needs to
 		# include the rate in tap_plug since OVS resets everything.
-- 
2.39.2




^ permalink raw reply	[flat|nested] 66+ messages in thread

* [pve-devel] [PATCH v4 pve-container 27/33] nic hotplug : add|del ips in ipam
  2023-11-17 11:39 [pve-devel] [PATCH v4 cluster/network/manager/qemu-server/container/docs 00/33] Add support for DHCP servers to SDN Stefan Hanreich
                   ` (25 preceding siblings ...)
  2023-11-17 11:40 ` [pve-devel] [PATCH v4 qemu-server 26/33] nic online bridge/vlan change: link disconnect/reconnect Stefan Hanreich
@ 2023-11-17 11:40 ` Stefan Hanreich
  2023-11-21 13:47   ` [pve-devel] applied-series: " Wolfgang Bumiller
  2023-11-17 11:40 ` [pve-devel] [PATCH v4 pve-container 28/33] vm_destroy: remove ips from ipam for all interfaces Stefan Hanreich
                   ` (8 subsequent siblings)
  35 siblings, 1 reply; 66+ messages in thread
From: Stefan Hanreich @ 2023-11-17 11:40 UTC (permalink / raw)
  To: pve-devel

From: Alexandre Derumier <aderumier@odiso.com>

Co-Authored-by: Stefan Hanreich <s.hanreich@proxmox.com>
Signed-off-by: Alexandre Derumier <aderumier@odiso.com>
---
 src/PVE/LXC.pm        | 17 +++++++++++++++++
 src/PVE/LXC/Config.pm | 12 ++++++++++++
 2 files changed, 29 insertions(+)

diff --git a/src/PVE/LXC.pm b/src/PVE/LXC.pm
index 8f53b53..b6df6d6 100644
--- a/src/PVE/LXC.pm
+++ b/src/PVE/LXC.pm
@@ -961,6 +961,12 @@ sub update_net {
 	    safe_string_ne($oldnet->{name}, $newnet->{name})) {
 
 	    PVE::Network::veth_delete($veth);
+
+	    if ($have_sdn) {
+		eval { PVE::Network::SDN::Vnets::del_ips_from_mac($oldnet->{bridge}, $oldnet->{hwaddr}, $conf->{hostname}) };
+		warn $@ if $@;
+	    }
+
 	    delete $conf->{$opt};
 	    PVE::LXC::Config->write_config($vmid, $conf);
 
@@ -974,14 +980,23 @@ sub update_net {
 	    ) {
 
 		if ($oldnet->{bridge}) {
+
 		    PVE::Network::tap_unplug($veth);
 		    foreach (qw(bridge tag firewall)) {
 			delete $oldnet->{$_};
 		    }
 		    $conf->{$opt} = PVE::LXC::Config->print_lxc_network($oldnet);
 		    PVE::LXC::Config->write_config($vmid, $conf);
+
+		    if ($have_sdn) {
+			eval { PVE::Network::SDN::Vnets::del_ips_from_mac($oldnet->{bridge}, $oldnet->{hwaddr}, $conf->{hostname}) };
+			warn $@ if $@;
+		    }
 		}
 
+		if ($have_sdn) {
+		    PVE::Network::SDN::Vnets::add_next_free_cidr($newnet->{bridge}, $conf->{hostname}, $newnet->{hwaddr}, $vmid, undef, 1);
+		}
 		PVE::LXC::net_tap_plug($veth, $newnet);
 
 		# This includes the rate:
@@ -1012,6 +1027,8 @@ sub hotplug_net {
     my $eth = $newnet->{name};
 
     if ($have_sdn) {
+	PVE::Network::SDN::Vnets::add_next_free_cidr($newnet->{bridge}, $conf->{hostname}, $newnet->{hwaddr}, $vmid, undef, 1);
+	PVE::Network::SDN::Vnets::add_dhcp_mapping($newnet->{bridge}, $newnet->{hwaddr});
 	PVE::Network::SDN::Zones::veth_create($veth, $vethpeer, $newnet->{bridge}, $newnet->{hwaddr});
     } else {
 	PVE::Network::veth_create($veth, $vethpeer, $newnet->{bridge}, $newnet->{hwaddr});
diff --git a/src/PVE/LXC/Config.pm b/src/PVE/LXC/Config.pm
index 56e1f10..c884313 100644
--- a/src/PVE/LXC/Config.pm
+++ b/src/PVE/LXC/Config.pm
@@ -22,6 +22,12 @@ use constant {
     FITHAW   => 0xc0045878,
 };
 
+my $have_sdn;
+eval {
+    require PVE::Network::SDN::Vnets;
+    $have_sdn = 1;
+};
+
 my $nodename = PVE::INotify::nodename();
 my $lock_handles =  {};
 my $lockdir = "/run/lock/lxc";
@@ -1383,6 +1389,12 @@ sub vmconfig_hotplug_pending {
 	    } elsif ($opt =~ m/^net(\d)$/) {
 		my $netid = $1;
 		PVE::Network::veth_delete("veth${vmid}i$netid");
+		if ($have_sdn) {
+		    my $net = PVE::LXC::Config->parse_lxc_network($conf->{$opt});
+		    print "delete ips from $opt\n";
+		    eval { PVE::Network::SDN::Vnets::del_ips_from_mac($net->{bridge}, $net->{hwaddr}, $conf->{hostname}) };
+		    warn $@ if $@;
+		}
 	    } else {
 		die "skip\n"; # skip non-hotpluggable opts
 	    }
-- 
2.39.2




^ permalink raw reply	[flat|nested] 66+ messages in thread

* [pve-devel] [PATCH v4 pve-container 28/33] vm_destroy: remove ips from ipam for all interfaces
  2023-11-17 11:39 [pve-devel] [PATCH v4 cluster/network/manager/qemu-server/container/docs 00/33] Add support for DHCP servers to SDN Stefan Hanreich
                   ` (26 preceding siblings ...)
  2023-11-17 11:40 ` [pve-devel] [PATCH v4 pve-container 27/33] nic hotplug : add|del ips in ipam Stefan Hanreich
@ 2023-11-17 11:40 ` Stefan Hanreich
  2023-11-17 11:40 ` [pve-devel] [PATCH v4 pve-container 29/33] vm_create|restore: create ips in ipam Stefan Hanreich
                   ` (7 subsequent siblings)
  35 siblings, 0 replies; 66+ messages in thread
From: Stefan Hanreich @ 2023-11-17 11:40 UTC (permalink / raw)
  To: pve-devel

From: Alexandre Derumier <aderumier@odiso.com>

Signed-off-by: Alexandre Derumier <aderumier@odiso.com>
---
 src/PVE/LXC.pm | 16 ++++++++++++++++
 1 file changed, 16 insertions(+)

diff --git a/src/PVE/LXC.pm b/src/PVE/LXC.pm
index b6df6d6..4472e0f 100644
--- a/src/PVE/LXC.pm
+++ b/src/PVE/LXC.pm
@@ -46,6 +46,7 @@ use PVE::LXC::Tools;
 my $have_sdn;
 eval {
     require PVE::Network::SDN::Zones;
+    require PVE::Network::SDN::Vnets;
     $have_sdn = 1;
 };
 
@@ -898,6 +899,8 @@ sub destroy_lxc_container {
 	});
     }
 
+    delete_ifaces_ipams_ips($conf, $vmid);
+
     rmdir "/var/lib/lxc/$vmid/rootfs";
     unlink "/var/lib/lxc/$vmid/config";
     rmdir "/var/lib/lxc/$vmid";
@@ -2755,4 +2758,17 @@ sub thaw($) {
     }
 }
 
+sub delete_ifaces_ipams_ips {
+    my ($conf, $vmid) = @_;
+
+    return if !$have_sdn;
+
+    for my $opt (keys %$conf) {
+	next if $opt !~ m/^net(\d+)$/;
+	my $net = PVE::QemuServer::parse_net($conf->{$opt});
+	eval { PVE::Network::SDN::Vnets::del_ips_from_mac($net->{bridge}, $net->{hwaddr}, $conf->{hostname}) };
+	warn $@ if $@;
+    }
+}
+
 1;
-- 
2.39.2




^ permalink raw reply	[flat|nested] 66+ messages in thread

* [pve-devel] [PATCH v4 pve-container 29/33] vm_create|restore: create ips in ipam
  2023-11-17 11:39 [pve-devel] [PATCH v4 cluster/network/manager/qemu-server/container/docs 00/33] Add support for DHCP servers to SDN Stefan Hanreich
                   ` (27 preceding siblings ...)
  2023-11-17 11:40 ` [pve-devel] [PATCH v4 pve-container 28/33] vm_destroy: remove ips from ipam for all interfaces Stefan Hanreich
@ 2023-11-17 11:40 ` Stefan Hanreich
  2023-11-17 11:40 ` [pve-devel] [PATCH v4 pve-container 30/33] vm_clone : create ips in ipams Stefan Hanreich
                   ` (6 subsequent siblings)
  35 siblings, 0 replies; 66+ messages in thread
From: Stefan Hanreich @ 2023-11-17 11:40 UTC (permalink / raw)
  To: pve-devel

From: Alexandre Derumier <aderumier@odiso.com>

also delete ips on create failure

Co-Authored-by: Stefan Hanreich <s.hanreich@proxmox.com>
Signed-off-by: Alexandre Derumier <aderumier@odiso.com>
---
 src/PVE/API2/LXC.pm |  4 ++++
 src/PVE/LXC.pm      | 13 +++++++++++++
 2 files changed, 17 insertions(+)

diff --git a/src/PVE/API2/LXC.pm b/src/PVE/API2/LXC.pm
index 8839105..e15de28 100644
--- a/src/PVE/API2/LXC.pm
+++ b/src/PVE/API2/LXC.pm
@@ -475,9 +475,11 @@ __PACKAGE__->register_method({
 		    if ($restore) {
 			print "merging backed-up and given configuration..\n";
 			PVE::LXC::Create::restore_configuration($vmid, $storage_cfg, $archive, $rootdir, $conf, !$is_root, $unique, $skip_fw_config_restore);
+			PVE::LXC::create_ifaces_ipams_ips($conf, $vmid) if $unique;
 			my $lxc_setup = PVE::LXC::Setup->new($conf, $rootdir);
 			$lxc_setup->template_fixup($conf);
 		    } else {
+			PVE::LXC::create_ifaces_ipams_ips($conf, $vmid);
 			my $lxc_setup = PVE::LXC::Setup->new($conf, $rootdir); # detect OS
 			PVE::LXC::Config->write_config($vmid, $conf); # safe config (after OS detection)
 			$lxc_setup->post_create_hook($password, $ssh_keys);
@@ -503,6 +505,8 @@ __PACKAGE__->register_method({
 		PVE::LXC::Config->write_config($vmid, $conf);
 	    };
 	    if (my $err = $@) {
+		eval { PVE::LXC::delete_ifaces_ipams_ips($conf, $vmid) };
+		warn $@ if $@;
 		PVE::LXC::destroy_disks($storage_cfg, $vollist);
 		if ($destroy_config_on_error) {
 		    eval { PVE::LXC::Config->destroy_config($vmid) };
diff --git a/src/PVE/LXC.pm b/src/PVE/LXC.pm
index 4472e0f..2dad83d 100644
--- a/src/PVE/LXC.pm
+++ b/src/PVE/LXC.pm
@@ -2758,6 +2758,19 @@ sub thaw($) {
     }
 }
 
+sub create_ifaces_ipams_ips {
+    my ($conf, $vmid) = @_;
+
+    return if !$have_sdn;
+
+    for my $opt (keys %$conf) {
+	next if $opt !~ m/^net(\d+)$/;
+	my $net = PVE::QemuServer::parse_net($conf->{$opt});
+	next if $net->{type} ne 'veth';
+        PVE::Network::SDN::Vnets::add_next_free_cidr($net->{bridge}, $conf->{hostname}, $net->{hwaddr}, $vmid, undef, 1);
+    }
+}
+
 sub delete_ifaces_ipams_ips {
     my ($conf, $vmid) = @_;
 
-- 
2.39.2




^ permalink raw reply	[flat|nested] 66+ messages in thread

* [pve-devel] [PATCH v4 pve-container 30/33] vm_clone : create ips in ipams
  2023-11-17 11:39 [pve-devel] [PATCH v4 cluster/network/manager/qemu-server/container/docs 00/33] Add support for DHCP servers to SDN Stefan Hanreich
                   ` (28 preceding siblings ...)
  2023-11-17 11:40 ` [pve-devel] [PATCH v4 pve-container 29/33] vm_create|restore: create ips in ipam Stefan Hanreich
@ 2023-11-17 11:40 ` Stefan Hanreich
  2023-11-17 11:40 ` [pve-devel] [PATCH v4 pve-container 31/33] vm_apply_pending: add|del ips from ipam for offline changes Stefan Hanreich
                   ` (5 subsequent siblings)
  35 siblings, 0 replies; 66+ messages in thread
From: Stefan Hanreich @ 2023-11-17 11:40 UTC (permalink / raw)
  To: pve-devel

From: Alexandre Derumier <aderumier@odiso.com>

also delete ips in case of failure

Signed-off-by: Alexandre Derumier <aderumier@odiso.com>
---
 src/PVE/API2/LXC.pm | 6 +++++-
 1 file changed, 5 insertions(+), 1 deletion(-)

diff --git a/src/PVE/API2/LXC.pm b/src/PVE/API2/LXC.pm
index e15de28..ee4fdca 100644
--- a/src/PVE/API2/LXC.pm
+++ b/src/PVE/API2/LXC.pm
@@ -1830,7 +1830,9 @@ __PACKAGE__->register_method({
 		$lock_and_reload->($newid, sub {
 		    my $conf = shift;
 		    my $rootdir = PVE::LXC::mount_all($newid, $storecfg, $conf, 1);
+
 		    eval {
+			PVE::LXC::create_ifaces_ipams_ips($conf, $vmid);
 			my $lxc_setup = PVE::LXC::Setup->new($conf, $rootdir);
 			$lxc_setup->post_clone_hook($conf);
 		    };
@@ -1850,7 +1852,7 @@ __PACKAGE__->register_method({
 	    warn $@ if $@;
 
 	    if ($err) {
-		# Now cleanup the config & disks:
+		# Now cleanup the config & disks & ipam:
 		sleep 1; # some storages like rbd need to wait before release volume - really?
 
 		foreach my $volid (@$newvollist) {
@@ -1860,6 +1862,8 @@ __PACKAGE__->register_method({
 
 		eval {
 		    $lock_and_reload->($newid, sub {
+			my $conf = shift;
+			PVE::LXC::delete_ifaces_ipams_ips($conf, $newid);
 			PVE::LXC::Config->destroy_config($newid);
 			PVE::Firewall::remove_vmfw_conf($newid);
 		    });
-- 
2.39.2




^ permalink raw reply	[flat|nested] 66+ messages in thread

* [pve-devel] [PATCH v4 pve-container 31/33] vm_apply_pending: add|del ips from ipam for offline changes
  2023-11-17 11:39 [pve-devel] [PATCH v4 cluster/network/manager/qemu-server/container/docs 00/33] Add support for DHCP servers to SDN Stefan Hanreich
                   ` (29 preceding siblings ...)
  2023-11-17 11:40 ` [pve-devel] [PATCH v4 pve-container 30/33] vm_clone : create ips in ipams Stefan Hanreich
@ 2023-11-17 11:40 ` Stefan Hanreich
  2023-11-17 11:40 ` [pve-devel] [PATCH v4 pve-container 32/33] lxc-pve-prestart-hook : add_dhcp_mapping Stefan Hanreich
                   ` (4 subsequent siblings)
  35 siblings, 0 replies; 66+ messages in thread
From: Stefan Hanreich @ 2023-11-17 11:40 UTC (permalink / raw)
  To: pve-devel

From: Alexandre Derumier <aderumier@odiso.com>

Co-Authored-by: Stefan Hanreich <s.hanreich@proxmox.com>
Signed-off-by: Alexandre Derumier <aderumier@odiso.com>
---
 src/PVE/LXC/Config.pm | 15 +++++++++++++++
 1 file changed, 15 insertions(+)

diff --git a/src/PVE/LXC/Config.pm b/src/PVE/LXC/Config.pm
index c884313..823a2b9 100644
--- a/src/PVE/LXC/Config.pm
+++ b/src/PVE/LXC/Config.pm
@@ -1471,6 +1471,12 @@ sub vmconfig_apply_pending {
 	    } elsif ($opt =~ m/^unused(\d+)$/) {
 		PVE::LXC::delete_mountpoint_volume($storecfg, $vmid, $conf->{$opt})
 		    if !$class->is_volume_in_use($conf, $conf->{$opt}, 1, 1);
+	    } elsif ($opt =~ m/^net(\d+)$/) {
+		if ($have_sdn) {
+		    my $net = $class->parse_lxc_network($conf->{$opt});
+		    eval { PVE::Network::SDN::Vnets::del_ips_from_mac($net->{bridge}, $net->{hwaddr}, $conf->{hostname}) };
+		    warn $@ if $@;
+		}
 	    }
 	};
 	if (my $err = $@) {
@@ -1493,6 +1499,15 @@ sub vmconfig_apply_pending {
 		my $netid = $1;
 		my $net = $class->parse_lxc_network($conf->{pending}->{$opt});
 		$conf->{pending}->{$opt} = $class->print_lxc_network($net);
+		if ($have_sdn) {
+		    if($conf->{$opt}) {
+			my $old_net = $class->parse_lxc_network($conf->{$opt});
+			if ($old_net->{bridge} ne $net->{bridge} || $old_net->{hwaddr} ne $net->{hwaddr}) {
+			    PVE::Network::SDN::Vnets::del_ips_from_mac($old_net->{bridge}, $old_net->{hwaddr}, $conf->{name});
+			}
+		    }
+		    PVE::Network::SDN::Vnets::add_next_free_cidr($net->{bridge}, $conf->{hostname}, $net->{hwaddr}, $vmid, undef, 1);
+		}
 	    }
 	};
 	if (my $err = $@) {
-- 
2.39.2




^ permalink raw reply	[flat|nested] 66+ messages in thread

* [pve-devel] [PATCH v4 pve-container 32/33] lxc-pve-prestart-hook : add_dhcp_mapping
  2023-11-17 11:39 [pve-devel] [PATCH v4 cluster/network/manager/qemu-server/container/docs 00/33] Add support for DHCP servers to SDN Stefan Hanreich
                   ` (30 preceding siblings ...)
  2023-11-17 11:40 ` [pve-devel] [PATCH v4 pve-container 31/33] vm_apply_pending: add|del ips from ipam for offline changes Stefan Hanreich
@ 2023-11-17 11:40 ` Stefan Hanreich
  2023-11-17 11:40 ` [pve-devel] [PATCH v4 pve-docs 33/33] sdn: dhcp: Add documentation for DHCP Stefan Hanreich
                   ` (3 subsequent siblings)
  35 siblings, 0 replies; 66+ messages in thread
From: Stefan Hanreich @ 2023-11-17 11:40 UTC (permalink / raw)
  To: pve-devel

From: Alexandre Derumier <aderumier@odiso.com>

Signed-off-by: Alexandre Derumier <aderumier@odiso.com>
---
 src/lxc-pve-prestart-hook | 15 +++++++++++++++
 1 file changed, 15 insertions(+)

diff --git a/src/lxc-pve-prestart-hook b/src/lxc-pve-prestart-hook
index 936d0bf..fc577e4 100755
--- a/src/lxc-pve-prestart-hook
+++ b/src/lxc-pve-prestart-hook
@@ -21,6 +21,12 @@ use PVE::Storage;
 use PVE::Syscall qw(:fsmount);
 use PVE::Tools qw(AT_FDCWD O_PATH);
 
+my $have_sdn;
+eval {
+    require PVE::Network::SDN::Vnets;
+    $have_sdn = 1;
+};
+
 my $WARNFD;
 sub log_warn {
     my ($vmid, $message) = @_;
@@ -140,6 +146,15 @@ PVE::LXC::Tools::lxc_hook('pre-start', 'lxc', sub {
 	}
 	PVE::Tools::file_set_contents($devlist_file, $devlist);
     }
+
+    if ($have_sdn) {
+	for my $k (keys %$conf) {
+	    next if $k !~ /^net(\d+)/;
+	    my $net = PVE::LXC::Config->parse_lxc_network($conf->{$k});
+	    next if $net->{type} ne 'veth';
+	    PVE::Network::SDN::Vnets::add_dhcp_mapping($net->{bridge}, $net->{hwaddr});
+	}
+    }
 });
 
 # Leftover cgroups prevent lxc from starting without any useful information
-- 
2.39.2




^ permalink raw reply	[flat|nested] 66+ messages in thread

* [pve-devel] [PATCH v4 pve-docs 33/33] sdn: dhcp: Add documentation for DHCP
  2023-11-17 11:39 [pve-devel] [PATCH v4 cluster/network/manager/qemu-server/container/docs 00/33] Add support for DHCP servers to SDN Stefan Hanreich
                   ` (31 preceding siblings ...)
  2023-11-17 11:40 ` [pve-devel] [PATCH v4 pve-container 32/33] lxc-pve-prestart-hook : add_dhcp_mapping Stefan Hanreich
@ 2023-11-17 11:40 ` Stefan Hanreich
  2023-11-21 19:03   ` [pve-devel] applied: " Thomas Lamprecht
  2023-11-17 15:47 ` [pve-devel] [PATCH v4 cluster/network/manager/qemu-server/container/docs 00/33] Add support for DHCP servers to SDN DERUMIER, Alexandre
                   ` (2 subsequent siblings)
  35 siblings, 1 reply; 66+ messages in thread
From: Stefan Hanreich @ 2023-11-17 11:40 UTC (permalink / raw)
  To: pve-devel

Signed-off-by: Stefan Hanreich <s.hanreich@proxmox.com>
---
 pvesdn.adoc | 122 ++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 122 insertions(+)

diff --git a/pvesdn.adoc b/pvesdn.adoc
index b796c5e..24878e2 100644
--- a/pvesdn.adoc
+++ b/pvesdn.adoc
@@ -79,6 +79,9 @@ In addition to this, the following options are offered:
 * DNS: Define a DNS server API for registering virtual guests' hostname and IP
   addresses
 
+* DHCP: Define a DHCP server for a zone that automatically allocates IPs for
+  guests in the IPAM and leases them to the guests via DHCP.
+
 [[pvesdn_config_main_sdn]]
 
 SDN
@@ -418,6 +421,17 @@ for all subnets defined in those zones.
 This is the default internal IPAM for your {pve} cluster, if you don't have
 external IPAM software.
 
+You can inspect the current status of the PVE IPAM Plugin via the Panel IPAM in
+the SDN section of the datacenter configuration. This UI can be used to create,
+update and delete IP mappings. This is particularly convenient in conjunction
+with the xref:pvesdn_config_dhcp[DHCP feature].
+
+If you are using DHCP, you can use the IPAM panel to create or edit leases for
+specific VMs, which enables you to change the IPs allocated via DHCP. When
+editing an IP of a VM that is using DHCP you must make sure to force the guest
+to acquire a new DHCP leases. This can usually be done by reloading the network
+stack of the guest or rebooting it.
+
 [[pvesdn_ipam_plugin_phpipam]]
 phpIPAM Plugin
 ~~~~~~~~~~~~~~
@@ -484,6 +498,114 @@ key:: An API access key
 ttl:: The default TTL for records
 
 
+[[pvesdn_config_dhcp]]
+DHCP
+------
+
+The DHCP plugin in {pve} SDN can be used to automatically deploy a DHCP server
+for a Zone. It provides DHCP for all Subnets in a Zone that have a DHCP range
+configured. Currently the only available backend plugin for DHCP is the dnsmasq
+plugin.
+
+The DHCP plugin works by allocating an IP in the IPAM plugin configured in the
+Zone when adding a new network interface to a VM/CT. You can find more
+information on how to configure an IPAM in the
+xref:pvesdn_config_ipam[respective section of our documentation].
+
+When the VM starts, a mapping for the MAC address and IP gets created in the DHCP
+plugin of the zone. When the network interfaces is removed or the VM/CT are
+destroyed, then the entry in the IPAM and the DHCP server are deleted as well.
+
+NOTE: Some features (adding/editing/removing IP mappings) are currently only
+available when using the xref:pvesdn_ipam_plugin_pveipam[PVE IPAM plugin].
+
+
+Configuration
+~~~~~~~~~~~~~
+
+You can enable automatic DHCP for a zone in the Web UI via the Zones panel and
+enabling DHCP in the advanced options of a zone.
+
+NOTE: Currently only Simple Zones have support for automatic DHCP
+
+After automatic DHCP has been enabled for a Zone, DHCP Ranges need to be
+configured for the subnets in a Zone. In order to that, go to the Vnets panel and
+select the Subnet for which you want to configure DHCP ranges. In the edit
+dialogue you can configure DHCP ranges in the respective Tab. Alternatively you
+can set DHCP ranges for a Subnet via the following CLI command:
+
+----
+pvesh set /cluster/sdn/vnets/<vnet>/subnets/<subnet>
+ -dhcp-range start-address=10.0.1.100,end-address=10.0.1.200
+ -dhcp-range start-address=10.0.2.100,end-address=10.0.2.200
+----
+
+You also need to have a gateway configured for the subnet - otherwise
+automatic DHCP will not work.
+
+The DHCP plugin will then allocate IPs in the IPAM only in the configured
+ranges.
+
+Do not forget to follow the installation steps for the
+xref:pvesdn_dhcp_dnsmasq_installation[dnsmasq DHCP plugin] as well.
+
+Plugins
+~~~~~~~
+
+Dnsmasq Plugin
+^^^^^^^^^^^^^^
+Currently this is the only DHCP plugin and therefore the plugin that gets used
+when you enable DHCP for a zone.
+
+[[pvesdn_dhcp_dnsmasq_installation]]
+.Installation
+In order to be able to use the Dnsmasq plugin you need to install
+the dnsmasq package and disable the default DNS server that gets automatically
+started:
+
+----
+apt install dnsmasq
+systemctl disable --now dnsmasq
+----
+
+.Configuration
+The plugin will create a new systemd service for each zone that dnsmasq gets
+deployed to. The name for the service is `dnsmasq@<zone>`. The lifecycle of this
+service is managed by the DHCP plugin.
+
+The plugin automatically generates the following configuration files in the
+folder `/etc/dnsmasq.d/<zone>`:
+
+`00-default.conf`::
+This contains the default global configuration for a dnsmasq instance.
+
+`10-<zone>-<subnet_cidr>.conf`::
+This file configures specific options for a subnet, such as the DNS server that
+should get configured via DHCP.
+
+`10-<zone>-<subnet_cidr>.ranges.conf`::
+This file configures the DHCP ranges for the dnsmasq instance.
+
+`ethers`::
+This file contains the MAC-address and IP mappings from the IPAM plugin. In
+order to override those mappings, please use the respective IPAM plugin rather
+than editing this file, as it will get overwritten by the dnsmasq plugin.
+
+You must not edit any of the above files, since they are managed by the DHCP
+plugin. In order to customize the dnsmasq configuration you can create
+additional files (e.g. `90-custom.conf`) in the configuration folder - they will
+not get changed by the dnsmasq DHCP plugin.
+
+Configuration files are read in order, so you can control the order of the
+configuration directives by naming your custom configuration files appropriately.
+
+DHCP leases are stored in the file `/var/lib/misc/dnsmasq.<zone>.leases`.
+
+When using the PVE IPAM plugin, you can update, create and delete DHCP leases.
+For more information please consult the documentation of
+xref:pvesdn_ipam_plugin_pveipam[the PVE IPAM plugin]. Changing DHCP leases is
+currently not supported for the other IPAM plugins.
+
 Examples
 --------
 
-- 
2.39.2




^ permalink raw reply	[flat|nested] 66+ messages in thread

* [pve-devel] applied: [PATCH v4 pve-cluster 01/33] add priv/macs.db
  2023-11-17 11:39 ` [pve-devel] [PATCH v4 pve-cluster 01/33] add priv/macs.db Stefan Hanreich
@ 2023-11-17 13:54   ` Thomas Lamprecht
  0 siblings, 0 replies; 66+ messages in thread
From: Thomas Lamprecht @ 2023-11-17 13:54 UTC (permalink / raw)
  To: Proxmox VE development discussion, Stefan Hanreich

Am 17/11/2023 um 12:39 schrieb Stefan Hanreich:
> From: Alexandre Derumier <aderumier@odiso.com>
> 
> use to cache mac-ip list association.
> 
> can be use by external ipam, firewall,etc for fast lookup
> 
> Signed-off-by: Alexandre Derumier <aderumier@odiso.com>
> ---
>  src/PVE/Cluster.pm  | 1 +
>  src/pmxcfs/status.c | 1 +
>  2 files changed, 2 insertions(+)
> 
>

I'm currently preparing a version bump, and it might be easier for all
if this is already included, even if we'd drop it again (unlikely), so:

applied this one for now, thanks!




^ permalink raw reply	[flat|nested] 66+ messages in thread

* Re: [pve-devel] [PATCH v4 pve-network 06/33] subnet: vnet: refactor IPAM related methods
  2023-11-17 11:39 ` [pve-devel] [PATCH v4 pve-network 06/33] subnet: vnet: refactor IPAM related methods Stefan Hanreich
@ 2023-11-17 14:13   ` Stefan Lendl
  2023-11-17 15:12     ` Stefan Hanreich
  0 siblings, 1 reply; 66+ messages in thread
From: Stefan Lendl @ 2023-11-17 14:13 UTC (permalink / raw)
  To: Stefan Hanreich, pve-devel

Stefan Hanreich <s.hanreich@proxmox.com> writes:

> @@ -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;

If an IP was found, the loop will just start again.
This should be (tested):

last 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 {




^ permalink raw reply	[flat|nested] 66+ messages in thread

* Re: [pve-devel] [PATCH v4 pve-manager 20/33] sdn: ipam: add ipam panel
  2023-11-17 11:39 ` [pve-devel] [PATCH v4 pve-manager 20/33] sdn: ipam: add ipam panel Stefan Hanreich
@ 2023-11-17 15:04   ` DERUMIER, Alexandre
  2023-11-17 15:15     ` Stefan Hanreich
  2023-11-20 13:44   ` Dominik Csapak
  1 sibling, 1 reply; 66+ messages in thread
From: DERUMIER, Alexandre @ 2023-11-17 15:04 UTC (permalink / raw)
  To: pve-devel

I wonder if this panel could be integrated in zone panel (accessible
from the tree).

as It's not related to the sdn configuration itself. (and don't need
sdn reload)

I think yhis could allow to give permissions to user to manage ips in
the zone, without need to access to datacenter panel


-------- Message initial --------
De: Stefan Hanreich <s.hanreich@proxmox.com>
Répondre à: Proxmox VE development discussion <pve-
devel@lists.proxmox.com>
À: pve-devel@lists.proxmox.com
Objet: [pve-devel] [PATCH v4 pve-manager 20/33] sdn: ipam: add ipam
panel
Date: 17/11/2023 12:39:58

Signed-off-by: Stefan Hanreich <s.hanreich@proxmox.com>
---
 www/css/ext6-pve.css          |  22 ++-
 www/manager6/Makefile         |   1 +
 www/manager6/dc/Config.js     |  12 +-
 www/manager6/sdn/IpamEdit.js  |  78 ++++++++++
 www/manager6/tree/DhcpTree.js | 267 ++++++++++++++++++++++++++++++++++
 5 files changed, 372 insertions(+), 8 deletions(-)
 create mode 100644 www/manager6/sdn/IpamEdit.js
 create mode 100644 www/manager6/tree/DhcpTree.js

diff --git a/www/css/ext6-pve.css b/www/css/ext6-pve.css
index e18b173f5..091855356 100644
--- a/www/css/ext6-pve.css
+++ b/www/css/ext6-pve.css
@@ -510,28 +510,38 @@ div.right-aligned {
     content: ' ';
 }
 
-.fa-sdn:before {
+.x-fa-sdn-treelist:before {
     width: 14px;
     height: 14px;
     position: absolute;
     left: 1px;
     top: 4px;
+}
+
+.fa-sdn:before {
     background-image:url(../images/icon-sdn.svg);
     background-size: 14px 14px;
     content: ' ';
 }
 
 .fa-network-wired:before {
-    width: 14px;
-    height: 14px;
-    position: absolute;
-    left: 1px;
-    top: 4px;
     background-image:url(../images/icon-fa-network-wired.svg);
     background-size: 14px 14px;
     content: ' ';
 }
 
+.x-fa-treepanel:before {
+    width: 16px;
+    height: 24px;
+    display: block;
+    background-repeat: no-repeat;
+    background-position: center;
+}
+
+.x-tree-icon-none {
+    display: none;
+}
+
 .x-treelist-row-over > * > .x-treelist-item-icon,
 .x-treelist-row-over > * > .x-treelist-item-text{
     color: #000;
diff --git a/www/manager6/Makefile b/www/manager6/Makefile
index 093452cd7..93b4ff155 100644
--- a/www/manager6/Makefile
+++ b/www/manager6/Makefile
@@ -108,6 +108,7 @@
JSSRC= 							\
 	tree/ResourceTree.js				\
 	tree/SnapshotTree.js				\
 	tree/ResourceMapTree.js				\
+	tree/DhcpTree.js				\
 	window/Backup.js				\
 	window/BackupConfig.js				\
 	window/BulkAction.js				\
diff --git a/www/manager6/dc/Config.js b/www/manager6/dc/Config.js
index 7d01da5fb..7c2b7b168 100644
--- a/www/manager6/dc/Config.js
+++ b/www/manager6/dc/Config.js
@@ -185,7 +185,7 @@ Ext.define('PVE.dc.Config', {
 		me.items.push({
 		    xtype: 'pveSDNStatus',
 		    title: gettext('SDN'),
-		    iconCls: 'fa fa-sdn',
+		    iconCls: 'fa fa-sdn x-fa-sdn-treelist',
 		    hidden: true,
 		    itemId: 'sdn',
 		    expandedOnInit: true,
@@ -203,7 +203,7 @@ Ext.define('PVE.dc.Config', {
 		    groups: ['sdn'],
 		    title: 'VNets',
 		    hidden: true,
-		    iconCls: 'fa fa-network-wired',
+		    iconCls: 'fa fa-network-wired x-fa-sdn-treelist',
 		    itemId: 'sdnvnet',
 		},
 		{
@@ -213,6 +213,14 @@ Ext.define('PVE.dc.Config', {
 		    hidden: true,
 		    iconCls: 'fa fa-gear',
 		    itemId: 'sdnoptions',
+		},
+		{
+		    xtype: 'pveDhcpTree',
+		    groups: ['sdn'],
+		    title: gettext('IPAM'),
+		    hidden: true,
+		    iconCls: 'fa fa-map-signs',
+		    itemId: 'sdnmappings',
 		});
 	    }
 
diff --git a/www/manager6/sdn/IpamEdit.js
b/www/manager6/sdn/IpamEdit.js
new file mode 100644
index 000000000..18e22c592
--- /dev/null
+++ b/www/manager6/sdn/IpamEdit.js
@@ -0,0 +1,78 @@
+Ext.define('PVE.sdn.IpamEditInputPanel', {
+    extend: 'Proxmox.panel.InputPanel',
+    mixins: ['Proxmox.Mixin.CBind'],
+
+    isCreate: false,
+
+    onGetValues: function(values) {
+	let me = this;
+
+	if (!values.vmid) {
+	    delete values.vmid;
+	}
+
+	return values;
+    },
+
+    items: [
+	{
+	    xtype: 'pmxDisplayEditField',
+	    name: 'vmid',
+	    fieldLabel: gettext('VMID'),
+	    allowBlank: false,
+	    editable: false,
+	    cbind: {
+		hidden: '{isCreate}',
+	    },
+	},
+	{
+	    xtype: 'pmxDisplayEditField',
+	    name: 'mac',
+	    fieldLabel: gettext('MAC'),
+	    allowBlank: false,
+	    cbind: {
+		editable: '{isCreate}',
+	    },
+	},
+	{
+	    xtype: 'proxmoxtextfield',
+	    name: 'ip',
+	    fieldLabel: gettext('IP'),
+	    allowBlank: false,
+	},
+    ],
+});
+
+Ext.define('PVE.sdn.IpamEdit', {
+    extend: 'Proxmox.window.Edit',
+
+    subject: gettext('DHCP Mapping'),
+    width: 350,
+
+    isCreate: false,
+    mapping: {},
+
+    submitUrl: function(url, values) {
+	return `${url}/${values.zone}/${values.vnet}/${values.mac}`;
+    },
+
+    initComponent: function() {
+	var me = this;
+
+	me.method = me.isCreate ? 'POST' : 'PUT';
+
+	let ipanel = Ext.create('PVE.sdn.IpamEditInputPanel', {
+	    isCreate: me.isCreate,
+	});
+
+	Ext.apply(me, {
+	    items: [
+		ipanel,
+	    ],
+	});
+
+	me.callParent();
+
+	ipanel.setValues(me.mapping);
+    },
+});
diff --git a/www/manager6/tree/DhcpTree.js
b/www/manager6/tree/DhcpTree.js
new file mode 100644
index 000000000..ca279c29a
--- /dev/null
+++ b/www/manager6/tree/DhcpTree.js
@@ -0,0 +1,267 @@
+Ext.define('PVE.sdn.DhcpTree', {
+    extend: 'Ext.tree.Panel',
+    xtype: 'pveDhcpTree',
+
+    layout: 'fit',
+    rootVisible: false,
+    animate: false,
+
+    store: {
+	sorters: ['ip', 'name'],
+    },
+
+    controller: {
+	xclass: 'Ext.app.ViewController',
+
+	reload: function() {
+	    let me = this;
+
+	    Proxmox.Utils.API2Request({
+		url: `/cluster/sdn/ipam`,
+		method: 'GET',
+		success: function(response, opts) {
+		    let root = {
+			name: '__root',
+			expanded: true,
+			children: [],
+		    };
+
+		    let zones = {};
+		    let vnets = {};
+		    let subnets = {};
+
+		    response.result.data.forEach((element) => {
+			element.leaf = true;
+
+			if (!(element.zone in zones)) {
+			    let zone = {
+				name: element.zone,
+				type: 'zone',
+				iconCls: 'fa fa-th',
+				expanded: true,
+				children: [],
+			    };
+
+			    zones[element.zone] = zone;
+			    root.children.push(zone);
+			}
+
+			if (!(element.vnet in vnets)) {
+			    let vnet = {
+				name: element.vnet,
+				zone: element.zone,
+				type: 'vnet',
+				iconCls: 'fa fa-network-wired x-fa-
treepanel',
+				expanded: true,
+				children: [],
+			    };
+
+			    vnets[element.vnet] = vnet;
+			    zones[element.zone].children.push(vnet);
+			}
+
+			if (!(element.subnet in subnets)) {
+			    let subnet = {
+				name: element.subnet,
+				zone: element.zone,
+				vnet: element.vnet,
+				type: 'subnet',
+				iconCls: 'x-tree-icon-none',
+				expanded: true,
+				children: [],
+			    };
+
+			    subnets[element.subnet] = subnet;
+			    vnets[element.vnet].children.push(subnet);
+			}
+
+			element.type = 'mapping';
+			element.iconCls = 'x-tree-icon-none';
+			subnets[element.subnet].children.push(element)
;
+		    });
+
+		    me.getView().setRootNode(root);
+		},
+	    });
+	},
+
+	init: function(view) {
+	    let me = this;
+	    me.reload();
+	},
+
+	onDelete: function(table, rI, cI, item, e, { data }) {
+	    let me = this;
+	    let view = me.getView();
+
+	    Ext.Msg.show({
+		title: gettext('Confirm'),
+		icon: Ext.Msg.WARNING,
+		message: Ext.String.format(gettext('Are you sure you
want to remove DHCP mapping {0}'), `${data.mac} / ${data.ip}`),
+		buttons: Ext.Msg.YESNO,
+		defaultFocus: 'no',
+		callback: function(btn) {
+		    if (btn !== 'yes') {
+		        return;
+		    }
+
+		    Proxmox.Utils.API2Request({
+			url:
`/cluster/sdn/ipam/${data.zone}/${data.vnet}/${data.mac}`,
+			method: 'DELETE',
+			waitMsgTarget: view,
+			failure: function(response, opts) {
+			    Ext.Msg.alert(gettext('Error'),
response.htmlStatus);
+			},
+			callback: me.reload.bind(me),
+		    });
+		},
+	    });
+	},
+
+	editAction: function(_grid, _rI, _cI, _item, _e, rec) {
+	    this.edit(rec);
+	},
+
+	editDblClick: function() {
+	    let me = this;
+
+	    let view = me.getView();
+	    let selection = view.getSelection();
+
+	    if (!selection || selection.length < 1) {
+		return;
+	    }
+
+	    me.edit(selection[0]);
+	},
+
+	edit: function(rec) {
+	    let me = this;
+
+	    if (rec.data.type === 'mapping' && !rec.data.gateway) {
+		me.openEditWindow(rec.data);
+	    }
+	},
+
+	openEditWindow: function(data) {
+	    let me = this;
+
+	    Ext.create('PVE.sdn.IpamEdit', {
+		autoShow: true,
+		mapping: data,
+		url: `/cluster/sdn/ipam`,
+		extraRequestParams: {
+		    vmid: data.vmid,
+		    mac: data.mac,
+		    zone: data.zone,
+		    vnet: data.vnet,
+		},
+		listeners: {
+		    destroy: () => me.reload(),
+		},
+	    });
+	},
+    },
+
+    listeners: {
+	itemdblclick: 'editDblClick',
+    },
+
+    tbar: [
+	{
+	    xtype: 'proxmoxButton',
+	    text: gettext('Reload'),
+	    handler: 'reload',
+	},
+    ],
+
+    columns: [
+	{
+	    xtype: 'treecolumn',
+	    text: gettext('Name / VMID'),
+	    dataIndex: 'name',
+	    width: 200,
+	    renderer: function(value, meta, record) {
+		if (record.get('gateway')) {
+		    return gettext('Gateway');
+		}
+
+		return record.get('name') ?? record.get('vmid') ?? '
';
+	    },
+	},
+	{
+	    text: gettext('IP'),
+	    dataIndex: 'ip',
+	    width: 200,
+	},
+	{
+	    text: gettext('MAC'),
+	    dataIndex: 'mac',
+	    width: 200,
+	},
+	{
+	    text: gettext('Gateway'),
+	    dataIndex: 'gateway',
+	    width: 200,
+	},
+	{
+	    header: gettext('Actions'),
+	    xtype: 'actioncolumn',
+	    dataIndex: 'text',
+	    width: 150,
+	    items: [
+		{
+		    handler: function(table, rI, cI, item, e, { data
}) {
+			let me = this;
+
+			Ext.create('PVE.sdn.IpamEdit', {
+			    autoShow: true,
+			    mapping: {},
+			    url: `/cluster/sdn/ipam`,
+			    isCreate: true,
+			    extraRequestParams: {
+				vnet: data.name,
+				zone: data.zone,
+			    },
+			    listeners: {
+				destroy: () => {
+				   
me.up('pveDhcpTree').controller.reload();
+				},
+			    },
+			});
+		    },
+		    getTip: (v, m, rec) => gettext('Add'),
+		    getClass: (v, m, { data }) => {
+			if (data.type === 'vnet') {
+			    return 'fa fa-plus-square';
+			}
+
+			return 'pmx-hidden';
+		    },
+                },
+		{
+		    handler: 'editAction',
+		    getTip: (v, m, rec) => gettext('Edit'),
+		    getClass: (v, m, { data }) => {
+			if (data.type === 'mapping' && !data.gateway)
{
+			    return 'fa fa-pencil fa-fw';
+			}
+
+			return 'pmx-hidden';
+		    },
+                },
+		{
+		    handler: 'onDelete',
+		    getTip: (v, m, rec) => gettext('Delete'),
+		    getClass: (v, m, { data }) => {
+			if (data.type === 'mapping' && !data.gateway)
{
+			    return 'fa critical fa-trash-o';
+			}
+
+			return 'pmx-hidden';
+		    },
+                },
+	    ],
+	},
+    ],
+});


^ permalink raw reply	[flat|nested] 66+ messages in thread

* Re: [pve-devel] [PATCH v4 pve-network 06/33] subnet: vnet: refactor IPAM related methods
  2023-11-17 14:13   ` Stefan Lendl
@ 2023-11-17 15:12     ` Stefan Hanreich
  0 siblings, 0 replies; 66+ messages in thread
From: Stefan Hanreich @ 2023-11-17 15:12 UTC (permalink / raw)
  To: Stefan Lendl, pve-devel



On 11/17/23 15:13, Stefan Lendl wrote:
> If an IP was found, the loop will just start again.
> This should be (tested):
> 
> last if $ip;

Ah yes, I fixed the symptom of this already elsewhere but here it makes
a lot more sense!




^ permalink raw reply	[flat|nested] 66+ messages in thread

* Re: [pve-devel] [PATCH v4 pve-manager 20/33] sdn: ipam: add ipam panel
  2023-11-17 15:04   ` DERUMIER, Alexandre
@ 2023-11-17 15:15     ` Stefan Hanreich
  2023-11-18 14:25       ` DERUMIER, Alexandre
  0 siblings, 1 reply; 66+ messages in thread
From: Stefan Hanreich @ 2023-11-17 15:15 UTC (permalink / raw)
  To: pve-devel



On 11/17/23 16:04, DERUMIER, Alexandre wrote:
> I wonder if this panel could be integrated in zone panel (accessible
> from the tree).

I fear that this might overload the panel a bit.

> as It's not related to the sdn configuration itself. (and don't need
> sdn reload)
> 
> I think yhis could allow to give permissions to user to manage ips in
> the zone, without need to access to datacenter panel
I agree with this general sentiment though I'm not sure whether adding
it to Zone might be a good solution. Doesn't the user then still require
access to the Datacenter panel (since Zone is included there as well).

Also another point was not adding any specific new permissions for now.




^ permalink raw reply	[flat|nested] 66+ messages in thread

* Re: [pve-devel] [PATCH v4 cluster/network/manager/qemu-server/container/docs 00/33] Add support for DHCP servers to SDN
  2023-11-17 11:39 [pve-devel] [PATCH v4 cluster/network/manager/qemu-server/container/docs 00/33] Add support for DHCP servers to SDN Stefan Hanreich
                   ` (32 preceding siblings ...)
  2023-11-17 11:40 ` [pve-devel] [PATCH v4 pve-docs 33/33] sdn: dhcp: Add documentation for DHCP Stefan Hanreich
@ 2023-11-17 15:47 ` DERUMIER, Alexandre
  2023-11-17 16:05   ` Stefan Hanreich
  2023-11-21 11:23   ` Stefan Lendl
  2023-11-18 14:38 ` DERUMIER, Alexandre
  2023-11-20 16:42 ` Thomas Lamprecht
  35 siblings, 2 replies; 66+ messages in thread
From: DERUMIER, Alexandre @ 2023-11-17 15:47 UTC (permalink / raw)
  To: pve-devel

>>  * dnsmasq and IPv6 (and DHCP in general) do not really play well
>>together,
>>    so using subnets with IPv6 configured is wonky

I didn't have tested yet, but it's seem that dnsmasq only support old
classic duid reservation and not mac ? 


because kea allow mac reservation for
https://kea.readthedocs.io/en/kea-2.2.0/arm/dhcp6-srv.html#mac-hardware-addresses-in-dhcpv6

using different techniques to find it.



But for now, I think it could be better indeed to disable ipv6 ipam
reservation in code.




^ permalink raw reply	[flat|nested] 66+ messages in thread

* Re: [pve-devel] [PATCH v4 cluster/network/manager/qemu-server/container/docs 00/33] Add support for DHCP servers to SDN
  2023-11-17 15:47 ` [pve-devel] [PATCH v4 cluster/network/manager/qemu-server/container/docs 00/33] Add support for DHCP servers to SDN DERUMIER, Alexandre
@ 2023-11-17 16:05   ` Stefan Hanreich
  2023-11-17 16:07     ` Stefan Hanreich
  2023-11-17 16:09     ` DERUMIER, Alexandre
  2023-11-21 11:23   ` Stefan Lendl
  1 sibling, 2 replies; 66+ messages in thread
From: Stefan Hanreich @ 2023-11-17 16:05 UTC (permalink / raw)
  To: Proxmox VE development discussion, DERUMIER, Alexandre



On 11/17/23 16:47, DERUMIER, Alexandre wrote:
>>>   * dnsmasq and IPv6 (and DHCP in general) do not really play well
>>> together,
>>>     so using subnets with IPv6 configured is wonky
> 
> I didn't have tested yet, but it's seem that dnsmasq only support old
> classic duid reservation and not mac ? 

I've checked the documentation and it seems to support it (in our case
at least, since we have direct connection - correct me if I am wrong):

From [1], the documentation for dhcp-host:

" Note that in IPv6 DHCP, the hardware address may not be available,
though it normally is for direct-connected clients, or clients using
DHCP relays which support RFC 6939. "

Maybe the issue here are the respective fwbr interfaces inbetween?

[1] https://thekelleys.org.uk/dnsmasq/docs/dnsmasq-man.html




^ permalink raw reply	[flat|nested] 66+ messages in thread

* Re: [pve-devel] [PATCH v4 cluster/network/manager/qemu-server/container/docs 00/33] Add support for DHCP servers to SDN
  2023-11-17 16:05   ` Stefan Hanreich
@ 2023-11-17 16:07     ` Stefan Hanreich
  2023-11-17 16:09     ` DERUMIER, Alexandre
  1 sibling, 0 replies; 66+ messages in thread
From: Stefan Hanreich @ 2023-11-17 16:07 UTC (permalink / raw)
  To: pve-devel

On 11/17/23 17:05, Stefan Hanreich wrote:
> Maybe the issue here are the respective fwbr interfaces inbetween?

I guess that's unlikely since that would affect VMs as well I suppose....




^ permalink raw reply	[flat|nested] 66+ messages in thread

* Re: [pve-devel] [PATCH v4 cluster/network/manager/qemu-server/container/docs 00/33] Add support for DHCP servers to SDN
  2023-11-17 16:05   ` Stefan Hanreich
  2023-11-17 16:07     ` Stefan Hanreich
@ 2023-11-17 16:09     ` DERUMIER, Alexandre
  2023-11-17 20:44       ` DERUMIER, Alexandre
  1 sibling, 1 reply; 66+ messages in thread
From: DERUMIER, Alexandre @ 2023-11-17 16:09 UTC (permalink / raw)
  To: pve-devel, s.hanreich


>>I've checked the documentation and it seems to support it (in our
case
>>at least, since we have direct connection - correct me if I am
wrong):

>>From [1], the documentation for dhcp-host:

>>" Note that in IPv6 DHCP, the hardware address may not be available,
>>though it normally is for direct-connected clients, or clients using
>>DHCP relays which support RFC 6939. "

>>Maybe the issue here are the respective fwbr interfaces inbetween?

mmmn, it seem that openstack is doing it with mac reservation

https://bugs.launchpad.net/neutron/+bug/1861032


but I think that the current format for ipv6 address in ether is wrong.
I'll do tests this weekend.

(I using dual stack ipv4/ipv6 in production, so I really need to get
this works ^_^ )



^ permalink raw reply	[flat|nested] 66+ messages in thread

* Re: [pve-devel] [PATCH v4 cluster/network/manager/qemu-server/container/docs 00/33] Add support for DHCP servers to SDN
  2023-11-17 16:09     ` DERUMIER, Alexandre
@ 2023-11-17 20:44       ` DERUMIER, Alexandre
  0 siblings, 0 replies; 66+ messages in thread
From: DERUMIER, Alexandre @ 2023-11-17 20:44 UTC (permalink / raw)
  To: pve-devel, s.hanreich

ok, I have fixed ipv6 support (tested with linux && windows).

reservation by mac seem to be work. (Maybe the dbus update is wrong, I
need to test it more deeply).

I'll send a patch tommorow.


-------- Message initial --------
De: "DERUMIER, Alexandre" <alexandre.derumier@groupe-cyllene.com>
Répondre à: Proxmox VE development discussion <pve-
devel@lists.proxmox.com>
À: pve-devel@lists.proxmox.com <pve-devel@lists.proxmox.com>,
s.hanreich@proxmox.com <s.hanreich@proxmox.com>
Objet: Re: [pve-devel] [PATCH v4 cluster/network/manager/qemu-
server/container/docs 00/33] Add support for DHCP servers to SDN
Date: 17/11/2023 17:09:51


> > I've checked the documentation and it seems to support it (in our
case
> > at least, since we have direct connection - correct me if I am
wrong):

> > From [1], the documentation for dhcp-host:

> > " Note that in IPv6 DHCP, the hardware address may not be
> > available,
> > though it normally is for direct-connected clients, or clients
> > using
> > DHCP relays which support RFC 6939. "

> > Maybe the issue here are the respective fwbr interfaces inbetween?

mmmn, it seem that openstack is doing it with mac reservation

https://bugs.launchpad.net/neutron/+bug/1861032


but I think that the current format for ipv6 address in ether is wrong.
I'll do tests this weekend.

(I using dual stack ipv4/ipv6 in production, so I really need to get
this works ^_^ )


_______________________________________________
pve-devel mailing list
pve-devel@lists.proxmox.com
https://lists.proxmox.com/cgi-bin/mailman/listinfo/pve-devel



^ permalink raw reply	[flat|nested] 66+ messages in thread

* Re: [pve-devel] [PATCH v4 pve-manager 20/33] sdn: ipam: add ipam panel
  2023-11-17 15:15     ` Stefan Hanreich
@ 2023-11-18 14:25       ` DERUMIER, Alexandre
  0 siblings, 0 replies; 66+ messages in thread
From: DERUMIER, Alexandre @ 2023-11-18 14:25 UTC (permalink / raw)
  To: pve-devel

On 11/17/23 16:04, DERUMIER, Alexandre wrote:
> I wonder if this panel could be integrated in zone panel (accessible
> from the tree).

>>I fear that this might overload the panel a bit.


I was think about a new menu
> as It's not related to the sdn configuration itself. (and don't need
> sdn reload)
> 
> I think yhis could allow to give permissions to user to manage ips in
> the zone, without need to access to datacenter panel
>>I agree with this general sentiment though I'm not sure whether
>>adding
>>it to Zone might be a good solution. Doesn't the user then still
>>require
>>access to the Datacenter panel (since Zone is included there as
well).
>>

Well, no. that's my point, we could give user access to zone (from the
main tree,  not the datacenter panel).
Currently it's only use to manage permissions on the vnets in the zone

>>Also another point was not adding any specific new permissions for
now.

But, yes, no problem. We can check that later! 


(I'm also look to make it working with other ipam too)




^ permalink raw reply	[flat|nested] 66+ messages in thread

* Re: [pve-devel] [PATCH v4 cluster/network/manager/qemu-server/container/docs 00/33] Add support for DHCP servers to SDN
  2023-11-17 11:39 [pve-devel] [PATCH v4 cluster/network/manager/qemu-server/container/docs 00/33] Add support for DHCP servers to SDN Stefan Hanreich
                   ` (33 preceding siblings ...)
  2023-11-17 15:47 ` [pve-devel] [PATCH v4 cluster/network/manager/qemu-server/container/docs 00/33] Add support for DHCP servers to SDN DERUMIER, Alexandre
@ 2023-11-18 14:38 ` DERUMIER, Alexandre
  2023-11-20 16:42 ` Thomas Lamprecht
  35 siblings, 0 replies; 66+ messages in thread
From: DERUMIER, Alexandre @ 2023-11-18 14:38 UTC (permalink / raw)
  To: pve-devel

>>* dnsmasq and IPv6 (and DHCP in general) do not really play well
>>together,
>>    so using subnets with IPv6 configured is wonky

It should be fixed my last patches.
The default gw with RA is still buggy for me.


I wonder if after 8.1 release, we shouldn't also implement static
configuration (through cloudinit for qemu  or directly with lxc).

Personnaly, I'm running vms with multiple ipv6 nics, with differents
static routes, and I'm really not sure It's possible to implement it
with RA.


As ipam/dhcp methods are now splitted, it should be easy to implement.
Maybe implement slaac for ipv6 too.


I think this should be defined at vnet or zone level
(static|dhcp|slaac) to avoid ips conflict.  

Then, the currently lxc gui (static/slaac/dhcp) or qemu cloudinit, the
options should be greyout. (and inherit/forced from the vnet/zone
configuration)


This avoid to have 1 vm configured with static, 1 other with slaac and
1 other with dhcp with potential conflict.




^ permalink raw reply	[flat|nested] 66+ messages in thread

* Re: [pve-devel] [PATCH v4 pve-network 10/33] api: add endpoints for managing PVE IPAM
  2023-11-17 11:39 ` [pve-devel] [PATCH v4 pve-network 10/33] api: add endpoints for managing PVE IPAM Stefan Hanreich
@ 2023-11-18 16:27   ` Thomas Lamprecht
  2023-11-20 10:55     ` Stefan Hanreich
  0 siblings, 1 reply; 66+ messages in thread
From: Thomas Lamprecht @ 2023-11-18 16:27 UTC (permalink / raw)
  To: Proxmox VE development discussion, Stefan Hanreich

Am 17/11/2023 um 12:39 schrieb Stefan Hanreich:
> Signed-off-by: Stefan Hanreich <s.hanreich@proxmox.com>
> ---
>  src/PVE/API2/Network/SDN.pm       |   6 +
>  src/PVE/API2/Network/SDN/Ipam.pm  | 221 ++++++++++++++++++++++++++++++
>  src/PVE/API2/Network/SDN/Makefile |   2 +-
>  3 files changed, 228 insertions(+), 1 deletion(-)
>  create mode 100644 src/PVE/API2/Network/SDN/Ipam.pm
> 
> diff --git a/src/PVE/API2/Network/SDN.pm b/src/PVE/API2/Network/SDN.pm
> index d216e48..551afcf 100644
> --- a/src/PVE/API2/Network/SDN.pm
> +++ b/src/PVE/API2/Network/SDN.pm
> @@ -15,6 +15,7 @@ use PVE::Network::SDN;
>  use PVE::API2::Network::SDN::Controllers;
>  use PVE::API2::Network::SDN::Vnets;
>  use PVE::API2::Network::SDN::Zones;
> +use PVE::API2::Network::SDN::Ipam;
>  use PVE::API2::Network::SDN::Ipams;

what's the deal with Ipam vs. Ipams?

I did not looked to closely into it but it seems like the existing Ipams, plural,
is for managing ipam plugins and Ipam, singular, here is for getting the current
state? That should really be better encoded in poth perl module names and api
endpoint paths, and is possibly also a smell about the choosen API path 
hierarchy.

Now, I know we're on a tight schedule here if this should make the next release,
but I cannot just wave _everything_ through, albeit I trust Alexandre's testing big
time, so that helps.

I can do some re-factoring myself, but I'd like to not find out such details on
my own (where's the commit message...? If one adds a module besides the exact same
module/api-endpoint name just differing in singular/plural, this really needs to
be explained somehwere...


>  use PVE::API2::Network::SDN::Dns;
>  
> @@ -35,6 +36,11 @@ __PACKAGE__->register_method ({
>      path => 'controllers',
>  });
>  
> +__PACKAGE__->register_method ({
> +    subclass => "PVE::API2::Network::SDN::Ipam",
> +    path => 'ipam',
> +});
> +
>  __PACKAGE__->register_method ({
>      subclass => "PVE::API2::Network::SDN::Ipams",
>      path => 'ipams',
> diff --git a/src/PVE/API2/Network/SDN/Ipam.pm b/src/PVE/API2/Network/SDN/Ipam.pm
> new file mode 100644
> index 0000000..e71ca7d
> --- /dev/null
> +++ b/src/PVE/API2/Network/SDN/Ipam.pm
> @@ -0,0 +1,221 @@
> +package PVE::API2::Network::SDN::Ipam;
> +
> +use strict;
> +use warnings;
> +
> +use PVE::Tools qw(extract_param);
> +use PVE::Cluster qw(cfs_read_file cfs_write_file);
> +
> +use PVE::Network::SDN;
> +use PVE::Network::SDN::Dhcp;
> +use PVE::Network::SDN::Vnets;
> +use PVE::Network::SDN::Ipams::Plugin;
> +
> +use PVE::JSONSchema qw(get_standard_option);
> +use PVE::RPCEnvironment;
> +
> +use PVE::RESTHandler;
> +
> +use base qw(PVE::RESTHandler);
> +
> +__PACKAGE__->register_method ({
> +    name => 'ipamindex',
> +    path => '',
> +    method => 'GET',
> +    description => 'List PVE IPAM Entries',
> +    protected => 1,
> +    permissions => {
> +	description => "Only list entries where you have 'SDN.Audit' or 'SDN.Allocate' permissions on '/sdn/zones/<zone>/<vnet>'",
> +	user => 'all',
> +    },
> +    parameters => {
> +	additionalProperties => 0,
> +    },
> +    returns => {
> +	type => 'array',
> +    },

any index should have a "links" definition here, otherwise api docs and browser won't
be complete and it's just not nice.

Also wondering, as the other sub-paths you registeter here have three template
components in the API path, but here you only got one index, shouldn't that be
either split over three levels (a bit of a nuisances but mostly boiler plate
code) or be a single endpoint the the actual thing passed as parameter (i.e.,
not part of the URL)





^ permalink raw reply	[flat|nested] 66+ messages in thread

* Re: [pve-devel] [PATCH v4 pve-network 10/33] api: add endpoints for managing PVE IPAM
  2023-11-18 16:27   ` Thomas Lamprecht
@ 2023-11-20 10:55     ` Stefan Hanreich
  2023-11-20 12:28       ` DERUMIER, Alexandre
  0 siblings, 1 reply; 66+ messages in thread
From: Stefan Hanreich @ 2023-11-20 10:55 UTC (permalink / raw)
  To: Thomas Lamprecht, Proxmox VE development discussion



On 11/18/23 17:27, Thomas Lamprecht wrote:
> what's the deal with Ipam vs. Ipams?
> 
> I did not looked to closely into it but it seems like the existing Ipams, plural,
> is for managing ipam plugins and Ipam, singular, here is for getting the current
> state? That should really be better encoded in poth perl module names and api
> endpoint paths, and is possibly also a smell about the choosen API path 
> hierarchy.
> 
> Now, I know we're on a tight schedule here if this should make the next release,
> but I cannot just wave _everything_ through, albeit I trust Alexandre's testing big
> time, so that helps.
> 
> I can do some re-factoring myself, but I'd like to not find out such details on
> my own (where's the commit message...? If one adds a module besides the exact same
> module/api-endpoint name just differing in singular/plural, this really needs to
> be explained somehwere...

All very valid points. One thing I have also considered was /ipams/pve
as a prefix - maybe that one is better suited? What do you think? It
shouldn't be too much of a hassle to move that around.

> any index should have a "links" definition here, otherwise api docs and browser won't
> be complete and it's just not nice.

will look into adding that!

> Also wondering, as the other sub-paths you registeter here have three template
> components in the API path, but here you only got one index, shouldn't that be
> either split over three levels (a bit of a nuisances but mostly boiler plate
> code) or be a single endpoint the the actual thing passed as parameter (i.e.,
> not part of the URL)

I was under the impression that DELETE should not include a request body
(at least they're not interoperable according to RFC 9110). I thought I
tried sending a DELETE with a request body to our API, but it didn't get
parsed. After looking through our API docs though I found DELETE
endpoints with optional parameters, so I don't know what went wrong there.

Anyway, that was the main reason why I designed the path the way it is,
since I needed to pass the parameters somehow. The other endpoints were
designed to have the same URL in order to be uniform.

I'll look again into this, maybe POST / PUT / DELETE
`/ipams/pve/mapping` or `/ipams/pve/ip` would be a good alternative
here? We need to move away from MAC addresses as a unique identifier
anyway (since with dual-stack there can be multiple IP addresses for the
same MAC address) so I would need to adjust those endpoints anyway to
work on IP/MAC pairings).

So the endpoints would then look like this:

GET /ipams/pve

POST /ipams/pve/ip
PUT /ipams/pve/ip
DELETE /ipams/pve/ip




^ permalink raw reply	[flat|nested] 66+ messages in thread

* Re: [pve-devel] [PATCH v4 pve-network 10/33] api: add endpoints for managing PVE IPAM
  2023-11-20 10:55     ` Stefan Hanreich
@ 2023-11-20 12:28       ` DERUMIER, Alexandre
  2023-11-20 12:34         ` Stefan Hanreich
  0 siblings, 1 reply; 66+ messages in thread
From: DERUMIER, Alexandre @ 2023-11-20 12:28 UTC (permalink / raw)
  To: pve-devel, t.lamprecht, s.hanreich

Hi,

>>I'll look again into this, maybe POST / PUT / DELETE
>>`/ipams/pve/mapping` or `/ipams/pve/ip` would be a good alternative
>>here? We need to move away from MAC addresses as a unique identifier
>>anyway (since with dual-stack there can be multiple IP addresses for
>>the same MAC address) so I would need to adjust those endpoints
>>anyway to
>>work on IP/MAC pairings).

>>So the endpoints would then look like this:
>>
>>GET /ipams/pve
>>
>>POST /ipams/pve/ip
>>PUT /ipams/pve/ip
>>DELETE /ipams/pve/ip


You should use plugin name in path    ,for me it should work with any
ipam plugin


so, simply : "/ipams/ip"






Another thing, it's theses api are not really calling ipam method
directly.

for example:


"
__PACKAGE__->register_method ({
    name => 'dhcpcreate',
    path => '{vnet}/{mac}',
...
        my $vnet = extract_param($param, 'vnet');
        my $mac = extract_param($param, 'mac');
        my $ip = extract_param($param, 'ip');

        PVE::Network::SDN::Vnets::add_ip($vnet, $ip, '', $mac, undef);
"

and Vnets::add_ip is calling

      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);


and Subnets::add_ip is registering dns + register ip in ipam plugin






So, I think that theses should be subpath of subnets

/subnets/<subnetid/ips


>>
>>GET /subnets/<subnetid/ips
>>
>>POST /subnets/<subnetid>/ips/<ip>
>>PUT /subnets/<subnetid>/ips/<ip>
>>DELETE /subnets/<subnetid/ips/<ip>



^ permalink raw reply	[flat|nested] 66+ messages in thread

* Re: [pve-devel] [PATCH v4 pve-network 10/33] api: add endpoints for managing PVE IPAM
  2023-11-20 12:28       ` DERUMIER, Alexandre
@ 2023-11-20 12:34         ` Stefan Hanreich
  2023-11-20 12:50           ` Stefan Hanreich
  2023-11-20 16:25           ` DERUMIER, Alexandre
  0 siblings, 2 replies; 66+ messages in thread
From: Stefan Hanreich @ 2023-11-20 12:34 UTC (permalink / raw)
  To: DERUMIER, Alexandre, pve-devel, t.lamprecht

On 11/20/23 13:28, DERUMIER, Alexandre wrote:
> Hi,
> 
>>> I'll look again into this, maybe POST / PUT / DELETE
>>> `/ipams/pve/mapping` or `/ipams/pve/ip` would be a good alternative
>>> here? We need to move away from MAC addresses as a unique identifier
>>> anyway (since with dual-stack there can be multiple IP addresses for
>>> the same MAC address) so I would need to adjust those endpoints
>>> anyway to
>>> work on IP/MAC pairings).
> 
>>> So the endpoints would then look like this:
>>>
>>> GET /ipams/pve
>>>
>>> POST /ipams/pve/ip
>>> PUT /ipams/pve/ip
>>> DELETE /ipams/pve/ip
> 
> 
> You should use plugin name in path    ,for me it should work with any
> ipam plugin
> 
> 
> so, simply : "/ipams/ip"

Not sure about this, since the endpoint returns the state of the PVE
IPAM and never returns the state of Netbox IPAM, for instance. Since if
you want to inspect that state you would use the Netbox API / Web UI.
For that reason it would make sense for me to use pve in the API path.

> So, I think that theses should be subpath of subnets
> 
> /subnets/<subnetid/ips

This sounds like a good idea - and is probably better than what I
proposed. I'll quickly talk with Thomas about this.




^ permalink raw reply	[flat|nested] 66+ messages in thread

* Re: [pve-devel] [PATCH v4 pve-network 10/33] api: add endpoints for managing PVE IPAM
  2023-11-20 12:34         ` Stefan Hanreich
@ 2023-11-20 12:50           ` Stefan Hanreich
  2023-11-20 16:25           ` DERUMIER, Alexandre
  1 sibling, 0 replies; 66+ messages in thread
From: Stefan Hanreich @ 2023-11-20 12:50 UTC (permalink / raw)
  To: DERUMIER, Alexandre, pve-devel, t.lamprecht



On 11/20/23 13:34, Stefan Hanreich wrote:
> Not sure about this, since the endpoint returns the state of the PVE
> IPAM and never returns the state of Netbox IPAM, for instance. Since if
> you want to inspect that state you would use the Netbox API / Web UI.
> For that reason it would make sense for me to use pve in the API path.
> 
>> So, I think that theses should be subpath of subnets
>>
>> /subnets/<subnetid/ips
> 
> This sounds like a good idea - and is probably better than what I
> proposed. I'll quickly talk with Thomas about this.

What would you think about adding it to /vnets instead of subnets?

Since the API endpoints are calling the VNets functions - which are
mostly calling the Subnet-Functions, yes - but they require less
parameters and are therefore easier to handle because we don't have to
send too much (implicitly available) information to/from the frontend.




^ permalink raw reply	[flat|nested] 66+ messages in thread

* Re: [pve-devel] [PATCH v4 pve-manager 19/33] sdn: subnet: add panel for editing dhcp ranges
  2023-11-17 11:39 ` [pve-devel] [PATCH v4 pve-manager 19/33] sdn: subnet: add panel for editing dhcp ranges Stefan Hanreich
@ 2023-11-20 13:20   ` Dominik Csapak
  0 siblings, 0 replies; 66+ messages in thread
From: Dominik Csapak @ 2023-11-20 13:20 UTC (permalink / raw)
  To: Proxmox VE development discussion, Stefan Hanreich

hi, a few issues here:

high level:

adding/modifying/deleting dhcp ranges does not trigger form 'isDirty'
check properly, leading to me unable to add dhcp ranges
to an existing subnet without also changing something else on
the first tab (there are probably some 'checkChange' triggers missing?)

also i can send invalid data because the 'getErrors' function is not implemented
i don't think we should be able to do this

also i'd show the dhcp ranges in the grid too, even if it's just
a hidden by default

remaining comments inline:

On 11/17/23 12:39, Stefan Hanreich wrote:
> Signed-off-by: Stefan Hanreich <s.hanreich@proxmox.com>
> ---
>   www/manager6/Makefile          |   1 +
>   www/manager6/sdn/SubnetEdit.js | 160 ++++++++++++++++++++++++++++++++-
>   2 files changed, 160 insertions(+), 1 deletion(-)
> 
> diff --git a/www/manager6/Makefile b/www/manager6/Makefile
> index dccd2ba1c..093452cd7 100644
> --- a/www/manager6/Makefile
> +++ b/www/manager6/Makefile
> @@ -274,6 +274,7 @@ JSSRC= 							\
>   	sdn/ZoneContentView.js				\
>   	sdn/ZoneContentPanel.js				\
>   	sdn/ZoneView.js					\
> +	sdn/IpamEdit.js					\
>   	sdn/OptionsPanel.js				\
>   	sdn/controllers/Base.js				\
>   	sdn/controllers/EvpnEdit.js			\
> diff --git a/www/manager6/sdn/SubnetEdit.js b/www/manager6/sdn/SubnetEdit.js
> index b9825d2a3..4fe16ab92 100644
> --- a/www/manager6/sdn/SubnetEdit.js
> +++ b/www/manager6/sdn/SubnetEdit.js
> @@ -56,6 +56,147 @@ Ext.define('PVE.sdn.SubnetInputPanel', {
>       ],
>   });
>   
> +Ext.define('PVE.sdn.SubnetDhcpRangePanel', {
> +    extend: 'Ext.form.FieldContainer',
> +    mixins: ['Ext.form.field.Field'],
> +
> +    initComponent: function() {
> +	let me = this;
> +
> +	me.callParent();
> +	me.initField();
> +    },
> +
> +    getValue: function() {
> +	let me = this;
> +	let store = me.lookup('grid').getStore();
> +
> +	let data = [];
> +
> +	store.getData()
> +	    .each((item) =>
> +		data.push(`start-address=${item.data['start-address']},end-address=${item.data['end-address']}`),
> +	    );

i'd not indent the 'each', because then you have one level less for the 'data.push'

> +
> +	return data;
> +    },
> +
> +    getSubmitData: function() {
> +	let me = this;
> +
> +	let data = {};
> +	let value = me.getValue();
> +
> +	if (value.length) {
> +	    data[me.getName()] = value;
> +	}
> +
> +	return data;
> +    },
> +
> +    setValue: function(dhcpRanges) {
> +	let me = this;
> +	let store = me.lookup('grid').getStore();
> +	store.setData(dhcpRanges);
> +    },

not sure if it's because of this, but it seems not to reset properly
the field always becomes empty but still has the reset enabled

> +
> +    getErrors: function() {
> +	let me = this;
> +        let errors = [];
> +
> +	return errors;
> +    },

please implement this (even if rudimentary) so users cannot send
invalid values

> +
> +    controller: {
> +	xclass: 'Ext.app.ViewController',
> +
> +	addRange: function() {
> +	    let me = this;
> +	    me.lookup('grid').getStore().add({});


i guess here would be a good place to add a 'me.getView().checkChange()'


> +	},
> +
> +	removeRange: function(field) {
> +	    let me = this;
> +	    let record = field.getWidgetRecord();
> +
> +	    me.lookup('grid').getStore().remove(record);


here too

> +	},
> +
> +	onValueChange: function(field, value) {
> +	    let me = this;
> +	    let record = field.getWidgetRecord();
> +	    let column = field.getWidgetColumn();
> +
> +	    record.set(column.dataIndex, value);
> +	    record.commit();


and here too


> +	},
> +
> +	control: {
> +	    'grid button': {
> +		click: 'removeRange',
> +	    },
> +	    'field': {
> +		change: 'onValueChange',
> +	    },
> +	},
> +    },
> +
> +    items: [
> +	{
> +	    xtype: 'grid',
> +	    reference: 'grid',
> +	    scrollable: true,
> +	    store: {
> +		fields: ['start-address', 'end-address'],
> +	    },
> +	    columns: [
> +		{
> +		    text: gettext('Start Address'),
> +		    xtype: 'widgetcolumn',
> +		    dataIndex: 'start-address',
> +		    flex: 1,
> +		    widget: {
> +			xtype: 'textfield',
> +			vtype: 'IP64Address',
> +		    },
> +		},
> +		{
> +		    text: gettext('End Address'),
> +		    xtype: 'widgetcolumn',
> +		    dataIndex: 'end-address',
> +		    flex: 1,
> +		    widget: {
> +			xtype: 'textfield',
> +			vtype: 'IP64Address',
> +		    },
> +		},
> +		{
> +		    xtype: 'widgetcolumn',
> +		    width: 40,
> +		    widget: {
> +			xtype: 'button',
> +			iconCls: 'fa fa-trash-o',
> +		    },
> +		},
> +	    ],
> +	},
> +	{
> +	    xtype: 'container',
> +	    layout: {
> +		type: 'hbox',
> +	    },
> +	    items: [
> +		{
> +		    xtype: 'button',
> +		    text: gettext('Add'),
> +		    iconCls: 'fa fa-plus-circle',
> +		    handler: 'addRange',
> +		},
> +	    ],
> +	},
> +    ],
> +});
> +
>   Ext.define('PVE.sdn.SubnetEdit', {
>       extend: 'Proxmox.window.Edit',
>   
> @@ -67,6 +208,8 @@ Ext.define('PVE.sdn.SubnetEdit', {
>   
>       base_url: undefined,
>   
> +    bodyPadding: 0,
> +
>       initComponent: function() {
>   	var me = this;
>   
> @@ -82,11 +225,22 @@ Ext.define('PVE.sdn.SubnetEdit', {
>   
>   	let ipanel = Ext.create('PVE.sdn.SubnetInputPanel', {
>   	    isCreate: me.isCreate,
> +	    title: gettext('General'),
> +	});
> +
> +	let dhcpPanel = Ext.create('PVE.sdn.SubnetDhcpRangePanel', {
> +	    isCreate: me.isCreate,
> +	    title: gettext('DHCP Ranges'),
> +	    name: 'dhcp-range',
>   	});
>   
>   	Ext.apply(me, {
>   	    items: [
> -		ipanel,
> +		{
> +		    xtype: 'tabpanel',
> +		    bodyPadding: 10,
> +		    items: [ipanel, dhcpPanel],
> +		},
>   	    ],
>   	});
>   
> @@ -97,6 +251,10 @@ Ext.define('PVE.sdn.SubnetEdit', {
>   		success: function(response, options) {
>   		    let values = response.result.data;
>   		    ipanel.setValues(values);
> +
> +		    if (values['dhcp-range']) {
> +			dhcpPanel.setValue(values['dhcp-range']);
> +		    }
>   		},
>   	    });
>   	}





^ permalink raw reply	[flat|nested] 66+ messages in thread

* Re: [pve-devel] [PATCH v4 pve-manager 20/33] sdn: ipam: add ipam panel
  2023-11-17 11:39 ` [pve-devel] [PATCH v4 pve-manager 20/33] sdn: ipam: add ipam panel Stefan Hanreich
  2023-11-17 15:04   ` DERUMIER, Alexandre
@ 2023-11-20 13:44   ` Dominik Csapak
  1 sibling, 0 replies; 66+ messages in thread
From: Dominik Csapak @ 2023-11-20 13:44 UTC (permalink / raw)
  To: Proxmox VE development discussion, Stefan Hanreich

generally looks ok, but i couldn't edit a manual dhcp
mapping with the error:

vmid: type check ('integer') failed - got ''

did not look too deep so i'm not sure where the empty vmid value
is coming from


also i like alexandres idea to put it into the main tree in the zone
panel as new panel that way we could even reduce the tree by one level
since we don't have to add the zone here

one minor thing inline (but that's not a blocker for me)

On 11/17/23 12:39, Stefan Hanreich wrote:
> Signed-off-by: Stefan Hanreich <s.hanreich@proxmox.com>
> ---
>   www/css/ext6-pve.css          |  22 ++-
>   www/manager6/Makefile         |   1 +
>   www/manager6/dc/Config.js     |  12 +-
>   www/manager6/sdn/IpamEdit.js  |  78 ++++++++++
>   www/manager6/tree/DhcpTree.js | 267 ++++++++++++++++++++++++++++++++++
>   5 files changed, 372 insertions(+), 8 deletions(-)
>   create mode 100644 www/manager6/sdn/IpamEdit.js
>   create mode 100644 www/manager6/tree/DhcpTree.js
> 
> diff --git a/www/css/ext6-pve.css b/www/css/ext6-pve.css
> index e18b173f5..091855356 100644
> --- a/www/css/ext6-pve.css
> +++ b/www/css/ext6-pve.css
> @@ -510,28 +510,38 @@ div.right-aligned {
>       content: ' ';
>   }
>   
> -.fa-sdn:before {
> +.x-fa-sdn-treelist:before {
>       width: 14px;
>       height: 14px;
>       position: absolute;
>       left: 1px;
>       top: 4px;
> +}
> +
> +.fa-sdn:before {
>       background-image:url(../images/icon-sdn.svg);
>       background-size: 14px 14px;
>       content: ' ';
>   }
>   
>   .fa-network-wired:before {
> -    width: 14px;
> -    height: 14px;
> -    position: absolute;
> -    left: 1px;
> -    top: 4px;
>       background-image:url(../images/icon-fa-network-wired.svg);
>       background-size: 14px 14px;
>       content: ' ';
>   }
>   
> +.x-fa-treepanel:before {
> +    width: 16px;
> +    height: 24px;
> +    display: block;
> +    background-repeat: no-repeat;
> +    background-position: center;
> +}
> +
> +.x-tree-icon-none {
> +    display: none;
> +}
> +
>   .x-treelist-row-over > * > .x-treelist-item-icon,
>   .x-treelist-row-over > * > .x-treelist-item-text{
>       color: #000;
> diff --git a/www/manager6/Makefile b/www/manager6/Makefile
> index 093452cd7..93b4ff155 100644
> --- a/www/manager6/Makefile
> +++ b/www/manager6/Makefile
> @@ -108,6 +108,7 @@ JSSRC= 							\
>   	tree/ResourceTree.js				\
>   	tree/SnapshotTree.js				\
>   	tree/ResourceMapTree.js				\
> +	tree/DhcpTree.js				\
>   	window/Backup.js				\
>   	window/BackupConfig.js				\
>   	window/BulkAction.js				\
> diff --git a/www/manager6/dc/Config.js b/www/manager6/dc/Config.js
> index 7d01da5fb..7c2b7b168 100644
> --- a/www/manager6/dc/Config.js
> +++ b/www/manager6/dc/Config.js
> @@ -185,7 +185,7 @@ Ext.define('PVE.dc.Config', {
>   		me.items.push({
>   		    xtype: 'pveSDNStatus',
>   		    title: gettext('SDN'),
> -		    iconCls: 'fa fa-sdn',
> +		    iconCls: 'fa fa-sdn x-fa-sdn-treelist',
>   		    hidden: true,
>   		    itemId: 'sdn',
>   		    expandedOnInit: true,
> @@ -203,7 +203,7 @@ Ext.define('PVE.dc.Config', {
>   		    groups: ['sdn'],
>   		    title: 'VNets',
>   		    hidden: true,
> -		    iconCls: 'fa fa-network-wired',
> +		    iconCls: 'fa fa-network-wired x-fa-sdn-treelist',
>   		    itemId: 'sdnvnet',
>   		},
>   		{
> @@ -213,6 +213,14 @@ Ext.define('PVE.dc.Config', {
>   		    hidden: true,
>   		    iconCls: 'fa fa-gear',
>   		    itemId: 'sdnoptions',
> +		},
> +		{
> +		    xtype: 'pveDhcpTree',
> +		    groups: ['sdn'],
> +		    title: gettext('IPAM'),
> +		    hidden: true,
> +		    iconCls: 'fa fa-map-signs',
> +		    itemId: 'sdnmappings',
>   		});
>   	    }
>   
> diff --git a/www/manager6/sdn/IpamEdit.js b/www/manager6/sdn/IpamEdit.js
> new file mode 100644
> index 000000000..18e22c592
> --- /dev/null
> +++ b/www/manager6/sdn/IpamEdit.js
> @@ -0,0 +1,78 @@
> +Ext.define('PVE.sdn.IpamEditInputPanel', {
> +    extend: 'Proxmox.panel.InputPanel',
> +    mixins: ['Proxmox.Mixin.CBind'],
> +
> +    isCreate: false,
> +
> +    onGetValues: function(values) {
> +	let me = this;
> +
> +	if (!values.vmid) {
> +	    delete values.vmid;
> +	}
> +
> +	return values;
> +    },
> +
> +    items: [
> +	{
> +	    xtype: 'pmxDisplayEditField',
> +	    name: 'vmid',
> +	    fieldLabel: gettext('VMID'),
> +	    allowBlank: false,
> +	    editable: false,
> +	    cbind: {
> +		hidden: '{isCreate}',
> +	    },
> +	},
> +	{
> +	    xtype: 'pmxDisplayEditField',
> +	    name: 'mac',
> +	    fieldLabel: gettext('MAC'),
> +	    allowBlank: false,
> +	    cbind: {
> +		editable: '{isCreate}',
> +	    },
> +	},
> +	{
> +	    xtype: 'proxmoxtextfield',
> +	    name: 'ip',
> +	    fieldLabel: gettext('IP'),
> +	    allowBlank: false,
> +	},
> +    ],
> +});
> +
> +Ext.define('PVE.sdn.IpamEdit', {
> +    extend: 'Proxmox.window.Edit',
> +
> +    subject: gettext('DHCP Mapping'),
> +    width: 350,
> +
> +    isCreate: false,
> +    mapping: {},
> +
> +    submitUrl: function(url, values) {
> +	return `${url}/${values.zone}/${values.vnet}/${values.mac}`;
> +    },
> +
> +    initComponent: function() {
> +	var me = this;
> +
> +	me.method = me.isCreate ? 'POST' : 'PUT';
> +
> +	let ipanel = Ext.create('PVE.sdn.IpamEditInputPanel', {
> +	    isCreate: me.isCreate,
> +	});
> +
> +	Ext.apply(me, {
> +	    items: [
> +		ipanel,
> +	    ],
> +	});
> +
> +	me.callParent();
> +
> +	ipanel.setValues(me.mapping);
> +    },
> +});
> diff --git a/www/manager6/tree/DhcpTree.js b/www/manager6/tree/DhcpTree.js
> new file mode 100644
> index 000000000..ca279c29a
> --- /dev/null
> +++ b/www/manager6/tree/DhcpTree.js
> @@ -0,0 +1,267 @@
> +Ext.define('PVE.sdn.DhcpTree', {
> +    extend: 'Ext.tree.Panel',
> +    xtype: 'pveDhcpTree',
> +
> +    layout: 'fit',
> +    rootVisible: false,
> +    animate: false,
> +
> +    store: {
> +	sorters: ['ip', 'name'],
> +    },
> +
> +    controller: {
> +	xclass: 'Ext.app.ViewController',
> +
> +	reload: function() {
> +	    let me = this;
> +
> +	    Proxmox.Utils.API2Request({
> +		url: `/cluster/sdn/ipam`,
> +		method: 'GET',
> +		success: function(response, opts) {
> +		    let root = {
> +			name: '__root',
> +			expanded: true,
> +			children: [],
> +		    };
> +
> +		    let zones = {};
> +		    let vnets = {};
> +		    let subnets = {};
> +
> +		    response.result.data.forEach((element) => {
> +			element.leaf = true;
> +
> +			if (!(element.zone in zones)) {
> +			    let zone = {
> +				name: element.zone,
> +				type: 'zone',
> +				iconCls: 'fa fa-th',
> +				expanded: true,
> +				children: [],
> +			    };
> +
> +			    zones[element.zone] = zone;
> +			    root.children.push(zone);
> +			}
> +
> +			if (!(element.vnet in vnets)) {
> +			    let vnet = {
> +				name: element.vnet,
> +				zone: element.zone,
> +				type: 'vnet',
> +				iconCls: 'fa fa-network-wired x-fa-treepanel',
> +				expanded: true,
> +				children: [],
> +			    };
> +
> +			    vnets[element.vnet] = vnet;
> +			    zones[element.zone].children.push(vnet);
> +			}
> +
> +			if (!(element.subnet in subnets)) {
> +			    let subnet = {
> +				name: element.subnet,
> +				zone: element.zone,
> +				vnet: element.vnet,
> +				type: 'subnet',
> +				iconCls: 'x-tree-icon-none',
> +				expanded: true,
> +				children: [],
> +			    };
> +
> +			    subnets[element.subnet] = subnet;
> +			    vnets[element.vnet].children.push(subnet);
> +			}
> +
> +			element.type = 'mapping';
> +			element.iconCls = 'x-tree-icon-none';
> +			subnets[element.subnet].children.push(element);
> +		    });
> +
> +		    me.getView().setRootNode(root);
> +		},
> +	    });
> +	},
> +
> +	init: function(view) {
> +	    let me = this;
> +	    me.reload();
> +	},
> +
> +	onDelete: function(table, rI, cI, item, e, { data }) {
> +	    let me = this;
> +	    let view = me.getView();
> +
> +	    Ext.Msg.show({
> +		title: gettext('Confirm'),
> +		icon: Ext.Msg.WARNING,
> +		message: Ext.String.format(gettext('Are you sure you want to remove DHCP mapping {0}'), `${data.mac} / ${data.ip}`),
> +		buttons: Ext.Msg.YESNO,
> +		defaultFocus: 'no',
> +		callback: function(btn) {
> +		    if (btn !== 'yes') {
> +		        return;
> +		    }
> +
> +		    Proxmox.Utils.API2Request({
> +			url: `/cluster/sdn/ipam/${data.zone}/${data.vnet}/${data.mac}`,
> +			method: 'DELETE',
> +			waitMsgTarget: view,
> +			failure: function(response, opts) {
> +			    Ext.Msg.alert(gettext('Error'), response.htmlStatus);
> +			},
> +			callback: me.reload.bind(me),
> +		    });
> +		},
> +	    });
> +	},
> +
> +	editAction: function(_grid, _rI, _cI, _item, _e, rec) {
> +	    this.edit(rec);
> +	},
> +
> +	editDblClick: function() {
> +	    let me = this;
> +
> +	    let view = me.getView();
> +	    let selection = view.getSelection();
> +
> +	    if (!selection || selection.length < 1) {
> +		return;
> +	    }
> +
> +	    me.edit(selection[0]);
> +	},
> +
> +	edit: function(rec) {
> +	    let me = this;
> +
> +	    if (rec.data.type === 'mapping' && !rec.data.gateway) {
> +		me.openEditWindow(rec.data);
> +	    }
> +	},
> +
> +	openEditWindow: function(data) {
> +	    let me = this;
> +
> +	    Ext.create('PVE.sdn.IpamEdit', {
> +		autoShow: true,
> +		mapping: data,
> +		url: `/cluster/sdn/ipam`,
> +		extraRequestParams: {
> +		    vmid: data.vmid,
> +		    mac: data.mac,
> +		    zone: data.zone,
> +		    vnet: data.vnet,
> +		},
> +		listeners: {
> +		    destroy: () => me.reload(),
> +		},
> +	    });
> +	},
> +    },
> +
> +    listeners: {
> +	itemdblclick: 'editDblClick',
> +    },
> +
> +    tbar: [
> +	{
> +	    xtype: 'proxmoxButton',
> +	    text: gettext('Reload'),
> +	    handler: 'reload',
> +	},
> +    ],
> +
> +    columns: [
> +	{
> +	    xtype: 'treecolumn',
> +	    text: gettext('Name / VMID'),
> +	    dataIndex: 'name',
> +	    width: 200,
> +	    renderer: function(value, meta, record) {
> +		if (record.get('gateway')) {
> +		    return gettext('Gateway');
> +		}
> +
> +		return record.get('name') ?? record.get('vmid') ?? ' ';
> +	    },
> +	},
> +	{
> +	    text: gettext('IP'),
> +	    dataIndex: 'ip',
> +	    width: 200,
> +	},
> +	{
> +	    text: gettext('MAC'),
> +	    dataIndex: 'mac',
> +	    width: 200,
> +	},
> +	{
> +	    text: gettext('Gateway'),
> +	    dataIndex: 'gateway',
> +	    width: 200,
> +	},
> +	{
> +	    header: gettext('Actions'),
> +	    xtype: 'actioncolumn',
> +	    dataIndex: 'text',
> +	    width: 150,
> +	    items: [
> +		{
> +		    handler: function(table, rI, cI, item, e, { data }) {
> +			let me = this;
> +
> +			Ext.create('PVE.sdn.IpamEdit', {
> +			    autoShow: true,
> +			    mapping: {},
> +			    url: `/cluster/sdn/ipam`,
> +			    isCreate: true,
> +			    extraRequestParams: {
> +				vnet: data.name,
> +				zone: data.zone,
> +			    },
> +			    listeners: {
> +				destroy: () => {
> +				    me.up('pveDhcpTree').controller.reload();
> +				},
> +			    },
> +			});
> +		    },

i'd put that function in the controller like the other handlers here

> +		    getTip: (v, m, rec) => gettext('Add'),
> +		    getClass: (v, m, { data }) => {
> +			if (data.type === 'vnet') {
> +			    return 'fa fa-plus-square';
> +			}
> +
> +			return 'pmx-hidden';
> +		    },
> +                },
> +		{
> +		    handler: 'editAction',
> +		    getTip: (v, m, rec) => gettext('Edit'),
> +		    getClass: (v, m, { data }) => {
> +			if (data.type === 'mapping' && !data.gateway) {
> +			    return 'fa fa-pencil fa-fw';
> +			}
> +
> +			return 'pmx-hidden';
> +		    },
> +                },
> +		{
> +		    handler: 'onDelete',
> +		    getTip: (v, m, rec) => gettext('Delete'),
> +		    getClass: (v, m, { data }) => {
> +			if (data.type === 'mapping' && !data.gateway) {
> +			    return 'fa critical fa-trash-o';
> +			}
> +
> +			return 'pmx-hidden';
> +		    },
> +                },
> +	    ],
> +	},
> +    ],
> +});





^ permalink raw reply	[flat|nested] 66+ messages in thread

* Re: [pve-devel] [PATCH v4 pve-network 10/33] api: add endpoints for managing PVE IPAM
  2023-11-20 12:34         ` Stefan Hanreich
  2023-11-20 12:50           ` Stefan Hanreich
@ 2023-11-20 16:25           ` DERUMIER, Alexandre
  1 sibling, 0 replies; 66+ messages in thread
From: DERUMIER, Alexandre @ 2023-11-20 16:25 UTC (permalink / raw)
  To: pve-devel, t.lamprecht, s.hanreich


On 11/20/23 13:28, DERUMIER, Alexandre wrote:
> Hi,
> 
> > > I'll look again into this, maybe POST / PUT / DELETE
> > > `/ipams/pve/mapping` or `/ipams/pve/ip` would be a good
> > > alternative
> > > here? We need to move away from MAC addresses as a unique
> > > identifier
> > > anyway (since with dual-stack there can be multiple IP addresses
> > > for
> > > the same MAC address) so I would need to adjust those endpoints
> > > anyway to
> > > work on IP/MAC pairings).
> 
> > > So the endpoints would then look like this:
> > > 
> > > GET /ipams/pve
> > > 
> > > POST /ipams/pve/ip
> > > PUT /ipams/pve/ip
> > > DELETE /ipams/pve/ip
> 
> 
> You should use plugin name in path    ,for me it should work with any
> ipam plugin
> 
> 
> so, simply : "/ipams/ip"

>>Not sure about this, since the endpoint returns the state of the PVE
>>IPAM and never returns the state of Netbox IPAM, for instance. Since
>>if
>>you want to inspect that state you would use the Netbox API / Web UI.
>>For that reason it would make sense for me to use pve in the API
path.


I think it could be great to be able to manage external ipam the
future, for example, if you only want to display only proxmox
subnets/ips from the proxmox gui, without need to give access to real
external ipam.




> So, I think that theses should be subpath of subnets
> 
> /subnets/<subnetid/ips

>>This sounds like a good idea - and is probably better than what I
>>proposed. I'll quickly talk with Thomas about this.
>>
>>What would you think about adding it to /vnets instead of subnets?
>>
>>Since the API endpoints are calling the VNets functions - which are
>>mostly calling the Subnet-Functions, yes - but they require less
>>parameters and are therefore easier to handle because we don't have
>>to
>>send too much (implicitly available) information to/from the
frontend.


Thinking about that, I wonder if it should be should be located in the
zone path
(as you can have duplicated ips in duplicate subnet in 2 zones)


listing
-------
/zone/<zoneid>/ips 

f you want to list all ips in 1 api call.  (if we put it on vnet or
subnet, we'll need multiple api call)


add|del|update an ip
-----------------------
/zone/<zoneid>/ips/<ip>

just loop on the subnets, the zoneid is in the subnetid



^ permalink raw reply	[flat|nested] 66+ messages in thread

* Re: [pve-devel] [PATCH v4 cluster/network/manager/qemu-server/container/docs 00/33] Add support for DHCP servers to SDN
  2023-11-17 11:39 [pve-devel] [PATCH v4 cluster/network/manager/qemu-server/container/docs 00/33] Add support for DHCP servers to SDN Stefan Hanreich
                   ` (34 preceding siblings ...)
  2023-11-18 14:38 ` DERUMIER, Alexandre
@ 2023-11-20 16:42 ` Thomas Lamprecht
  35 siblings, 0 replies; 66+ messages in thread
From: Thomas Lamprecht @ 2023-11-20 16:42 UTC (permalink / raw)
  To: Proxmox VE development discussion, Stefan Hanreich

Am 17/11/2023 um 12:39 schrieb Stefan Hanreich:
> pve-network:
> 
> Alexandre Derumier (3):
>   sdn: fix tests
>   sdn: fix subnets && netbox ipam tests
>   add add_dhcp_mapping
> 
> Stefan Hanreich (12):
>   sdn: preparations for DHCP plugin
>   subnet: add dhcp options
>   sdn: zone: add dhcp option
>   ipam: plugins: preparations for DHCP
>   subnet: vnet: refactor IPAM related methods
>   dhcp: add abstract class for DHCP plugins
>   sdn: dhcp: add dnsmasq plugin
>   sdn: dhcp: add helper for creating DHCP leases
>   api: add endpoints for managing PVE IPAM
>   api: subnet: add dhcp ranges
>   api: zone: add dhcp option
>   dhcp: regenerate config for DHCP plugins on applying configuration
> 
>  debian/control                                |   1 +
>  src/PVE/API2/Network/SDN.pm                   |   6 +
>  src/PVE/API2/Network/SDN/Ipam.pm              | 221 +++++++++++++++++
>  src/PVE/API2/Network/SDN/Makefile             |   2 +-
>  src/PVE/API2/Network/SDN/Subnets.pm           |   1 +
>  src/PVE/API2/Network/SDN/Zones.pm             |   1 +
>  src/PVE/Network/SDN.pm                        |   9 +-
>  src/PVE/Network/SDN/Dhcp.pm                   | 118 +++++++++
>  src/PVE/Network/SDN/Dhcp/Dnsmasq.pm           | 226 ++++++++++++++++++
>  src/PVE/Network/SDN/Dhcp/Makefile             |   8 +
>  src/PVE/Network/SDN/Dhcp/Plugin.pm            |  65 +++++
>  src/PVE/Network/SDN/Ipams.pm                  |  80 ++++++-
>  src/PVE/Network/SDN/Ipams/NetboxPlugin.pm     |  86 ++++++-
>  src/PVE/Network/SDN/Ipams/PVEPlugin.pm        |  85 ++++++-
>  src/PVE/Network/SDN/Ipams/PhpIpamPlugin.pm    |  29 +++
>  src/PVE/Network/SDN/Ipams/Plugin.pm           |  19 +-
>  src/PVE/Network/SDN/Makefile                  |   3 +-
>  src/PVE/Network/SDN/SubnetPlugin.pm           |  32 ++-
>  src/PVE/Network/SDN/Subnets.pm                |  98 +++++---
>  src/PVE/Network/SDN/Vnets.pm                  | 147 ++++++++----
>  src/PVE/Network/SDN/Zones.pm                  |  34 ++-
>  src/PVE/Network/SDN/Zones/SimplePlugin.pm     |   7 +-
>  src/test/ipams/netbox/expected.add_ip         |   2 +-
>  .../ipams/netbox/expected.add_ip_notgateway   |   2 +-
>  .../ipams/netbox/expected.add_next_freeip     |   2 +-
>  src/test/ipams/netbox/expected.update_ip      |   2 +-
>  src/test/run_test_subnets.pl                  |  16 +-
>  src/test/run_test_vnets.pl                    |   4 +-
>  28 files changed, 1181 insertions(+), 125 deletions(-)
>  create mode 100644 src/PVE/API2/Network/SDN/Ipam.pm
>  create mode 100644 src/PVE/Network/SDN/Dhcp.pm
>  create mode 100644 src/PVE/Network/SDN/Dhcp/Dnsmasq.pm
>  create mode 100644 src/PVE/Network/SDN/Dhcp/Makefile
>  create mode 100644 src/PVE/Network/SDN/Dhcp/Plugin.pm
> 

applied above pve-network part




^ permalink raw reply	[flat|nested] 66+ messages in thread

* Re: [pve-devel] [PATCH v4 cluster/network/manager/qemu-server/container/docs 00/33] Add support for DHCP servers to SDN
  2023-11-17 15:47 ` [pve-devel] [PATCH v4 cluster/network/manager/qemu-server/container/docs 00/33] Add support for DHCP servers to SDN DERUMIER, Alexandre
  2023-11-17 16:05   ` Stefan Hanreich
@ 2023-11-21 11:23   ` Stefan Lendl
  2023-11-21 13:02     ` DERUMIER, Alexandre
                       ` (2 more replies)
  1 sibling, 3 replies; 66+ messages in thread
From: Stefan Lendl @ 2023-11-21 11:23 UTC (permalink / raw)
  To: DERUMIER, Alexandre, pve-devel

"DERUMIER, Alexandre" <alexandre.derumier@groupe-cyllene.com> writes:

>>>  * dnsmasq and IPv6 (and DHCP in general) do not really play well
>>>together,
>>>    so using subnets with IPv6 configured is wonky
>
> I didn't have tested yet, but it's seem that dnsmasq only support old
> classic duid reservation and not mac ?
>

I tested the DHCPv6 setup now and unfortunately it does not work fully.

I have a VNet with both IPv4 and IPv6 subnets.
The IPAM part works correctly, registering both IPv4 and IPv6.
In the VM I can get an IPv4 from the DHCP but IPv6 does not work.

I only get a /128 address but I configured a /64 subnet.
For dual stack as well as only IPv6. Here my setup for only IPv6:

subnet.cfg:
subnet: DHCPNAT3-dead:beef::-64
	vnet dhcpnat3
	dhcp-range start-address=dead:beef::100,end-address=dead:beef::200
	gateway dead:beef::1

Upon adding a VM, I get an IP [dead:beef::100]

ethers file looks correct according to your syntax with:

BC:24:11:24:5F:AD,[dead:beef::100]

In a VM (archiso) I run `dhclient -6 ens19`, and get the following IP
assigned:

dead:beef::100/128




^ permalink raw reply	[flat|nested] 66+ messages in thread

* Re: [pve-devel] [PATCH v4 cluster/network/manager/qemu-server/container/docs 00/33] Add support for DHCP servers to SDN
  2023-11-21 11:23   ` Stefan Lendl
@ 2023-11-21 13:02     ` DERUMIER, Alexandre
  2023-11-21 13:25     ` DERUMIER, Alexandre
  2023-11-21 13:28     ` DERUMIER, Alexandre
  2 siblings, 0 replies; 66+ messages in thread
From: DERUMIER, Alexandre @ 2023-11-21 13:02 UTC (permalink / raw)
  To: s.lendl, pve-devel

>>
>>I tested the DHCPv6 setup now and unfortunately it does not work
>>fully.
>>
>>I have a VNet with both IPv4 and IPv6 subnets.
>>The IPAM part works correctly, registering both IPv4 and IPv6.
>>In the VM I can get an IPv4 from the DHCP but IPv6 does not work.
>>
>>I only get a /128 address but I configured a /64 subnet.

Yes, this is expected with dhcpv6. It's always /128 mask, and all
traffic is forwarded through the local-link if you have a RA.


I have talked about this with other network admin, this is the way
how dhcpv6/RA is implemented.


It can't be use like dhcp4 to send classic routes. (it don't even sent
network mask, on the ip)

The only way is to use static configuration, or hack dhcp protocol,
sending dhcpv6 custom options + a dhclient script on the vm guest side,
parsing theses options and setting config statically.


with slaac, it should always give you a /64 , but this is the same
problem with RA.


RA has been invented for mobile/desktop client, forcing all traffic
through the local-link.
For server, to use true gateway, ipv6 should be set statically.


^ permalink raw reply	[flat|nested] 66+ messages in thread

* Re: [pve-devel] [PATCH v4 cluster/network/manager/qemu-server/container/docs 00/33] Add support for DHCP servers to SDN
  2023-11-21 11:23   ` Stefan Lendl
  2023-11-21 13:02     ` DERUMIER, Alexandre
@ 2023-11-21 13:25     ` DERUMIER, Alexandre
  2023-11-21 13:28     ` DERUMIER, Alexandre
  2 siblings, 0 replies; 66+ messages in thread
From: DERUMIER, Alexandre @ 2023-11-21 13:25 UTC (permalink / raw)
  To: s.lendl, pve-devel

see this old article: 

https://www.isc.org/blogs/routing-configuration-over-dhcpv6-2/

for the custom dhcp option + script on client side


^ permalink raw reply	[flat|nested] 66+ messages in thread

* Re: [pve-devel] [PATCH v4 cluster/network/manager/qemu-server/container/docs 00/33] Add support for DHCP servers to SDN
  2023-11-21 11:23   ` Stefan Lendl
  2023-11-21 13:02     ` DERUMIER, Alexandre
  2023-11-21 13:25     ` DERUMIER, Alexandre
@ 2023-11-21 13:28     ` DERUMIER, Alexandre
  2023-11-21 16:34       ` Stefan Lendl
  2 siblings, 1 reply; 66+ messages in thread
From: DERUMIER, Alexandre @ 2023-11-21 13:28 UTC (permalink / raw)
  To: s.lendl, pve-devel

Another example with custom dhcpv6 options + client script

https://serverfault.com/a/528425


^ permalink raw reply	[flat|nested] 66+ messages in thread

* [pve-devel] applied-series: [PATCH v4 pve-container 27/33] nic hotplug : add|del ips in ipam
  2023-11-17 11:40 ` [pve-devel] [PATCH v4 pve-container 27/33] nic hotplug : add|del ips in ipam Stefan Hanreich
@ 2023-11-21 13:47   ` Wolfgang Bumiller
  0 siblings, 0 replies; 66+ messages in thread
From: Wolfgang Bumiller @ 2023-11-21 13:47 UTC (permalink / raw)
  To: Stefan Hanreich; +Cc: pve-devel

applied container series with a small `$have_sdn` guard fixup




^ permalink raw reply	[flat|nested] 66+ messages in thread

* [pve-devel] applied-series: [PATCH v4 qemu-server 21/33] vmnic add|remove : add|del ip in ipam
  2023-11-17 11:39 ` [pve-devel] [PATCH v4 qemu-server 21/33] vmnic add|remove : add|del ip in ipam Stefan Hanreich
@ 2023-11-21 13:53   ` Wolfgang Bumiller
  0 siblings, 0 replies; 66+ messages in thread
From: Wolfgang Bumiller @ 2023-11-21 13:53 UTC (permalink / raw)
  To: Stefan Hanreich; +Cc: pve-devel

applied qemu-server series (also with a small $have_sdn guard fixup ;-)
)
thanks




^ permalink raw reply	[flat|nested] 66+ messages in thread

* Re: [pve-devel] [PATCH v4 cluster/network/manager/qemu-server/container/docs 00/33] Add support for DHCP servers to SDN
  2023-11-21 13:28     ` DERUMIER, Alexandre
@ 2023-11-21 16:34       ` Stefan Lendl
  2023-11-21 18:15         ` DERUMIER, Alexandre
  2023-11-22  8:06         ` DERUMIER, Alexandre
  0 siblings, 2 replies; 66+ messages in thread
From: Stefan Lendl @ 2023-11-21 16:34 UTC (permalink / raw)
  To: DERUMIER, Alexandre, pve-devel

"DERUMIER, Alexandre" <alexandre.derumier@groupe-cyllene.com> writes:

> Another example with custom dhcpv6 options + client script
>
> https://serverfault.com/a/528425

Thank you very much for clarifying. So the /128 is fine.

I also tried to ping6 dead:beef::1 to reach the gateway or from the host
to reach the VM and that did not work.  So I assume RA is not working
correctly then?




^ permalink raw reply	[flat|nested] 66+ messages in thread

* Re: [pve-devel] [PATCH v4 cluster/network/manager/qemu-server/container/docs 00/33] Add support for DHCP servers to SDN
  2023-11-21 16:34       ` Stefan Lendl
@ 2023-11-21 18:15         ` DERUMIER, Alexandre
  2023-11-22  8:06         ` DERUMIER, Alexandre
  1 sibling, 0 replies; 66+ messages in thread
From: DERUMIER, Alexandre @ 2023-11-21 18:15 UTC (permalink / raw)
  To: s.lendl, pve-devel


"DERUMIER, Alexandre" <alexandre.derumier@groupe-cyllene.com> writes:

> Another example with custom dhcpv6 options + client script
> 
> https://antiphishing.cetsi.fr/proxy/v3?i=Z09lbVhROVBaOHd3NjZZMVfTogSU
> UCLtvQYv_TI7-
> Qg&r=eVVJWkdic3NtZTdZV2pycd0wEJhcfg3n60sFMbDyx7fGbFFjGiZx-
> 8mpk_5ozxPQiZBWULDfan1Z0cBIdGk9Gw&f=UU9IMnpkdmJmSk9YOHVtc4QQq34JU-
> jw9SwzciT7i6aSKaghiIfZ2rZSqVj1KJWC&u=https%3A//serverfault.com/a/5284
> 25&k=eFRI

>>Thank you very much for clarifying. So the /128 is fine.

>>I also tried to ping6 dead:beef::1 to reach the gateway or from the
>>host
>>to reach the VM and that did not work.  So I assume RA is not working
>>correctly then?

Personnally, I can't get the RA feature of dnsmasq working.

I have done some tcpdump on vnet, I don't see any RA packet sent
regulary,  on RA response packet after an RS request.


I have done with with radvd daemon, and it's working out of the box...

Maybe it's missing something in dnsmasq config, but I don't have found
i t yet.....



^ permalink raw reply	[flat|nested] 66+ messages in thread

* [pve-devel] applied: [PATCH v4 pve-docs 33/33] sdn: dhcp: Add documentation for DHCP
  2023-11-17 11:40 ` [pve-devel] [PATCH v4 pve-docs 33/33] sdn: dhcp: Add documentation for DHCP Stefan Hanreich
@ 2023-11-21 19:03   ` Thomas Lamprecht
  0 siblings, 0 replies; 66+ messages in thread
From: Thomas Lamprecht @ 2023-11-21 19:03 UTC (permalink / raw)
  To: Proxmox VE development discussion, Stefan Hanreich

Am 17/11/2023 um 12:40 schrieb Stefan Hanreich:
> Signed-off-by: Stefan Hanreich <s.hanreich@proxmox.com>
> ---
>  pvesdn.adoc | 122 ++++++++++++++++++++++++++++++++++++++++++++++++++++
>  1 file changed, 122 insertions(+)
> 
>

applied, with some merge conflicts resolved, maybe you could also
recheck to ensure nothing is off, thanks!




^ permalink raw reply	[flat|nested] 66+ messages in thread

* [pve-devel] applied: [PATCH v4 pve-manager 17/33] sdn: regenerate DHCP config on reload
  2023-11-17 11:39 ` [pve-devel] [PATCH v4 pve-manager 17/33] sdn: regenerate DHCP config on reload Stefan Hanreich
@ 2023-11-21 21:15   ` Thomas Lamprecht
  0 siblings, 0 replies; 66+ messages in thread
From: Thomas Lamprecht @ 2023-11-21 21:15 UTC (permalink / raw)
  To: Proxmox VE development discussion, Stefan Hanreich

Am 17/11/2023 um 12:39 schrieb Stefan Hanreich:
> Signed-off-by: Stefan Hanreich <s.hanreich@proxmox.com>
> ---
>  PVE/API2/Network.pm | 1 +
>  1 file changed, 1 insertion(+)
> 
>

applied the manager ones, thanks!




^ permalink raw reply	[flat|nested] 66+ messages in thread

* Re: [pve-devel] [PATCH v4 cluster/network/manager/qemu-server/container/docs 00/33] Add support for DHCP servers to SDN
  2023-11-21 16:34       ` Stefan Lendl
  2023-11-21 18:15         ` DERUMIER, Alexandre
@ 2023-11-22  8:06         ` DERUMIER, Alexandre
  1 sibling, 0 replies; 66+ messages in thread
From: DERUMIER, Alexandre @ 2023-11-22  8:06 UTC (permalink / raw)
  To: s.lendl, pve-devel

Hi,
I just sent a patch to fix ipv6 RA.
(we need to remove listen-ip  from config, and use interface=    , to
get it work)


I correctly see the packet sent now:

Nov 22 08:55:13 formationkvm3 dnsmasq-dhcp[1222769]: IPv6 router
advertisement enabled
Nov 22 08:55:13 formationkvm3 dnsmasq[1222769]: cleared cache
Nov 22 08:55:13 formationkvm3 dnsmasq-dhcp[1222769]: read
/etc/dnsmasq.d/simpve/ethers
Nov 22 08:55:14 formationkvm3 dnsmasq-dhcp[1222769]: RTR-
ADVERT(vnetpve) 2a0a:1580:2000:6000::
Nov 22 08:55:26 formationkvm3 dnsmasq-dhcp[1222769]: RTR-
ADVERT(vnetpve) 2a0a:1580:2000:6000::
Nov 22 08:55:38 formationkvm3 dnsmasq-dhcp[1222769]: RTR-
ADVERT(vnetpve) 2a0a:1580:2000:6000::



testing with a vm,  got 2a0a:1580:2000:6000::11   (with /128 mask)

# ip addr
2: eth0@if409: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue
state UP group default qlen 1000
    link/ether 12:45:e3:2e:f0:bf brd ff:ff:ff:ff:ff:ff link-netnsid 0
    inet 192.168.2.14/24 brd 192.168.2.255 scope global eth0
       valid_lft forever preferred_lft forever
    inet6 2a0a:1580:2000:6000::11/128 scope global 
       valid_lft forever preferred_lft forever
    inet6 fe80::1045:e3ff:fe2e:f0bf/64 scope link 
       valid_lft forever preferred_lft forever

and default route from RA.  (via the local-link)


~# ip -6 route
::1 dev lo proto kernel metric 256 pref medium
2a0a:1580:2000:6000::11 dev eth0 proto kernel metric 256 pref medium
2a0a:1580:2000:6000::/64 dev eth0 proto kernel metric 256 pref medium
fe80::/64 dev eth0 proto kernel metric 256 pref medium
default via fe80::a0f5:fdff:feb3:f04a dev eth0 proto ra metric 1024
expires 1456sec hoplimit 64 pref medium


^ permalink raw reply	[flat|nested] 66+ messages in thread

end of thread, other threads:[~2023-11-22  8:06 UTC | newest]

Thread overview: 66+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2023-11-17 11:39 [pve-devel] [PATCH v4 cluster/network/manager/qemu-server/container/docs 00/33] Add support for DHCP servers to SDN Stefan Hanreich
2023-11-17 11:39 ` [pve-devel] [PATCH v4 pve-cluster 01/33] add priv/macs.db Stefan Hanreich
2023-11-17 13:54   ` [pve-devel] applied: " Thomas Lamprecht
2023-11-17 11:39 ` [pve-devel] [PATCH v4 pve-network 02/33] sdn: preparations for DHCP plugin Stefan Hanreich
2023-11-17 11:39 ` [pve-devel] [PATCH v4 pve-network 03/33] subnet: add dhcp options Stefan Hanreich
2023-11-17 11:39 ` [pve-devel] [PATCH v4 pve-network 04/33] sdn: zone: add dhcp option Stefan Hanreich
2023-11-17 11:39 ` [pve-devel] [PATCH v4 pve-network 05/33] ipam: plugins: preparations for DHCP Stefan Hanreich
2023-11-17 11:39 ` [pve-devel] [PATCH v4 pve-network 06/33] subnet: vnet: refactor IPAM related methods Stefan Hanreich
2023-11-17 14:13   ` Stefan Lendl
2023-11-17 15:12     ` Stefan Hanreich
2023-11-17 11:39 ` [pve-devel] [PATCH v4 pve-network 07/33] dhcp: add abstract class for DHCP plugins Stefan Hanreich
2023-11-17 11:39 ` [pve-devel] [PATCH v4 pve-network 08/33] sdn: dhcp: add dnsmasq plugin Stefan Hanreich
2023-11-17 11:39 ` [pve-devel] [PATCH v4 pve-network 09/33] sdn: dhcp: add helper for creating DHCP leases Stefan Hanreich
2023-11-17 11:39 ` [pve-devel] [PATCH v4 pve-network 10/33] api: add endpoints for managing PVE IPAM Stefan Hanreich
2023-11-18 16:27   ` Thomas Lamprecht
2023-11-20 10:55     ` Stefan Hanreich
2023-11-20 12:28       ` DERUMIER, Alexandre
2023-11-20 12:34         ` Stefan Hanreich
2023-11-20 12:50           ` Stefan Hanreich
2023-11-20 16:25           ` DERUMIER, Alexandre
2023-11-17 11:39 ` [pve-devel] [PATCH v4 pve-network 11/33] api: subnet: add dhcp ranges Stefan Hanreich
2023-11-17 11:39 ` [pve-devel] [PATCH v4 pve-network 12/33] api: zone: add dhcp option Stefan Hanreich
2023-11-17 11:39 ` [pve-devel] [PATCH v4 pve-network 13/33] dhcp: regenerate config for DHCP plugins on applying configuration Stefan Hanreich
2023-11-17 11:39 ` [pve-devel] [PATCH v4 pve-network 14/33] sdn: fix tests Stefan Hanreich
2023-11-17 11:39 ` [pve-devel] [PATCH v4 pve-network 15/33] sdn: fix subnets && netbox ipam tests Stefan Hanreich
2023-11-17 11:39 ` [pve-devel] [PATCH v4 pve-network 16/33] add add_dhcp_mapping Stefan Hanreich
2023-11-17 11:39 ` [pve-devel] [PATCH v4 pve-manager 17/33] sdn: regenerate DHCP config on reload Stefan Hanreich
2023-11-21 21:15   ` [pve-devel] applied: " Thomas Lamprecht
2023-11-17 11:39 ` [pve-devel] [PATCH v4 pve-manager 18/33] sdn: add DHCP option to Zone dialogue Stefan Hanreich
2023-11-17 11:39 ` [pve-devel] [PATCH v4 pve-manager 19/33] sdn: subnet: add panel for editing dhcp ranges Stefan Hanreich
2023-11-20 13:20   ` Dominik Csapak
2023-11-17 11:39 ` [pve-devel] [PATCH v4 pve-manager 20/33] sdn: ipam: add ipam panel Stefan Hanreich
2023-11-17 15:04   ` DERUMIER, Alexandre
2023-11-17 15:15     ` Stefan Hanreich
2023-11-18 14:25       ` DERUMIER, Alexandre
2023-11-20 13:44   ` Dominik Csapak
2023-11-17 11:39 ` [pve-devel] [PATCH v4 qemu-server 21/33] vmnic add|remove : add|del ip in ipam Stefan Hanreich
2023-11-21 13:53   ` [pve-devel] applied-series: " Wolfgang Bumiller
2023-11-17 11:40 ` [pve-devel] [PATCH v4 qemu-server 22/33] vm_start : vm-network-scripts: add_dhcp_reservation Stefan Hanreich
2023-11-17 11:40 ` [pve-devel] [PATCH v4 qemu-server 23/33] api2: create|restore|clone: add_free_ip Stefan Hanreich
2023-11-17 11:40 ` [pve-devel] [PATCH v4 qemu-server 24/33] vm_destroy: delete ip from ipam Stefan Hanreich
2023-11-17 11:40 ` [pve-devel] [PATCH v4 qemu-server 25/33] nic hotplug: add_dhcp_mapping Stefan Hanreich
2023-11-17 11:40 ` [pve-devel] [PATCH v4 qemu-server 26/33] nic online bridge/vlan change: link disconnect/reconnect Stefan Hanreich
2023-11-17 11:40 ` [pve-devel] [PATCH v4 pve-container 27/33] nic hotplug : add|del ips in ipam Stefan Hanreich
2023-11-21 13:47   ` [pve-devel] applied-series: " Wolfgang Bumiller
2023-11-17 11:40 ` [pve-devel] [PATCH v4 pve-container 28/33] vm_destroy: remove ips from ipam for all interfaces Stefan Hanreich
2023-11-17 11:40 ` [pve-devel] [PATCH v4 pve-container 29/33] vm_create|restore: create ips in ipam Stefan Hanreich
2023-11-17 11:40 ` [pve-devel] [PATCH v4 pve-container 30/33] vm_clone : create ips in ipams Stefan Hanreich
2023-11-17 11:40 ` [pve-devel] [PATCH v4 pve-container 31/33] vm_apply_pending: add|del ips from ipam for offline changes Stefan Hanreich
2023-11-17 11:40 ` [pve-devel] [PATCH v4 pve-container 32/33] lxc-pve-prestart-hook : add_dhcp_mapping Stefan Hanreich
2023-11-17 11:40 ` [pve-devel] [PATCH v4 pve-docs 33/33] sdn: dhcp: Add documentation for DHCP Stefan Hanreich
2023-11-21 19:03   ` [pve-devel] applied: " Thomas Lamprecht
2023-11-17 15:47 ` [pve-devel] [PATCH v4 cluster/network/manager/qemu-server/container/docs 00/33] Add support for DHCP servers to SDN DERUMIER, Alexandre
2023-11-17 16:05   ` Stefan Hanreich
2023-11-17 16:07     ` Stefan Hanreich
2023-11-17 16:09     ` DERUMIER, Alexandre
2023-11-17 20:44       ` DERUMIER, Alexandre
2023-11-21 11:23   ` Stefan Lendl
2023-11-21 13:02     ` DERUMIER, Alexandre
2023-11-21 13:25     ` DERUMIER, Alexandre
2023-11-21 13:28     ` DERUMIER, Alexandre
2023-11-21 16:34       ` Stefan Lendl
2023-11-21 18:15         ` DERUMIER, Alexandre
2023-11-22  8:06         ` DERUMIER, Alexandre
2023-11-18 14:38 ` DERUMIER, Alexandre
2023-11-20 16:42 ` Thomas Lamprecht

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
Service provided by Proxmox Server Solutions GmbH | Privacy | Legal