【网络协议】基于netmap的用户态协议栈实现
1、 netmap原理
2、 netmap安装
2.1 系统环境及软件版本
先后尝试了两个版本,在Ubuntu 22.04上安装失败,换成Centos 7.9成功了。
Ubuntu 22.04(未完成安装)
Centos 7.9.2009(安装成功)
-- > CentOS Linux release 7.9.2009 (Core)
--> Linux 10.0.2.4 3.10.0-1127.el7.x86_64 #1 SMP Tue Mar 31 23:36:51 UTC 2020 x86_64 x86_64 x86_64 GNU/Linux
netmap版本:11.3
2.2 系统环境配置
2.2.1 更改网卡接口名称
2.2.1.1 查看网卡mac地址
方法一 ip link show eth0 | awk '/ether/ {print $2}'
方法二(需要安装net-tools)ifconfig eth0 | awk '/ether/ {print $2}'
2.2.1.2 新建配置文件
sudo vim /etc/udev/rules.d/10-network.rules
2.2.1.3 添加配置信息
SUBSYSTEM=="net", ACTION=="add", ATTR{address}=="<mac_address_of_interface>", NAME="<your new interface name>"
# 这是将eth0更改名称为eth1的配置
SUBSYSTEM=="net", ACTION=="add", ATTR{address}=="f8:e4:3b:60:be:21", NAME="eth1"
2.2.1.4 保存文件并重启系统
2.2.2 手动下载驱动
正常情况下,netmap配置时会自动下载驱动程序,如果无法下载,需要我们手动下载后拷贝到指定的路径下:
相关提示信息如下

2.2.3 下载内核源码树

netmap编译时需要内核源码,如果系统没有,需要下载安装;
sudo apt-get install -y linux-headers-$(uname -r)
sudo apt-get install -y linux-source
2.2.4 配置和安装netmap
$cd /usr/src/kernels/linux-3.10.0-1127.el7
$make oldconfig
$make modules_prepare
/usr/netmap-11.3$ sudo ./configure --kernel-sources=xxx --kernel-dir=xxx
/usr/netmap-11.3$ make && make install
需要注意:
1、 内核源码包一定要下载正确,不要下载成内核头文件包了,一定要源码包;
Centos7 安装内核源码参考文章:Centos7 内核源码编译 网桥 bridge模块_centos bridge模块是内置模块吗-CSDN博客
2、 配置netmap时,--kernel-sources和--kernel-dir路径都要加上,前者是提供源码目录,后者是模块编译环境;
3、 需要在内核源码目录执行下面命令后,再去执行./configure --kernel-sources=xxx --kernel-dir=xxx命令配置netmap
cd /usr/src/kernels/linux-3.10.0-1127.el7
make oldconfig
make modules_prepare
否则,配置可能通过,但是make的时候会出现下面错误:

4、 要取github上netmap的master分支的最新代码,不要下载release包,否则也会出现上面问题。
5、 要注意查看配置提示
6、 ./configure时报下面错误,记得要先去源码目录编译一下modules:make modules
7、 ./configure配置时,最好加上--drivers=xxx参数,不加的话,在make阶段可能会报下面的错误:

原因可能时在配置检测时,错误检测出多类型网卡驱动:

8、 编译出来的ko驱动无法被系统加载,提示:

通过dmesg查看内核日志,也没什么思路。
查资料,根据insmod error could not insert module ... invalid module format [已解决]_linux_正义之兔-华为云开发者联盟 (csdn.net)的提示,通过modinfo查看对比驱动,发现vermagic值不一致,去到源码目录修改utsrelease.h文件(/usr/src/kernels/3.10.0-1127.el7.x86_64/include/generated/utsrelease.h),修改后vermagic一致,但是并没有解决问题。
又查了很多资料,感觉可能和Module.symvers这个文件有关,Module.symvers据我猜测,可能是编译内核模块时的一些配置。这个文件怎么获得,没有找到文档说明,可能需要完整编译一次内核才能得到。看博文,很多博主都在自身系统里能找得到该文件。但是无奈,我的系统里没有找到。最后,看到这篇文章CentOS 7 安装 netmap_netmap centos7-CSDN博客,突然想到kernel-devel包里应该有,我通过yum -y install kernel-devel-3.10.0-1127.el7.x86_64.rpm命令想去安装,但是都找不到资源,从mirror.centos.org上找发现这个版本的包已经不提供支持了。在网上找了很多都没找到,许多需要收费的资源不敢轻易尝试,最后在google上终于找到了资源,解压得到Module.symvers放入我的内核源码中,完美解决。
至此,耗时两天,终于安装完成。
3、 基于netmap实现协议
#ifndef _NETMAP_PROTOCOL_STACK_H #define _NETMAP_PROTOCOL_STACK_H #define ETH_ALEN 6 #define BUFFER_SIZE 1024 // eth type #define TYPE_IP 0x0800 #define TYPE_ARP 0x0806 // ip protocol #define PROTO_TCP 6 #define PROTO_UDP 17 #define PROTO_ICMP 1 #define PROTO_IGMP 2 #define LOCAL_MAC "08:00:27:ff:f7:9b" #define LOCAL_IP "192.168.56.102" struct eth_header { unsigned char d_mac[ETH_ALEN]; unsigned char s_mac[ETH_ALEN]; unsigned short type; }__attribute__((packed)); struct ip_header { unsigned char version : 4; unsigned char ihl : 4; unsigned char tos; unsigned short tot_len; unsigned short iden; unsigned short flag : 3; unsigned short foffset : 13; unsigned char ttl; unsigned char protocol; unsigned short checksum; unsigned int s_addr; unsigned int d_addr; }__attribute__((packed)); struct udp_pseudo_header { unsigned int s_addr; unsigned int d_addr; unsigned char all0s; unsigned char protocol; unsigned short len; }__attribute__((packed)); struct udp_header { unsigned short s_port; unsigned short d_port; unsigned short len; unsigned short checksum; }__attribute__((packed)); struct udp_segment { struct udp_header hdr; unsigned char data[0]; }__attribute__((packed)); struct icmp_header { unsigned char type; unsigned char code; unsigned short checksum; }__attribute__((packed)); struct icmp_echo_segment { struct icmp_header hdr; unsigned short id; unsigned short sequence; unsigned char data[32]; }__attribute__((packed)); struct arp_packet { unsigned short hwtype; unsigned short prototype; unsigned char hwaddrlen; unsigned char protoaddrlen; unsigned short opcode; unsigned char s_mac[ETH_ALEN]; unsigned int s_ip; unsigned char d_mac[ETH_ALEN]; unsigned int d_ip; }__attribute__((packed)); unsigned short in_checksum(unsigned short *addr, int len); int arp_packet_proc(unsigned char *stream, unsigned char *rbuff, int rsize, unsigned char *mac, unsigned char *ip); int ip_packet_proc(unsigned char *stream, unsigned char *rbuff, int rsize); int udp_segment_proc(unsigned char *stream, unsigned char *rbuff, int rsize, unsigned int s_addr, unsigned int d_addr); int tcp_segment_proc(unsigned char *stream); int icmp_segment_proc(unsigned char *stream, unsigned char *rbuff, int rsize); #endif // _NETMAP_PROTOCOL_STACK_H
#include "netmap_protocol_stack.h" #include <stdio.h> #include <unistd.h> #include <string.h> #include <stdlib.h> #include <assert.h> #include <sys/poll.h> #include <arpa/inet.h> #define NETMAP_WITH_LIBS #include <net/netmap_user.h> 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; } unsigned short in_checksum(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); } int ip_packet_proc(unsigned char *stream, unsigned char *rbuff, int rsize) { int buff_size = 0; struct ip_header *ip = (struct ip_header *)stream; struct in_addr s_addr, d_addr; s_addr.s_addr = ip->s_addr; d_addr.s_addr = ip->d_addr; printf("%s to %s--> ", inet_ntoa(s_addr), inet_ntoa(d_addr)); struct ip_header ip_hdr; memcpy(&ip_hdr, ip, sizeof(struct ip_header)); ip_hdr.d_addr = ip->s_addr; ip_hdr.s_addr = ip->d_addr; ip_hdr.checksum = 0; //ip_hdr.flag = 2; int cpy_len = rsize < sizeof(struct ip_header) ? rsize : sizeof(struct ip_header); memcpy(rbuff, &ip_hdr, cpy_len); buff_size += cpy_len; stream += sizeof(struct ip_header); if (ip->protocol == PROTO_TCP) { // } else if (ip->protocol == PROTO_UDP) { buff_size += udp_segment_proc(stream, rbuff+cpy_len, rsize-cpy_len, ip->s_addr, ip->d_addr); } else if (ip->protocol == PROTO_ICMP) { buff_size += icmp_segment_proc(stream, rbuff+cpy_len, rsize-cpy_len); } ((struct ip_header *)rbuff)->tot_len = htons(buff_size); ((struct ip_header *)rbuff)->checksum = in_checksum((unsigned short *)rbuff, sizeof(struct ip_header)); return buff_size; } int icmp_segment_proc(unsigned char *stream, unsigned char *rbuff, int rsize) { int buff_size = 0; struct icmp_header *hdr = (struct icmp_header *)stream; if (hdr->type == 8 && hdr->code == 0) { struct icmp_echo_segment *icmp = (struct icmp_echo_segment *)stream; struct icmp_echo_segment icmp_seg; memcpy(&icmp_seg, icmp, sizeof(struct icmp_echo_segment)); icmp_seg.hdr.type = 0; icmp_seg.hdr.code = 0; icmp_seg.hdr.checksum = 0; icmp_seg.id = icmp->id; icmp_seg.sequence = icmp->sequence; int icmp_len = sizeof(struct icmp_echo_segment); icmp_seg.hdr.checksum = in_checksum((unsigned short *)&icmp_seg, icmp_len); int cpy_len = rsize < icmp_len ? rsize : icmp_len; memcpy(rbuff, (unsigned char *)&icmp_seg, cpy_len); buff_size += cpy_len; } return buff_size; } int udp_segment_proc(unsigned char *stream, unsigned char *rbuff, int rsize, unsigned int s_addr, unsigned int d_addr) { struct udp_segment *udp = (struct udp_segment *)stream; int udp_total_len = ntohs(udp->hdr.len); const udp_hdr_len = sizeof(struct udp_header); printf("udp(from fort : %u --> to port : %u)\n", ntohs(udp->hdr.s_port), ntohs(udp->hdr.d_port)); udp->data[udp_total_len-udp_hdr_len] = '\0'; printf("segment len:%d data--> %s\n", udp_total_len, udp->data); char buffer[] = {"from netmap --> udp reply"}; struct udp_segment udp_seg; memcpy(&udp_seg, &udp->hdr, sizeof(struct udp_header)); udp_seg.hdr.s_port = udp->hdr.d_port; udp_seg.hdr.d_port = udp->hdr.s_port; udp_seg.hdr.checksum = 0; udp_seg.hdr.len = htons(sizeof(buffer)+sizeof(struct udp_header)); int cpy_len = 0; assert(rsize > sizeof(struct udp_header)); memcpy(rbuff, &udp_seg, sizeof(struct udp_header)); cpy_len += sizeof(struct udp_header); memcpy(rbuff+cpy_len, buffer, sizeof(buffer)); cpy_len += sizeof(buffer); struct udp_pseudo_header pseudo_header; pseudo_header.s_addr = s_addr; pseudo_header.d_addr = d_addr; pseudo_header.all0s = 0; pseudo_header.protocol = PROTO_UDP; pseudo_header.len = udp_seg.hdr.len; unsigned char buf[BUFFER_SIZE] = {0}; unsigned short buf_len = 0; memcpy(buf, &pseudo_header, sizeof(struct udp_pseudo_header)); buf_len += sizeof(struct udp_pseudo_header); memcpy(buf+buf_len, rbuff, cpy_len); buf_len += cpy_len; ((struct udp_header *)rbuff)->checksum = in_checksum((unsigned short *)&buf, buf_len); return cpy_len; } int tcp_segment_proc(unsigned char *stream) { return 0; } int arp_packet_proc(unsigned char *stream, unsigned char *rbuff, int rsize, unsigned char *mac, unsigned char *ip) { int cpy_len = 0; struct arp_packet *arp = (struct arp_packet *)stream; if (ntohs(arp->opcode) == 0x1 && inet_addr(ip) == arp->d_ip) { struct arp_packet arp_reply; memcpy(&arp_reply, arp, sizeof(struct arp_packet)); arp_reply.opcode = htons(0x0002); arp_reply.d_ip = arp->s_ip; arp_reply.s_ip = arp->d_ip; memcpy(arp_reply.d_mac, arp->s_mac, ETH_ALEN); memcpy(arp_reply.s_mac, mac, ETH_ALEN); cpy_len += sizeof(struct arp_packet); memcpy(rbuff, &arp_reply, cpy_len); } return cpy_len; } int main() { unsigned char local_mac[ETH_ALEN] = {0}; struct eth_header *eth; struct pollfd pfd = {0}; struct nm_pkthdr h; unsigned char *stream = NULL; unsigned char buffer[BUFFER_SIZE] = {0}; struct nm_desc *nmr = nm_open("netmap:eth1", 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) { int buff_size = 0; stream = nm_nextpkt(nmr, &h); eth = (struct eth_header *)stream; struct eth_header eth_hdr; memcpy(ð_hdr, eth, sizeof(struct eth_header)); memcpy(eth_hdr.d_mac, eth->s_mac, ETH_ALEN); str2mac(local_mac, LOCAL_MAC); memcpy(eth_hdr.s_mac, local_mac, ETH_ALEN); eth_hdr.type = eth->type; int cpy_len = BUFFER_SIZE < sizeof(struct eth_header) ? BUFFER_SIZE : sizeof(struct eth_header); memcpy(buffer, ð_hdr, cpy_len); buff_size += cpy_len; if (ntohs(eth->type) == TYPE_IP) { buff_size += ip_packet_proc(stream + sizeof(struct eth_header), buffer+buff_size, BUFFER_SIZE-buff_size); } else if (ntohs(eth->type) == TYPE_ARP) { buff_size += arp_packet_proc(stream + sizeof(struct eth_header), buffer+buff_size, BUFFER_SIZE-buff_size, local_mac, LOCAL_IP); } nm_inject(nmr, buffer, buff_size); } } return 0; }
浙公网安备 33010602011771号