首页 -> 安全研究

安全研究

绿盟月刊
绿盟安全月刊->第19期->技术专题
期刊号: 类型: 关键词:
ip_filter中solaris核心包截获部分原码解析

作者:newchess(mailto: newchess@21cn.com)
日期:2001-02-19



    在solaris核心态获取包一直是个让人头疼的问题,又没有相关的正式资料可参考,最近读了读ip_filter3.4.14的源码,获益非浅,因此写了点东东出来,介绍一下ip_filter的编写思路,由于ip_filter是个比较大的东东,我只介绍关于包截获的结构部分,其他关于规则,过滤等就要等有时间再说了:),水平有限,大家不要用烂柿子,臭鸡蛋什么的丢我啊:)

1  概述

  ip_filter 3.4.14 的思想与ttywatcher的思想有些相近之处,作者设计了一个设备驱动模块用于和用户态通信,在设备驱动模块的初始化例程solattach()中作者建立了一个全局qif结构链表,这个链表中的每一项与内核全局ill_s链表(这个链表中的每一项都是一个流模块,有上行及下行队列,大概是对应网络设备的底层模块)中的项存在对应关系,任一个qif链表项将ill_s对应项的模块接入点指针q_qinfo保存下来,然后让它指向新的处理程序fr_qin()及fr_qout(),在新的处理程序中进行包的检查,分析,过滤,这种方式真的很妙,可以认为是钩子原理的一种应用吧:)
   ttywatcher则是压入一个流模块,引出一条消息旁路,接到一设备驱动模块上
并与外界通信(强烈建议大家读一读它的核心部分的原码,精彩的很!)


2  数据结构

    在头文件 inet/ip.h 中定义了如下全局指针

    extern struct ill_s *ill_g_head;
    
    它指向一个全局链表,每个链表项是ill_s类型。
    对于一个ill_s 结构而言,它是一个流模块的私有数据,在下层的驱动设备打开时实例化并对应一条消息通路,它有读写队列,ip_filter正是通过修改它的读队列的q_qinfo指针及它的写队列的下邻接队列(这里我也搞不明白为什么要这样做,好像应该修改它的写队列的q_qinfo指针才对,见  solaris.c 中  solattach函数的 out = il->ill_wq->q_next;)的q_qinfo指针来修改消息的处理例程,加入消息(即包数据)的分析,校验等处理.
     


hook之前:
               ill_s结构
               -------
               |      |
               |      |
......-----〉 |      |-----〉......  
               |      |
               |      |
               |      |
               --------
                |            读队列
                | ill_rq     ------                  
                |----------->|    | q_qinfo指针      
                             |    |----------------->qinit结构体
                             |    |             
                             |    |            
                             |    |            
                             ------            
                                       
hook之后:

               ill_s结构
               -------
               |      |
               |      |
......-----〉 |      |-----〉......  
               |      |
               |      |
               |      |
               --------
                |            读队列
                | ill_rq     ------                    qif结构
                |----------->|    | q_qinfo指针 ----------------------      
                             |    |-------------|---|     qf_rqinfo---|---->该队列的原qinit结构
                             |    |             |   |   qf_rqinit     |
                             |    |             |   |    ---------    |
                             |    |             |   |--->|       |    |
                             ------             |        |qi_putp------->fr_qin    
                                                |        |       |    |
                                                |        |       |    |
                                                |        |       |    |
                                                |        |-------|    |
                                  ......------> |                     |------>......
                                                |---------------------|

                   注:  ill_s结构体的ill_wq的下邻接队列的qinit也包含在此图的qif结构体中


    作者定义了结构qif用于hook

    typedef struct qif {  
struct qif *qf_next;  /*指向链表中下一项*/
ill_t *qf_ill;           /*指向对应的ill_s 结构*/
kmutex_t qf_lock;   
void *qf_iptr;  /*        */
void *qf_optr;  /*        */
queue_t *qf_in;    /*下行队列*/
queue_t *qf_out;   /*上行队列*/
struct qinit *qf_wqinfo;  /*保存ill_s 中写队列的qinfo指针*/    
struct qinit *qf_rqinfo;  /*保存ill_s 中写队列的qinfo指针*/
struct qinit qf_wqinit;   /*作者自己的写队列qinit结构*/
struct qinit qf_rqinit;   /*作者自己的读队列qinit结构*/

/*私有数据*/
mblk_t *qf_m;
queue_t *qf_q;
size_t qf_off;
size_t qf_len;
char qf_name[8];  
/*
  * in case the ILL has disappeared...
  */
size_t qf_hl; /* header length ,这个东东似乎与ipv6及solaris 8有关*/
int qf_sap;
     }
    作者在内核中建立了一个qif结构全局链表,与ill_s 链表相对应,分别勾对应 ill_s 项的qinfo指针      


