首页 -> 安全研究
安全研究
绿盟月刊
绿盟安全月刊->第19期->技术专题
作者: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;
}
版权所有,未经许可,不得转载