上一篇文章我们讲了一个异步的线程池大概需要如何去实现,现在的话,我们如何来解析一个UDP的包。

环境的搭配

这个环境的问题困扰了很久,这个netmap已经不再更新了,支持Ubuntu16.04-Ubuntu18.04的系统内核,我们需要按照这样的步骤去安装我们的netmap

建立Ubuntu虚拟机的过程,我们就略过了。

切换到su用户下,先去安装我们的编译的环境

apt-get install build-essential

安装完我们的编译工具之后,就需要看看我们的环境是否已经安装git工具。

git --version

没有安装,就需要安装一下我们需要的git工具。

apt-get install git

安装成功以后,我们开始拉取netmap的代码

git clone git://github.com/luigirizzo/netmap.git

拉取代码这个过程可能会遇到一个问题,就是443连接失败的问题,这个问题可以通过魔法上网解决,或者是多尝试几次就好了。

等待拉取代码成功以后,我们来进行netmap的编译和安装。

cd netmap
./configure

这里我们可能会遇到一个问题

Cannot find full kernel sources.
***
*** Please note that most distributions provide separate packages for kernel
*** headers and sources. Once you know the path to kernel sources, you can
*** rerun this script with the additional
***
*** --kernel-sources=/path/to/sources

当我们遇到这个问题的时候,就需要下载一下我们的内核头文件。

apt-get install -y linux-headers-$(uname -r)

拉取内核头文件成功以后,就可以进行netmap的编译了。

./configure
make & make install
insmod netmap.ko

等待运行完毕,这样就完成了netmap的环境搭配。

udp协议解析的代码编写

看一下一个UDP的数据帧是什么样的?

定义我们需要的结构体

以太网头

#define ETH_ALEN 6
struct ethhr{
unsigned char h_dest[ETH_ALEN];
unsigned char h_source[ETH_ALEN];
unsigned short h_proto;
}

ip头

struct iphdr {
unsigned char version : 4,
hdrlen : 4;
unsigned char tos;
unsigned short tot_len;
unsigned short id;
unsigned short flag : 3,
flag_off : 13;
unsigned char ttl;
unsigned char protocol;
unsigned short check;
unsigned int saddr;
unsigned int daddr;
};

UDP协议

struct udphdr {
unsigned short source;
unsigned short dest;
unsigned short len;
unsigned short check;
};

最后拼接成UDP协议

struct udppkt {
struct ethhdr eh;
struct iphdr ip;
struct udphdr udp;
unsigned char body[0];
};

arp协议


struct arphdr {
unsigned short h_type;
unsigned short h_proto;
unsigned char h_addrlen;
unsigned char protolen;
unsigned short oper;
unsigned char smac[ETH_ALEN];
unsigned int sip;
unsigned char dmac[ETH_ALEN];
unsigned int dip;
};

arp包

struct arppkt {
struct ethhdr eh;
struct arphdr arp;
};

icmp协议


struct icmphdr {
unsigned char type;
unsigned char code;
unsigned short check;
unsigned short identifier;
unsigned short seq;
unsigned char data[32];
};

icmp协议包

struct icmppkt {
struct ethhdr eh;
struct iphdr ip;
struct icmphdr icmp;
};

编写我们解析包的代码

这样定义完我们需要的结构体,就可以编写我们的代码了

最终代码如下:



