手写用户态网络协议栈(udp)
分析
- 协议栈的数据如何封装
- 如何抓取网络原始数据
获取网卡原始数据
使用netmap工具
- raw socket
- pf_ring
- netmap
- dpdk
netmap安装
安装记录在我的另外一篇博客https://www.cnblogs.com/hunxiaoheibai/p/15802983.html
code
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <sys/poll.h>
#include <arpa/inet.h>
#define NETMAP_WITH_LIBS //避免netmap和系统api冲突,用来区分
#include <net/netmap_user.h>
#pragma pack(1) //设置一个字节对齐
#define PROTO_IP 0x800 //ip标志
#define PROTO_UDP 17 //udp标志
#define ETH_LENGTH 6
//以太网协议头
struct ethhdr
{
//目的地址
unsigned char dst[ETH_LENGTH];
//源地址
unsigned char src[ETH_LENGTH];
//类型
unsigned short proto;
};
// ip协议封装
struct iphdr
{
unsigned char version : 4; //版本
unsigned char height : 4; //首部长度。占用半个字节,两个一起占用一个字节
unsigned char tos; //服务类型
unsigned short length;
unsigned short id; //标志
unsigned short flag_offset; //偏移
unsigned char ttl; //生存时间
unsigned char proto; //协议
unsigned short check; //检验
unsigned int sip; //源ip
unsigned int dip; //目的ip
};
// udp协议封装
struct udphdr
{
unsigned short sport; //源端口
unsigned short dport; //目的端口
unsigned short length;
unsigned short check;
};
struct udppkt
{
//转化为内存块,不适用指针
struct ethhdr eh; //14字节
struct iphdr ip; //20字节
struct udphdr udp; //8字节
unsigned char data[0]; //零长数组,0字节
};
int main()
{
struct nm_desc *nmr = nm_open("netmap:enp0s3", NULL, 0, NULL);
if (nmr == NULL)
{
return 0;
}
struct pollfd pfd = {0};
pfd.fd = nmr->fd; //操作标识网卡信息,区分多个网卡信息
pfd.events = POLLIN;
struct nm_pkthdr h;
while (1)
{
int ret = poll(&pfd, 1, -1);
if (ret < 0)
continue;
if (pfd.revents & POLLIN)
{
unsigned char *stream = nm_nextpkt(nmr, &h);
struct ethhdr *eh = (struct ethhdr *)stream;
// 可能有arp协议,加一层ip判断
if (ntohs(eh->proto) == PROTO_IP)
{
struct udppkt *udp = (struct udppkt *)stream;
if (udp->ip.proto == PROTO_UDP)
{
int udp_length = ntohs(udp->udp.length);
udp->data[udp_length - 8] = '\0';
printf("udp->%s\n", udp->data);
}
}
}
}
}
测试
测试显示发送成功
参考