注册 登录  
 加关注
   显示下一条  |  关闭
温馨提示!由于新浪微博认证机制调整,您的新浪微博帐号绑定已过期,请重新绑定!立即重新绑定新浪微博》  |  关闭

Tsecer的回音岛

Tsecer的博客

 
 
 

日志

 
 

netfilter中环路检测代码注释  

2014-04-04 20:54:22|  分类: Linux内核 |  标签: |举报 |字号 订阅

  下载LOFTER 我的照片书  |
最近业余时间看了看netfilter的代码,其中rule中的target可以有跳转,这就是一个相对比较危险的操作,如果这个跳转存在环路,在运行时就会出现内核锁死,所以在netfilter注册的时候会静态检测是否"可能"存在跳转环路,这个代码就是在mark_source_chains中完成的,代码有些绕,看了一点,作个总结,很多其它的总结也没时间在继续码了,就匆忙扔个大致写的注释,作个记录,可能有错别字,而且可能也比较乱,但是大致的意思应该是没错的。

/* Figures out from what hook each rule can be called: returns 0 if
   there are loops.  Puts hook bitmask in comefrom. */
static int
mark_source_chains(struct xt_table_info *newinfo,
  unsigned int valid_hooks, void *entry0)
{
unsigned int hook;

/* No recursion; use packet counter to save back ptrs (reset
  to 0 as we leave), and comefrom to save source hook bitmask */
for (hook = 0; hook < NF_INET_NUMHOOKS; hook++) {
unsigned int pos = newinfo->hook_entry[hook];
struct ipt_entry *e = (struct ipt_entry *)(entry0 + pos);

if (!(valid_hooks & (1 << hook)))
continue;
         /* 这里复用了e->counters中的pcnt和comefrom两个字段,其pcnt复用为了一个rule
         * 的前向跳转链的链表,这一点如果如果对各种体系结构下函数堆栈链表
         *熟悉的话,这个相当于对应的反向链表 ,而pos相当于栈帧头指针,相当于
         * EBP指针
         * comefrom相当于栈帧链表中各个存储空间,也就是链表中的一个节点,或者更为通俗
         * 的方法就是链表中的各个元素的next指针
         * 这里最为直观和简单的方法使用递归来判断这个跳转有向图中是否有环结构
         * 但是由于内核中每个进程的堆栈空间是定长的,大概为8KB空间,使用递归的
         * 在内核中没办法实现,所以这里使用了一个看起来比较不太直观的方法
         *  回归为基本的问题,判断是否存在环路就需要对每个rule的match假设匹配成功
         * 和不成功的途径都要遍历一下,这个地方就是使用非递归的方法来完成遍历和回溯
         *  这个地方有一个特殊地方:每个match只有两种结果,或者说只有bool值,所以使用
         *  一个bit就表示有向图的所有所有遍历情况
         *     这里使用的方法是使用comefrom表示来存储这些结构
         *      其中(1 << NF_INET_NUMHOOKS)表示是从五个标准入口开始尝试否访问过这个节点
         *        如果访问过这个节点,该节点的(1 << NF_INET_NUMHOOKS)位置位,但是当从调用链
         *          返回之后,这个bit位需要清除,相当于这个调用链返回了
         *      (1 << hook)表示之前走到该节点时走对match是否匹配的遍历结果,这个标志位在jump
         *      返回之后不会清零,再下次遍历尝试时根据该标志位来进行下一中情况的尝试
         */
         
/* Set initial back pointer. */
e->counters.pcnt = pos;

for (;;) {
struct ipt_standard_target *t
= (void *)ipt_get_target(e);
int visited = e->comefrom & (1 << hook);
//如果存在环路,会在这个地方报错,大家可以自己试下。
if (e->comefrom & (1 << NF_INET_NUMHOOKS)) {
printk("iptables: loop hook %u pos %u %08X.\n",
      hook, pos, e->comefrom);
return 0;
}
e->comefrom |= ((1 << hook) | (1 << NF_INET_NUMHOOKS));
            /*对于每一个rule,这里开始尝试进行遍历搜索,整个if的前一个条件
                            是对于无条件跳转的规则,这种情况下就不需要尝试跳转不跳转两种情况了
                            其中的visited如果为1,表示之前已经对这个规则进行过了尝试,
                            并且结合该if的else就可以看到,当第一次尝试rule的都是假设match是满足
                            进而该rule是进行跳转的,此处在第二个visited就是假设之前进行了跳转,此处
                            if就是尝试不进行跳转
                        */
/* Unconditional return/END. */
if ((e->target_offset == sizeof(struct ipt_entry)
   && (strcmp(t->target.u.user.name,
      IPT_STANDARD_TARGET) == 0)
   && t->verdict < 0
   && unconditional(&e->ip)) || visited) {
unsigned int oldpos, size;

if ((strcmp(t->target.u.user.name,
       IPT_STANDARD_TARGET) == 0) &&
   t->verdict < -NF_MAX_VERDICT - 1) {
duprintf("mark_source_chains: bad "
"negative verdict (%i)\n",
t->verdict);
return 0;
}
                //遇到能够给出判断的结点时,开始堆栈回溯
/* Return: backtrack through the last
  big jump. */
/*这里的循环从调用链回溯到上次的一个长跳转,这个"长跳转"是相对于直接"滑落"
   的连续rule,例如ruleA,ruleB是同一个chain中两个连续的rule,那么这个循环
   就是跳跃过这些连续的没有jump的rule,因为这些规则如果匹配成功,那
   就直接继续,如果不匹配成功,这里激进的假设匹配成功都没有环路
   那匹配不成功就更不会有环路了
*/
do {
e->comefrom ^= (1<<NF_INET_NUMHOOKS);//堆栈回溯时清空曾经访问过该节点标志位
#ifdef DEBUG_IP_FIREWALL_USER
if (e->comefrom
   & (1 << NF_INET_NUMHOOKS)) {
duprintf("Back unset "
"on hook %u "
"rule %u\n",
hook, pos);
}
#endif
oldpos = pos;
pos = e->counters.pcnt;
e->counters.pcnt = 0;//堆栈回溯,相当于从堆栈中pop出一个target

/* We're at the start. */
if (pos == oldpos)//回到jump的非跳转的位置
goto next;

e = (struct ipt_entry *)
(entry0 + pos);
} while (oldpos == pos + e->next_offset);
                //这里就是假设rule的match完成匹配,继续下一条rule
/* Move along one */
size = e->next_offset;
e = (struct ipt_entry *)
(entry0 + pos + size);
e->counters.pcnt = pos;
pos += size;
} else {
int newpos = t->verdict;
                //第一次遇到该rule,
if (strcmp(t->target.u.user.name,
  IPT_STANDARD_TARGET) == 0
   && newpos >= 0) {//如果该rule的target是jump,第一次遇到时假设匹配完成所以进行跳转
if (newpos > newinfo->size -
sizeof(struct ipt_entry)) {
duprintf("mark_source_chains: "
"bad verdict (%i)\n",
newpos);
return 0;
}
/* This a jump; chase it. */
duprintf("Jump rule %u -> %u\n",
pos, newpos);
} else {//不是跳转,直接"滑落"到下一个rule
/* ... this is a fallthru */
newpos = pos + e->next_offset;
}
e = (struct ipt_entry *)
(entry0 + newpos);
e->counters.pcnt = pos;//将回向调用链记录在该rule的pcnt中
pos = newpos;
}
}
next:
duprintf("Finished chain %u\n", hook);
}
return 1;
}

  评论这张
 
阅读(361)| 评论(0)
推荐 转载

历史上的今天

评论

<#--最新日志,群博日志--> <#--推荐日志--> <#--引用记录--> <#--博主推荐--> <#--随机阅读--> <#--首页推荐--> <#--历史上的今天--> <#--被推荐日志--> <#--上一篇,下一篇--> <#-- 热度 --> <#-- 网易新闻广告 --> <#--右边模块结构--> <#--评论模块结构--> <#--引用模块结构--> <#--博主发起的投票-->
 
 
 
 
 
 
 
 
 
 
 
 
 
 

页脚

网易公司版权所有 ©1997-2017