【智能路由器】设备流量、网速统计及上下线提醒(基于netfilter编程)
来源:互联网 发布:苹果windows怎么截屏 编辑:程序博客网 时间:2024/05/17 02:06
【智能路由器】系列文章连接
http://blog.csdn.net/u012819339/article/category/5803489
模块目的
本文用户流量统计是统计路由器子网下每台设备的流量,下图展现了该模块具体是要实现怎样的功能
内核模块
依然是在netfilter的框架上进行数据捕获,分别监控每台子网设备流量信息。
原理:在netfilter的pre_routing统计上传流量和上行速度以及在post_routing节点统计下载流量和下行速度,通过/proc文件系统提供给上层应用程序利用。
流量拦截及统计
关键代码如下
/*目的:统计每个MAC的上传流量*/static unsigned int flow_hook_out( unsigned int hooknum, struct sk_buff * skb, const struct net_device *in, const struct net_device *out, int (*okfn) (struct sk_buff *)) { struct ethhdr *eth; struct iphdr *ip; uint32_t ip1, ip2; if (!skb) return NF_ACCEPT; if(skb->protocol != htons(0x0800)) return NF_ACCEPT; eth = eth_hdr(skb); if(!eth) { printk("eth is error!\n"); return NF_ACCEPT; } ip = ip_hdr(skb); if(!ip) return NF_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 { return NF_ACCEPT; } if(ip->saddr == local_ip.IP) //local return NF_ACCEPT; ip1 = ip1&0xff000000; if(ntohl(ip1)==255) //broadcast return NF_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); } return NF_ACCEPT; }struct nf_hook_ops flow_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()该函数用于向链表中添加数据
设备网速计算及打印
关键代码如下:
int mac_func(void *data){ uint32_t len = 0; uint32_t lenRx = 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); //clear offline mac flow_format_and_clear(Mac_flow_tableTX, flow_buf); } else { free_maclist(Mac_flow_tableTX); //clear all mac,there may be a lot of invalid mac! Mac_flow_tableTX = create_maclist(); //a new list } 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); } return 1;}int k_threadinit(void){ info_kthread = kthread_run(mac_func, NULL, "flow"); if(NULL == info_kthread) return -1; return 1;}int k_threadstop(void){ kthread_stop(info_kthread); info_kthread = NULL; return 1;}
在内核创建了一个线程用于统计网速,维护设备链表。
代码每秒统计一次设备网速,同时剔除一些已经下线的设备,防止输出缓冲区过满(删除整个链表然后重新建表)。
将数据映射到/proc虚拟文件系统
关键代码如下:
static int flow_read(char *page,char **start,off_t off, int count ,int *eof,void *data){ int len; mutex_lock(&visit_mutex_tx); len = strlen(flow_buf); if(off > len) { mutex_unlock(&visit_mutex_tx); *eof = 1; return 0; } if(count > len-off) { count = len - off; *eof = 1; } memcpy(page, flow_buf, count); mutex_unlock(&visit_mutex_tx); *start = page + off; //*eof = 1; //return off + count; return count;}static int flow_readRx(char *page,char **start,off_t off, int count ,int *eof,void *data){ int len; //printk("flow_readRx\n"); mutex_lock(&visit_mutex_rx); len = strlen(flow_bufRx); if(off > len) { mutex_unlock(&visit_mutex_rx); *eof = 1; return 0; } if(count > len-off) { count = len - off; *eof = 1; } memcpy(page, flow_bufRx, count); mutex_unlock(&visit_mutex_rx); *start = page + off; //*eof = 1; //return off + count; return count;}int init_flowproc_moudle(void){ int ret = 0; flow_root = proc_mkdir("flow_m", NULL); if(flow_root == NULL) { printk("create dir flow_root fail\n"); return -1; } //Tx proc_entry = create_proc_entry("flowwatchTx", 0444, flow_root); if(proc_entry==NULL) { printk("fortune :couldn't create proc entry\n"); ret = -2; return ret; } proc_entry->read_proc = flow_read; //Rx proc_entryRx = create_proc_entry("flowwatchRx", 0444, flow_root); if(proc_entryRx==NULL) { printk("fortune :couldn't create proc entry\n"); ret = -3; return ret; } proc_entryRx->read_proc = flow_readRx; return ret;}void exit_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文件,对这两个文件分别进行读取可以得到设备流量信息。
应用层模块
应用层模块对设备上传及下载流量进行了整合,同时具备设备上下线提醒功能。
主要代码如下:
typedef std::list<Flow_Info> F_list;F_list Flow_table;/*name:cat_user_flowdescription: cat /proc/flow_m/flowwatchTxcat /proc/flow_m/flowwatchRx*/int cat_user_flow(HASH_TABLE *pMACtable){ Flow_Info temflow; char up_buf[80]; char down_buf[80]; FILE *up_flow; FILE *down_flow; char flag=0; time_t currenttime; up_flow = fopen("flowwatchTx", "r"); down_flow = fopen("flowwatchRx", "r"); if(up_flow==NULL) { std::cout<<"open file fail!"<<std::endl; return -1; } if(down_flow==NULL) { std::cout<<"open file fail!"<<std::endl; return -1; } //read first line and drop out if(NULL == fgets(up_buf, sizeof(up_buf), up_flow)) { //error or end of file std::cout<<"read file failed!"<<std::endl; fclose(up_flow); fclose(down_flow); return -2; } if(NULL == fgets(down_buf, sizeof(down_buf), down_flow)) { //error or end of file std::cout<<"read file failed!"<<std::endl; fclose(up_flow); fclose(down_flow); return -2; } currenttime = time((time_t *)NULL); std::cout<<"currenttime:"<<currenttime<<std::endl; for(;;) //Tx { memset(up_buf, 0, sizeof(up_buf)); if(NULL == fgets(up_buf, sizeof(up_buf), up_flow)) //error or end of file 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; //update list for(F_list::iterator f_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); //add additional a null character 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) //new online device { char buf[12]; NODE *MACdataNode = NULL; ElemType val = 0; memset(buf, 0, sizeof(buf)); memcpy(buf, temflow.mac, 8); //get local time temflow.s_time = time((time_t *)NULL); temflow.NewOnlineD = 1; //online action //std::cout<<"*-*-*-*-*-*-*-*"<<std::endl; val = (ElemType)(MAC_str_to10)(buf); MACdataNode = find_data_in_hash(pMACtable, val); if(MACdataNode == NULL) { std::cout<<"find no data!\n"<<std::endl; //unfound! memcpy(temflow.dev_name,"unknown", strlen("unknown")); memcpy(temflow.dev_type,"unknown", strlen("unknown")); } else //find success { memcpy(temflow.dev_name, ((Code_to_Str_t *)MACdataNode->data)->remark, strlen(((Code_to_Str_t *)MACdataNode->data)->remark)+1); memcpy(temflow.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_table is empty"<<std::endl; fclose(up_flow); fclose(down_flow); char buf[4]; memset(buf, 0, sizeof(buf)); write_info(buf, dev_info_file, "w+"); return -3; } while(1) //combine Tx with Rx { memset(down_buf, 0, sizeof(down_buf)); if(NULL == fgets(down_buf, sizeof(down_buf), down_flow)) //error or end of file break; memset(&temflow, 0, sizeof(temflow)); sscanf(down_buf, "%s %s %s", temflow.ip, temflow.download, temflow.totaldown); for(F_list::iterator f_list_iter=Flow_table.begin(); f_list_iter != Flow_table.end(); f_list_iter++)//compare ip { 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; //this ip insert ok! } //if not find , ignore it! } } for(F_list::iterator f_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::iterator f_list_iter=Flow_table.begin(); f_list_iter != Flow_table.end(); f_list_iter++) { std::cout<<"****************"<<std::endl; std::cout<<(*f_list_iter).mac<<" "<<(*f_list_iter).ip<<" "<<(*f_list_iter).totalup<<std::endl; }*/ return 0;}/*name:description: notice other some information */int write_dev_info(pid_t pid){ char buf[256]; memset(buf, 0, sizeof(buf)); write_info(buf, dev_info_file, "w+"); //clear dev_info_file for(F_list::iterator f_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)) //offline check and notice { 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("*******\n offline notice: %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); } //set the flag for waitting delete; (*f_list_iter).wait_erase = 1; } else if((*f_list_iter).NewOnlineD==1) //online check and notice { (*f_list_iter).NewOnlineD = 0; //clear flag 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("*******\n online notice: %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::iterator f_list_iter=Flow_table.begin(); f_list_iter != Flow_table.end(); f_list_iter++) { if((*f_list_iter).wait_erase == 1) { printf("clear offline dev!\n"); Flow_table.erase(f_list_iter); goto deletedev; } } return 0;}
cat_user_flow()函数代码首先读取/proc/flow_m/flowwatchTx和/proc/flow_m/flowwatchRx数据,然后进行根据链表判断是否是新设备,接着通过哈希表查询该设备所属设备厂商(根据已有的MAC厂商列表),然后整合设备上传和下载流量。
write_dev_info()函数将数据写入缓存文件,同时给其他进程发送设备上下线通知的消息,并将上下线的设备数据写入对应缓存。
总体结构如此。
好啦,本文到此结束,作者arvik,【智能路由器】系列文章见
http://blog.csdn.net/u012819339/article/category/5803489
- 【智能路由器】设备流量、网速统计及上下线提醒(基于netfilter编程)
- 【智能路由器】动态域名(基于netfilter编程的DNS数据伪造)
- 【智能路由器】基于netfilter的高效广告植入(非代理方式)
- MFC网速及流量监控程序的实现(一)
- MFC网速及流量监控程序的实现(完)
- Linux路由器流量统计系统
- libpcap编程--流量统计。。
- 统计分析师:Linux路由器流量统计系统
- Android实现流量统计和网速监控悬浮窗
- golang web流量统计 上
- 基于openwrt系统的智能路由器
- 路由器如何限制网速
- 我想有个用得上的智能路由器
- 基于Nginx dyups模块的站点动态上下线
- Android基于TrafficStats实现实时流量统计
- iOS 客户端基于 WebP 图片格式的流量优化(上)
- iOS 客户端基于 WebP 图片格式的流量优化(上)
- iOS 客户端基于 WebP 图片格式的流量优化(上)
- webView的用法
- hdu 3346 Lucky Number
- 新的博客
- Theano-Deep Learning Tutorials 笔记:LSTM Networks for Sentiment Analysis
- 【Java】java日历
- 【智能路由器】设备流量、网速统计及上下线提醒(基于netfilter编程)
- virtualbox 安装增强包 实现 centos 6.5 / 7 与宿主机文件共享
- 哪种数学知识你可以轻松向外行解释,并且听起来超级酷?
- 根据指定地点的经纬度算出指定的距离的经纬度范围
- hdoj Max Sum Plus Plus Plus 1244 (DP)m个连续段的最大和
- Spring+Struts2+Hibernate概述
- UI学习知识总结
- 杭电1201 18岁生日
- android利用xmpp进行即时通信