#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h> #include <sys/poll.h>
#include <arpa/inet.h> #define NETMAP_WITH_LIBS #include <net/netmap_user.h>
#pragma pack(1) #define ETH_ALEN 6
#define PROTO_IP 0x0800
#define PROTO_ARP 0x0806 #define PROTO_UDP 17
#define PROTO_ICMP 1
#define PROTO_IGMP 2 struct ethhdr {
unsigned char h_dest[ETH_ALEN];
unsigned char h_source[ETH_ALEN];
unsigned short h_proto;
}; struct iphdr {
unsigned char version:4,
hdrlen:4;
unsigned char tos;
unsigned short tot_len;
unsigned short id;
unsigned short flag:3,
flag_off:13;
unsigned char ttl;
unsigned char protocol;
unsigned short check;
unsigned int saddr;
unsigned int daddr;
}; struct udphdr {
unsigned short source;
unsigned short dest;
unsigned short len;
unsigned short check;
}; struct udppkt {
struct ethhdr eh;
struct iphdr ip;
struct udphdr udp;
unsigned char body[0];
}; struct arphdr {
unsigned short h_type;
unsigned short h_proto;
unsigned char h_addrlen;
unsigned char protolen;
unsigned short oper;
unsigned char smac[ETH_ALEN];
unsigned int sip;
unsigned char dmac[ETH_ALEN];
unsigned int dip;
}; struct arppkt {
struct ethhdr eh;
struct arphdr arp;
}; struct icmphdr {
unsigned char type;
unsigned char code;
unsigned short check;
unsigned short identifier;
unsigned short seq;
unsigned char data[32];
}; struct icmppkt {
struct ethhdr eh;
struct iphdr ip;
struct icmphdr icmp;
}; void print_mac(unsigned char *mac) {
int i = 0;
for (i = 0;i < ETH_ALEN-1;i ++) {
printf("%02x:", mac[i]);
}
printf("%02x", mac[i]);
} void print_ip(unsigned char *ip) {
int i = 0; for (i = 0;i < 3;i ++) {
printf("%d.", ip[i]);
}
printf("%d", ip[i]);
} void print_arp(struct arppkt *arp) {
print_mac(arp->eh.h_dest);
printf(" "); print_mac(arp->eh.h_source);
printf(" "); printf("0x%04x ", ntohs(arp->eh.h_proto));
printf(" "); } int str2mac(char *mac, char *str) { char *p = str;
unsigned char value = 0x0;
int i = 0; while (p != '\0') { if (*p == ':') {
mac[i++] = value;
value = 0x0;
} else { unsigned char temp = *p;
if (temp <= '9' && temp >= '0') {
temp -= '0';
} else if (temp <= 'f' && temp >= 'a') {
temp -= 'a';
temp += 10;
} else if (temp <= 'F' && temp >= 'A') {
temp -= 'A';
temp += 10;
} else {
break;
}
value <<= 4;
value |= temp;
}
p ++;
} mac[i] = value; return 0;
} void echo_arp_pkt(struct arppkt *arp, struct arppkt *arp_rt, char *hmac) { memcpy(arp_rt, arp, sizeof(struct arppkt)); memcpy(arp_rt->eh.h_dest, arp->eh.h_source, ETH_ALEN);
str2mac(arp_rt->eh.h_source, hmac);
arp_rt->eh.h_proto = arp->eh.h_proto; arp_rt->arp.h_addrlen = 6;
arp_rt->arp.protolen = 4;
arp_rt->arp.oper = htons(2); str2mac(arp_rt->arp.smac, hmac);
arp_rt->arp.sip = arp->arp.dip; memcpy(arp_rt->arp.dmac, arp->arp.smac, ETH_ALEN);
arp_rt->arp.dip = arp->arp.sip; } void echo_udp_pkt(struct udppkt *udp, struct udppkt *udp_rt) { memcpy(udp_rt, udp, sizeof(struct udppkt)); memcpy(udp_rt->eh.h_dest, udp->eh.h_source, ETH_ALEN);
memcpy(udp_rt->eh.h_source, udp->eh.h_dest, ETH_ALEN); udp_rt->ip.saddr = udp->ip.daddr;
udp_rt->ip.daddr = udp->ip.saddr; udp_rt->udp.source = udp->udp.dest;
udp_rt->udp.dest = udp->udp.source; } unsigned short in_cksum(unsigned short *addr, int len)
{
register int nleft = len;
register unsigned short *w = addr;
register int sum = 0;
unsigned short answer = 0; while (nleft > 1) {
sum += *w++;
nleft -= 2;
} if (nleft == 1) {
*(u_char *)(&answer) = *(u_char *)w ;
sum += answer;
} sum = (sum >> 16) + (sum & 0xffff);
sum += (sum >> 16);
answer = ~sum; return (answer); } void echo_icmp_pkt(struct icmppkt *icmp, struct icmppkt *icmp_rt) { memcpy(icmp_rt, icmp, sizeof(struct icmppkt)); icmp_rt->icmp.type = 0x0; //
icmp_rt->icmp.code = 0x0; //
icmp_rt->icmp.check = 0x0; icmp_rt->ip.saddr = icmp->ip.daddr;
icmp_rt->ip.daddr = icmp->ip.saddr; memcpy(icmp_rt->eh.h_dest, icmp->eh.h_source, ETH_ALEN);
memcpy(icmp_rt->eh.h_source, icmp->eh.h_dest, ETH_ALEN); icmp_rt->icmp.check = in_cksum((unsigned short*)&icmp_rt->icmp, sizeof(struct icmphdr)); } int main() { struct ethhdr *eh;
struct pollfd pfd = {0};
struct nm_pkthdr h;
unsigned char *stream = NULL; struct nm_desc *nmr = nm_open("netmap:eth0", NULL, 0, NULL);
if (nmr == NULL) {
return -1;
} pfd.fd = nmr->fd;
pfd.events = POLLIN; while (1) {
int ret = poll(&pfd, 1, -1);
if (ret < 0) continue; if (pfd.revents & POLLIN) {
//获取收到的包
stream = nm_nextpkt(nmr, &h);
//强转为以太网头
eh = (struct ethhdr*)stream;
//如果是IP协议
if (ntohs(eh->h_proto) == PROTO_IP) {
//强转为udp的包
struct udppkt *udp = (struct udppkt*)stream;
//判断是否为UDP协议
if (udp->ip.protocol == PROTO_UDP) { struct in_addr addr;
addr.s_addr = udp->ip.saddr; int udp_length = ntohs(udp->udp.len);
printf("%s:%d:length:%d, ip_len:%d --> ", inet_ntoa(addr), udp->udp.source,
udp_length, ntohs(udp->ip.tot_len)); udp->body[udp_length-8] = '\0';
printf("udp --> %s\n", udp->body);
#if 1
struct udppkt udp_rt;
echo_udp_pkt(udp, &udp_rt);
nm_inject(nmr, &udp_rt, sizeof(struct udppkt));
#endif //判断是否为ICMP的包
} else if (udp->ip.protocol == PROTO_ICMP) {
//强转为 imcp的包
struct icmppkt *icmp = (struct icmppkt*)stream; printf("icmp ---------- --> %d, %x\n", icmp->icmp.type, icmp->icmp.check);
if (icmp->icmp.type == 0x08) {
struct icmppkt icmp_rt = {0};
echo_icmp_pkt(icmp, &icmp_rt); //printf("icmp check %x\n", icmp_rt.icmp.check);
nm_inject(nmr, &icmp_rt, sizeof(struct icmppkt));
}
//如果是IGMP协议的话
} else if (udp->ip.protocol == PROTO_IGMP) { } else {
printf("other ip packet");
} } else if (ntohs(eh->h_proto) == PROTO_ARP) { struct arppkt *arp = (struct arppkt *)stream;
struct arppkt arp_rt; if (arp->arp.dip == inet_addr("192.168.0.26")) {
echo_arp_pkt(arp, &arp_rt, "00:50:56:33:1c:ca");
nm_inject(nmr, &arp_rt, sizeof(struct arppkt));
}
}
}
}
}

