【智能路由器】设备流量、网速统计及上下线提醒(基于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

2 0
原创粉丝点击