DPDK系列之四十二DPDK应用网络编程UDP编程

一、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的框架呢?拭目以待!

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值