Created
December 4, 2018 14:04
-
-
Save Cazzar/dde916a9c2dc187356c78df02e251c50 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package Vyatta::DhcpPd; | |
use strict; | |
use warnings; | |
use lib "/opt/vyatta/share/perl5"; | |
use Sys::Syslog qw(:standard :macros); | |
use File::Compare; | |
our @EXPORT = qw(get_base_dir get_radvd_conffile get_radvd_gen | |
get_commit_notdone start_dhcpv6_daemon stop_dhcpv6_daemon | |
radvd_gen_add radvd_gen_del get_dhcp6c_pidfile | |
get_dhcp6c_conffile restart_radvd stop_radvd sysctl_set_ra | |
start_pd_daemon sighup_pd_daemon stop_pd_daemon | |
find_ipv6_addr pd_write_file validate_sla_id validate_ifid | |
pd_clear_intf_resolv_conf pd_add_to_resolv_conf | |
pd_update_resolv_conf); | |
use base qw(Exporter); | |
my $basedir = '/var/run'; | |
my $radvd_gen = '/opt/vyatta/sbin/vyatta_gen_radvd.pl'; | |
my $pd_daemon = '/usr/sbin/dhcp6c'; | |
my $pd_logfile = '/var/log/dhcp6c.log'; | |
sub get_base_dir { | |
return $basedir; | |
} | |
sub get_dhcp6s_pidfile { | |
my $intf = shift; | |
return "$basedir/dhcpv6-$intf-pd.pid"; | |
} | |
sub get_dhcp6s_conffile { | |
my $intf = shift; | |
return "$basedir/dhcpv6-$intf-pd.conf"; | |
} | |
sub get_dhcp6s_leasefile { | |
my $intf = shift; | |
my $file = "$basedir/dhcpv6-$intf-pd.leases"; | |
system("touch $file"); | |
return $file; | |
} | |
sub get_dhcp6c_pidfile { | |
my $intf = shift; | |
return "$basedir/dhcp6c-$intf-pd.pid"; | |
} | |
sub get_dhcp6c_conffile { | |
my $intf = shift; | |
return "$basedir/dhcp6c-$intf-pd.conf"; | |
} | |
sub get_radvd_conffile { | |
return '/etc/radvd.conf'; | |
} | |
sub is_running { | |
my ($pidfile) = @_; | |
if (-f $pidfile) { | |
my $pid = `cat $pidfile`; | |
$pid =~ s/\s+$//; # chomp doesn't remove nl | |
my $ps = `ps -p $pid -o comm=`; | |
if (defined($ps) && $ps ne "") { | |
return 1; | |
} | |
} | |
return 0; | |
} | |
sub find_ipv6_addr { | |
my $intf = shift; | |
my $line = `ip -6 addr sh dev $intf | grep global`; | |
if (defined $line and $line ne '') { | |
my ($junk, $proto, $addr) = split(/\s+/, $line); | |
my $ip = new6 NetAddr::IP($addr); | |
next if ! defined $ip; | |
my $net = $ip->network(); | |
return $net; | |
} | |
return; | |
} | |
sub get_commit_notdone { | |
return '/opt/vyatta/config/.commit'; | |
} | |
sub is_same_as_file { | |
my ($file, $value) = @_; | |
return if ! -e $file; | |
my $mem_file = ''; | |
open my $MF, '+<', \$mem_file or die "couldn't open memfile $!\n"; | |
print $MF $value; | |
seek($MF, 0, 0); | |
my $rc = compare($file, $MF); | |
if ($rc == 0) { | |
system("echo $file same >> /tmp/rad.log"); | |
} else { | |
system("echo $file differ >> /tmp/rad.log"); | |
} | |
return 1 if $rc == 0; | |
return; | |
} | |
sub pd_write_file { | |
my ($file, $data) = @_; | |
return 'same' if is_same_as_file($file, $data); | |
my $fh; | |
unless (open($fh, '>', $file)) { | |
syslog(LOG_ERR, "Couldn't open $file - $!"); | |
exit 1; | |
} | |
print $fh $data; | |
close $fh; | |
} | |
sub sysctl_set_ra { | |
my ($intf, $val) = @_; | |
$intf =~ s/\./\//; | |
my $sysctl = 'net.ipv6.conf.' . $intf . ".accept_ra=$val"; | |
system("sysctl -w $sysctl >/dev/null 2>&1"); | |
} | |
sub start_dhcpv6_daemon { | |
my ($intf, $output) = @_; | |
my $pidfile = get_dhcp6s_pidfile($intf); | |
my $conffile = get_dhcp6s_conffile($intf); | |
my $leasefile = get_dhcp6s_leasefile($intf); | |
my $cmd; | |
# Start daemon if it is not running. | |
# Restart daemon if configuration changed. | |
if (pd_write_file($conffile, $output) eq 'same') { | |
return if is_running($pidfile); | |
} else { | |
if (is_running($pidfile)) { | |
my $pid = `cat $pidfile`; | |
system("kill $pid"); | |
unlink($pidfile); | |
} | |
} | |
$cmd = "/usr/sbin/dhcpd3 -6 -d -pf $pidfile -cf $conffile -lf $leasefile"; | |
system("$cmd 2> /dev/null &"); | |
} | |
sub stop_dhcpv6_daemon { | |
my $intf = shift; | |
my $pidfile = get_dhcp6s_pidfile($intf); | |
my $conffile = get_dhcp6s_conffile($intf); | |
return if ! -e $pidfile; | |
my $pid = `cat $pidfile`; | |
return if ! defined $pid or $pid eq ''; | |
system("kill $pid"); | |
unlink($pidfile); | |
unlink($conffile); | |
} | |
sub radvd_gen_add { | |
my ($intf, $opt) = @_; | |
my $cmd = "sudo $radvd_gen --generate-pd $intf $opt"; | |
system($cmd); | |
} | |
sub radvd_gen_del { | |
my ($intf) = @_; | |
my $cmd = "sudo $radvd_gen --delete-pd $intf"; | |
system($cmd); | |
} | |
sub restart_radvd { | |
my $cmd = "sudo /etc/init.d/radvd stop > /tmp/junk"; | |
$cmd .= "; sudo /etc/init.d/radvd start > /tmp/junk"; | |
system($cmd); | |
} | |
sub stop_radvd { | |
my $cmd = "sudo /etc/init.d/radvd stop"; | |
system("$cmd > /dev/null"); | |
} | |
sub start_pd_daemon { | |
my $ifname = shift; | |
unlink($pd_logfile); | |
printf("Starting new daemon...\n"); | |
my $pd_pidfile = get_dhcp6c_pidfile($ifname); | |
my $pd_conffile = get_dhcp6c_conffile($ifname); | |
my $cmd = "$pd_daemon -c $pd_conffile -p $pd_pidfile -df $ifname"; | |
system("$cmd 2> $pd_logfile &"); | |
} | |
sub stop_pd_daemon { | |
my $ifname = shift; | |
my $pd_pidfile = get_dhcp6c_pidfile($ifname); | |
return if ! -e $pd_pidfile; | |
printf("Stopping daemon...\n"); | |
system('/sbin/start-stop-daemon --stop --quiet ' | |
. '--retry TERM/20/forever/KILL/1 ' | |
. "--pidfile $pd_pidfile --oknodo"); | |
} | |
sub sighup_pd_daemon { | |
my $ifname = shift; | |
my $pd_pidfile = get_dhcp6c_pidfile($ifname); | |
return if ! -e $pd_pidfile; | |
my $pid = `cat $pd_pidfile`; chomp $pid; | |
return if ! defined $pid or $pid eq ''; | |
printf("Signal daemon restart...\n"); | |
system ("kill -SIGHUP $pid"); | |
} | |
# prefix-id :1 | |
sub validate_sla_id { | |
my ($sla_len, $sla_id) = @_; | |
$sla_id =~ s/^[:]+//; | |
my $ip = new6 NetAddr::IP("::$sla_id"); | |
if (!defined $ip) { | |
die "Invalid IPv6 ID\n"; | |
} | |
my $dec_sla_id = $ip->numeric(); | |
if (!defined $dec_sla_id) { | |
die "Unable to convert prefix-id\n"; | |
} | |
my $size = (2 ** $sla_len) - 1; | |
if ($dec_sla_id > $size) { | |
my $prefix = 64 - $sla_len; | |
my $hex_size = sprintf("%x", $size); | |
$ip = new6 NetAddr::IP("$size"); | |
if (!defined $ip) { | |
die "failed to convert $size\n"; | |
} | |
my $limit = $ip->short(); | |
die "prefix-id must be less than $limit for prefix /$prefix\n"; | |
} | |
return $dec_sla_id; | |
} | |
# host-address ::1 | |
sub validate_ifid { | |
my ($ifid) = @_; | |
$ifid =~ s/^[:]+//; | |
my $ip = new6 NetAddr::IP("::$ifid"); | |
if (!defined $ip) { | |
die "Invalid IPv6 host address\n"; | |
} | |
my $dec_sla_id = $ip->numeric(); | |
if (!defined $dec_sla_id) { | |
die "Unable to convert IPv6 host address\n"; | |
} | |
$ip = new6 NetAddr::IP("::ffff:ffff:ffff:ffff"); | |
my $max = $ip->numeric(); | |
if ($dec_sla_id > $max) { | |
my $host_addr_max = $ip->short(); | |
die "host-address must be less than $host_addr_max\n"; | |
} | |
return $dec_sla_id; | |
} | |
# | |
# Clear contents from /var/run/pd-resolv-$ifname.conf. | |
# | |
sub pd_clear_intf_resolv_conf { | |
my ($ifname) = @_; | |
pd_add_to_resolv_conf($ifname, 0, 0, "", ""); | |
} | |
# | |
# Generate /var/run/pd-resolv-$ifname.conf based on: | |
# * $nameservers - string of name servers separated with a space character; | |
# * $domain - domain to be added to 'search'; | |
# * $sys_domain_set - boolean, indicates if "system domain-name" is set; | |
# * $nodns_set - boolean, indicates if "set interfaces ethernet eth4 dhcpv6-pd no-dns" is set. | |
# | |
# The file is empty if $nodns_set is true. 'search' is not added if $sys_domain_set is true. | |
# | |
sub pd_add_to_resolv_conf { | |
my ($ifname, $nodns_set, $sys_domain_set, $domain, $nameservers) = @_; | |
my @ns_arr = split / /, $nameservers; | |
my $intf_resolv_conf="/var/run/pd-resolv-$ifname.conf"; | |
# Reset the PD interface specific resolv.conf. | |
`echo -n > $intf_resolv_conf`; | |
if (!$nodns_set) { | |
# Add received configuration to PD interface specific resolv.conf. | |
my $comment="# written by /opt/vyatta/share/perl5/Vyatta/DhcpPd.pm"; | |
foreach my $ns (@ns_arr) { | |
`echo -e "nameserver $ns\t\t$comment" >> $intf_resolv_conf`; | |
} | |
if (!$sys_domain_set) { | |
if ($domain ne "") { | |
`echo -e "search $domain" >> $intf_resolv_conf`; | |
} | |
} | |
} | |
pd_update_resolv_conf("/etc/resolv.conf"); | |
} | |
# | |
# Update /etc/resolv.conf with the latest data received via PD: | |
# * add new domains sorted in search order to the 'search' entry but do not remove anything; | |
# * add 'nameserver' entries at the end so that IPv4 nameservers are preferred. | |
# | |
sub pd_update_resolv_conf { | |
my $base_resolv_conf=shift; | |
my $tmp_resolv_conf="/tmp/resolv.conf.$$.tmp"; | |
my $main_resolv_conf="/etc/resolv.conf"; | |
my $search = ""; | |
# Reset the working copy of resolv.conf. | |
`echo -n > $tmp_resolv_conf`; | |
# Add lines from base resolv.conf that were not generated by this module. | |
`cat $base_resolv_conf | grep -v /opt/vyatta/share/perl5/Vyatta/DhcpPd.pm >> $tmp_resolv_conf`; | |
# Prepare the updated 'search' line with domains from PD interfaces. | |
# Note: comments at the end of the 'search' line are not handled, do not add them! | |
my @doms; | |
if (`ls /var/run | grep -e 'pd-resolv-.*\.conf'`) { | |
@doms = `cat $base_resolv_conf /var/run/pd-resolv-*.conf | awk '/^search/ { for (i = 2; i <= NF; i++) print \$i }' | sort | uniq`; | |
} else { | |
@doms = `cat $base_resolv_conf | awk '/^search/ { for (i = 2; i <= NF; i++) print \$i }' | sort | uniq`; | |
} | |
if (scalar @doms > 0) { | |
# Sort the domains in search-order. | |
@doms = map { join ".", reverse split /\./ } | |
sort | |
map { join ".", reverse split /\./ } | |
@doms; | |
$search = "search "; | |
foreach my $dom (@doms) { | |
chomp $dom; | |
$search .= " " . $dom; | |
} | |
# Add or replace 'search' line. | |
if (!`cat $tmp_resolv_conf | grep -e '^domain'`) { | |
if (`cat $tmp_resolv_conf | grep -e '^search'`) { | |
`sed -i 's/^search.*/$search/' $tmp_resolv_conf`; | |
} else { | |
`echo -e '$search' >> $tmp_resolv_conf`; | |
} | |
} | |
} | |
# Add 'nameserver' lines from all PD interfaces to working copy of resolv.conf. | |
if (`ls /var/run | grep -e 'pd-resolv-.*\.conf'`) { | |
`cat /var/run/pd-resolv-*.conf | grep -e "^nameserver" | sort | uniq >> $tmp_resolv_conf`; | |
} | |
# Replace /etc/resolv.conf with the new one. | |
if (`cmp $tmp_resolv_conf $main_resolv_conf 2>&1` ne "") { | |
`cp $tmp_resolv_conf $main_resolv_conf`; | |
} | |
`rm $tmp_resolv_conf`; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment