一、UDP编程
UDP编程的应用和TCP编程的应用同样非常广泛,如果说真得想使用UDP编程,一般情况下还真得不至于运用DPDK这种重量级的框架。但一个框架的优秀与否,不仅仅在于自身的整体设计优秀,更重要的在于其对应用的支持更完善。
正如DPDK对TCP的支持一样,其实对于DPDK这种更侧重于底层的应用来说,实现UDP和TCP没有本质的区别,只是套的一层解析的协议不同罢了。同样,UDP与TCP的不同及其协议的内容如有不明白可自行查看相关资料,此处不再赘述。
二、DPDK实现UDP源码分析
下面看一下例程(代码来源与TCP相同):
1、数据结构和协议
// arp表的单个条目
struct arp_entry
{
uint32_t ip;
unsigned char hwaddr[RTE_ETHER_ADDR_LEN];
unsigned char type;
struct arp_entry *next;
struct arp_entry *prev;
};
// arp表结构
struct arp_table
{
struct arp_entry *entries;
int count;
pthread_spinlock_t spinlock;
};
// udp control block
struct localhost
{
int fd;
//unsigned int status; //
uint32_t localip; // ip --> mac
unsigned char localmac[RTE_ETHER_ADDR_LEN];
uint16_t localport;
unsigned char protocol;
struct rte_ring *sndbuf;
struct rte_ring *rcvbuf;
struct localhost *prev;
struct localhost *next;
pthread_cond_t cond;
pthread_mutex_t mutex;
};
//UDP的数据包
struct offload
{
uint32_t sip;
uint32_t dip;
uint16_t sport;
uint16_t dport;
int protocol;
unsigned char *data;
uint16_t length;
};
//common.c
int ng_arp_entry_insert(uint32_t ip, unsigned char *mac)
{
struct arp_table *pstTbl = arp_table_instance();
struct arp_entry *pstEntry = NULL;
unsigned char *pstHwaddr = NULL;
pstHwaddr = ng_get_dst_macaddr(ip);
if(pstHwaddr == NULL)
{
pstEntry = rte_malloc("arp_entry", sizeof(struct arp_entry), 0);
if (pstEntry)
{
memset(pstEntry, 0, sizeof(struct arp_entry));
pstEntry->ip = ip;
rte_memcpy(pstEntry->hwaddr, mac, RTE_ETHER_ADDR_LEN);
pstEntry->type = 0;
pthread_spin_lock(&pstTbl->spinlock);
LL_ADD(pstEntry, pstTbl->entries);
pstTbl->count ++;
pthread_spin_unlock(&pstTbl->spinlock);
}
return 1;
}
return 0;
}
static struct arp_table *arp_table_instance(void)
{
if (g_pstArpTbl == NULL)
{
g_pstArpTbl = rte_malloc("arp table", sizeof(struct arp_table), 0);
if (g_pstArpTbl == NULL)
rte_exit(EXIT_FAILURE, "rte_malloc arp table failed\n");
memset(g_pstArpTbl, 0, sizeof(struct arp_table));
pthread_spin_init(&g_pstArpTbl->spinlock, PTHREAD_PROCESS_SHARED);
}
return g_pstArpTbl;
}
unsigned char* ng_get_dst_macaddr(uint32_t dip)
{
struct arp_entry *pstIter;
struct arp_table *pstTbl = arp_table_instance();
int count = pstTbl->count;
for (pstIter = pstTbl->entries; count-- != 0 && pstIter != NULL; pstIter = pstIter->next)
{
if (dip == pstIter->ip)
return pstIter->hwaddr;
}
return NULL;
}
UDP数据处理分为两类一类是控制包,一类是负载包也就是数据包。arp表的作用类似于快递小哥的作用,查找IP与MAC的映射并实现数据包的准确传输,即其有两个出口,一个是数据发送时的出口,保存发送时的IP和MAC(没有则先广播);一个是接收时的出口,用来保存收到的数据包对应的IP和MAC。代码没有什么难度,大家对照着相关的协议实现就明白了。
int nsocket(__attribute__((unused)) int domain, int type, __attribute__((unused)) int protocol)
{
int iFd;
struct localhost *pstHost;
pthread_cond_t pctCond = PTHREAD_COND_INITIALIZER;
pthread_mutex_t pmtMutex = PTHREAD_MUTEX_INITIALIZER;
iFd = get_fd_frombitmap();
if(type == SOCK_DGRAM) // udp
{
pstHost = rte_malloc("localhost", sizeof(struct localhost), 0);
if(pstHost == NULL)
{
printf("[%s][%d]: rte_malloc fail!\n", __FUNCTION__, __LINE__);
return -1;
}
memset(pstHost, 0x00, sizeof(struct localhost));
pstHost->fd = iFd;
pstHost->protocol = IPPROTO_UDP;
pstHost->rcvbuf = rte_ring_create("recv buffer", D_RING_SIZE, rte_socket_id(), RING_F_SP_ENQ | RING_F_SC_DEQ);
if (pstHost->rcvbuf == NULL)
{
printf("[%s][%d]: rte_ring_create fail!\n", __FUNCTION__, __LINE__);
rte_free(pstHost);
return -1;
}
pstHost->sndbuf = rte_ring_create("send buffer", D_RING_SIZE, rte_socket_id(), RING_F_SP_ENQ | RING_F_SC_DEQ);
if (pstHost->sndbuf == NULL)
{
printf("[%s][%d]: rte_ring_create fail!\n", __FUNCTION__, __LINE__);
rte_ring_free(pstHost->rcvbuf);
rte_free(pstHost);
return -1;
}
rte_memcpy(&pstHost->cond, &pctCond, sizeof(pthread_cond_t));
rte_memcpy(&pstHost->mutex, &pmtMutex, sizeof(pthread_mutex_t));
LL_ADD(pstHost, g_pstHost);
}
else if(type == SOCK_STREAM) // tcp
{
struct tcp_stream *pstStream = rte_malloc("tcp_stream", sizeof(struct tcp_stream), 0);
if (pstStream == NULL)
return -1;
memset(pstStream, 0, sizeof(struct tcp_stream));
pstStream->fd = iFd;
pstStream->protocol = IPPROTO_TCP;
pstStream->next = pstStream->prev = NULL;
pstStream->rcvbuf = rte_ring_create("tcp recv buffer", D_RING_SIZE, rte_socket_id(), RING_F_SP_ENQ | RING_F_SC_DEQ);
if (pstStream->rcvbuf == NULL)
{
rte_free(pstStream);
return -1;
}
pstStream->sndbuf = rte_ring_create("tcp send buffer", D_RING_SIZE, rte_socket_id(), RING_F_SP_ENQ | RING_F_SC_DEQ);
if (pstStream->sndbuf == NULL)
{
rte_ring_free(pstStream->rcvbuf);
rte_free(pstStream);
return -1;
}
pthread_cond_t blank_cond = PTHREAD_COND_INITIALIZER;
rte_memcpy(&pstStream->cond, &blank_cond, sizeof(pthread_cond_t));
pthread_mutex_t blank_mutex = PTHREAD_MUTEX_INITIALIZER;
rte_memcpy(&pstStream->mutex, &blank_mutex, sizeof(pthread_mutex_t));
g_pstTcpTbl = tcpInstance();
LL_ADD(pstStream, g_pstTcpTbl->tcb_set); // todo :hash
}
return iFd;
}
int nbind(int sockfd, const struct sockaddr *addr, __attribute__((unused)) socklen_t addrlen)
{
void *info = NULL;
info = get_hostinfo_fromfd(sockfd);
if(info == NULL)
return -1;
struct localhost *pstHostInfo = (struct localhost *)info;
if(pstHostInfo->protocol == IPPROTO_UDP)
{
const struct sockaddr_in *pstAddr = (const struct sockaddr_in *)addr;
pstHostInfo->localport = pstAddr->sin_port;
rte_memcpy(&pstHostInfo->localip, &pstAddr->sin_addr.s_addr, sizeof(uint32_t));
rte_memcpy(pstHostInfo->localmac, &g_stCpuMac, RTE_ETHER_ADDR_LEN);
}
else if(pstHostInfo->protocol == IPPROTO_TCP)
{
struct tcp_stream* pstStream = (struct tcp_stream*)pstHostInfo;
const struct sockaddr_in *pstAddr = (const struct sockaddr_in *)addr;
pstStream->dport = pstAddr->sin_port;
rte_memcpy(&pstStream->dip, &pstAddr->sin_addr.s_addr, sizeof(uint32_t));
rte_memcpy(pstStream->localmac, &g_stCpuMac, RTE_ETHER_ADDR_LEN);
pstStream->status = TCP_STATUS_CLOSED;
}
return 0;
}
ssize_t nrecvfrom(int sockfd, void *buf, size_t len, __attribute__((unused)) int flags,
struct sockaddr *src_addr, __attribute__((unused)) socklen_t *addrlen)
{
struct localhost *pstHostInfo = NULL;
struct offload *pstOffLoad = NULL;
struct sockaddr_in *pstAddr = NULL;
unsigned char *pucPtr = NULL;
int iLen = 0;
int iRet = -1;
pstHostInfo = (struct localhost *)get_hostinfo_fromfd(sockfd);
if(pstHostInfo == NULL)
return -1;
pthread_mutex_lock(&pstHostInfo->mutex);
while((iRet = rte_ring_mc_dequeue(pstHostInfo->rcvbuf, (void**)&pstOffLoad)) < 0)
{
pthread_cond_wait(&pstHostInfo->cond, &pstHostInfo->mutex);
}
pthread_mutex_unlock(&pstHostInfo->mutex);
pstAddr = (struct sockaddr_in *)src_addr;
pstAddr->sin_port = pstOffLoad->sport;
rte_memcpy(&pstAddr->sin_addr.s_addr, &pstOffLoad->sip, sizeof(uint32_t));
if(len < pstOffLoad->length)
{
rte_memcpy(buf, pstOffLoad->data, len);
pucPtr = rte_malloc("unsigned char *", pstOffLoad->length - len, 0);
rte_memcpy(pucPtr, pstOffLoad->data + len, pstOffLoad->length - len);
pstOffLoad->length -= len;
rte_free(pstOffLoad->data);
pstOffLoad->data = pucPtr;
rte_ring_mp_enqueue(pstHostInfo->rcvbuf, pstOffLoad);
return len;
}
iLen = pstOffLoad->length;
rte_memcpy(buf, pstOffLoad->data, pstOffLoad->length);
rte_free(pstOffLoad->data);
rte_free(pstOffLoad);
return iLen;
}
ssize_t nsendto(int sockfd, const void *buf, size_t len, __attribute__((unused)) int flags,
const struct sockaddr *dest_addr, __attribute__((unused)) socklen_t addrlen)
{
struct localhost *pstHostInfo = NULL;
struct offload *pstOffLoad = NULL;
const struct sockaddr_in *pstAddr = (const struct sockaddr_in *)dest_addr;
pstHostInfo = (struct localhost *)get_hostinfo_fromfd(sockfd);
if(pstHostInfo == NULL)
return -1;
pstOffLoad = rte_malloc("offload", sizeof(struct offload), 0);
if (pstOffLoad == NULL)
return -1;
pstOffLoad->dip = pstAddr->sin_addr.s_addr;
pstOffLoad->dport = pstAddr->sin_port;
pstOffLoad->sip = pstHostInfo->localip;
pstOffLoad->sport = pstHostInfo->localport;
pstOffLoad->length = len;
struct in_addr addr;
addr.s_addr = pstOffLoad->dip;
printf("nsendto ---> src: %s:%d \n", inet_ntoa(addr), ntohs(pstOffLoad->dport));
pstOffLoad->data = rte_malloc("unsigned char *", len, 0);
if (pstOffLoad->data == NULL) {
rte_free(pstOffLoad);
return -1;
}
rte_memcpy(pstOffLoad->data, buf, len);
puts("rte_ring_mp_enqueue before !");
rte_ring_mp_enqueue(pstHostInfo->sndbuf, pstOffLoad);
puts("rte_ring_mp_enqueue after !");
return len;
}
int nclose(int fd)
{
void *info = NULL;
info = (struct localhost *)get_hostinfo_fromfd(fd);
if(info == NULL)
return -1;
struct localhost *pstHostInfo = (struct localhost *)info;
if(pstHostInfo->protocol == IPPROTO_UDP)
{
LL_REMOVE(pstHostInfo, g_pstHost);
if (pstHostInfo->rcvbuf)
rte_ring_free(pstHostInfo->rcvbuf);
if (pstHostInfo->sndbuf)
rte_ring_free(pstHostInfo->sndbuf);
rte_free(pstHostInfo);
set_fd_frombitmap(fd);
}
else if(pstHostInfo->protocol == IPPROTO_TCP)
{
struct tcp_stream *pstStream = (struct tcp_stream*)info;
if (pstStream->status != TCP_STATUS_LISTEN)
{
struct tcp_fragment *pstFragment = rte_malloc("tcp_fragment", sizeof(struct tcp_fragment), 0);
if (pstFragment == NULL)
return -1;
memset(pstFragment, 0x00, sizeof(struct tcp_fragment));
pstFragment->data = NULL;
pstFragment->length = 0;
pstFragment->sport = pstStream->dport;
pstFragment->dport = pstStream->sport;
pstFragment->seqnum = pstStream->snd_nxt;
pstFragment->acknum = pstStream->rcv_nxt;
pstFragment->tcp_flags = RTE_TCP_FIN_FLAG | RTE_TCP_ACK_FLAG; // 发送FIN
pstFragment->windows = D_TCP_INITIAL_WINDOW;
pstFragment->hdrlen_off = 0x50;
rte_ring_mp_enqueue(pstStream->sndbuf, pstFragment);
pstStream->status = TCP_STATUS_LAST_ACK;
set_fd_frombitmap(fd);
}
else
{
LL_REMOVE(pstStream, g_pstTcpTbl->tcb_set);
rte_free(pstStream);
}
}
return 0;
}
2、接口实现
#include "udp.h"
int udp_process(struct rte_mbuf *pstUdpMbuf)
{
struct rte_ipv4_hdr *pstIpHdr;
struct rte_udp_hdr *pstUdpHdr;
struct localhost *pstHost;
struct offload *pstOffLoad;
pstIpHdr = rte_pktmbuf_mtod_offset(pstUdpMbuf, struct rte_ipv4_hdr *, sizeof(struct rte_ether_hdr));
pstUdpHdr = (struct rte_udp_hdr *)(pstIpHdr + 1);
pstHost = get_hostinfo_fromip_port(pstIpHdr->dst_addr, pstUdpHdr->dst_port, pstIpHdr->next_proto_id);
if (pstHost == NULL)
{
rte_pktmbuf_free(pstUdpMbuf);
return -3;
}
struct in_addr addr;
addr.s_addr = pstIpHdr->src_addr;
printf("udp_process ---> src: %s:%d \n", inet_ntoa(addr), ntohs(pstUdpHdr->src_port));
pstOffLoad = rte_malloc("offload", sizeof(struct offload), 0);
if (pstOffLoad == NULL)
{
rte_pktmbuf_free(pstUdpMbuf);
return -1;
}
pstOffLoad->dip = pstIpHdr->dst_addr;
pstOffLoad->sip = pstIpHdr->src_addr;
pstOffLoad->sport = pstUdpHdr->src_port;
pstOffLoad->dport = pstUdpHdr->dst_port;
pstOffLoad->protocol = IPPROTO_UDP;
pstOffLoad->length = ntohs(pstUdpHdr->dgram_len);
pstOffLoad->data = rte_malloc("unsigned char*", pstOffLoad->length - sizeof(struct rte_udp_hdr), 0);
if (pstOffLoad->data == NULL)
{
rte_pktmbuf_free(pstUdpMbuf);
rte_free(pstOffLoad);
return -2;
}
rte_memcpy(pstOffLoad->data, (unsigned char *)(pstUdpHdr+1), pstOffLoad->length - sizeof(struct rte_udp_hdr));
rte_ring_mp_enqueue(pstHost->rcvbuf, pstOffLoad); // recv buffer
pthread_mutex_lock(&pstHost->mutex);
pthread_cond_signal(&pstHost->cond);
pthread_mutex_unlock(&pstHost->mutex);
rte_pktmbuf_free(pstUdpMbuf);
return 0;
}
static int ng_encode_udp_apppkt(uint8_t *msg, uint32_t sip, uint32_t dip,
uint16_t sport, uint16_t dport, uint8_t *srcmac, uint8_t *dstmac,
unsigned char *data, uint16_t total_len)
{
struct rte_ether_hdr *pstEth;
struct rte_ipv4_hdr *pstIp;
struct rte_udp_hdr *pstUdp;
// 1 ethhdr
pstEth = (struct rte_ether_hdr *)msg;
rte_memcpy(pstEth->s_addr.addr_bytes, srcmac, RTE_ETHER_ADDR_LEN);
rte_memcpy(pstEth->d_addr.addr_bytes, dstmac, RTE_ETHER_ADDR_LEN);
pstEth->ether_type = htons(RTE_ETHER_TYPE_IPV4);
// 2 iphdr
pstIp = (struct rte_ipv4_hdr *)(pstEth + 1);
pstIp->version_ihl = 0x45;
pstIp->type_of_service = 0;
pstIp->total_length = htons(total_len - sizeof(struct rte_ether_hdr));
pstIp->packet_id = 0;
pstIp->fragment_offset = 0;
pstIp->time_to_live = 64; // ttl = 64
pstIp->next_proto_id = IPPROTO_UDP;
pstIp->src_addr = sip;
pstIp->dst_addr = dip;
pstIp->hdr_checksum = 0;
pstIp->hdr_checksum = rte_ipv4_cksum(pstIp);
// 3 udphdr
pstUdp = (struct rte_udp_hdr *)(pstIp + 1);
pstUdp->src_port = sport;
pstUdp->dst_port = dport;
uint16_t udplen = total_len - sizeof(struct rte_ether_hdr) - sizeof(struct rte_ipv4_hdr);
pstUdp->dgram_len = htons(udplen);
rte_memcpy((uint8_t*)(pstUdp + 1), data, udplen);
pstUdp->dgram_cksum = 0;
pstUdp->dgram_cksum = rte_ipv4_udptcp_cksum(pstIp, pstUdp);
return 0;
}
static struct rte_mbuf * ng_udp_pkt(struct rte_mempool *mbuf_pool, uint32_t sip, uint32_t dip,
uint16_t sport, uint16_t dport, unsigned char *srcmac, unsigned char *dstmac,
unsigned char *data, uint16_t length)
{
unsigned int uiTotalLen;
struct rte_mbuf *pstMbuf;
unsigned char *pucPktData;
uiTotalLen = length + 42; // 42 = eth + ip
pstMbuf = rte_pktmbuf_alloc(mbuf_pool);
if (!pstMbuf)
rte_exit(EXIT_FAILURE, "rte_pktmbuf_alloc\n");
pstMbuf->pkt_len = uiTotalLen;
pstMbuf->data_len = uiTotalLen;
pucPktData = rte_pktmbuf_mtod(pstMbuf, unsigned char*);
ng_encode_udp_apppkt(pucPktData, sip, dip, sport, dport, srcmac, dstmac,
data, uiTotalLen);
return pstMbuf;
}
int udp_out(struct rte_mempool *pstMbufPool)
{
struct localhost *pstHost;
for(pstHost = g_pstHost; pstHost != NULL; pstHost = pstHost->next)
{
struct offload *pstOffLoad = NULL;
int iSendCnt = rte_ring_mc_dequeue(pstHost->sndbuf, (void **)&pstOffLoad);
if(iSendCnt < 0)
continue;
struct in_addr addr;
addr.s_addr = pstOffLoad->dip;
printf("udp_out ---> src: %s:%d \n", inet_ntoa(addr), ntohs(pstOffLoad->dport));
unsigned char *dstmac = ng_get_dst_macaddr(pstOffLoad->dip); // 查询对端mac地址
if (dstmac == NULL) // 先广播发个arp包确定对端mac地址
{
struct rte_mbuf *pstArpbuf = ng_send_arp(pstMbufPool, RTE_ARP_OP_REQUEST, g_aucDefaultArpMac,
pstOffLoad->sip, pstOffLoad->dip);
rte_ring_mp_enqueue_burst(g_pstRingIns->pstOutRing, (void **)&pstArpbuf, 1, NULL);
rte_ring_mp_enqueue(pstHost->sndbuf, pstOffLoad); // 将取出的udp数据再次写入队列
}
else
{
struct rte_mbuf *pstUdpbuf = ng_udp_pkt(pstMbufPool, pstOffLoad->sip, pstOffLoad->dip,
pstOffLoad->sport, pstOffLoad->dport, pstHost->localmac,
dstmac, pstOffLoad->data, pstOffLoad->length);
rte_ring_mp_enqueue_burst(g_pstRingIns->pstOutRing, (void **)&pstUdpbuf, 1, NULL);
if (pstOffLoad->data != NULL)
rte_free(pstOffLoad->data);
rte_free(pstOffLoad);
}
}
return 0;
}
机制和TCP类似。
3、上层应用
//netfamily.c
unsigned char g_aucDefaultArpMac[RTE_ETHER_ADDR_LEN] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};
unsigned char g_ucFdTable[D_MAX_FD_COUNT] = {0};
static struct St_InOut_Ring *ringInstance(void)
{
if (g_pstRingIns == NULL)
{
g_pstRingIns = rte_malloc("in/out ring", sizeof(struct St_InOut_Ring), 0);
memset(g_pstRingIns, 0, sizeof(struct St_InOut_Ring));
}
return g_pstRingIns;
}
void ng_init_port(struct rte_mempool *pstMbufPoolPub)
{
unsigned int uiPortsNum;
const int iRxQueueNum = 1;
const int iTxQueueNum = 1;
int iRet;
struct rte_eth_dev_info stDevInfo;
struct rte_eth_txconf stTxConf;
struct rte_eth_conf stPortConf = // 端口配置信息
{
.rxmode = {.max_rx_pkt_len = 1518 } // RTE_ETHER_MAX_LEN = 1518
};
uiPortsNum = rte_eth_dev_count_avail();
if (uiPortsNum == 0)
rte_exit(EXIT_FAILURE, "No Supported eth found\n");
rte_eth_dev_info_get(D_PORT_ID, &stDevInfo);
// 配置以太网设备
rte_eth_dev_configure(D_PORT_ID, iRxQueueNum, iTxQueueNum, &stPortConf);
iRet = rte_eth_rx_queue_setup(D_PORT_ID, 0 , 1024, rte_eth_dev_socket_id(D_PORT_ID), NULL, pstMbufPoolPub);
if(iRet < 0)
rte_exit(EXIT_FAILURE, "Could not setup RX queue!\n");
stTxConf = stDevInfo.default_txconf;
stTxConf.offloads = stPortConf.txmode.offloads;
iRet = rte_eth_tx_queue_setup(D_PORT_ID, 0 , 1024, rte_eth_dev_socket_id(D_PORT_ID), &stTxConf);
if (iRet < 0)
rte_exit(EXIT_FAILURE, "Could not setup TX queue\n");
if (rte_eth_dev_start(D_PORT_ID) < 0 )
rte_exit(EXIT_FAILURE, "Could not start\n");
rte_eth_promiscuous_enable(D_PORT_ID);
}
static int ng_config_network_if(uint16_t port_id, unsigned char if_up) {
if (!rte_eth_dev_is_valid_port(port_id)) {
return -EINVAL;
}
int ret = 0;
if (if_up) {
rte_eth_dev_stop(port_id);
ret = rte_eth_dev_start(port_id);
} else {
rte_eth_dev_stop(port_id);
}
if (ret < 0) {
printf("Failed to start port : %d\n", port_id);
}
return 0;
}
static struct rte_kni *ng_alloc_kni(struct rte_mempool *mbuf_pool) {
struct rte_kni *kni_hanlder = NULL;
struct rte_kni_conf conf;
memset(&conf, 0, sizeof(conf));
snprintf(conf.name, RTE_KNI_NAMESIZE, "vEth%u", D_PORT_ID);
conf.group_id = D_PORT_ID;
conf.mbuf_size = D_MAX_PACKET_SIZE;
rte_eth_macaddr_get(D_PORT_ID, (struct rte_ether_addr *)conf.mac_addr);
rte_eth_dev_get_mtu(D_PORT_ID, &conf.mtu);
// print_ethaddr("ng_alloc_kni: ", (struct ether_addr *)conf.mac_addr);
/*
struct rte_eth_dev_info dev_info;
memset(&dev_info, 0, sizeof(dev_info));
rte_eth_dev_info_get(D_PORT_ID, &dev_info);
*/
struct rte_kni_ops ops;
memset(&ops, 0, sizeof(ops));
ops.port_id = D_PORT_ID;
ops.config_network_if = ng_config_network_if;
kni_hanlder = rte_kni_alloc(mbuf_pool, &conf, &ops);
if (!kni_hanlder) {
rte_exit(EXIT_FAILURE, "Failed to create kni for port : %d\n", D_PORT_ID);
}
return kni_hanlder;
}
static int pkt_process(void *arg)
{
struct rte_mempool *pstMbufPool;
int iRxNum;
int i;
struct rte_ether_hdr *pstEthHdr;
struct rte_ipv4_hdr *pstIpHdr;
pstMbufPool = (struct rte_mempool *)arg;
while(1)
{
struct rte_mbuf *pstMbuf[32];
iRxNum = rte_ring_mc_dequeue_burst(g_pstRingIns->pstInRing, (void**)pstMbuf, D_BURST_SIZE, NULL);
if(iRxNum <= 0)
continue;
for(i = 0; i < iRxNum; ++i)
{
pstEthHdr = rte_pktmbuf_mtod_offset(pstMbuf[i], struct rte_ether_hdr *, 0);
if (pstEthHdr->ether_type == rte_cpu_to_be_16(RTE_ETHER_TYPE_IPV4)) //IPv4: 0800
{
pstIpHdr = rte_pktmbuf_mtod_offset(pstMbuf[i], struct rte_ipv4_hdr *, sizeof(struct rte_ether_hdr));
// 维护一个arp表
ng_arp_entry_insert(pstIpHdr->src_addr, pstEthHdr->s_addr.addr_bytes);
if(pstIpHdr->next_proto_id == IPPROTO_UDP) // udp
{
// udp process
udp_process(pstMbuf[i]);
}
else if(pstIpHdr->next_proto_id == IPPROTO_TCP) // tcp
{
// printf("tcp_process ---\n");
tcp_process(pstMbuf[i]);
}
else
{
rte_kni_tx_burst(g_pstKni, pstMbuf, iRxNum);
// printf("tcp/udp --> rte_kni_handle_request\n");
}
}
else
{
// ifconfig vEth0 192.168.181.169 up
rte_kni_tx_burst(g_pstKni, pstMbuf, iRxNum);
// printf("ip --> rte_kni_handle_request\n");
}
}
rte_kni_handle_request(g_pstKni);
// to send
udp_out(pstMbufPool);
tcp_out(pstMbufPool);
}
return 0;
}
int udp_server_entry(__attribute__((unused)) void *arg)
{
int iConnfd;
struct sockaddr_in stLocalAddr, stClientAddr;
socklen_t uiAddrLen = sizeof(stClientAddr);;
char acBuf[D_UDP_BUFFER_SIZE] = {0};
iConnfd = nsocket(AF_INET, SOCK_DGRAM, 0);
if (iConnfd == -1)
{
printf("nsocket failed\n");
return -1;
}
memset(&stLocalAddr, 0, sizeof(struct sockaddr_in));
stLocalAddr.sin_port = htons(8889);
stLocalAddr.sin_family = AF_INET;
stLocalAddr.sin_addr.s_addr = inet_addr("192.168.181.169");
nbind(iConnfd, (struct sockaddr*)&stLocalAddr, sizeof(stLocalAddr));
while (1)
{
if (nrecvfrom(iConnfd, acBuf, D_UDP_BUFFER_SIZE, 0,
(struct sockaddr*)&stClientAddr, &uiAddrLen) < 0)
{
continue;
}
else
{
printf("recv from %s:%d, data:%s\n", inet_ntoa(stClientAddr.sin_addr),
ntohs(stClientAddr.sin_port), acBuf);
nsendto(iConnfd, acBuf, strlen(acBuf), 0,
(struct sockaddr*)&stClientAddr, sizeof(stClientAddr));
}
}
nclose(iConnfd);
return 0;
}
int main(int argc, char *argv[])
{
struct rte_mempool *pstMbufPoolPub;
struct St_InOut_Ring *pstRing;
struct rte_mbuf *pstRecvMbuf[32] = {NULL};
struct rte_mbuf *pstSendMbuf[32] = {NULL};
int iRxNum;
int iTotalNum;
int iOffset;
int iTxNum;
unsigned int uiCoreId;
if(rte_eal_init(argc, argv) < 0)
rte_exit(EXIT_FAILURE, "Error with EAL init\n");
pstMbufPoolPub = rte_pktmbuf_pool_create("MBUF_POOL_PUB", D_NUM_MBUFS, 0, 0,
RTE_MBUF_DEFAULT_BUF_SIZE, rte_socket_id());
if(pstMbufPoolPub == NULL)
{
printf("rte_errno = %x, errmsg = %s\n", rte_errno, rte_strerror(rte_errno));
return -1;
}
if (-1 == rte_kni_init(D_PORT_ID))
rte_exit(EXIT_FAILURE, "kni init failed\n");
ng_init_port(pstMbufPoolPub);
g_pstKni = ng_alloc_kni(pstMbufPoolPub);
// ng_init_port(pstMbufPoolPub);
rte_eth_macaddr_get(D_PORT_ID, &g_stCpuMac);
pstRing = ringInstance();
if(pstRing == NULL)
rte_exit(EXIT_FAILURE, "ring buffer init failed\n");
pstRing->pstInRing = rte_ring_create("in ring", D_RING_SIZE, rte_socket_id(), RING_F_SP_ENQ | RING_F_SC_DEQ);
pstRing->pstOutRing = rte_ring_create("out ring", D_RING_SIZE, rte_socket_id(), RING_F_SP_ENQ | RING_F_SC_DEQ);
uiCoreId = rte_lcore_id();
uiCoreId = rte_get_next_lcore(uiCoreId, 1, 0);
rte_eal_remote_launch(pkt_process, pstMbufPoolPub, uiCoreId);
uiCoreId = rte_get_next_lcore(uiCoreId, 1, 0);
rte_eal_remote_launch(udp_server_entry, pstMbufPoolPub, uiCoreId);
uiCoreId = rte_get_next_lcore(uiCoreId, 1, 0);
rte_eal_remote_launch(tcp_server_entry, pstMbufPoolPub, uiCoreId);
while (1)
{
// rx
iRxNum = rte_eth_rx_burst(D_PORT_ID, 0, pstRecvMbuf, D_BURST_SIZE);
if(iRxNum > 0)
rte_ring_sp_enqueue_burst(pstRing->pstInRing, (void**)pstRecvMbuf, iRxNum, NULL);
// tx
iTotalNum = rte_ring_sc_dequeue_burst(pstRing->pstOutRing, (void**)pstSendMbuf, D_BURST_SIZE, NULL);
if(iTotalNum > 0)
{
iOffset = 0;
while(iOffset < iTotalNum)
{
iTxNum = rte_eth_tx_burst(D_PORT_ID, 0, &pstSendMbuf[iOffset], iTotalNum - iOffset);
if(iTxNum > 0)
iOffset += iTxNum;
}
}
}
}
在前面的应用中,其实可以发现处理的TCP和UDP的数据有的是写在一个函数里了,这其实是从设计上看不是很好,以后不容易扩展,可它也有好处啊,容易让人明白啊。更多的依赖代码可以参看GITHUB上的开源地址。
四、总结
UDP其实在普通的网络应用中,基本用来做一些辅助的工作,如果地址发现、广播简单消息等等。但由于它的占用资源少,在一些场景下就能发挥很大作用。包括在前文提到的QUIC其实都借鉴了UDP的一些特点。所以是不是觉得,很多技术其实并不是完全隔离的,它们会不断的整合、独立再如此循环。所以以后别的公司会不会也会也一个类似于DPDK的框架呢?拭目以待!