配色: 字号:
【智能路由器】设备流量、网速统计及上下线提醒(基于netfilter编程)
2016-09-22 | 阅:  转:  |  分享 
  
【智能路由器】设备流量、网速统计及上下线提醒(基于netfilter编程)

模块目的



本文用户流量统计是统计路由器子网下每台设备的流量,下图展现了该模块具体是要实现怎样的功能



内核模块



依然是在netfilter的框架上进行数据捕获,分别监控每台子网设备流量信息。

原理:在netfilter的pre_routing统计上传流量和上行速度以及在post_routing节点统计下载流量和下行速度,通过/proc文件系统提供给上层应用程序利用。



流量拦截及统计



关键代码如下



/

目的:统计每个MAC的上传流量

/

staticunsignedintflow_hook_out(

unsignedinthooknum,

structsk_buffskb,

conststructnet_devicein,

conststructnet_deviceout,

int(okfn)(structsk_buff))

{

structethhdreth;

structiphdrip;

uint32_tip1,ip2;



if(!skb)

returnNF_ACCEPT;



if(skb->protocol!=htons(0x0800))

returnNF_ACCEPT;



eth=eth_hdr(skb);

if(!eth)

{

printk("ethiserror!\n");

returnNF_ACCEPT;

}





ip=ip_hdr(skb);

if(!ip)

returnNF_ACCEPT;



if(!strnicmp(in->name,"br0",strlen("br0")))//流出流量,在pre_routing统计,统计源mac,ip

{

ip1=(ip->saddr)&0x00ffffff;

ip2=(local_ip.IP)&0x00ffffff;



if(ip1!=ip2)//subnet

{

returnNF_ACCEPT;

}



if(ip->saddr==local_ip.IP)//local

returnNF_ACCEPT;



ip1=ip1&0xff000000;

if(ntohl(ip1)==255)//broadcast

returnNF_ACCEPT;



//mutex_lock(&visit_mutex_tx);

add_maclist_data(Mac_flow_tableTX,eth->h_source,ip->saddr,ntohs(ip->tot_len));

//mutex_unlock(&visit_mutex_tx);

}



returnNF_ACCEPT;

}





structnf_hook_opsflow_ops_out={//外出流量

.list={NULL,NULL},

.hook=flow_hook_out,

.pf=PF_INET,

.hooknum=NF_INET_PRE_ROUTING,//必须在NAT转发前统计

.priority=NF_IP_PRI_FIRST+1

//.hooknum=NF_INET_POST_ROUTING,

//.priority=NF_IP_PRI_LAST-1

};

代码给出了上传流量的统计方法,下载流量统计代码如法炮制,不再贴出。代码思路步骤:

1.代码首先剔除arp数据(提取ip包)

2.过滤局域网外其他干扰设备数据、本地数据、广播数据

3.添加到设备链表,这里做了一个单向链表用于存储设备mac、ip、流量、网速等信息。add_maclist_data()该函数用于向链表中添加数据



设备网速计算及打印



关键代码如下:



intmac_func(voiddata)

{

uint32_tlen=0;

uint32_tlenRx=0;



while(!kthread_should_stop())

{

//ssleep(10);

msleep(500);



mutex_lock(&visit_mutex_tx);

len=strlen(flow_buf)+40;

if(len<1800)//上传流量

{

memset(flow_buf,0,len);

//memset(flow_buf,0,sizeof(flow_buf));

delete_macnode_by_ct(Mac_flow_tableTX);//clearofflinemac

flow_format_and_clear(Mac_flow_tableTX,flow_buf);

}

else

{

free_maclist(Mac_flow_tableTX);//clearallmac,theremaybealotofinvalidmac!

Mac_flow_tableTX=create_maclist();//anewlist

}

mutex_unlock(&visit_mutex_tx);



msleep(500);

mutex_lock(&visit_mutex_rx);

lenRx=strlen(flow_bufRx)+40;

if(lenRx<1800)//下发流量,只有ip

{

memset(flow_bufRx,0,lenRx);

//memset(flow_bufRx,0,sizeof(flow_bufRx));

delete_ipnode_by_ct(ip_flow_tableRx);

ipflow_format_and_clear(ip_flow_tableRx,flow_bufRx);

}

else

{

free_iplist(ip_flow_tableRx);

ip_flow_tableRx=create_iplist();

}

mutex_unlock(&visit_mutex_rx);





}



return1;

}



