在tctable中实现基于ip地址的流量统计

2632阅读 0评论2010-05-28 qtdszws
分类:LINUX

前面我们实现了基于conn的流量统计,在此基础上,我们来看看如何实现基于ip地址的流量统计,因为这更有用。

1.什么ip

对于前面的网关应用环境,我们最想统计的是内网ip的流量,为了不致于把问题搞复杂,我们先不考虑按网络地址和子网掩码区分该统计谁,而认为从eth1进来的源ip地址和到eth1去的目的ip地址都在统计之列,暂时不考虑ip欺骗等安全问题.

实际上,推而广之,这种统计模式也可以应用在任何主机(服务器)之上,而不仅仅只是网关,这样就能统计外网访问主机的按ip的数量信息。

2.统计什么
前面基于conn的流量统计统计了两个方向的包数和字节数.两个方向是orignal和reply。基于ip的流量统计至少也需要两个方向的包数和字节数,这里的方向是经过eht1由该ip发送来的方向(该ip接收 tx)和发往该ip的方向(该ip发送 rx)。有时要进步细分统计信息,例如按协议分tcp,udp,icmp以及other.这样更好了。这样组合一下有[tcp,udp,icmp,ohter]x[tx,rx]x[bcnt,pcnt]有4x2x2=16个数据.基于ip地址的流量统计还要统计连接数,有[tcp,udp,icmp,ohter]共4个连接数数据.

3.方向如何确定
在iptables中可以指定规则适用的接口,是eth0还是eth1或eth*(匹配所以eth开头接口名).数据包ksb中有指向indev和outdev的指针,dev->name为网卡名,网卡名最长为16字节字符串(含0).规则时看成4个整形数比较,速度很快。借用这种思想,eth1接受数据包的ksb->indev->name为eth1,是tx方向。发往eth1的数据包ksb->outdev->name为eth1,是rx方向。

4.管理属于ip的conn
把所有属于该ip的conn链接在一起,并为conn编上本ip中唯一id。使用ip地址和id能唯一指定conn.

说了许多废话,直接贴代码。代码参考其它内核代码写成的(例如ip_conntrack_core.c),虽然简单,也花了我两三天时间编写和调试。发现应用程序员太幸福了,程序有错误终止就行,而内核程序一旦出错,基本上就只能按power键了。

#define CONFIG_NF_DEBUG

#include
#include
#include
#include
#include
#include
#include
#include

#define ASSERT_READ_LOCK(x) MUST_BE_READ_LOCKED(&tc_counter_lock)
#define ASSERT_WRITE_LOCK(x) MUST_BE_WRITE_LOCKED(&tc_counter_lock)

#include
#include
#include


enum tc_dir_enum
{
    TC_DIR_TX,
    TC_DIR_RX,
    TC_DIR_MAX
};

enum tc_proto_enum
{
    TC_PROTO_TCP,
    TC_PROTO_UDP,
    TC_PROTO_ICMP,
    TC_PROTO_OTHER,
    TC_PROTO_MAX
};

DECLARE_RWLOCK(tc_counter_lock);
static unsigned int tc_counter_htable_size = 8209;//借用ELF中对符号的hash
static int tc_counter_max = 4099;//最大不超过4099个ip地址
static atomic_t tc_counter_count = ATOMIC_INIT(0);
static struct list_head *tc_counter_hash;
static kmem_cache_t *tc_counter_cachep;
static unsigned long tc_counter_timeout=15*60*HZ;
static char tcdevname[IFNAMSIZ] = { "eth0" };   
static char * tc_dev;
MODULE_PARM(tc_dev, "s");

struct tc_counter
{
    struct list_head list;//链入hash表
   
    u_int32_t ip;//ip地址,可作为host_id
    atomic_t use;//本结构引用
       
    struct
    {
        struct
        {
            u_int64_t pcnt, bcnt;            /*Packet and byte counters */
        }
        dir[TC_DIR_MAX];//两个方向,rx和tx
    }
    proto[TC_PROTO_MAX];//tcp,udp,icmp,ohter四个
   
    unsigned long last; /*最后一次更新时的jiffies值*/
    struct timer_list timeout;//本结构超时
       
    spinlock_t lock;//更新锁
   
    struct list_head conn_list;//属于本ip的连接
   
    u_int64_t next_conn_id;//下一个连接的id
   
    u_int32_t conn[TC_PROTO_MAX];//连接数
};