这样就通过使用netmap完成了icmp和arp,以及UDP包的解析。

UDP、IMCP、ARP协议通过netmap解析的实现。的更多相关文章

  1. TCP/IP, UDP, ICMP, ARP协议族简介--纯图慎点

    ISO/OSI的网络模型架构 TCP/IP参考模型的层次结果 以太网头部结构 以太网属于数据链路层, 属于最基本的协议结构 IP协议 IP协议为TCP, UDP, ICMP提供最基本的数据传输通路 I ...

  2. 图解ARP协议(四)代理ARP原理与实践(“善意的欺骗”)

    一.代理ARP概述 我:当电脑要访问互联网上的服务器,目标MAC是什么? 很多小伙伴在刚学习网络协议的时候,经常这样直接回应:不就是服务器的MAC嘛! 这时我会反问:那电脑怎么拿到这个服务器的MAC地 ...

  3. 网络基础:ARP 协议、IP协议、路由协议 均属于网络层协议

    ARP协议 ARP--地址解析协议(Address Resolution Protocol),实现通过 对方的IP地址(域名) 寻找对方的 MAC地址 ARP的功能 本地电脑查看 IP 和 MAC 对 ...

  4. UDP协议疑难杂症全景解析

    转载:http://blog.csdn.net/dog250/article/details/6896949 UDP协议疑难杂症全景解析 2011-10-22 19:26 2989人阅读 评论(4)  ...

  5. HTTP TCP UDP ICMP IP ARP 协议详解(10.15 第二十一天)

    ARP协议 ARP(Address Resolution Protocol)协议 地址解析协议 把网络层的IP地址翻译成在数据链路层寻址的48位硬件地址(MAC地址) 在OSI模型中ARP协议属于链路 ...

  6. TCP/IP详解学习笔记(3)-IP协议,ARP协议,RARP协议

    把这三个协议放到一起学习是因为这三个协议处于同一层,ARP协议用来找到目标主机的Ethernet网卡Mac地址,IP则承载要发送的消息.数据链路层可以从ARP得到数据的传送信息,而从IP得到要传输的数 ...

  7. 3.IP协议,ARP协议,RARP协议

    1.IP协议 IP协议是TCP/IP协议的核心,所有的TCP,UDP,IMCP,IGCP的数据都以IP数据格式传输.要注意的是,IP不是可靠的协议,这是说,IP协议没有提供一种数据未传达以后的处理机制 ...

  8. 通信原理之IP协议,ARP协议 (三)

    把这三个协议放到一起学习是因为这三个协议处于同一层,ARP协议用来找到目标主机的Ethernet网卡Mac地址,IP则承载要发送的消息.数据链路层可以从ARP得到数据的传送信息,而从IP得到要传输的数 ...

  9. TCP/IP详解学习笔记(3)-IP协议,ARP协议,RARP协议【转】

    转自:http://blog.csdn.net/goodboy1881/article/details/668556 把这三个协议放到一起学习是因为这三个协议处于同一层(网际层),ARP协议用来找到目 ...

  10. Winpcap网络编程九之Winpcap实战,ARP协议获得MAC表及主机通信

    大家好,本次我们须要完毕的任务是: 完毕两台主机之间的数据通信(数据链路层) 仿真ARP协议获得网段内主机的MAC表 使用帧完毕两台主机的通信(Hello! I'm -) 声明:本文章的目的是为大家的 ...

随机推荐

  1. MD5算法:高效安全的数据完整性保障

    摘要:在数字世界中,确保数据完整性和安全性至关重要.消息摘要算法就是一种用于实现这一目标的常用技术.其中,Message Digest Algorithm 5(MD5)算法因其高效性和安全性而受到广泛 ...

  2. 会话跟踪技术之COOKIE

    会话跟踪技术之COOKIE 一.为什么要用会话控制 我们需要我们的站点可以跟踪客户端与服务器之间的交互,保存和记忆每个用户的身份和信息. 几个疑问 我先访问A页面后访问B页面,HTTP无法知道是不是同 ...

  3. Python初学(请大神多多指教)

    python的注释单行注释用#号多行注释用'''  '''注释 基本数据类型 字符串--   n1 = "alex"   n2 = 'root'       n3 = " ...

  4. Web-background information

    Client and Server A connection on the Internet takes place between 2 computers only: one that has th ...

  5. 探索C语言的数据类型:解密编程世界的核心秘密

    欢迎大家来到贝蒂大讲堂 养成好习惯,先赞后看哦~ 所属专栏:C语言学习 贝蒂的主页:Betty's blog 1. 常量与变量 1.1 常量 (1) 常量的概念 常量顾名思义就是无法改变的量,比如一周 ...

  6. Java并发编程-CompletableFuture(下)

    大家好,我是小高先生,书接上文,我们继续来学习CompletableFuture.上文我们讲了基础装Future是如何升级为神装CompletableFuture以及如何购买CompletableFu ...

  7. junit运行Parameterized参擞化测试

    Parameterized (参数化)的测试运行器允许你使用不同的参数多次运行同一个侧试. 运行此测试的必备条件: 1.必须使用@RunWith(Parameterized.class) 2.必须声明 ...

  8. Lua 中如何实现继承

    本文主要参考了菜鸟教程中的 Lua 面向对象,再加上自己学习过程的中思考,特此记录,如果文中有不对的地方,请不吝赐教. 这里就不在介绍面向对象的基本思想了,主要讲一讲 Lua 中如何实现继承,包括单继 ...

  9. Mysql 删除binlog日志方法

    方法1 RESET MASTER; 解释: 该方法可以删除列于索引文件中的所有二进制日志,把二进制日志索引文件重新设置为空,并创建一个以.000001为后缀新的二进制日志文件. 该语法一般只用在主从环 ...

  10. 常用 Maven 插件介绍

    我们都知道Maven本质上是一个插件框架,它的核心并不执行任何具体的构建任务,所有这些任务都交给插件来完成,例如编译源代码是由maven- compiler-plugin完成的.进一步说,每个任务对应 ...