IP 碎片重组

内核中的IP重组函数.
struct sk_buff * ip_defrag(struct sk_buff * skb, u32 user)
{
......
//如果内核范围超出限制
if (atomic_read(&ip_frag_mem) > sysctl_ipfrag_high_thresh)
ip_evictor(); //回收内存到限制之内
......
if ((qp = ip_find(iph, user)) != NULL) { //查找或者创建一个队列头
......
ip_frag_queue(qp, skb); //收集IP碎片
if (qp->last_in == (FIRST_IN|LAST_IN) && qp->meat == qp->len) //IP碎片收集完毕
ret = ip_frag_reasm(qp, dev); //把收集的碎片重组,主要是更新头结点
......
return ret; //返回新重组后的数据报头
}
......
return NULL; //队列头创建失败
}
我们看到主要有三个函数最重要,我们一个一个看.
首先看一下数据结构
struct ipq {
struct hlist_node list; //hash list
......
struct sk_buff *fragments; // linked list of received fragments
int len; // 应该接收数据长度
int meat; // 实际接收数据长度
......
};
static inline struct ipq * ip_find(struct iphdr * iph, u32 user)
{
......
//根据ip中的信息,计算出一个hash值
hash = ipqhashfn(id, saddr, daddr, protocol);
//在hash表中查找相关队列头
hlist_for_each_entry(qp, n, &ipq_hash[hash], list) {
if(......) //找到匹配
return qp;
}
......
//没找到,创建一个然后放入hash表中
return ip_frag_create(iph, user);
}
//好好看看,不太难
static void ip_frag_queue(struct ipq * qp, struct sk_buff * skb)
{
struct sk_buff * prev, * next;
int flags, offset;
int ihl, end;
......
offset = ntohs(skb->nh.iph->frag_off);
flags = offset & ~IP_OFFSET; // 获取标志位
offset &= IP_OFFSET;
offset <<= 3; // 确定偏移位置
ihl = skb->nh.iph->ihl * 4; // ip 头长度
end = offset + skb->len - ihl; // 确定这个IP包数据在完整包中的结束点 if ((flags & IP_MF) == 0) { //是最后一个IP包吗
......
qp->last_in |= LAST_IN; //也许没有受到全部的包,但这个报一定是那最后一个,所以加标志
......
} else {
......
if (end > qp->len) {
.......
qp->len = end; //记录最后的位置
}
}
.......
//找到这个数据报在全部数据报中的位置
prev = NULL;
for(next = qp->fragments; next != NULL; next = next->next) {
if (FRAG_CB(next)->offset >= offset)
break; /* bingo! */
prev = next;
} if (prev) { //有上个位置的包
//计算复盖的长度
int i = (FRAG_CB(prev)->offset + prev->len) - offset; if (i > 0) { //有复盖
offset += i; //调整这个包的偏移
.......
}
}
//有下个位置的包
while (next && FRAG_CB(next)->offset < end) {
int i = end - FRAG_CB(next)->offset; //计算复盖的长度
// 没有复盖全部的next包
if (i < next->len) {
......
FRAG_CB(next)->offset += i; //调整next的偏移
qp->meat -= i; //调整实际数据长度
......
} else {
struct sk_buff * free_it = next;
......
frag_kfree_skb(free_it, NULL); //释放掉她
}
}
//记录这个偏移
FRAG_CB(skb)->offset = offset;
//插入这个数据包
skb->next = next;
if (prev)
prev->next = skb;
else
qp->fragments = skb;
......
qp->meat += skb->len; //记录实际接收数据的长度
......
}
//重组一个新包,并不实际拷贝数据,因为包已经被连接好 skb->netx->next->next
static struct sk_buff * ip_frag_reasm(struct ipq * qp, struct net_device * dev)
{
......
struct sk_buff * fp, * head = qp->fragments;
......
ihlen = head->nh.iph->ihl * 4; //IP头长度
len = ihlen + qp->len; //头加数据的总长度
......
skb_shinfo(head)->frag_list = head->next; //指向头下的一个数据包
skb_push(head, head->data - head->nh.raw); //调整指针
......
for (fp = head->next; fp; fp = fp->next) { //长度收集
head->data_len += fp->len;
head->len += fp->len;
.......
head->truesize += fp->truesize;
atomic_sub(fp->truesize, &ip_frag_mem); //使用内存调整
}
head->next = NULL; //next NULL
.......
qp->fragments = NULL; // set NULL
return head;
.......
}
IP 分片
ip_output->ip_finish_output 中
......
if (skb->len > dst_mtu(skb->dst) && !skb_is_gso(skb)) //包长度超过mtu 且没有开启 gso (gso参考: http://lwn.net/Articles/189970/)
return ip_fragment(skb, ip_finish_output2); //分片
else
return ip_finish_output2(skb); int ip_fragment(struct sk_buff *skb, int (*output)(struct sk_buff*))
{
   struct iphdr *iph;
  int raw = 0;
  int ptr;
  struct net_device *dev;
    struct sk_buff *skb2;
unsigned int mtu, hlen, left, len, ll_rs;
int offset;
__be16 not_last_frag;
struct rtable *rt = (struct rtable*)skb->dst; //路由项
int err = 0;
dev = rt->u.dst.dev;
iph = skb->nh.iph; //获取ip头 //设置了不分片标志
if (unlikely((iph->frag_off & htons(IP_DF)) && !skb->local_df)) {
IP_INC_STATS(IPSTATS_MIB_FRAGFAILS);
icmp_send(skb, ICMP_DEST_UNREACH, ICMP_FRAG_NEEDED, htonl(dst_mtu(&rt->u.dst)));
kfree_skb(skb);
return -EMSGSIZE;
}
hlen = iph->ihl * 4; //ip头长度
mtu = dst_mtu(&rt->u.dst) - hlen; //没有ip头的mtu大小
IPCB(skb)->flags |= IPSKB_FRAG_COMPLETE; if (skb_shinfo(skb)->frag_list) { //如果skb已经被分片了
struct sk_buff *frag;
int first_len = skb_pagelen(skb); //返回skb数据长度
//数据长度大于mtu 或 没有8字节对齐 或 ip头中标志了分片 或 skb被cloned
if (first_len - hlen > mtu || ((first_len - hlen) & 7) || (iph->frag_off & htons(IP_MF|IP_OFFSET)) || skb_cloned(skb))
goto slow_path; //进入慢速路径 //循环每一个碎片
for (frag = skb_shinfo(skb)->frag_list; frag; frag = frag->next) {
//长度检查
if (frag->len > mtu || ((frag->len & 7) && frag->next) || skb_headroom(frag) < hlen)
goto slow_path; /* Partially cloned skb? */
if (skb_shared(frag)) //碎片被共享
goto slow_path; BUG_ON(frag->sk);
if (skb->sk) { //skb持有 sock结构
sock_hold(skb->sk);
frag->sk = skb->sk;
frag->destructor = sock_wfree;
skb->truesize -= frag->truesize; //长度减少
}
}
/* Everything is OK. Generate! */
err = 0;
offset = 0;
frag = skb_shinfo(skb)->frag_list; //碎片头
skb_shinfo(skb)->frag_list = NULL; //skb 碎片头指针清空
skb->data_len = first_len - skb_headlen(skb);
skb->len = first_len; //更新长度
iph->tot_len = htons(first_len);
iph->frag_off = htons(IP_MF); //设置标志
ip_send_check(iph); //计算skb的校验和 for (;;) {
/* Prepare header of the next frame, before previous one went down. */
if (frag) { //碎片
frag->ip_summed = CHECKSUM_NONE;
frag->h.raw = frag->data; //传输头
frag->nh.raw = __skb_push(frag, hlen); //留出网络头空间
memcpy(frag->nh.raw, iph, hlen); //copy skb中的ip头到这个碎片中当作网络头
iph = frag->nh.iph;
iph->tot_len = htons(frag->len); //更新长度
ip_copy_metadata(frag, skb); //copy skb中的一些其他字段的值到碎片
if (offset == 0) //第一个碎片
ip_options_fragment(frag); //构建选项 offset += skb->len - hlen; //更新偏移
iph->frag_off = htons(offset>>3); //ip头中保存偏移
if (frag->next != NULL) //还有下一个碎片
iph->frag_off |= htons(IP_MF); /* Ready, complete checksum */
ip_send_check(iph); //计算校验和
}
err = output(skb);//发送数据包 if (!err)
IP_INC_STATS(IPSTATS_MIB_FRAGCREATES); if (err || !frag)
break; //循环到下一个
skb = frag;
frag = skb->next;
skb->next = NULL; }
if (err == 0) { //正确
IP_INC_STATS(IPSTATS_MIB_FRAGOKS);
return 0;
} while (frag) { //发送不正确
skb = frag->next;
kfree_skb(frag);
frag = skb;
}
IP_INC_STATS(IPSTATS_MIB_FRAGFAILS);
return err;
}
slow_path: //慢速路径
left = skb->len - hlen; //ip头后面数据长度
ptr = raw + hlen; // 0 + ip头长度
#ifdef CONFIG_BRIDGE_NETFILTER
/* for bridged IP traffic encapsulated inside f.e. a vlan header,
* we need to make room for the encapsulating header */
ll_rs = LL_RESERVED_SPACE_EXTRA(rt->u.dst.dev, nf_bridge_pad(skb));
mtu -= nf_bridge_pad(skb);
#else
ll_rs = LL_RESERVED_SPACE(rt->u.dst.dev); //预留头空间
#endif /* Fragment the datagram. */
offset = (ntohs(iph->frag_off) & IP_OFFSET) << 3; //碎片偏移
not_last_frag = iph->frag_off & htons(IP_MF); //是否是最后一个碎片 while(left > 0) {
len = left;
/* IF: it doesn't fit, use 'mtu' - the data space left */
if (len > mtu) //数据包长度 > mtu
len = mtu; /* IF: we are not sending upto and including the packet end then align the next start on an eight byte boundary */
if (len < left) {
len &= ~7; //8 字节对齐
}
//分配碎片
if ((skb2 = alloc_skb(len + hlen + ll_rs, GFP_ATOMIC)) == NULL) {
NETDEBUG(KERN_INFO "IP: frag: no memory for new fragment!\n");
err = -ENOMEM;
goto fail; }
ip_copy_metadata(skb2, skb); //copy skb中的其他数据到skb2
skb_reserve(skb2, ll_rs); //skb2->data += ll_rs 保留空间
skb_put(skb2, len + hlen); //设置skb长度
skb2->nh.raw = skb2->data; //指向网络头
skb2->h.raw = skb2->data + hlen; //指向传输头 if (skb->sk) //skb属于一个 sock
skb_set_owner_w(skb2, skb->sk); memcpy(skb2->nh.raw, skb->data, hlen); //copy网络头 if (skb_copy_bits(skb, ptr, skb2->h.raw, len))//copy网络头后面的数据 长度是len,不超过mtu
BUG(); left -= len; //总长度减少
iph = skb2->nh.iph; //获取ip头
iph->frag_off = htons((offset >> 3)); //设置碎片偏移 if (offset == 0) //第一个碎片,构建ip选项
ip_options_fragment(skb); if (left > 0 || not_last_frag) //还有碎片需要处理
iph->frag_off |= htons(IP_MF); ptr += len;//更新copy位置
offset += len;//偏移增加
iph->tot_len = htons(len + hlen); //更新 ip头中长度
ip_send_check(iph); //计算校验和
err = output(skb2); //发送数据包
if (err)
goto fail; IP_INC_STATS(IPSTATS_MIB_FRAGCREATES);
}
kfree_skb(skb);
IP_INC_STATS(IPSTATS_MIB_FRAGOKS);
return err;
fail: kfree_skb(skb);
IP_INC_STATS(IPSTATS_MIB_FRAGFAILS);
return err;
}

  

