我们提供安全,免费的手游软件下载!
IP分片重组技术在Linux内核中一直是一个重要的网络协议栈功能。随着内核版本的不断更新迭代,IP分片重组技术也在不断进行改进和优化。本文将针对4.19内核与3.10内核的IP分片重组进行对比分析,以及4.19内核中的新特性和参数设置进行详细介绍。
在4.19内核中,IP分片重组的实现与3.10内核有了一些显著的差异。4.9.134版本以后的内核中不再使用低水位和工作队列,而是引入了rhashtable替代了hash bucket的概念。在3.10内核中,使用1024个hash bucket,每个bucket中最多存放128个分片队列。而在4.19内核中,所有的分片队列都保存在可动态调整的rhashtable中,同时不再使用低水位和工作队列对IP分片进行回收。
在4.19内核中,系统会在内存中分配一个reassembly buffer用于IP分片的重组。同时,也定义了一系列的参数用于控制IP分片处理过程:
net.ipv4.ipfrag_high_thresh
: 用于IP分片重组的最大内存用量(默认为4194304,即4Mb)。
net.ipv4.ipfrag_time
: IP分片在内存中的保留时间(默认30,单位:秒)。
内核层定义了结构体netns_frags,包含分片重组功能需要的全局控制信息,其定义如下:
struct netns_frags {
struct percpu_counter mem ____cacheline_aligned_in_smp;
int timeout;
int high_thresh;
int low_thresh;
int max_dist;
struct inet_frags *f;
struct rhashtable rhashtable ____cacheline_aligned_in_smp;
atomic_long_t mem ____cacheline_aligned_in_smp;
};
其中rhashtable为分片队列(inet_frag_queue)所在的hash表,IP分片包在内核中根据IP报头的4个字段计算得到一个hash值(key值),每个hash值对应一个分片队列,实现分片包重组功能时,IP层需要先缓存收到的所有分片包,等待同一个IP报文的所有分片包都到达后,将它们重组成一个大包再提交给L4(TCP/UDP等)协议。
当收到新的IP分片包时,系统会对内存中所有待重组分片包占用的内存进行判断,如果占用内存高于高水位(net.ipv4.ipfrag_high_thresh),则丢弃分片包;否则,将接收到的分片包与rhashtable表中缓存的分片队列进行匹配,将属于同一数据包的分片包放在同一个分片队列中。若一个数据包的所有分片包都接收完成,则进入数据包的重构流程;若匹配失败,说明该分片属于一个新的数据包,进入分片队列新建流程。分片队列的接收查找函数inet_frag_find的实现如下:
struct inet_frag_queue *inet_frag_find(struct netns_frags *nf, void *key) {
struct inet_frag_queue *fq = NULL, *prev;
if (!nf->high_thresh || frag_mem_limit(nf) > nf->high_thresh)
return NULL;
rcu_read_lock();
prev = rhashtable_lookup(&nf->rhashtable, key, nf->f->rhash_params);
if (!prev)
fq = inet_frag_create(nf, key, &prev);
if (prev && !IS_ERR(prev)) {
fq = prev;
if (!refcount_inc_not_zero(&fq->refcnt))
fq = NULL;
}
rcu_read_unlock();
return fq;
}
在分片队列的新建流程中,系统会从slab中分配一段空间,增加分片包占用的内存,同时设置定时器(超时时常为30秒)用来检查重组结果。若定时器超时未重组成功,该分片包也将被丢弃。分片包的新建函数inet_frag_alloc的实现如下:
static struct inet_frag_queue *inet_frag_alloc(struct netns_frags *nf, struct inet_frags *f, void *arg) {
struct inet_frag_queue *q;
q = kmem_cache_zalloc(f->frags_cachep, GFP_ATOMIC);
if (!q)
return NULL;
add_frag_mem_limit(nf, f->qsize);
setup_timer(&q->timer, f->frag_expire, (unsigned long)q);
return q;
}
如果一个数据包的所有分片包都已接收,则需将所有分片包整合获得原始数据包,并将整合后的数据包提交给高层协议。同时,处理与分片包相关的数据结构,如更新当前分片包占用的内存,停止与分片包相关的定时器等。数据包的重构函数ip_frag_reasm的实现如下:
static int ip_frag_reasm(struct ipq *qp, struct sk_buff *skb, struct sk_buff *prev_tail, struct net_device *dev) {
ipq_kill(qp);
sub_frag_mem_limit(qp->q.net, head->truesize);
}
综上所述,一个分片包的接收通常经历了查找分片、缓存、重组、释放等多个阶段。根据分析,内核中待重组的分片包占用内存量由高水位(net.ipv4.ipfrag_high_thresh)阈值和分片保留时间(net.ipv4.ipfrag_time)来控制。如果待重组分片包内存占用高于高水位,新收到的数据包分片将会直接丢弃。如果分片包超过最大保留时间,已经收到的数据包也会被丢弃。
图1:3.10版本下的分片包接收流程
相关资讯
热门资讯