UDP用户态协议栈详细实现

news/2024/5/18 15:14:10 标签: udp, 网络协议, 网络

UDP用户态协议栈详细实现

  • 1 前言
  • 2 网络协议>网络协议格式
    • 2.1 以太网协议
    • 2.2 IP协议
    • 2.3 UDP协议
    • 2.4 ARP协议
    • 2.5 ICMP协议
  • 3 UDP用户协议栈实现

1 前言

首先需要回答一个问题,为什么要学习实现用户态协议栈,从技术角度分析,主要是由于用户态的网络协议>网络协议栈更高效,第二个是用户态协议栈可以实现定制。更高效主要是针对,网卡数据拷贝到协议栈这部分,如果采用用户态协议栈就可以进行一个零拷贝的过程,即利用mmap技术完成。这里实现UDP用户态协议主要是对于协议栈的理解的加深,了解内核协议栈的工作原理。

2 网络协议>网络协议格式

2.1 以太网协议

以太网协议分为三个部分,协议头,数据和CRC校验。源地址和目的地址是指网卡的硬件地址(也叫MAC地址),类型主要包括IP协议,ARP协议和RARP协议。协议定义来自于RFC894,具体如下图。
在这里插入图片描述
头结构定义:

#define ETH_HDR_LEN             6

#define IP_PROTO                0x0800
#define ARP_PROTO               0x0806
#define RARP_PROTO              0x0835

typedef struct _eth_hdr {
    unsigned char src_mac[ETH_HDR_LEN];
    unsigned char dst_mac[ETH_HDR_LEN];
    unsigned short proto;
}eth_hdr;

2.2 IP协议

IP协议比较复杂,包括内容比较多,主要是定义了源ip地址和目的ip地址。默认是20字节的头部长度,当存在选项时头部长度将进行扩展。具体定义见RFC791:
在这里插入图片描述头结构定义:

typedef struct  _ip_header
{
#if __BYTE_ORDER == __LITTLE_ENDIAN
    unsigned char hdr_len:4,
                  version:4;
#else
    unsigned char version:4,
                  hdr_len:4;
#endif
    unsigned char tos_type;
    unsigned short pkt_len;
    unsigned short mark;
    unsigned short flag_offset;
    unsigned char ttl;
    unsigned char proto;
    unsigned short hdr_checksum;
    unsigned int src_ip;
    unsigned int dst_ip;
    unsigned int opt[0];
}ip_header;

2.3 UDP协议

udp协议是定义最为简单的协议,协议基本不提供什么保障,主要定义了源端口和目的端口,也使得UDP在用户层可以进行更为灵活的设计。详细见RFC768:
在这里插入图片描述

#define PROTO_UDP	17
typedef struct  _udp_header
{
    unsigned short src_port;
    unsigned short dst_port;

    unsigned short length;
    unsigned short checksum;
}udp_header;

typedef struct  _udp_pkt
{
    eth_header eth;
    ip_header ip;
    udp_header udp;
    
    unsigned char data[0];
}udp_pkt;

2.4 ARP协议

ARP协议的作用是通过IP地址获取MAC地址,主要工作流程是ARP进程在本局域网上广播发送一个ARP请求分组,主要内容是A自己的IP地址、MAC地址,询问的IP地址。然后被询问对象既可以回复自身的ip和mac地址,建立一个arp表,后面可以直接查表。具体见RFC 826:
在这里插入图片描述

typedef struct  _arp_header
{
    unsigned short hw_type;
    unsigned short proto;
    unsigned char hw_len;
    unsigned char addr_len;
    unsigned short op;

    unsigned short src_mac[ETH_MAC_LEN];
    unsigned int src_ip;
    unsigned short dst_mac[ETH_MAC_LEN];
    unsigned int dst_ip;
}arp_header;

typedef struct  _arp_pkt
{
    eth_header eth;
    arp_header arp;

    unsigned char data[0];
}arp_pkt;

注意:
这里存在一个arp攻击问题,就是对于arp欺骗,利用arp广播的原理,然后发送错误的ip和mac个对方造成arp表错乱整个网络无法使用。

2.5 ICMP协议

ICMP协议是一种面向无连接的协议,用于传输出错报告控制信息。具体见RFC792:
在这里插入图片描述
结构定义:

typedef struct  _icmp_header
{
    unsigned char type;

    unsigned char code;
    unsigned short checksum;
    unsigned short identifier;
    unsigned short seq;
    unsigned char data[32];
}icmp_header;

typedef struct  _icmp_pkt
{
    eth_header eth;
    ip_header ip;
    icmp_header icmp;

    unsigned char data[0];
}icmp_pkt;

3 UDP用户协议栈实现

利用netmap实现一个简单的UDP协议的解析过程,用到netmap接口:nm_open打开网卡;nm_nextpkt获取网络数据;nm_inject发送网络数据。利用poll进行网络数据接收状态的获取,根据协议一层一层的强转,最终得到用户数据,数据发送也是一层层封装的过程。整体比较简单。网络编程需要考虑网络字节序和用户字节序,后续完善ARP查表和ICMP ping功能的实现。

