免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
最近访问板块 发新帖
查看: 1045 | 回复: 0
打印 上一主题 下一主题

Linux下如何实现网络状态检测 [复制链接]

论坛徽章:
0
跳转到指定楼层
1 [收藏(0)] [报告]
发表于 2008-11-07 17:52 |只看该作者 |倒序浏览
. iptables规则中的state匹配
在2.4/2.6内核的Linux中的防火墙代码netfilter中实现了状态检测(stateful inspection)检测技术,在命令行接口的iptables命令是通过匹配“-m state”来实现,“-m state”匹配中定义了四种状态:
NEW,表示新连接;
ESTABLISHED,表示已经建立的连接;
RELATED,表示相关的子连接;
INVALID,表示非法状态。
所以在制定iptables规则时,只需要定义单向的规则,再加上如下两条规则,就可以保证连接回应包能正确通过防火墙而不必加反向规则:
iptables -I FORWARD -m state --state ESTABLISHED,RELATED -j ACCEPT
iptables -I FORWARD -m state --state INVALID -j DROP
(如果是保护本机,将FORWARD改为INPUT)
2. 内核中的state匹配的实现
(以下的内核代码基于2.4.26内核)
和匹配state对应的内核部分实现是ipt_state.c(net/ipv4/netfilter),匹配部分的代码非常简单,就是根据函数ip_conntrack_get()的返回值来判断数据包的状态类型:
if (!ip_conntrack_get((struct sk_buff *)skb, &ctinfo))
   statebit = IPT_STATE_INVALID;
else
   statebit = IPT_STATE_BIT(ctinfo);
