Linux不同用户ip,ip分片与linux实现(整理自网络)

系统定义了一个大小为IPQ_HASHSZ=64的ipq_hash表,static struct ipq *ipq_hash[IPQ_HASHSZ];每个数组元素就是一个具有相同hash值的链表。每个链表上的节点就代表着同一个链接的IP的碎片。

ip_frage_mem

存储分片的内存,其初始化

什么情况,或者说什么协议会尝试发送这么长的数据?常见的有UDP和ICMP,需要特别注意的是,TCP一般不会。

为什么TCP不会造成IP分片呢?原因是TCP自身支持分段:当TCP要传输长度超过MSS(Maxitum Segment Size)的数据时,会先对数据进行分段,正常情况下,MSS小于MTU,因此,TCP一般不会造成IP分片。

而UDP和ICMP就不支持这种分段功能了,UDP和ICMP认为网络层可以传输无限长(实际上有65535的限制)的数据,当这两种协议发送数据时,它们不考虑数据长度,仅在其头部添加UDP或ICMP首部,然后直接交给网络层就万事大吉了。接着网络层IP协议对这种“身长头短”的数据进行分片,不要指望IP能很“智能”地识别传给它的数据上层头部在哪里,载荷又在哪里,它会直接将整个的数据切成N个分片,这样做的结果是,只有第一个分片具有UDP或者ICMP首部,而其它分片则没有。

相关代码实现:

//处理一个传过来的IP数据报

struct sk_buff *ip_defrag(struct sk_buff *skb)

{

struct iphdr *iph = skb->nh.iph;

struct ipq *qp;

struct net_device *dev;

IP_INC_STATS_BH(IpReasmReqds);

/* Start by cleaning up the memory. */

//如果用于分片处理的内存空间大于系统规定的最大值256k,那么要进行清洗ip_evictor

if (atomic_read(&ip_frag_mem) > sysctl_ipfrag_high_thresh)

ip_evictor();

//指定IP包对应设备dev

dev = skb->dev;

/* Lookup (or create) queue header */

if ((qp = ip_find(iph)) != NULL) { //根据HASH值,定位该包在分片链中的位置:

struct sk_buff *ret = NULL;

spin_lock(&qp->lock);

//将该分片插入到分片队列中:设置设备,增加meat分片总长度,如果偏移为0则说明是第一个包,置上            FIRST_IN标志。

ip_frag_queue(qp, skb);

//检查该分片是否是最后一个分片(分片是否都到齐了,包长度)如果是则进行分片重组调用                    ip_frag_reasm函数

if (qp->last_in == (FIRST_IN|LAST_IN) && //首包与尾包已收到

qp->meat == qp->len) //队列中字节数恰好等于队列尾部

ret = ip_frag_reasm(qp, dev); //重组碎片

spin_unlock(&qp->lock);

ipq_put(qp);

return ret;

}

IP_INC_STATS_BH(IpReasmFails);

kfree_skb(skb);

return NULL;

}

// 碎片的重组过程

static struct sk_buff *ip_frag_reasm(struct ipq *qp, struct net_device *dev)