intk_threadinit(void)

{

info_kthread=kthread_run(mac_func,NULL,"flow");

if(NULL==info_kthread)

return-1;

return1;

}



intk_threadstop(void)

{

kthread_stop(info_kthread);

info_kthread=NULL;

return1;

}

在内核创建了一个线程用于统计网速,维护设备链表。

代码每秒统计一次设备网速,同时剔除一些已经下线的设备,防止输出缓冲区过满(删除整个链表然后重新建表)。



将数据映射到/proc虚拟文件系统



关键代码如下:



staticintflow_read(charpage,charstart,off_toff,intcount,inteof,voiddata)

{

intlen;



mutex_lock(&visit_mutex_tx);

len=strlen(flow_buf);

if(off>len)

{

mutex_unlock(&visit_mutex_tx);

eof=1;

return0;

}



if(count>len-off)

{

count=len-off;

eof=1;

}



memcpy(page,flow_buf,count);

mutex_unlock(&visit_mutex_tx);



start=page+off;



//eof=1;

//returnoff+count;

returncount;

}



staticintflow_readRx(charpage,charstart,off_toff,intcount,inteof,voiddata)

{

intlen;



//printk("flow_readRx\n");

mutex_lock(&visit_mutex_rx);

len=strlen(flow_bufRx);

if(off>len)

{

mutex_unlock(&visit_mutex_rx);

eof=1;

return0;

}



if(count>len-off)

{

count=len-off;

eof=1;

}



memcpy(page,flow_bufRx,count);

mutex_unlock(&visit_mutex_rx);



start=page+off;



//eof=1;

//returnoff+count;

returncount;

}



intinit_flowproc_moudle(void)

{

intret=0;



flow_root=proc_mkdir("flow_m",NULL);

if(flow_root==NULL)

{

printk("createdirflow_rootfail\n");

return-1;

}



//Tx

proc_entry=create_proc_entry("flowwatchTx",0444,flow_root);

if(proc_entry==NULL)

{

printk("fortune:couldn''tcreateprocentry\n");

ret=-2;



returnret;

}

proc_entry->read_proc=flow_read;



//Rx

proc_entryRx=create_proc_entry("flowwatchRx",0444,flow_root);

if(proc_entryRx==NULL)

{

printk("fortune:couldn''tcreateprocentry\n");

ret=-3;



returnret;

}

proc_entryRx->read_proc=flow_readRx;





returnret;

}



voidexit_flowproc_moudle(void)

{

remove_proc_entry("flowwatchTx",flow_root);//删除文件

remove_proc_entry("flowwatchRx",flow_root);



remove_proc_entry("flow_m",NULL);//删除目录

}

代码在/proc下建立/flow_m文件夹,同时在/proc/flow_m下建立flowwatchTx和flowwatchRx文件,对这两个文件分别进行读取可以得到设备流量信息。



应用层模块



应用层模块对设备上传及下载流量进行了整合,同时具备设备上下线提醒功能。

主要代码如下:



typedefstd::listF_list;

F_listFlow_table;





/

name:cat_user_flow

description:

cat/proc/flow_m/flowwatchTx

cat/proc/flow_m/flowwatchRx

/

intcat_user_flow(HASH_TABLEpMACtable)

