在新框架下实现conntrack中增加包数和字节数统计

1866阅读 0评论2010-05-26 qtdszws
分类:LINUX

1.除了在linux/netfilter_ipv4/ip_conntrack.h中增加
struct ipc_counters
{
    u_int64_t pcnt, bcnt;            /* Packet and byte counters */
    spinlock_t lock;
};
和在struct ip_conntrack末尾添加
    struct ipc_counters counters;
以及net/ipv4/netfilter/ip_conntrack_core.c的init_conntrack中增加
spin_lock_init(conntrack->counters.lock);

其他添加或修改全部取消

2.现在的tctable_filter.c
#include
#include
#include
#include
   
#define ADD_COUNTER(c,b,p) do { spin_lock(&c.lock);(c).bcnt += (b); (c).pcnt += (p);spin_unlock(&c.lock);} while(0)

static void sum(struct sk_buff * skb,const char * msg)
{
    struct ip_conntrack *ct;
    enum ip_conntrack_info ctinfo;
       
    ct = ip_conntrack_get(skb, &ctinfo);
    if(ct)//有跟踪
    {                            
        ADD_COUNTER(ct->counters, ntohs(skb->nh.iph->tot_len), 1);                                           
    }
    else
    {   
        if (net_ratelimit())
            printk(KERN_DEBUG "%s:got untracked packet %p %u %u.%u.%u.%u -> %u.%u.%u.%u %u\n",
                   msg,
                   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,"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,"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,"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 int __init init(void)
{
    int ret;

    /* 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 __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]);
       
    if (ip_conntrack_module)
        __MOD_DEC_USE_COUNT(ip_conntrack_module);       
}

module_init(init);
module_exit(fini);




需要注意的地方有两点
a.在查询包的连接跟踪时可能为空,因此需要判断
b.由于tctable需要使用ip_conntrack,因此在初始化引用了ip_conntrack模块,反正卸载时要递减引用计数。

编译并加载tctable_filter看看计数功能是否有效

dmesg
tct_local_out_hook:got untracked packet dfdd2b20 1 10.1.9.30 -> 10.1.9.45 1
tct_local_in_hook:got untracked packet cc02ef00 1 192.168.18.1 -> 224.0.0.1 1
tct_local_in_hook:got untracked packet cc02ef00 1 192.168.2.1 -> 224.0.0.1 1

这些conntrack的包都是icmp.原因在net/ipv4/netfilter/ip_conntrack_proto_icmp.c的代码中
static int icmp_invert_tuple(struct ip_conntrack_tuple *tuple,
                 const struct ip_conntrack_tuple *orig)
{
    /* Add 1; spaces filled with 0. */
    static u_int8_t invmap[]
        = { [ICMP_ECHO] = ICMP_ECHOREPLY + 1,
            [ICMP_ECHOREPLY] = ICMP_ECHO + 1,
            [ICMP_TIMESTAMP] = ICMP_TIMESTAMPREPLY + 1,
            [ICMP_TIMESTAMPREPLY] = ICMP_TIMESTAMP + 1,
            [ICMP_INFO_REQUEST] = ICMP_INFO_REPLY + 1,
            [ICMP_INFO_REPLY] = ICMP_INFO_REQUEST + 1,
            [ICMP_ADDRESS] = ICMP_ADDRESSREPLY + 1,
            [ICMP_ADDRESSREPLY] = ICMP_ADDRESS + 1};

    if (orig->dst.u.icmp.type >= sizeof(invmap)
        || !invmap[orig->dst.u.icmp.type])
        return 0;

    tuple->src.u.icmp.id = orig->src.u.icmp.id;
    tuple->dst.u.icmp.type = invmap[orig->dst.u.icmp.type] - 1;
    tuple->dst.u.icmp.code = orig->dst.u.icmp.code;
    return 1;
}

/* Returns verdict for packet, or -1 for invalid. */
static int icmp_packet(struct ip_conntrack *ct,
               struct iphdr *iph, size_t len,
               enum ip_conntrack_info ctinfo)
{
    /* Try to delete connection immediately after all replies:
           won't actually vanish as we still have skb, and del_timer
           means this will only run once even if count hits zero twice
           (theoretically possible with SMP) */
        
    if (CTINFO2DIR(ctinfo) == IP_CT_DIR_REPLY) {
        if (atomic_dec_and_test(&ct->proto.icmp.count)
            && del_timer(&ct->timeout))
            ct->timeout.function((unsigned long)ct);
    } else {
        atomic_inc(&ct->proto.icmp.count);
        ip_ct_refresh(ct, ICMP_TIMEOUT);
    }

    ip_ct_refresh(ct, ICMP_TIMEOUT);
   
    return NF_ACCEPT;
}

/* Called when a new connection for this protocol found. */
static int icmp_new(struct ip_conntrack *conntrack,
            struct iphdr *iph, size_t len)
{

    static u_int8_t valid_new[]
        = { [ICMP_ECHO] = 1,
            [ICMP_TIMESTAMP] = 1,
            [ICMP_INFO_REQUEST] = 1,
            [ICMP_ADDRESS] = 1 };

    if (conntrack->tuplehash[0].tuple.dst.u.icmp.type >= sizeof(valid_new)
        || !valid_new[conntrack->tuplehash[0].tuple.dst.u.icmp.type]) {
        /* Can't create a new ICMP `conn' with this. */
        DEBUGP("icmp: can't create new conn with type %u\n",
               conntrack->tuplehash[0].tuple.dst.u.icmp.type);
        DUMP_TUPLE(&conntrack->tuplehash[0].tuple);
        return 0;
    }
    atomic_set(&conntrack->proto.icmp.count, 0);   
   
    return 1;
}

可以修改这三个函数,使得conn可见
修改如下
static int icmp_invert_tuple(struct ip_conntrack_tuple *tuple,
                 const struct ip_conntrack_tuple *orig)
{
    /* Add 1; spaces filled with 0. */
    static u_int8_t invmap[]
        = { [ICMP_ECHO] = ICMP_ECHOREPLY + 1,
            [ICMP_ECHOREPLY] = ICMP_ECHO + 1,
            [ICMP_TIMESTAMP] = ICMP_TIMESTAMPREPLY + 1,
            [ICMP_TIMESTAMPREPLY] = ICMP_TIMESTAMP + 1,
            [ICMP_INFO_REQUEST] = ICMP_INFO_REPLY + 1,
            [ICMP_INFO_REPLY] = ICMP_INFO_REQUEST + 1,
            [ICMP_ADDRESS] = ICMP_ADDRESSREPLY + 1,
            [ICMP_ADDRESSREPLY] = ICMP_ADDRESS + 1};

    if (orig->dst.u.icmp.type >= sizeof(invmap)
        || !invmap[orig->dst.u.icmp.type])
        //return 0;
        {       
            tuple->src.u.icmp.id = 0;
            tuple->dst.u.icmp.type = 0;
            tuple->dst.u.icmp.code = 0;
            return 1;
        }

    tuple->src.u.icmp.id = orig->src.u.icmp.id;
    tuple->dst.u.icmp.type = invmap[orig->dst.u.icmp.type] - 1;
    tuple->dst.u.icmp.code = orig->dst.u.icmp.code;
    return 1;
}
/* Returns verdict for packet, or -1 for invalid. */
static int icmp_packet(struct ip_conntrack *ct,
               struct iphdr *iph, size_t len,
               enum ip_conntrack_info ctinfo)
{
    ip_ct_refresh(ct, ICMP_TIMEOUT);
   
    return NF_ACCEPT;
}

/* Called when a new connection for this protocol found. */
static int icmp_new(struct ip_conntrack *conntrack,
            struct iphdr *iph, size_t len)

    return 1;
}

make modules
make modules_install
重新加载ip_conntrack和tctable_filter发现icmp出现在/proc/net/ip_conntrack中,而tctable_filter也不报错了


测试时候发现linux下ping发出的icmp包id值为进程id,而windows下的值都是1024,不知道windows下如何处理的多个ping同一地址, icmp包id和seq发生冲突怎么办?



上一篇:新的流量控制框架tctable
下一篇:进一步完善tctable下的conntrack流量统计功能