以下是基于DPDK的简单分片重组示例代码:
#include <stdio.h>
#include <string.h>
#include <stdint.h>
#include <rte_eal.h>
#include <rte_ethdev.h>
#include <rte_mbuf.h>
#define MAX_PKT_BURST 32
#define MAX_FRAGMENTS 4
struct ipv4_hdr {
uint8_t version_ihl;
uint8_t type_of_service;
uint16_t total_length;
uint16_t packet_id;
uint16_t fragment_offset;
uint8_t time_to_live;
uint8_t next_proto_id;
uint16_t hdr_checksum;
uint32_t src_addr;
uint32_t dst_addr;
} __attribute__((__packed__));
static struct rte_mempool *pktmbuf_pool;
// Helper function to calculate the IPv4 header checksum
static inline void ipv4_hdr_cksum(struct ipv4_hdr *hdr) {
hdr->hdr_checksum = 0;
int len = (hdr->version_ihl & 0x0F) << 2; // Length of the IPv4 header in bytes
uint32_t sum = 0;
for (int i = 0; i < len; i += 2)
sum += ((uint16_t *)hdr)[i / 2];
while (sum >> 16)
sum = (sum & 0xFFFF) + (sum >> 16);
hdr->hdr_checksum = ~sum;
}
// Function to reassemble fragmented packets
static void reassemble_packet(struct rte_mbuf **fragments, int num_fragments) {
struct ipv4_hdr *first_frag_hdr = rte_pktmbuf_mtod(fragments[0], struct ipv4_hdr *);
if ((first_frag_hdr->fragment_offset & htons(0x1FFF)) != 0) {
// The first fragment doesn't have a zero offset, so it's not the first fragment
printf("Error: not the first fragment\n");
return;
}
int total_length = ntohs(first_frag_hdr->total_length);
int hdr_len = (first_frag_hdr->version_ihl & 0x0F) << 2;
int payload_len = total_length - hdr_len;
if (payload_len > (num_fragments - 1) * rte_pktmbuf_data_room_size()) {
// The reassembled packet would be too large to fit in one mbuf
printf("Error: packet too large\n");
return;
}
struct rte_mbuf *pkt = rte_pktmbuf_alloc(pktmbuf_pool);
if (!pkt) {
printf("Error: could not allocate mbuf for reassembled packet\n");
return;
}
uint8_t *pkt_data = rte_pktmbuf_mtod(pkt, uint8_t *);
memcpy(pkt_data, rte_pktmbuf_mtod(fragments[0], void *), hdr_len); // Copy the header
uint8_t *payload_ptr = pkt_data + hdr_len; // Pointer to the start of the payload
int payload_bytes_copied = 0; // Number of bytes of payload copied so far
for (int i = 0; i < num_fragments; i++) {
struct ipv4_hdr *frag_hdr = rte_pktmbuf_mtod(fragments[i], struct ipv4_hdr *);
if ((frag_hdr->fragment_offset & htons(0x1FFF)) != (i * MAX_FRAGMENTS)) {
// This is not the correct offset for this fragment
printf("Error: incorrect fragment offset\n");
rte_pktmbuf_free(pkt);
return;
}
int frag_payload_len = ntohs(frag_hdr->total_length) - ((frag_hdr->version_ihl & 0x0F) << 2);
uint8_t *frag_payload_ptr = rte_pktmbuf_mtod_offset(fragments[i], uint8_t *, (frag_hdr->version_ihl & 0x0F) << 2); // Pointer to the start of the payload in this fragment
memcpy(payload_ptr, frag_payload_ptr, frag_payload_len); // Copy the payload from this fragment
payload_bytes_copied += frag_payload_len;
if (payload_bytes_copied >= payload_len)
break; // We've copied all the payload bytes we need
}
pkt->pkt_len = total_length;
pkt->data_len = total_length;
ipv4_hdr_cksum((struct ipv4_hdr *)pkt_data); // Recalculate the checksum
printf("Reassembled packet with ID %d\n", ntohs(first_frag_hdr->packet_id));
}
int main(int argc, char *argv[]) {
int ret = rte_eal_init(argc, argv);
if (ret < 0)
return -1;
argc -= ret;
argv += ret;
if (argc != 3) {
printf("Usage: %s <port_id> <num_queues>\n", argv[0]);
return -1;
}
uint16_t port_id = atoi(argv[1]);
uint16_t num_queues = atoi(argv[2]);
if (!rte_eth_dev_is_valid_port(port_id)) {
printf("Error: invalid port number\n");
return -1;
}
pktmbuf_pool = rte_pktmbuf_pool_create("MBUF_POOL", 8192,
32 /* cache size */, 0, RTE_MBUF_DEFAULT_BUF_SIZE, rte_socket_id());
if (!pktmbuf_pool) {
printf("Error: could not create mbuf pool\n");
return -1;
}
ret = rte_eth_dev_configure(port_id, num_queues, num_queues, NULL);
if (ret < 0) {
printf("Error: could not configure port %u\n", port_id);
return -1;
}
for (int i = 0; i < num_queues; i++) {
ret = rte_eth_rx_queue_setup(port_id, i, MAX_PKT_BURST,
rte_eth_dev_socket_id(port_id), NULL, pktmbuf_pool);
if (ret < 0) {
printf("Error: could not setup RX queue %d on port %u\n", i, port_id);
return -1;
}
}
ret = rte_eth_dev_start(port_id);
if (ret < 0) {
printf("Error: could not start port %u\n", port_id);
return -1;
}
struct rte_mbuf *pkts_burst[MAX_PKT_BURST];
while (1) {
for (int qid = 0; qid < num_queues; qid++) {
int num_rx = rte_eth_rx_burst(port_id, qid, pkts_burst, MAX_PKT_BURST);
for (int i = 0; i < num_rx; i++) {
struct ipv4_hdr *ipv4_hdr = rte_pktmbuf_mtod_offset(pkts_burst[i], struct ipv4_hdr *, sizeof(struct ether_hdr));
if ((ipv4_hdr->version_ihl >> 4) != 4 || ipv4_hdr->next_proto_id != IPPROTO_UDP)
continue; // Not an IPv4 or UDP packet
int num_frags = (ntohs(ipv4_hdr->total_length) - sizeof(struct ipv4_hdr)) / rte_pktmbuf_data_room_size() + 1;
if (num_frags <= 1)
continue; // Not a fragmented packet
struct rte_mbuf *fragments[MAX_FRAGMENTS];
for (int j = 0; j < num_frags; j++) {
fragments[j] = rte_pktmbuf_alloc(pktmbuf_pool);
if (!fragments[j]) {
printf("Error: could not allocate mbuf for fragment\n");
for (int k = 0; k < j; k++)
rte_pktmbuf_free(fragments[k]);
goto next_packet;
}
uint8_t *pkt_data = rte_pktmbuf_mtod(pkts_burst[i], uint8_t *);
uint32_t frag_offset = ((uint16_t)(pkt_data[6]) << 8) | pkt_data[7]; // Offset in bytes from the start of the original packet
uint32_t frag_len = ntohs(((uint16_t *)(ipv4_hdr + 1))[j]); // Length of this fragment's payload
memcpy(rte_pktmbuf_mtod(fragments[j], void *), pkt_data + sizeof(struct ipv4_hdr) + frag_offset, frag_len); // Copy the payload from the original packet
struct ipv4_hdr *frag_hdr = rte_pktmbuf_mtod(fragments[j], struct ipv4_hdr *);
frag_hdr->fragment_offset &= htons(0xE000); // Clear lower 13 bits of fragment offset
frag_hdr->fragment_offset |= htons(j * MAX_FRAGMENTS); // Set the correct fragment offset for this fragment
ipv4_hdr_cksum(frag_hdr); // Recalculate the checksum
fragments[j]->pkt_len = sizeof(struct ipv4_hdr) + frag_len;
fragments[j]->data_len = sizeof(struct ipv4_hdr) + frag_len;
}
reassemble_packet(fragments, num_frags);
next_packet:
rte_pktmbuf_free(pkts_burst[i]);
}
}
}
return 0;
}
该示例代码包含以下几个关键部分:
reassemble_packet
函数:用于将分片重组成原始数据包。该函数需要接收一个指向所有碎片的rte_mbuf
结构体的指针数组以及碎片数作为参数,并返回重新组装后的完整数据包。在主循环中,从DPDK驱动程序中接收分组并检查它们是否是IPV4和UDP协议。如果它们是分段的,则使用
reassemble_packet
函数将它们重组回完整的数据包。ipv4_hdr_cksum
函数:用于计算IPv4头校验和。