{

Flow_Infotemflow;

charup_buf[80];

chardown_buf[80];

FILEup_flow;

FILEdown_flow;



charflag=0;

time_tcurrenttime;



up_flow=fopen("flowwatchTx","r");

down_flow=fopen("flowwatchRx","r");

if(up_flow==NULL)

{

std::cout<<"openfilefail!"<
return-1;

}



if(down_flow==NULL)

{

std::cout<<"openfilefail!"<
return-1;

}



//readfirstlineanddropout

if(NULL==fgets(up_buf,sizeof(up_buf),up_flow))

{

//errororendoffile

std::cout<<"readfilefailed!"<
fclose(up_flow);

fclose(down_flow);

return-2;

}

if(NULL==fgets(down_buf,sizeof(down_buf),down_flow))

{

//errororendoffile

std::cout<<"readfilefailed!"<
fclose(up_flow);

fclose(down_flow);

return-2;

}



currenttime=time((time_t)NULL);

std::cout<<"currenttime:"<




for(;;)//Tx

{

memset(up_buf,0,sizeof(up_buf));

if(NULL==fgets(up_buf,sizeof(up_buf),up_flow))//errororendoffile

break;



memset(&temflow,0,sizeof(temflow));

sscanf(up_buf,"%s%s%s%s",temflow.mac,temflow.ip,temflow.upload,temflow.totalup);



if(strncmp(temflow.ip,"0.0.0.0",strlen("0.0.0.0"))==0)

continue;

//updatelist

for(F_list::iteratorf_list_iter=Flow_table.begin();f_list_iter!=Flow_table.end();f_list_iter++)

{

if(0==strncmp((f_list_iter).mac,temflow.mac,strlen(temflow.mac)))

{

memcpy((f_list_iter).ip,temflow.ip,strlen(temflow.ip)+1);//addadditionalanullcharacter

memcpy((f_list_iter).upload,temflow.upload,strlen(temflow.upload)+1);



memcpy((f_list_iter).totalup,temflow.totalup,strlen(temflow.totalup)+1);



(f_list_iter).loop=0;



(f_list_iter).alive=currenttime-(f_list_iter).s_time;



flag=1;

break;

}

}



if(!flag)//newonlinedevice

{

charbuf[12];

NODEMACdataNode=NULL;

ElemTypeval=0;



memset(buf,0,sizeof(buf));

memcpy(buf,temflow.mac,8);

//getlocaltime

temflow.s_time=time((time_t)NULL);

temflow.NewOnlineD=1;//onlineaction



//std::cout<<"-------"<
val=(ElemType)(MAC_str_to10)(buf);

MACdataNode=find_data_in_hash(pMACtable,val);

if(MACdataNode==NULL)

{

std::cout<<"findnodata!\n"<
//unfound!

memcpy(temflow.dev_name,"unknown",strlen("unknown"));

memcpy(temflow.dev_type,"unknown",strlen("unknown"));

}

else//findsuccess

{

memcpy(temflow.dev_name,((Code_to_Str_t)MACdataNode->data)->remark,strlen(((Code_to_Str_t)MACdataNode->data)->remark)+1);

memcpy(temfwww.shanxiwang.netlow.dev_type,((Code_to_Str_t)MACdataNode->data)->type,strlen(((Code_to_Str_t)MACdataNode->data)->type)+1);

}



Flow_table.push_back(temflow);



}



flag=0;



}



if(Flow_table.empty())

{

std::cout<<"Flow_tableisempty"<
fclose(up_flow);

fclose(down_flow);

charbuf[4];

memset(buf,0,sizeof(buf));

write_info(buf,dev_info_file,"w+");

return-3;

}



while(1)//combineTxwithRx

{

memset(down_buf,0,sizeof(down_buf));

if(NULL==fgets(down_buf,sizeof(down_buf),down_flow))//errororendoffile

break;



memset(&temflow,0,sizeof(temflow));

sscanf(down_buf,"%s%s%s",temflow.ip,temflow.download,temflow.totaldown);



for(F_list::iteratorf_list_iter=Flow_table.begin();f_list_iter!=Flow_table.end();f_list_iter++)//compareip

{

if(0==strncmp((f_list_iter).ip,temflow.ip,strlen(temflow.ip)))

{

memcpy((f_list_iter).download,temflow.download,strlen(temflow.download)+1);

memcpy((f_list_iter).totaldown,temflow.totaldown,strlen(temflow.totaldown)+1);

(f_list_iter).looprx=0;



break;//thisipinsertok!

}//ifnotfind,ignoreit!

}

}



for(F_list::iteratorf_list_iter=Flow_table.begin();f_list_iter!=Flow_table.end();f_list_iter++)

{

(f_list_iter).loop=(f_list_iter).loop+1;

(f_list_iter).looprx=(f_list_iter).looprx+1;

}



fclose(up_flow);

fclose(down_flow);

/

for(F_list::iteratorf_list_iter=Flow_table.begin();f_list_iter!=Flow_table.end();f_list_iter++)

{

std::cout<<""<
std::cout<<(f_list_iter).mac<<""<<(f_list_iter).ip<<""<<(f_list_iter).totalup<
}

/

return0;



}