static inline u_int32_t
hash_tc_counter(u_int32_t ipaddr)
{
    return ntohl(ipaddr)
        % tc_counter_htable_size;
}

static inline int
tc_counter_cmp(const struct tc_counter * tc,u_int32_t ipaddr)
{
    MUST_BE_READ_LOCKED(&tc_counter_lock);
    return tc->ip==ipaddr;
}

static struct tc_counter *
__tc_counter_find(u_int32_t ipaddr)
{
    struct tc_counter *tc;

    MUST_BE_READ_LOCKED(&tc_counter_lock);
    tc = LIST_FIND(&tc_counter_hash[hash_tc_counter(ipaddr)],
              tc_counter_cmp,
              struct tc_counter *,
              ipaddr);
    return tc;
}

static struct tc_counter *
tc_counter_find_get(u_int32_t ipaddr)
{
    struct tc_counter *tc;

    READ_LOCK(&tc_counter_lock);
    tc = __tc_counter_find(ipaddr);
    if (tc)
        atomic_inc(&tc->use);//增加引用
    READ_UNLOCK(&tc_counter_lock);

    return tc;
}

static inline void
tc_counter_put(struct tc_counter * tc)
{
    if (tc && atomic_dec_and_test(&tc->use)){
        printk("free tc_counter:ip-%u.%u.%u.%u tc_counter_count=%u\n",NIPQUAD(tc->ip),atomic_read(&tc_counter_count));
        kmem_cache_free(tc_counter_cachep, tc);
        atomic_dec(&tc_counter_count);               
    }
}

static inline int
tc_counter_conn_cmp(const struct ipc_counter * cc,u_int64_t id)
{
    return cc->id==id;
}

static void tc_counter_add_new_conn(struct tc_counter * tc,struct ipc_counter * cc,enum tc_proto_enum tc_proto)
{
    struct ipc_counter * tcc;
    struct ip_conntrack * conn;
    struct list_head * head;
   
    spin_lock(&cc->lock);

    if(cc->parent) goto out;
/*       
    do
    {
        tcc = LIST_FIND(&tc->conn_list,tc_counter_conn_cmp,struct ipc_counter *,tc->next_conn_id++);
    }
    while(tcc);
    优化本算法,使连接按id降序
*/
    spin_lock(&tc->lock);
   
    head=&tc->conn_list;
   
    if(!list_empty(head))
    {
        struct ipc_counter * max=(struct ipc_counter *)list_entry(head->next,struct ipc_counter,list);   
        if(tc->next_conn_id>max->id)
        {
        ok:                                                       
            list_prepend(head,&cc->list);
            cc->parent=tc;
            cc->id=tc->next_conn_id++;   
           
            tc->conn[tc_proto]++;
            atomic_inc(&tc->use);

            printk("add conn:ip=%u.%u.%u.%u id=%Lu use=%u\n",NIPQUAD(tc->ip),cc->id,atomic_read(&tc->use));
           
            spin_unlock(&tc->lock);
        out:
            spin_unlock(&cc->lock);
            return;
        }
        else
        {
            //重新编号
            struct list_head *prev=head->prev;
            u_int64_t i=0;
            while(prev!=head)
            {
                ((struct ipc_counter *)list_entry(prev,struct ipc_counter,list))->id=i++;
            }
            tc->next_conn_id=i;
        }
    }   
   
    goto ok;
}

static void death_by_timeout(unsigned long ul_tc)
{
    struct tc_counter *tc = (void *)ul_tc;

    if(atomic_read(&tc->use)>1)//仍然有引用
    {
        tc->timeout.expires = jiffies+tc_counter_timeout;
        add_timer(&tc->timeout);//修改定时器   
    }
    else
    {
        WRITE_LOCK(&tc_counter_lock);
        IP_NF_ASSERT(list_empty(&tc->conn_list));
        LIST_DELETE(&tc_counter_hash
            [hash_tc_counter(tc->ip)],
            &tc->list);                                       
        WRITE_UNLOCK(&tc_counter_lock);
        tc_counter_put(tc);
    }   
}

