【包】netfilter、libpcap使用

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/Li_Jiaqian/article/details/83001743

利用netfilter、libpcap、scapy抓取数据包,分析netfilter、libpcap、scapy、tcpdump、wireshark的cpu及内存占用率,使用netfilter、scapy、tcpdump、wireshark实现openstack网络架构上patch-tun端口抓包,查阅了一些其他的数据包捕获技术(例如,libpcap-mmap、pf_ring、dsniff以及Windows的HOOK技术)。

一.netfilter

1.Netfilter/IPTables是Linux2.4.x之后新一代的Linux防火墙机制,是linux内核的一个子系统,在整个网络流程的若干位置放置了一些检测点(HOOK),在每个检测点上登记了一些处理函数进行处理,可以实现诸如数据包过滤、网络地址转换(NAT)和基于协议类型的连接跟踪。

2. 在IP层有5个HOOK点:

[1]:NF_IP_PRE_ROUTING:刚刚进入网络层的数据包(刚刚进行完版本号,校验和等检测), 目的地址转换在此点进行;

[2]:NF_IP_LOCAL_IN:经路由查找后,送往本机的数据包通过此检查点;    [3]:NF_IP_FORWARD:要转发的包通过此检测点;

[4]:NF_IP_POST_ROUTING:所有马上便要通过网络设备出去的包通过此检测点;

[5]:NF_IP_LOCAL_OUT:本机进程发出的包通过此检测点。

3.使用nf_register_hook函数可以加入自己的代码,函数原型:int nf_register_hook(struct nf_hook_ops *reg),需要生成一个nf_hook_ops结构的实例:

struct nf_hook_ops {

       struct list_head list;

       /* User fills in from here down. */

       nf_hookfn *hook;//用户定义的钩子函数

       struct module *owner;//注册这个钩子函数的模块,

       u_int8_t pf;//索引到特定协议,ip层是PF_INET

       unsigned int hooknum;//索引到特定编号,即选择的HOOK

       /* Hooks are ordered in ascending priority. */

       int priority;//每个HOOK点可以有多个处理函数,定义处理函数的优先级

};

 

4.netfilter2.c:

#include <linux/netfilter.h> 

#include <linux/init.h> 

#include <linux/module.h> 

#include <linux/netfilter_ipv4.h> 

#include <linux/ip.h> 

#include <linux/inet.h> 

unsigned int my_hookfn(unsigned int hooknum, 

    struct sk_buff *skb, 

    const struct net_device *in,  //net_device网络设备结构体,用于内核自身(设备驱动、上层协议等)对网络设备的操作

    const struct net_device *out, 

    //net_device结构体:网卡硬件类信息,统计类信息,上层协议处理接口,流控接口

    int (*okfn)(struct sk_buff *)) 

    struct iphdr *iph;  //iphdrlinuxip数据包的描述结构体

    iph = ip_hdr(skb); 

    printk(KERN_INFO"%pI4->%pI4\n",&iph->saddr,&iph->daddr); //在内核中运行的printf,%p用来输出指针类型自身的值

    return NF_ACCEPT;  //返回值,继续正常传输数据报

 

/* A netfilter instance to use */ 

static struct nf_hook_ops nfho1 = { 

    .hook = my_hookfn, 

    .pf = PF_INET, 

    .hooknum = NF_INET_PRE_ROUTING,  //刚进入网络层

    .priority = NF_IP_PRI_FIRST, 

    .owner = THIS_MODULE, 

}; 

static int __init catch_init(void) 

    if (nf_register_hook(&nfho1)) { 

        printk(KERN_ERR"nf_register_hook() failed\n"); 

        return -1; 

    }

    return 0; 

static void __exit catch_exit(void) 

    nf_unregister_hook(&nfho1);

}  

module_init(catch_init); 

module_exit(catch_exit); 

MODULE_LICENSE("GPL"); 

 

5.Makefile:

