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

时间:2023-03-09 02:33:35
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字节 */
};

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

ARP数据报结构图:

010 使用netmap API接管网卡,接收数据包,回应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