https://wiki.debian.org/IPv6PrefixDelegation
1、 请求前缀
可以在/etc/network/interfaces
文件中配置request_prefix
选项请求IPv6前缀。
iface enp1s0 inet6 dhcp
request_prefix 1
这将在enp1s0网卡的上通过DHCPv6请求一个可用的前缀。
请求前缀也可以与无状态自动配置 (SLAAC) 结合使用。只需指定 auto 而不是 dhcp 作为配置方法:
iface enp1s0 inet6 auto
dhcp 1
request_prefix 1
这样eth0的ipv6地址将会通过SLAAC配置,而不是通过DHCP,IPv6前缀将公共DHCPv6请求
最后,一些提供商会通过 DHCPv6 发送地址,但默认路由通过 SLAAC:
iface enp1s0 inet6 dhcp
accept_ra 2
request_prefix 1
2、将前缀分配给另一个接口
一旦获得前缀,就可以将其分配给另一个接口。
对于要使用前缀的接口(在以下示例中,接口是enp2s0),需要手动配置,并且在/etc/network/interfaces
中要进行如下配置:
iface enp2s0 inet6 manual
不幸的是,ISC DHCP(v6)
客户端无法自动将获得的前缀分配给另一个接口,因此需要一个小脚本来执行自动分配。
该脚本需要放置在目录/etc/dhcp/etc/dhcp/dhclient-exit-hooks.d/
中,以便在租用前缀更改时由DHCPv6 客户端执行。
3、脚本示例
(1)将此脚本保存到/etc/dhcp/dhclient-exit-hooks.d/prefix_delegation
并使其可执行;
(2)修改头部的IA_PD_IFACE
配置,指定要分配前缀的网卡名称;
(3)dhcpv6-pd消息日志将会记录到syslog;
(4)建议安装包 ipv6calc 以便根据前缀和接口的 MAC 地址计算 IPv6 地址,这是可选的。
(5)此脚本是一个示例,应涵盖前缀委托所需的基本操作。根据需要,它可被简化或扩展。一个限制是它不会将前缀拆分为多个子网以分配给多个接口。如果需要,请随意扩展脚本或使用其他 DHCPv6 客户端。
# This script assigns a delegated IPv6 prefix obtained via DHCPv6 to another interface
#
# Usage: This scrips is designed to be called from dhclient-script (isc-dhcp-client).
#
# LOCATION: /etc/dhcp/dhclient-exit-hooks.d/prefix_delegation
# RECOMMENDED PACKAGES: ipv6calc
# CONFIGURATION OPTIONS
# Define the interface to which a delegated prefix will be assigned
# This must not be the same interface on which the prefix is learned!
IA_PD_IFACE="enp2s0"
# Provide a space separated list of services that need to be restarted or reloaded after a prefix change
# Services must be controllable via systemd's systemctl, the default action is restart
# Service names may be followed by a colon and action name, to override the default action
# Supported actions are: restart and reload
# Example: IA_PD_SERVICES="shorewall6:reload dnsmasq"
IA_PD_SERVICES=""
# Define the location of the ipv6calc executable, if installed
# If this is empty or no executable file, no EUI-64 based IPv6 address will be calculated for the interface set in IA_PD_IFACE; instead, a static interface identifier (::1) will be appended to the prefix
# Example: IA_PD_IPV6CALC="/usr/bin/ipv6calc"
IA_PD_IPV6CALC=""
# Set to yes to make logging more verbose
IA_PD_DEBUG="no"
# END OF CONFIGURATION OPTIONS
fn_calc_ip6addr() {
[ -z "$1" ] && return
local ia_pd_mac
local ia_pd_addr
# PD=$(echo "$1" | sed -r 's#::/.*#::/64#')
[ -e "/sys/class/net/${IA_PD_IFACE}/address" ] && ia_pd_mac="$(cat /sys/class/net/${IA_PD_IFACE}/address)"
if [ -n "$ia_pd_mac" ] && [ -n "$IA_PD_IPV6CALC" ] && [ -x "$IA_PD_IPV6CALC" ]; then
[ "$IA_PD_DEBUG" = "yes" ] && logger -t "dhcpv6-pd" -p daemon.debug "Debug: Determined MAC address $ia_pd_mac for interface $IA_PD_IFACE."
ia_pd_addr="$("$IA_PD_IPV6CALC" -I prefix+mac -A prefixmac2ipv6 -O ipv6addr "$1" "$ia_pd_mac")"
fi
if [ -z "$ia_pd_addr" ]; then
[ "$IA_PD_DEBUG" = "yes" ] && logger -t "dhcpv6-pd" -p daemon.debug "Debug: Failed to calculate EUI-64 based IPv6 address, using static client suffix ::1 instead."
echo "$1" | sed 's#::/#::1/#'
else
echo "$ia_pd_addr"
fi
}
fn_restart_services() {
if [ -n "$IA_PD_SERVICES" ]; then
local pair
local action
local daemon
for pair in $IA_PD_SERVICES ; do
action="$(echo "$pair" | cut -d':' -f2)"
daemon="$(echo "$pair" | cut -d':' -f1)"
# Check if a valid action was provided or default to 'restart'
case $action in
reload) action="reload";;
*) action="restart";;
esac
# Check if daemon is active before trying to restart or reload it (avoids non-zero exit code)
if ! systemctl -q is-active "${daemon}.service" > /dev/null ; then
logger -t "dhcpv6-pd" -p daemon.info "Info: $daemon is inactive. No $action required."
continue
fi
if systemctl -q "$action" "${daemon}.service" > /dev/null ; then
logger -t "dhcpv6-pd" -p daemon.info "Info: Performed $action of $daemon due to change of IPv6 prefix."
else
logger -t "dhcpv6-pd" -p daemon.err "Error: Failed to perform $action of $daemon after change of IPv6 prefix."
fi
done
elif [ "$IA_PD_DEBUG" = "yes" ]; then
logger -t "dhcpv6-pd" -p daemon.debug "Debug: No list of services to restart or reload defined."
fi
}
fn_remove_prefix() {
[ -z "$1" ] && return
[ "$IA_PD_DEBUG" = "yes" ] && logger -t "dhcpv6-pd" -p daemon.debug "Debug: Old prefix $1 expired."
if [ "$(ip -6 addr show dev "$IA_PD_IFACE" scope global | wc -l)" -gt 0 ]; then
logger -t "dhcpv6-pd" -p daemon.info "Info: Flushing global IPv6 addresses from interface $IA_PD_IFACE."
if ! ip -6 addr flush dev "$IA_PD_IFACE" scope global ; then
logger -t "dhcpv6-pd" -p daemon.err "Error: Failed to flush global IPv6 addresses from interface $IA_PD_IFACE."
return
fi
# Restart services in case there is no new prefix to assign
[ -z "$new_ip6_prefix" ] && fn_restart_services
elif [ "$IA_PD_DEBUG" = "yes" ]; then
logger -t "dhcpv6-pd" -p daemon.debug "Debug: No global IPv6 addresses assigned to interface $IA_PD_IFACE."
fi
}
fn_assign_prefix() {
[ -z "$1" ] && return
local new_ia_pd_addr
# PD=$(echo "$1" | sed -r 's#::/.*#::/64#')
new_ia_pd_addr="$(fn_calc_ip6addr "$1")"
if [ -z "$new_ia_pd_addr" ]; then
logger -t "dhcpv6-pd" -p daemon.err "Error: Failed to calculate address for interface $IA_PD_IFACE and prefix $1"
return
fi
[ "$IA_PD_DEBUG" = "yes" ] && logger -t "dhcpv6-pd" -p daemon.debug "Debug: Received new prefix $1."
# dhclient may return an old_ip6_prefix even after a reboot, so manually check if the address is already assigned to the interface
if [ "$(ip -6 addr show dev "$IA_PD_IFACE" | grep -c "$new_ia_pd_addr")" -lt 1 ]; then
logger -t "dhcpv6-pd" -p daemon.info "Info: Adding new address $new_ia_pd_addr to interface $IA_PD_IFACE."
if ! ip -6 addr add "$new_ia_pd_addr" dev "$IA_PD_IFACE" ; then
logger -t "dhcpv6-pd" -p daemon.err "Error: Failed to add new address $new_ia_pd_addr to interface $IA_PD_IFACE."
return
fi
fn_restart_services
elif [ "$IA_PD_DEBUG" = "yes" ]; then
logger -t "dhcpv6-pd" -p daemon.debug "Debug: Address $new_ia_pd_addr already assigned to interface $IA_PD_IFACE."
fi
}
# Only execute on specific occasions
case $reason in
BOUND6|EXPIRE6|REBIND6|REBOOT6|RENEW6)
# Only execute if either an old or a new prefix is defined
new_ip6_prefix_64=$(echo "$new_ip6_prefix" | sed -r 's#::/.*#::/64#')
old_ip6_prefix_64=$(echo "$old_ip6_prefix" | sed -r 's#::/.*#::/64#')
if [ -n "$old_ip6_prefix" ] || [ -n "$new_ip6_prefix" ]; then
if [ -z "$IA_PD_IFACE" ] || [ ! -e "/sys/class/net/${IA_PD_IFACE}" ]; then
logger -t "dhcpv6-pd" -p daemon.err "Error: Interface ${IA_PD_IFACE:-<undefined>} not found. Cannot assign delegated prefix!"
else
# Remove old prefix if it differs from new prefix
[ -n "$old_ip6_prefix_64" ] && [ "$old_ip6_prefix_64" != "$new_ip6_prefix_64" ] && fn_remove_prefix "$old_ip6_prefix_64"
# Assign new prefix
[ -n "$new_ip6_prefix_64" ] && fn_assign_prefix "$new_ip6_prefix_64"
fi
fi
;;
esac
4、radvd配置
通过使用prefix ::/64
进行设置,使其无论分配的前缀如何都可以工作
interface enp2s0 {
AdvSendAdvert on;
MinRtrAdvInterval 3;
MaxRtrAdvInterval 10;
prefix ::/64 {
AdvOnLink on;
AdvAutonomous on;
AdvRouterAddr on;
AdvValidLifetime 3600;
AdvPreferredLifetime 3600;
};
};