本代码利用dpdk实现一个发包器,每2秒发送64个COLOR_GET数据包。
由于dpdk直接通过物理层发送数据包,需要写代码发数据帧格式如下:
其中rte_ether_hdr和rte_ipv4_hdr在dpdk的lib中均有封装的struct,其结构如下:
struct rte_ether_hdr {
struct rte_ether_addr d_addr; /**< Destination address. */
struct rte_ether_addr s_addr; /**< Source address. */
uint16_t ether_type; /**< Frame type. */
} __attribute__((aligned(2)));
struct rte_ipv4_hdr {
uint8_t version_ihl; /**< version and header length */
uint8_t type_of_service; /**< type of service */
rte_be16_t total_length; /**< length of packet */
rte_be16_t packet_id; /**< packet ID */
rte_be16_t fragment_offset; /**< fragmentation offset */
uint8_t time_to_live; /**< time to live */
uint8_t next_proto_id; /**< protocol ID */
rte_be16_t hdr_checksum; /**< header checksum */
rte_be32_t src_addr; /**< source address */
rte_be32_t dst_addr; /**< destination address */
} __attribute__((__packed__));
COLOR是ICN体系结构的一种网络,Customer端如果需要获取数据,需要发送get包进行请求,其包格式如下:
结构体定义如下:
#define CONTENTLEN 4
#define PUBKEYLEN 4
struct CoLoR_get
{
uint8_t version_type; //版本4位,类型4位 1
uint8_t ttl; //生存时间 1
uint16_t total_len; //总长度 2
uint16_t port_src; //源端口号 2
uint16_t port_dst; //目的端口号 2
uint16_t minmal_PID_CP; //pid改变的周期 2
uint8_t PIDs; //PID的数目 1
uint8_t Offest_RES; //位运算取Offset 1
uint32_t offset; //偏移量 4
uint32_t length; //偏移长度 4
uint16_t content_len; //公钥长度 2
uint16_t mtu; //最大传输单元 2
uint16_t publickey_len; //公钥长度 2
uint16_t checksum; //检验和 2
uint8_t n_sid[16]; //NID part of an SID,长度为16字节 16*8=128 16
uint8_t l_sid[20]; //SID的长度为20字节 20*8 = 160 20
uint8_t nid[16]; //NID的长度为16字节 16
uint8_t content[CONTENTLEN]; // Content characteristics 4
uint8_t publickey[PUBKEYLEN]; //公钥 4
}__attribute__((__packed__));
typedef struct CoLoR_get CoLoR_get_t;
/* SPDX-License-Identifier: BSD-3-Clause
* Copyright(c) 2010-2015 Intel Corporation
* Author: Lianpeng
*/
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define RX_RING_SIZE 1024
#define TX_RING_SIZE 1024
#define NUM_MBUFS 8191
#define MBUF_CACHE_SIZE 250
#define BURST_SIZE 32
#define TXONLY_DEF_PACKET_LEN 64
//IP_SRC_ADDR表示虚拟机对应网卡的IP地址(192.168.73.3)
//IP_DST_ADDR表示宿主机对应的IP地址(192.168.73.1)
//在VirtualBox中网卡enp0s8采用Host-Only的连接模式
//这种模式下宿主机和虚拟机都能ping通
#define IP_SRC_ADDR ((192U << 24) | (168 << 16) | (73 << 8) | 3)
#define IP_DST_ADDR ((192U << 24) | (168 << 16) | (73 << 8) | 1)
//IP数据包必填部分
#define IP_DEFTTL 64 /* from RFC 1340. */
#define IP_VERSION 0x40
#define IP_HDRLEN 0x05 /* default IP header length == five 32-bits words. */
#define IP_VHL_DEF (IP_VERSION | IP_HDRLEN)
//COLOR_get包对应端口的设置
#define COLOR_SRC_PORT 9191
#define COLOR_DEST_PORT 9191
#define PKG_GEN_COUNT 64
static struct rte_ipv4_hdr pkt_ip_hdr;
static struct CoLoR_get pkt_colorget_hdr;
static struct rte_ether_hdr pkt_eth_hdr;
static const struct rte_eth_conf port_conf_default = {
.rxmode = {
.max_rx_pkt_len = RTE_ETHER_MAX_LEN,
},
};
static void setup_pkt_color_ip_headers(struct rte_ipv4_hdr *ip_hdr, struct CoLoR_get *color_hdr);
//设置COLOR_GET和IP数据报报头
static inline void show_mac(const char *info,struct rte_ether_addr *addr );
//显示addr指向的MAC地址
static inline void display_rx_mac(struct rte_mbuf **bufs, uint16_t nb_mbufs);
//显示获取到的数据包的SRC和DEST MAC地址
static inline int port_init(uint16_t port, struct rte_mempool *mbuf_pool);
//初始化网口
static void copy_buf_to_pkt_segs(void* buf, unsigned len, struct rte_mbuf *pkt, unsigned offset);
//如果mbuf分配的第一个片段长度不够,需要对mbuf的下一个结点进行赋值
static inline void copy_buf_to_pkt(void* buf, unsigned len, struct rte_mbuf *pkt, unsigned offset);
//先判断是否可以直接赋给第一个片段,如果不够则需要调用copy_buf_to_pkt_segs
static inline void pkt_prepare(struct rte_mbuf *pkt);
//为生成的packet_mbuf进行数据填充
static __attribute__((noreturn)) void lcore_main(struct rte_mempool *mbuf_pool);
//主线程函数
int main(int argc, char *argv[]);
//主函数
static void
setup_pkt_color_ip_headers(struct rte_ipv4_hdr *ip_hdr,
struct CoLoR_get *color_hdr)
{
uint16_t *ptr16;
uint32_t ip_cksum;
uint16_t pkt_len;
/*
* Initialize COLOR header.
* get packet size, version_type, ttl, port_src, port_dst, check_sum
*/
pkt_len = sizeof(struct CoLoR_get);
color_hdr->length = rte_cpu_to_be_16(sizeof(struct CoLoR_get));
color_hdr->version_type = rte_cpu_to_be_16(1);
color_hdr->ttl=rte_cpu_to_be_16(88);//ttl
color_hdr->port_src=rte_cpu_to_be_16(COLOR_SRC_PORT);
color_hdr->port_dst=rte_cpu_to_be_16(COLOR_DEST_PORT);
//懒得计算color的checksum
/*
* Initialize IP header.
*
*/
pkt_len = (uint16_t) (pkt_len + sizeof(struct rte_ipv4_hdr));
ip_hdr->version_ihl = IP_VHL_DEF;
ip_hdr->type_of_service = 0;
ip_hdr->fragment_offset = 0;
ip_hdr->time_to_live = IP_DEFTTL;
ip_hdr->next_proto_id = IPPROTO_IP;//选择报头0
ip_hdr->packet_id = 0;
ip_hdr->total_length = rte_cpu_to_be_16(pkt_len);
ip_hdr->src_addr = rte_cpu_to_be_32(IP_SRC_ADDR);
ip_hdr->dst_addr = rte_cpu_to_be_32(IP_DST_ADDR);
/*
* Compute IP header checksum.
*/
ptr16 = (unaligned_uint16_t*) ip_hdr;
ip_cksum = 0;
ip_cksum += ptr16[0]; ip_cksum += ptr16[1];
ip_cksum += ptr16[2]; ip_cksum += ptr16[3];
ip_cksum += ptr16[4];
ip_cksum += ptr16[6]; ip_cksum += ptr16[7];
ip_cksum += ptr16[8]; ip_cksum += ptr16[9];
/*
* Reduce 32 bit checksum to 16 bits and complement it.
*/
ip_cksum = ((ip_cksum & 0xFFFF0000) >> 16) +
(ip_cksum & 0x0000FFFF);
if (ip_cksum > 65535)
ip_cksum -= 65535;
ip_cksum = (~ip_cksum) & 0x0000FFFF;
if (ip_cksum == 0)
ip_cksum = 0xFFFF;
ip_hdr->hdr_checksum = (uint16_t) ip_cksum;
}
/*Function: Display INFORMATION and MAC address.
* Input: info, rte_ether_addr *addr
* Output: VOID
*/
static inline void
show_mac(const char *info,struct rte_ether_addr *addr ){
printf("%s %02" PRIx8 " %02" PRIx8 " %02" PRIx8
" %02" PRIx8 " %02" PRIx8 " %02" PRIx8 "\n",
info,
(addr->addr_bytes)[0], (addr->addr_bytes)[1],
(addr->addr_bytes)[2], (addr->addr_bytes)[3],
(addr->addr_bytes)[4], (addr->addr_bytes)[5]);
}
/*Function: Display SRC and DEST MAC address after receiving some packets
* Input: bufs, nb_mbufs
* Output: VOID
*/
static inline void
display_rx_mac(struct rte_mbuf **bufs, uint16_t nb_mbufs){
struct rte_ether_hdr *eth;
struct rte_mbuf *m;
int buf_count;
for(buf_count=0;buf_count<nb_mbufs;buf_count++)
{
m=bufs[buf_count];
eth = rte_pktmbuf_mtod(m, struct rte_ether_hdr *);
show_mac("src_MAC :", &(eth->s_addr));
show_mac("dst_MAC :", &(eth->d_addr));
}
}
/*
* Initializes a given port using global settings and with the RX buffers
* coming from the mbuf_pool passed as a parameter.
*/
static inline int
port_init(uint16_t port, struct rte_mempool *mbuf_pool)
{
struct rte_eth_conf port_conf = port_conf_default;
const uint16_t rx_rings = 1, tx_rings = 1;
uint16_t nb_rxd = RX_RING_SIZE;
uint16_t nb_txd = TX_RING_SIZE;
int retval;
uint16_t q;
struct rte_eth_dev_info dev_info;
struct rte_eth_txconf txconf;
if (!rte_eth_dev_is_valid_port(port))
return -1;
retval = rte_eth_dev_info_get(port, &dev_info);
if (retval != 0) {
printf("Error during getting device (port %u) info: %s\n",
port, strerror(-retval));
return retval;
}
if (dev_info.tx_offload_capa & DEV_TX_OFFLOAD_MBUF_FAST_FREE)
port_conf.txmode.offloads |=
DEV_TX_OFFLOAD_MBUF_FAST_FREE;
/* Configure the Ethernet device. */
retval = rte_eth_dev_configure(port, rx_rings, tx_rings, &port_conf);
if (retval != 0)
return retval;
retval = rte_eth_dev_adjust_nb_rx_tx_desc(port, &nb_rxd, &nb_txd);
if (retval != 0)
return retval;
/* Allocate and set up 1 RX queue per Ethernet port. */
for (q = 0; q < rx_rings; q++) {
retval = rte_eth_rx_queue_setup(port, q, nb_rxd,
rte_eth_dev_socket_id(port), NULL, mbuf_pool);
if (retval < 0)
return retval;
}
txconf = dev_info.default_txconf;
txconf.offloads = port_conf.txmode.offloads;
/* Allocate and set up 1 TX queue per Ethernet port. */
for (q = 0; q < tx_rings; q++) {
retval = rte_eth_tx_queue_setup(port, q, nb_txd,
rte_eth_dev_socket_id(port), &txconf);
if (retval < 0)
return retval;
}
/* Start the Ethernet port. */
retval = rte_eth_dev_start(port);
if (retval < 0)
return retval;
/* Display the port MAC address. */
struct rte_ether_addr addr;
retval = rte_eth_macaddr_get(port, &addr);
if (retval != 0)
return retval;
/* Enable RX in promiscuous mode for the Ethernet device. */
retval = rte_eth_promiscuous_enable(port);
if (retval != 0)
return retval;
return 0;
}
static void
copy_buf_to_pkt_segs(void* buf, unsigned len, struct rte_mbuf *pkt,
unsigned offset)
{
struct rte_mbuf *seg;
void *seg_buf;
unsigned copy_len;
seg = pkt;
while (offset >= seg->data_len) {
offset -= seg->data_len;
seg = seg->next;
}
copy_len = seg->data_len - offset;
seg_buf = rte_pktmbuf_mtod_offset(seg, char *, offset);
while (len > copy_len) {
rte_memcpy(seg_buf, buf, (size_t) copy_len);
len -= copy_len;
buf = ((char*) buf + copy_len);
seg = seg->next;
seg_buf = rte_pktmbuf_mtod(seg, char *);
}
rte_memcpy(seg_buf, buf, (size_t) len);
}
/*Function: Memory Copy from the source header to pkt by offset
* Input: void* buf, unsigned len, struct rte_mbuf *pkt, unsigned offset
* Output: VOID
* */
static inline void
copy_buf_to_pkt(void* buf, unsigned len, struct rte_mbuf *pkt, unsigned offset)
{
if (offset + len <= pkt->data_len) {
rte_memcpy(rte_pktmbuf_mtod_offset(pkt, char *, offset),
buf, (size_t) len);
return;
}
copy_buf_to_pkt_segs(buf, len, pkt, offset);
//In gereral, it's useless.So note it.
}
/*Funciton: PrePare a COLOR_get Packet
* Input: struct rte_mbuf *pkt
* Output: VOID
*/
static inline void
pkt_prepare(struct rte_mbuf *pkt){
pkt->data_len = sizeof(struct rte_ether_hdr) + sizeof(struct rte_ipv4_hdr) + sizeof(struct CoLoR_get);
//set basic info of pkg
pkt->nb_segs = 1;//nb_segs;//
pkt->pkt_len = pkt->data_len;
pkt->ol_flags = PKT_TX_IPV4;//ol_flags;//
pkt->vlan_tci = 0;//vlan_tci;//
pkt->vlan_tci_outer = 0;//vlan_tci_outer;//
pkt->l2_len = sizeof(struct rte_ether_hdr);
pkt->l3_len = sizeof(struct rte_ipv4_hdr);
pkt_eth_hdr.s_addr.addr_bytes[0] = 0x08;
pkt_eth_hdr.s_addr.addr_bytes[1] = 0x00;
pkt_eth_hdr.s_addr.addr_bytes[2] = 0x27;
pkt_eth_hdr.s_addr.addr_bytes[3] = 0xea;
pkt_eth_hdr.s_addr.addr_bytes[4] = 0x05;
pkt_eth_hdr.s_addr.addr_bytes[5] = 0x4f;
pkt_eth_hdr.d_addr.addr_bytes[0] = 0x0a;
pkt_eth_hdr.d_addr.addr_bytes[1] = 0x00;
pkt_eth_hdr.d_addr.addr_bytes[2] = 0x27;
pkt_eth_hdr.d_addr.addr_bytes[3] = 0x00;
pkt_eth_hdr.d_addr.addr_bytes[4] = 0x00;
pkt_eth_hdr.d_addr.addr_bytes[5] = 0x14;
//本来以为这里需要转换成网络字节序,但在调试过程中发现存在问题。
//因此在Ethernet中无需调整MAC地址的字节序信息
pkt_eth_hdr.ether_type = rte_cpu_to_be_16(RTE_ETHER_TYPE_IPV4);
copy_buf_to_pkt(&pkt_eth_hdr, sizeof(pkt_eth_hdr), pkt, 0);
copy_buf_to_pkt(&pkt_ip_hdr, sizeof(pkt_ip_hdr), pkt, sizeof(struct rte_ether_hdr));
copy_buf_to_pkt(&pkt_colorget_hdr, sizeof(pkt_colorget_hdr), pkt,
sizeof(struct rte_ether_hdr) +
sizeof(struct rte_ipv4_hdr));
//set ethernet flame header
}
/*
* The lcore main. This is the main thread that does the work, reading from
* an input port and writing to an output port.
*/
static __attribute__((noreturn)) void
lcore_main(struct rte_mempool *mbuf_pool)
{
uint16_t port;
uint16_t i;
struct rte_mbuf *tx_bufs_pt[PKG_GEN_COUNT];
/*
* Check that the port is on the same NUMA node as the polling thread
* for best performance.
*/
RTE_ETH_FOREACH_DEV(port)
if (rte_eth_dev_socket_id(port) > 0 &&
rte_eth_dev_socket_id(port) !=
(int)rte_socket_id())
printf("WARNING, port %u is on remote NUMA node to "
"polling thread.\n\tPerformance will "
"not be optimal.\n", port);
printf("\nCore %u producing get packets. [Ctrl+C to quit]\n",
rte_lcore_id());
/* Run until the application is quit or killed. */
for (;;) {
/*
* Ensure that Only ONE Ethernet Device is working under DPDK module
* So FOREACH_DEV means the only one device is used and the device is supposed to send COLOR_get Packet per second
*/
RTE_ETH_FOREACH_DEV(port) {
/* malloc some storage memory to initialize the Header:
* Ethernet | IPV4 | COLOR_GET
* 14 | 20 | 88 bytes == 122 bytes
* sizeof(struct ether_hdr) | sizeof(struct rte_ipv4_hdr) | sizeof(struct CoLoR_get)
*/
struct rte_mbuf *rx_buf[BURST_SIZE];
const uint16_t nb_rx = rte_eth_rx_burst(port, 0, rx_buf, BURST_SIZE);
//对于网口port,将队列0的BURST_SIZE个包放入首地址rx_buf中
//函数返回值是接收到的包的数量,数量应该<= BURST_SIZE
//if get the pkg from other host
if(nb_rx!=0)
{
printf("get packet from port \n");
display_rx_mac(rx_buf,nb_rx);
for(i=0; i<nb_rx; i++)
rte_pktmbuf_free(rx_buf[i]);
//把这些包的内存空间全部还给缓冲池
}
//decode the packet
for(int i=0;i<PKG_GEN_COUNT;i++){
struct rte_mbuf *pkt = (struct rte_mbuf *)rte_pktmbuf_alloc((struct rte_mempool *)mbuf_pool);
if(pkt==NULL){
rte_exit(EXIT_FAILURE, "Cannot alloc storage memory in port %"PRIu16 "\n",port);
}
pkt_prepare(pkt);
tx_bufs_pt[i] = pkt;
}
int nb_tx=rte_eth_tx_burst(port , 0, tx_bufs_pt, PKG_GEN_COUNT);
printf("Successfully sent %d packets to MAC:0A:00:27:00:00:14\n",nb_tx);
sleep(2);
for(i=0;i<PKG_GEN_COUNT;i++)
{
if(unlikely(tx_bufs_pt[i]!=NULL))
rte_pktmbuf_free(tx_bufs_pt[i]);
}
}
}
}
/*
* The main function, which does initialization and calls the per-lcore
* functions.
*/
int
main(int argc, char *argv[])
{
struct rte_mempool *mbuf_pool;
unsigned nb_ports;
uint16_t portid;
/* Initialize the Environment Abstraction Layer (EAL). */
int ret = rte_eal_init(argc, argv);
if (ret < 0)
rte_exit(EXIT_FAILURE, "Error with EAL initialization\n");
argc -= ret;
argv += ret;
/* Check that there is an even number of ports to send/receive on. */
nb_ports = rte_eth_dev_count_avail();
if (nb_ports != 1)
rte_exit(EXIT_FAILURE, "Error: It only need ONE Ethernet Device!\n");
/* Creates a new mempool in memory to hold the mbufs. */
mbuf_pool = rte_pktmbuf_pool_create("TX_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");
/* Initialize all ports. */
RTE_ETH_FOREACH_DEV(portid)
if (port_init(portid, mbuf_pool) != 0)
rte_exit(EXIT_FAILURE, "Cannot init port %"PRIu16 "\n",
portid);
if (rte_lcore_count() > 1)
printf("\nWARNING: Too many lcores enabled. Only 1 used.\n");
setup_pkt_color_ip_headers(&pkt_ip_hdr,&pkt_colorget_hdr);
/* Call lcore_main on the master core only. */
lcore_main(mbuf_pool);
return 0;
}
EAL: Detected 4 lcore(s)
EAL: Detected 1 NUMA nodes
EAL: Multi-process socket /var/run/dpdk/rte/mp_socket
EAL: Selected IOVA mode 'PA'
EAL: Probing VFIO support...
EAL: PCI device 0000:00:03.0 on NUMA socket -1
EAL: Invalid NUMA socket, default to 0
EAL: probe driver: 8086:100e net_e1000_em
EAL: PCI device 0000:00:08.0 on NUMA socket -1
EAL: Invalid NUMA socket, default to 0
EAL: probe driver: 8086:100e net_e1000_em
WARNING: Too many lcores enabled. Only 1 used.
Core 0 producing get packets. [Ctrl+C to quit]
Successfully sent 64 packets to MAC:0A:00:27:00:00:14
get packet from port
src_MAC : 0a 00 27 00 00 14
dst_MAC : ff ff ff ff ff ff
Successfully sent 64 packets to MAC:0A:00:27:00:00:14
get packet from port
src_MAC : 0a 00 27 00 00 14
dst_MAC : ff ff ff ff ff ff
Successfully sent 64 packets to MAC:0A:00:27:00:00:14
get packet from port
src_MAC : 0a 00 27 00 00 14
dst_MAC : ff ff ff ff ff ff
src_MAC : 0a 00 27 00 00 14
dst_MAC : ff ff ff ff ff ff
src_MAC : 0a 00 27 00 00 14
dst_MAC : ff ff ff ff ff ff
# 由于宿主机一直在发布ARP通告查询192.168.73.3对应设备的MAC地址
# 所以收到该数据包