本文主要是介绍邻居表项的unres_qlen_bytes长度,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
对于每个未解析的邻居地址,变量unres_qlen和unres_qlen_bytes分别控制可换成的报文数量和报文字节数量,其中前者unres_qlen在内核linux-3.3版本已经废弃,应使用后一个变量unres_qlen_bytes,其默认值为SK_WMEM_MAX(即net.core.wmem_default),内核建议此值的设置应能够容纳256个中型长度的报文。
通过PROC文件unres_qlen和unres_qlen_bytes可查看和修改其值。
$ cat /proc/sys/net/ipv4/neigh/ens33/unres_qlen
101
$ cat /proc/sys/net/ipv4/neigh/ens33/unres_qlen_bytes
212992
在arp邻居表arp_tbl中将NEIGH_VAR_QUEUE_LEN_BYTES索引所对应的表项(unres_qlen_bytes)初始化为SK_WMEM_MAX。
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_QUEUE_LEN_BYTES] = SK_WMEM_MAX,[NEIGH_VAR_PROXY_QLEN] = 64,
内核中静态变量neigh_sysctl_table定义了unres_qlen_bytes和unres_qlen的PROC文件信息。注意这里的参数QUEUE_LEN_BYTES,其实际上为NEIGH_VAR_QUEUE_LEN_BYTES,两者使用的是相同的,即指向同一个变量,只是在显示时unres_qlen需要进行转换。
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_ZERO_INTMAX_ENTRY(QUEUE_LEN_BYTES, "unres_qlen_bytes"),...NEIGH_SYSCTL_UNRES_QLEN_REUSED_ENTRY(QUEUE_LEN, QUEUE_LEN_BYTES, "unres_qlen"),
如下unres_qlen的转换函数proc_unres_qlen,对于读操作,需要将unres_qlen_bytes的值除以SKB_TRUESIZE(ETH_FRAME_LEN)的值,以得到unres_qlen的值。而对于写操作需进行相反的操作,将设置的unres_qlen乘以SKB_TRUESIZE(ETH_FRAME_LEN)的值。
unres_qlen_max限定了unres_qlen的最大值。
static int int_max = INT_MAX;
static int unres_qlen_max = INT_MAX / SKB_TRUESIZE(ETH_FRAME_LEN);static int proc_unres_qlen(struct ctl_table *ctl, int write,void __user *buffer, size_t *lenp, loff_t *ppos)
{int size, ret;struct ctl_table tmp = *ctl;tmp.extra1 = &zero;tmp.extra2 = &unres_qlen_max;tmp.data = &size;size = *(int *)ctl->data / SKB_TRUESIZE(ETH_FRAME_LEN);ret = proc_dointvec_minmax(&tmp, write, buffer, lenp, ppos);if (write && !ret)*(int *)ctl->data = size * SKB_TRUESIZE(ETH_FRAME_LEN);return ret;
netlink接口
通过netlink设置unres_qlen和unres_qlen_bytes的值由以下函数neightbl_set处理,注意对于NDTPA_QUEUE_LEN,需要先行将其转换为字节数进行设置,两者设置的都是同一个变量,即NEIGH_VAR_QUEUE_LEN_BYTES为索引的数组成员。
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_QUEUE_LEN:NEIGH_VAR_SET(p, QUEUE_LEN_BYTES,nla_get_u32(tbp[i]) * SKB_TRUESIZE(ETH_FRAME_LEN));break;case NDTPA_QUEUE_LENBYTES:NEIGH_VAR_SET(p, QUEUE_LEN_BYTES, nla_get_u32(tbp[i]));break;
以下函数neightbl_fill_parms读取内核中的unres_qlen和unres_qlen_bytes的值,其中前者为一个近似值。
static int neightbl_fill_parms(struct sk_buff *skb, struct neigh_parms *parms)
{struct nlattr *nest;nest = nla_nest_start(skb, NDTA_PARMS);if (nest == NULL)return -ENOBUFS;if ((parms->dev &&nla_put_u32(skb, NDTPA_QUEUE_LENBYTES,NEIGH_VAR(parms, QUEUE_LEN_BYTES)) ||/* approximative value for deprecated QUEUE_LEN (in packets) */nla_put_u32(skb, NDTPA_QUEUE_LEN,NEIGH_VAR(parms, QUEUE_LEN_BYTES) / SKB_TRUESIZE(ETH_FRAME_LEN)) ||
unres_qlen处理
变量unres_qlen的值在数据流程中用到,如下函数__neigh_event_send,如果邻居表项的状态位没有设置NUD_STALE和NUD_INCOMPLETE,并且探测次数不为零,立即开始地址探测(immediate_probe)。并且,将邻居表项的状态位图设置为NUD_INCOMPLETE。
int __neigh_event_send(struct neighbour *neigh, struct sk_buff *skb)
{int rc;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 {neigh->nud_state = NUD_FAILED;...kfree_skb(skb);return 1;}} else if (neigh->nud_state & NUD_STALE) {...}
如果在发送报文时,邻居地址的状态等于NUD_INCOMPLETE,并且arp_queue队列中的报文总长度arp_queue_len_bytes与当前发送报文的长度(skb->truesize)之和,大于限定的QUEUE_LEN_BYTES值,将arp_queue队列中头部(最老的)报文释放,之后,再次检测总长度是否超限。
最终,将当前报文添加到arp_queue队列的尾部,同时增加缓存报文的总长度arp_queue_len_bytes。
if (neigh->nud_state == NUD_INCOMPLETE) {if (skb) {while (neigh->arp_queue_len_bytes + skb->truesize >NEIGH_VAR(neigh->parms, QUEUE_LEN_BYTES)) {struct sk_buff *buff;buff = __skb_dequeue(&neigh->arp_queue);if (!buff)break;neigh->arp_queue_len_bytes -= buff->truesize;kfree_skb(buff);NEIGH_CACHE_STAT_INC(neigh->tbl, unres_discards);}skb_dst_force(skb);__skb_queue_tail(&neigh->arp_queue, skb);neigh->arp_queue_len_bytes += skb->truesize;}rc = 1;}
out_unlock_bh:if (immediate_probe)neigh_probe(neigh);
内核版本 5.0
这篇关于邻居表项的unres_qlen_bytes长度的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!