IP 碎片重组的更多相关文章

  1. IP分片重组的分析和常见碎片攻击 v0.2

    IP分片重组的分析和常见碎片攻击 v0.2http://www.nsfocus.net/index.php?act=magazine&do=view&mid=584 作者:yawl ( ...

  2. IP碎片原理:攻击和防护

    为了加深理解IP协议和一些DoS攻击手段大家有必要看看以下内容,也许对你理解这个概念有所帮助.先来看看IP碎片是如何产生的吧.         一.IP碎片是如何产生的       链路层具有最大传输 ...

  3. ip分片重组 ip_defrag

    在ip_local_deliver中,如果检测到是分片包,则需要进行分片重组: ip_local_deliver |-->ip_is_fragment //判断是否为分片包 |-->ip_ ...

  4. PROC 文件系统调节参数介绍(netstat -us)

    转自:http://www.cnblogs.com/super-king/p/3296333.html /proc/net/* snmp文件 Ip: ip项 Forwarding        : 是 ...

  5. proc 文件系统调节参数介绍

    /proc/net/* snmp文件 Ip: ip项 Forwarding        : 是否开启ip_forward,1开启,2关闭 DefaultTTL       : IP默认ttl. In ...

  6. Linux协议栈函数调用流程

    普通网络驱动程序中必须要调用的函数是eth_type_trans(略),然后向上递交sk_buff时调用netif_rx()(net/core/dev.c).其函数中主要几行 __skb_queue_ ...

  7. 014_IP专项研究监控

    一.数据demo cat /proc/net/snmp Ip: Forwarding DefaultTTL InReceives InHdrErrors InAddrErrors ForwDatagr ...

  8. Libnids(Library Network Intrusion Detection System) .

    Libnids(Library Network Intrusion Detection System)是一个网络入侵检测开发的专业编程接口.它实现了基于网络的入侵检测系统的基本框架,并提供了一些基本的 ...

  9. Libnids读书笔记 (转)

    一.当日工作(或学习)内容及进展情况(以条目式陈述,必要时配图说明) Libnids读书笔记: Libnids(Library Network Intusion Detection System)网络 ...

随机推荐

  1. Service层和DTO层的作用

    Service层主要提供的几个作用:1.将业务逻辑层进行封装,对外提供业务服务调用.2.通过外观模式,屏蔽业务逻辑内部方法.3.降低业务逻辑层与UI层的依赖,业务逻辑接口或实现的变化不会影像UI层.4 ...

  2. ElastciSearch常用APi

    列出所有的索引: GET /_cat/indices?v 删除索引 DELETE /customer?pretty

  3. 最全的ASP.NET开源CMS汇总

    转载:http://www.cnblogs.com/cxd4321/archive/2011/11/16/2250707.html 国内: 1.SiteServer CMS SiteServer CM ...

  4. Win10 10586 更新

    最近发现,电脑c盘突然少了许多,发现c盘多了个windowsBT文件夹,大概6个G,恩,win10 又推出更新了,版本10586. 不知道为啥,更新时win10 把原来的下载的删除了,大概出了什么错误 ...

  5. android studio 导入的项目有乱码-笔记2

    如果导入的项目原本就是UTF-8.且android studio编码设置为UTF-8就不会乱码.这种情况多是导入的原项目编码为GBK. 解决方法:在android studio 右下角,切换编码为GB ...

  6. SecurityException:Not allowed to start service Intent ,without permission not exported from

    本来是学长以前的项目,我正在重做一遍.结果突然出现了异常,我很是不解啊,怎么莫名其妙的就出现异常了呢?我昨天用还是好好的,根本就没动过源代码.于是在网上开始了一遍又一遍的查询,有的说要加权限.有的说这 ...

  7. eclipse Ctrl+1 没反应

    今天上午写代码,突然发现Ctrl+1没反应了,顿时无语.昨天还好好的,今天就不行了…… 无奈,只好在网上查了查,据说快捷键冲突的原因比较大. 于是我将Ctrl+1换成了Alt+1.在eclipse中测 ...

  8. 类库探源——System.Drawing.Bitmap

    一.System.Drawing.Bitmap Bitmap 类: 封装GDI+ 位图,此位图由图形图像及其属性的像素数据组成.Bitmap 是用于处理由像素定义的图像的对象 命名空间: System ...

  9. 怎么安装MySQL,安装MySQL遇到的一些问题!!!!!!

    简介: 对于初学,我们在安装MySQL时,会出现各种各样的报错,这让我们非常的头痛.那么我来分享一下我在安装的过程中遇到的一些问题吧! 我们在安装MySQL之前,先安装好dotNetFx40_Clie ...

  10. Java中多线程的使用!!

    简介:       1.要了解多线程,首先我们得先了解进程和线程.那么什么是进程?进程就是一个正在运行的程序分配内存让应用程序能够运行的叫做进程.那么什么又是线程呢?线程:在一个程序中,负责代码的执行 ...