+ # fixme: howto pass gateway for that pool --dhcp-option='option:router,192.168.2.1'
+ # fixme: howto pass additional dhcp options + --dhcp-option='option:optionnumber,optionvalue' I'm thinking if restarting the dnsmasq daemon at each vm start is good ? (what happen if multiple vm stop/start at same time, can we loose some dhcp requests?) Maybe can we restart it only if ressources changes (ip pool, range). and use dhcp-script option to give leases without need to restart daemon ? ----- Mail original ----- De: "Dietmar Maurer" <diet...@proxmox.com> À: pve-devel@pve.proxmox.com Envoyé: Lundi 2 Septembre 2013 10:11:12 Objet: [pve-devel] [PATCH 2/2] setup DHCP server at vm_start Signed-off-by: Dietmar Maurer <diet...@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 _______________________________________________ pve-devel mailing list pve-devel@pve.proxmox.com http://pve.proxmox.com/cgi-bin/mailman/listinfo/pve-devel _______________________________________________ pve-devel mailing list pve-devel@pve.proxmox.com http://pve.proxmox.com/cgi-bin/mailman/listinfo/pve-devel