[pve-devel] [PATCH 2/2] setup DHCP server at vm_start

Dietmar Maurer dietmar at proxmox.com
Mon Sep 2 10:11:12 CEST 2013


Signed-off-by: Dietmar Maurer <dietmar at proxmox.com>
---
 PVE/DHCP.pm       |  170 +++++++++++++++++++++++++++++++++++++++++++++++++++++
 PVE/Makefile      |    1 +
 PVE/QemuServer.pm |    6 ++
 3 files changed, 177 insertions(+)
 create mode 100755 PVE/DHCP.pm

diff --git a/PVE/DHCP.pm b/PVE/DHCP.pm
new file mode 100755
index 0000000..ba307cb
--- /dev/null
+++ b/PVE/DHCP.pm
@@ -0,0 +1,170 @@
+package PVE::DHCP;
+
+use strict;
+use warnings;
+
+use Time::HiRes qw(usleep);
+use PVE::ProcFSTools;
+use PVE::INotify;
+use PVE::RPCEnvironment;
+use PVE::QemuServer;
+use PVE::Cluster qw(cfs_read_file cfs_write_file);
+use PVE::Resource;
+use Net::IP;
+
+use Data::Dumper;
+
+#fixme: this already exists in PVE::JSONSchema
+my $ipv4_mask_hash = {
+    '128.0.0.0' => 1,
+    '192.0.0.0' => 2,
+    '224.0.0.0' => 3,
+    '240.0.0.0' => 4,
+    '248.0.0.0' => 5,
+    '252.0.0.0' => 6,
+    '254.0.0.0' => 7,
+    '255.0.0.0' => 8,
+    '255.128.0.0' => 9,
+    '255.192.0.0' => 10,
+    '255.224.0.0' => 11,
+    '255.240.0.0' => 12,
+    '255.248.0.0' => 13,
+    '255.252.0.0' => 14,
+    '255.254.0.0' => 15,
+    '255.255.0.0' => 16,
+    '255.255.128.0' => 17,
+    '255.255.192.0' => 18,
+    '255.255.224.0' => 19,
+    '255.255.240.0' => 20,
+    '255.255.248.0' => 21,
+    '255.255.252.0' => 22,
+    '255.255.254.0' => 23,
+    '255.255.255.0' => 24,
+    '255.255.255.128' => 25,
+    '255.255.255.192' => 26,
+    '255.255.255.224' => 27,
+    '255.255.255.240' => 28,
+    '255.255.255.248' => 29,
+    '255.255.255.252' => 30
+};
+
+sub get_dhcp_ifaces {
+    my ($ifaces) = @_;
+
+    my $dhcp_ifaces = {};
+    foreach my $iface (keys %$ifaces) {
+	next if $iface eq 'lo';
+	my $d = $ifaces->{$iface};
+	next if $d->{method} ne 'static';
+	next if !$d->{address};
+	next if !$d->{netmask};
+	next if !$d->{gateway};
+
+	my $binip = Net::IP::ip_iptobin($d->{address}, 4);
+	my $binmask = Net::IP::ip_iptobin($d->{netmask}, 4);
+	my $network =  Net::IP::ip_bintoip($binip & $binmask, 4);
+	my $prefixlen = $ipv4_mask_hash->{$d->{netmask}} || next;
+
+	$d->{netobj} = Net::IP->new("$network/$prefixlen") || next;
+
+	$dhcp_ifaces->{$iface} = $d;
+    }
+
+    return $dhcp_ifaces;
+}
+
+my $dhcpd_conf_fn = "/tmp/pve-dhcpd.conf";
+my $dhcpd_pid_fn = "/tmp/pve-dhcpd.pid";
+
+sub restart_dnsmasq_daemon  {
+
+    # Note: there is no wqay to reload dnsmasq config (author 
+    # claims this is a security feature).
+    
+    if (my $pid = PVE::Tools::file_read_firstline($dhcpd_pid_fn)) {
+	# fixme: do it in a safer way! (dont kill wrong process)
+	kill 9, $pid;
+	unlink $dhcpd_pid_fn;
+	my $count = 0;
+	while (PVE::ProcFSTools::check_process_running($pid)) {
+	    usleep(100000);
+	    die "unable to stop dnsmasq (retry count == $count)\n"
+		if ++$count > 100;
+	}
+    }
+
+    my $cmd = ['dnsmasq', "--pid-file=$dhcpd_pid_fn", "--conf-file=$dhcpd_conf_fn"];
+    push @$cmd, "--log-queries"; # fixme: for debug
+    PVE::Tools::run_command($cmd);
+}
+
+sub setup_dhcpd {
+
+    my $rc = cfs_read_file('resource.cfg');
+    #print Dumper($rc);
+
+    my $ippools = {}; 
+    # detect what interfaces have ippools with dhcp enabled
+    foreach my $name (keys %{$rc->{ids}}) {
+	my $d = $rc->{ids}->{$name};
+	next if $d->{type} ne 'ippool';
+	next if !$d->{dhcp};
+	$d->{name} = $name;
+	$d->{iprangeobj} = Net::IP->new($d->{iprange}); 
+ 	push @{$ippools->{$d->{dhcp}}}, $d;
+    }
+
+    my $ifaces = PVE::INotify::read_file("interfaces");
+    
+    my $dhcp_ifaces = get_dhcp_ifaces($ifaces); # interface with address/netmask/gateway
+
+    #print Dumper($dhcp_ifaces);
+
+    my $raw = ''; # dnsmasq configuration
+    $raw .= "# autogenerate by Proxmox VE - Please no not edit this file\n\n";
+
+    $raw .= "port=0\n"; # disable DNS
+    $raw .= "except-interface=lo\n";
+    $raw .= "bind-interfaces\n";
+    $raw .= "dhcp-ignore=tag:!known\n";
+
+    my $vmlist = PVE::QemuServer::vzlist();
+    my $vmconfhash = {};
+    foreach my $vmid (keys %$vmlist) {
+	my $cfspath = PVE::QemuServer::cfs_config_path($vmid);
+	$vmconfhash->{$vmid} = PVE::Cluster::cfs_read_file($cfspath) || {};
+    }
+
+    foreach my $iface (keys %$dhcp_ifaces) {
+	my $ifdata = $dhcp_ifaces->{$iface};
+	next if !$ippools->{$iface};
+	foreach my $ippool (@{$ippools->{$iface}}) {
+	    $raw .= "\n# interface '$iface', IP pool '$ippool->{name}'\n\n";
+	    $raw .= "interface=$iface\n";
+	    $raw .= "listen-address=$ifdata->{address}\n";
+	    # fixme: howto pass gateway for that pool
+	    # fixme: howto pass additional dhcp options
+	    
+	    my $startip = $ippool->{iprangeobj}->ip();
+	    my $endip = $ippool->{iprangeobj}->last_ip();
+	    $raw .= "dhcp-range=interface:$iface,$startip,$endip,static,$ifdata->{netmask},8h\n";
+
+	    my $MAX_NETS = 32; #fixme
+	    foreach my $vmid (keys %$vmconfhash) {
+		my $conf = $vmconfhash->{$vmid};
+		for (my $i = 0; $i < $MAX_NETS; $i++) {
+		    next if !$conf->{"net$i"};
+		    my $d = PVE::QemuServer::parse_net($conf->{"net$i"});
+		    next if !$d || !$d->{ip} || !$d->{macaddr};
+		    if ($ippool->{iprangeobj}->overlaps(Net::IP->new($d->{ip})) == $IP_B_IN_A_OVERLAP) {
+			$raw .= "dhcp-host=$d->{macaddr},$d->{ip}\n"; 
+		    }
+		}
+	    }
+	}
+    }
+
+    PVE::Tools::file_set_contents($dhcpd_conf_fn, $raw);
+}
+
+1;
diff --git a/PVE/Makefile b/PVE/Makefile
index 232c881..fa55b8d 100644
--- a/PVE/Makefile
+++ b/PVE/Makefile
@@ -1,4 +1,5 @@
 PERLSOURCE = 			\
+	DHCP.pm			\
 	QemuServer.pm		\
 	QemuMigrate.pm		\
 	QMPClient.pm
diff --git a/PVE/QemuServer.pm b/PVE/QemuServer.pm
index 2a4f683..b7845c1 100644
--- a/PVE/QemuServer.pm
+++ b/PVE/QemuServer.pm
@@ -28,6 +28,8 @@ use PVE::INotify;
 use PVE::ProcFSTools;
 use PVE::QMPClient;
 use PVE::RPCEnvironment;
+use PVE::DHCP;
+
 use Time::HiRes qw(gettimeofday);
 
 my $cpuinfo = PVE::ProcFSTools::read_cpuinfo();
@@ -3061,6 +3063,10 @@ sub vm_start {
 	# set environment variable useful inside network script
 	$ENV{PVE_MIGRATED_FROM} = $migratedfrom if $migratedfrom;
 
+	# setup DHCP server
+	PVE::DHCP::setup_dhcpd();
+	PVE::DHCP::restart_dnsmasq_daemon();
+
 	my ($cmd, $vollist, $spice_port) = config_to_command($storecfg, $vmid, $conf, $defaults, $forcemachine);
 
 	my $migrate_port = 0;
-- 
1.7.10.4



More information about the pve-devel mailing list