{

struct sk_buff *skb;

struct iphdr *iph;

struct sk_buff *fp, *head = qp->fragments;

int len;

int ihlen;

ipq_kill(qp); //重组前,先删除该分片队列。

BUG_TRAP(head != NULL);

BUG_TRAP(FRAG_CB(head)->offset == 0);

ihlen = head->nh.iph->ihl*4; //取队列头IP包头长度

len = ihlen + qp->len; //总长度

if(len > 65535)

goto out_oversize;

//为新的包分配sk_buff结构,填入相应的值:设置新的IP总长度(不能超过65535字节);帧头位置、IP头位置、选项数据

skb = dev_alloc_skb(len);

if (!skb)

goto out_nomem;

/* Fill in the basic details. */

skb->mac.raw = skb->data;

skb->nh.raw = skb->data;

FRAG_CB(skb)->h = FRAG_CB(head)->h; //复制IP选项信息

skb->ip_summed = head->ip_summed;

skb->csum = 0;

//拷贝原始的IP头(分片队列的第一个分片中有记录)到新的skb结构

memcpy(skb_put(skb, ihlen), head->nh.iph, ihlen);

//循环拷贝:将分片链上的分片skb数据拷贝到新的skb结构中。

for (fp=head; fp; fp = fp->next) {

memcpy(skb_put(skb, fp->len), fp->data, fp->len);

//增加校样值

if (skb->ip_summed != fp->ip_summed)

skb->ip_summed = CHECKSUM_NONE;

else if (skb->ip_summed == CHECKSUM_HW)

skb->csum = csum_add(skb->csum, fp->csum);

}

//设置目的地址(克隆)、包类型、协议、设备。

skb->dst = dst_clone(head->dst);

skb->pkt_type = head->pkt_type;

skb->protocol = head->protocol;

skb->dev = dev;

/*

*  Clearly bogus, because security markings of the individual

*  fragments should have been checked for consistency before

*  gluing, and intermediate coalescing of fragments may have

*  taken place in ip_defrag() before ip_glue() ever got called.

*  If we're not going to do the consistency checking, we might

*  as well take the value associated with the first fragment.

*--rct

*/

skb->security = head->security;

//进行防火墙处理

#ifdef CONFIG_NETFILTER

/* Connection association is same as fragment (if any). */

skb->nfct = head->nfct;

nf_conntrack_get(skb->nfct);

#ifdef CONFIG_NETFILTER_DEBUG

skb->nf_debug = head->nf_debug;

#endif

#endif

/* Done with all fragments. Fixup the new IP header. */

//重新设置IP头、将3位标志和13位偏移设置为0、计算总长度。

iph = skb->nh.iph;

iph->frag_off = 0;

iph->tot_len = htons(len);

IP_INC_STATS_BH(IpReasmOKs);

return skb;

out_nomem:

NETDEBUG(printk(KERN_ERR

"IP: queue_glue: no memory for gluing queue %p\n",

qp));

goto out_fail;

out_oversize:

if (net_ratelimit())

printk(KERN_INFO

"Oversized IP packet from %d.%d.%d.%d.\n",

NIPQUAD(qp->saddr));

out_fail:

IP_INC_STATS_BH(IpReasmFails);

return NULL;

}

/在队列中增加新的分片处理过程. */

static void ip_frag_queue(struct ipq *qp, struct sk_buff *skb)

