何谓libnet、libpcap

来源:互联网 发布:史明克固彩淘宝 编辑:程序博客网 时间:2024/05/17 12:46

目前众多的网络安全程序、工具和软件都是基于socket设计和开发的。由于在安全程序中通常需要对网络通讯的细节(如连接双方地址/端口、服务类型、传输控制等)进行检查、处理或控制,象数据包截获、数据包头分析、数据包重写、甚至截断连接等,都几乎在每个网络安全程序中必须实现。为了简化网络安全程序的编写过程,提高网络安全程序的性能和健壮性,同时使代码更易重用与移植,最好的方法就是将最常用和最繁复的过程函数,如监听套接口的打开/关闭、数据包截获、数据包构造/发送/接收等,封装起来,以API library的方式提供给开发人员使用。

在众多的API library中,对于类Unix系统平台上的网络安全工具开发而言,目前最为流行的C API library有libnet、libpcap、libnids和libicmp等。它们分别从不同层次和角度提供了不同的功能函数。使网络开发人员能够忽略网络底层细节的实现,从而专注于程序本身具体功能的设计与开发。其中,

libnet提供的接口函数主要实现和封装了数据包的构造和发送过程。

libpcap提供的接口函数主要实现和封装了与数据包截获有关的过程。

利用这些C函数库的接口,网络安全工具开发人员可以很方便地编写出具有结构化强、健壮性好、可移植性高等特点的程序。因此,这些函数库在网络安全工具的开发中具有很大的价值,在scanner、sniffer、firewall、IDS等领域都获得了极其广泛的应用,著名的tcpdump软件、ethereal软件等就是在libpcap的基础上开发的。

另外也应该指出:由于其功能强大,这些函数库也被黑客用来构造TCP/IP网络程序对目标主机进行攻击。然而,TCP/IP网络的安全不可能也不应该建立在禁止大家使用工具的基础上,一个理想的网络,首先必须是一个开放的网络,这个网络应该在使用任何工具的情况下都是安全的和健壮的。从这点考虑,这些工具的使用,对促进现有网络系统的不断完善是大有裨益的。

 




libnet函数库框架和使用

libnet是一个小型的接口函数库,主要用C语言写成,提供了低层网络数据报的构造、处理和发送功能。libnet的开发目的是:建立一个简单统一的网络编程接口以屏蔽不同操作系统低层网络编程的差别,使得程序员将精力集中在解决关键问题上。他的主要特点是:

高层接口:libnet主要用C语言写成

可移植性:libnet目前可以在Linux、FreeBSD、Solaris、WindowsNT等操作系统上运行,并且提供了统一的接口

数据报构造:libnet提供了一系列的TCP/IP数据报文的构造函数以方便用户使用

数据报的处理:libnet提供了一系列的辅助函数,利用这些辅助函数,帮助用户简化那些烦琐的事务性的编程工作

数据报发送:libnet允许用户在两种不同的数据报发送方法中选择。

另外libnet允许程序获得对数据报的绝对的控制,其中一些是传统的网络程序接口所不提供的。这也是libnet的魅力之一。

libnet支持TCP/IP协议族中的多种协议,比如其上一个版本libnet1.0支持了10种协议,一些新的协议,比如对IPV6的支持还在开发之中。

libnet目前最新的版本是1.1版本,在该版本中,作者将这些函数做了进一步的封装,用户的使用步骤也得到了进一步的简化。内存的初始化、管理、释放等以及校验和的计算等函数,在默认情况下,都无须用户直接干预,使得libnet的使用更为方便。作者还提供了基于老版本的应用程序移植到新版本上的方法指导。利用libnet1.1函数库开发应用程序的基本步骤以及几个关键的函数使用方法简介如下:

  1. 初始化
    libnet_t *libnet_init(int injection_type, char *device, char *err_buf); 

    该函数初始化libnet函数库,返回一个libnet_t类型的描述符,以备随后的构造数据报和发送数据报的函数中使用。injection_type指明了发送数据报使用的接口类型,如数据链路层或者原始套接字等。Device是一个网络设备名称的字符串,在Linux下是"eth0"等。如果函数错误,返回NULL,而err_buf字符串中将携带有错误的原因。

  2. 数据报的构造

    libnet提供了丰富的数据报的构造函数,可以构造TCP/IP协议族中大多数协议的报文,还提供了一些对某些参数取默认数值的更简练的构造函数供用户选择。比如libnet_autobuild_ipv4()等。

  3. 数据报的发送
    int libnet_write(libnet_t *l); 

    该函数将l中描述的数据报发送的网络上。成功将返回发送的字节数,如果失败,返回-1。你可以调用libnet_geterror()得到错误的原因

  4. 退出
    void libnet_destroy(libnet_t *l); 

 




libpcap函数库框架和使用

