diff -urN linux-2.4.30-wt1-403/include/linux/netfilter_ipv4/ip_conntrack.h linux-2.4.30-wt1-ctstat/include/linux/netfilter_ipv4/ip_conntrack.h --- linux-2.4.30-wt1-403/include/linux/netfilter_ipv4/ip_conntrack.h 2005-04-14 11:47:01.000000000 +0200 +++ linux-2.4.30-wt1-ctstat/include/linux/netfilter_ipv4/ip_conntrack.h 2005-04-14 11:47:18.000000000 +0200 @@ -285,5 +285,25 @@ } extern unsigned int ip_conntrack_htable_size; + +struct ip_conntrack_stat +{ + unsigned int searched; + unsigned int found; + unsigned int new; + unsigned int invalid; + unsigned int ignore; + unsigned int delete; + unsigned int delete_list; + unsigned int insert; + unsigned int insert_failed; + unsigned int drop; + unsigned int early_drop; + unsigned int icmp_error; + unsigned int expect_new; + unsigned int expect_create; + unsigned int expect_delete; +} ____cacheline_aligned_in_smp; + #endif /* __KERNEL__ */ #endif /* _IP_CONNTRACK_H */ diff -urN linux-2.4.30-wt1-403/net/ipv4/netfilter/ip_conntrack_core.c linux-2.4.30-wt1-ctstat/net/ipv4/netfilter/ip_conntrack_core.c --- linux-2.4.30-wt1-403/net/ipv4/netfilter/ip_conntrack_core.c 2005-04-14 11:47:01.000000000 +0200 +++ linux-2.4.30-wt1-ctstat/net/ipv4/netfilter/ip_conntrack_core.c 2005-04-14 11:49:59.000000000 +0200 @@ -61,11 +61,13 @@ static LIST_HEAD(helpers); unsigned int ip_conntrack_htable_size = 0; int ip_conntrack_max = 0; -static atomic_t ip_conntrack_count = ATOMIC_INIT(0); +atomic_t ip_conntrack_count = ATOMIC_INIT(0); struct list_head *ip_conntrack_hash; static kmem_cache_t *ip_conntrack_cachep; static LIST_HEAD(unconfirmed); +struct ip_conntrack_stat ip_conntrack_stat[NR_CPUS]; + extern struct ip_conntrack_protocol ip_conntrack_generic_protocol; static inline int proto_cmpfn(const struct ip_conntrack_protocol *curr, @@ -180,6 +182,7 @@ IP_NF_ASSERT(!timer_pending(&exp->timeout)); kfree(exp); + ip_conntrack_stat[smp_processor_id()].expect_delete++; } inline void ip_conntrack_expect_put(struct ip_conntrack_expect *exp) @@ -354,12 +357,15 @@ DEBUGP("destroy_conntrack: returning ct=%p to slab\n", ct); kmem_cache_free(ip_conntrack_cachep, ct); atomic_dec(&ip_conntrack_count); + ip_conntrack_stat[smp_processor_id()].delete++; } static void death_by_timeout(unsigned long ul_conntrack) { struct ip_conntrack *ct = (void *)ul_conntrack; + ip_conntrack_stat[smp_processor_id()].delete_list++; + WRITE_LOCK(&ip_conntrack_lock); clean_from_lists(ct); WRITE_UNLOCK(&ip_conntrack_lock); @@ -382,13 +388,18 @@ { struct ip_conntrack_tuple_hash *h; unsigned int hash = hash_conntrack(tuple); + unsigned int cpu = smp_processor_id(); MUST_BE_READ_LOCKED(&ip_conntrack_lock); - h = LIST_FIND(&ip_conntrack_hash[hash], - conntrack_tuple_cmp, - struct ip_conntrack_tuple_hash *, - tuple, ignored_conntrack); - return h; + list_for_each_entry(h, &ip_conntrack_hash[hash], list) { + if (conntrack_tuple_cmp(h, tuple, ignored_conntrack)) { + ip_conntrack_stat[cpu].found++; + return h; + } + ip_conntrack_stat[cpu].searched++; + } + + return NULL; } /* Find a connection corresponding to a tuple. */ @@ -485,10 +496,12 @@ atomic_inc(&ct->ct_general.use); set_bit(IPS_CONFIRMED_BIT, &ct->status); WRITE_UNLOCK(&ip_conntrack_lock); + ip_conntrack_stat[smp_processor_id()].insert++; return NF_ACCEPT; } WRITE_UNLOCK(&ip_conntrack_lock); + ip_conntrack_stat[smp_processor_id()].insert_failed++; return NF_DROP; } @@ -622,6 +635,7 @@ if (del_timer(&h->ctrack->timeout)) { death_by_timeout((unsigned long)h->ctrack); dropped = 1; + ip_conntrack_stat[smp_processor_id()].early_drop++; } ip_conntrack_put(h->ctrack); return dropped; @@ -745,7 +759,10 @@ LIST_DELETE(&ip_conntrack_expect_list, expected); expected->expectant->expecting--; nf_conntrack_get(&master_ct(conntrack)->infos[0]); - } + ip_conntrack_stat[smp_processor_id()].expect_new++; + } else + ip_conntrack_stat[smp_processor_id()].new++; + /* Overload tuple linked list to put us in unconfirmed list. */ list_add(&conntrack->tuplehash[IP_CT_DIR_ORIGINAL].list, &unconfirmed); @@ -755,6 +772,7 @@ if (expected && expected->expectfn) expected->expectfn(conntrack); + return &conntrack->tuplehash[IP_CT_DIR_ORIGINAL]; } @@ -843,8 +861,10 @@ /* Previously seen (loopback)? Ignore. Do this before fragment check. */ - if ((*pskb)->nfct) + if ((*pskb)->nfct) { + ip_conntrack_stat[smp_processor_id()].ignore++; return NF_ACCEPT; + } /* Gather fragments. */ if ((*pskb)->nh.iph->frag_off & htons(IP_MF|IP_OFFSET)) { @@ -860,12 +880,16 @@ /* It may be an icmp error... */ if ((*pskb)->nh.iph->protocol == IPPROTO_ICMP - && icmp_error_track(*pskb, &ctinfo, hooknum)) + && icmp_error_track(*pskb, &ctinfo, hooknum)) { + ip_conntrack_stat[smp_processor_id()].icmp_error++; return NF_ACCEPT; + } - if (!(ct = resolve_normal_ct(*pskb, proto,&set_reply,hooknum,&ctinfo))) + if (!(ct = resolve_normal_ct(*pskb, proto,&set_reply,hooknum,&ctinfo))) { /* Not valid part of a connection */ + ip_conntrack_stat[smp_processor_id()].invalid++; return NF_ACCEPT; + } if (IS_ERR(ct)) /* Too stressed to deal. */ @@ -877,6 +901,8 @@ if (ret < 0 ) { /* Invalid: inverse of the return code tells * to the netfilter core what to do. */ + if (ret == -NF_ACCEPT || ret == -NF_DROP) + ip_conntrack_stat[smp_processor_id()].invalid++; nf_conntrack_put((*pskb)->nfct); (*pskb)->nfct = NULL; return -ret; @@ -887,6 +913,7 @@ ct, ctinfo); if (ret == -1) { /* Invalid */ + ip_conntrack_stat[smp_processor_id()].invalid++; nf_conntrack_put((*pskb)->nfct); (*pskb)->nfct = NULL; return NF_ACCEPT; @@ -1071,6 +1098,8 @@ WRITE_UNLOCK(&ip_conntrack_lock); + ip_conntrack_stat[smp_processor_id()].expect_create++; + return ret; } diff -urN linux-2.4.30-wt1-403/net/ipv4/netfilter/ip_conntrack_standalone.c linux-2.4.30-wt1-ctstat/net/ipv4/netfilter/ip_conntrack_standalone.c --- linux-2.4.30-wt1-403/net/ipv4/netfilter/ip_conntrack_standalone.c 2005-04-14 11:47:01.000000000 +0200 +++ linux-2.4.30-wt1-ctstat/net/ipv4/netfilter/ip_conntrack_standalone.c 2005-04-14 11:50:26.000000000 +0200 @@ -39,6 +39,9 @@ MODULE_LICENSE("GPL"); +extern atomic_t ip_conntrack_count; +extern struct ip_conntrack_stat ip_conntrack_stat[NR_CPUS]; + static int kill_proto(struct ip_conntrack *i, void *data) { return (i->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.protonum == @@ -179,6 +182,46 @@ return len; } +static int ip_conntrack_get_stat(char *buffer, char **start, off_t offset, int length) +{ + unsigned int nr_conntracks = atomic_read(&ip_conntrack_count); + int i, lcpu; + int len = 0; + + for (lcpu = 0; lcpu < smp_num_cpus; lcpu++) { + i = cpu_logical_map(lcpu); + + len += sprintf(buffer+len, "%08x %08x %08x %08x %08x %08x %08x %08x %08x %08x %08x %08x %08x %08x %08x %08x \n", + nr_conntracks, + ip_conntrack_stat[i].searched, + ip_conntrack_stat[i].found, + ip_conntrack_stat[i].new, + ip_conntrack_stat[i].invalid, + ip_conntrack_stat[i].ignore, + ip_conntrack_stat[i].delete, + ip_conntrack_stat[i].delete_list, + ip_conntrack_stat[i].insert, + ip_conntrack_stat[i].insert_failed, + ip_conntrack_stat[i].drop, + ip_conntrack_stat[i].early_drop, + ip_conntrack_stat[i].icmp_error, + + ip_conntrack_stat[i].expect_new, + ip_conntrack_stat[i].expect_create, + ip_conntrack_stat[i].expect_delete + ); + } + len -= offset; + + if (len > length) + len = length; + if (len < 0) + len = 0; + + *start = buffer + offset; + return len; +} + static unsigned int ip_confirm(unsigned int hooknum, struct sk_buff **pskb, const struct net_device *in, @@ -360,7 +403,7 @@ #endif static int init_or_cleanup(int init) { - struct proc_dir_entry *proc; + struct proc_dir_entry *proc, *procstat; int ret = 0; if (!init) goto cleanup; @@ -373,10 +416,15 @@ if (!proc) goto cleanup_init; proc->owner = THIS_MODULE; + procstat = proc_net_create("ip_conntrack_stat", 0, ip_conntrack_get_stat); + if (!procstat) + goto cleanup_proc; + procstat->owner = THIS_MODULE; + ret = nf_register_hook(&ip_conntrack_in_ops); if (ret < 0) { printk("ip_conntrack: can't register pre-routing hook.\n"); - goto cleanup_proc; + goto cleanup_procstat; } ret = nf_register_hook(&ip_conntrack_local_out_ops); if (ret < 0) { @@ -414,6 +462,8 @@ nf_unregister_hook(&ip_conntrack_local_out_ops); cleanup_inops: nf_unregister_hook(&ip_conntrack_in_ops); + cleanup_procstat: + proc_net_remove("ip_conntrack_stat"); cleanup_proc: proc_net_remove("ip_conntrack"); cleanup_init: