LwIP BUG之ARP缓存

来源:互联网 发布:软件系统演示平台 编辑:程序博客网 时间:2024/05/22 17:10


在1.4.1中,ARP数据缓存存在一个很明显的缺陷。当IP层试图发送一个IP包时,如果这个目的IP地址在ARP中找不到对应的MAC地址,那么这个IP包会被首先缓存在ARP层,只有当ARP解析到目标MAC地址后,这个IP包才会被发送出去。但是这里的数据缓存过程,有点小问题。

   如果IP包被缓存在ARP中,ARP需要对这个包进行一次拷贝。有一种情况是不需要拷贝的,即IP包对应的pbuf是以PBUF_ROM形式存在,因为这种形式的数据包引用的是外部ROM中的一些固定数据(如静态网页数据,文件数据等等),即使这个IP包被缓存在ARP中后,不会担心所引用数据被别人修改,因此也不会出现数据异常。但是对于其他类型的数据而言(PBUF_RAM,PBUF_POOL,PBUF_REF)ARP需要拷贝一份到PBUF_RAM中并缓冲。为什么必须要拷贝呢,这里面的历史很长,见这里:
       http://savannah.nongnu.org/bugs/?func=detailitem&item_id=11400#comment0

  好了,我们来看看这里进行pbuf拷贝判断以及拷贝的例子,etharp.c中的etharp_query函数中:

if (arp_table[i].state == ETHARP_STATE_PENDING) {  //如果目的IP地址的MAC解析还没有完成
     struct pbuf *p;
      intcopy_needed = 0;   //标记是否需要拷贝
      p =q;

     //这里循环检测pbuf链表,如果发现任意的非PBUF_ROM节点,那么就需要拷贝
      while(p) { 
         if(p->type != PBUF_ROM){
            copy_needed = 1;
            break;
         }
         p = p->next;
     }

     if(copy_needed) {      //如果需要拷贝,则申请p->tot_len长度的空间,
         p = pbuf_alloc(PBUF_RAW,p->tot_len, PBUF_RAM);
         if(p != NULL) {
              if (pbuf_copy(p, q) !=ERR_OK) {     //拷贝pbuf链表
                 pbuf_free(p);
                 p = NULL;
         }
      }
    }

   这里的问题是,经过while(p)的循环后,p->tot_len并不是原始IP数据包的长度了,而是第一个非PBUF_ROM节点处的tot_len,因此如果我们试图发送一个pbuf(PBUF_ROM)->pbuf(PBUF_RAM/PBUF_POOL)->....类型的数据包时,内核就必然出错(assert发生)。
    这里正确的写法是:
       p =pbuf_alloc(PBUF_RAW, q->tot_len, PBUF_RAM);
   值得庆幸的是,在内核中,极少产生这种pbuf(PBUF_ROM)->pbuf(PBUF_RAM/PBUF_POOL)->....类型的数据包,因此内核这种异常也很难被跑到。但是这是一个很明显的代码设计缺陷,值得引起大家的注意。


0 0