libpcap的英文意思是 Packet Capture library,即数据包捕获函数库。该库提供的C函数接口可用于需要捕获经过网络接口(通过将网卡设置为混杂模式,可以捕获所有经过该接口的数据报,目标地址不一定为本机)数据包的系统开发上。著名的TCPDUMP就是在libpcap的基础上开发而成的。libpcap提供的接口函数主要实现和封装了与数据包截获有关的过程。这个库为不同的平台提供了一致的编程接口,在安装了libpcap的平台上,以libpcap为接口写的程序,能够自由的跨平台使用。在Linux系统下,libpcap可以使用BPF(Berkeley Packet Filter)分组捕获机制来获得很高的性能。

利用libpcap函数库开发应用程序的基本步骤以及几个关键的函数使用方法简介如下:


  1. char *pcap_lookupdev(char *errbuf) 

    该函数用于返回可被pcap_open_live()或pcap_lookupnet()函数调用的网络设备名(一个字符串指针)。如果函数出错,则返回NULL,同时errbuf中存放相关的错误消息。


  2. int pcap_lookupnet(char *device, bpf_u_int32 *netp,bpf_u_int32 *maskp, char *errbuf) 

    获得指定网络设备的网络号和掩码。netp参数和maskp参数都是bpf_u_int32指针。如果函数出错,则返回-1,同时errbuf中存放相关的错误消息。

  3. 打开设备
    pcap_t *pcap_open_live(char *device, int snaplen,int promisc, int to_ms,char *ebuf) 

    获得用于捕获网络数据包的数据包捕获描述字。device参数为指定打开的网络设备名。snaplen参数定义捕获数据的最大字节数。promisc指定是否将网络接口置于混杂模式。to_ms参数指定超时时间(毫秒)。ebuf参数则仅在pcap_open_live()函数出错返回NULL时用于传递错误消息。

  4. 编译和设置过滤器
    int pcap_compile(pcap_t *p, struct bpf_program *fp,char *str, int optimize, bpf_u_int32 netmask) 

    将str参数指定的字符串编译到过滤程序中。fp是一个bpf_program结构的指针,在pcap_compile()函数中被赋值。optimize参数控制结果代码的优化。netmask参数指定本地网络的网络掩码。

    int pcap_setfilter(pcap_t *p, struct bpf_program *fp)  

    指定一个过滤程序。fp参数是bpf_program结构指针,通常取自pcap_compile()函数调用。出错时返回-1;成功时返回0。抓取下一个数据包

  5. 抓取数据包
    int pcap_dispatch(pcap_t *p, int cnt,pcap_handler callback, u_char *user) 

    捕获并处理数据包。cnt参数指定函数返回前所处理数据包的最大值。cnt=-1表示在一个缓冲区中处理所有的数据包。cnt=0表示处理所有数据包,直到产生以下错误之一:读取到EOF;超时读取。callback参数指定一个带有三个参数的回调函数,这三个参数为:一个从pcap_dispatch()函数传递过来的u_char指针,一个pcap_pkthdr结构的指针,和一个数据包大小的u_char指针。如果成功则返回读取到的字节数。读取到EOF时则返回零值。出错时则返回-1,此时可调用pcap_perror()或pcap_geterr()函数获取错误消息。

    int pcap_loop(pcap_t *p, int cnt, pcap_handler callback, u_char *user)  

    功能基本与pcap_dispatch()函数相同,只不过此函数在cnt个数据包被处理或出现错误时才返回,但读取超时不会返回。而如果为pcap_open_live()函数指定了一个非零值的超时设置,然后调用pcap_dispatch()函数,则当超时发生时pcap_dispatch()函数会返回。cnt参数为负值时pcap_loop()函数将始终循环运行,除非出现错误。

    u_char *pcap_next(pcap_t *p, struct pcap_pkthdr *h)  

    返回指向下一个数据包的u_char指针。


  6. void pcap_close(pcap_t *p) 

    关闭p参数相应的文件,并释放资源。

  7. 其他的辅助函数
    FILE *pcap_file(pcap_t *p) 

    返回被打开文件的文件名。

    int pcap_fileno(pcap_t *p) 

    返回被打开文件的文件描述字号码。

 



综合使用libnet和libpcap:ARP例程

综合使用libnet和libpcap可以构造强有力的网络分析、诊断、和应用程序。一个具有普遍意义的综合使用libnet和libpcap的程序的原理框架如图1所示:

本节给出一个综合应用libnet和libpcap的简单例程,其功能是在接收到一个来自特定主机的ARP请求报文之后,发出ARP回应报文,通知该主机请求的IP地址对应的MAC地址。这个程序实现了标准的ARP协议,但是却不同于操作系统内核中标准的实现方法:该程序利用了libpcap在数据链路层抓包,利用了libnet向数据链路层发包,是使用libnet和libpcap构造TCP/IP协议软件的一个例程。该程序很简单,但已经可以说明libnet和libpcap的综合使用方法:

/* tell destination host with ip 'dstip' that the host with request ip 'srcip' is with mac address srcmac
* author: white cpf  2003.5.15.
* compile: gcc arp.c -lnet -lpcap -o arp
*/

