邻居表项的retrans_time时长

retrans_time控制在发送过邻居地址探测报文之后,经过多长时间检测邻居表项的变化,如果检测不成功,进行重新探测,对于arp协议,内核默认的retrans_time为1秒钟。

通过PROC文件retrans_time可查看和修改其值,单位时USR_HZ(100),PROC文件的内容为毫秒值,这里为默认的1000ms。

$ cat /proc/sys/net/ipv4/neigh/ens33/retrans_time
100
$ cat /proc/sys/net/ipv4/neigh/ens33/retrans_time_ms 
1000

在arp邻居表arp_tbl中将NEIGH_VAR_RETRANS_TIME索引所对应的表项初始化为1HZ(内核的HZ通常为1000,根据配置而定)。

struct neigh_table arp_tbl = {
    .family     = AF_INET,
    .key_len    = 4,
    .protocol   = cpu_to_be16(ETH_P_IP),
    .hash       = arp_hash,
    .key_eq     = arp_key_eq,
    .constructor    = arp_constructor,
    .proxy_redo = parp_redo,
    .id     = "arp_cache",
    .parms      = {
        .tbl            = &arp_tbl,
        .reachable_time     = 30 * HZ,
        .data   = {
            [NEIGH_VAR_RETRANS_TIME] = 1 * HZ,

内核中静态变量neigh_sysctl_table定义了retrans_time和retrans_time_ms的PROC文件信息,注意这里的参数RETRANS_TIME,其实际上为NEIGH_VAR_RETRANS_TIME,两者使用的是相同的,即指向同一个变量,只是在显示时一个使用USERHZ为单位,另一个使用毫秒MS为单位,内核内部使用jiffies为单位。

static struct neigh_sysctl_table {
    struct ctl_table_header *sysctl_header;
    struct ctl_table neigh_vars[NEIGH_VAR_MAX + 1];
} neigh_sysctl_template __read_mostly = {
    .neigh_vars = {
        NEIGH_SYSCTL_USERHZ_JIFFIES_ENTRY(RETRANS_TIME, "retrans_time"),
		...
		NEIGH_SYSCTL_MS_JIFFIES_REUSED_ENTRY(RETRANS_TIME_MS, RETRANS_TIME, "retrans_time_ms"),

netlink接口

除了以上的PROC文件外,还可使用ip ntable命令查看和修改设备的邻居表参数retrans。

# $ ip ntable show dev ens33
inet arp_cache 
    dev ens33 
    refcnt 1 reachable 37268 base_reachable 30000 retrans 1000 
    gc_stale 60000 delay_probe 5000 queue 101 
    app_probes 0 ucast_probes 3 mcast_probes 3 
    anycast_delay 1000 proxy_delay 800 proxy_queue 64 locktime 1000 
	
inet6 ndisc_cache 
    dev ens33 
    refcnt 1 reachable 31516 base_reachable 30000 retrans 1000 
    gc_stale 60000 delay_probe 5000 queue 101 
    app_probes 0 ucast_probes 3 mcast_probes 3 
    anycast_delay 1000 proxy_delay 800 proxy_queue 64 locktime 0 

与PROC文件不同,这里使用的名称为retrans,其值等于1000,单位为毫秒,即1秒。如下将设备ens33的邻居表参数retrans修改为2秒。

# ip ntable change name arp_cache dev ens33 retrans 2000

内核函数neightbl_set负责以上ip ntable change命令的处理。函数nla_get_msecs读取IP命令行设置的retrans的毫秒值。对于arp协议,宏NEIGH_VAR_SET将修改全局变量arp_tbl的成员parms的data数组,具体为以NEIGH_VAR_RETRANS_TIME为所对应的成员的值。

static int neightbl_set(struct sk_buff *skb, struct nlmsghdr *nlh, struct netlink_ext_ack *extack)
{
    struct neigh_table *tbl;
    struct nlattr *tb[NDTA_MAX+1];
	
    if (tb[NDTA_PARMS]) {
	    struct neigh_parms *p;
	    p = lookup_neigh_parms(tbl, net, ifindex);
        ...
        for (i = 1; i <= NDTPA_MAX; i++) {
            if (tbp[i] == NULL) continue;
            switch (i) {
            ...
            case NDTPA_RETRANS_TIME:
                NEIGH_VAR_SET(p, RETRANS_TIME,
                          nla_get_msecs(tbp[i]));
                break;

显示命令ip ntable show由内核中的函数neightbl_fill_parms处理,,负责填充内核中retrans的参数值,对于retrans的值,由nla_put_msecs函数由邻居表参数中取出并转换为毫秒值进行填充。

static int neightbl_fill_parms(struct sk_buff *skb, struct neigh_parms *parms)
{
    ...
    if ((parms->dev &&
         ...
       nla_put_msecs(skb, NDTPA_RETRANS_TIME,
             NEIGH_VAR(parms, RETRANS_TIME), NDTPA_PAD) ||

retrans_time处理

在邻居表项定时处理函数中,如果表项的状态不是NUD_REACHABLE,但是设置了NUD_DELAY状态位,并且,当前时刻已经超出了发送邻居探测报文的时间(DELAY_PROBE_TIME),即邻居地址已经探测过了,但是状态还没改变,这里去除其中的NUD_DELAY状态位,设置唯一的状态位NUD_PROBE,使用RETRANS_TIME时长作为下一次检测邻居状态的间隔时长,即下一次neigh_timer_handler执行的时间,在这期间应是执行了邻居的探测。

如果以上都不成立,当前邻居表项的状态位即没有设置NUD_REACHABLE,也没有设置NUD_DELAY,邻居表项的定时器也设置为RETRANS_TIME时长,这可能是上一次的探测并没有成功,在探测次数没有超限的情况下,还应进行探测。

static void neigh_timer_handler(struct timer_list *t)
{
    struct neighbour *neigh = from_timer(neigh, t, timer);

    state = neigh->nud_state;
    now = jiffies;
    next = now + HZ;

    if (!(state & NUD_IN_TIMER)) goto out;

    if (state & NUD_REACHABLE) {
        ...
    } else if (state & NUD_DELAY) {
        if (time_before_eq(now,
                   neigh->confirmed +
                   NEIGH_VAR(neigh->parms, DELAY_PROBE_TIME))) {
            neigh_dbg(2, "neigh %p is now reachable\n", neigh);
            ...
        } else {
            neigh_dbg(2, "neigh %p is probed\n", neigh);
            neigh->nud_state = NUD_PROBE;
            neigh->updated = jiffies;
            atomic_set(&neigh->probes, 0);
            notify = 1;
            next = now + NEIGH_VAR(neigh->parms, RETRANS_TIME);
        }
    } else {
        /* NUD_PROBE|NUD_INCOMPLETE */
        next = now + NEIGH_VAR(neigh->parms, RETRANS_TIME);
    }
    ...
    if (neigh->nud_state & NUD_IN_TIMER) {
        if (time_before(next, jiffies + HZ/2))
            next = jiffies + HZ/2;
        if (!mod_timer(&neigh->timer, next))
            neigh_hold(neigh);
    }

以下函数__neigh_set_probe_once属于上面介绍的第二种情况,邻居表项的状态位只有NUD_INCOMPLETE,并且探测次数设置成了最大值,仅能执行一次探测。此函数在IPv6探测下一跳网关地址时使用到。

void __neigh_set_probe_once(struct neighbour *neigh)
{
    if (neigh->dead)
        return;
    neigh->updated = jiffies;
    if (!(neigh->nud_state & NUD_FAILED))
        return;
    neigh->nud_state = NUD_INCOMPLETE;
    atomic_set(&neigh->probes, neigh_max_probes(neigh));
    neigh_add_timer(neigh,
            jiffies + NEIGH_VAR(neigh->parms, RETRANS_TIME));

不同于以上的邻居表项定时处理,在数据处理流程中,如果邻居表项的状态为NUD_STALE或者NUD_INCOMPLETE等状态,并且多播类型(MCAST_PROBES)和APP_PROBES类型的探测数量之和不等于零,发送探测报文(非单播类型的)。

这里将邻居表项的定时器设定为RETRANS_TIME和1/2秒,二者之间的最大值。

int __neigh_event_send(struct neighbour *neigh, struct sk_buff *skb)
{
    bool immediate_probe = false;
	
    if (neigh->nud_state & (NUD_CONNECTED | NUD_DELAY | NUD_PROBE))
        goto out_unlock_bh;
    if (neigh->dead) goto out_dead;

    ...
    if (!(neigh->nud_state & (NUD_STALE | NUD_INCOMPLETE))) {
        if (NEIGH_VAR(neigh->parms, MCAST_PROBES) +
            NEIGH_VAR(neigh->parms, APP_PROBES)) {
            unsigned long next, now = jiffies;

            atomic_set(&neigh->probes, NEIGH_VAR(neigh->parms, UCAST_PROBES));
            neigh->nud_state     = NUD_INCOMPLETE;
            neigh->updated = now;
            next = now + max(NEIGH_VAR(neigh->parms, RETRANS_TIME), HZ/2);
            neigh_add_timer(neigh, next);
            immediate_probe = true;
        } else {

内核版本 5.0

相关推荐
©️2020 CSDN 皮肤主题: 编程工作室 设计师:CSDN官方博客 返回首页