]>
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";
38 return "$DNSMASQ_CONFIG_ROOT/$dhcpid/ethers";
42 my ($dhcpid, $ip4, $mac) = @_;
43 #update lease as ip could still be associated to an old removed mac
44 my $bus = Net
::DBus-
>system();
45 my $dnsmasq = $bus->get_service("uk.org.thekelleys.dnsmasq.$dhcpid");
46 my $manager = $dnsmasq->get_object("/uk/org/thekelleys/dnsmasq","uk.org.thekelleys.dnsmasq.$dhcpid");
48 my @hostname = unpack("C*", "*");
49 $manager->AddDhcpLease($ip4, $mac, \
@hostname, undef, 0, 0, 0) if $ip4;
53 my ($class, $dhcpid, $macdb, $mac, $ip4, $ip6) = @_;
55 my $ethers_file = ethers_file
($dhcpid);
56 my $ethers_tmp_file = "$ethers_file.tmp";
61 open(my $in, '<', $ethers_file) or die "Could not open file '$ethers_file' $!\n";
62 open(my $out, '>', $ethers_tmp_file) or die "Could not open file '$ethers_tmp_file' $!\n";
66 while (my $line = <$in>) {
68 my $parsed_ip4 = undef;
69 my $parsed_ip6 = undef;
70 my ($parsed_mac, $parsed_ip1, $parsed_ip2) = split(/,/, $line);
73 $parsed_ip4 = $parsed_ip1;
74 $parsed_ip6 = $parsed_ip2;
75 } elsif (Net
::IP
::ip_is_ipv4
($parsed_ip1)) {
76 $parsed_ip4 = $parsed_ip1;
78 $parsed_ip6 = $parsed_ip1;
80 $parsed_ip6 = $1 if $parsed_ip6 && $parsed_ip6 =~ m/\[(\S+)\]/;
83 if (!defined($macdb->{macs
}->{$parsed_mac}) ||
84 ($parsed_ip4 && $macdb->{macs
}->{$parsed_mac}->{'ip4'} && $macdb->{macs
}->{$parsed_mac}->{'ip4'} ne $parsed_ip4) ||
85 ($parsed_ip6 && $macdb->{macs
}->{$parsed_mac}->{'ip6'} && $macdb->{macs
}->{$parsed_mac}->{'ip6'} ne $parsed_ip6)) {
90 if ($parsed_mac eq $mac) {
91 $match = 1 if $ip4 && $parsed_ip4 && $ip4;
92 $match = 1 if $ip6 && $parsed_ip6 && $ip6;
99 my $reservation = $mac;
100 $reservation .= ",$ip4" if $ip4;
101 $reservation .= ",[$ip6]" if $ip6;
102 print $out "$reservation\n";
108 move
$ethers_tmp_file, $ethers_file;
109 chmod 0644, $ethers_file;
112 PVE
::Tools
::lock_file
($ethers_file, 10, $appendFn);
115 warn "Unable to add $mac to the dnsmasq configuration: $@\n";
119 my $service_name = "dnsmasq\@$dhcpid";
120 systemctl_service
('reload', $service_name) if $reload;
121 update_lease
($dhcpid, $ip4, $mac);
124 sub configure_subnet
{
125 my ($class, $config, $dhcpid, $vnetid, $subnet_config) = @_;
127 die "No gateway defined for subnet $subnet_config->{id}"
128 if !$subnet_config->{gateway
};
130 my $tag = $subnet_config->{id
};
133 if (ip_is_ipv6
($subnet_config->{network
})) {
134 $option_string = 'option6';
136 $option_string = 'option';
137 push @{$config}, "dhcp-option=tag:$tag,$option_string:router,$subnet_config->{gateway}";
140 push @{$config}, "dhcp-option=tag:$tag,$option_string:dns-server,$subnet_config->{'dhcp-dns-server'}"
141 if $subnet_config->{'dhcp-dns-server'};
145 sub configure_range
{
146 my ($class, $config, $dhcpid, $vnetid, $subnet_config, $range_config) = @_;
148 my $tag = $subnet_config->{id
};
150 my ($zone, $network, $mask) = split(/-/, $tag);
152 if (Net
::IP
::ip_is_ipv4
($network)) {
153 $mask = (2 ** $mask - 1) << (32 - $mask);
154 $mask = join( '.', unpack( "C4", pack( "N", $mask ) ) );
157 push @{$config}, "dhcp-range=set:$tag,$network,static,$mask,infinite";
161 my ($class, $config, $dhcpid, $vnetid, $vnet_config) = @_;
163 return if @{$config} < 1;
165 push @{$config}, "interface=$vnetid";
167 PVE
::Tools
::file_set_contents
(
168 "$DNSMASQ_CONFIG_ROOT/$dhcpid/10-$vnetid.conf",
169 join("\n", @{$config}) . "\n"
173 sub systemctl_service
{
174 my ($action, $service) = @_;
176 PVE
::Tools
::run_command
(['systemctl', $action, $service]);
179 sub before_configure
{
180 my ($class, $dhcpid, $zone_cfg) = @_;
182 my $dbus_config = <<DBUSCFG;
183 <!DOCTYPE busconfig PUBLIC
184 "-//freedesktop//DTD D-BUS Bus Configuration 1.0//EN"
185 "http://www.freedesktop.org/standards/dbus/1.0/busconfig.dtd">
188 <allow own="uk.org.thekelleys.dnsmasq.$dhcpid"/>
189 <allow send_destination="uk.org.thekelleys.dnsmasq.$dhcpid"/>
191 <policy user="dnsmasq">
192 <allow own="uk.org.thekelleys.dnsmasq.$dhcpid"/>
193 <allow send_destination="uk.org.thekelleys.dnsmasq.$dhcpid"/>
195 <policy context="default">
196 <deny own="uk.org.thekelleys.dnsmasq.$dhcpid"/>
197 <deny send_destination="uk.org.thekelleys.dnsmasq.$dhcpid"/>
202 PVE
::Tools
::file_set_contents
(
203 "/etc/dbus-1/system.d/dnsmasq.$dhcpid.conf",
207 my $config_directory = "$DNSMASQ_CONFIG_ROOT/$dhcpid";
209 mkdir($config_directory, 0755) if !-d
$config_directory;
211 my $default_config = <<CFG;
212 CONFIG_DIR='$config_directory,\*.conf'
213 DNSMASQ_OPTS="--conf-file=/dev/null --enable-dbus=uk.org.thekelleys.dnsmasq.$dhcpid"
216 PVE
::Tools
::file_set_contents
(
217 "$DNSMASQ_DEFAULT_ROOT/dnsmasq.$dhcpid",
221 my $mtu = PVE
::Network
::SDN
::Zones
::get_mtu
($zone_cfg);
223 my $default_dnsmasq_config = <<CFG;
229 dhcp-leasefile=$DNSMASQ_LEASE_ROOT/dnsmasq.$dhcpid.leases
230 dhcp-hostsfile=$config_directory/ethers
231 dhcp-ignore=tag:!known
234 ra-param=*,mtu:$mtu,0
236 # Send an empty WPAD option. This may be REQUIRED to get windows 7 to behave.
237 dhcp-option=252,"\\n"
239 # Send microsoft-specific option to tell windows to release the DHCP lease
240 # when it shuts down. Note the "i" flag, to tell dnsmasq to send the
241 # value as a four-byte integer - that's what microsoft wants.
242 dhcp-option=vendor:MSFT,2,1i
244 # If a DHCP client claims that its name is "wpad", ignore that.
245 # This fixes a security hole. see CERT Vulnerability VU#598349
246 dhcp-name-match=set:wpad-ignore,wpad
247 dhcp-ignore-names=tag:wpad-ignore
250 PVE
::Tools
::file_set_contents
(
251 "$config_directory/00-default.conf",
252 $default_dnsmasq_config
255 my @config_files = ();
256 PVE
::Tools
::dir_glob_foreach
($config_directory, '10-.*\.conf', sub {
258 push @config_files, "$config_directory/$file";
261 unlink @config_files;
264 sub after_configure
{
265 my ($class, $dhcpid, $noerr) = @_;
267 return if !assert_dnsmasq_installed
($noerr);
269 my $service_name = "dnsmasq\@$dhcpid";
271 systemctl_service
('reload', 'dbus');
272 systemctl_service
('enable', $service_name);
273 systemctl_service
('restart', $service_name);
276 sub before_regenerate
{
277 my ($class, $noerr) = @_;
279 return if !assert_dnsmasq_installed
($noerr);
281 systemctl_service
('stop', "dnsmasq@*");
282 systemctl_service
('disable', 'dnsmasq@');
285 sub after_regenerate
{