Featured image of post DPDK控制消息协议ICMP的实现案例

DPDK控制消息协议ICMP的实现案例

在DPDK中实现ICMP协议

核心框架

核心框架与“DPDK地址解析协议ARP的实现案例”基本一致,可参考:DPDK地址解析协议ARP的实现案例 ,本代码案例在ARP的基础上增加了对ICMP协议的处理。

数据包处理子线程

数据包分发

process_packet()函数用于处理接收到的网络数据包。首先通过rte_pktmbuf_mtod()获取以太网头部,并检查以太网类型字段是否为IPv4(0x0800)。若是IPv4数据包,则继续提取IP头部,进一步判断其协议字段是否为ICMP。如果确认是ICMP报文,根据IP头部中的头部长度定位ICMP头部,并调用handle_ping()函数进行处理,实现对ICMP请求的应答逻辑。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
void process_packet(struct rte_mbuf *mbuf)
{
    // Get the Ethernet header from mbuf
    struct rte_ether_hdr *eth_hdr = rte_pktmbuf_mtod(mbuf, struct rte_ether_hdr *);

    // ......

    // Check if it's an IP packet (EtherType == 0x0800)
    if (rte_be_to_cpu_16(eth_hdr->ether_type) == RTE_ETHER_TYPE_IPV4)
    {
        struct rte_ipv4_hdr *ip_hdr = (struct rte_ipv4_hdr *)(eth_hdr + 1);
        // Check if it is an ICMP packet
        if (ip_hdr->next_proto_id == IPPROTO_ICMP)
        {
            // Get ICMP header
            uint8_t ihl = (ip_hdr->version_ihl & 0x0f) * 4;
            struct rte_icmp_hdr *icmp_hdr = (struct rte_icmp_hdr *)((uint8_t *)ip_hdr + ihl);
            handle_ping(icmp_hdr, ip_hdr, eth_hdr);
            return;
        }
    }
}

handle_ping()函数判断ICMP报文的类型字段是否为ICMP_ECHO。如果是,则说明收到了一个ICMP Echo Request,随后调用handle_ping_echo_packet()函数,对该Ping请求进行处理和应答。

1
2
3
4
5
6
7
8
void handle_ping(struct rte_icmp_hdr *icmp_hdr, struct rte_ipv4_hdr *ip_hdr, struct rte_ether_hdr *eth_hdr)
{
    if (icmp_hdr->icmp_type == ICMP_ECHO) // "RTE_IP_ICMP_ECHO_REQUEST" is deprecated
    {
        // ICMP Echo Request received
        handle_ping_echo_packet(icmp_hdr, ip_hdr, eth_hdr);
    }
}

ICMP Ping