static int
new_tc_counter(u_int32_t ipaddr)
{
    struct tc_counter *tc;
    size_t hash;

    if (tc_counter_max &&
        atomic_read(&tc_counter_count) >= tc_counter_max) {
        if (net_ratelimit())
            printk(KERN_WARNING "new_tc_counter: maximum limit of"
                   " %d entries exceeded\n", tc_counter_max);
            return 0;
    }

    hash = hash_tc_counter(ipaddr);

    tc = kmem_cache_alloc(tc_counter_cachep, GFP_ATOMIC);
    if (!tc) {
        if (net_ratelimit())
            printk(KERN_WARNING "new_tc_counter:out of memory\n");
        return 0;
    }
   

    memset(tc, 0, sizeof(struct tc_counter));
    INIT_LIST_HEAD(&tc->list);
    tc->ip=ipaddr;
    atomic_set(&tc->use, 1);
    spin_lock_init(&tc->lock);
       
    init_timer(&tc->timeout);
    tc->timeout.data = (unsigned long)tc;
    tc->timeout.function = death_by_timeout;
    tc->timeout.expires = jiffies+tc_counter_timeout;

    INIT_LIST_HEAD(&tc->conn_list);

    WRITE_LOCK(&tc_counter_lock);
    if (__tc_counter_find(ipaddr)) {
        WRITE_UNLOCK(&tc_counter_lock);
        kmem_cache_free(tc_counter_cachep, tc);
        return 1;
    }
           
    printk("new tc_counter:ip=%u.%u.%u.%u\n",NIPQUAD(ipaddr));

    add_timer(&tc->timeout);
       
    list_prepend(&tc_counter_hash[hash],
             &tc->list);
            
    atomic_inc(&tc_counter_count);
   
   
    WRITE_UNLOCK(&tc_counter_lock);

    return 1;
}

static int get_tc_proto(u_int8_t proto)
{
    enum tc_proto_enum tc_proto=TC_PROTO_OTHER;
   
    //确定流量协议
    switch(proto)
    {
        case IPPROTO_TCP:
            tc_proto=TC_PROTO_TCP;
            break;
        case IPPROTO_UDP:
            tc_proto=TC_PROTO_UDP;
            break;
        case IPPROTO_ICMP:
            tc_proto=TC_PROTO_ICMP;
            break;
    }
   
    return tc_proto;
}

/*
static void tc_counter_refresh(struct tc_counter *tc, unsigned long extra_jiffies)
{
    IP_NF_ASSERT(tc->timeout.data == (unsigned long)tc);

    WRITE_LOCK(&tc_counter_lock);
    / * Need del_timer for race avoidance (may already be dying). * /
    if (del_timer(&tc->timeout)) {//定时器已启动
        tc->timeout.expires = jiffies + extra_jiffies;
        add_timer(&tc->timeout);//修改定时器
    }
    WRITE_UNLOCK(&ip_conntrack_lock);
}
*/

