]>
git.proxmox.com Git - pve-network.git/blob - src/PVE/Network/SDN/Dhcp/Dnsmasq.pm
1 package PVE
::Network
::SDN
::Dhcp
::Dnsmasq
;
6 use base
qw(PVE::Network::SDN::Dhcp::Plugin);
9 use PVE
::Tools
qw(file_set_contents run_command lock_file);
14 use PVE
::RESTEnvironment
qw(log_warn);
16 my $DNSMASQ_CONFIG_ROOT = '/etc/dnsmasq.d';
17 my $DNSMASQ_DEFAULT_ROOT = '/etc/default';
18 my $DNSMASQ_LEASE_ROOT = '/var/lib/misc';
24 my sub assert_dnsmasq_installed
{
27 my $bin_path = "/usr/sbin/dnsmasq";
29 return if $noerr; # just ignore, e.g., in case zone doesn't use DHCP at all
30 log_warn
("please install the 'dnsmasq' package in order to use the DHCP feature!");
31 die "cannot reload with missing 'dnsmasq' package\n";
37 my ($class, $dhcpid, $macdb, $mac, $ip4, $ip6) = @_;
39 my $ethers_file = "$DNSMASQ_CONFIG_ROOT/$dhcpid/ethers";
40 my $ethers_tmp_file = "$ethers_file.tmp";
45 open(my $in, '<', $ethers_file) or die "Could not open file '$ethers_file' $!\n";
46 open(my $out, '>', $ethers_tmp_file) or die "Could not open file '$ethers_tmp_file' $!\n";
50 while (my $line = <$in>) {
52 my $parsed_ip4 = undef;
53 my $parsed_ip6 = undef;
54 my ($parsed_mac, $parsed_ip1, $parsed_ip2) = split(/,/, $line);
57 $parsed_ip4 = $parsed_ip1;
58 $parsed_ip6 = $parsed_ip2;
59 } elsif (Net
::IP
::ip_is_ipv4
($parsed_ip1)) {
60 $parsed_ip4 = $parsed_ip1;
62 $parsed_ip6 = $parsed_ip1;
64 $parsed_ip6 = $1 if $parsed_ip6 && $parsed_ip6 =~ m/\[(\S+)\]/;
67 if (!defined($macdb->{macs
}->{$parsed_mac}) ||
68 ($parsed_ip4 && $macdb->{macs
}->{$parsed_mac}->{'ip4'} && $macdb->{macs
}->{$parsed_mac}->{'ip4'} ne $parsed_ip4) ||
69 ($parsed_ip6 && $macdb->{macs
}->{$parsed_mac}->{'ip6'} && $macdb->{macs
}->{$parsed_mac}->{'ip6'} ne $parsed_ip6)) {
74 if ($parsed_mac eq $mac) {
75 $match = 1 if $ip4 && $parsed_ip4 && $ip4;
76 $match = 1 if $ip6 && $parsed_ip6 && $ip6;
83 my $reservation = $mac;
84 $reservation .= ",$ip4" if $ip4;
85 $reservation .= ",[$ip6]" if $ip6;
86 print $out "$reservation\n";
92 move
$ethers_tmp_file, $ethers_file;
93 chmod 0644, $ethers_file;
96 PVE
::Tools
::lock_file
($ethers_file, 10, $appendFn);
99 warn "Unable to add $mac to the dnsmasq configuration: $@\n";
103 my $service_name = "dnsmasq\@$dhcpid";
104 PVE
::Tools
::run_command
(['systemctl', 'reload', $service_name]) if $reload;
106 #update lease as ip could still be associated to an old removed mac
107 my $bus = Net
::DBus-
>system();
108 my $dnsmasq = $bus->get_service("uk.org.thekelleys.dnsmasq.$dhcpid");
109 my $manager = $dnsmasq->get_object("/uk/org/thekelleys/dnsmasq","uk.org.thekelleys.dnsmasq.$dhcpid");
111 my @hostname = unpack("C*", "*");
112 $manager->AddDhcpLease($ip4, $mac, \
@hostname, undef, 0, 0, 0) if $ip4;
113 # $manager->AddDhcpLease($ip6, $mac, \@hostname, undef, 0, 0, 0) if $ip6;
117 sub configure_subnet
{
118 my ($class, $config, $dhcpid, $vnetid, $subnet_config) = @_;
120 die "No gateway defined for subnet $subnet_config->{id}"
121 if !$subnet_config->{gateway
};
123 my $tag = $subnet_config->{id
};
126 if (ip_is_ipv6
($subnet_config->{network
})) {
127 $option_string = 'option6';
129 $option_string = 'option';
130 push @{$config}, "dhcp-option=tag:$tag,$option_string:router,$subnet_config->{gateway}";
133 push @{$config}, "dhcp-option=tag:$tag,$option_string:dns-server,$subnet_config->{'dhcp-dns-server'}"
134 if $subnet_config->{'dhcp-dns-server'};
138 sub configure_range
{
139 my ($class, $config, $dhcpid, $vnetid, $subnet_config, $range_config) = @_;
141 my $tag = $subnet_config->{id
};
143 my ($zone, $network, $mask) = split(/-/, $tag);
145 if (Net
::IP
::ip_is_ipv4
($network)) {
146 $mask = (2 ** $mask - 1) << (32 - $mask);
147 $mask = join( '.', unpack( "C4", pack( "N", $mask ) ) );
150 push @{$config}, "dhcp-range=set:$tag,$network,static,$mask,infinite";
154 my ($class, $config, $dhcpid, $vnetid, $vnet_config) = @_;
156 return if @{$config} < 1;
158 push @{$config}, "interface=$vnetid";
160 PVE
::Tools
::file_set_contents
(
161 "$DNSMASQ_CONFIG_ROOT/$dhcpid/10-$vnetid.conf",
162 join("\n", @{$config}) . "\n"
166 sub before_configure
{
167 my ($class, $dhcpid) = @_;
169 my $dbus_config = <<DBUSCFG;
170 <!DOCTYPE busconfig PUBLIC
171 "-//freedesktop//DTD D-BUS Bus Configuration 1.0//EN"
172 "http://www.freedesktop.org/standards/dbus/1.0/busconfig.dtd">
175 <allow own="uk.org.thekelleys.dnsmasq.$dhcpid"/>
176 <allow send_destination="uk.org.thekelleys.dnsmasq.$dhcpid"/>
178 <policy user="dnsmasq">
179 <allow own="uk.org.thekelleys.dnsmasq.$dhcpid"/>
180 <allow send_destination="uk.org.thekelleys.dnsmasq.$dhcpid"/>
182 <policy context="default">
183 <deny own="uk.org.thekelleys.dnsmasq.$dhcpid"/>
184 <deny send_destination="uk.org.thekelleys.dnsmasq.$dhcpid"/>
189 PVE
::Tools
::file_set_contents
(
190 "/etc/dbus-1/system.d/dnsmasq.$dhcpid.conf",
194 my $config_directory = "$DNSMASQ_CONFIG_ROOT/$dhcpid";
196 mkdir($config_directory, 0755) if !-d
$config_directory;
198 my $default_config = <<CFG;
199 CONFIG_DIR='$config_directory,\*.conf'
200 DNSMASQ_OPTS="--conf-file=/dev/null --enable-dbus=uk.org.thekelleys.dnsmasq.$dhcpid"
203 PVE
::Tools
::file_set_contents
(
204 "$DNSMASQ_DEFAULT_ROOT/dnsmasq.$dhcpid",
208 my $default_dnsmasq_config = <<CFG;
214 dhcp-leasefile=$DNSMASQ_LEASE_ROOT/dnsmasq.$dhcpid.leases
215 dhcp-hostsfile=$config_directory/ethers
216 dhcp-ignore=tag:!known
218 # Send an empty WPAD option. This may be REQUIRED to get windows 7 to behave.
219 dhcp-option=252,"\\n"
221 # Send microsoft-specific option to tell windows to release the DHCP lease
222 # when it shuts down. Note the "i" flag, to tell dnsmasq to send the
223 # value as a four-byte integer - that's what microsoft wants.
224 dhcp-option=vendor:MSFT,2,1i
226 # If a DHCP client claims that its name is "wpad", ignore that.
227 # This fixes a security hole. see CERT Vulnerability VU#598349
228 dhcp-name-match=set:wpad-ignore,wpad
229 dhcp-ignore-names=tag:wpad-ignore
232 PVE
::Tools
::file_set_contents
(
233 "$config_directory/00-default.conf",
234 $default_dnsmasq_config
237 unlink glob "$config_directory/10-*.conf";
240 sub after_configure
{
241 my ($class, $dhcpid, $noerr) = @_;
243 return if !assert_dnsmasq_installed
($noerr);
245 my $service_name = "dnsmasq\@$dhcpid";
247 PVE
::Tools
::run_command
(['systemctl', 'reload', 'dbus']);
248 PVE
::Tools
::run_command
(['systemctl', 'enable', $service_name]);
249 PVE
::Tools
::run_command
(['systemctl', 'restart', $service_name]);
252 sub before_regenerate
{
253 my ($class, $noerr) = @_;
255 return if !assert_dnsmasq_installed
($noerr);
257 PVE
::Tools
::run_command
(['systemctl', 'stop', "dnsmasq@*"]);
258 PVE
::Tools
::run_command
(['systemctl', 'disable', 'dnsmasq@']);
261 sub after_regenerate
{