一.本文目的:

上一节中,我们已经在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字节 */
};

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 = ; i < ; i++)
printf("%02x:", str[i]);
printf("%02x", str[i]);
}

2.打印ip地址:

void Print_ip_addr(const unsigned char *str) /* 打印ip地址 */
{
int i;
for (i = ; i < ; 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, ); /* 填入目的地址 */
bcopy(ether_aton(hmac), arp_rt->eh.ether_shost, ); /* 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 = ;
arp_rt->arp.ea_hdr.ar_pln = ;
arp_rt->arp.ea_hdr.ar_op = htons(); /* ARP应答 */
;
bcopy(ether_aton(hmac), &arp_rt->arp.arp_sha, ); /* 发送端以太网地址*/
bcopy(arp->arp.arp_tpa, &arp_rt->arp.arp_spa, ); /* 发送端IP地址 */
bcopy(arp->arp.arp_sha, &arp_rt->arp.arp_tha, ); /* 目的以太网地址 */
bcopy(arp->arp.arp_spa, &arp_rt->arp.arp_tpa, ); /* 目的IP地址 */
}

六.源码实现:

其中:

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

本机ip地址:192.168.11.134

发送udp数据包程序:

 /*
============================================================================
Name : send_packet_01.c
Author : huh
Version :
Copyright : huh's copyright notice
Description : Hello World in C, Ansi-style
============================================================================
*/ #include <sys/types.h>
#include <sys/socket.h>
#include <stdio.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <netinet/ip_icmp.h>
#include <netinet/udp.h> #define MAXLINE 1024*10 int main()
{
int sockfd;
struct sockaddr_in server_addr;
//创建原始套接字
sockfd = socket(AF_INET, SOCK_DGRAM, );
//创建套接字地址
bzero(&server_addr,sizeof(server_addr));
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons();
server_addr.sin_addr.s_addr = inet_addr("192.168.11.134");
char sendline[]="adcde";
while()
{
sendto(sockfd, sendline, strlen(sendline), , (struct sockaddr *)&server_addr, sizeof(server_addr));
printf("%s\n",sendline);
sleep();
}
return ;
}

send_packet_01.c

