【网络协议】基于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(&eth_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, &eth_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;
}

 

posted on 2024-01-31 11:33  lingzshen  阅读(147)  评论(0)    收藏  举报