ifneq ($(KERNELRELEASE),)

    obj-m += netfilter2.o

else

    PWD := $(shell pwd)

    KVER := $(shell uname -r)

    KDIR := /lib/modules/$(KVER)/build

default:   

       $(MAKE) -C $(KDIR)  M=$(PWD) modules

all:

       make -C $(KDIR) M=$(PWD) modules

clean:

       rm -rf *.o *.mod.c *.ko *.symvers *.order *.makers

endif

 

6.使用tail -f /var/log/messages查看捕获到的数据包

 

二.libpcap

1.libpcap是一个网络数据包捕获函数库,对网卡接收到的链路层数据包进行过滤,根据过滤规则决定是否接收或拷贝,若没有规则,则全部上交。libpcap使用流程:确定嗅探的端口,使用文件句柄传入需要嗅探的设备,创建规则集合,进入pcap主循环。

 

2. libpcap的抓包框架:

pcap_lookupdev():查找网络设备,返回可被pcap_open_live()函数调用的网络设备名指针。

pcap_lookupnet():获得指定网络设备的网络号和掩码。

pcap_open_live():打开设备。

pcap_compile():函数用于将用户制定的过滤策略编译到过滤程序中

pcap_setfilter():函数用于设置过滤器

pcap_loop():与pcap_next()和pcap_next_ex()两个函数一样用来捕获数据包

pcap_close():函数用于关闭网络设备,释放资源      

 

3.利用libpcap抓包的工作流程:

4.libpcaptest3.c:

#include <stdio.h>

#include <pcap.h>

#include <arpa/inet.h>

#include <time.h>

#include <stdlib.h>

#include <string.h>

#define BUFSIZE 1514

struct ether_header

{

    unsigned char mac_dhost[6];    //以太网目的地址

    unsigned char mac_shost[6];    //以太网源地址

    unsigned short mac_type;  //以太网数据包类型

};

struct ether_header_IP      //IP数据包

{

    unsigned char mac_dhost[6];   

    unsigned char mac_shost[6];

    unsigned short mac_type;

    unsigned char ip_head[12];  //版本号-首部校验和的12字节

    unsigned char ip_shost[4];   //IP

    unsigned char ip_dhost[4];   //目的IP

};

struct ether_header_ARP         //ARP数据包

{

    unsigned char mac_dhost[6];

    unsigned char mac_shost[6];

    unsigned short mac_type;

    unsigned char arp_head[8];      //arp首部

    unsigned char arp_sha[6];   //MAC

    unsigned char arp_spa[4];   //IP

    unsigned char arp_tha[6];   //目的MAC

    unsigned char arp_tpa[4];   //目的IP

};

/*******************************回调函数************************************/

void ethernet_protocol_callback(unsigned char *argument,const struct pcap_pkthdr *packet_heaher,const unsigned char *packet_content)

