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
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
}
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,例如感觉对锁的掌握和使用很烂。