接收数据包程序:

 /*
============================================================================
Name : recv_pkt.c
Author : huh
Version :
Copyright : huh's copyright notice
Description : Hello World in C, Ansi-style
============================================================================
*/ #include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <ifaddrs.h>
#include <net/ethernet.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <netinet/udp.h>
#include <netinet/ether.h>
#include <netinet/if_ether.h> #include <unistd.h> // sysconf()
#include <sys/poll.h>
#include <arpa/inet.h> #include "netmap_user.h" /* 来源与netmap */
#pragma pack(1) /* 设置结构体的边界对齐为1个字节 */ 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字节 */
}; struct arp_pkt /* 完整arp数据包(以太网首部 + ARP字段) */
{
struct ether_header eh; /* 以太网头部,14字节,<net/ethernet.h>头文件中 */
struct ether_arp arp; /* ARP字段 ,28字节,<netinet/if_ether.h>头文件中 */
}; void Print_mac_addr(const unsigned char *str) /* 打印mac地址 */
{
int i;
for (i = ; i < ; i++)
printf("%02x:", str[i]);
printf("%02x", str[i]);
} void Print_ip_addr(const unsigned char *str) /* 打印ip地址 */
{
int i;
for (i = ; i < ; i++)
printf("%d.", str[i]);
printf("%d", str[i]);
} 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");
} /*
* 根据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, ); /* 填入目的地址 */
bcopy(ether_aton(hmac), arp_rt->eh.ether_shost, ); /* 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 = ;
arp_rt->arp.ea_hdr.ar_pln = ;
arp_rt->arp.ea_hdr.ar_op = htons(); /* ARP应答 */
;
bcopy(ether_aton(hmac), &arp_rt->arp.arp_sha, ); /* 发送端以太网地址*/
bcopy(arp->arp.arp_tpa, &arp_rt->arp.arp_spa, ); /* 发送端IP地址 */
bcopy(arp->arp.arp_sha, &arp_rt->arp.arp_tha, ); /* 目的以太网地址 */
bcopy(arp->arp.arp_spa, &arp_rt->arp.arp_tpa, ); /* 目的IP地址 */
} int main()
{
struct ether_header *eh;
struct nm_pkthdr h;
struct nm_desc *nmr;
nmr = nm_open("netmap:eth0", NULL, , NULL); /* 打开netmap对应的文件描述符,并做了相关初始化操作! */
struct pollfd pfd;
pfd.fd = nmr->fd;
pfd.events = POLLIN;
u_char *str;
printf("ready!!!\n");
while ()
{
poll(&pfd, , -); /* 使用poll来监听是否有事件到来 */
if (pfd.revents & POLLIN)
{
str = nm_nextpkt(nmr, &h); /* 接收到来的数据包 */
eh = (struct ether_header *) str;
if (ntohs(eh->ether_type) == 0x0800) /* 接受的是普通IP包 */
{
struct udp_pkt *p = (struct udp_pkt *)str;
if(p->ip.protocol == IPPROTO_UDP) /*如果是udp协议的数据包*/
printf("udp:%s\n", p->body);
else
printf("其它IP协议包!\n");
}
else if (ntohs(eh->ether_type) == 0x0806) /* 接受的是ARP包 */
{
struct arp_pkt arp_rt;
struct arp_pkt *arp = (struct arp_pkt *)str;
if( *(uint32_t *)arp->arp.arp_tpa == inet_addr("192.168.11.134") ) /*如果请求的IP是本机IP地址(两边都是网络字节序)*/
{
printf("ARP请求:");
Print_arp_pkt(arp);
Init_echo_pkt(arp, &arp_rt, "00:0C:29:E4:D6:2A"); /*根据收到的ARP请求生成ARP应答数据包*/
printf("ARP应答:");
Print_arp_pkt(&arp_rt);
nm_inject(nmr, &arp_rt, sizeof(struct arp_pkt)); /* 发送ARP应答包 */
}
else
printf("其它主机的ARP请求!\n");
}
}
}
nm_close(nmr);
return ;
}

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

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

  1. 多队列网卡简介以及Linux通过网卡发送数据包源码解读

    http://blog.csdn.net/yanghua_kobe/article/details/7485254 首先我们看一下一个主流多队列网卡(E1000)跟多核CPU之间的关系图: 非多队列: ...

  2. C#开发BIMFACE系列45 服务端API之创建离线数据包

    BIMFACE二次开发系列目录     [已更新最新开发文章,点击查看详细] BIMFACE的常规应用方式有公有云与私有化部署两种方式,并且浏览模型或者图纸需要使用ViewToken,ViewToke ...

  3. 从网卡发送数据再谈TCP/IP协议—网络传输速度计算-网卡构造

    在<在深谈TCP/IP三步握手&四步挥手原理及衍生问题—长文解剖IP>里面提到 单个TCP包每次打包1448字节的数据进行发送(以太网Ethernet最大的数据帧是1518字节,以 ...

  4. Linux内核网络数据包处理流程

    Linux内核网络数据包处理流程 from kernel-4.9: 0. Linux内核网络数据包处理流程 - 网络硬件 网卡工作在物理层和数据链路层,主要由PHY/MAC芯片.Tx/Rx FIFO. ...

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

    wireshark:是一个网络封包分析软件.网络封包分析软件的功能是撷取网络封包,并尽可能显示出最为详细的网络封包资料.Wireshark使用WinPCAP作为接口,直接与网卡进行数据报文交换,是目前 ...

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

    想利用手头上的DE2-115 写一个关于以太网通信的驱动,经过了这么多天的实验调试终于有了一些认识. 1.我在观察网卡发送数据与接收数据的过程中发现,我从fpga上的一个网卡发送数据,然后另一个网卡接 ...

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

    [版权声明:转载请保留出处:blog.csdn.net/gentleliu.邮箱:shallnew*163.com] 每一个cpu都有队列来处理接收到的帧,都有其数据结构来处理入口和出口流量,因此,不 ...

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

    与网络数据包的发送不同,网络收包是异步的的.由于你不确定谁会在什么时候突然发一个网络包给你.因此这个网络收包逻辑事实上包括两件事:1.数据包到来后的通知2.收到通知并从数据包中获取数据这两件事发生在协 ...

  9. 多CPU下基于e1000e驱动的数据包以及网卡中断流程分析.doc

    http://wenku.baidu.com/link?url=mMKDH_fKmUXN7L6rANIFHjoHdKCYBLlDrqoYB1daDTEkNFk9Bt9xlJtS_4BKBj6w22WD ...

随机推荐

  1. 习题:codevs 1519 过路费 解题报告

    今天拿了这道题目练练手,感觉自己代码能力又增强了不少: 我的思路跟别人可能不一样. 首先我们很容易就能看出,我们需要的边就是最小生成树算法kruskal算法求出来的边,其余的边都可以删掉,于是就有了这 ...

  2. python 实时遍历日志文件

    首先尝试使用 python open 遍历一个大日志文件, 使用 readlines() 还是 readline() ? 总体上 readlines() 不慢于python 一次次调用 readlin ...

  3. 高性能 Windows Socket 组件 HP-Socket v3.0.2 正式发布

    HP-Socket 是一套通用的高性能 Windows Socket 组件包,包含服务端组件(IOCP 模型)和客户端组件(Event Select 模型),广泛适用于 Windows 平台的 TCP ...

  4. SQL SERVER 2008 R2 SP1更新时,遇上共享功能更新失败解决方案

    SQL SERVER 2008 R2 SP1更新时,遇上共享功能更新失败的问题,可作如下尝试: 更新失败后,在windows的[事件查看器→应用程序]中找到来源为MsiInstaller,事件ID为1 ...

  5. canvas滤镜-刮刮乐

    <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title> ...

  6. 以【猫叫、老鼠跑、主人醒】为例子,使用 javascript 来实现 观察者模式 (有在线演示)

    “猫叫.老鼠跑.主人醒”是一个很古老的话题了,大家也都有各自的想法和解决方案.我也是看了很多,一开始的时候是相当的迷糊,这个怎么就是面试题了?考的是啥呀,和编程有关系吗?又是猫又是老鼠的,晕死了.后来 ...

  7. Ajax+PHP+MySQL 登陆示例

    PHP是一门很好的语言,可以很方便的开发web应用程序,下面介绍一下PHP如何通过AJAX方式实现登录功能: 1 login.php 登录界面中,javascript脚本用ajax方式异步请求dolo ...

  8. CSS3盒模型温故

    CSS有一种基础设计模式叫盒模型,定义了Web页面中的元素是如何看做盒子来解析的.每一个盒子有不同的展示界面,下面就来介绍盒模型,主要有一下几种盒模型:inline.inline-block.bloc ...

  9. sublime安装

    1.sublime下载安装 下载地址:http://www.sublimetext.com/3 选择合适版本下载 2.自定义快捷键 Ctrl+Shift+P调出命令面板 3.自定义设置 Ctrl+Sh ...

  10. HTML基础知识总结

    经过这段时间的学习,对于html的一些基础知识有了一定的了解.所谓好记性不如烂笔头,唯有一点点累积,才能汇聚成知识的海洋.现在,我对这段时间的学习做一个总结. 一.HTML的定义 HTML,超文本标记 ...