DHCPv6 PD前缀获取及分配

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;
   };
};
上一篇
下一篇