• hello world

hello world

学习一门编程语言,首先要做的就是编写一个hello world程序。

dpdk没有提供安装程序,需要使用源码编译。

本例使用dpdk-22.11版本

关于IDE和代码编辑器

dpdk没有专门的IDE,只能使用打印来进行验证。

代码编辑器推荐vscode和source insight, 个人推荐使用vscode

环境依赖

官网详细描述:

内核版本: Kernel version >= 4.14; 查询命令 uname -r

glibc版本: glibc >= 2.7 ; 查询命令 ldd --version

python: Python 3.6 or later

meson: Meson (version 0.53.2+) and ninja;pip3 install meson ninja

gcc: gcc (version 4.9+)

pkg-config: apt install build-essential

pyelftools: version 0.22+ ; apt install python3-pyelftools

numa: apt install libnuma-dev

编译

解压

tar Jxf dpdk-22.11.6.tar.xz
cd dpdk-stable-22.11.6/

常规方式:

meson  build
cd build
ninja -j 4

全平台编译:

这种方式可以在高性能设备上编译好,复制到低性能设备上。

meson setup  -Dplatform=generic build

示例程序examples

编译helloworld

make  
# 或者使用 make DEBUG=1

设置内存大页,igb_uio,绑定网口

内存大页

设置内存大页

echo 1024 > /sys/devices/system/node/node0/hugepages/hugepages-2048kB/nr_hugepages
echo 2 > /sys/devices/system/node/node0/hugepages/hugepages-1048576kB/nr_hugepages

挂载大页

mkdir /mnt/huge
mount -t hugetlbfs pagesize=1GB /mnt/huge

自动挂载: /etc/fstab

nodev /mnt/huge hugetlbfs pagesize=1GB 0 0

自动分配大页

default_hugepagesz=1G hugepagesz=1G hugepages=2

igb_uio

首先下载dpdk-kmods : git clone git://dpdk.org/dpdk-kmods

编译:

cd dpdk-kmods/linux/igb_uio
make

加载驱动

modprobe uio
insmod /home/dpdk-kmods/linux/igb_uio/igb_uio.ko

绑定网口到igb_uio

cd /home/dpdk-stable-22.11.6
./usertools/dpdk-devbind.py -b igb_uio 04:00.0
echo 128 > /sys/devices/system/node/node0/hugepages/hugepages-2048kB/nr_hugepages

代码

作用是打印网口收到的udp包内容

main.c

#include <stdint.h>
#include <stdlib.h>
#include <inttypes.h>
#include <rte_eal.h>
#include <rte_ethdev.h>
#include <rte_cycles.h>
#include <rte_lcore.h>
#include <rte_mbuf.h>

#define RX_RING_SIZE 1024
#define TX_RING_SIZE 1024

#define NUM_MBUFS 8191
#define MBUF_CACHE_SIZE 250
#define BURST_SIZE 32


static const struct rte_eth_conf port_conf_default ;

