荔园在线

荔园之美,在春之萌芽,在夏之绽放,在秋之收获,在冬之沉淀

[回到开始] [上一篇][下一篇]


发信人: jjk (UNIX+C+XML+?? 傻了?), 信区: Linux
标  题: IP的碎片重组函数分析(转寄)
发信站: 荔园晨风BBS站 (Mon Apr 22 19:48:59 2002), 转信

【 以下文字转载自 jjk 的信箱 】
【 原文由 jjk.bbs@apue.dhs.org 所发表 】
发信人: tl (tl), 信区: UKP
标  题: IP的碎片重组函数分析
发信站: UNIX编程 (2002年04月16日20:59:22 星期二), 转信

作者:硅谷农民<mailto:ggnm@kerneldiary.net>

 struct sk_buff *ip_defrag(struct sk_buff *skb)
    Linux的ip_fragment.c包含了一系列用来将IP包分块重组的函数。当系统接收到IP
碎片的时候,将它们
分队列保存起来。一直等到所有的碎片都收到,则将它们重新装配,再送给上一层协议
栈(例如tcp,udp)处理。
实现过程:
 * 如果defragment使用了太多的内存,则调用ip_evictor释放那些太老的fragment队列

 * 根据IP Header调用ip_find找到或者创建一个IP Fragment的队列(qp)。
 * 调用ip_frag_queue将这个网络包skb插入到队列qp中。
 * 如果第一个和最后一个fragment都已到达,并且队列fragment长度总和等于IP包的长
度,则调用
   ip_frag_reasm将这个IP包重新装配起来。注意对fragment队列操作的时候,要加上
spin lock,防止多个CPU
   同时插入或删除fragment。
        struct sk_buff *ip_defrag(struct sk_buff *skb)
        {
         struct iphdr *iph = skb->nh.iph;
         struct ipq *qp;
         struct net_device *dev;
         /* Start by cleaning up the memory. */
         if (atomic_read(&ip_frag_mem) > sysctl_ipfrag_high_thresh)
          ip_evictor();
         dev = skb->dev;
         /* Lookup (or create) queue header */
         if ((qp = ip_find(iph)) != NULL) {
          struct sk_buff *ret = NULL;
          spin_lock(&qp->lock);
          ip_frag_queue(qp, skb);
          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;
         }
         kfree_skb(skb);
         return NULL;
        }
----------------------------------------------------------------------------
---------
static inline struct ipq *ip_find(struct iphdr *iph)
实现过程:
 * 所有的fragment队列实际上是放在一个哈希表(ipq_hash)中,所以第一步先根据IP包
的id,源IP地址,目的IP地址和协议,
   调用ipqhashfn算出哈希值hash。
 * 遍历相同hash值的队列,找到以后增加它的reference counter,返回这个队列。
 * 如果找不到,则调用ip_frag_create创建一个fragment队列,返回。
        static inline struct ipq *ip_find(struct iphdr *iph)
        {
         __u16 id = iph->id;
         __u32 saddr = iph->saddr;
         __u32 daddr = iph->daddr;
         __u8 protocol = iph->protocol;
         unsigned int hash = ipqhashfn(id, saddr, daddr, protocol);
         struct ipq *qp;
         read_lock(&ipfrag_lock);
         for(qp = ipq_hash[hash]; qp; qp = qp->next) {
          if(qp->id == id  &&
             qp->saddr == saddr &&
             qp->daddr == daddr &&
             qp->protocol == protocol) {
           atomic_inc(&qp->refcnt);
           read_unlock(&ipfrag_lock);
           return qp;
          }
         }
         read_unlock(&ipfrag_lock);
         return ip_frag_create(hash, iph);
        }
----------------------------------------------------------------------------
---------
static void ip_frag_queue(struct ipq *qp, struct sk_buff *skb)
实现过程:
 * 算出这个fragment的起始和结束的偏移量(offset, end),如果是最后的一个fragme
