010 使用netmap函数接管网卡,接收数据包,回应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("\n");
}

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\n",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("\n");
 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!!!\n");
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\n", p->body);
124                 else
125                     printf("其它IP协议包!\n");
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请求!\n");
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
时间: 2024-10-10 17:17:40

010 使用netmap函数接管网卡,接收数据包,回应ARP请求的相关文章

发送和接收数据包

发送和接收数据包 原文:Game Networking系列,作者是Glenn Fiedler,专注于游戏网络编程相关工作多年. 概述 在之前的网游中的网络编程系列1:UDP vs. TCP中(推荐先看前面那篇),我们经过讨论得出:网游中传输数据应该使用UDP而不是TCP.我们选择UDP是为了不需要等待重发数据包,从而达到数据的实时性. 注意,因为接下来英文原文中所有的代码是C++写的,而我是个pythoner,我的计划是:通过理解文章,我用python实现UDP收发数据包.虚拟连接(原文后两章的

发送自修改数据包进行arp欺骗

一丶使用平台 对Microsoft Visual Studio 2010编译平台进行前期操作 项目-->**属性(alt+F7)     配置属性-->清单工具-->输入和输出-->嵌入清单-->否     配置属性-->C/C++-->常规-->附加包含目录-->     配置属性-->链接器-->常规-->附加库目录-->     配置属性-->链接器-->输入-->附加依赖项-->补       充

CORE网络数据包接收传递过程分析

能够接收实际网络流量是CORE的一个显著优点,这使得已有的系统能方便地接入虚拟网络进行模拟.CORE对网络设备的虚拟是通过LXC技术来实现的,而对网络的虚拟则是通过虚拟网卡(veth).网桥(Bridge).Quagga来实现的.本文档主要通过分析CORE中网络数据传递过程,来理解CORE网络模拟. 拓扑结构 为了方便描述,以如图1所示拓扑结构为例子,分析数据流从网卡eth0到虚拟节点n2的过程. 图1 示例拓扑 虚拟网络创建由CORE后台根据前台的拓扑结构和配置,执行相应的命令进行实现,如下:

SDN Overlay 网络中虚机数据包的转发(2)

在配置了网络虚拟化(Overlay)的网络结构中,处于Overlay网络中的虚机数据包的封装和MAC地址学习和传统物理网络(Underlay)相似又不尽相同.除了我们了解Overlay网络需要借助Underlay网络进行二次封装之外,其MAC地址学习过程也相对要曲折一些.这些MAC地址学习过程取决于多种因素: 虚机是否在同一虚拟子网? 虚机是否在同一虚机网络的不同虚拟子网? 虚机是否运行于同一台物理机? 虚机是否运行在不同的物理机? 不同的场景,虚机之间学习对方的MAC地址,以及在互相学习到对方

从零开始学安全(四十二)●利用Wireshark分析ARP协议数据包

wireshark:是一个网络封包分析软件.网络封包分析软件的功能是撷取网络封包,并尽可能显示出最为详细的网络封包资料.Wireshark使用WinPCAP作为接口,直接与网卡进行数据报文交换,是目前全世界最广泛的网络封包分析软件 什么是ARP协议    协议分析篇第一个要研究的就是ARP协议.ARP(Address Resolution Protocol,地址解析协议)用于将IP地址解析为物理地址(MAC地址).这里之所以需要使用MAC地址,是因为网络中用于连接各个设备的交换机使用了内容可寻址

linux内核数据包转发流程(三)网卡帧接收分析

[版权声明:转载请保留出处:blog.csdn.net/gentleliu.邮箱:shallnew*163.com] 每个cpu都有队列来处理接收到的帧,都有其数据结构来处理入口和出口流量,因此,不同cpu之间没有必要使用上锁机制,.此队列数据结构为softnet_data(定义在include/linux/netdevice.h中): /* * Incoming packets are placed on per-cpu queues so that * no locking is neede

DE2-115 以太网通信之一88E1111网卡接收PC数据

想利用手头上的DE2-115 写一个关于以太网通信的驱动,经过了这么多天的实验调试终于有了一些认识. 1.我在观察网卡发送数据与接收数据的过程中发现,我从fpga上的一个网卡发送数据,然后另一个网卡接收数据,接收到的数据前面会有55h这8bit的数据.我从PC上发送数据,用fpga上的网卡接收数据,那么在接收到的数据前面会有55h,55h,55h,55h,55h,55h,55h,5dh这64bit的数据.那么如果55h这8bit数据是PHY发送时自动添加那么从PC上接收到的最后应该是55而不应该

Linux内核中网络数据包的接收-第一部分 概念和框架

与网络数据包的发送不同,网络收包是异步的的,因为你不确定谁会在什么时候突然发一个网络包给你,因此这个网络收包逻辑其实包含两件事:1.数据包到来后的通知2.收到通知并从数据包中获取数据这两件事发生在协议栈的两端,即网卡/协议栈边界以及协议栈/应用边界:网卡/协议栈边界:网卡通知数据包到来,中断协议栈收包:协议栈栈/应用边界:协议栈将数据包填充socket队列,通知应用程序有数据可读,应用程序负责接收数据.本文就来介绍一下关于这两个边界的这两件事是怎么一个细节,关乎网卡中断,NAPI,网卡poll,

linux 内核网络数据包接收流程

转:https://segmentfault.com/a/1190000008836467 本文将介绍在Linux系统中,数据包是如何一步一步从网卡传到进程手中的. 如果英文没有问题,强烈建议阅读后面参考里的两篇文章,里面介绍的更详细. 本文只讨论以太网的物理网卡,不涉及虚拟设备,并且以一个UDP包的接收过程作为示例. 本示例里列出的函数调用关系来自于kernel 3.13.0,如果你的内核不是这个版本,函数名称和相关路径可能不一样,但背后的原理应该是一样的(或者有细微差别) 网卡到内存 网卡需