#include<stdio.h>
#include <poll.h>
#include <arpa/inet.h>
#define NETMAP_WITH_LIBS
#include <net/netmap_user.h> 
#pragma pack(1)

int main() 
{
    eth_header *eth = NULL;
    struct pollfd pfd = {0};
    struct nm_pkthdr nmh;
    unsigned char *buffer = NULL;

    //netmap:打开eth0网卡
    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) 
        {
            buffer = nm_nextpkt(nmr, &nmh);
            eth = (eth_header *)buffer;

            if(ntohs(eth->proto) == PROTO_IP)
            {
                udp_pkt *udp = (udp_pkt *)buffer;
                if(udp->ip.proto == PROTO_UDP)
                {
                    struct in_addr addr;
					addr.s_addr = udp->ip.src_ip;

					int udp_length = ntohs(udp->udp.length);
					printf("%s:%d:length:%d, ip_len:%d --> ", inet_ntoa(addr), udp->udp.src_port, 
						udp_length, ntohs(udp->ip.pkt_len));

					udp->data[udp_length-8] = '\0';
					printf("udp buffer: %s\n", udp->data);

                    udp_pkt udp_rt;
					echo_udp_pkt(udp, &udp_rt);
					nm_inject(nmr, &udp_rt, sizeof(udp_pkt));
                }

            }
            
        }
    }

    return 0;
}

注意点:
结构体会进行对齐,造成最后数据包的大小计算错误,这里需要指定#pragma pack(1)


http://www.niftyadmin.cn/n/927470.html

相关文章

企业级线程池实现

异步多任务组件-线程池实现1 前言2 线程池的工作原理2.1 结构体定义2.2 线程池处理流程3 线程池实现3.1 线程池创建3.2 线程池销毁3.3 线程池添加任务3.4 线程池woker处理函数3.5 队列操作函数1 前言 线程池作为作为开发过程中的利器&#xff0c;具有的优势也非常明显&#xf…

内存管理-内存池的实现

内存池的实现1 前言2 内存池的原理2.1 内存利用链表进行管理2.2 分配固定大小2.3 按块进行内存管理3 内存池的实现3.1 内存池的创建3.2 内存池的销毁3.3 内存分配3.4 大块内存分配3.5 小块内存分配3.6 内存的对其问题1 前言 内存池出现的意义比较重大&#xff0c;对于服务器这…

企业级请求池实现

企业级请求池实现1 前言2 异步请求池原理2.1 基本原理2.2 结构体设计3 请求池实现3.1 请求池创建3.2 请求池销毁3.3 请求响应函数3.3 请求函数1 前言 请求池出现的运用场景&#xff0c;主要是为了解决同步处理带来的阻塞导致效率的大大降低&#xff0c;异步请求池将请求事件和…

企业级连接池实现

企业级连接池实现1 前言2 连接池设计2.1 连接池原理2.2 数据结构设计3 mysql连接过程4 连接池的实现4.1 连接池创建4.2 连接池销毁4.3 连接获取4.4 连接归还5 测试1 前言 连接池主要解决的问题就是&#xff0c;服务器对数据库的后半段的连接问题&#xff0c;这个过程连接对象是…

多线程操作与安全

多线程操作与安全1 互斥锁1.1 使用场景1.2 基本操作2 自旋锁2.1 使用场景2.2 基本操作3 原子操作3.1 使用场景3.2 基本操作4 线程私有空间4.1 使用场景4.2 基本操作5 信号量5.1 使用场景5.2 基本操作1 互斥锁 1.1 使用场景 互斥锁的特性&#xff1a; 当遇到锁被占用&#xff…

地表最强队列-ZMQ无锁队列

1 前言 老规矩&#xff0c;介绍前先简单聊一下为啥需要无锁队列&#xff0c;主要解决了哪些问题。首先是为啥需要无锁队列&#xff0c;我们最常见的就是利用锁保护临界资源&#xff0c;在多线程中进行队列操作&#xff0c;当并发量起来会带来大量的线程切换开销&#xff0c;而…

企业级定时器实现

1 前言 定时器在工程项目中应该是使用最为频繁的一个组件&#xff0c;很多时候处理任务并不是外部触发的任务&#xff0c;而是需要定时执行一些常规任务&#xff0c;比如说心跳包&#xff1b;定时更新更新信息&#xff1b;定时发送邮件等功能都需要用到定时器。 2 定时器的实…

音视频协议-RTP协议

1 协议简介 音视频传输的基石&#xff1a;RTP和RTCP。对于协议的讲解主要是是对于RFC文档的阅读和理解。不同的使用场景用到的字段也有所侧重&#xff0c;RTP和RTCP定义在RFC3550中。其中RTP用于数据流的传输&#xff1b;RTCP用于数据流的控制。可以说rtp/rtcp协议是即时通讯不…