IP

公司用的移动WiFi,和宿舍里不一样,看一下IP。

https://imagehyj.oss-cn-hangzhou.aliyuncs.com/blog/20240718110232.png

UDP/IP数据包

https://imagehyj.oss-cn-hangzhou.aliyuncs.com/blog/20240717224949.png

  • Ethernet 2:以太网头

  • Destination:目的MAC地址

  • Source:源MAC地址

  • Type:上层协议

    • 0x0800:IP协议

    • 0x0806:ARP协议

    • 0x86DD:IPv6协议

https://imagehyj.oss-cn-hangzhou.aliyuncs.com/blog/20240717232141.png

  • Internet Protocol Version 4:ipv4头

  • Version:版本号,用来表明IP协议实现的版本号,当前一般为IPv4,即0100。

  • Header Length:首部长度,因为头部长度不固定(Option可选部分不固定),所以需要标识该分组的头部长度多少,用4bit表示,以4byte为单位,取值范围:5-15,即20(5_4)-60(15_4)byte(其他字段也是类似的计算方式,因为bit位是不够表示该字段的值)

  • Differentiated Services Field:服务类型,前3比特为优先权子字段(Precedence,现已被忽略)。第8比特保留未用。第4至第7比特分别代表延迟、吞吐量、可靠性和花费。当它们取值为1时分别代表要求最小时延、最大吞吐量、最高可靠性和最小费用。这4比特的服务类型中只能置其中1比特为1。可以全为0,若全为0则表示一般服务。服务类型字段声明了数据报被网络系统传输时可以被怎样处理。例如:TELNET协议可能要求有最小的延迟,FTP协议(数据)可能要求有最大吞吐量,SNMP协议可能要求有最高可靠性,NNTP(Network News Transfer Protocol,网络新闻传输协议)可能要求最小费用,而ICMP协议可能无特殊要求(4比特全为0)。实际上,大部分主机会忽略这个字段,但一些动态路由协议如OSPF(Open Shortest Path First Protocol)、IS-IS(Intermediate System to Intermediate System Protocol)可以根据这些字段的值进行路由决策。RFC2474的ToS取消了IP precedence字段而使用了DSCP,QoS里有描述,给QoS用来打标签。

  • Total Length:总长度,指明整个数据报的长度(以字节为单位,含头长度)。最大长度为65535字节。可用总长度减去头部长度获得实际报文数据的长度,取值范围0-65535byte,链路只允许1500byte,所以一般都需要MTU分片 。

  • Identification:标识,来唯一地标识主机发送的每一份数据报。通常每发一份报文,它的值会加1。通常与标记字段和分片偏移字段一起用于IP报文的分片。当原始报文大小超过MTU,那么就必须将原始数据进行分片。每个被分片的报文大小不得超过MTU,而这个字段还将在同一原始文件被分片的报文上打上相同的标记,以便接收设备可以识别出属于同一个报文的分片,“类似于进程号”,有时候电信会用他来识别流量是否是同一台主机(因为做了PAT后源ip都是一样的,鸡贼!)

  • Flags:标志,第1位没有被使用;第2位D是不分片位(DF),Do not fragment,顾名思义,不要分片,当DF位设置为1时,表示路由器不能对报文进行分片处理;- 第3位M表示还有后继分片(MF),More fragment,多分片,当路由器对报分进行分片时,除了最后一个分片的MF位设置为0外,其他所有分片的MF位均设置1,以便接收者直到收到MF位为0的分片为止;

  • Fragment Offset:片偏移,如果一份数据报要求分段的话,此字段指明该段偏移距原始数据报开始的位置。

  • Time to Live:生存时间,用来设置数据报最多可以经过的路由器数。由发送数据的源主机设置,通常为32、64(win7)、128、255(linux)等。每经过一个路由器,其值减1,直到0时该数据报被丢弃。

  • Protocol:协议,指明IP层所封装的上层协议类型,如ICMP(1)、IGMP(2) 、TCP(6)、UDP(17)等。

  • Header checksum status:首部校验和,内容是根据IP头部计算得到的校验和码。计算方法是:对头部中每个16比特进行二进制反码求和。(和ICMP、IGMP、TCP、UDP不同,IP不对头部后的数据进行校验)。

    https://imagehyj.oss-cn-hangzhou.aliyuncs.com/blog/20240717232222.png

  • User Datagram Protocol:udp头

  • Source Port:源端口号

  • Destination Port:目的端口号

  • Length:udp数据包总长度

  • Checksum:校验

  • UDP payload:数据负载