{

struct iphdr *iph = skb->nh.iph;

struct sk_buff *prev, *next;

int flags, offset;

int ihl, end;

if (qp->last_in & COMPLETE)

goto err;

//根据该包的标志位与偏移量:如果是最后一个分片包(但不一定分片完全到齐),设置分片队列长度为原         包的长度;如果是比当前分片靠后的包,改变分片队列的长度;如果是靠前的包。

offset = ntohs(iph->frag_off); //取片偏移量描述字

flags = offset & ~IP_OFFSET; //取片标志

offset &= IP_OFFSET; //求片偏移量

offset <<= 3;/* offset is in 8-byte chunks */

ihl = iph->ihl * 4;

/* Determine the position of this fragment. */

end = offset + (ntohs(iph->tot_len) - ihl); //求该片段尾部的数据偏移量

/* Is this the final fragment? */

if ((flags & IP_MF) == 0) { //最后一片段

/* If we already have some bits beyond end

* or have different end, the segment is corrrupted.

*/

if (end < qp->len || //最后一个片段的未尾小于队列内最大的未尾

((qp->last_in & LAST_IN) && end != qp->len))

goto err;

qp->last_in |= LAST_IN;

qp->len = end; //设取队列最大未尾

} else { //是中间某个片段

if (end&7) { //如果片段尾部不在8字节上对齐

end &= ~7;

if (skb->ip_summed != CHECKSUM_UNNECESSARY)

skb->ip_summed = CHECKSUM_NONE; 不计算校验和

}

if (end > qp->len) { ///该片段比队列内其它成员位置要大

/* Some bits beyond end -> corruption. */

if (qp->last_in & LAST_IN)

goto err;

qp->len = end;

}

}

if (end == offset) //片段的数据区长度为零

goto err;

/* Point into the IP datagram 'data' part. */

skb_pull(skb, (skb->nh.raw+ihl) - skb->data); //删除IP包的头部

skb_trim(skb, end - offset); //去队尾部可能的衬垫

/* Find out which fragments are in front and at the back of us

* in the chain of fragments so far.  We must know where to put

* this fragment, right?

*/

prev = NULL; //扫描重组队列中的包片段,取偏移大于或等于当前包偏移的前一成员作为插入位置

for(next = qp->fragments; next != NULL; next = next->next) {

if (FRAG_CB(next)->offset >= offset)

break;/* bingo! */

prev = next;

}

//当前偏移的包

/* We found where to put this one.  Check for overlap with

* preceding fragment, and, if needed, align things so that

* any overlaps are eliminated.

*/

//如果该分片不是第一个分片(prev!=null),先消除与前一分片重叠:求prev尾部与当前偏移之差,(该偏移包,不一定就是紧接着的那一个包);再消除与后一分片的重叠,求当前偏移与next重叠之差,如果当前包尾部小于后一包尾部,后一包起点后移,后一分片交叠部分清除,减少分片统计总长度meat;如果后一分片完全包含在此分片中,清除它next,减少分片统计总长度meat。

if (prev) {

int i = (FRAG_CB(prev)->offset + prev->len) - offset; //求prev尾部与当前偏移之差

if (i > 0) { //插入点成员尾部大于当前包开始, 说明当前包与前一包重叠

offset += i; //当前包起点后移

if (end <= offset)

goto err;

skb_pull(skb, i); //删除当前包前部i字节.

if (skb->ip_summed != CHECKSUM_UNNECESSARY)

skb->ip_summed = CHECKSUM_NONE;

}

}

// next是当前包的后一包

while (next && FRAG_CB(next)->offset < end) { //后一包与当前包有重叠

int i = end - FRAG_CB(next)->offset; /* overlap is 'i' bytes */

if (i < next->len) { //当前包尾部小于后一包尾部

/* Eat head of the next overlapped fragment

* and leave the loop. The next ones cannot overlap.

*/

FRAG_CB(next)->offset += i; //后一包起点后移

skb_pull(next, i); //删除后一包i字节

qp->meat -= i; //meat为队列已容纳的总字节数

if (next->ip_summed != CHECKSUM_UNNECESSARY)

next->ip_summed = CHECKSUM_NONE;

break;

} else { //当前包尾部大于或等于后一包尾部, 则删除后一包

struct sk_buff *free_it = next;

/* Old fragmnet is completely overridden with

* new one drop it.

*/

next = next->next;

if (prev)

prev->next = next;

else //说明next包是队列首包

qp->fragments = next;

qp->meat -= free_it->len;

frag_kfree_skb(free_it);

}

}

FRAG_CB(skb)->offset = offset; //在skb的cb[]块上记录当前包代表的数据位移

/* Insert this fragment in the chain of fragments. */

//将该分片插入到分片队列中:设置设备,增加meat分片总长度,如果偏移为0则说明是第一个包,置上FIRST_IN标志。

skb->next = next;

if (prev)

prev->next = skb;

else

qp->fragments = skb; //作为首包

if (skb->dev)

qp->iif = skb->dev->ifindex; //取包的输入设号号

skb->dev = NULL;

qp->meat += skb->len;

atomic_add(skb->truesize, &ip_frag_mem); //truesize为包描述结构与数据区总长

if (offset == 0) //首包

qp->last_in |= FIRST_IN;

return;

err:

kfree_skb(skb);

}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值