3  原码解析

    在solaris.c中作者写了一个设备驱动程序,其中的一些函数(如ipf_getinfo,ipf_identify,ipf_probe,ipf_attach,ipf_detach)的写法与标准的设备驱动程序并没有什么不同,在此不加讨论,至于_init,_fini,_info这几个更不用多说,我们首先关注的是函数solattach(),它负责qif链表的建立,初始化,我们先来讨论一下它的结构。
       
    函数功能:  在全局链表ill_g_head中查找那些无对应qif结构的项,为它们分配qif结构,并进行hook,这里所说的对应是指某个ill_s结构的读队列及写队列已被hook到同一个qif结构上
                

void solattach()
{

queue_t *in, *out;
struct frentry *f;
qif_t *qif, *qf2;
ipnat_t *np;
size_t len;
ill_t *il;

/*为了便于分析,我把部分源码加了行号*/  

1 for (il = ill_g_head; il; il = il->ill_next) {

                /*
                遍历 ill_g_head 链表 ,寻找读写队列均不为空的项
                */


2                in = il->ill_rq;
        
3  if (!in || !il->ill_wq)
4   continue;
         

5  out = il->ill_wq->q_next; /*这里有个疑问,稍后我们会提到*/

6  WRITE_ENTER(&ipfs_mutex);

  /*
   * Look for entry already setup for this device
   *
   */

        /*
           只有在qif链表中无当前ill_s项的对应项时才分配qif结构
       
            详述:   当hook成功时qif结构的qf_iptr及qf_optr域均被置成对应读写队列的q_ptr,因此可以根据这一点来搜索链表,当前的ill_s项的读写队列如果均被链接到同一个业已存在的qif结构,则可以认定当前ill_s结构对应的qif结构已存在,不必再分配新的qif结构了
                -----------------从这里似乎可以看出ill_s链表中存在队列的复用

        */
           
7  for (qif = qif_head; qif; qif = qif->qf_next)
8   if (qif->qf_iptr == in->q_ptr &&
9       qif->qf_optr == out->q_ptr)
10    break;
11  if (qif) {
12   RWLOCK_EXIT(&ipfs_mutex);
13   continue;
14  }
        
15                KMALLOC(qif, qif_t *);
16  if (!qif) {
   
17   RWLOCK_EXIT(&ipfs_mutex);
18   continue;
19  }
        

        /*
             假如队列可被复用:
           当当前ill_t 结构的读队列的模块接入点程序put()已被赋为fr_qin() 时(即当前ill_t 结构的读队列已被hook了,但要注意的是此时写队列或者未被hook,或者被hook到另一个qif结构),搜索qif链表,找到对应同一个qinit 结构的qif结构,取得保存的原始qinit结构
          
              
             详述:  读队列被hook了,就无法通过in->q_qinfo来取得原始的qinit结构,因此才会出现对qif链表的搜索
        
        */
20  if (in->q_qinfo->qi_putp == fr_qin) {
21   for (qf2 = qif_head; qf2; qf2 = qf2->qf_next)
22    if (&qf2->qf_rqinit == in->q_qinfo) {

                                /*先保存一下qinit 结构体指针,为以后拷贝做准备*/
     qif->qf_rqinfo = qf2->qf_rqinfo;
     break;
23    }
             /* 如果在链表中找不到已存在的用于读队列钩子的qif项,也就是链表出错*/
24   if (!qf2) {

25    RWLOCK_EXIT(&ipfs_mutex);
26    KFREE(qif);
27    continue;
28   }
29  } else

                    /* 假如in->q_qinfo->qi_putp != fr_qin  
                     保存qinit 结构指针*/
30    qif->qf_rqinfo = in->q_qinfo;



31  if (out->q_qinfo->qi_putp == fr_qout) {
32   for (qf2 = qif_head; qf2; qf2 = qf2->qf_next)
33    if (&qf2->qf_wqinit == out->q_qinfo) {
34     qif->qf_wqinfo = qf2->qf_wqinfo;
35     break;
36    }
37   if (!qf2) {

38    RWLOCK_EXIT(&ipfs_mutex);
39    KFREE(qif);
40    continue;
41   }
42  } else
    /* 假如out->q_qinfo->qi_putp != fr_qout  
       保存qinit 结构指针*/
43       qif->qf_wqinfo = out->q_qinfo;

                  /*初始化其他域*/
44  qif->qf_ill = il;
45  qif->qf_in = in;
46  qif->qf_out = out;
47  qif->qf_iptr = in->q_ptr;
48  qif->qf_optr = out->q_ptr;


        /* 下面涉及的一些东西(不影响函数整体结构)我还没有搞清楚,在此略去部分源代码  */

                
                 ......           
                 ......


49  strncpy(qif->qf_name, il->ill_name, sizeof(qif->qf_name));
50  qif->qf_name[sizeof(qif->qf_name) - 1] = '\0';


                /*将初始化后的qif结构加入链表(从链表头加入)*/

51  qif->qf_next = qif_head;
52  qif_head = qif;



  /*
   * Activate any rules directly associated with this interface
   */


                /*规则相关数据初始化*/
               ......
           
                /*规则相关数据初始化完成*/


                /*
                  初始化qif 的qf_rqinit域
                  同时将读队列的qinit 结构体转移到qif 中(钩子的实现)
                */

53          bcopy((caddr_t)qif->qf_rqinfo, (caddr_t)&qif->qf_rqinit,
54        sizeof(struct qinit));
55  qif->qf_rqinit.qi_putp = fr_qin;
56  in->q_qinfo = &qif->qf_rqinit;

                /*
                  初始化qif 的qf_wqinit域
                  同时将写队列的qinit 结构体转移到qif 中(钩子的实现)
                */

57  bcopy((caddr_t)qif->qf_wqinfo, (caddr_t)&qif->qf_wqinit,
58        sizeof(struct qinit));
59  qif->qf_wqinit.qi_putp = fr_qout;
60  out->q_qinfo = &qif->qf_wqinit;

61  RWLOCK_EXIT(&ipfs_mutex);
62 }
63 if (!qif_head)
64  cmn_err(CE_CONT, "IP Filter: not attached to any interfaces\n");
65 return;
}