#include "/usr/include/libnet.h"
#include
void usage(char * exename){
printf(" tell dstip with dstmac that srcip is at srcmac. /n");
printf(" usage: %s -d dstip -s srcip -D dstmac -S srcmac /n",exename);
return ;
}
//程序输入:来自命令行参数
u_char ip_src[4],ip_dst[4];
u_char enet_src[6],enet_dst[6];
extern int mac_strtochar6(u_char * enet,char * macstr);//将字符串格式的MAC地址转换为6字节类型r
int get_cmdline(int argc,char *argv[]);//命令行参数处理函数
int main(int argc, char *argv[]){
    libnet_t *l;
    libnet_ptag_t t;
    u_char *packet;
    u_long packet_s;
    char device[5]="eth0";
    char errbuf[LIBNET_ERRBUF_SIZE];
    char filter_str[100]="";
    struct bpf_program fp;      /* hold compiled program     */
    char *dev;
    pcap_t* descr;
    struct pcap_pkthdr hdr;     /* pcap.h    */
    u_char * packet;
    bpf_u_int32 maskp;          /* subnet mask               */
    bpf_u_int32 netp;           /* ip                        */
    int promisc=0;              /* set to promisc mode? */
    int pcap_time_out=5;
    int c, ret;
    u_long i;
    if(get_cmdline(argc,argv)<=0){
usage(argv[0]);
exit(0);
    }
  
dev = pcap_lookupdev(errbuf);
if(dev == NULL){
    fprintf(stderr,"%s/n",errbuf);
    return -1;
    }
    ret=pcap_lookupnet(dev,&netp,&maskp,errbuf);
if(ret==-1){
    fprintf(stderr,"%s/n",errbuf);
    return -1;
}
descr = pcap_open_live(dev,BUFSIZ,promisc,pcap_time_out,errbuf);
    if(descr == NULL){
    printf("pcap_open_live(): %s/n",errbuf);
    return -1;
    }
sprintf(filter_str,"arp and (src net %d.%d.%d.%d)",ip_dst[0],ip_dst[1],ip_dst[2],ip_dst[3]);
if(pcap_compile(descr,&fp,filter_str,0,netp) == -1){
printf("Error calling pcap_compile/n");
return -1;
}
    if(pcap_setfilter(descr,&fp) == -1){
    printf("Error setting filter/n");
    return -1;
    }
while(1){
printf("wait packet:filter:%s/n",filter_str);
packet=pcap_next(descr, &hdr);
if(packet == NULL){
    continue;
}
    l = libnet_init(LIBNET_LINK_ADV,device,errbuf);
    if (l == NULL){
        fprintf(stderr, "libnet_init() failed: %s", errbuf);
        exit(EXIT_FAILURE);
    }
    t = libnet_build_arp(
            ARPHRD_ETHER,                           /* hardware addr */
            ETHERTYPE_IP,                           /* protocol addr */
            6,                                      /* hardware addr size */
            4,                                      /* protocol addr size */
            ARPOP_REPLY,                            /* operation type */
            enet_src,                               /* sender hardware addr */
            ip_src,                           /* sender protocol addr */
            enet_dst,                               /* target hardware addr */
            ip_dst,                           /* target protocol addr */
            NULL,                                   /* payload */
            0,                                      /* payload size */
            l,                                      /* libnet handle */
            0);                                     /* libnet id */
    if (t == -1){
        fprintf(stderr, "Can't build ARP header: %s/n", libnet_geterror(l));
        goto bad;
    }
    t = libnet_autobuild_ethernet(
            enet_dst,                               /* ethernet destination */
            ETHERTYPE_ARP,                          /* protocol type */
            l);                                     /* libnet handle */
    if (t == -1){
        fprintf(stderr, "Can't build ethernet header: %s/n", libnet_geterror(l));
        goto bad;
    }
    c = libnet_adv_cull_packet(l, &packet, &packet_s);
    if (c == -1){
        fprintf(stderr, "libnet_adv_cull_packet: %s/n", libnet_geterror(l));
        goto bad;
    }
    c = libnet_write(l);
    if (c == -1){
        fprintf(stderr, "Write error: %s/n", libnet_geterror(l));
        goto bad;
    }
    continue;
bad:
    libnet_destroy(l);
    return (EXIT_FAILURE);
}
    libnet_destroy(l);
    return (EXIT_FAILURE);
}
int get_cmdline(int argc,char *argv[]){
char c;
char string[]="d:s:D:S:h";
    while((c = getopt(argc, argv, string)) != EOF){
        if(c=='d')
            *((unsigned int*)ip_dst)=(unsigned int)inet_addr(optarg);
        else if(c== 's')
            *((unsigned int*)ip_src)=(unsigned int)inet_addr(optarg);
        else if(c=='D')
            mac_strtochar6(enet_dst,optarg);
        else if(c=='S')
            mac_strtochar6(enet_dst,optarg);
        else if(c=='h')
            return 0;
        else
            return -1;
    }
return 1;
}