• 邻居子系统 之 更新neigh_update


    概述

    neigh_update函数用来更新指定的邻居项,更新内容是硬件地址和状态,更新之后,会根据新状态设置其输出函数,CONNECTED状态则使用快速输出,否则使用慢速输出;如果是由原来的无效状态变为现在的有效状态,则需要将数据包缓存队列中的数据包发送出去;

    该函数在邻居子系统中被频繁调用;arp模块再收到邻居应答,收到邻居的情况,转发单播代理请求后,会调用该函数更新地址和状态;netlink或者ioctl模块添加或者删除邻居项,也会调用该函数更新地址和状态;

    源码分析
      1 /* 更新指定的邻居项,更新内容为硬件地址和状态,新状态有效,并且有缓存包,则发送 */
      2 int neigh_update(struct neighbour *neigh, const u8 *lladdr, u8 new,
      3          u32 flags, u32 nlmsg_pid)
      4 {
      5     u8 old;
      6     int err;
      7     int notify = 0;
      8     struct net_device *dev;
      9     int update_isrouter = 0;
     10 
     11     write_lock_bh(&neigh->lock);
     12 
     13     dev    = neigh->dev;
     14     old    = neigh->nud_state;
     15     err    = -EPERM;
     16 
     17     /* 原状态是NOARP或者PERMANENT,必须要求是用户管理员发生的更新 */
     18     if (!(flags & NEIGH_UPDATE_F_ADMIN) &&
     19         (old & (NUD_NOARP | NUD_PERMANENT)))
     20         goto out;
     21 
     22     /* 已经在销毁状态 */
     23     if (neigh->dead)
     24         goto out;
     25 
     26     /* 新状态不是有效状态 */
     27     if (!(new & NUD_VALID)) {
     28 
     29         /* 删除定时器 */
     30         neigh_del_timer(neigh);
     31 
     32         /* 原状态是已连接状态,更新输出函数 */
     33         if (old & NUD_CONNECTED)
     34             neigh_suspect(neigh);
     35         /* 设置状态 */
     36         neigh->nud_state = new;
     37         err = 0;
     38         notify = old & NUD_VALID;
     39 
     40         /* 原状态为INCOMPLETE或者PROBE,新状态为失败状态 */
     41         if ((old & (NUD_INCOMPLETE | NUD_PROBE)) &&
     42             (new & NUD_FAILED)) {
     43             /* 清空缓存包队列 */
     44             neigh_invalidate(neigh);
     45             notify = 1;
     46         }
     47         goto out;
     48     }
     49 
     50     /* Compare new lladdr with cached one */
     51     if (!dev->addr_len) {
     52         /* First case: device needs no address. */
     53         lladdr = neigh->ha;
     54     } else if (lladdr) {
     55         /* The second case: if something is already cached
     56            and a new address is proposed:
     57            - compare new & old
     58            - if they are different, check override flag
     59          */
     60         if ((old & NUD_VALID) &&
     61             !memcmp(lladdr, neigh->ha, dev->addr_len))
     62             lladdr = neigh->ha;
     63     } else {
     64         /* No address is supplied; if we know something,
     65            use it, otherwise discard the request.
     66          */
     67         err = -EINVAL;
     68         if (!(old & NUD_VALID))
     69             goto out;
     70         lladdr = neigh->ha;
     71     }
     72 
     73     /* If entry was valid and address is not changed,
     74        do not change entry state, if new one is STALE.
     75      */
     76     err = 0;
     77     update_isrouter = flags & NEIGH_UPDATE_F_OVERRIDE_ISROUTER;
     78 
     79     /* 原状态有效 */
     80     if (old & NUD_VALID) {
     81         /* 地址不同 && 无UPDATE_F_OVERRIDE标记 */
     82         if (lladdr != neigh->ha && !(flags & NEIGH_UPDATE_F_OVERRIDE)) {
     83             update_isrouter = 0;
     84             /* 有UPDATE_F_WEAK_OVERRIDE状态 && 原状态是连接状态 */
     85             if ((flags & NEIGH_UPDATE_F_WEAK_OVERRIDE) &&
     86                 (old & NUD_CONNECTED)) {
     87                 /* 更新硬件地址为邻居项地址 */
     88                 lladdr = neigh->ha;
     89                 /* 更新状态为STALE */
     90                 new = NUD_STALE;
     91             } else
     92                 goto out;
     93         } 
     94         /* 地址相同或者有UPDATE_F_OVERRIDE标记 */
     95         else {
     96             /* 地址相同&&新状态为STALE&&不是管理员更新,新状态设置为原状态 */
     97             if (lladdr == neigh->ha && new == NUD_STALE &&
     98                 !(flags & NEIGH_UPDATE_F_ADMIN))
     99                 new = old;
    100         }
    101     }
    102 
    103     /* Update timestamps only once we know we will make a change to the
    104      * neighbour entry. Otherwise we risk to move the locktime window with
    105      * noop updates and ignore relevant ARP updates.
    106      */
    107     /* 新旧状态不同或新旧地址不同 */
    108     if (new != old || lladdr != neigh->ha) {
    109         /* 新状态是连接状态,更新确认时间 */
    110         if (new & NUD_CONNECTED)
    111             neigh->confirmed = jiffies;
    112         /* 更新更新时间 */
    113         neigh->updated = jiffies;
    114     }
    115 
    116     /* 新旧状态不同 */
    117     if (new != old) {
    118         /* 删除定时器 */
    119         neigh_del_timer(neigh);
    120 
    121         /* 新状态为PROBE,设置未接受到应答计数为0 */
    122         if (new & NUD_PROBE)
    123             atomic_set(&neigh->probes, 0);
    124 
    125         /* 新状态需要定时器,则添加 */
    126         if (new & NUD_IN_TIMER)
    127             neigh_add_timer(neigh, (jiffies +
    128                         ((new & NUD_REACHABLE) ?
    129                          neigh->parms->reachable_time :
    130                          0)));
    131         /* 设置新状态 */
    132         neigh->nud_state = new;
    133         notify = 1;
    134     }
    135 
    136     /* 新旧地址不同 */
    137     if (lladdr != neigh->ha) {
    138         write_seqlock(&neigh->ha_lock);
    139         /* 拷贝新地址 */
    140         memcpy(&neigh->ha, lladdr, dev->addr_len);
    141         write_sequnlock(&neigh->ha_lock);
    142         /* 更新二层头缓存 */
    143         neigh_update_hhs(neigh);
    144 
    145         /* 新状态不是连接状态,更新确认时间 */
    146         if (!(new & NUD_CONNECTED))
    147             neigh->confirmed = jiffies -
    148                       (NEIGH_VAR(neigh->parms, BASE_REACHABLE_TIME) << 1);
    149         notify = 1;
    150     }
    151 
    152     /* 新旧状态相同 */
    153     if (new == old)
    154         goto out;
    155 
    156     /* 新状态为CONNECTED,更新输出函数为connected_out */
    157     if (new & NUD_CONNECTED)
    158         neigh_connect(neigh);
    159     /* 否则,输出函数为output */
    160     else
    161         neigh_suspect(neigh);
    162 
    163     /* 原状态无效,新状态有效 */
    164     if (!(old & NUD_VALID)) {
    165         struct sk_buff *skb;
    166 
    167         /* Again: avoid dead loop if something went wrong */
    168 
    169         /* 新状态有效,缓存队列不为空 */
    170         while (neigh->nud_state & NUD_VALID &&
    171                (skb = __skb_dequeue(&neigh->arp_queue)) != NULL) {
    172             struct dst_entry *dst = skb_dst(skb);
    173             struct neighbour *n2, *n1 = neigh;
    174             write_unlock_bh(&neigh->lock);
    175 
    176             rcu_read_lock();
    177 
    178             /* Why not just use 'neigh' as-is?  The problem is that
    179              * things such as shaper, eql, and sch_teql can end up
    180              * using alternative, different, neigh objects to output
    181              * the packet in the output path.  So what we need to do
    182              * here is re-lookup the top-level neigh in the path so
    183              * we can reinject the packet there.
    184              */
    185             n2 = NULL;
    186             /* 有路由缓存,则根据路由缓存获取邻居项,有则替换 */
    187             if (dst) {
    188                 n2 = dst_neigh_lookup_skb(dst, skb);
    189                 if (n2)
    190                     n1 = n2;
    191             }
    192 
    193             /* 输出数据包 */
    194             n1->output(n1, skb);
    195 
    196             /* 是否引用的邻居项 */
    197             if (n2)
    198                 neigh_release(n2);
    199             rcu_read_unlock();
    200 
    201             write_lock_bh(&neigh->lock);
    202         }
    203 
    204         /* 清空数据包缓存队列 */
    205         __skb_queue_purge(&neigh->arp_queue);
    206         neigh->arp_queue_len_bytes = 0;
    207     }
    208 out:
    209     if (update_isrouter) {
    210         neigh->flags = (flags & NEIGH_UPDATE_F_ISROUTER) ?
    211             (neigh->flags | NTF_ROUTER) :
    212             (neigh->flags & ~NTF_ROUTER);
    213     }
    214     write_unlock_bh(&neigh->lock);
    215 
    216     /* 通知其他关心的模块 */
    217     if (notify)
    218         neigh_update_notify(neigh, nlmsg_pid);
    219 
    220     return err;
    221 }
  • 相关阅读:
    [创建型] 原型模式
    深复制和浅复制讨论
    设计模式扫盲
    selenium定位不到元素 yimu
    Jmeter拓展插件可查看和lr一样的图形结果 yimu
    python用字典实现switch..case类似的函数调用 yimu
    pycharm运行Pytest,有没有将Pytest写入Python代码中的区别 yimu
    Jmeter HTTPS接口测试的证书导入 yimu
    杭电acm2203
    杭电acm1259
  • 原文地址:https://www.cnblogs.com/wanpengcoder/p/11755396.html
Copyright © 2020-2023  润新知