Dit artikel schrijf ik enkel in het Engels vanwege het technische karakter.
A couple of weeks ago the physical installation of our second internet connection has been completed. We're now connected to the internet by both VDSL2 and Fiber to the premises using GPON technology. We've been waiting a long time for the availability of glass fiber connection.
Almost two decades ago, digital radio, television and internet over glass fiber was announced to be available within a couple of years. This was just before the privatization of the municipal electricity and radio and television broadcasting company, Energie Bedrijf Haarlem and Centrale Antenne Inrichting Haarlem. With the privatization of the electric power distribution division to Continuon and the cable television division to UPC, the plans were canceled.
After adding the necessary PPPoE, firewall and DHPCv6 daemon configuration and plugging in the cable between eth8
and the Nokia optical network terminator, we observed instability of both IPv6 /48
prefixes. The logs showed messages like:
Mar/27/2022 10:58:26: dhcp6_ctl_init: bind(control sock): Address already in use
Mar/27/2022 10:58:26: client6_init: failed to initialize control channel
We've been using IPv6 since 2007 for our servers and cannot simply turn off IPv6 as suggested at many places on the internet. Working towards improving the internet has always been a strong part of our mission. We've been involved in the development and deployment of IPv6 since the late nineties and have first used IPv6 through a 6RD tunnel starting in 2002. So lets dive into the EdgeRouter's EdgeOS and Vyatta/VyOS.
Both lines use PPPoE with IPv6 prefix delegation (DHCPv6-pd). Without an active lease the Uniper gateway at the end of our ISPs block traffic. Obtaining an IPv6 prefix delegation lease is done on Vyatta/VyOS by the wide-dhcpv6-client. The /usr/sbin/dhcp6c
daemon is started for each connected PPPoE interface which has IPv6 prefix delegation configured. By design the dhcp6c daemon opens a socket on port 5546
to communicate with the control process dhcp6ctl. The dhcp6ctl has a command line option to specify the port to use but the daemon does not. The daemon is designed to be run only once and can serve multiple interfaces at once. But this is not how Vyatta/VyOS and EdgeOS are setup.
The script /opt/vyatta/sbin/dhcpv6-pd-client.pl
is called for each interface. This script creates the global /etc/default/wide-dhcpv6-client
configuration file. And a per interface configuration file at /var/run/dhcpv6-\$intf-pd.conf
. After creating these two files, the daemon is started. This results in the config file /etc/default/wide-dhcpv6-client
to randomly contain either pppoe0
or pppoe1
. And the dhcp6c to be running for only one of the interfaces; which interface is random. For the other interface the errors about the address of the control socket already being used are given.
We need to modify two files to fix this behavior. The above mentioned /opt/vyatta/sbin/dhcpv6-pd-client.pl
and the Perl module /opt/vyatta/share/perl5/Vyatta/DhcpPd.pm
.
Firstly I've created a helper function that returns a list of all the interfaces that require IPv6 prefix delegation:
sub get_pd_interfaces {
my \$interfaces = '';
opendir my \$dir, \$basedir
or syslog(LOG_ERR, "Can't open \$basedir: \$!\\n");
my @confs = grep { /^dhcp6c-.*-pd\\.conf/ } readdir(\$dir);
closedir \$dir;
my \$first = 0;
foreach my \$conf (@confs) {
chomp \$conf;
if (\$conf =~ /dhcp6c-(.+)-pd\\.conf/) {
my \$ifname = \$1;
if (\$first++) { \$interfaces .= ' '; }
\$interfaces .= "\$ifname";
}
}
return \$interfaces;
}
Next I've modified the code in dhcpv6-pd-client.pl
to generate a config file in /etc/default
containing all the interfaces:
my \$output = gen_pd_conf(\$ifname);
pd_write_file(\$pd_conffile, \$output);
- \$output = "INTERFACES=\\"\$ifname\\"\\n";
+ \$output = "INTERFACES=\\"" . get_pd_interfaces() . "\\"\\n";
pd_write_file(\$pd_default, \$output);
This forms the first step to have only a single daemon being started for all interfaces. Next I've modified the DhcpPd.pm
Perl module to use a single pid file.
sub get_dhcp6c_pidfile {
my \$intf = shift;
- return "\$basedir/dhcp6c-\$intf-pd.pid";
+ return "\$basedir/dhcp6c_combined-pd.pid";
}
And added a helper function to return a new config file name that will contain the configuration for all interfaces combined:
sub get_dhcp6c_combined_conffile {
my \$intf = shift;
return "\$basedir/dhcp6c_combined-pd.conf";
}
The only thing remaining is to alter the function that starts the daemon. It must combine the configuration for all the interfaces to a single file and start the daemon for all interfaces needing IPv6 prefix delegation:
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 = "\$setsid \$pd_daemon -c \$pd_conffile -p \$pd_pidfile -df \$ifname";
+ my \$pd_conffile = get_dhcp6c_combined_conffile(\$ifname);
+ my \$interfaces = get_pd_interfaces();
+
+ # combine the config files as to have only one
+ # daemon actually running to prevent socket in use.
+ my \$combinedconf = '';
+ opendir my \$dir, \$basedir
+ or syslog(LOG_ERR, "Can't open \$basedir: \$!\\n");
+ my @confs = grep { /^dhcp6c-.*-pd\\.conf/ } readdir(\$dir);
+ closedir \$dir;
+ foreach my \$conf (@confs) {
+ chomp \$conf;
+
+ open my \$fh, '<', "\$basedir/\$conf" or syslog(LOG_ERR, "Can't open file \$!");
+ \$combinedconf .= do { local \$/; <\$fh> };
+ close \$fh;
+ }
+ pd_write_file(\$pd_conffile, \$combinedconf);
+
+ my \$cmd = "\$setsid \$pd_daemon -c \$pd_conffile -p \$pd_pidfile -df \$interfaces";
system("\$cmd 1> \$pd_logfile 2>&1 &");
}
Lets test our modifications:
$ sudo su
# ps ax |grep dhc
698 ? Ss 10:34 /usr/sbin/dhcpd3 -6 -pf /var/run/dhcpdv6.pid -cf /opt/vyatta/etc/dhcpdv6.conf -lf /var/run/dhcpdv6.leases
4941 ? Ss 0:00 /usr/sbin/dhcp6c -c /var/run/dhcp6c-pppoe0-pd.conf -p /var/run/dhcp6c-pppoe0-pd.pid -df pppoe0
5507 pts/0 S+ 0:00 grep dhc
# killall dhcp6c
# disconnect pppoe1
# connect pppoe1
# ps ax |grep dhc
698 ? Ss 10:34 /usr/sbin/dhcpd3 -6 -pf /var/run/dhcpdv6.pid -cf /opt/vyatta/etc/dhcpdv6.conf -lf /var/run/dhcpdv6.leases
5634 ? Ss 0:00 /usr/sbin/dhcp6c -c /var/run/dhcp6c_combined-pd.conf -p /var/run/dhcp6c_combined-pd.pid -df pppoe1 pppoe0
5816 pts/0 S+ 0:00 grep dhc
# show interfaces
Codes: S - State, L - Link, u - Up, D - Down, A - Admin Down
Interface IP Address S/L Description
--------- ---------- --- -----------
bond0 - u/u
bond0.10 192.168.10.2/24 u/u VLAN 10 Servers
2a10:3781:2271:10::1/64
2001:980:4e4b:10::1/64
…
eth8 - u/u LAN Glass Fiber
eth8.6 - u/u
eth9 192.168.0.2/24 u/u LAN VDSL2
…
pppoe0 83.161.134.185 u/u WAN xs4all
pppoe1 45.80.169.104 u/u WAN freedom
…
And on one of the servers:
$ ifconfig ens3
ens3: flags=4163 mtu 1500
inet 192.168.10.8 netmask 255.255.255.0 broadcast 192.168.10.255
inet6 fe80::a9a0:abe9:d623:f591 prefixlen 64 scopeid 0x20
inet6 2001:980:4e4b:10::8 prefixlen 128 scopeid 0x0
ether 02:11:32:27:9c:b4 txqueuelen 1000 (Ethernet)
RX packets 10578757 bytes 4253035700 (3.9 GiB)
RX errors 0 dropped 19522 overruns 0 frame 0
TX packets 4411102 bytes 15834189338 (14.7 GiB)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
$ ifconfig ens3
ens3: flags=4163 mtu 1500
inet 192.168.10.10 netmask 255.255.255.0 broadcast 192.168.10.255
inet6 fe80::9104:4eb:a79:f62d prefixlen 64 scopeid 0x20>
inet6 2a10:3781:2271:10::10 prefixlen 128 scopeid 0x0
ether 02:11:32:2f:27:73 txqueuelen 1000 (Ethernet)
RX packets 5978211 bytes 1731720702 (1.6 GiB)
RX errors 0 dropped 19522 overruns 0 frame 0
TX packets 1513345 bytes 701169404 (668.6 MiB)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
For us, and most users, above modifications are a good solution. However for integration in Vyatta/VyOS and EdgeOS a better fix would be to have only a single configuration file generated in /var/run
. And to not generate the list of interfaces, to start the daemon for, from the set of configuration files present in /var/run
. This information could be obtained directly from the configuration class Vyatta::Config
. Doing so would be a more elegant solution preventing a few extra writes to the router's EdgeOS-v2.0.9-hotfix.2-dhcpv6-pd-client.pl.patch | March 28, 2022 | 1.0 | GNU GPLv2 | Code | Patch | download |
EdgeOS-v2.0.9-hotfix.2-DhcpPd.pm.patch | March 28, 2022 | 1.0 | GNU GPLv2 | Code | Patch | download |