{

    unsigned char *mac_string;

    unsigned short mac_t;

    unsigned char *ip_s,*ip_d;

    struct ether_header *ethernet_protocol;

    ethernet_protocol = (struct ether_header *)packet_content;

    mac_t=ntohs(ethernet_protocol->mac_type);//数据包类型

    if (mac_t==0x0800){          //IP数据包

           struct ether_header_IP *ethernet_protocol_IP;

           ethernet_protocol_IP = (struct ether_header_IP *)packet_content;

        printf("----------------------------------------------------\n");

        printf("%s", ctime((time_t *)&(packet_heaher->ts.tv_sec))); //转换时间

        ip_s=(unsigned char *)ethernet_protocol_IP->ip_shost;

        ip_d=(unsigned char *)ethernet_protocol_IP->ip_dhost;

        printf("ip_s:%3d.%3d.%3d.%3d\n",*(ip_s+0),*(ip_s+1),*(ip_s+2),*(ip_s+3));

        printf("ip_d:%3d.%3d.%3d.%3d\n",*(ip_d+0),*(ip_d+1),*(ip_d+2),*(ip_d+3));

    }

    else if(mac_t==0x0806){           //ARP数据包

           struct ether_header_ARP *ethernet_protocol_ARP;

           ethernet_protocol_ARP = (struct ether_header_ARP *)packet_content;

        printf("----------------------------------------------------\n");

        printf("%s", ctime((time_t *)&(packet_heaher->ts.tv_sec))); //转换时间

        ip_s=(unsigned char *)ethernet_protocol_ARP->arp_spa;

        ip_d=(unsigned char *)ethernet_protocol_ARP->arp_tpa;

        printf("ip_s:%3d.%3d.%3d.%3d\n",*(ip_s+0),*(ip_s+1),*(ip_s+2),*(ip_s+3));

        printf("ip_d:%3d.%3d.%3d.%3d\n",*(ip_d+0),*(ip_d+1),*(ip_d+2),*(ip_d+3));

       }

    switch(mac_t)

    {

        case 0x0800:printf("The network layer is IP protocol\n");break;//ip

        case 0x0806:printf("The network layer is ARP protocol\n");break;//arp

        case 0x0835:printf("The network layer is RARP protocol\n");break;//rarp

        default:break;

    }

    usleep(800*1000);             //挂起0.8s

}

 

int main(int argc, char *argv[])

{

    char error_content[100];    //出错信息

    pcap_t * pcap_handle;

    unsigned char *mac_string;

    unsigned short ethernet_type;           //以太网类型

    char *net_interface = "eth0";                 //接口名字

    struct pcap_pkthdr protocol_header;

    struct ether_header *ethernet_protocol;

    pcap_handle = pcap_open_live(net_interface,BUFSIZE,1,0,error_content);//打开网络接口

if(pcap_loop(pcap_handle,-1,ethernet_protocol_callback,NULL) < 0)

//持续抓包,使用-1

    {

        perror("pcap_loop");   //用来将上一个函数发生错误的原因输出到stderr

    }

    pcap_close(pcap_handle);       //关闭接口

    return 0;

}

 

5. gcc -o libpcaptest3 libpcaptest3.c -lpcap

./libpcaptest3

 

三.scapy

 

1.scapy是一个可用于网络嗅探的非常强大的第三方库,内部实现了大量的网络协议,(DNS,ARP,IP,TCP,UDP等等),能够伪造或者解码大量的网络协议数据包,能够发送、捕捉、匹配请求和回复包。

 

2.sniff(filter="",iface="any", prn=function, count=N)

filter可以过滤:源/目的地址,协议,端口号等(例如,filter=”ip src 10.0.0.210 and icmp”);

iface用来指定要在哪个网络接口上进行抓包(通常不指定即所有网络接口);

