Libnet 1.1教程

发布时间:2025-12-09 19:12:59 浏览次数:4

原文链接: Libnet 1.1 tutorial.

注意:本文不提供安装编译的详细过程,可参见其他博客

由于译者本人水平原因,只翻译了前5章,后续章节涉及IPv6、IP分片和高级

函数则未翻译。在Libnet函数编程资料少的情况下,原博文十分详尽。

😛 😛 😛

libnet 1.1 教程

        • 1. Libnet介绍
          • * libnet如何工作
          • * libnet会话(libnet_t )
          • * 接收和检查你的包
            • 同时使用libpcap和libnet时的建议
          • * 编译
          • * 整数类型
          • * 错误
        • 2. 打开和关闭libnet
          • * Example 1 : init.c
          • * Example 2 : init_devname.c
        • 3. 处理地址
          • * 字符地址与数字地址
          • * 处理地址所需要用到的函数
          • * Example 3: addr.c
          • * Example 4: get_own_addr.c
        • 4. 造包函数
          • * 造包注意
          • * Example 5: ping.c
          • * Example 6: arp.c
        • 5. 发送多个包
          • * 程序崩溃
          • * Example 7: reping_tags.c
          • * Example 8: reping_clear.c

1. Libnet介绍

在这之前,你需要安装最新的 libnet.

与本教程相关的代码在 here.