构造ICMP Ping应答报文并发送:

  1. 计算ICMP报文长度:由于ICMP报文会携带负载,其长度需要动态计算,将根据IP报文总长度减去IP报文头部长度,得到ICMP报文的长度;
  2. 分配mbuf并申请空间;
  3. 构建以太网头部、IP头部;
  4. 构建ICMP:为了简化构建过程,直接将接收到的Echo Request报文(包括头部和负载)拷贝到新分配的应答报文中,这样可以保留原始请求的标识符icmp_ident、序列号icmp_seq_nb、负载,用于应答的匹配;随后修改报文类型icmp_type和代码字段icmp_code为ICMP_ECHOREPLY(Echo Reply)和0(Echo Reply的唯一合法值);最后更新校验和字段,rte_raw_cksum()函数将计算整个ICMP报文的校验和,然后取反作为最终的校验和;
  5. 将构造好的应答报文入队发送。
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
void handle_ping_echo_packet(struct rte_icmp_hdr *icmp_hdr, struct rte_ipv4_hdr *ip_hdr, struct rte_ether_hdr *eth_hdr)
{
    // Calculate ICMP length
    uint16_t ip_hdr_len = (ip_hdr->version_ihl & 0x0F) * 4;
    uint16_t ip_total_len = rte_be_to_cpu_16(ip_hdr->total_length);
    uint16_t icmp_len = ip_total_len - ip_hdr_len;

    // Allocate new mbuf for reply
    struct rte_mbuf *reply_mbuf = rte_pktmbuf_alloc(mempool);
    if (!reply_mbuf)
    {
        printf("[PING] Failed to allocate mbuf for ICMP reply\n");
        return;
    }

    // Total packet length = Ethernet + IP + ICMP
    uint16_t pkt_len = sizeof(struct rte_ether_hdr) + ip_hdr_len + icmp_len;
    char *pkt_data = rte_pktmbuf_append(reply_mbuf, pkt_len);
    if (!pkt_data)
    {
        printf("[PING] Failed to append data for ICMP reply\n");
        rte_pktmbuf_free(reply_mbuf);
        return;
    }

    // Construct headers
    struct rte_ether_hdr *r_eth_hdr = (struct rte_ether_hdr *)pkt_data;
    struct rte_ipv4_hdr *r_ip_hdr = (struct rte_ipv4_hdr *)(r_eth_hdr + 1);
    struct rte_icmp_hdr *r_icmp_hdr = (struct rte_icmp_hdr *)((char *)r_ip_hdr + sizeof(struct rte_ipv4_hdr));

    // Ethernet header
    rte_ether_addr_copy(&eth_hdr->src_addr, &r_eth_hdr->dst_addr);
    rte_ether_addr_copy(local_mac, &r_eth_hdr->src_addr);
    r_eth_hdr->ether_type = rte_cpu_to_be_16(RTE_ETHER_TYPE_IPV4);

    // IPv4 header
    memset(r_ip_hdr, 0, sizeof(struct rte_ipv4_hdr));
    r_ip_hdr->version_ihl = 0x45;
    r_ip_hdr->total_length = rte_cpu_to_be_16(ip_hdr_len + icmp_len);
    r_ip_hdr->time_to_live = 64;
    r_ip_hdr->next_proto_id = IPPROTO_ICMP;
    r_ip_hdr->src_addr = ip_hdr->dst_addr;
    r_ip_hdr->dst_addr = ip_hdr->src_addr;
    r_ip_hdr->hdr_checksum = 0;
    r_ip_hdr->hdr_checksum = rte_ipv4_cksum(r_ip_hdr);

    // ICMP header and payload
    memcpy(r_icmp_hdr, icmp_hdr, icmp_len);
    r_icmp_hdr->icmp_type = ICMP_ECHOREPLY;
    r_icmp_hdr->icmp_code = 0;
    r_icmp_hdr->icmp_ident = icmp_hdr->icmp_ident;
    r_icmp_hdr->icmp_seq_nb = icmp_hdr->icmp_seq_nb;
    r_icmp_hdr->icmp_cksum = 0;
    r_icmp_hdr->icmp_cksum = ~rte_raw_cksum(r_icmp_hdr, icmp_len);

    // Enqueue reply
    if (rte_ring_enqueue(send_ring, reply_mbuf) < 0)
    {
        printf("[PING] Failed to enqueue ICMP reply\n");
        rte_pktmbuf_free(reply_mbuf);
    }
    else
    {
        struct in_addr dst;
        dst.s_addr = r_ip_hdr->dst_addr;
        printf("[PING] ICMP reply sent to %s (%u bytes)\n", inet_ntoa(dst), pkt_len);
    }
}

程序测试

主机B编译运行该程序,随后主机A运行Ping主机B。

1
2
3
4
sudo make clean
sudo make
cd build
sudo ./arp_icmp

源码

icmp.c

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
#include "icmp.h"

extern struct rte_mempool *mempool;
extern struct rte_ring *send_ring;
extern struct rte_ring *recv_ring;

extern struct rte_ether_addr *local_mac;