prn指定回调函数,每当一个符合filter的报文被探测到时,就会执行回调函数,通常使用lambda表达式来写回调函数(例如,prn=lambda x:x.sprintf("%IP.src%:%TCP.sport% -> %IP.dst%:%TCP.dport%”)打印对话的IP地址及其端口号)

count指定最多嗅探多少个报文(是指符合filter条件的报文,而非所有报文)。

  

 

3. sp.py

from scapy.all import *

import scapy

pc=sniff(iface='eth0',prn=lambda x:x.summary())

//过滤出经过eth0端口的所有数据报,打印出报文摘要

 

四.占用资源对比

 

tcpdump

wireshark

scapy

netfilter

libpcap

cpu占用(%)

0.0

0.2

1.6

0.0

0.0

内存占用(%)

0.3

3.8

2.1

0.0

0.1 

 可以看到除了wireshark和scapy占用内存与cpu较大,其余方法的内存与cpu占用均较小,netfilter直接将捕获结果写入/var/log/messages中,tcpdump可以使用-w命令写入文件,但是该文件只能使用tcpdump -r查看。

 

五.openstack网络架构上patch-tun端口抓包记录

 

  1. VXLAN模式的基本结构:

      假设虚拟机VM1发送数据包给VM2,则数据包流向如图所示:虚拟机网卡连接物理机的tap设备(A)——》通过VETH pair连接到网桥qbr的端口vnet(B)——》通过VETH pair连接到网桥br-int的端口qvo(D)——》patch-tun(E)——》patch-int(F)——》br-tun根据转发规则转发。因此虚拟机之间的通信一定会经过内部端口patch-tun。

 

·为何TAP不直接连接到br-int上?

OpenStack需要通过iptables实现security group的安全策略功能,而目前openvswitch不支持应用iptables规则的Tap设备,所以TAP设备A没有直接连接到网桥br-int上。qbr的存在主要是为了辅助iptables来实现security group功能,有时候也被称为安全网桥。

 

2.在物理机10.190.88.29的patch-tun端口上抓取数据包:

由于patch-tun是内部端口,所以需要在patch-tun端口上添加镜像,设置所有经过patch-tun端口的数据包同样发送到ptun1,从而捕获到虚拟机之间通信的数据包:

利用tcpdump监听ptun1上的数据包:tcpdump -i ptun1

 

3.tcpdump、wireshark、scapy在ptun端口抓包的功耗分析:

(1)tcpdump抓包:

使用ps aux|grep tcpdump命令,可以看到tcpdump进程几乎不占用CPU及内存:

(2)wireshark抓包:
而使用tshark命令抓包,若将捕获到的数据包写入文件,占用CPU为0.1%,不写入文件直接输出占用CPU为0.3%。

4.scapy模块抓包:

利用python的scapy模块,捕获ptun端口上的数据包,占用CPU为3.4%,明显高于(1)和(2):

 

5.netfilter捕获所有进入网络层的数据包,可以捕获到虚拟机相互通信的数据包;将libpcaptest.c文件中的接口名net_interface修改为”ptun1”,但是无法捕获到虚拟机之间的数据包,尝试将patch-tun收到/发送的数据包镜像到em1?

ovs-vsctl add-port br-int em1

ovs-vsctl -- set Bridge br-int mirrors=@m -- --id=@em1 get Port em1 -- --id=@patch-tun get Port patch-tun -- --id=@m create Mirror name=mymirror2 select-dst-port=@patch-tun select-src-port=@patch-tun output-port=@em1 select_all=1

 

六.其他数据包捕获技术

 

1.libpcap-mmap

利用libpcap过滤网络数据包的工作流程:

libpcap接收数据包时会产生中断,若将数据包拷贝到内核、用户空间,将消耗大量的cpu资源。libpcap-mmap减少了内核的拷贝次数和系统的调用开销。

libpcap-mmap建立核心态内存和用户态内存之间的映射,可以通过调用系统函数recvfrom()将数据包从网卡上直接传送到用户态内存中,从而减少数据拷贝的次数。

 

2.pf_ring

在linux内核层添加带环状缓存的socket,提供socket接口,将网卡接收到的数据包拷贝到该环状缓存中,释放原数据包,应用层可以通过socket连接从环状缓存中读取数据包。

 

3.dsniff

dsniff是一个综合性的网络嗅探工具包,可以进行网络监视、针对SSH和SSL发起攻击、发起主动欺骗、分析数据包获取密码等。

 

4.利用windows的HOOK技术

将编写的具有抓包功能的动态链接库(DLL)程序模块注入每个进程中,选择需要抓包的目标进程,在DLL中拦截该进程对操作系统套接字函数接口(SOCKET API)的调用,利用动态装载技术,获取套接字函数接口的地址,保存该地址,修改该地址为自定义的抓包函数,抓包完成后恢复原套接字函数地址。

抓包流程图:

 

猜你喜欢

转载自blog.csdn.net/Li_Jiaqian/article/details/83001743
今日推荐