概述
对于每个未解析的邻居地址,变量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长度的全部内容,希望文章能够帮你解决邻居表项的unres_qlen_bytes长度所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复