* libnet如何工作
  • libnet_init()初始化

  • 从应用层到链路层构筑报文头。以一个UDP报文为例,
    libnet_build_udp()
    libnet_build_ipv4()
    libnet_build_ethernet()

  • 用libnet_write()注入一个报文

  • 以下两种方式中任一种发送另一个报文
    a: 用libnet_clear_packet()清除报文,回到2
    b: 直接回到2,但是使用上一次返回的libnet_tag

  • 当所有包发送完后,使用libnet_destroy()结束会话。

  • libnet-functions.h 和 libnet-headers.h对你也许有用。

    * libnet会话(libnet_t )

    几乎每一个libnet函数都会接受一个libnet_t *的参数,libnet_t保存了

    所有你需要的信息,你可以改变上一个已经建立好的包而不是重新开始一个

    会话。所以不要每次构建一个包都开启和关闭一个会话,保证在程序开始

    时初始化libnet_t, 在程序结束的时候关闭对话。不要每次构造一个包

    的时候重新初始化和关闭一个libnet会话,如果是构造相同的多个包则只需要

    重新使用libnet_ptag_t 即可。

    * 接收和检查你的包

    libnet只能发送包,如果你的程序需要抓取包,你可以使用libpcap。

    为了检查你的包是否抓到,或者想抓取指定的包,你可以使用wireshark

    或者更轻量级的tcpdump。

    同时使用libpcap和libnet时的建议

    在使用libnet的构造包的函数时,最好在捕获时使用u_char[]或者

    uint8_t。当然你也可以使用一个结构来更简洁的表示,但最终每个字段都

    将转换为一个u_char[]。在使用捕获包的数据来构造时,不需要额外的工

    作,因为libnet所需要的数据就是网络字节序的。

    举个例子,你想把抓到的包的IP地址作为目的地址。通过回调函数

    loop_back你可以嗅探每一个包,它传回一个u_char[]。以太网头部长度

    为14,源IP地址在IP头部的第13字节。因此用packet[26]:

    void build_reply(..., const uchar *packet) {u_char *ip_src = packet[26];...ip_tag = libnet_build_ipv4(..., *(u_int_32t*)ip_src, ...);...}

    来获取源IP地址,之后需要的字段也可以通过强制转换指针来获取。

    * 编译

    最少使用-lnet来编译程序,作者用的编译规则如下

    gcc -ggdb -Wall `libnet-config --defines``libnet-config --libs` example.c -o example
    * 整数类型

    C允许平台对int、long、short等有不同长度的实现,但是对于网络报文头来

    说长度应该是固定的。你处理到最常用的类型应该是u_int32_t, u表示无符

    号,32表示长度。注意,u_char、u_int8_t、uchar、uint8_t在几乎

    所有平台表示同种的数据类型。

    * 错误

    当使用llibnet_init()时,用char errbuf[]来处理错误信息。而在这之

    后,你可以使用

    char * libnet_geterror (libnet_t *l)

    来了解出错信息,它唯一需要的是一个libnet会话。


    第一章已经介绍了基础概念,接下来开始编码吧!

    2. 打开和关闭libnet

    libnet_init函数原型

    libnet_t * libnet_init (int injection_type, char *device,char *err_buf)

    从最后一个参数到第一个参数分别为

    • err_buf 存储出错信息的地方
    • device 是你的嗅探设备名(eth0)或者IP地址(10.0.0.1。如果device字段被设置成NULL,libnet会自动给你找一个。但是如果设置为NULL时出了问题,将device替换为一个嗅探器的名字(ens33、lo)。
    • injection_type 是报文注入类型,从链路层到网络层。下面用到的是 LIBNET_RAW4 (IPv4 ) 和 LIBNET_LINK (链路层之上).

    返回值是一个libnet_t,几乎在所有造包函数中都需要用到。

    开始写代码了!

    * Example 1 : init.c
    #include <stdio.h>#include <stdlib.h>#include <libnet.h>int main() {libnet_t *l; /* the libnet context */char errbuf[LIBNET_ERRBUF_SIZE];l = libnet_init(LIBNET_RAW4, NULL, errbuf);if ( l == NULL ) {fprintf(stderr, "libnet_init() failed: %s\n", errbuf);exit(EXIT_FAILURE);}libnet_destroy(l);return 0;}

    我们也可以自己提供设备名

    * Example 2 : init_devname.c
    #include <stdio.h>#include <stdlib.h>#include <libnet.h>int main(int argc, char **argv) {libnet_t *l; /* the libnet context */char errbuf[LIBNET_ERRBUF_SIZE];if ( argc == 1 ) {fprintf(stderr,"Usage: %s device\n", argv[0]);exit(EXIT_FAILURE);}l = libnet_init(LIBNET_RAW4, argv[1], errbuf);if ( l == NULL ) {fprintf(stderr, "libnet_init() failed: %s\n", errbuf);exit(EXIT_FAILURE);}libnet_destroy(l);return 0;}

    3. 处理地址

    如果你要处理IPv4地址和以太网物理地址,你需要的u_int32_t和

    u_int8_t [6]来保存这些地址。注意,libnet需要的是网络字节序

    即使你的主机是采用小端模式,但你在内存中读取的还是高字节在低地址。

    下面的例子将有助于你理解这一点

    * 字符地址与数字地址

    注意区别字符地址和数字地址。

    如127.0.0.1:

    • 在数字地址中u_int8_t [4] = {127, 0, 0, 1}
    • 在字符地址中char[] = {'1','2','7','.','0','.','0','.','1'}

    造包函数中需要的是数字地址,域名解析函数返回的是字符地址。从libnet中

    获取的同样也是数组地址。

    * 处理地址所需要用到的函数
  • 数字地址转字符地址
  • char* libnet_addr2name4 (u_int32_t in, u_int8_t use_name)
    • in是数字地址,u_int32_t
    • use_name为LIBNET_RESOLVE和LIBNET_DONT_RESOLVE,前者将其解析为域名(google.com),后者只将其转为点分十进制(8.8.8.8)。
  • 字符地址转数字地址

    u_int32_t libnet_name2addr4 (libnet_t *l, char *host_name,u_int8_t use_name)
  • mac字符串地址转数字地址

    u_int8_t* libnet_hex_aton (int8_t * s, int * len)
    • len是以太网地址长度,通常是6
    • s是字符串地址数组

    libnet_hex_aton返回的数组会隐式调用malloc,所以记得free。

    接下来就是例子。

    * Example 3: addr.c
    #include <stdio.h>#include <stdlib.h>#include <libnet.h>#include <stdint.h>int main() {libnet_t *l; /* the libnet context */char errbuf[LIBNET_ERRBUF_SIZE];char ip_addr_str[16], mac_addr_str[18];u_int32_t ip_addr;u_int8_t *ip_addr_p, *mac_addr;int i, length; /* for libnet_hex_aton() */l = libnet_init(LIBNET_RAW4, NULL, errbuf);if ( l == NULL ) {fprintf(stderr, "libnet_init() failed: %s\n", errbuf);exit(EXIT_FAILURE);}/* IP address */printf("IP address: ");scanf("%15s",ip_addr_str);ip_addr = libnet_name2addr4(l, ip_addr_str,\LIBNET_DONT_RESOLVE);if ( ip_addr != -1 ) {/* 打印ip_addr看是否正确 *//* libnet_name2addr4 返回网络字节序(大端). */ip_addr_p = (u_int8_t*)(&ip_addr);/* 看看系统是大端还是小端: *//*printf("ip_addr: %08X\n", ip_addr);printf("ip_addr_p: %02X%02X%02X%02X\n", ip_addr_p[0],\ip_addr_p[1], ip_addr_p[2], ip_addr_p[3]);*/printf("Address read: %d.%d.%d.%d\n", ip_addr_p[0],\ip_addr_p[1], ip_addr_p[2], ip_addr_p[3]);/* This would output the same thing, but I wanted to* show you how the address is stored in memory. *//*printf("Address read: %s\n", libnet_addr2name4(ip_addr,\LIBNET_DONT_RESOLVE));*/}elsefprintf(stderr, "Error converting IP address.\n");/* MAC address */printf("MAC address: ");scanf("%17s", mac_addr_str);mac_addr = libnet_hex_aton(mac_addr_str, &length);if (mac_addr != NULL) {/*打印mac_addr看是否正常工作 */printf("Address read: ");for ( i=0; i < length; i++) {printf("%02X", mac_addr[i]);if ( i < length-1 )printf(":");}printf("\n");/* 记得free掉分配的内存* libnet_hex_aton() */free(mac_addr);}elsefprintf(stderr, "Error converting MAC address.\n");libnet_destroy(l);return 0;}

    在这里用到了一种指针转换的技巧,在程序的42行将&ip_addr转换为

    u_int8_t*并且将它存储在ip_addr_p中。我们将一个指向4B的整形指针

    转换为了一个指向u_int8_t 大小为4的数组指针。这样做的原因是,如果你

    的系统是小端,这段代码也能正常显示。去掉44-46行的注释,你将看到同样

    的结果除了字节交换外。我们可以通过libnet_addr2name4得到相同的结

    果。可以去掉53-54行的注释来验证。


    你可以通过libnet来获取IP地址和mac地址

    u_int32_t libnet_get_ipaddr4 (libnet_t *l)libnet_ether_addr * libnet_get_hwaddr (libnet_t *l)

    你还需要了解结构体

    struct libnet_ether_addr { u_char ether_addr_octet[6]; /* Ethernet address */};
    • 注意当你遇到ioctl(): Can't assign requested address when calling libnet_get_ipaddr4()报警时,可能是你选择的设备名字不对。如果libnet_get_hwaddr正常,则查看你的网卡信息(ifconfig)看上面的mac与libnet所给的mac地址是否一致。若不一致,则在libnet_init时选择正确的设备名,而不是用NULL

    下面时获取本机IP与mac的例子

    * Example 4: get_own_addr.c
    #include <stdio.h>#include <stdlib.h>#include <libnet.h>#include <stdint.h>int main() {libnet_t *l; /* libnet context */char errbuf[LIBNET_ERRBUF_SIZE];u_int32_t ip_addr;struct libnet_ether_addr *mac_addr;l = libnet_init(LIBNET_RAW4, NULL, errbuf);if ( l == NULL ) {fprintf(stderr, "libnet_init() failed: %s\n", errbuf);exit(EXIT_FAILURE);}ip_addr = libnet_get_ipaddr4(l);if ( ip_addr != -1 )printf("IP address: %s\n", libnet_addr2name4(ip_addr,\LIBNET_DONT_RESOLVE));elsefprintf(stderr, "Couldn't get own IP address: %s\n",\libnet_geterror(l));mac_addr = libnet_get_hwaddr(l);if ( mac_addr != NULL )printf("MAC address: %02X:%02X:%02X:%02X:%02X:%02X\n",\mac_addr->ether_addr_octet[0],\mac_addr->ether_addr_octet[1],\mac_addr->ether_addr_octet[2],\mac_addr->ether_addr_octet[3],\mac_addr->ether_addr_octet[4],\mac_addr->ether_addr_octet[5]);elsefprintf(stderr, "Couldn't get own MAC address: %s\n",\libnet_geterror(l));libnet_destroy(l);return 0;}

    4. 造包函数

    libnet将构造报文头部的行为都封装了函数,一些还能自动造包

    (autobuild)。我们通常上只关心一些最重要的字段,然后剩下的让

    libnet帮助我们完成。

    在接下来的例子中,我们将尽可能的使用自动构造函数。你需要了解的是

    各个协议报文头部的含义(建议看RFC 😮)。

    比如这是一个普通的ipv4构造函数

    libnet_ptag_t libnet_build_ipv4 (u_int16_t len,u_int8_t tos, u_int16_t id, u_int16_t frag,u_int8_t ttl, u_int8_t prot, u_int16_t sum,u_int32_t src, u_int32_t dst, u_int8_t * payload,u_int32_t payload_s, libnet_t * l, libnet_ptag_t ptag)
    • len: 是报文长度,包括链路层头部
    • tos: 是服务类型
    • id: 序列号(id设置为0,系统就自己帮你填了)
    • frag: 分段标志和段偏移(没有分片就是0)
    • prot: 协议类型,上层协议的类型(IPPROTO_ICMP, IPPROTO_TCP, IPPROTO_UDP)
    • ttl: 生存期
    • sum: 校验和(设置为0,自动填)
    • src dst 源地址和目的地址
    • payload payload_s: 填充的报文内容指针和填充长度
    • ptag: 用来表示造包的头部,接下来会讲到。

    字段都是简单直接的,如果你有疑问可以阅读RFC或者直接设置为0。 😛

    * 造包注意

    当你需要构造ipv4上层的协议时,不要向函数中传入payload、payload_s参

    数。否则你造出的包不符合预期。


    现在让我们构造一个ICMP请求报文(从终端读入)。报文将携带10Byte或者

    更多但不超过MTU的长度。在这个例子中,我们仅仅使用一个字符

    串"libnet:D"。我们把它传递给libnet_build_icmpv4_echo(),用

    u_int8_t *的形式 来传递。如果我们想要读取回复或者是捕获,我们要做

    的是将他再重新转换为char *。如果你什么都不想发则,设置为NULL和0

    😛。

    需要的构造函数

    libnet_ptag_t libnet_build_icmpv4_echo (u_int8_t type,u_int8_t code, u_int16_t sum, u_int16_t id,u_int16_t seq, u_int8_t * payload, u_int32_t payload_s,libnet_t * l, libnet_ptag_t ptag)libnet_ptag_t libnet_autobuild_ipv4 (u_int16_t len,u_int8_t prot, u_int32_t dst, libnet_t *l)

    libnet_ptag_t、ptag在之后发送多个包的时候会讲到。

    关于这两个函数更多参见libnet-function.h。

    在下面的程序中还会使用到libnet_seed_prand()、

    libnet_get_prand()来生成和获取随机数。

    开始造包吧!


    * Example 5: ping.c
    #include <stdio.h>#include <stdlib.h>#include <libnet.h>#include <stdint.h>int main() {libnet_t *l; /* libnet context */char errbuf[LIBNET_ERRBUF_SIZE], ip_addr_str[16];u_int32_t ip_addr;u_int16_t id, seq;char payload[] = "libnet :D";int bytes_written;l = libnet_init(LIBNET_RAW4, NULL, errbuf);if ( l == NULL ) {fprintf(stderr, "libnet_init() failed: %s\n", errbuf);exit(EXIT_FAILURE);}/* Generating a random id */libnet_seed_prand (l);id = (u_int16_t)libnet_get_prand(LIBNET_PR16);/* Getting destination IP address */printf("Destination IP address: ");scanf("%15s",ip_addr_str);ip_addr = libnet_name2addr4(l, ip_addr_str,\LIBNET_DONT_RESOLVE);if ( ip_addr == -1 ) {fprintf(stderr, "Error converting IP address.\n");libnet_destroy(l);exit(EXIT_FAILURE);}/* Building ICMP header */seq = 1;if (libnet_build_icmpv4_echo(ICMP_ECHO, 0, 0, id, seq,\(u_int8_t*)payload,sizeof(payload), l, 0) == -1){fprintf(stderr, "Error building ICMP header: %s\n",\libnet_geterror(l));libnet_destroy(l);exit(EXIT_FAILURE);}/* Building IP header */if (libnet_autobuild_ipv4(LIBNET_IPV4_H +\LIBNET_ICMPV4_ECHO_H + sizeof(payload),\IPPROTO_ICMP, ip_addr, l) == -1 ){fprintf(stderr, "Error building IP header: %s\n",\libnet_geterror(l));libnet_destroy(l);exit(EXIT_FAILURE);}/* Writing packet */bytes_written = libnet_write(l);if ( bytes_written != -1 )printf("%d bytes written.\n", bytes_written);elsefprintf(stderr, "Error writing packet: %s\n",\libnet_geterror(l));libnet_destroy(l);return 0;}

    造包需要先从上层,再到下层,即

    libnet_build_icmpv4_echo=>libnet_autobuild_ipv4。

    在虚拟机上(192.168.186.112)ping 8.8.8的结果,在wireshark上捕获vnet8

    通过tcpdump抓取的结果


    现在让我们在链路层上,试试相同的事情。

    我们将构造一个ARP请求,IP地址由终端输入。

    我们需要用到的函数:

    libnet_ptag_t libnet_autobuild_arp (u_int16_t op,u_int8_t * sha, u_int8_t * spa, u_int8_t * tha,u_int8_t * tpa, libnet_t * l)libnet_ptag_t libnet_autobuild_ethernet (u_int8_t * dst,u_int16_t type, libnet_t * l)

    具体字段名看RFC 😃 和libnet_function.h。

    * Example 6: arp.c
    #include <stdio.h>#include <stdlib.h>#include <libnet.h>#include <stdint.h>int main() {libnet_t *l; /* the libnet context */char errbuf[LIBNET_ERRBUF_SIZE], target_ip_addr_str[16];u_int32_t target_ip_addr, src_ip_addr;u_int8_t mac_broadcast_addr[6] = {0xff, 0xff, 0xff, 0xff,\0xff, 0xff},mac_zero_addr[6] = {0x0, 0x0, 0x0, 0x0, 0x0, 0x0};struct libnet_ether_addr *src_mac_addr;int bytes_written;l = libnet_init(LIBNET_LINK, NULL, errbuf);if ( l == NULL ) {fprintf(stderr, "libnet_init() failed: %s\n", errbuf);exit(EXIT_FAILURE);}/* Getting our own MAC and IP addresses */src_ip_addr = libnet_get_ipaddr4(l);if ( src_ip_addr == -1 ) {fprintf(stderr, "Couldn't get own IP address: %s\n",\libnet_geterror(l));libnet_destroy(l);exit(EXIT_FAILURE);}src_mac_addr = libnet_get_hwaddr(l);if ( src_mac_addr == NULL ) {fprintf(stderr, "Couldn't get own IP address: %s\n",\libnet_geterror(l));libnet_destroy(l);exit(EXIT_FAILURE);}/* Getting target IP address */printf("Target IP address: ");scanf("%15s",target_ip_addr_str);target_ip_addr = libnet_name2addr4(l, target_ip_addr_str,\LIBNET_DONT_RESOLVE);if ( target_ip_addr == -1 ) {fprintf(stderr, "Error converting IP address.\n");libnet_destroy(l);exit(EXIT_FAILURE);}/* Building ARP header */if ( libnet_autobuild_arp (ARPOP_REQUEST,\src_mac_addr->ether_addr_octet,\(u_int8_t*)(&src_ip_addr), mac_zero_addr,\(u_int8_t*)(&target_ip_addr), l) == -1){fprintf(stderr, "Error building ARP header: %s\n",\libnet_geterror(l));libnet_destroy(l);exit(EXIT_FAILURE);}/* Building Ethernet header */if ( libnet_autobuild_ethernet (mac_broadcast_addr,\ETHERTYPE_ARP, l) == -1 ){fprintf(stderr, "Error building Ethernet header: %s\n",\libnet_geterror(l));libnet_destroy(l);exit(EXIT_FAILURE);}/* Writing packet */bytes_written = libnet_write(l);if ( bytes_written != -1 )printf("%d bytes written.\n", bytes_written);elsefprintf(stderr, "Error writing packet: %s\n",\libnet_geterror(l));libnet_destroy(l);return 0;}

    libnet_autobuild_ipv4中期望的是u_int8_t [],此时需要强制转换。

    抓取到的结果:


    5. 发送多个包

    有两种方式可以发送多个包

    • libnet_ptag_t标识一个造好的类型的包
    • libnet_clear_packet()直接清楚原有的再重新造

    当你每次调用构造包的函数时,都会返回一个标签,这个标签时

    libnet_ptag_t类型的。当你第一次使用这个函数的时候,用0 或者

    LIBNET_PTAG_INITIALIZER来初始化标签,它将丢弃掉原有内容造一个新的

    ;所以当你所造的头部没有改变时尽量使用之前的标签。

    * 程序崩溃

    频繁的销毁和再造新包可能会使你的程序崩溃,所以除非每一个包都与之前

    不同才使用libnet_clear_packet()。作者使用这种不推荐的方式在例8

    中,完全是因为作者是个懒狗。

    * Example 7: reping_tags.c
    #include <stdio.h>#include <stdlib.h>#include <libnet.h>#include <stdint.h>#include <unistd.h>int main() {libnet_t *l; /* libnet context */char errbuf[LIBNET_ERRBUF_SIZE], ip_addr_str[16];u_int32_t ip_addr;libnet_ptag_t icmp_tag, ip_tag;u_int16_t id, seq;int i;l = libnet_init(LIBNET_RAW4, NULL, errbuf);if ( l == NULL ) {fprintf(stderr, "libnet_init() failed: %s\n", errbuf);exit(EXIT_FAILURE);}icmp_tag = ip_tag = LIBNET_PTAG_INITIALIZER;/* Generating a random id */libnet_seed_prand(l);id = (u_int16_t)libnet_get_prand(LIBNET_PR16);/* Getting destination IP address */printf("Destination IP address: ");scanf("%15s",ip_addr_str);ip_addr = libnet_name2addr4(l, ip_addr_str,\LIBNET_DONT_RESOLVE);if ( ip_addr == -1 ) {fprintf(stderr, "Error converting IP address.\n");libnet_destroy(l);exit(EXIT_FAILURE);}/* Building ICMP header */seq = 1;icmp_tag = libnet_build_icmpv4_echo(ICMP_ECHO, 0, 0, id,\seq, NULL, 0, l, 0);if (icmp_tag == -1) {fprintf(stderr, "Error building ICMP header: %s\n",\libnet_geterror(l));libnet_destroy(l);exit(EXIT_FAILURE);}/* Building IP header */ip_tag = libnet_autobuild_ipv4(LIBNET_IPV4_H +\LIBNET_ICMPV4_ECHO_H, IPPROTO_ICMP, ip_addr, l);if (ip_tag == -1) {fprintf(stderr, "Error building IP header: %s\n",\libnet_geterror(l));libnet_destroy(l);exit(EXIT_FAILURE);}/* Writing 4 packets */for ( i = 0; i < 4; i++ ) {/* Updating the ICMP header */icmp_tag = libnet_build_icmpv4_echo(ICMP_ECHO, 0, 0,\id, (seq + i), NULL, 0, l, icmp_tag);if (icmp_tag == -1) {fprintf(stderr, "Error building ICMP header: %s\n",\libnet_geterror(l));libnet_destroy(l);exit(EXIT_FAILURE);}if ( libnet_write(l) == -1 )fprintf(stderr, "Error writing packet: %s\n",\libnet_geterror(l));/* Waiting 1 second between each packet */sleep(1);}libnet_destroy(l);return 0;}

    测试结果,本机(192.168.186.112)发到百度(220.181.38.251)。


    接下来的代码仅仅是为了用libnet_clear_packet(),在原则上是不允许的。

    * Example 8: reping_clear.c
    #include <stdio.h>#include <stdlib.h>#include <libnet.h>#include <stdint.h>#include <unistd.h>int main() {libnet_t *l; /* libnet context */char errbuf[LIBNET_ERRBUF_SIZE], ip_addr_str[16];u_int32_t ip_addr;u_int16_t id, seq;int i;l = libnet_init(LIBNET_RAW4, NULL, errbuf);if ( l == NULL ) {fprintf(stderr, "libnet_init() failed: %s\n", errbuf);exit(EXIT_FAILURE);}/* Generating a random id */libnet_seed_prand(l);id = (u_int16_t)libnet_get_prand(LIBNET_PR16);/* Getting destination IP address */printf("Destination IP address: ");scanf("%15s",ip_addr_str);ip_addr = libnet_name2addr4(l, ip_addr_str,\LIBNET_DONT_RESOLVE);if ( ip_addr == -1 ) {fprintf(stderr, "Error converting IP address.\n");libnet_destroy(l);exit(EXIT_FAILURE);}/* Writing 4 packets */seq = 1;for ( i = 0; i < 4; i++ ) {/* Building the ICMP header */if ( libnet_build_icmpv4_echo(ICMP_ECHO, 0, 0, id,\(seq + i), NULL, 0, l, 0) == -1 ) {fprintf(stderr, "Error building ICMP header: %s\n",\libnet_geterror(l));libnet_destroy(l);exit(EXIT_FAILURE);}/* Building the IP header */if ( libnet_autobuild_ipv4(LIBNET_IPV4_H + \LIBNET_ICMPV4_ECHO_H, IPPROTO_ICMP,\ip_addr, l) == -1 ) {fprintf(stderr, "Error building IP header: %s\n",\libnet_geterror(l));libnet_destroy(l);exit(EXIT_FAILURE);}if ( libnet_write(l) == -1 )fprintf(stderr, "Error writing packet: %s\n",\libnet_geterror(l));/* Clearing the packet *//* Comment this to see what happens when you rebuild* headers without calling libnet_clear_packet() */libnet_clear_packet(l);/* Waiting 1 second between each packet */sleep(1);}libnet_destroy(l);return 0;}

    当我们将87行注释掉后,即去掉libnet_clear_packet时,结果为


    我们可以看到包大小每次增大了28个字节。

    tcpdump显示的包大小是除去IP头部的大小。

    第一个包: ICMP_HEAD(8 Byte) + IP_HEAD(20 Byte)。

    后面每次造包都附加了在第一次造的包上,而且大小也相同。

    需要做网站?需要网络推广?欢迎咨询客户经理 13272073477