void handle_ping_echo_packet(struct rte_icmp_hdr *icmp_hdr, struct rte_ipv4_hdr *ip_hdr, struct rte_ether_hdr *eth_hdr)
{
    // Calculate ICMP length
    uint16_t ip_hdr_len = (ip_hdr->version_ihl & 0x0F) * 4;
    uint16_t ip_total_len = rte_be_to_cpu_16(ip_hdr->total_length);
    uint16_t icmp_len = ip_total_len - ip_hdr_len;

    // Allocate new mbuf for reply
    struct rte_mbuf *reply_mbuf = rte_pktmbuf_alloc(mempool);
    if (!reply_mbuf)
    {
        printf("[PING] Failed to allocate mbuf for ICMP reply\n");
        return;
    }

    // Total packet length = Ethernet + IP + ICMP
    uint16_t pkt_len = sizeof(struct rte_ether_hdr) + ip_hdr_len + icmp_len;
    char *pkt_data = rte_pktmbuf_append(reply_mbuf, pkt_len);
    if (!pkt_data)
    {
        printf("[PING] Failed to append data for ICMP reply\n");
        rte_pktmbuf_free(reply_mbuf);
        return;
    }

    // Construct headers
    struct rte_ether_hdr *r_eth_hdr = (struct rte_ether_hdr *)pkt_data;
    struct rte_ipv4_hdr *r_ip_hdr = (struct rte_ipv4_hdr *)(r_eth_hdr + 1);
    struct rte_icmp_hdr *r_icmp_hdr = (struct rte_icmp_hdr *)((char *)r_ip_hdr + sizeof(struct rte_ipv4_hdr));

    // Ethernet header
    rte_ether_addr_copy(&eth_hdr->src_addr, &r_eth_hdr->dst_addr);
    rte_ether_addr_copy(local_mac, &r_eth_hdr->src_addr);
    r_eth_hdr->ether_type = rte_cpu_to_be_16(RTE_ETHER_TYPE_IPV4);

    // IPv4 header
    memset(r_ip_hdr, 0, sizeof(struct rte_ipv4_hdr));
    r_ip_hdr->version_ihl = 0x45;
    r_ip_hdr->total_length = rte_cpu_to_be_16(ip_hdr_len + icmp_len);
    r_ip_hdr->time_to_live = 64;
    r_ip_hdr->next_proto_id = IPPROTO_ICMP;
    r_ip_hdr->src_addr = ip_hdr->dst_addr;
    r_ip_hdr->dst_addr = ip_hdr->src_addr;
    r_ip_hdr->hdr_checksum = 0;
    r_ip_hdr->hdr_checksum = rte_ipv4_cksum(r_ip_hdr);

    // ICMP header and payload
    memcpy(r_icmp_hdr, icmp_hdr, icmp_len);
    r_icmp_hdr->icmp_type = ICMP_ECHOREPLY;
    r_icmp_hdr->icmp_code = 0;
    r_icmp_hdr->icmp_ident = icmp_hdr->icmp_ident;
    r_icmp_hdr->icmp_seq_nb = icmp_hdr->icmp_seq_nb;
    r_icmp_hdr->icmp_cksum = 0;
    r_icmp_hdr->icmp_cksum = ~rte_raw_cksum(r_icmp_hdr, icmp_len);

    // Enqueue reply
    if (rte_ring_enqueue(send_ring, reply_mbuf) < 0)
    {
        printf("[PING] Failed to enqueue ICMP reply\n");
        rte_pktmbuf_free(reply_mbuf);
    }
    else
    {
        struct in_addr dst;
        dst.s_addr = r_ip_hdr->dst_addr;
        printf("[PING] ICMP reply sent to %s (%u bytes)\n", inet_ntoa(dst), pkt_len);
    }
}

void handle_ping(struct rte_icmp_hdr *icmp_hdr, struct rte_ipv4_hdr *ip_hdr, struct rte_ether_hdr *eth_hdr)
{
    if (icmp_hdr->icmp_type == ICMP_ECHO) // "RTE_IP_ICMP_ECHO_REQUEST" is deprecated
    {
        // ICMP Echo Request received
        handle_ping_echo_packet(icmp_hdr, ip_hdr, eth_hdr);
    }
}

源码打包下载

源码打包下载:dpdk_arp_icmp.zip

Licensed under CC BY-NC-SA 4.0
皖ICP备2025083746号-1
公安备案 陕公网安备61019002003315号



使用 Hugo 构建
主题 StackJimmy 设计