3. skb与ip_conntrack的关联
再跟踪到ip_conntrack_get()函数 (net/ipv4/netfilter_ipv4/ip_conntrack_core.c),该函数有两个参数,一个是指向sk_buff数据包的指 针skb,另一个是枚举类型enum ip_conntrack_info的指针,
实际上是一个返回参数,将返回数据包的状态类型;该函数的返回值是连接结构struct ip_conntrack的指针,表示该包属于哪个连接,如果连接不存在,返回空,该包也将被认为是非法包:
struct ip_conntrack *
ip_conntrack_get(struct sk_buff *skb, enum ip_conntrack_info *ctinfo)
{
if (skb->nfct)
   return __ip_conntrack_get(skb->nfct, ctinfo);
return NULL;
}
ip_conntrack_get()函数本身也很简单,实际上是__ip_conntrack_get()函数 (net/ipv4/netfilter_ipv4/ip_conntrack_core.c)的包裹函数,判断skb的nfct是否非空,非空则将其和 枚举指针ctinfo传给__ip_conntrack_get()函数,nfct是sk_buff中用于描述netfilter conntrack信息的struct nf_ct_info指针,在内核配置了CONFIG_NETFILTER后有效。struct nf_ct_info结构在include/linux/sk_buff.h中定义:
struct nf_conntrack {
atomic_t use;
void (*destroy)(struct nf_conntrack *);
};
struct nf_ct_info {
struct nf_conntrack *master;
};
在__ip_conntrack_get()函数中,通过nfct->master获取连接指针ct,而nfct相对于ct成员infos的偏移就是连接状态:
static inline struct ip_conntrack *
__ip_conntrack_get(struct nf_ct_info *nfct, enum ip_conntrack_info *ctinfo)
{
struct ip_conntrack *ct
   = (struct ip_conntrack *)nfct->master;
/* ctinfo is the index of the nfct inside the conntrack */
*ctinfo = nfct - ct->infos;
IP_NF_ASSERT(*ctinfo >= 0 && *ctinfo
由此可见,netfilter的状态检测过程很简洁,如果skb包中的nfct指针设置了的话,可以很快地确定该skb包属于哪个连接,如果不属于任何连接则是非法包。现在的焦点是skb中的nfct值是如何设置的?
4. netfilter如何实现中skb与ip_conntrack连接的关联
nfct值是在resolve_normal_ct()函数(net/ipv4/netfilter/ip_conntrack_core.c)中定义的,该函
数被ip_conntrack_in()函数(net/ipv4/netfilter/ip_conntrack_core.c)调用,在 ip_conntrack_local()函数(net/ipv4/netfilter/ip_conntrack_standalone.c)中调用了
ip_conntrack_in()函数,而这两个函数分别是PREROUTING链和OUTPUT链的起始函数,由下面两个结构
定义(net/ipv4/netfilter/ip_conntrack_standalone.c):
static struct nf_hook_ops ip_conntrack_in_ops
= { { NULL, NULL }, ip_conntrack_in, PF_INET, NF_IP_PRE_ROUTING,
NF_IP_PRI_CONNTRACK };
static struct nf_hook_ops ip_conntrack_local_out_ops
= { { NULL, NULL }, ip_conntrack_local, PF_INET, NF_IP_LOCAL_OUT,
NF_IP_PRI_CONNTRACK };
这说明netfilter是在连接的第一个包时就建立连接了,这个连接可能是由外部发起的,也可能是由本机发起的,后续包查找连接HASH表找到相应的连接,然后赋值给skb结构中的nfct,这就是resolve_normal_ct()函数的工作:
/* On success, returns conntrack ptr, sets skb->nfct and ctinfo */
static inline struct ip_conntrack *
resolve_normal_ct(struct sk_buff *skb, // 数据包
    struct ip_conntrack_protocol *proto, // 连接协议跟踪,该结构对应协议特殊处理过程,如TCP的状态转换等
    int *set_reply, // 返回值,表示该包是否是响应方的包
    unsigned int hooknum,
    enum ip_conntrack_info *ctinfo)
{
struct ip_conntrack_tuple tuple;
struct ip_conntrack_tuple_hash *h;
IP_NF_ASSERT((skb->nh.iph->frag_off & htons(IP_OFFSET)) == 0);
// 确定该包对应的tuple,对TCP/UDP协议来说就是地址协议端口的5元组
if (!get_tuple(skb->nh.iph, skb->len, &tuple, proto))
   return NULL;
/* look for tuple match */
// 根据tuple值查找连接HASH
h = ip_conntrack_find_get(&tuple, NULL);
if (!h) {
// 在当前连接表中没找到,说明是新连接的包,初始化新连接
   h = init_conntrack(&tuple, proto, skb);
   if (!h)
    return NULL;
// 该包非法返回NULL,其他错误会返回错误号的负值
   if (IS_ERR(h))
    return (void *)h;
}
/* It exists; we have (non-exclusive) reference. */
if (DIRECTION(h) == IP_CT_DIR_REPLY) {
// 连接回应方向包
   *ctinfo = IP_CT_ESTABLISHED + IP_CT_IS_REPLY;
   /* Please set reply bit if this packet OK */
// 这个包是响应方的包
   *set_reply = 1;
} else {
// 连接原始方向的包
   /* Once we've had two way comms, always ESTABLISHED. */
   if (test_bit(IPS_SEEN_REPLY_BIT, &h->ctrack->status)) {
    DEBUGP("ip_conntrack_in: normal packet for %p\n",
          h->ctrack);
// 如果不是第一个包,设置ESTABLISHED属性,这个位标志表示发现了响应方发的包,
// 表示连接可以建立,该标志在ip_conntrack_in()函数中设置
          *ctinfo = IP_CT_ESTABLISHED;
   } else if (test_bit(IPS_EXPECTED_BIT, &h->ctrack->status)) {
    DEBUGP("ip_conntrack_in: related packet for %p\n",
          h->ctrack);
// 该标志表示是子连接的包,如FTP的数据连接
// 该标志在init_conntrack()函数中设置
    *ctinfo = IP_CT_RELATED;
   } else {
    DEBUGP("ip_conntrack_in: new packet for %p\n",
          h->ctrack);
// 没有任何标志,是连接的第一个包,设置为NEW属性
    *ctinfo = IP_CT_NEW;
   }
// 这个包是发起方的包
   *set_reply = 0;
}
// infos是数量为IP_CT_NUMBER(=5)的数组,分别对应连接发起方向的
// NEW(2)/ESTABLISHED(0)/RELATED(1)
// 和连接响应方向的ESTABLISHED(3)/RELATED(4)
// 每个skb包都会划归为其中一类, 也就是ctinfo的值
// 每个skb包根据其类型,将其nfct值设为相应infos项的地址
skb->nfct = &h->ctrack->infos[*ctinfo];
return h->ctrack;
}
在init_conntrack()函数中将连接的infos值初始化为:
...
for (i=0; i infos.master = &conntrack->ct_general;
...
也就是infos各项的值都是相同的,没有发现在其他地方重新赋值,所以把所有项实际都是相同的,指向同一个连接。设计之初可能是考虑要区分, 但实现时还是只用到一个就行了,在其他地方基本只用到infos[0]进行处理,infos的用处目前只用来判断状态,也就是每一项相对于infos [0]的偏移值。
最后讨论一下为什么在__ip_conntrack_get()函数中nfct->master值就是连接的地址:
__ip_conntrack_get(struct nf_ct_info *nfct, enum ip_conntrack_info *ctinfo)
{
struct ip_conntrack *ct
   = (struct ip_conntrack *)nfct->master;
...
skb的nfct值是infos数组中某一项的地址,而这些项中的master成员都初始化为连接中ct_general的地址,而ct_general参数是连接结构的第一项参数,所以其地址和连接结构的地址是相同,在Linux内核中,类似用法很多。
5. 结论
在每个skb数据包进入netfilter架构时,netfilter就会给其建立连接,通过简单方法就能找到连接,能区分出发起方、响应方以及第一个包还是后续包,并能识别子连接,从而实现状态检测。
连接结构中的infos数组就目前而言似乎不是很必要,单值就可以,然后增加一个连接状态参数,而不是通过infos的偏移来判断。

本文来自ChinaUnix博客,如果查看原文请点:http://blog.chinaunix.net/u/26185/showart_1386475.html
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP