接口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

相关推荐
<p> <span style="font-size:16px;"><br /> </span> </p> <p> <span style="font-size:16px;"><strong>课程简介:<br /> </strong>本课程主要是跟各位小伙伴分享、介绍并实战两大核心的用户身份认证(接口鉴权)模式,即</span><span style="font-size:16px;">基于</span><span style="font-size:16px;">Token</span><span style="font-size:16px;">的认证模式</span><span style="font-size:16px;"> 以及 </span><span style="font-size:16px;">基于</span><span style="font-size:16px;">Session</span><span style="font-size:16px;">的认证模式</span><span style="font-size:16px;">,其中</span><span></span> </p> <p> <span style="font-size:16px;">(1)   </span><span style="font-size:16px;">基于</span><span style="font-size:16px;">Token</span><span style="font-size:16px;">的认证模式</span><span style="font-size:16px;"> 则主要介绍了三种核心、主流的认证模式,即基于</span><span style="font-size:16px;">Token+</span><span style="font-size:16px;">数据库、基于</span><span style="font-size:16px;">Token+</span><span style="font-size:16px;">缓存中间件</span><span style="font-size:16px;">Redis</span><span style="font-size:16px;">、基于</span><span style="font-size:16px;">Token+JWT</span><span style="font-size:16px;">的认证模式。</span><span></span> </p> <p> <span style="font-size:16px;">(2)   </span><span style="font-size:16px;">基于</span><span style="font-size:16px;">Session</span><span style="font-size:16px;">的认证模式 </span><span style="font-size:16px;">也主要介绍了三种核心、主流的认证模式,即基于原生</span><span style="font-size:16px;">Spring Session</span><span style="font-size:16px;">以及</span><span style="font-size:16px;">Session</span><span style="font-size:16px;">共享的认证模式、基于</span><span style="font-size:16px;">Shiro Session</span><span style="font-size:16px;">的认证模式、基于</span><span style="font-size:16px;">Shiro + Redis </span><span style="font-size:16px;">的</span><span style="font-size:16px;">Session</span><span style="font-size:16px;">共享认证模式</span><span></span> </p> <p> <span style="font-size:16px;">即课程的整体介绍如下图所示:</span> </p> <p> <span style="font-size:16px;"><img src="https://img-bss.csdn.net/201909120730297517.png" alt="" /><br /> </span> </p> <p> <span style="font-size:16px;"> </span> </p> <p> 核心技术栈列表: </p> 值得介绍的是,本课程在技术栈层面涵盖了“用户身份认证”、“接口鉴权”等业务场景 常用的大部分技术,包括<span>Spring Boot2.x</span>、<span>Spring MVC</span>、<span>Mybatis</span>、加密解密算法<span>AES</span>、雪花算法<span>Snowflake</span>、统一验参工具<span>ValidatorUtil</span>、<span>JWT</span>(<span>Json Web Token</span>)、缓存中间件<span>Redis</span>、<span>Shiro(</span>身份认证与会话等等<span>)</span>、过滤器<span>Filter</span>、拦截器<span>Interceptor</span>、热部署插件<span>Devtools</span>、等等,如下图所示<br /> <p> <span style="font-size:16px;"><img src="https://img-bss.csdn.net/201909120732073201.png" alt="" /><br /> </span> </p> <p> <span style="font-size:16px;"> </span> </p> <p> <br /> </p> <p> 值得一提的是,本课程所介绍的核心重点在于“仅仅围绕基于<span>Token</span>的认证模式”进行展开讲解与实战,如下图所示为<span>Debug</span>亲自罗列、归纳出来的几大核心要点(面试官就经常喜欢这样面): </p> <img src="https://img-bss.csdn.net/201909120732381227.png" alt="" /><br /> <p> <span style="font-size:16px;"><br /> </span> </p> <p> <span style="font-size:16px;"> </span> </p> <p> 如下图所示为 基于<span>Token</span>认证模式 总体上的时序图:<span></span> </p> <img src="https://img-bss.csdn.net/201909120733009772.png" alt="" /><br /> <p> <br /> </p>
©️2020 CSDN 皮肤主题: 编程工作室 设计师:CSDN官方博客 返回首页