/

name:

description:noticeothersomeinformation

/

intwrite_dev_info(pid_tpid)

{

charbuf[256];



memset(buf,0,sizeof(buf));

write_info(buf,dev_info_file,"w+");//cleardev_info_file

for(F_list::iteratorf_list_iter=Flow_table.begin();f_list_iter!=Flow_table.end();f_list_iter++)

{

memset(buf,0,sizeof(buf));

sprintf(buf,"%d,%s,%s,%s,%s,%s,%s,%s,%s,%ld,%d\n",Flow_table.size(),

(f_list_iter).mac,

(f_list_iter).ip,

(f_list_iter).upload,

(f_list_iter).download,

(f_list_iter).totalup,

(f_list_iter).totaldown,

(f_list_iter).dev_name,

(f_list_iter).dev_type,

(f_list_iter).alive,

(f_list_iter).loop

);



printf("-------\n%s\n",buf);

write_info(buf,dev_info_file,"a+");

if(((f_list_iter).loop>5)&&((f_list_iter).looprx>5))//offlinecheckandnotice

{

memset(buf,0,sizeof(buf));

sprintf(buf,"%s,%s,%s\n",(f_list_iter).mac,(f_list_iter).dev_type,(f_list_iter).dev_name);

printf("\nofflinenotice:%s,%s,%s\n",(f_list_iter).mac,(f_list_iter).dev_type,(f_list_iter).dev_name);

if(!write_info(buf,offline_notice_file,"a+"))

{

//kill(pid,SIGUSR1);

}



//settheflagforwaittingdelete;

(f_list_iter).wait_erase=1;

}

elseif((f_list_iter).NewOnlineD==1)//onlinecheckandnotice

{

(f_list_iter).NewOnlineD=0;//clearflag

memset(buf,0,sizeof(buf));

sprintf(buf,"%s,%s,%s\n",(f_list_iter).mac,(f_list_iter).dev_type,(f_list_iter).dev_name);

printf("\nonlinenotice:%s,%s,%s\n",(f_list_iter).mac,(f_list_iter).dev_type,(f_list_iter).dev_name);

if(!write_info(buf,online_notice_file,"a+"))

{

//kill(pid,SIGUSR2);

}

}

}



//clear_offline_dev

//Flow_table.erase(f_list_iter);

deletedev:

for(F_list::iteratorf_list_iter=Flow_table.begin();f_list_iter!=Flow_table.end();f_list_iter++)

{

if((f_list_iter).wait_erase==1)

{

printf("clearofflinedev!\n");

Flow_table.erase(f_list_iter);

gotodeletedev;

}

}



return0;

}

cat_user_flow()函数代码首先读取/proc/flow_m/flowwatchTx和/proc/flow_m/flowwatchRx数据,然后进行根据链表判断是否是新设备,接着通过哈希表查询该设备所属设备厂商(根据已有的MAC厂商列表),然后整合设备上传和下载流量。

write_dev_info()函数将数据写入缓存文件,同时给其他进程发送设备上下线通知的消息,并将上下线的设备数据写入对应缓存。

总体结构如此。

献花(0)
+1
(本文系网络学习天...首藏)