编译代码

/*
 * 版权所有: Copyright (c) 2024-2025  XXX Company. All rights reserved.
 * 系统名称: 
 * 文件名称: roboteth-send.c
 * 内容摘要: 简要描述本文件的内容,包括主要模块、函数及其功能的说明
 * 作     者: 黄彦杰 Lenn
 * 设计日期: 2024-07-18
*/

#include <rte_ethdev.h>
#include <rte_eal.h>
#include <stdio.h>
#include <arpa/inet.h>

#define NUM_MBUFS (4096-1)
#define BURST_SIZE 32

#ifndef PKT_OFFLOAD
#define PKT_OFFLOAD

#define UDP_ENABLE 1

#endif

#if UDP_ENABLE

static uint8_t nSrcMac[RTE_ETHER_ADDR_LEN];
static uint8_t nDstMac[RTE_ETHER_ADDR_LEN];
static uint32_t nSrcIp;
static uint32_t nDstIp;
static uint16_t nSrcPort;
static uint16_t nDstPort;

#endif

static const struct rte_eth_conf port_dev_conf_default = {

    .rxmode = {.max_rx_pkt_len = RTE_ETHER_MAX_LEN}
};

int nDevPortId = 0;


/*
 * 函数名称: ln_init_port
 * 作     者:  黄彦杰 Lenn
 * 设计日期: 2024-07-17
 * 功能描述: 初始化虚拟网口,配置收发队列
 * 返 回 值: None
*/
static void ln_init_port(struct rte_mempool* mbuf_pool) {

    int nb_sys_port = rte_eth_dev_count_avail();

    if (nb_sys_port == 0) {

        rte_exit(EXIT_FAILURE, "No available system port");
    }

    struct rte_eth_dev_info dev_info;
    rte_eth_dev_info_get(nDevPortId, &dev_info);

    int nb_rx_queues = 1;
    int nb_tx_queues = 0;
    struct rte_eth_conf port_conf = port_dev_conf_default;
    rte_eth_dev_configure(nDevPortId, nb_rx_queues, nb_tx_queues, &port_conf);
    if (rte_eth_rx_queue_setup(nDevPortId, 0, 1024, rte_eth_dev_socket_id(nDevPortId), NULL, mbuf_pool) < 0) {

        rte_exit(EXIT_FAILURE, "Setup rx queue failed");
    }

#if UDP_ENABLE

    struct rte_eth_txconf txq_conf = dev_info.default_txconf;
    txq_conf.offloads = port_conf.rxmode.offloads;
    if (rte_eth_tx_queue_setup(nDevPortId, 0, 1024, rte_eth_dev_socket_id(nDevPortId), &txq_conf) < 0) {

        rte_exit(EXIT_FAILURE, "Setup tx queue falied");
    }

#endif

    if (rte_eth_dev_start(nDevPortId) < 0) {

        rte_exit(EXIT_FAILURE, "Could not start eth dev");
    }
}

#if UDP_ENABLE