static void sum(struct sk_buff * skb,
     const struct net_device *in,
     const struct net_device *out,
    const char * msg)
{
    static const char nulldevname[IFNAMSIZ] = { 0 };
    //static const char lodevname[IFNAMSIZ] = { "lo" };   
    struct iphdr *ip;
    const char *indev, *outdev;
    struct ip_conntrack *ct;
    enum ip_conntrack_info ctinfo;
    unsigned long ret;
    u_int32_t ipaddr;
    enum tc_dir_enum tc_dir;
    const char * msg2;
    enum tc_proto_enum tc_proto;
   
    //计算流量方向,相对于定义的内网网卡tc_dev,数据从该网卡来就是TX,发往该网卡就是RX
    ip=skb->nh.iph;
    tc_proto=get_tc_proto(ip->protocol);
   
    indev = in ? in->name : nulldevname;
    outdev = out ? out->name : nulldevname;
   
    do
    {
        int i;
       
        //indev是否是tc_dev
        for (i = 0, ret = 0; i < IFNAMSIZ/sizeof(unsigned long); i++) {
            ret |= ((const unsigned long *)indev)[i]
                ^ ((const unsigned long *)tcdevname)[i];
        }
   
        if(!ret){
            ipaddr=ip->saddr;
            tc_dir=TC_DIR_TX;
            break;
        }

        //outdev是否是tc_dev
        for (i = 0, ret = 0; i < IFNAMSIZ/sizeof(unsigned long); i++) {
            ret |= ((const unsigned long *)outdev)[i]
                ^ ((const unsigned long *)tcdevname)[i];
        }

        if(!ret){
            ipaddr=ip->daddr;
            tc_dir=TC_DIR_RX;
            break;
        }
/*           
        //in_dev是否是lo
        if(((const unsigned long *)indev)[0]^ ((const unsigned long *)lodevname)[0]){
            ipaddr=htonl(INADDR_LOOPBACK);
            tc_dir=TC_DIR_TX;
            break;
        }

        //out_dev是否是lo
        if(((const unsigned long *)outdev)[0]^ ((const unsigned long *)lodevname)[0]){
            ipaddr=htonl(INADDR_LOOPBACK);
            tc_dir=TC_DIR_RX;
            break;
        }
        msg2="tc_dev missed";
        goto err;
*/       
        return;
    }
    while(0);
   
    ct = ip_conntrack_get(skb, &ctinfo);
    if(ct)//有跟踪
    {              
        struct ipc_counter *cc=&ct->counter;
        int conn_dir=CTINFO2DIR(ctinfo);
        struct tc_counter * tc;
   
    again:
        spin_lock(&cc->lock);
        tc=(struct tc_counter *)cc->parent;
       
        if(tc){                   
            spin_lock(&tc->lock);                       

            cc->cnt[conn_dir].pcnt++;
            cc->cnt[conn_dir].bcnt+=ntohs(ip->tot_len);
            cc->last=jiffies;

            spin_unlock(&tc->lock);

            tc->proto[tc_proto].dir[tc_dir].pcnt++;
            tc->proto[tc_proto].dir[tc_dir].bcnt+=ntohs(ip->tot_len);
            tc->last=jiffies;
           
            //tc_counter_refresh(tc,tc_counter_timeout);
                       
            spin_unlock(&cc->lock);           
            return;
        }
       
        spin_unlock(&cc->lock);
                       
        do {
            tc = tc_counter_find_get(ipaddr);
            if (!tc
            && !new_tc_counter(ipaddr)){
                msg2="new_tc_counter failed";
                goto err;
                }
        } while (!tc);                           
       
        tc_counter_add_new_conn(tc,cc,tc_proto);           
       
        tc_counter_put(tc);
       
        goto again;
    }
   
    msg2="untracked";
err:   
    if (net_ratelimit())
        printk(KERN_DEBUG "%s:got %s packet indev:%s->outdev:%s %p %u %u.%u.%u.%u -> %u.%u.%u.%u %u\n",
           msg,
           msg2,
           indev,
           outdev,
           skb,
           skb->nh.iph->protocol,
           NIPQUAD(skb->nh.iph->saddr),
           NIPQUAD(skb->nh.iph->daddr),
           skb->nh.iph->protocol
           );       
}

/* The work comes in here from netfilter.c. */
static unsigned int
tct_forward_hook(unsigned int hook,
     struct sk_buff **pskb,
     const struct net_device *in,
     const struct net_device *out,
     int (*okfn)(struct sk_buff *))
{
    sum(*pskb,in,out,"tct_forward_hook");   
   
    return NF_ACCEPT;
}

static unsigned int
tct_local_in_hook(unsigned int hook,
     struct sk_buff **pskb,
     const struct net_device *in,
     const struct net_device *out,
     int (*okfn)(struct sk_buff *))
{
    sum(*pskb,in,out,"tct_local_in_hook");   
   
    return NF_ACCEPT;
}

static unsigned int
tct_local_out_hook(unsigned int hook,
           struct sk_buff **pskb,
           const struct net_device *in,
           const struct net_device *out,
           int (*okfn)(struct sk_buff *))
{
    /* root is playing with raw sockets. */
    if ((*pskb)->len < sizeof(struct iphdr)
        || (*pskb)->nh.iph->ihl * 4 < sizeof(struct iphdr)) {
        if (net_ratelimit())
            printk("ipt_hook: happy cracking.\n");
        return NF_ACCEPT;
    }

    sum(*pskb,in,out,"tct_local_out_hook");   
       
    return NF_ACCEPT;
}

static struct nf_hook_ops tct_ops[]
= { { { NULL, NULL }, tct_local_in_hook, PF_INET, NF_IP_LOCAL_IN, NF_IP_PRI_FILTER+10 },
    { { NULL, NULL }, tct_forward_hook, PF_INET, NF_IP_FORWARD, NF_IP_PRI_FILTER+10 },
    { { NULL, NULL }, tct_local_out_hook, PF_INET, NF_IP_LOCAL_OUT, NF_IP_PRI_FILTER+10 }
};

