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
呵呵,成功了.