在读这个函数的过程中有几个问题待解决

   1   ill_s 链表中是否存在队列复用( 这个"复用"是我起的名字:)  )的情况

    所谓复用,如图:


               ill_s结构                   ill_s结构
               -------                      ------
               |      |                    |      |   
               |      |                    |      |
......-----〉 |      |-----〉...... ----->|      |----->......
               |      |                    |      |
               |      |                    |      |
               |      |                    |      |
               --------                    --------
                |            读队列          |ill_rq
                | ill_rq     ------ <---------                 
                |----------->|    | q_qinfo指针      
                             |    |----------------->qinit结构体
                             |    |             
                             |    |            
                             |    |            
                             ------                            

             即两个不同的ill_s 结构项的ill_rq或ill_wq指针均指向同一个队列
个人认识:

  ill_s应该是与低层的打开的device有个一一对应关系,有多少个被同时打开的网络device(可以对
同一个device打开若干次),也可以说有几个流,ill_g_head指向的链表中就有多少个ill_s结构,而
在solaris 的流机制中,流模块的队列应该是与流相关的,两个不同的流即使在多路复用模块中也不
大可能复用同一个队列吧?                 
  但如果不存在复用,第20行的代码又无法解释:56行的代码改变了一个队列的q_qinfo指针,怎么会
导致其他的ill_s结构的队列的q_qinfo发生变化?
  这个问题有待大牛们指点......

   2   ill_s 结构的一些细节还有待了解        

   3   读队列与写队列不对称,读队列是ill_s的ill_rq指针,而写队列比ill_wq指针指向的队列还要低
一层,这是为什么?                 




     下一个我们要关心的函数是fr_qin及fr_qout ,这两个函数被用于消息队列的put例程,负责着消息
的处理,过滤的主要部分就是在这个函数及其调用函数完成的。

     内核中的数据结构(尤其是链表)不断发生变化,就要求我们在处理消息前必须考虑ill_s链表与
qif链表的同步问题。


int fr_qin(q, mb)
queue_t *q;
mblk_t *mb;
{

        /*这个函数指针可不是白定义的,它的作用与slkm中的old_open大致相同*/
int (*pnext) __P((queue_t *, mblk_t *)), type, synced = 0, err = 0;
qif_t qf, *qif;
     
        /*检查是否模块已attach*/          
if (fr_running <= 0) {
  mb->b_prev = NULL;
  freemsg(mb);
  return 0;
}

/*如果消息的数据块引用次数大于一,复制一个,为消息上行做准备*/

if (mb->b_datap->db_ref > 1) {

  mblk_t *m1;
        m1 = copymsg(mb);
  freemsg(mb);
  mb = m1;
  frstats[0].fr_copy++;
}

READ_ENTER(&ipf_solaris);



again:
if (fr_running <= 0) {
  
  RWLOCK_EXIT(&ipf_solaris);
  mb->b_prev = NULL;
  freemsg(mb);
  return 0;
}
READ_ENTER(&ipfs_mutex);


    /*  
          随着接口的不断打开和关闭,solattach()建立的qif链表也许需要刷新,某些
          ill_s结构可能已经不存在了,而它们对应的qif结构却可能仍在qif链表中,同样
          某些新的ill_s结构可能没有对应的qif结构         

          函数ipfsync()是刷新ill_s链表与qif链表的函数,功能是搜索qif链表,找到那些
          无对应ill_s结构的qif项,将它们释放后重新执行solattach()                
    */
if (!(qif = qif_from_queue(q))) {

        
  for (qif = qif_head; qif; qif = qif->qf_next)
          
   if (&qif->qf_rqinit == q->q_qinfo && qif->qf_rqinfo &&
     qif->qf_rqinfo->qi_putp) {
               
                                /* 置pnext 为qif结构保存的原put 例程*/
    pnext = qif->qf_rqinfo->qi_putp;
    frstats[0].fr_notip++;
    RWLOCK_EXIT(&ipfs_mutex);
    if (!synced) {
     ipfsync();
     synced = 1;
     goto again;
    }
    RWLOCK_EXIT(&ipf_solaris);
    /* fr_donotip(0, NULL, q, mb, mb, NULL, 0); */
    return (*pnext)(q, mb);
   }
  RWLOCK_EXIT(&ipfs_mutex);
  if (!synced) {
   ipfsync();
   synced = 1;
   goto again;
  }

  frstats[0].fr_drop++;
  RWLOCK_EXIT(&ipf_solaris);
  mb->b_prev = NULL;
  freemsg(mb);
  return 0;
}

        /* 当由队列q可以找到对应的qif结构时 */

        /* 复制qif结构 */
   
bcopy((char *)qif, (char *)&qf, sizeof(qf));
qif = &qf;
type = MTYPE(mb);
pnext = qif->qf_rqinfo->qi_putp;

       /*
        如果是数据消息或M_BREAK 消息,转入处理函数
       */
        /*  datamsg(type) 测试一个消息是否是数据消息,type 是这个消息的类型*/

        if (datamsg(type) || (type == M_BREAK))
       

             /*实际的过滤及校验在fr_precheck中完成*/

             err = fr_precheck(&mb, q, qif, 0);

RWLOCK_EXIT(&ipfs_mutex);
RWLOCK_EXIT(&ipf_solaris);

        /* 如果通过过滤 */
if ((err == 0) && (mb != NULL)) {

         /* 将消息 发往下一个队列 */
  if (pnext)
   return (*pnext)(q, mb);

}
if (mb) {
  mb->b_prev = NULL;
  freemsg(mb);
}
return 0;
}




    最主要的消息重组,脱包头工作是在fr_precheck函数中完成的
    它又调用fr_check进行过滤处理

    在这里我们关心的重点是消息的结构,我从原码中得到的信息是(不一定完全准确)

              
    -----------------------
    |                      |
    | 消息类型:           |
    |   m_proto/m_pcproto  |       ---------------------
    |            b_cont----------->| 类型:m_data      |
    |  消息数据:          |       |    b_cont------->.|......
    |                      |       |  消息数据:       |
    |  dl_unitdata_ind_t   |       | 链路层包头+ip包头+|....
    |      结构体          |       |                   |
    | (ind或req)           |       |--------------------
    |-----------------------

    第一种消息结构是协议类型消息块(内含dl_unitdata_ind_t结构体),后挂一个或多个m_data类型消息块