nt(IP_MF为零),
   则设置LAST_IN标志。
 * 如果不是最后一个fragment,修正队列(qp)的长度qp->len = end。
        static void ip_frag_queue(struct ipq *qp, struct sk_buff *skb)
        {
         struct sk_buff *prev, *next;
         int flags, offset;
         int ihl, end;
         if (qp->last_in & COMPLETE)
          goto err;
          offset = ntohs(skb->nh.iph->frag_off);
         flags = offset & ~IP_OFFSET;
         offset &= IP_OFFSET;
         offset <<= 3;  /* offset is in 8-byte chunks */
          ihl = skb->nh.iph->ihl * 4;
         /* Determine the position of this fragment. */
          end = offset + skb->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) {
           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;
         if (pskb_pull(skb, ihl) == NULL)
          goto err;
         if (pskb_trim(skb, end-offset))
          goto err;
 * 遍历qp队列中的所有fragment,通过比较offset,找出要插入的位置的前一个和后一
个fragment(prev和next)。
 * 如果prev和要插入的fragment有重叠,调用pskb_pull(skb, i)缩小skb,对齐offse
t。
         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;
           if (end <= offset)
            goto err;
           if (!pskb_pull(skb, i))
            goto err;
           if (skb->ip_summed != CHECKSUM_UNNECESSARY)
            skb->ip_summed = CHECKSUM_NONE;
          }
         }
 * 从next开始,检查每一个fragment,如果有部分重叠(i字节)的话,调用pskb_pul
l(next, i)修正当前的fragment,
   如果是全部重叠的话,则调用frag_kfree_skb释放当前的fragment。
 * 如果不是最后一个fragment,修正队列(qp)的长度qp->len = end。
         while (next && FRAG_CB(next)->offset < end) {
          int i = end - FRAG_CB(next)->offset; /* overlap is 'i' bytes */
          if (i < next->len) {
           if (!pskb_pull(next, i))
            goto err;
           FRAG_CB(next)->offset += i;
           qp->meat -= i;
           if (next->ip_summed != CHECKSUM_UNNECESSARY)
            next->ip_summed = CHECKSUM_NONE;
           break;
          } else {
           struct sk_buff *free_it = next;
           next = next->next;
           if (prev)
            prev->next = next;
           else
            qp->fragments = next;
           qp->meat -= free_it->len;
           frag_kfree_skb(free_it);
          }
         }
         FRAG_CB(skb)->offset = offset;
 * 将skb插入队列qp中,修正qp的修改时间,qp的长度,记录这个队列已使用的内存(i
p_frag_mem),
   如果offset等于零,标志第一个fragment已来到(qp->last_in |= FIRST_IN)。
 * 如果有任何错误,记住调用kfree_skb释放内存。
         /* Insert this fragment in the chain of fragments. */
         skb->next = next;
         if (prev)
          prev->next = skb;
         else
          qp->fragments = skb;
          if (skb->dev)
           qp->iif = skb->dev->ifindex;
         skb->dev = NULL;
         qp->stamp = skb->stamp;
         qp->meat += skb->len;
         atomic_add(skb->truesize, &ip_frag_mem);
         if (offset == 0)
          qp->last_in |= FIRST_IN;
         return;
        err:
         kfree_skb(skb);
        }

--

    欲慰韶华携吴钩,剑雪刀霜遣风流。
    一蓑烟雨平生事,道无狂嚣亦无愁!

※ 来源:·UNIX编程 apue.dhs.org·[FROM: 166.111.160.6] --
※ 转寄:·UNIX编程 apue.dhs.org·[FROM: 210.39.3.50]
--
※ 转载:·荔园晨风BBS站 bbs.szu.edu.cn·[FROM: 192.168.0.146]


[回到开始] [上一篇][下一篇]

荔园在线首页 友情链接:深圳大学 深大招生 荔园晨风BBS S-Term软件 网络书店