int
main(int argc, char *argv[])
{
        struct rte_mempool *mbuf_pool;
        unsigned nb_ports;
        uint16_t portid = 0;
        uint16_t nb_rx_q = 1;
        uint16_t nb_tx_q = 0;


        int ret = rte_eal_init(argc, argv);
        if (ret < 0)
                rte_exit(EXIT_FAILURE, "Error with EAL initialization\n");


        nb_ports = rte_eth_dev_count_avail();
        // if (nb_ports < 2 || (nb_ports & 1))
        //      rte_exit(EXIT_FAILURE, "Error: number of ports must be even\n");


        mbuf_pool = rte_pktmbuf_pool_create("MBUF_POOL", NUM_MBUFS * nb_ports,
                MBUF_CACHE_SIZE, 0, RTE_MBUF_DEFAULT_BUF_SIZE, rte_socket_id());

        if (mbuf_pool == NULL)
                rte_exit(EXIT_FAILURE, "Cannot create mbuf pool\n");


        int retval = rte_eth_dev_configure(portid, nb_rx_q, nb_tx_q, &port_conf_default);
        if (retval != 0)
                rte_exit(EXIT_FAILURE, "Error with eth dev configure\n");

        retval = rte_eth_rx_queue_setup(portid, 0, 128,
                        rte_eth_dev_socket_id(portid), NULL, mbuf_pool);
        if (retval < 0)
                rte_exit(EXIT_FAILURE, "Error with rx queue setup\n");


        retval = rte_eth_dev_start(portid);
        if (retval < 0)
                rte_exit(EXIT_FAILURE, "Error with eth dev start\n");


        retval = rte_eth_promiscuous_enable(portid);
        if (retval != 0)
                rte_exit(EXIT_FAILURE, "Error with eth promiscuous enable\n");

        for (;;) {
                struct rte_mbuf *bufs[BURST_SIZE];
                const uint16_t nb_rx = rte_eth_rx_burst(portid, 0,
                                bufs, BURST_SIZE);

                if (unlikely(nb_rx == 0))
                        continue;

                if (nb_rx > BURST_SIZE)
                        rte_exit(EXIT_FAILURE, "Error with rte_eth_rx_burst\n");

                uint16_t i = 0;
                for ( i = 0; i < nb_rx; i++)
                {
                        struct rte_ether_hdr *ehdr = rte_pktmbuf_mtod(bufs[i], struct rte_ether_hdr *);
                        if (ehdr->ether_type != rte_cpu_to_be_16(RTE_ETHER_TYPE_IPV4))
                        {
                                continue;
                        }
                        struct rte_ipv4_hdr * iphdr = rte_pktmbuf_mtod_offset(bufs[i], struct rte_ipv4_hdr *, sizeof(struct rte_ether_hdr));
                        if (iphdr->next_proto_id == IPPROTO_UDP)
                        {
                                struct rte_udp_hdr *udphdr = (struct rte_udp_hdr *)(iphdr+1);
                                uint16_t length = udphdr->dgram_len;
                                *(char *)(udphdr + length-1) = '\0';
                                printf("udp: %s\n", (char *)(udphdr+1));
                        }
                }
        }



        if (rte_lcore_count() > 1)
                printf("\nWARNING: Too many lcores enabled. Only 1 used.\n");


        rte_eal_cleanup();

        return 0;
}

代码执行流程

  • 初始化环境抽象层(Environment Abstraction Layer ): rte_eal_init

  • 创建内存池: rte_pktmbuf_pool_create

  • 配置网口: rte_eth_dev_configure

  • 配置rx队列: rte_eth_rx_queue_setup

  • 启动网口: rte_eth_dev_start

  • 开启网口混杂模式: rte_eth_promiscuous_enable

  • 通过循环,通过内存块(rte_mbuf)不断从接收队列中取值: rte_eth_rx_burst

  • 解析内存块,打印收到的udp信息。

wireshark:

  • Frame 物理层的数据帧概况:47字节
  • Ethernet II: 数据链路层以太网帧头部信息,14字节
  • Internet Protocol Version 4: 互联网层ip包头部信息:20字节
  • 传输层头部信息:8字节
  • Data: 数据:5字节

演示

发送udp数据

网络调试助手

hello world收到数据:

udp: hello

wireshark抓包

image-20241110235825321

代码和抓到包对应关系

数据链路层

rte_ether_hdr,这个结构体对应数据包中的数据链路层,占14个字节。

eth.dst(接收方mac),6个字节: 60:be:b4:01:7e:eb

eth.src(发送方mac),6个字节:c8:7f:54:70:32:64

eth.type(类型),2个字节:0800(表示ipv4),另外常用的几种:

* 0x0806 arp
* 8100 vlan
* 88a8 qinq
* 8847 mpls
* 0x86DD ipv6

网络层

rte_ipv4_hdr,这个结构体对应数据包中的网络层,是ipv4,这里占用20个字节。

第一个字节是ip.version + ip.hdr_len: 4对应的是ipv4,5乘以4等于20这个是网络层头的大小。

第二个字节是ip.dsfield,这里不关注:Differentiated Services Field

第3,4个字节ip.len :总长度 = ip头长度(字节)+数据部分长度

第5-6,7,8字节和ip分段有关,这里不关注。ip.id(Identification) + ip.flags + ip.frag_offset

第9个字节: ip.ttl

第10个字节:ip.proto, 传输协议,这里是UDP(17)

第11,12字节:ip.checksum

第13-16字节:ip.src, 这里是192.168.100.33

第17-20:ip.dst,这里是192.168.100.66

传输层

rte_udp_hdr,这个结构体对应数据包中的传输层,是udp,这里占13个字节

第1-2字节:源端口 udp.srcport

第3-4字节:udp.dstport

第5-6字节: udp.length

第7-8字节: udp.checksum

Data

TCP/IP五层模型

  • 物理层
  • 数据链路层
  • 网络层 (ip, icmp, arp)
  • 传输层(tcp, udp)
  • 应用层

ip协议层抓包内容视频解析