里面的东东可多了: 数据链路层包头,ip包头,tcp/udp......

    -----------------------
    |                      |
    | 消息类型:           |
    |   m_proto/m_pcproto  |   
    |                      |
    |  消息数据:          |
    |              b_cont--|--->null      
    | ip包                 |        
    |                      |       
    |                      |       
    |-----------------------

      第二种消息结构是单消息块协议类型消息块,内含一个完整的ip包

下面我们来分析这个函数:

static int fr_precheck(mp, q, qif, out)
mblk_t **mp;
queue_t *q;
qif_t *qif;
int out;
{
register mblk_t *m, *mt = *mp;
register ip_t *ip;
size_t hlen, len, off, mlen, iphlen, plen;
int err, synced = 0, sap, p;
u_char *bp;


#if SOLARIS2 >= 8
ip6_t *ip6;
#endif

#ifndef sparc
u_short __ipoff;
#endif
tryagain:

ip = NULL;
m = NULL;
/*
         * If there is only M_DATA for a packet going out, then any header
  * information (which would otherwise appear in an M_PROTO mblk before
  * the M_DATA) is prepended(预先考虑) before the IP header.  We need to set the
  * offset to account for this. - see MMM
        */

         /*off初值为ip头标长度*/
off = (out) ? qif->qf_hl : 0;

/*
  * If the message protocol block indicates that there isn't a data
  * block following it, just return back.
  */
        

bp = (u_char *)ALIGN32(mt->b_rptr);

  /*如果为协议类型消息*/
  if (MTYPE(mt) == M_PROTO || MTYPE(mt) == M_PCPROTO) {
  
         /*
           dl_unitdata_ind_t 定义于dlpi.h
           如果当前消息块不是无连接链路层数据消息
         */
  dl_unitdata_ind_t *dl = (dl_unitdata_ind_t *)bp;
  if (dl->dl_primitive != DL_UNITDATA_IND &&
      dl->dl_primitive != DL_UNITDATA_REQ) {
   
   /*以IP 头方式访问数据*/
   
   ip = (ip_t *)dl;
   
   
   /*注意啊!这里的ip_t结构是作者定义的,不包含选项及填充域
                          ip_hl的单位好像是32位字,因此sizeof(*ip)才要除以4
                          如果是一个正确的单消息块消息,里面是一个完整的ip包,进行下一步处理*/
   
   if ((ip->ip_v == IPVERSION) &&
       
       (ip->ip_hl == (sizeof(*ip) >> 2)) &&
          /*ip->ip_len 为数据包总长度*/
       (ntohs(ip->ip_len) == mt->b_wptr - mt->b_rptr)) {
    off = 0;
    m = mt;
   } else {
           frstats[out].fr_notdata++;
    return 0;
   }
  }
}

/*
  * Find the first data block, count the data blocks in this chain and
  * the total amount of data.
        */

/*数据类型消息不用处理
          DL_UNITDATA_IND,DL_UNITDATA_REQ协议类型消息查找后面的第一个m_data类型消息块*/

if (ip == NULL)
  /*遍历消息,找到第一个m_data类型的消息块*/
  
  for (m = mt; m && (MTYPE(m) != M_DATA); m = m->b_cont)
   off = 0; /* Any non-M_DATA cancels the offset */
                                        /*任何非M_DATA类型的数据都置off为零*/
             
    
if (!m) {
  
                  
  frstats[out].fr_nodata++;
  return 0; /* No data blocks */

}


        /*
         到此时为止,m指向消息中第一个m_data类型的消息块
        */

/*
  * This is a complete kludge to try and work around some bizarre(奇特的)
  * packets which drop through into fr_donotip.
  */

/*如果当前消息块不是M_DATA类型且为协议类型
          是广播模式下的DL_UNITDATA_IND (无连接链路层数据)
          注:  dl->dl_group_address == 1表示工作在广播模式下
*/

if ((mt != m) && (MTYPE(mt) == M_PROTO || MTYPE(mt) == M_PCPROTO)) {
    
  dl_unitdata_ind_t *dl = (dl_unitdata_ind_t *)bp;
  
    
  if ((dl->dl_primitive == DL_UNITDATA_IND) &&
      (dl->dl_group_address == 1))
   
                           
                           /*为什么?那位老大见多识广,帮忙指点一下*/
   if (((*((u_char *)m->b_rptr) == 0x0) &&
       ((*((u_char *)m->b_rptr + 2) == 0x45))))
    off += 2;
}

/*将外加协议去掉,露出ip的东东*/

ip = (ip_t *)(m->b_rptr + off);  /* MMM */

/*
  * We might have a 1st data block which is really M_PROTO, i.e. it is
  * only big enough for the link layer header
  */

       /*
         下面的这段代码用于把m起始的多消息块脱去外加协议头(似乎是数据链路层包头)
         露出ip包                    
       */
-----
| /*如果本消息块小于外加协议头的长度,跳到下一个消息块,直到本消息块含有ip包的东东为止*/
|
| while ((u_char *)ip >= m->b_wptr) {
|  len = (u_char *)ip - m->b_wptr;
|  m = m->b_cont;
|  if (m == NULL)
|   return 0; /* not enough data for IP */
|  ip = (ip_t *)(m->b_rptr + len);
| }
|
|
| off = (u_char *)ip - m->b_rptr;
|
| if (off != 0)
|  m->b_rptr = (u_char *)ip;
|        /*去除头后的本消息块有效数据长度*/
| len = m->b_wptr - m->b_rptr;
|
| /* 坏了的包 ,返回-1*/
| if (m->b_wptr < m->b_rptr) {
|  cmn_err(CE_NOTE, "!IP Filter: Bad packet: wptr %p < rptr %p",
|   m->b_wptr, m->b_rptr);
|  frstats[out].fr_bad++;
|  return -1;
| }
--------

/*获得消息中的数据字节数*/

mlen = msgdsize(m);


/* sap是啥?  是dlpi中的 service access  pointer  ? */

sap = qif->qf_ill->ill_sap;
        /* 那位大哥能提供关于sap的资料,在下感激不尽*/
if (sap == 0x800) {
  u_short tlen;
                /*hlen为ip头的大小*/
  hlen = sizeof(*ip);

  /* XXX - might not be aligned (from ppp?) */
  ((char *)&tlen)[0] = ((char *)&ip->ip_len)[0];
  ((char *)&tlen)[1] = ((char *)&ip->ip_len)[1];
                /*plen为包的大小*/
  plen = ntohs(tlen);

  sap = 0;
}
#if SOLARIS2 >= 8
else if (sap == IP6_DL_SAP) {
  u_short tlen;

  hlen = sizeof(ip6_t);
  ip6 = (ip6_t *)ip;
  /* XXX - might not be aligned (from ppp?) */
  ((char *)&tlen)[0] = ((char *)&ip->ip_len)[0];
  ((char *)&tlen)[1] = ((char *)&ip->ip_len)[1];
  plen = ntohs(tlen);
  sap = IP6_DL_SAP;
}
#endif
else {

  hlen = 0;
  sap = -1;
}

/*
  * Ok, the IP header isn't on a 32bit aligned address so junk it.
  */

            
if (((u_int)ip & 0x3) || (len < hlen) || (sap == -1)) {
  mblk_t *m2;
  u_char *s;

  /*
   * Junk using pullupmsg - it's next to useless.
   */
fixalign:
  
/*下面的代码将以m为起始的多消息块组装为单消息块数据类型消息mt,将原来的消息释放掉*/

------  
|  
|  /*消息的数据大小*/
|  
|  len = msgdsize(m);
|  
|         /*分配消息块*/
|
|  m2 = allocb(len, BPRI_HI);
|  
|  /*分配不成功*/
|  if (m2 == NULL) {
|   frstats[out].fr_pull[1]++;
|   return -1;
|  }
|                /*分配成功*/
|  m2->b_wptr = m2->b_rptr + len;
|  
|  
|  /*将消息 m(多个消息块)中的数据全拷到消息块 m2 中 */
|  s = (u_char *)ip;
|   
|  for (bp = m2->b_rptr; m; bp += len) {
|  
|          /* 消息块m中还有多少有效数据(除去ip头)*/
|   len = m->b_wptr - s;
|   /*好像不大对劲 似乎应为 bcopy(s, bp, len); */
|   bcopy(m->b_rptr, bp, len);
|   m = m->b_cont;
|   if (m)
|    s = m->b_rptr;
|  }
|  
|  *mp = m2;
|  MTYPE(m2) = M_DATA;
|  
|  /*将原来的消息释放掉*/
|  freemsg(mt);
|  
|  mt = m2;
|
|  frstats[out].fr_pull[0]++;
|  synced = 1;
|  off = 0;
|              
|                goto tryagain;
| }
------------




if (((sap == 0) && (ip->ip_v != IP_VERSION))
#if SOLARIS2 >= 8
     || ((sap == IP6_DL_SAP) && ((ip6->ip6_vfc >> 4) != 6))
#endif
) {
  m->b_rptr -= off;
  if (!synced) {
   synced = 1;
   RWLOCK_EXIT(&ipfs_mutex);
   ipfsync();
   READ_ENTER(&ipfs_mutex);
   goto tryagain;
  }
  frstats[out].fr_notip++;
  return (fr_flags & FF_BLOCKNONIP) ? -1 : 0;
}




/*假如 x86 系统 */

#ifndef sparc
# if SOLARIS2 >= 8
if (sap == IP6_DL_SAP) {
  ip6->ip6_plen = plen;
} else {
# endif
  __ipoff = (u_short)ip->ip_off;

  ip->ip_len = plen;
  ip->ip_off = ntohs(__ipoff);
# if SOLARIS2 >= 8
}
# endif
#endif

/* end 假如 x86 系统*/



if (sap == 0)
  iphlen = ip->ip_hl << 2;
#if SOLARIS2 >= 8
else if (sap == IP6_DL_SAP)
  iphlen = sizeof(ip6_t);
#endif

          /*假如包长度不对*/   
-------
| if ((
|#if SOLARIS2 >= 8
|      (sap == IP6_DL_SAP) && (mlen < iphlen + plen)) ||
|     ((sap == 0) &&
|#endif
|      ((iphlen < hlen) || (iphlen > plen) || (mlen < plen)))) {
|  /*
|   * Bad IP packet or not enough data/data length mismatches
|   */
|
|
|/*  假如 x86 系统 */
|
|#ifndef sparc
|# if SOLARIS2 >= 8
|  if (sap == IP6_DL_SAP) {
|   ip6->ip6_plen = htons(plen);
|  } else {
|# endif
|   __ipoff = (u_short)ip->ip_off;
|
|   ip->ip_len = htons(plen);
|   ip->ip_off = htons(__ipoff);
|# if SOLARIS2 >= 8
|  }
|# endif
|#endif
|
|/* end 假如 x86 系统*/
|
|  m->b_rptr -= off;
|  frstats[out].fr_bad++;
|  return -1;
| }
-----------


/*
  * Make hlen the total size of the IP header plus TCP/UDP/ICMP header
  * (if it is one of these three).
  */
if (sap == 0)
  p = ip->ip_p;

#if SOLARIS2 >= 8
else if (sap == IP6_DL_SAP)
  p = ip6->ip6_nxt;

if ((sap == IP6_DL_SAP) || ((ip->ip_off & IP_OFFMASK) == 0))
#else
if ((ip->ip_off & IP_OFFMASK) == 0)
#endif

/*分析协议类型*/

  switch (p)
  {
  case IPPROTO_TCP :
   hlen += sizeof(tcphdr_t);
   break;
  case IPPROTO_UDP :
   hlen += sizeof(udphdr_t);
   break;
  case IPPROTO_ICMP :
   /* 76 bytes is enough for a complete ICMP error. */
   hlen += 76 + sizeof(icmphdr_t);
   break;
  default :
   break;
  }
          /*主要是针对icmp包*/
if (hlen > mlen) {
  hlen = mlen;
#if SOLARIS2 >= 8
} else if (sap == IP6_DL_SAP) {
  if (m->b_wptr - m->b_rptr > plen + hlen)
   m->b_wptr = m->b_rptr + plen + hlen;
#endif
} else
            if (m->b_wptr - m->b_rptr > plen)
  m->b_wptr = m->b_rptr + plen;

/*
  * If we don't have enough data in the mblk or we haven't yet copied
  * enough (above), then copy some more.
  */
if ((hlen > len)) {
  
  /* pullupmsg()  连接并排列消息中的字节*/
  if (!pullupmsg(m, (int)hlen)) {
   cmn_err(CE_NOTE, "pullupmsg failed\n");
   frstats[out].fr_pull[1]++;
   return -1;
  }
  frstats[out].fr_pull[0]++;
  ip = (ip_t *)ALIGN32(m->b_rptr);
}
qif->qf_m = m;
qif->qf_q = q;
qif->qf_off = off;
qif->qf_len = len;

        err = fr_check(ip, iphlen, qif->qf_ill, out, qif, mp);
if (err == 2)
  goto fixalign;
/*
  * Copy back the ip header data if it was changed, we haven't yet
  * freed the message and we aren't going to drop the packet.
  * BUT only do this if there were no changes to the buffer, else
  * we can't be sure that the ip pointer is still correct!
  */
if (*mp != NULL) {
  if (*mp == mt) {
   m->b_rptr -= off;
#ifndef sparc
# if SOLARIS2 >= 8
   if (sap == IP6_DL_SAP) {
    ip6->ip6_plen = htons(plen);
   } else {
# endif
    __ipoff = (u_short)ip->ip_off;
    /*
     * plen is useless because of NAT.
     */
    ip->ip_len = htons(ip->ip_len);
    ip->ip_off = htons(__ipoff);
# if SOLARIS2 >= 8
   }
# endif
#endif
  } else
   cmn_err(CE_NOTE,
    "!IP Filter: *mp %p mt %p %s\n", *mp, mt,
    "mblk changed, cannot revert ip_len, ip_off");
}
return err;
}

版权所有,未经许可,不得转载