When replacing ISP-provided CPE with a Linux router handling IPv6 via DHCPv6 Prefix Delegation, we need several components working in concert. Here's my working configuration on CentOS 6:
# /etc/dhcp/dhclient6.conf
script "/etc/dhcp/dhclient6-script";
interface "eth0" {
send dhcp6.reconf-accept;
request
dhcp6.name-servers,
dhcp6.domain-search,
dhcp6.sip-servers-addresses,
dhcp6.vendor-opts;
send dhcp6.oro 8,14,15,16,24;
}
The magic happens in the custom script that processes PD assignments:
#!/bin/bash
# /etc/dhcp/dhclient6-script
case $reason in
BOUND6|RENEW6|REBIND6)
# Process delegated prefix
if [ -n "$new_ip6_prefix" ]; then
# Assign first /64 to eth1
ip -6 addr add ${new_ip6_prefix%::/*}1::1/64 dev eth1
# Assign second /64 to eth2
ip -6 addr add ${new_ip6_prefix%::/*}2::1/64 dev eth2
# Update radvd config
cat > /etc/radvd.conf <
Dynamic RADVD configuration is handled by the dhclient script above. For a static fallback:
# /etc/radvd.conf
interface eth1 {
AdvSendAdvert on;
MinRtrAdvInterval 30;
MaxRtrAdvInterval 100;
prefix ::/64 {
AdvOnLink on;
AdvAutonomous on;
AdvRouterAddr on;
};
};
For serving DHCPv6 options to LAN clients:
# /etc/dhcp/dhcpd6.conf
option dhcp6.domain-search "example.com";
option dhcp6.name-servers 2001:db8::1;
subnet6 2001:db8:1::/64 {
range6 2001:db8:1::1000 2001:db8:1::2000;
option dhcp6.name-servers $new_dhcp6_name_servers;
}
For proper prefix transition during reconfiguration:
# In dhclient6-script
handle_reconfig() {
# Keep old prefix running during DAD timeout
ip -6 addr add $old_prefix deprecated
# Start new prefix
ip -6 addr add $new_prefix
# Update radvd with both prefixes briefly
sed -i "/prefix.*/a prefix $old_prefix { DeprecatePrefix on; };" /etc/radvd.conf
service radvd reload
# After 1 hour, clean up old prefix
sleep 3600
ip -6 addr del $old_prefix
sed -i "/$old_prefix/d" /etc/radvd.conf
service radvd reload
}
Essential troubleshooting commands:
# Check DHCPv6 traffic
tcpdump -i eth0 -vvv port 546 or port 547
# Verify prefix assignment
ip -6 addr show dev eth1
# Test router advertisements
rdisc6 eth1
# Check DHCPv6 server operation
dhcp6 -c /etc/dhcp/dhcpd6.conf -d -f eth1
Essential ip6tables rules for the router:
# Allow DHCPv6
ip6tables -A INPUT -i eth0 -p udp --dport 546 -j ACCEPT
ip6tables -A INPUT -i eth0 -p udp --dport 547 -j ACCEPT
# Allow ICMPv6 (essential for IPv6 operation)
ip6tables -A INPUT -p icmpv6 -j ACCEPT
# NAT64 if needed
ip6tables -t nat -A POSTROUTING -o eth0 -j MASQUERADE
When replacing ISP-provided CPE with a Linux router handling dual-stack connectivity, proper IPv6 configuration becomes crucial. The key components we need to address:
Network Topology:
[ISP] --(WAN/eth0)-- [Linux Router] --(LAN/eth1,eth2)-- [Clients]
The existing dhclient6.conf is correctly configured for prefix delegation. Let's enhance it to handle route and prefix assignments:
# /etc/dhcp/dhclient6.conf
interface "eth0" {
send dhcp6.reconf-accept;
request dhcp6.vendor-opts, dhcp6.name-servers,
dhcp6.domain-search, dhcp6.sip-servers-addresses,
dhcp6.sntp-servers;
script "/etc/dhcp/dhclient6-script";
send ia-pd 1;
}
Create /etc/dhcp/dhclient6-script with executable permissions to handle prefix delegation events:
#!/bin/bash
# /etc/dhcp/dhclient6-script
PREFIX_FILE="/var/lib/dhclient/dhclient6-prefixes"
RA_CONFIG="/etc/radvd.conf"
DHCPD6_CONFIG="/etc/dhcp/dhcpd6.conf"
case $reason in
BOUND6|RENEW6|REBIND6)
# Handle default route
if [ -n "${new_routers}" ]; then
ip -6 route del default 2>/dev/null
ip -6 route add default via ${new_routers} dev ${interface}
fi
# Process delegated prefixes
if [ -n "${new_ip6_prefix}" ]; then
echo "${new_ip6_prefix}" > ${PREFIX_FILE}
# Assign /64 subnets to LAN interfaces
PREFIX64_1=$(echo ${new_ip6_prefix} | sed 's/\/48/\/64/')::1
PREFIX64_2=$(echo ${new_ip6_prefix} | sed 's/\/48\//1:/')::1
ip -6 addr add ${PREFIX64_1} dev eth1
ip -6 addr add ${PREFIX64_2} dev eth2
# Update radvd configuration
generate_radvd_config ${new_ip6_prefix} > ${RA_CONFIG}
systemctl restart radvd
# Update dhcpd6 configuration
generate_dhcpd6_config ${new_ip6_prefix} > ${DHCPD6_CONFIG}
systemctl restart dhcpd6
fi
;;
EXPIRE6|RELEASE6)
# Clean up old configuration
ip -6 route del default 2>/dev/null
rm -f ${PREFIX_FILE}
;;
esac
Here's the generate_radvd_config function for proper Router Advertisement:
generate_radvd_config() {
local prefix=$1
local subnet1=${prefix/\/48/1:\/64}
local subnet2=${prefix/\/48/2:\/64}
cat << EOF
interface eth1 {
AdvSendAdvert on;
MinRtrAdvInterval 3;
MaxRtrAdvInterval 10;
prefix ${subnet1} {
AdvOnLink on;
AdvAutonomous on;
AdvRouterAddr on;
AdvValidLifetime 86400;
AdvPreferredLifetime 14400;
};
};
interface eth2 {
AdvSendAdvert on;
MinRtrAdvInterval 3;
MaxRtrAdvInterval 10;
prefix ${subnet2} {
AdvOnLink on;
AdvAutonomous on;
AdvRouterAddr on;
AdvValidLifetime 86400;
AdvPreferredLifetime 14400;
};
};
EOF
}
The DHCPv6 server needs to distribute additional information received from ISP:
generate_dhcpd6_config() {
local prefix=$1
local subnet1=${prefix/\/48/1:\/64}
local subnet2=${prefix/\/48/2:\/64}
cat << EOF
option dhcp6.name-servers ${new_dhcp6_name_servers};
option dhcp6.domain-search "${new_dhcp6_domain_search}";
option dhcp6.sip-servers-addresses ${new_dhcp6_sip_servers_addresses};
subnet6 ${subnet1} {
range6 ${subnet1/64\//64::1000} ${subnet1/64\//64::2000};
prefix6 ${subnet1};
}
subnet6 ${subnet2} {
range6 ${subnet2/64\//64::1000} ${subnet2/64\//64::2000};
prefix6 ${subnet2};
}
EOF
}
For seamless transition during prefix changes, modify the script to handle both old and new prefixes:
# Add to dhclient6-script
handle_reconfiguration() {
# First advertise old prefix with short lifetimes
sed -i 's/AdvValidLifetime [0-9]*/AdvValidLifetime 60/' ${RA_CONFIG}
sed -i 's/AdvPreferredLifetime [0-9]*/AdvPreferredLifetime 30/' ${RA_CONFIG}
systemctl reload radvd
# Wait for clients to get deprecated prefix
sleep 30
# Then update with new configuration
generate_radvd_config ${new_ip6_prefix} > ${RA_CONFIG}
generate_dhcpd6_config ${new_ip6_prefix} > ${DHCPD6_CONFIG}
systemctl restart radvd
systemctl restart dhcpd6
}
Create a systemd service to ensure proper sequencing of network services:
# /etc/systemd/system/ipv6-pd.service
[Unit]
Description=IPv6 Prefix Delegation Handler
After=network.target
Requires=network.target
[Service]
Type=oneshot
RemainAfterExit=yes
ExecStart=/etc/dhcp/dhclient6-script BOUND6
ExecStop=/etc/dhcp/dhclient6-script RELEASE6
[Install]
WantedBy=multi-user.target