• 010 使用netmap API接管网卡,接收数据包,回应ARP请求


    一.本文目的:

        上一节中,我们已经在CentOS 6.7 上安装好了netmap,也能接收和发送包了,这节我们来调用netmap中的API,接管网卡,对网卡上收到的数据包做分析,并回应ARP请求。

      

    二.netmap API简要介绍:

        1.netmap API 主要包含在两个头文件中:netmap.h和netmap_user.h。在netmap/sys/net/目录下,其中netmap_user.h调用netmap.h。

        2.netmap API一共七八个函数调用:nm_open()生成文件描述符,并做一系列初始化操作。nm_mmap()被nm_open()调用,申请存放数据包的内存池,并做相应初始化。

        3.nm_nexkpkt()函数负责取出内存池的数据包,nm_inject()函数往内存池中写入数据包,发送到网卡。

        4.nm_close()函数关闭先前的有关操作,并做相应清理。

    (本文的目的是对ARP数据报的接收发送分析,所以对netmap API先只是简单介绍。)

    三.具体内容:

        1.实验中主机为centos 6.7,虚拟机也为centos6.7.所以就直接用主机给虚拟机发udp数据包了。(单纯的网络环境,没有其它主机的干扰!)

        2.当调用了netmap API的程序运行时,会接管网卡,网卡上的数据都要通过netmap API中的方法得到(包括发送)。

        3.当我们拿到数据包的时候,是一个含以太网首部的完整数据包,我们需要查看数据包的结构,确认是发给自己的。

        4.实验发现,当网卡被接管后,相应的ARP功能没有了,所以我们需要手动实现一个ARP reply程序。

    四.数据结构的定义:

    实验过程中,会收到ARP请求和UDP数据包,我们主要对这两者进行分析:

    1.完整的以太网udp数据包结构

    结构体定义: 

    struct udp_pkt  /* 普通的完整udp数据包 */
    {
        struct ether_header eh;       /* 以太网头部,14字节,<net/ethernet.h>头文件中 */
        struct iphdr ip;              /* ip部分,20字节,<netinet/ip.h>头文件中 */
        struct udphdr udp;            /* udp部分,8字节,<netinet/udp.h>头文件中 */
        uint8_t body[20];             /* 数据部分,暂时分配20字节 */
    };

      

    2.完整的以太网ARP数据包结构

    ARP数据报结构图:

     结构体定义:

    struct arp_pkt /* 完整arp数据包(以太网首部 + ARP字段) */
    {
        struct ether_header eh;        /* 以太网头部,14字节,<net/ethernet.h>头文件中 */
        struct ether_arp arp;          /* ARP字段  ,28字节,<netinet/if_ether.h>头文件中 */
    };

      

    五.相关的函数封装(以后使用)

    1.打印mac地址函数:

    void Print_mac_addr(const unsigned char *str) /* 打印mac地址 */
    {
        int i;
        for (i = 0; i < 5; i++)
            printf("%02x:", str[i]);
        printf("%02x", str[i]);
    }

    2.打印ip地址:

    void Print_ip_addr(const unsigned char *str) /* 打印ip地址 */
    {
        int i;
        for (i = 0; i < 3; i++)
            printf("%d.", str[i]);
        printf("%d", str[i]);
    }

    3.打印完整的arp数据包的内容 

    void Print_arp_pkt(struct arp_pkt* arp_pkt) /* 打印完整的以太网arp数据包的内容 */
    {
        Print_mac_addr(arp_pkt->eh.ether_dhost), printf(" "); /* 以太网目的地址 */
        Print_mac_addr(arp_pkt->eh.ether_shost), printf(" "); /* 以太网源地址 */
        printf("0x%04x ", ntohs(arp_pkt->eh.ether_type));     /* 帧类型:0x0806 */
        printf("  ");
        printf("%d ",     ntohs(arp_pkt->arp.ea_hdr.ar_hrd));         /* 硬件类型:1 */
        printf("0x%04x ", ntohs(arp_pkt->arp.ea_hdr.ar_pro));         /* 协议类型:0x0800 */
        printf("%d ",arp_pkt->arp.ea_hdr.ar_hln);      /* 硬件地址:6 */
        printf("%d ",arp_pkt->arp.ea_hdr.ar_pln);      /* 协议地址长度:4 */
        printf("%d ",     ntohs(arp_pkt->arp.ea_hdr.ar_op));  /* 操作字段:ARP请求值为1,ARP应答值为2 */
        printf("  ");
        Print_mac_addr(arp_pkt->arp.arp_sha), printf(" ");    /* 发送端以太网地址*/
        Print_ip_addr(arp_pkt->arp.arp_spa), printf(" ");     /* 发送端IP地址 */
        Print_mac_addr(arp_pkt->arp.arp_tha), printf(" ");    /* 目的以太网地址 */
        Print_ip_addr(arp_pkt->arp.arp_tpa), printf(" ");     /* 目的IP地址 */
        printf("
    ");
    }

    4.根据ARP request生成ARP reply的packet

    /*
     * 根据ARP request生成ARP reply的packet
     * hmac为本机mac地址
     */
    void Init_echo_pkt(struct arp_pkt *arp, struct arp_pkt *arp_rt, char *hmac)
    {
        bcopy(arp->eh.ether_shost,     arp_rt->eh.ether_dhost, 6);   /* 填入目的地址 */
        bcopy(ether_aton(hmac),     arp_rt->eh.ether_shost, 6);   /* hmac为本机mac地址 */
        arp_rt->eh.ether_type =     arp->eh.ether_type;           /* 以太网帧类型 */
        ;
        arp_rt->arp.ea_hdr.ar_hrd = arp->arp.ea_hdr.ar_hrd;
        arp_rt->arp.ea_hdr.ar_pro = arp->arp.ea_hdr.ar_pro;
        arp_rt->arp.ea_hdr.ar_hln = 6;
        arp_rt->arp.ea_hdr.ar_pln = 4;
        arp_rt->arp.ea_hdr.ar_op = htons(2);                     /* ARP应答 */
        ;
        bcopy(ether_aton(hmac), &arp_rt->arp.arp_sha, 6);        /* 发送端以太网地址*/
        bcopy(arp->arp.arp_tpa, &arp_rt->arp.arp_spa, 4);        /* 发送端IP地址 */
        bcopy(arp->arp.arp_sha, &arp_rt->arp.arp_tha, 6);        /* 目的以太网地址 */
        bcopy(arp->arp.arp_spa, &arp_rt->arp.arp_tpa, 4);        /* 目的IP地址 */
    }

    六.源码实现:

    其中:

        本机mac地址:00:0C:29:E4:D6:2A

        本机ip地址:192.168.11.134

    发送udp数据包程序:

     1 /*
     2  ============================================================================
     3  Name        : send_packet_01.c
     4  Author      : huh
     5  Version     :
     6  Copyright   : huh's copyright notice
     7  Description : Hello World in C, Ansi-style
     8  ============================================================================
     9  */
    10 
    11 #include <sys/types.h>
    12 #include <sys/socket.h>
    13 #include <stdio.h>
    14 #include <netinet/in.h>
    15 #include <arpa/inet.h>
    16 #include <unistd.h>
    17 #include <stdlib.h>
    18 #include <string.h>
    19 #include <netinet/ip_icmp.h>
    20 #include <netinet/udp.h>
    21 
    22 #define MAXLINE 1024*10
    23 
    24 int main()
    25 {
    26     int sockfd;
    27     struct sockaddr_in server_addr;
    28     //创建原始套接字
    29     sockfd = socket(AF_INET, SOCK_DGRAM, 0);
    30     //创建套接字地址
    31     bzero(&server_addr,sizeof(server_addr));
    32     server_addr.sin_family = AF_INET;
    33     server_addr.sin_port = htons(8686);
    34     server_addr.sin_addr.s_addr = inet_addr("192.168.11.134");
    35     char sendline[10]="adcde";
    36     while(1)
    37     {
    38         sendto(sockfd, sendline, strlen(sendline), 0, (struct sockaddr *)&server_addr, sizeof(server_addr));
    39         printf("%s
    ",sendline);
    40         sleep(1);
    41     }
    42     return 0;
    43 }
    send_packet_01.c

    接收数据包程序:

      1 /*
      2  ============================================================================
      3  Name        : recv_pkt.c
      4  Author      : huh
      5  Version     :
      6  Copyright   : huh's copyright notice
      7  Description : Hello World in C, Ansi-style
      8  ============================================================================
      9  */
     10 
     11 #include <stdio.h>
     12 #include <unistd.h>
     13 #include <stdlib.h>
     14 #include <string.h>
     15 #include <ifaddrs.h>
     16 #include <net/ethernet.h>
     17 #include <netinet/in.h>
     18 #include <netinet/ip.h>
     19 #include <netinet/udp.h>
     20 #include <netinet/ether.h>
     21 #include <netinet/if_ether.h>
     22 
     23 #include <unistd.h>    // sysconf()
     24 #include <sys/poll.h>
     25 #include <arpa/inet.h>
     26 
     27 #include "netmap_user.h" /* 来源与netmap */
     28 #pragma pack(1) /* 设置结构体的边界对齐为1个字节 */
     29 
     30 struct udp_pkt  /* 普通的完整udp数据包 */
     31 {
     32     struct ether_header eh;      /* 以太网头部,14字节,<net/ethernet.h>头文件中 */
     33     struct iphdr ip;              /* ip部分,20字节,<netinet/ip.h>头文件中 */
     34     struct udphdr udp;             /* udp部分,8字节,<netinet/udp.h>头文件中 */
     35     uint8_t body[20];            /* 数据部分,暂时分配20字节 */
     36 };
     37 
     38 struct arp_pkt /* 完整arp数据包(以太网首部 + ARP字段) */
     39 {
     40     struct ether_header eh;      /* 以太网头部,14字节,<net/ethernet.h>头文件中 */
     41     struct ether_arp arp;          /* ARP字段  ,28字节,<netinet/if_ether.h>头文件中 */
     42 };
     43 
     44 void Print_mac_addr(const unsigned char *str) /* 打印mac地址 */
     45 {
     46     int i;
     47     for (i = 0; i < 5; i++)
     48         printf("%02x:", str[i]);
     49     printf("%02x", str[i]);
     50 }
     51 
     52 void Print_ip_addr(const unsigned char *str) /* 打印ip地址 */
     53 {
     54     int i;
     55     for (i = 0; i < 3; i++)
     56         printf("%d.", str[i]);
     57     printf("%d", str[i]);
     58 }
     59 
     60 void Print_arp_pkt(struct arp_pkt* arp_pkt) /* 打印完整的arp数据包的内容 */
     61 {
     62     Print_mac_addr(arp_pkt->eh.ether_dhost), printf(" "); /* 以太网目的地址 */
     63     Print_mac_addr(arp_pkt->eh.ether_shost), printf(" "); /* 以太网源地址 */
     64     printf("0x%04x ", ntohs(arp_pkt->eh.ether_type));     /* 帧类型:0x0806 */
     65     printf("  ");
     66     printf("%d ",     ntohs(arp_pkt->arp.ea_hdr.ar_hrd));         /* 硬件类型:1 */
     67     printf("0x%04x ", ntohs(arp_pkt->arp.ea_hdr.ar_pro));         /* 协议类型:0x0800 */
     68     printf("%d ",arp_pkt->arp.ea_hdr.ar_hln);      /* 硬件地址:6 */
     69     printf("%d ",arp_pkt->arp.ea_hdr.ar_pln);      /* 协议地址长度:4 */
     70     printf("%d ",     ntohs(arp_pkt->arp.ea_hdr.ar_op));  /* 操作字段:ARP请求值为1,ARP应答值为2 */
     71     printf("  ");
     72     Print_mac_addr(arp_pkt->arp.arp_sha), printf(" ");    /* 发送端以太网地址*/
     73     Print_ip_addr(arp_pkt->arp.arp_spa), printf(" ");     /* 发送端IP地址 */
     74     Print_mac_addr(arp_pkt->arp.arp_tha), printf(" ");    /* 目的以太网地址 */
     75     Print_ip_addr(arp_pkt->arp.arp_tpa), printf(" ");     /* 目的IP地址 */
     76     printf("
    ");
     77 }
     78 
     79 /*
     80  * 根据ARP request生成ARP reply的packet
     81  * hmac为本机mac地址
     82  */
     83 void Init_echo_pkt(struct arp_pkt *arp, struct arp_pkt *arp_rt, char *hmac)
     84 {
     85     bcopy(arp->eh.ether_shost,     arp_rt->eh.ether_dhost, 6);   /* 填入目的地址 */
     86     bcopy(ether_aton(hmac),     arp_rt->eh.ether_shost, 6);   /* hmac为本机mac地址 */
     87     arp_rt->eh.ether_type =     arp->eh.ether_type;           /* 以太网帧类型 */
     88     ;
     89     arp_rt->arp.ea_hdr.ar_hrd = arp->arp.ea_hdr.ar_hrd;
     90     arp_rt->arp.ea_hdr.ar_pro = arp->arp.ea_hdr.ar_pro;
     91     arp_rt->arp.ea_hdr.ar_hln = 6;
     92     arp_rt->arp.ea_hdr.ar_pln = 4;
     93     arp_rt->arp.ea_hdr.ar_op = htons(2);                     /* ARP应答 */
     94     ;
     95     bcopy(ether_aton(hmac), &arp_rt->arp.arp_sha, 6);        /* 发送端以太网地址*/
     96     bcopy(arp->arp.arp_tpa, &arp_rt->arp.arp_spa, 4);        /* 发送端IP地址 */
     97     bcopy(arp->arp.arp_sha, &arp_rt->arp.arp_tha, 6);        /* 目的以太网地址 */
     98     bcopy(arp->arp.arp_spa, &arp_rt->arp.arp_tpa, 4);        /* 目的IP地址 */
     99 }
    100 
    101 int main()
    102 {
    103     struct ether_header *eh;
    104     struct nm_pkthdr h;
    105     struct nm_desc *nmr;
    106     nmr = nm_open("netmap:eth0", NULL, 0, NULL);  /* 打开netmap对应的文件描述符,并做了相关初始化操作! */
    107     struct pollfd pfd;
    108     pfd.fd = nmr->fd;
    109     pfd.events = POLLIN;
    110     u_char *str;
    111     printf("ready!!!
    ");
    112     while (1)
    113     {
    114         poll(&pfd, 1, -1); /* 使用poll来监听是否有事件到来 */
    115         if (pfd.revents & POLLIN)
    116         {
    117             str = nm_nextpkt(nmr, &h);                     /* 接收到来的数据包 */
    118             eh = (struct ether_header *) str;
    119             if (ntohs(eh->ether_type) == 0x0800)                     /* 接受的是普通IP包 */
    120             {
    121                 struct udp_pkt *p = (struct udp_pkt *)str;
    122                 if(p->ip.protocol == IPPROTO_UDP)   /*如果是udp协议的数据包*/
    123                     printf("udp:%s
    ", p->body);
    124                 else
    125                     printf("其它IP协议包!
    ");
    126             }
    127             else if (ntohs(eh->ether_type) == 0x0806)                  /* 接受的是ARP包 */
    128             {
    129                 struct arp_pkt arp_rt;
    130                 struct arp_pkt *arp = (struct arp_pkt *)str;
    131                 if( *(uint32_t *)arp->arp.arp_tpa == inet_addr("192.168.11.134") ) /*如果请求的IP是本机IP地址(两边都是网络字节序)*/
    132                 {
    133                     printf("ARP请求:");
    134                     Print_arp_pkt(arp);
    135                     Init_echo_pkt(arp, &arp_rt, "00:0C:29:E4:D6:2A");     /*根据收到的ARP请求生成ARP应答数据包*/
    136                     printf("ARP应答:");
    137                     Print_arp_pkt(&arp_rt);
    138                     nm_inject(nmr, &arp_rt, sizeof(struct arp_pkt));      /* 发送ARP应答包 */
    139                 }
    140                 else
    141                     printf("其它主机的ARP请求!
    ");
    142             }
    143         }
    144     }
    145     nm_close(nmr);
    146     return 0;
    147 }
    recv_pkt.c

    七.运行结果: 

    ready!!!
    udp:adcde
    udp:adcde
    udp:adcde
    udp:adcde
    udp:adcde
    udp:adcde
    udp:adcde
    udp:adcde
    udp:adcde
    udp:adcde
    ARP请求:00:0c:29:e4:d6:2a 00:50:56:c0:00:08 0x0806   1 0x0800 6 4 1   00:50:56:c0:00:08 192.168.11.1 00:00:00:00:00:00 192.168.11.134 
    ARP应答:00:50:56:c0:00:08 00:0c:29:e4:d6:2a 0x0806   1 0x0800 6 4 2   00:0c:29:e4:d6:2a 192.168.11.134 00:50:56:c0:00:08 192.168.11.1 
    udp:adcde
    udp:adcde
    udp:adcde
  • 相关阅读:
    Apache POI使用详解
    util.Date与sql.Date转换
    【转】javascript中not defined、undefined、null以及NaN的区别
    SELECT INTO 和 INSERT INTO SELECT 两种表复制语句
    DC维护工具Repadmin
    Outlook 配置exchange 缓慢,无法通过
    Windows 服务器站点设置
    Exchange 挂载点权限问题
    Windwos 查看WWN Number
    Exchange 2016 多站点创建oab shadow 副本
  • 原文地址:https://www.cnblogs.com/ruo-yu/p/5084270.html
Copyright © 2020-2023  润新知