/*
 * 函数名称: ln_udp_encode
 * 作     者: 黄彦杰 Lenn
 * 设计日期: 2024-07-18
 * 功能描述: 组织udp数据包
 * 返 回 值: 0
*/
static int ln_udp_encode(uint8_t* pktbuf, uint8_t* data, uint16_t total_len) {

    struct rte_ether_hdr* ehdr = (struct rte_ether_hdr*)pktbuf;
    rte_memcpy(ehdr->s_addr.addr_bytes, nSrcMac, RTE_ETHER_ADDR_LEN);
    rte_memcpy(ehdr->d_addr.addr_bytes, nDstMac, RTE_ETHER_ADDR_LEN);
    ehdr->ether_type = htons(RTE_ETHER_TYPE_IPV4);

    struct rte_ipv4_hdr* iphdr = (struct rte_ipv4_hdr*)(ehdr + 1);
    iphdr->version_ihl = 0x45;
    iphdr->type_of_service = 0;
    iphdr->total_length = htons(total_len);
    iphdr->packet_id = htons(0);
    iphdr->fragment_offset = htons(0);
    iphdr->time_to_live = 64;
    iphdr->next_proto_id = IPPROTO_UDP;
    iphdr->src_addr = nSrcIp;
    iphdr->dst_addr = nDstIp;
    iphdr->hdr_checksum = 0;
    iphdr->hdr_checksum = rte_ipv4_cksum(iphdr);

    struct rte_udp_hdr* udphdr = (struct rte_udp_hdr*)(iphdr + 1);
    udphdr->src_port = nSrcPort;
    udphdr->dst_port = nDstPort;
    uint16_t udp_len = total_len - sizeof(struct rte_ether_hdr) - sizeof(struct rte_ipv4_hdr);
    udphdr->dgram_len = htons(udp_len);
    rte_memcpy((uint8_t*)(udphdr + 1), data, udp_len - sizeof(struct rte_udp_hdr));
    udphdr->dgram_cksum = 0;
    udphdr->dgram_cksum = rte_ipv4_udptcp_cksum(iphdr, udphdr);

    return 0;
}

/*
 * 函数名称: ln_udp_send
 * 作     者: 黄彦杰 Lenn
 * 设计日期: 2024-07-18
 * 功能描述: udp数据包发送
 * 返 回 值: 存储udp数据包的一段内存
*/
static struct rte_mbuf* ln_udp_send(struct rte_mempool* mbuf_pool, uint8_t* msg, uint16_t length) {

    const unsigned total_len = length + 42;
    struct rte_mbuf* mbuf = rte_pktmbuf_alloc(mbuf_pool);
    if (!mbuf) {

        rte_exit(EXIT_FAILURE, "Alloc udp mbuf failed");
    }
    mbuf->pkt_len = total_len;
    mbuf->data_len = total_len;

    uint8_t* pktdata = rte_pktmbuf_mtod(mbuf, uint8_t*);
    ln_udp_encode(pktdata, msg, total_len);

    return mbuf;
}

#endif