static void tc_counter_cleanup_conntrack(struct ip_conntrack *conn)
{
    struct ipc_counter* cc=(struct ipc_counter*)&conn->counter;   
    struct tc_counter* tc;
   
    spin_lock(&cc->lock);
   
    tc=(struct tc_counter*)cc->parent;   
   
    if(tc)
    {
        spin_lock(&tc->lock);
        LIST_DELETE(&tc->conn_list[hash_tc_counter(tc->ip)],&conn->counter.list);
        atomic_dec(&tc->use);   
        tc->conn[get_tc_proto(conn->tuplehash[0].tuple.dst.protonum)]--;       
        spin_unlock(&tc->lock);
       
        printk("delete conn:ip=%u.%u.%u.%u id=%Lu use=%u\n",
            NIPQUAD(tc->ip),
            conn->counter.id,
            atomic_read(&tc->use)
            );                       
    }
   
    spin_unlock(&cc->lock);           
}

static const char * tc_proto_names[]=
{
    [TC_PROTO_TCP]="tcp",
    [TC_PROTO_UDP]="udp",
    [TC_PROTO_ICMP]="icmp",
    [TC_PROTO_OTHER]="other",
};

static const char * tc_dir_names[]=
{
    [TC_DIR_TX]="tx",
    [TC_DIR_RX]="rx",
};

