用户态协议栈设计实现udp,arp与icmp协议( 二 )


需要C/C++ linux服务器架构师学习资料加qun812855908获取(资料包括C/C++,Linux,golang技术,Nginx,ZeroMQ,MySQL,redis,fastdfs,MongoDB,ZK,流媒体,CDN,P2P,K8S,Docker,TCP/IP,协程,DPDK,ffmpeg等),免费分享

用户态协议栈设计实现udp,arp与icmp协议

文章插图
 
协议栈协议栈的定义所谓协议栈,“栈”怎么理解,先进后出,正如下图udp协议所示 。在应用层我们调用sendto发送数据时,我们需要在用户数据前面加上udp的协议头,然后进入网络层加入ip头,进入链路层加入以太网的头,最后由网卡进行数模转换变成光电信号发送给对端 。对端网卡接收到光电信号后进行模数转换,再依次拆包,最终到达应用层就是我们最初发送的数据了 。这个过程就像栈一样,先进后出 。
协议栈在一定意义是又可以称为协议族,“族”怎么理解,我们看到传输层有udp,tcp等协议,网络层有ip,icmp协议等等,这些协议形成了一个家族 。
用户态协议栈设计实现udp,arp与icmp协议

文章插图
 
内核协议栈帮我们解析了传输层,网络层和数据链路层的协议,所以我们用户态协议栈正是去做这三层的协议 。
用户态协议栈设计实现udp,arp与icmp协议

文章插图
 
链路层首部以太网协议
用户态协议栈设计实现udp,arp与icmp协议

文章插图
 
?以太网协议:两个地址皆为6字节的mac地址,后面2字节的类型区分是什么协议
#pragma pack(1) //一字节对齐#define NETMAP_WITH_LIBS#define ETH_ADDR_LENGTH 6#define PROTO_IP 0x0800 //ip协议#define PROTO_ARP 0x0806 //arp请求协议#define PROTO_RARP 0x0835 //rarp应答协议#define PROTP_UPD 17struct ethhdr {unsigned char h_dst[ETH_ADDR_LENGTH];unsigned char h_src[ETH_ADDR_LENGTH];unsigned short h_proto;};网络层首部ip协议
用户态协议栈设计实现udp,arp与icmp协议

文章插图
 
?关于ip协议里面第一个字节内的大小端转换不懂的请查看大端与小端概念、多字节之间与单字节多部分的大小端转换详解
struct iphdr {unsigned char hdrlen: 4, //一字节,手动大小端转换version: 4;unsigned char tos;unsigned short totlen;unsigned short id;unsigned short flag_offset;unsigned char ttl;unsigned char type;unsigned short check;unsigned int sip;unsigned int dip;};struct ippkt {struct ethhdr eh; //14struct iphdr ip; //20};arp协议
用户态协议栈设计实现udp,arp与icmp协议

文章插图
 
struct arphdr {unsigned short h_type;unsigned short h_proto;unsigned char h_addrlen;unsigned char h_protolen;unsigned short oper;unsigned char smac[ETH_ADDR_LENGTH];unsigned int sip;unsigned char dmac[ETH_ADDR_LENGTH];unsigned int dip;};struct arppkt {struct ethhdr eh;struct arphdr arp;};传输层首部udp协议这里传输层在本文中先只介绍udp,tcp下文再写
用户态协议栈设计实现udp,arp与icmp协议

文章插图
 
?为什么在udppkt里面,定义了一个unsigned char data[0];,这个叫柔性数组,或者叫零长数组,是用来定义用户数据包的起始地址而不占用实际的结构体空间 。具体内容自行百度 。
struct udphdr {unsigned short sport;unsigned short dport;unsigned short length;unsigned short check;};struct udppkt {struct ethhdr eh; //14struct iphdr ip; //20struct udphdr udp;//8unsigned char data[0];};用户态协议栈设计实现1. 实现udp协议设计思路我们使用udp工具,给我们的服务器发送udp包,看是否能解析出来,然后按再回发回去
如果要解析udp,那么协议顺序为 ether -> ip ->udp,所以我们按照这个顺序依次解析即可 。
用户态协议栈设计实现udp,arp与icmp协议

文章插图
 
?代码
//// Created by 68725 on 2022/7/19.//#include <stdio.h>#include <sys/poll.h>#include <netinet/in.h>#include <arpa/inet.h>#define NETMAP_WITH_LIBS#include <net/netmap_user.h>#include <string.h>#pragma pack(1)#define ETH_ADDR_LENGTH 6#define PROTO_IP 0x0800#define PROTO_ARP 0x0806#define PROTP_UPD 17struct ethhdr {unsigned char h_dst[ETH_ADDR_LENGTH];unsigned char h_src[ETH_ADDR_LENGTH];unsigned short h_proto;};struct iphdr {unsigned char hdrlen: 4,version: 4;unsigned char tos;unsigned short totlen;unsigned short id;unsigned short flag_offset;unsigned char ttl;unsigned char type;unsigned short check;unsigned int sip;unsigned int dip;};struct ippkt {struct ethhdr eh; //14struct iphdr ip; //20};struct udphdr {unsigned short sport;unsigned short dport;unsigned short length;unsigned short check;};struct udppkt {struct ethhdr eh; //14struct iphdr ip; //20struct udphdr udp;//8unsigned char data[0];};struct arphdr {unsigned short h_type;unsigned short h_proto;unsigned char h_addrlen;unsigned char h_protolen;unsigned short oper;unsigned char smac[ETH_ADDR_LENGTH];unsigned int sip;unsigned char dmac[ETH_ADDR_LENGTH];unsigned int dip;};struct arppkt {struct ethhdr eh;struct arphdr arp;};void echo_udp_pkt(struct udppkt *udp, struct udppkt *udp_rt) {memcpy(udp_rt, udp, sizeof(struct udppkt));memcpy(udp_rt->eh.h_dst, udp->eh.h_src, ETH_ADDR_LENGTH);memcpy(udp_rt->eh.h_src, udp->eh.h_dst, ETH_ADDR_LENGTH);udp_rt->ip.sip = udp->ip.dip;udp_rt->ip.dip = udp->ip.sip;udp_rt->udp.sport = udp->udp.dport;udp_rt->udp.dport = udp->udp.sport;}int main() {struct nm_pkthdr h;struct nm_desc *nmr = nm_open("netmap:ens33", NULL, 0, NULL);if (nmr == NULL) {return -1;}printf("open ens33 seccessn");struct pollfd pfd = {0};pfd.fd = nmr->fd;pfd.events = POLLIN;while (1) {printf("new data coming!n");int ret = poll(&pfd, 1, -1);if (ret < 0) {continue;}if (pfd.revents & POLLIN) {unsigned char *stream = nm_nextpkt(nmr, &h);//etherstruct ethhdr *eh = (struct ethhdr *) stream;if (ntohs(eh->h_proto) == PROTO_IP) {//ipstruct ippkt *iph=(struct ippkt *)stream;if (iph->ip.type == PROTP_UPD) {//udpstruct udppkt *udp = (struct udppkt *) stream;int udplength = ntohs(udp->udp.length);udp->data[udplength - 8] = '';printf("udp ---> %sn", udp->data);struct udppkt udp_rt;echo_udp_pkt(udp, &udp_rt);nm_inject(nmr, &udp_rt, sizeof(struct udppkt));}}}}nm_close(nmr);}


推荐阅读