接口token设置

如下ip命令所示,可设置接口的token值。

# ip token set ::0102:0304 dev ens33     
# 
# ip token list                          
token ::1.2.3.4 dev ens33
token :: dev ens34
token :: dev ens35

内核处理TOKEN添加

如下inet6_set_iftoken函数,先检查合法性:

  1. 对于环回loopback接口,或者不需要邻居地址的接口(设置了IFF_NOARP标志位),不设置接口token;
    2) 接口不接收邻居发现报文RA时,没有必要设置接口token;
    3) solicit重传次数为0,不设置接口token。
static int inet6_set_iftoken(struct inet6_dev *idev, struct in6_addr *token)
{
    struct inet6_ifaddr *ifp;
    struct net_device *dev = idev->dev;
    bool clear_token, update_rs = false;
    struct in6_addr ll_addr;

    ASSERT_RTNL();

    if (!token)
        return -EINVAL;
    if (dev->flags & (IFF_LOOPBACK | IFF_NOARP))
        return -EINVAL;
    if (!ipv6_accept_ra(idev))
        return -EINVAL;
    if (idev->cnf.rtr_solicits == 0)
        return -EINVAL;

保存token的后8字节到接口结构中。如果设置的token最后8字节为零,删除据此token生成的地址。

    write_lock_bh(&idev->lock);

    BUILD_BUG_ON(sizeof(token->s6_addr) != 16);
    memcpy(idev->token.s6_addr + 8, token->s6_addr + 8, 8);

    write_unlock_bh(&idev->lock);

    clear_token = ipv6_addr_any(token);
    if (clear_token)
        goto update_lft;

否则,如果设备的SLAAC状态为IF_READY,发送RS请求,在接收到RA报文之后,将根据新的token重新生成前缀地址。

    if (!idev->dead && (idev->if_flags & IF_READY) &&
        !ipv6_get_lladdr(dev, &ll_addr, IFA_F_TENTATIVE | IFA_F_OPTIMISTIC)) {
        /* If we're not ready, then normal ifup will take care
         * of this. Otherwise, we need to request our rs here.
         */
        ndisc_send_rs(dev, &ll_addr, &in6addr_linklocal_allrouters);
        update_rs = true;
    }

接下来,初始化RS超时时间,启动RS重传定时器。

update_lft:
    write_lock_bh(&idev->lock);

    if (update_rs) {
        idev->if_flags |= IF_RS_SENT;
        idev->rs_interval = rfc3315_s14_backoff_init(idev->cnf.rtr_solicit_interval);
        idev->rs_probes = 1;
        addrconf_mod_rs_timer(idev, idev->rs_interval);
    }

在设置接口新的token之后,需要删除接口上依据旧的token生成的地址。以下遍历接口的地址链表,对于由接口token生成的地址,将其valid和prefered时长设置为0,之后在addrconf_verify_rtnl函数中,将删除这些地址。

    /* Well, that's kinda nasty ... */
    list_for_each_entry(ifp, &idev->addr_list, if_list) {
        spin_lock(&ifp->lock);
        if (ifp->tokenized) {
            ifp->valid_lft = 0;
            ifp->prefered_lft = 0;
        }
        spin_unlock(&ifp->lock);
    }

    write_unlock_bh(&idev->lock);
    inet6_ifinfo_notify(RTM_NEWLINK, idev);
    addrconf_verify_rtnl();

token地址生成

在处理接收到的地址前缀信息时,如果前缀设置了自动配置标志,并且前缀长度为64bit,在接口token不为空的情况下,优先使用token的后8个字节作为新地址的接口ID,与前缀的8个字节一同生成新地址。

void addrconf_prefix_rcv(struct net_device *dev, u8 *opt, int len, bool sllao)
{
    ...
    /* Try to figure out our local address for this prefix */

    if (pinfo->autoconf && in6_dev->cnf.autoconf) {
        struct in6_addr addr;
        bool tokenized = false, dev_addr_generated = false;

        if (pinfo->prefix_len == 64) {
            memcpy(&addr, &pinfo->prefix, 8);

            if (!ipv6_addr_any(&in6_dev->token)) {
                read_lock_bh(&in6_dev->lock);
                memcpy(addr.s6_addr + 8, in6_dev->token.s6_addr + 8, 8);
                read_unlock_bh(&in6_dev->lock);
                tokenized = true;

以下函数负责新生成地址的添加。目前仅有6lowpan会使用到函数ndisc_ops_prefix_rcv_add_addr。

        err = addrconf_prefix_rcv_add_addr(net, dev, pinfo, in6_dev,
                           &addr, addr_type, addr_flags, sllao,
                           tokenized, valid_lft, prefered_lft);
        if (err) goto put;

        /* Ignore error case here because previous prefix add addr was
         * successful which will be notified.
         */
        ndisc_ops_prefix_rcv_add_addr(net, dev, pinfo, in6_dev, &addr,
                          addr_type, addr_flags, sllao,
                          tokenized, valid_lft, prefered_lft, dev_addr_generated);

如果地址不存在,使用ipv6_add_addr添加新的地址,并且设置tokenized标志,启动DAD检测。

int addrconf_prefix_rcv_add_addr(struct net *net, struct net_device *dev,...,
                 const struct in6_addr *addr, int addr_type,
                 u32 addr_flags, bool sllao, bool tokenized, ...)
{
    struct inet6_ifaddr *ifp = ipv6_get_ifaddr(net, addr, dev, 1);
    int create = 0;

    if (!ifp && valid_lft) {

        /* Do not allow to create too much of autoconfigured
         * addresses; this would be too easy way to crash kernel.
         */
        if (!max_addresses ||
            ipv6_count_addresses(in6_dev) < max_addresses)
            ifp = ipv6_add_addr(in6_dev, &cfg, false, NULL);

        ifp->flags |= IFA_F_MANAGETEMPADDR;
        ifp->cstamp = jiffies;
        ifp->tokenized = tokenized;
        addrconf_dad_start(ifp);

内核版本 5.10

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值