static unsigned int
print_tc_counter(char *buffer, const struct tc_counter *tc)
{
    unsigned int len,i,j;

    len = sprintf(buffer, "%u.%u.%u.%u %u ",
              NIPQUAD(tc->ip),
              timer_pending(&tc->timeout)
              ? (tc->timeout.expires - jiffies)/HZ : 0);

    spin_lock(&tc->lock);
   
    for(i=0;i    {
        len+=sprintf(buffer + len,"{ %s ",tc_proto_names[i]);           
       
        for(j=0;j        {
            len+=sprintf(buffer + len,"[ %s ",tc_dir_names[j]);           
           
            len += sprintf(buffer + len, "pcnt=%Lu ",
               tc->proto[i].dir[j].pcnt);
   
            len += sprintf(buffer + len, "bcnt=%Lu ] ",
               tc->proto[i].dir[j].bcnt);
        }
       
        len += sprintf(buffer + len, "conn=%u } ",
           tc->conn[i]);   
    }
   
    len += sprintf(buffer + len, "use=%u ",
               atomic_read(&tc->use));

    len += sprintf(buffer + len, "last=%u ",
               tc->last);
              
    len += sprintf(buffer + len, "next_id=%Lu ",
               tc->next_conn_id);

    spin_unlock(&tc->lock);

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

    return len;
}

/* Returns true when finished. */
static inline int
tc_counter_iterate(const struct tc_counter *tc,
          char *buffer, off_t offset, off_t *upto,
          unsigned int *len, unsigned int maxlen)
{
    unsigned int newlen;

    MUST_BE_READ_LOCKED(&tc_counter_lock);

    if ((*upto)++ < offset)
        return 0;

    newlen = print_tc_counter(buffer + *len, tc);
    if (*len + newlen > maxlen)
        return 1;
    else *len += newlen;

    return 0;
}


static int
list_tc_counter(char *buffer, char **start, off_t offset, int length)
{
    unsigned int i;
    unsigned int len = 0;
    off_t upto = 0;

    READ_LOCK(&tc_counter_lock);
    /* Traverse hash; print originals then reply. */
    for (i = 0; i < tc_counter_htable_size; i++) {
        if (LIST_FIND(&tc_counter_hash[i], tc_counter_iterate,
                  struct tc_counter *,
                  buffer, offset, &upto, &len, length))
            break;
    }

    READ_UNLOCK(&ip_conntrack_lock);

    /* `start' hack - see fs/proc/generic.c line ~165 */
    *start = (char *)((unsigned int)upto - offset);
    return len;
}

static int __init init(void)
{
    int ret,i;
       
    if(tc_dev){
        printk("tc_dev:%s\n",tc_dev);
        if(strlen(tc_dev)>=IFNAMSIZ){
        return -EINVAL;
        }       
        memset(tcdevname,0,IFNAMSIZ);
        for(i=0;i            if(tc_dev[i]){
                tcdevname[i]=tc_dev[i];
            }else
                break;
        }
    }
   
    IP_NF_ASSERT(tc_counter_destroyed == NULL);
    tc_counter_destroyed = &tc_counter_cleanup_conntrack;
   
    tc_counter_hash = vmalloc(sizeof(struct list_head)
                    * tc_counter_htable_size);
    if(!tc_counter_hash) {
        return -ENOMEM;
    }
           
    tc_counter_cachep = kmem_cache_create("tc_counter",
                                            sizeof(struct tc_counter), 0,
                                            SLAB_HWCACHE_ALIGN, NULL, NULL);
    if (!tc_counter_cachep) {
        printk(KERN_ERR "Unable to create tc_couner slab cache\n");
        vfree(tc_counter_hash);
        return -ENOMEM;
    }
                   
    for (i = 0; i < tc_counter_htable_size; i++)
        //初始化hash
        INIT_LIST_HEAD(&tc_counter_hash[i]);

    proc_net_create("tc_counter",0,list_tc_counter);

                       
    /* Register hooks */
    ret = nf_register_hook(&tct_ops[0]);
    if (ret < 0)
        return ret;

    ret = nf_register_hook(&tct_ops[1]);
    if (ret < 0)
        goto cleanup_hook0;

    ret = nf_register_hook(&tct_ops[2]);
    if (ret < 0)
        goto cleanup_hook1;

    if (ip_conntrack_module)
        __MOD_INC_USE_COUNT(ip_conntrack_module);//要使用conntrack
       
    return ret;

 cleanup_hook1:
    nf_unregister_hook(&tct_ops[1]);
 cleanup_hook0:
    nf_unregister_hook(&tct_ops[0]);

    return ret;
}

static void cleanup_a_conn(struct tc_counter *tc,struct ip_conntrack * ct)
{
    struct ipc_counter * cc=&ct->counter;
   
    spin_lock(&cc->lock);
    if((struct tc_counter *)cc->parent==tc)
    {
        INIT_LIST_HEAD(&cc->list);
        cc->parent=NULL;
        memset(&cc->cnt,0,sizeof(cc->cnt));

        spin_lock(&tc->lock);
        tc->conn[get_tc_proto(ct->tuplehash[0].tuple.dst.protonum)]--;           
        atomic_dec(&tc->use);
        spin_unlock(&tc->lock);
    }
    spin_unlock(&cc->lock);   
}

static void cleanup_a_tc_counter(struct tc_counter * tc)
{   
    struct list_head * head=&tc->conn_list;
    int j;
   
    MUST_BE_WRITE_LOCKED(&tc_counter_lock);
   
    if(timer_pending(&tc->timeout))
         del_timer(&tc->timeout);
   
    spin_lock(&tc->lock);
                   
    while(!list_empty(head))
    {
        struct ip_conntrack * ct=(struct ip_conntrack *)list_entry(head->next,struct ip_conntrack,counter.list);
        LIST_DELETE(head,head->next);
        spin_unlock(&tc->lock);
       
        cleanup_a_conn(tc,ct);
    }
   
    spin_unlock(&tc->lock);
   
    IP_NF_ASSERT(atomic_read(&tc->use)==1);
    for(j=0;j        IP_NF_ASSERT(tc->conn[j]==0);
    }
       
    tc_counter_put(tc);
}

static void cleanup_a_bucket(struct list_head * head)
{
    MUST_BE_WRITE_LOCKED(&tc_counter_lock);
   
    while(!list_empty(head))
    {
        struct tc_counter * tc=(struct tc_counter *)list_entry(head->next,struct tc_counter,list);
        LIST_DELETE(head,head->next);
        cleanup_a_tc_counter(tc);
    }           
}

static void cleanup_all_tc_counters(void)
{
    int i;
   
    WRITE_LOCK(&tc_counter_lock);
       
    for (i = 0; i < tc_counter_htable_size; i++) {       
        cleanup_a_bucket(&tc_counter_hash[i]);
    }

    WRITE_UNLOCK(&tc_counter_lock);
   
    IP_NF_ASSERT(atomic_read(&tc_counter_count)==0);
}

static void __exit fini(void)
{
    unsigned int i;
   
    for (i = 0; i < sizeof(tct_ops)/sizeof(struct nf_hook_ops); i++)
        nf_unregister_hook(&tct_ops[i]);
   
    proc_net_remove("tc_counter");
   
    tc_counter_destroyed = NULL;

    cleanup_all_tc_counters();
   
    kmem_cache_destroy(tc_counter_cachep);
    vfree(tc_counter_hash);
       
    if (ip_conntrack_module)
        __MOD_DEC_USE_COUNT(ip_conntrack_module);       
}

module_init(init);
module_exit(fini);


内核中其他修改地方
ip_conntrack.h

struct ipc_counter
{
    struct list_head list;

    struct
    {
        u_int64_t pcnt, bcnt;            /*Packet and byte counters */
    }
    cnt[IP_CT_DIR_MAX];//两个方向
   
    //unsigned long create;
    unsigned long last; /*最后一次更新时的jiffies值*/
    spinlock_t lock;
       
    void * parent;
   
    u_int64_t id;
};

extern void (*ip_conntrack_destroyed)(struct ip_conntrack *conntrack);
extern void (*tc_counter_destroyed)(struct ip_conntrack *conntrack);//增加


ip_conntrack_core.c中

void (*ip_conntrack_destroyed)(struct ip_conntrack *conntrack) = NULL;
void (*tc_counter_destroyed)(struct ip_conntrack *conntrack) = NULL;//新增

static void
destroy_conntrack(struct nf_conntrack *nfct)
{
    struct ip_conntrack *ct = (struct ip_conntrack *)nfct;

    IP_NF_ASSERT(atomic_read(&nfct->use) == 0);
    IP_NF_ASSERT(!timer_pending(&ct->timeout));

    if (ct->master.master)
        nf_conntrack_put(&ct->master);

    if (ip_conntrack_destroyed)
        ip_conntrack_destroyed(ct);
    if (tc_counter_destroyed)//《《《《《《《《新增
        tc_counter_destroyed(ct);//《《《《《《《
    kmem_cache_free(ip_conntrack_cachep, ct);
    atomic_dec(&ip_conntrack_count);
}


init_conntrack函数中
    /* Mark clearly that it's not in the hash table. */
    conntrack->tuplehash[IP_CT_DIR_ORIGINAL].list.next = NULL;
   
    INIT_LIST_HEAD(&conntrack->counter.list);//新增
    spin_lock_init(&conntrack->counter.lock);

ip_conntrack_standalone.c中
EXPORT_SYMBOL(ip_conntrack_destroyed);
EXPORT_SYMBOL(tc_counter_destroyed);//新增


编译和测试
make modules
make modules_install

[root@localhost root]# cat /proc/net/tc_counter  
192.168.199.1 731 { tcp [ tx pcnt=3 bcnt=144 ] [ rx pcnt=16 bcnt=768 ] conn=1 } { udp [ tx pcnt=0 bcnt=0 ] [ rx pcnt=0 bcnt=0 ] conn=0 } { icmp [ tx pcnt=0 bcnt=0 ] [ rx pcnt=0 bcnt=0 ] conn=0 } { other [ tx pcnt=0 bcnt=0 ] [ rx pcnt=0 bcnt=0 ] conn=0 } use=2 last=638178 next_id=3
192.168.168.16 731 { tcp [ tx pcnt=0 bcnt=0 ] [ rx pcnt=0 bcnt=0 ] conn=0 } { udp [ tx pcnt=3 bcnt=354 ] [ rx pcnt=3 bcnt=204 ] conn=1 } { icmp [ tx pcnt=0 bcnt=0 ] [ rx pcnt=0 bcnt=0 ] conn=0 } { other [ tx pcnt=0 bcnt=0 ] [ rx pcnt=0 bcnt=0 ] conn=0 } use=2 last=637569 next_id=2

[root@localhost root]# dmesg
add conn:ip=10.1.9.156 id=2 use=3
delete conn:ip=10.1.9.33 id=0 use=1
delete conn:ip=10.1.9.45 id=9 use=2
delete conn:ip=192.168.154.1 id=2 use=3
delete conn:ip=192.168.199.1 id=2 use=3
add conn:ip=10.1.9.45 id=13 use=4
add conn:ip=10.1.9.45 id=14 use=5
add conn:ip=192.168.199.1 id=5 use=5
add conn:ip=192.168.154.1 id=5 use=5
delete conn:ip=10.1.9.45 id=13 use=3
add conn:ip=192.168.168.16 id=4 use=3

代码中还有许多不完善的地方,例如为tc_counter_timeout,tc_counter_count等增加proc项,进一步优化等。还有可能存在bug,例如感觉对锁的掌握和使用很烂。





上一篇:进一步完善tctable下的conntrack流量统计功能
下一篇:实现tctable的用户层接口