/*
 * 函数名称: main
 * 作    者: 黄彦杰 Lenn
 * 设计日期: 2024-07-17
 * 功能描述: while(1)主循环,收发
 * 返 回 值: 0
*/
int main(int argc, char* argv[]) {

    rte_eal_init(argc, argv);

    struct rte_mempool* mbuf_pool = rte_pktmbuf_pool_create("mbuf pool", NUM_MBUFS, 0, 0,NUM_MBUFS, rte_socket_id());

    if(mbuf_pool == NULL) {

    rte_exit(EXIT_FAILURE, "Membuf pool create failed");
    }

    ln_init_port(mbuf_pool);

    while (1) {

        struct rte_mbuf* mbufs[BURST_SIZE];

        int nb_recv = rte_eth_rx_burst(nDevPortId, 0, mbufs, BURST_SIZE);

        int i = 0;
        for (i = 0; i < nb_recv; i++) {

            struct rte_ether_hdr* ehdr = rte_pktmbuf_mtod(mbufs[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(mbufs[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);

#if UDP_ENABLE

                rte_memcpy(nSrcMac, ehdr->d_addr.addr_bytes, RTE_ETHER_ADDR_LEN);
                rte_memcpy(nDstMac, ehdr->s_addr.addr_bytes, RTE_ETHER_ADDR_LEN);
                rte_memcpy(&nSrcIp, &iphdr->dst_addr, sizeof(uint32_t));
                rte_memcpy(&nDstIp, &iphdr->src_addr, sizeof(uint32_t));
                nSrcPort = ntohs(udphdr->dst_port);
                nDstPort = ntohs(udphdr->src_port);

#endif

                int length = ntohs(udphdr->dgram_len);
                *((char*)udphdr + length) = '\0';

                struct in_addr addr;
                addr.s_addr = iphdr->src_addr;
                printf("---> udp pkt src %s:%d, ", inet_ntoa(addr), ntohs(udphdr->src_port));

                addr.s_addr = iphdr->dst_addr;
                printf("dst %s:%d %s\n", inet_ntoa(addr), ntohs(udphdr->dst_port), (char*)(udphdr + 1));

#if UDP_ENABLE

                struct rte_mbuf* txbuf = ln_udp_send(mbuf_pool, (uint8_t*)(udphdr + 1), length - sizeof(struct rte_udp_hdr));
                rte_eth_tx_burst(nDevPortId, 0, &txbuf, 1);
                rte_pktmbuf_free(txbuf);

#endif

                rte_pktmbuf_free(mbufs[i]);
            }
        }
    }


    return 0;
}

主要函数分析

rte_pktmbuf_alloc

rte_pktmbuf_alloc是DPDK库中用于分配packet buffer(pktmbuf)的函数。pktmbuf是一种用于存储网络数据包的数据结构,它包含了网络包的元数据信息和对应的数据缓冲区。rte_pktmbuf_alloc函数会从内存池中获取一个空闲的pktmbuf,并返回指向该pktmbuf的指针。开发人员可以使用这个指针来操作和处理接收到或要发送的网络数据包。

static inline struct rte_mbuf rte_pktmbuf_alloc(struct rte_mempool mp)

  • mp:内存池

调试

初始化bug

https://imagehyj.oss-cn-hangzhou.aliyuncs.com/blog/20240718112917.png

static void ln_init_port(struct rte_mempool* mbuf_pool) {

    int nb_sys_port = rte_eth_dev_count_avail();

    if (nb_sys_port == 0) {

        rte_exit(EXIT_FAILURE, "No available system port");
    }

    struct rte_eth_dev_info dev_info;
    rte_eth_dev_info_get(nDevPortId, &dev_info);

    int nb_rx_queues = 1;
    int nb_tx_queues = 1;
    struct rte_eth_conf port_conf = port_dev_conf_default;
    rte_eth_dev_configure(nDevPortId, nb_rx_queues, nb_tx_queues, &port_conf);
    if (rte_eth_rx_queue_setup(nDevPortId, 0, 1024, rte_eth_dev_socket_id(nDevPortId), NULL, mbuf_pool) < 0) {

        rte_exit(EXIT_FAILURE, "Setup rx queue failed");
    }

#if UDP_ENABLE

    struct rte_eth_txconf txq_conf = dev_info.default_txconf;
    txq_conf.offloads = port_conf.rxmode.offloads;
    if (rte_eth_tx_queue_setup(nDevPortId, 0, 1024, rte_eth_dev_socket_id(nDevPortId), &txq_conf) < 0) {

        rte_exit(EXIT_FAILURE, "Setup tx queue falied");
    }

#endif

    if (rte_eth_dev_start(nDevPortId) < 0) {

        rte_exit(EXIT_FAILURE, "Could not start eth dev");
    }
}

https://imagehyj.oss-cn-hangzhou.aliyuncs.com/blog/20240718113205.png

没有返回数据

https://imagehyj.oss-cn-hangzhou.aliyuncs.com/blog/20240718113443.png

https://imagehyj.oss-cn-hangzhou.aliyuncs.com/blog/20240718113741.png

https://imagehyj.oss-cn-hangzhou.aliyuncs.com/blog/20240718113857.png

https://imagehyj.oss-cn-hangzhou.aliyuncs.com/blog/20240718115927.png

演示效果

https://imagehyj.oss-cn-hangzhou.aliyuncs.com/blog/20240718120007.png

debug后的代码

/*
 * 版权所有: Copyright (c) 2024-2025  XXX Company. All rights reserved.
 * 系统名称: 
 * 文件名称: roboteth-send.c
 * 内容摘要: 简要描述本文件的内容,包括主要模块、函数及其功能的说明
 * 作     者: 黄彦杰 Lenn
 * 设计日期: 2024-07-18
*/

#include <rte_ethdev.h>
#include <rte_eal.h>
#include <stdio.h>
#include <arpa/inet.h>

#define NUM_MBUFS (4096-1)
#define BURST_SIZE 32

#ifndef PKT_OFFLOAD
#define PKT_OFFLOAD

#define UDP_ENABLE 1

#endif

#if UDP_ENABLE

static uint8_t nSrcMac[RTE_ETHER_ADDR_LEN];
static uint8_t nDstMac[RTE_ETHER_ADDR_LEN];
static uint32_t nSrcIp;
static uint32_t nDstIp;
static uint16_t nSrcPort;
static uint16_t nDstPort;

#endif

static const struct rte_eth_conf port_dev_conf_default = {

    .rxmode = {.max_rx_pkt_len = RTE_ETHER_MAX_LEN}
};

int nDevPortId = 0;


/*
 * 函数名称: ln_init_port
 * 作     者:  黄彦杰 Lenn
 * 设计日期: 2024-07-17
 * 功能描述: 初始化虚拟网口,配置收发队列
 * 返 回 值: None
*/
static void ln_init_port(struct rte_mempool* mbuf_pool) {

    int nb_sys_port = rte_eth_dev_count_avail();

    if (nb_sys_port == 0) {

        rte_exit(EXIT_FAILURE, "No available system port");
    }

    struct rte_eth_dev_info dev_info;
    rte_eth_dev_info_get(nDevPortId, &dev_info);

    int nb_rx_queues = 1;
    int nb_tx_queues = 1;
    struct rte_eth_conf port_conf = port_dev_conf_default;
    rte_eth_dev_configure(nDevPortId, nb_rx_queues, nb_tx_queues, &port_conf);
    if (rte_eth_rx_queue_setup(nDevPortId, 0, 1024, rte_eth_dev_socket_id(nDevPortId), NULL, mbuf_pool) < 0) {

        rte_exit(EXIT_FAILURE, "Setup rx queue failed");
    }

#if UDP_ENABLE

    struct rte_eth_txconf txq_conf = dev_info.default_txconf;
    txq_conf.offloads = port_conf.rxmode.offloads;
    if (rte_eth_tx_queue_setup(nDevPortId, 0, 1024, rte_eth_dev_socket_id(nDevPortId), &txq_conf) < 0) {

        rte_exit(EXIT_FAILURE, "Setup tx queue falied");
    }

#endif

    if (rte_eth_dev_start(nDevPortId) < 0) {

        rte_exit(EXIT_FAILURE, "Could not start eth dev");
    }
}

#if UDP_ENABLE


/*
 * 函数名称: ln_udp_encode
 * 作     者: 黄彦杰 Lenn
 * 设计日期: 2024-07-18
 * 功能描述: 组织udp数据包
 * 返 回 值: 0
*/
static int ln_udp_encode(uint8_t* pktbuf, uint8_t* data, uint16_t total_len) {

    struct rte_ether_hdr* ehdr = (struct rte_ether_hdr*)pktbuf;
    rte_memcpy(ehdr->s_addr.addr_bytes, nSrcMac, RTE_ETHER_ADDR_LEN);
    rte_memcpy(ehdr->d_addr.addr_bytes, nDstMac, RTE_ETHER_ADDR_LEN);
    ehdr->ether_type = htons(RTE_ETHER_TYPE_IPV4);

    struct rte_ipv4_hdr* iphdr = (struct rte_ipv4_hdr*)(ehdr + 1);
    iphdr->version_ihl = 0x45;
    iphdr->type_of_service = 0;
    iphdr->total_length = htons(total_len - sizeof(struct rte_ether_hdr));
    iphdr->packet_id = htons(0);
    iphdr->fragment_offset = htons(0);
    iphdr->time_to_live = 64;
    iphdr->next_proto_id = IPPROTO_UDP;
    iphdr->src_addr = nSrcIp;
    iphdr->dst_addr = nDstIp;
    iphdr->hdr_checksum = 0;
    iphdr->hdr_checksum = rte_ipv4_cksum(iphdr);

    struct rte_udp_hdr* udphdr = (struct rte_udp_hdr*)(iphdr + 1);
    udphdr->src_port = nSrcPort;
    udphdr->dst_port = nDstPort;
    uint16_t udp_len = total_len - sizeof(struct rte_ether_hdr) - sizeof(struct rte_ipv4_hdr);
    udphdr->dgram_len = htons(udp_len);
    rte_memcpy((uint8_t*)(udphdr + 1), data, udp_len - sizeof(struct rte_udp_hdr));
    udphdr->dgram_cksum = 0;
    udphdr->dgram_cksum = rte_ipv4_udptcp_cksum(iphdr, udphdr);

    return 0;
}

/*
 * 函数名称: ln_udp_send
 * 作     者: 黄彦杰 Lenn
 * 设计日期: 2024-07-18
 * 功能描述: udp数据包发送
 * 返 回 值: 存储udp数据包的一段内存
*/
static struct rte_mbuf* ln_udp_send(struct rte_mempool* mbuf_pool, uint8_t* msg, uint16_t length) {

    const unsigned total_len = length + 42;
    struct rte_mbuf* mbuf = rte_pktmbuf_alloc(mbuf_pool);
    if (!mbuf) {

        rte_exit(EXIT_FAILURE, "Alloc udp mbuf failed");
    }
    mbuf->pkt_len = total_len;
    mbuf->data_len = total_len;

    uint8_t* pktdata = rte_pktmbuf_mtod(mbuf, uint8_t*);
    ln_udp_encode(pktdata, msg, total_len);

    return mbuf;
}

#endif

/*
 * 函数名称: main
 * 作    者: 黄彦杰 Lenn
 * 设计日期: 2024-07-17
 * 功能描述: while(1)主循环,收发
 * 返 回 值: 0
*/
int main(int argc, char* argv[]) {

    rte_eal_init(argc, argv);

    struct rte_mempool* mbuf_pool = rte_pktmbuf_pool_create("mbuf pool", NUM_MBUFS, 0, 0,NUM_MBUFS, rte_socket_id());

    if(mbuf_pool == NULL) {

    rte_exit(EXIT_FAILURE, "Membuf pool create failed");
    }

    ln_init_port(mbuf_pool);

    while (1) {

        struct rte_mbuf* mbufs[BURST_SIZE];

        int nb_recv = rte_eth_rx_burst(nDevPortId, 0, mbufs, BURST_SIZE);

        int i = 0;
        for (i = 0; i < nb_recv; i++) {

            struct rte_ether_hdr* ehdr = rte_pktmbuf_mtod(mbufs[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(mbufs[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);

#if UDP_ENABLE

                rte_memcpy(nSrcMac, ehdr->d_addr.addr_bytes, RTE_ETHER_ADDR_LEN);
                rte_memcpy(nDstMac, ehdr->s_addr.addr_bytes, RTE_ETHER_ADDR_LEN);
                rte_memcpy(&nSrcIp, &iphdr->dst_addr, sizeof(uint32_t));
                rte_memcpy(&nDstIp, &iphdr->src_addr, sizeof(uint32_t));
                nSrcPort = udphdr->dst_port;
                nDstPort = udphdr->src_port;

#endif

                int length = ntohs(udphdr->dgram_len);
                *((char*)udphdr + length) = '\0';

                struct in_addr addr;
                addr.s_addr = iphdr->src_addr;
                printf("---> udp pkt src %s:%d, ", inet_ntoa(addr), ntohs(udphdr->src_port));

                addr.s_addr = iphdr->dst_addr;
                printf("dst %s:%d %s\n", inet_ntoa(addr), ntohs(udphdr->dst_port), (char*)(udphdr + 1));

#if UDP_ENABLE

                struct rte_mbuf* txbuf = ln_udp_send(mbuf_pool, (uint8_t*)(udphdr + 1), length - sizeof(struct rte_udp_hdr));
                rte_eth_tx_burst(nDevPortId, 0, &txbuf, 1);
                rte_pktmbuf_free(txbuf);

#endif

                rte_pktmbuf_free(mbufs[i]);
            }
        }
    }


    return 0;
}

项目地址

roboteth-send