Chinaunix首页 | 论坛 | 博客
  • 博客访问: 363355
  • 博文数量: 64
  • 博客积分: 2975
  • 博客等级: 少校
  • 技术积分: 831
  • 用 户 组: 普通用户
  • 注册时间: 2007-01-14 10:59
文章存档

2014年(2)

2012年(7)

2010年(40)

2009年(5)

2008年(8)

2007年(2)

分类: LINUX

2010-05-25 21:48:23

1.定义计数数据结构
(linux/netfilter_ipv4/ip_conntrach.h)

struct ipc_counters
{
    u_int64_t pcnt, bcnt;            /* Packet and byte counters */
    spinlock_t lock;//在SMP环境中更新pcnt和bcnt时同步
};

2.修改ip_conntrack数据结构
(linux/netfilter_ipv4/ip_conntrach.h)
在struct ip_conntrack末尾添加

    struct ipc_counters counters;

3.ipc_counters结构初始化
(net/ipv4/netfilter/ip_conntrack_core.c)
ipc_counters结构应该随同ip_conntrack结构的分配和初始化时进行,这只有一个地方init_conntrack中

    /* Mark clearly that it's not in the hash table. */
    conntrack->tuplehash[IP_CT_DIR_ORIGINAL].list.next = NULL;
   
    spin_lock_init(conntrack->counters.lock);//只初始化lock. pcnt和bcnt因为ip_conntrack结构的memset一并清0

4.ipc_counters数据采集
采集时机尽量靠前,理由是当进行流量控制时,在第一时间丢包比经过复杂的处理流程后再丢包代价小的多。最靠前的地方是conntrack在PREROUTING中注册的钩子函数ip_conntrack_in.在该函数中,会根据数据包找到对应的conntrack结构(没有的话会创建),接着执行状态转换和维护工作。因此采集放在找到conntrack结构之后最好。

查找conntrack的代码在ip_conntrack_in->resolve_normal_ct中,添加如下

///《《《《《《《《《《《《《《《《《《《《《
#define ADD_COUNTER(c,b,p) do { spin_lock(&c.lock);(c).bcnt += (b); (c).pcnt += (p);spin_unlock(&c.lock);} while(0)//计数宏,里面有自选锁
///《《《《《《《《《《《《《《《《《《《《《

/* 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,
          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);

    if (!get_tuple(skb->nh.iph, skb->len, &tuple, proto))
        return NULL;

    /* look for tuple match */
    h = ip_conntrack_find_get(&tuple, NULL);
    if (!h) {
        h = init_conntrack(&tuple, proto, skb);
        if (!h)
            return NULL;
        if (IS_ERR(h))
            return (void *)h;
    }

//现在找到ip_conntrack,可以采集数据了
//《《《《《《《《《《《《《《《《《《《《《《       
    ADD_COUNTER(h->ctrack->counters, ntohs(skb->nh.iph->tot_len), 1);
//<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<

这是最主要的采集点,仔细研究ip_conntrack_in后,还有一个地方也要加采集代码

    /* It may be an icmp error... */
    if ((*pskb)->nh.iph->protocol == IPPROTO_ICMP
        && (ct=icmp_error_track(*pskb, &ctinfo, hooknum)))
    {       
//icmp错数据包不会经过resolve_normal_ct
//<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
        ADD_COUNTER(ct->counters, ntohs((*pskb)->nh.iph->tot_len), 1);
//<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
        return NF_ACCEPT;
    }

到这里,采集代码添加完成了

5.数据呈现
显示conntrack数据的最好地方是/proc/net/ip_conntrack,通过print_conntrack(net/ipv4/netfilter/ip_conntrack_standalone.c)

    len += sprintf(buffer + len, "use=%u ",
               atomic_read(&conntrack->ct_general.use));

//<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
    spin_lock(&conntrack->counters.lock);
   
    len += sprintf(buffer + len, "pcnt=%Lu ",
               conntrack->counters.pcnt);

    len += sprintf(buffer + len, "bcnt=%Lu ",
               conntrack->counters.bcnt);
   
    spin_unlock(&conntrack->counters.lock);
//<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<

    len += sprintf(buffer + len, "\n");

到这里,所有修改工作完成

6.编译
make modules
make modules_install

ip_conntrack是作为模块编译的,不必重启内核,就能验证修改的效果。

lsmod看看有没有ip_conntrack模块,rmmod掉,再insmod.

cat /proc/net/ip_conntrack看看
[root@localhost linux-2.4]# cat /proc/net/ip_conntrack
tcp      6 431989 ESTABLISHED src=10.1.9.45 dst=10.1.9.30 sport=4784 dport=139 src=10.1.9.30 dst=10.1.9.45 sport=139 dport=4784 use=1 pcnt=3325 bcnt=615287
tcp      6 431999 ESTABLISHED src=10.1.9.30 dst=10.1.9.45 sport=22 dport=4762 src=10.1.9.45 dst=10.1.9.30 sport=4762 dport=22 use=1 pcnt=328 bcnt=24500

呵呵,成功了.





阅读(1491) | 评论(1) | 转发(0) |
给主人留下些什么吧!~~

chinaunix网友2010-11-29 16:39:01

这是基于什么内核版本的,在内核2.6.24.7等版本已经有计数统计了。