[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