xref: /freebsd/contrib/libpcap/pcap-dpdk.c (revision afdbf109c6a661a729938f68211054a0a50d38ac)
1 /*
2  * Copyright (C) 2018 jingle YANG. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  *
8  *   1. Redistributions of source code must retain the above copyright
9  *      notice, this list of conditions and the following disclaimer.
10  *   2. Redistributions in binary form must reproduce the above copyright
11  *      notice, this list of conditions and the following disclaimer in the
12  *      documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS''AND
15  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17  * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24  * SUCH DAMAGE.
25  */
26 
27 /*
28 Date: Dec 16, 2018
29 
30 Description:
31 1. Pcap-dpdk provides libpcap the ability to use DPDK with the device name as dpdk:{portid}, such as dpdk:0.
32 2. DPDK is a set of libraries and drivers for fast packet processing. (https://www.dpdk.org/)
33 3. The testprogs/capturetest provides 6.4Gbps/800,000 pps on Intel 10-Gigabit X540-AT2 with DPDK 18.11.
34 
35 Limitations:
36 1. DPDK support will be on if DPDK is available. Please set DIR for --with-dpdk[=DIR] with ./configure or -DDPDK_DIR[=DIR] with cmake if DPDK is installed manually.
37 2. Only support link libdpdk.so dynamically, because the libdpdk.a will not work correctly.
38 3. Only support read operation, and packet injection has not been supported yet.
39 
40 Usage:
41 1. Compile DPDK as shared library and install.(https://github.com/DPDK/dpdk.git)
42 
43 You shall modify the file $RTE_SDK/$RTE_TARGET/.config and set:
44 CONFIG_RTE_BUILD_SHARED_LIB=y
45 By the following command:
46 sed -i 's/CONFIG_RTE_BUILD_SHARED_LIB=n/CONFIG_RTE_BUILD_SHARED_LIB=y/' $RTE_SDK/$RTE_TARGET/.config
47 
48 2. Launch l2fwd that is one of DPDK examples correctly, and get device information.
49 
50 You shall learn how to bind nic with DPDK-compatible driver by $RTE_SDK/usertools/dpdk-devbind.py, such as igb_uio.
51 And enable hugepages by dpdk-setup.sh
52 
53 Then launch the l2fwd with dynamic driver support. For example:
54 $RTE_SDK/examples/l2fwd/$RTE_TARGET/l2fwd -dlibrte_pmd_e1000.so -dlibrte_pmd_ixgbe.so -dlibrte_mempool_ring.so -- -p 0x1
55 
56 3. Compile libpcap with dpdk options.
57 
58 If DPDK has not been found automatically, you shall export DPDK environment variable which are used for compiling DPDK. And then pass $RTE_SDK/$RTE_TARGET to --with-dpdk or -DDPDK_DIR
59 
60 export RTE_SDK={your DPDK base directory}
61 export RTE_TARGET={your target name}
62 
63 3.1 With configure
64 
65 ./configure --with-dpdk=$RTE_SDK/$RTE_TARGET && make -s all && make -s testprogs && make install
66 
67 3.2 With cmake
68 
69 mkdir -p build && cd build && cmake -DDPDK_DIR=$RTE_SDK/$RTE_TARGET ../ && make -s all && make -s testprogs && make install
70 
71 4. Link your own program with libpcap, and use DPDK with the device name as dpdk:{portid}, such as dpdk:0.
72 And you shall set DPDK configure options by environment variable DPDK_CFG
73 For example, the testprogs/capturetest could be launched by:
74 
75 env DPDK_CFG="--log-level=debug -l0 -dlibrte_pmd_e1000.so -dlibrte_pmd_ixgbe.so -dlibrte_mempool_ring.so" ./capturetest -i dpdk:0
76 */
77 
78 #include <config.h>
79 
80 #include <errno.h>
81 #include <netdb.h>
82 #include <stdio.h>
83 #include <stdlib.h>
84 #include <string.h>
85 #include <unistd.h>
86 #include <limits.h> /* for INT_MAX */
87 #include <time.h>
88 
89 #include <sys/time.h>
90 
91 //header for calling dpdk
92 #include <rte_config.h>
93 #include <rte_common.h>
94 #include <rte_errno.h>
95 #include <rte_log.h>
96 #include <rte_malloc.h>
97 #include <rte_memory.h>
98 #include <rte_eal.h>
99 #include <rte_launch.h>
100 #include <rte_atomic.h>
101 #include <rte_cycles.h>
102 #include <rte_lcore.h>
103 #include <rte_per_lcore.h>
104 #include <rte_branch_prediction.h>
105 #include <rte_interrupts.h>
106 #include <rte_random.h>
107 #include <rte_debug.h>
108 #include <rte_ether.h>
109 #include <rte_ethdev.h>
110 #include <rte_mempool.h>
111 #include <rte_mbuf.h>
112 #include <rte_bus.h>
113 
114 #include "pcap-int.h"
115 #include "pcap-dpdk.h"
116 
117 /*
118  * Deal with API changes that break source compatibility.
119  */
120 
121 #ifdef HAVE_STRUCT_RTE_ETHER_ADDR
122 #define ETHER_ADDR_TYPE	struct rte_ether_addr
123 #else
124 #define ETHER_ADDR_TYPE	struct ether_addr
125 #endif
126 
127 #define DPDK_DEF_LOG_LEV RTE_LOG_ERR
128 //
129 // This is set to 0 if we haven't initialized DPDK yet, 1 if we've
130 // successfully initialized it, a negative value, which is the negative
131 // of the rte_errno from rte_eal_init(), if we tried to initialize it
132 // and got an error.
133 //
134 static int is_dpdk_pre_inited=0;
135 #define DPDK_LIB_NAME "libpcap_dpdk"
136 #define DPDK_DESC "Data Plane Development Kit (DPDK) Interface"
137 #define DPDK_ERR_PERM_MSG "permission denied, DPDK needs root permission"
138 #define DPDK_ARGC_MAX 64
139 #define DPDK_CFG_MAX_LEN 1024
140 #define DPDK_DEV_NAME_MAX 32
141 #define DPDK_DEV_DESC_MAX 512
142 #define DPDK_CFG_ENV_NAME "DPDK_CFG"
143 #define DPDK_DEF_MIN_SLEEP_MS 1
144 static char dpdk_cfg_buf[DPDK_CFG_MAX_LEN];
145 #define DPDK_MAC_ADDR_SIZE 32
146 #define DPDK_DEF_MAC_ADDR "00:00:00:00:00:00"
147 #define DPDK_PCI_ADDR_SIZE 16
148 #define DPDK_DEF_CFG "--log-level=error -l0 -dlibrte_pmd_e1000.so -dlibrte_pmd_ixgbe.so -dlibrte_mempool_ring.so"
149 #define DPDK_PREFIX "dpdk:"
150 #define DPDK_PORTID_MAX 65535U
151 #define MBUF_POOL_NAME "mbuf_pool"
152 #define DPDK_TX_BUF_NAME "tx_buffer"
153 //The number of elements in the mbuf pool.
154 #define DPDK_NB_MBUFS 8192U
155 #define MEMPOOL_CACHE_SIZE 256
156 #define MAX_PKT_BURST 32
157 // Configurable number of RX/TX ring descriptors
158 #define RTE_TEST_RX_DESC_DEFAULT 1024
159 #define RTE_TEST_TX_DESC_DEFAULT 1024
160 
161 static uint16_t nb_rxd = RTE_TEST_RX_DESC_DEFAULT;
162 static uint16_t nb_txd = RTE_TEST_TX_DESC_DEFAULT;
163 
164 #ifdef RTE_ETHER_MAX_JUMBO_FRAME_LEN
165 #define RTE_ETH_PCAP_SNAPLEN RTE_ETHER_MAX_JUMBO_FRAME_LEN
166 #else
167 #define RTE_ETH_PCAP_SNAPLEN ETHER_MAX_JUMBO_FRAME_LEN
168 #endif
169 
170 static struct rte_eth_dev_tx_buffer *tx_buffer;
171 
172 struct dpdk_ts_helper{
173 	struct timeval start_time;
174 	uint64_t start_cycles;
175 	uint64_t hz;
176 };
177 struct pcap_dpdk{
178 	pcap_t * orig;
179 	uint16_t portid; // portid of DPDK
180 	int must_clear_promisc;
181 	uint64_t bpf_drop;
182 	int nonblock;
183 	struct timeval required_select_timeout;
184 	struct timeval prev_ts;
185 	struct rte_eth_stats prev_stats;
186 	struct timeval curr_ts;
187 	struct rte_eth_stats curr_stats;
188 	uint64_t pps;
189 	uint64_t bps;
190 	struct rte_mempool * pktmbuf_pool;
191 	struct dpdk_ts_helper ts_helper;
192 	ETHER_ADDR_TYPE eth_addr;
193 	char mac_addr[DPDK_MAC_ADDR_SIZE];
194 	char pci_addr[DPDK_PCI_ADDR_SIZE];
195 	unsigned char pcap_tmp_buf[RTE_ETH_PCAP_SNAPLEN];
196 };
197 
198 static struct rte_eth_conf port_conf = {
199 	.rxmode = {
200 		.split_hdr_size = 0,
201 	},
202 	.txmode = {
203 		.mq_mode = ETH_MQ_TX_NONE,
204 	},
205 };
206 
207 static void	dpdk_fmt_errmsg_for_rte_errno(char *, size_t, int,
208     PCAP_FORMAT_STRING(const char *), ...) PCAP_PRINTFLIKE(4, 5);
209 
210 /*
211  * Generate an error message based on a format, arguments, and an
212  * rte_errno, with a message for the rte_errno after the formatted output.
213  */
dpdk_fmt_errmsg_for_rte_errno(char * errbuf,size_t errbuflen,int errnum,const char * fmt,...)214 static void dpdk_fmt_errmsg_for_rte_errno(char *errbuf, size_t errbuflen,
215     int errnum, const char *fmt, ...)
216 {
217 	va_list ap;
218 	size_t msglen;
219 	char *p;
220 	size_t errbuflen_remaining;
221 
222 	va_start(ap, fmt);
223 	vsnprintf(errbuf, errbuflen, fmt, ap);
224 	va_end(ap);
225 	msglen = strlen(errbuf);
226 
227 	/*
228 	 * Do we have enough space to append ": "?
229 	 * Including the terminating '\0', that's 3 bytes.
230 	 */
231 	if (msglen + 3 > errbuflen) {
232 		/* No - just give them what we've produced. */
233 		return;
234 	}
235 	p = errbuf + msglen;
236 	errbuflen_remaining = errbuflen - msglen;
237 	*p++ = ':';
238 	*p++ = ' ';
239 	*p = '\0';
240 	msglen += 2;
241 	errbuflen_remaining -= 2;
242 
243 	/*
244 	 * Now append the string for the error code.
245 	 * rte_strerror() is thread-safe, at least as of dpdk 18.11,
246 	 * unlike strerror() - it uses strerror_r() rather than strerror()
247 	 * for UN*X errno values, and prints to what I assume is a per-thread
248 	 * buffer (based on the "PER_LCORE" in "RTE_DEFINE_PER_LCORE" used
249 	 * to declare the buffers statically) for DPDK errors.
250 	 */
251 	snprintf(p, errbuflen_remaining, "%s", rte_strerror(errnum));
252 }
253 
dpdk_init_timer(struct pcap_dpdk * pd)254 static int dpdk_init_timer(struct pcap_dpdk *pd){
255 	gettimeofday(&(pd->ts_helper.start_time),NULL);
256 	pd->ts_helper.start_cycles = rte_get_timer_cycles();
257 	pd->ts_helper.hz = rte_get_timer_hz();
258 	if (pd->ts_helper.hz == 0){
259 		return -1;
260 	}
261 	return 0;
262 }
calculate_timestamp(struct dpdk_ts_helper * helper,struct timeval * ts)263 static inline void calculate_timestamp(struct dpdk_ts_helper *helper,struct timeval *ts)
264 {
265 	uint64_t cycles;
266 	// delta
267 	struct timeval cur_time;
268 	cycles = rte_get_timer_cycles() - helper->start_cycles;
269 	cur_time.tv_sec = (time_t)(cycles/helper->hz);
270 	cur_time.tv_usec = (suseconds_t)((cycles%helper->hz)*1e6/helper->hz);
271 	timeradd(&(helper->start_time), &cur_time, ts);
272 }
273 
dpdk_gather_data(unsigned char * data,uint32_t len,struct rte_mbuf * mbuf)274 static uint32_t dpdk_gather_data(unsigned char *data, uint32_t len, struct rte_mbuf *mbuf)
275 {
276 	uint32_t total_len = 0;
277 	while (mbuf && (total_len+mbuf->data_len) < len ){
278 		rte_memcpy(data+total_len, rte_pktmbuf_mtod(mbuf,void *),mbuf->data_len);
279 		total_len+=mbuf->data_len;
280 		mbuf=mbuf->next;
281 	}
282 	return total_len;
283 }
284 
285 
dpdk_read_with_timeout(pcap_t * p,struct rte_mbuf ** pkts_burst,const uint16_t burst_cnt)286 static int dpdk_read_with_timeout(pcap_t *p, struct rte_mbuf **pkts_burst, const uint16_t burst_cnt){
287 	struct pcap_dpdk *pd = (struct pcap_dpdk*)(p->priv);
288 	int nb_rx = 0;
289 	int timeout_ms = p->opt.timeout;
290 	int sleep_ms = 0;
291 	if (pd->nonblock){
292 		// In non-blocking mode, just read once, no matter how many packets are captured.
293 		nb_rx = (int)rte_eth_rx_burst(pd->portid, 0, pkts_burst, burst_cnt);
294 	}else{
295 		// In blocking mode, read many times until packets are captured or timeout or break_loop is set.
296 		// if timeout_ms == 0, it may be blocked forever.
297 		while (timeout_ms == 0 || sleep_ms < timeout_ms){
298 			nb_rx = (int)rte_eth_rx_burst(pd->portid, 0, pkts_burst, burst_cnt);
299 			if (nb_rx){ // got packets within timeout_ms
300 				break;
301 			}else{ // no packet arrives at this round.
302 				if (p->break_loop){
303 					break;
304 				}
305 				// sleep for a very short while.
306 				// block sleep is the only choice, since usleep() will impact performance dramatically.
307 				rte_delay_us_block(DPDK_DEF_MIN_SLEEP_MS*1000);
308 				sleep_ms += DPDK_DEF_MIN_SLEEP_MS;
309 			}
310 		}
311 	}
312 	return nb_rx;
313 }
314 
pcap_dpdk_dispatch(pcap_t * p,int max_cnt,pcap_handler cb,u_char * cb_arg)315 static int pcap_dpdk_dispatch(pcap_t *p, int max_cnt, pcap_handler cb, u_char *cb_arg)
316 {
317 	struct pcap_dpdk *pd = (struct pcap_dpdk*)(p->priv);
318 	int burst_cnt = 0;
319 	int nb_rx = 0;
320 	struct rte_mbuf *pkts_burst[MAX_PKT_BURST];
321 	struct rte_mbuf *m;
322 	struct pcap_pkthdr pcap_header;
323 	// In DPDK, pkt_len is sum of lengths for all segments. And data_len is for one segment
324 	uint32_t pkt_len = 0;
325 	uint32_t caplen = 0;
326 	u_char *bp = NULL;
327 	int i=0;
328 	unsigned int gather_len =0;
329 	int pkt_cnt = 0;
330 	u_char *large_buffer=NULL;
331 	int timeout_ms = p->opt.timeout;
332 
333 	/*
334 	 * This can conceivably process more than INT_MAX packets,
335 	 * which would overflow the packet count, causing it either
336 	 * to look like a negative number, and thus cause us to
337 	 * return a value that looks like an error, or overflow
338 	 * back into positive territory, and thus cause us to
339 	 * return a too-low count.
340 	 *
341 	 * Therefore, if the packet count is unlimited, we clip
342 	 * it at INT_MAX; this routine is not expected to
343 	 * process packets indefinitely, so that's not an issue.
344 	 */
345 	if (PACKET_COUNT_IS_UNLIMITED(max_cnt))
346 		max_cnt = INT_MAX;
347 
348 	if (max_cnt < MAX_PKT_BURST){
349 		burst_cnt = max_cnt;
350 	}else{
351 		burst_cnt = MAX_PKT_BURST;
352 	}
353 
354 	while( pkt_cnt < max_cnt){
355 		if (p->break_loop){
356 			p->break_loop = 0;
357 			return PCAP_ERROR_BREAK;
358 		}
359 		// read once in non-blocking mode, or try many times waiting for timeout_ms.
360 		// if timeout_ms == 0, it will be blocked until one packet arrives or break_loop is set.
361 		nb_rx = dpdk_read_with_timeout(p, pkts_burst, burst_cnt);
362 		if (nb_rx == 0){
363 			if (pd->nonblock){
364 				RTE_LOG(DEBUG, USER1, "dpdk: no packets available in non-blocking mode.\n");
365 			}else{
366 				if (p->break_loop){
367 					RTE_LOG(DEBUG, USER1, "dpdk: no packets available and break_loop is set in blocking mode.\n");
368 					p->break_loop = 0;
369 					return PCAP_ERROR_BREAK;
370 
371 				}
372 				RTE_LOG(DEBUG, USER1, "dpdk: no packets available for timeout %d ms in blocking mode.\n", timeout_ms);
373 			}
374 			// break if dpdk reads 0 packet, no matter in blocking(timeout) or non-blocking mode.
375 			break;
376 		}
377 		pkt_cnt += nb_rx;
378 		for ( i = 0; i < nb_rx; i++) {
379 			m = pkts_burst[i];
380 			calculate_timestamp(&(pd->ts_helper),&(pcap_header.ts));
381 			pkt_len = rte_pktmbuf_pkt_len(m);
382 			// caplen = min(pkt_len, p->snapshot);
383 			// caplen will not be changed, no matter how long the rte_pktmbuf
384 			caplen = pkt_len < (uint32_t)p->snapshot ? pkt_len: (uint32_t)p->snapshot;
385 			pcap_header.caplen = caplen;
386 			pcap_header.len = pkt_len;
387 			// volatile prefetch
388 			rte_prefetch0(rte_pktmbuf_mtod(m, void *));
389 			bp = NULL;
390 			if (m->nb_segs == 1)
391 			{
392 				bp = rte_pktmbuf_mtod(m, u_char *);
393 			}else{
394 				// use fast buffer pcap_tmp_buf if pkt_len is small, no need to call malloc and free
395 				if ( pkt_len <= RTE_ETH_PCAP_SNAPLEN)
396 				{
397 					gather_len = dpdk_gather_data(pd->pcap_tmp_buf, RTE_ETH_PCAP_SNAPLEN, m);
398 					bp = pd->pcap_tmp_buf;
399 				}else{
400 					// need call free later
401 					large_buffer = (u_char *)malloc(caplen*sizeof(u_char));
402 					gather_len = dpdk_gather_data(large_buffer, caplen, m);
403 					bp = large_buffer;
404 				}
405 
406 			}
407 			if (bp){
408 				if (p->fcode.bf_insns==NULL || pcapint_filter(p->fcode.bf_insns, bp, pcap_header.len, pcap_header.caplen)){
409 					cb(cb_arg, &pcap_header, bp);
410 				}else{
411 					pd->bpf_drop++;
412 				}
413 			}
414 			//free all pktmbuf
415 			rte_pktmbuf_free(m);
416 			if (large_buffer){
417 				free(large_buffer);
418 				large_buffer=NULL;
419 			}
420 		}
421 	}
422 	return pkt_cnt;
423 }
424 
pcap_dpdk_inject(pcap_t * p,const void * buf _U_,int size _U_)425 static int pcap_dpdk_inject(pcap_t *p, const void *buf _U_, int size _U_)
426 {
427 	//not implemented yet
428 	pcapint_strlcpy(p->errbuf,
429 	    "dpdk error: Inject function has not been implemented yet",
430 	    PCAP_ERRBUF_SIZE);
431 	return PCAP_ERROR;
432 }
433 
pcap_dpdk_close(pcap_t * p)434 static void pcap_dpdk_close(pcap_t *p)
435 {
436 	struct pcap_dpdk *pd = p->priv;
437 	if (pd==NULL)
438 	{
439 		return;
440 	}
441 	if (pd->must_clear_promisc)
442 	{
443 		rte_eth_promiscuous_disable(pd->portid);
444 	}
445 	rte_eth_dev_stop(pd->portid);
446 	rte_eth_dev_close(pd->portid);
447 	pcapint_cleanup_live_common(p);
448 }
449 
nic_stats_display(struct pcap_dpdk * pd)450 static void nic_stats_display(struct pcap_dpdk *pd)
451 {
452 	uint16_t portid = pd->portid;
453 	struct rte_eth_stats stats;
454 	rte_eth_stats_get(portid, &stats);
455 	RTE_LOG(INFO,USER1, "portid:%d, RX-packets: %-10"PRIu64"  RX-errors:  %-10"PRIu64
456 	       "  RX-bytes:  %-10"PRIu64"  RX-Imissed:  %-10"PRIu64"\n", portid, stats.ipackets, stats.ierrors,
457 	       stats.ibytes,stats.imissed);
458 	RTE_LOG(INFO,USER1, "portid:%d, RX-PPS: %-10"PRIu64" RX-Mbps: %.2lf\n", portid, pd->pps, pd->bps/1e6f );
459 }
460 
pcap_dpdk_stats(pcap_t * p,struct pcap_stat * ps)461 static int pcap_dpdk_stats(pcap_t *p, struct pcap_stat *ps)
462 {
463 	struct pcap_dpdk *pd = p->priv;
464 	calculate_timestamp(&(pd->ts_helper), &(pd->curr_ts));
465 	rte_eth_stats_get(pd->portid,&(pd->curr_stats));
466 	if (ps){
467 		ps->ps_recv = pd->curr_stats.ipackets;
468 		ps->ps_drop = pd->curr_stats.ierrors;
469 		ps->ps_drop += pd->bpf_drop;
470 		ps->ps_ifdrop = pd->curr_stats.imissed;
471 	}
472 	uint64_t delta_pkt = pd->curr_stats.ipackets - pd->prev_stats.ipackets;
473 	struct timeval delta_tm;
474 	timersub(&(pd->curr_ts),&(pd->prev_ts), &delta_tm);
475 	uint64_t delta_usec = delta_tm.tv_sec*1e6+delta_tm.tv_usec;
476 	uint64_t delta_bit = (pd->curr_stats.ibytes-pd->prev_stats.ibytes)*8;
477 	RTE_LOG(DEBUG, USER1, "delta_usec: %-10"PRIu64" delta_pkt: %-10"PRIu64" delta_bit: %-10"PRIu64"\n", delta_usec, delta_pkt, delta_bit);
478 	pd->pps = (uint64_t)(delta_pkt*1e6f/delta_usec);
479 	pd->bps = (uint64_t)(delta_bit*1e6f/delta_usec);
480 	nic_stats_display(pd);
481 	pd->prev_stats = pd->curr_stats;
482 	pd->prev_ts = pd->curr_ts;
483 	return 0;
484 }
485 
pcap_dpdk_setnonblock(pcap_t * p,int nonblock)486 static int pcap_dpdk_setnonblock(pcap_t *p, int nonblock){
487 	struct pcap_dpdk *pd = (struct pcap_dpdk*)(p->priv);
488 	pd->nonblock = nonblock;
489 	return 0;
490 }
491 
pcap_dpdk_getnonblock(pcap_t * p)492 static int pcap_dpdk_getnonblock(pcap_t *p){
493 	struct pcap_dpdk *pd = (struct pcap_dpdk*)(p->priv);
494 	return pd->nonblock;
495 }
check_link_status(uint16_t portid,struct rte_eth_link * plink)496 static int check_link_status(uint16_t portid, struct rte_eth_link *plink)
497 {
498 	// wait up to 9 seconds to get link status
499 	rte_eth_link_get(portid, plink);
500 	return plink->link_status == ETH_LINK_UP;
501 }
eth_addr_str(ETHER_ADDR_TYPE * addrp,char * mac_str,int len)502 static void eth_addr_str(ETHER_ADDR_TYPE *addrp, char* mac_str, int len)
503 {
504 	int offset=0;
505 	if (addrp == NULL){
506 		snprintf(mac_str, len-1, DPDK_DEF_MAC_ADDR);
507 		return;
508 	}
509 	for (int i=0; i<6; i++)
510 	{
511 		if (offset >= len)
512 		{ // buffer overflow
513 			return;
514 		}
515 		if (i==0)
516 		{
517 			snprintf(mac_str+offset, len-1-offset, "%02X",addrp->addr_bytes[i]);
518 			offset+=2; // FF
519 		}else{
520 			snprintf(mac_str+offset, len-1-offset, ":%02X", addrp->addr_bytes[i]);
521 			offset+=3; // :FF
522 		}
523 	}
524 	return;
525 }
526 // return portid by device name, otherwise return -1
portid_by_device(char * device)527 static uint16_t portid_by_device(char * device)
528 {
529 	uint16_t ret = DPDK_PORTID_MAX;
530 	size_t len = strlen(device);
531 	size_t prefix_len = strlen(DPDK_PREFIX);
532 	unsigned long ret_ul = 0L;
533 	char *pEnd;
534 	if (len<=prefix_len || strncmp(device, DPDK_PREFIX, prefix_len)) // check prefix dpdk:
535 	{
536 		return ret;
537 	}
538 	//check all chars are digital
539 	for (int i=prefix_len; device[i]; i++){
540 		if (device[i]<'0' || device[i]>'9'){
541 			return ret;
542 		}
543 	}
544 	ret_ul = strtoul(&(device[prefix_len]), &pEnd, 10);
545 	if (pEnd == &(device[prefix_len]) || *pEnd != '\0'){
546 		return ret;
547 	}
548 	// too large for portid
549 	if (ret_ul >= DPDK_PORTID_MAX){
550 		return ret;
551 	}
552 	ret = (uint16_t)ret_ul;
553 	return ret;
554 }
555 
parse_dpdk_cfg(char * dpdk_cfg,char ** dargv)556 static int parse_dpdk_cfg(char* dpdk_cfg,char** dargv)
557 {
558 	int cnt=0;
559 	memset(dargv,0,sizeof(dargv[0])*DPDK_ARGC_MAX);
560 	//current process name
561 	int skip_space = 1;
562 	int i=0;
563 	RTE_LOG(INFO, USER1,"dpdk cfg: %s\n",dpdk_cfg);
564 	// find first non space char
565 	// The last opt is NULL
566 	for (i=0;dpdk_cfg[i] && cnt<DPDK_ARGC_MAX-1;i++){
567 		if (skip_space && dpdk_cfg[i]!=' '){ // not space
568 			skip_space=!skip_space; // skip normal char
569 			dargv[cnt++] = dpdk_cfg+i;
570 		}
571 		if (!skip_space && dpdk_cfg[i]==' '){ // find a space
572 			dpdk_cfg[i]=0x00; // end of this opt
573 			skip_space=!skip_space; // skip space char
574 		}
575 	}
576 	dargv[cnt]=NULL;
577 	return cnt;
578 }
579 
580 // only called once
581 // Returns:
582 //
583 //    1 on success;
584 //
585 //    0 if "the EAL cannot initialize on this system", which we treat as
586 //    meaning "DPDK isn't available";
587 //
588 //    a PCAP_ERROR_ code for other errors.
589 //
590 // If eaccess_not_fatal is non-zero, treat "a permissions issue" the way
591 // we treat "the EAL cannot initialize on this system".  We use that
592 // when trying to find DPDK devices, as we don't want to fail to return
593 // *any* devices just because we can't support DPDK; when we're trying
594 // to open a device, we need to return a permissions error in that case.
dpdk_pre_init(char * ebuf,int eaccess_not_fatal)595 static int dpdk_pre_init(char * ebuf, int eaccess_not_fatal)
596 {
597 	int dargv_cnt=0;
598 	char *dargv[DPDK_ARGC_MAX];
599 	char *ptr_dpdk_cfg = NULL;
600 	int ret;
601 	// global var
602 	if (is_dpdk_pre_inited != 0)
603 	{
604 		// already inited; did that succeed?
605 		if (is_dpdk_pre_inited < 0)
606 		{
607 			// failed
608 			goto error;
609 		}
610 		else
611 		{
612 			// succeeded
613 			return 1;
614 		}
615 	}
616 	// init EAL
617 	ptr_dpdk_cfg = getenv(DPDK_CFG_ENV_NAME);
618 	// set default log level to debug
619 	rte_log_set_global_level(DPDK_DEF_LOG_LEV);
620 	if (ptr_dpdk_cfg == NULL)
621 	{
622 		RTE_LOG(INFO,USER1,"env $DPDK_CFG is unset, so using default: %s\n",DPDK_DEF_CFG);
623 		ptr_dpdk_cfg = DPDK_DEF_CFG;
624 	}
625 	memset(dpdk_cfg_buf,0,sizeof(dpdk_cfg_buf));
626 	snprintf(dpdk_cfg_buf,DPDK_CFG_MAX_LEN-1,"%s %s",DPDK_LIB_NAME,ptr_dpdk_cfg);
627 	dargv_cnt = parse_dpdk_cfg(dpdk_cfg_buf,dargv);
628 	ret = rte_eal_init(dargv_cnt,dargv);
629 	if (ret == -1)
630 	{
631 		// Indicate that we've called rte_eal_init() by setting
632 		// is_dpdk_pre_inited to the negative of the error code,
633 		// and process the error.
634 		is_dpdk_pre_inited = -rte_errno;
635 		goto error;
636 	}
637 	// init succeeded, so we do not need to do it again later.
638 	is_dpdk_pre_inited = 1;
639 	return 1;
640 
641 error:
642 	switch (-is_dpdk_pre_inited)
643 	{
644 		case EACCES:
645 			// This "indicates a permissions issue.".
646 			RTE_LOG(ERR, USER1, "%s\n", DPDK_ERR_PERM_MSG);
647 			// If we were told to treat this as just meaning
648 			// DPDK isn't available, do so.
649 			if (eaccess_not_fatal)
650 				return 0;
651 			// Otherwise report a fatal error.
652 			snprintf(ebuf, PCAP_ERRBUF_SIZE,
653 			    "DPDK requires that it run as root");
654 			return PCAP_ERROR_PERM_DENIED;
655 
656 		case EAGAIN:
657 			// This "indicates either a bus or system
658 			// resource was not available, setup may
659 			// be attempted again."
660 			// There's no such error in pcap, so I'm
661 			// not sure what we should do here.
662 			snprintf(ebuf, PCAP_ERRBUF_SIZE,
663 			    "Bus or system resource was not available");
664 			break;
665 
666 		case EALREADY:
667 			// This "indicates that the rte_eal_init
668 			// function has already been called, and
669 			// cannot be called again."
670 			// That's not an error; set the "we've
671 			// been here before" flag and return
672 			// success.
673 			is_dpdk_pre_inited = 1;
674 			return 1;
675 
676 		case EFAULT:
677 			// This "indicates the tailq configuration
678 			// name was not found in memory configuration."
679 			snprintf(ebuf, PCAP_ERRBUF_SIZE,
680 			    "The tailq configuration name was not found in the memory configuration");
681 			return PCAP_ERROR;
682 
683 		case EINVAL:
684 			// This "indicates invalid parameters were
685 			// passed as argv/argc."  Those came from
686 			// the configuration file.
687 			snprintf(ebuf, PCAP_ERRBUF_SIZE,
688 			    "The configuration file has invalid parameters");
689 			break;
690 
691 		case ENOMEM:
692 			// This "indicates failure likely caused by
693 			// an out-of-memory condition."
694 			snprintf(ebuf, PCAP_ERRBUF_SIZE,
695 			    "Out of memory");
696 			break;
697 
698 		case ENODEV:
699 			// This "indicates memory setup issues."
700 			snprintf(ebuf, PCAP_ERRBUF_SIZE,
701 			    "An error occurred setting up memory");
702 			break;
703 
704 		case ENOTSUP:
705 			// This "indicates that the EAL cannot
706 			// initialize on this system."  We treat
707 			// that as meaning DPDK isn't available
708 			// on this machine, rather than as a
709 			// fatal error, and let our caller decide
710 			// whether that's a fatal error (if trying
711 			// to activate a DPDK device) or not (if
712 			// trying to enumerate devices).
713 			return 0;
714 
715 		case EPROTO:
716 			// This "indicates that the PCI bus is
717 			// either not present, or is not readable
718 			// by the eal."  Does "the PCI bus is not
719 			// present" mean "this machine has no PCI
720 			// bus", which strikes me as a "not available"
721 			// case?  If so, should "is not readable by
722 			// the EAL" also something we should treat
723 			// as a "not available" case?  If not, we
724 			// can't distinguish between the two, so
725 			// we're stuck.
726 			snprintf(ebuf, PCAP_ERRBUF_SIZE,
727 			    "PCI bus is not present or not readable by the EAL");
728 			break;
729 
730 		case ENOEXEC:
731 			// This "indicates that a service core
732 			// failed to launch successfully."
733 			snprintf(ebuf, PCAP_ERRBUF_SIZE,
734 			    "A service core failed to launch successfully");
735 			break;
736 
737 		default:
738 			//
739 			// That's not in the list of errors in
740 			// the documentation; let it be reported
741 			// as an error.
742 			//
743 			dpdk_fmt_errmsg_for_rte_errno(ebuf,
744 			    PCAP_ERRBUF_SIZE, -is_dpdk_pre_inited,
745 			    "dpdk error: dpdk_pre_init failed");
746 			break;
747 	}
748 	// Error.
749 	return PCAP_ERROR;
750 }
751 
pcap_dpdk_activate(pcap_t * p)752 static int pcap_dpdk_activate(pcap_t *p)
753 {
754 	struct pcap_dpdk *pd = p->priv;
755 	pd->orig = p;
756 	int ret = PCAP_ERROR;
757 	uint16_t nb_ports=0;
758 	uint16_t portid= DPDK_PORTID_MAX;
759 	unsigned nb_mbufs = DPDK_NB_MBUFS;
760 	struct rte_eth_rxconf rxq_conf;
761 	struct rte_eth_txconf txq_conf;
762 	struct rte_eth_conf local_port_conf = port_conf;
763 	struct rte_eth_dev_info dev_info;
764 	int is_port_up = 0;
765 	struct rte_eth_link link;
766 	do{
767 		//init EAL; fail if we have insufficient permission
768 		char dpdk_pre_init_errbuf[PCAP_ERRBUF_SIZE];
769 		ret = dpdk_pre_init(dpdk_pre_init_errbuf, 0);
770 		if (ret < 0)
771 		{
772 			// This returns a negative value on an error.
773 			snprintf(p->errbuf, PCAP_ERRBUF_SIZE,
774 			    "Can't open device %s: %s",
775 			    p->opt.device, dpdk_pre_init_errbuf);
776 			// ret is set to the correct error
777 			break;
778 		}
779 		if (ret == 0)
780 		{
781 			// This means DPDK isn't available on this machine.
782 			snprintf(p->errbuf, PCAP_ERRBUF_SIZE,
783 			    "Can't open device %s: DPDK is not available on this machine",
784 			    p->opt.device);
785 			return PCAP_ERROR_NO_SUCH_DEVICE;
786 		}
787 
788 		ret = dpdk_init_timer(pd);
789 		if (ret<0)
790 		{
791 			snprintf(p->errbuf, PCAP_ERRBUF_SIZE,
792 				"dpdk error: Init timer is zero with device %s",
793 				p->opt.device);
794 			ret = PCAP_ERROR;
795 			break;
796 		}
797 
798 		nb_ports = rte_eth_dev_count_avail();
799 		if (nb_ports == 0)
800 		{
801 			snprintf(p->errbuf, PCAP_ERRBUF_SIZE,
802 			    "dpdk error: No Ethernet ports");
803 			ret = PCAP_ERROR;
804 			break;
805 		}
806 
807 		portid = portid_by_device(p->opt.device);
808 		if (portid == DPDK_PORTID_MAX){
809 			snprintf(p->errbuf, PCAP_ERRBUF_SIZE,
810 			    "dpdk error: portid is invalid. device %s",
811 			    p->opt.device);
812 			ret = PCAP_ERROR_NO_SUCH_DEVICE;
813 			break;
814 		}
815 
816 		pd->portid = portid;
817 
818 		if (p->snapshot <= 0 || p->snapshot > MAXIMUM_SNAPLEN)
819 		{
820 			p->snapshot = MAXIMUM_SNAPLEN;
821 		}
822 		// create the mbuf pool
823 		pd->pktmbuf_pool = rte_pktmbuf_pool_create(MBUF_POOL_NAME, nb_mbufs,
824 			MEMPOOL_CACHE_SIZE, 0, RTE_MBUF_DEFAULT_BUF_SIZE,
825 			rte_socket_id());
826 		if (pd->pktmbuf_pool == NULL)
827 		{
828 			dpdk_fmt_errmsg_for_rte_errno(p->errbuf,
829 			    PCAP_ERRBUF_SIZE, rte_errno,
830 			    "dpdk error: Cannot init mbuf pool");
831 			ret = PCAP_ERROR;
832 			break;
833 		}
834 		// config dev
835 		rte_eth_dev_info_get(portid, &dev_info);
836 		if (dev_info.tx_offload_capa & DEV_TX_OFFLOAD_MBUF_FAST_FREE)
837 		{
838 			local_port_conf.txmode.offloads |=DEV_TX_OFFLOAD_MBUF_FAST_FREE;
839 		}
840 		// only support 1 queue
841 		ret = rte_eth_dev_configure(portid, 1, 1, &local_port_conf);
842 		if (ret < 0)
843 		{
844 			dpdk_fmt_errmsg_for_rte_errno(p->errbuf,
845 			    PCAP_ERRBUF_SIZE, -ret,
846 			    "dpdk error: Cannot configure device: port=%u",
847 			    portid);
848 			ret = PCAP_ERROR;
849 			break;
850 		}
851 		// adjust rx tx
852 		ret = rte_eth_dev_adjust_nb_rx_tx_desc(portid, &nb_rxd, &nb_txd);
853 		if (ret < 0)
854 		{
855 			dpdk_fmt_errmsg_for_rte_errno(p->errbuf,
856 			    PCAP_ERRBUF_SIZE, -ret,
857 			    "dpdk error: Cannot adjust number of descriptors: port=%u",
858 			    portid);
859 			ret = PCAP_ERROR;
860 			break;
861 		}
862 		// get MAC addr
863 		rte_eth_macaddr_get(portid, &(pd->eth_addr));
864 		eth_addr_str(&(pd->eth_addr), pd->mac_addr, DPDK_MAC_ADDR_SIZE-1);
865 
866 		// init one RX queue
867 		rxq_conf = dev_info.default_rxconf;
868 		rxq_conf.offloads = local_port_conf.rxmode.offloads;
869 		ret = rte_eth_rx_queue_setup(portid, 0, nb_rxd,
870 					     rte_eth_dev_socket_id(portid),
871 					     &rxq_conf,
872 					     pd->pktmbuf_pool);
873 		if (ret < 0)
874 		{
875 			dpdk_fmt_errmsg_for_rte_errno(p->errbuf,
876 			    PCAP_ERRBUF_SIZE, -ret,
877 			    "dpdk error: rte_eth_rx_queue_setup:port=%u",
878 			    portid);
879 			ret = PCAP_ERROR;
880 			break;
881 		}
882 
883 		// init one TX queue
884 		txq_conf = dev_info.default_txconf;
885 		txq_conf.offloads = local_port_conf.txmode.offloads;
886 		ret = rte_eth_tx_queue_setup(portid, 0, nb_txd,
887 				rte_eth_dev_socket_id(portid),
888 				&txq_conf);
889 		if (ret < 0)
890 		{
891 			dpdk_fmt_errmsg_for_rte_errno(p->errbuf,
892 			    PCAP_ERRBUF_SIZE, -ret,
893 			    "dpdk error: rte_eth_tx_queue_setup:port=%u",
894 			    portid);
895 			ret = PCAP_ERROR;
896 			break;
897 		}
898 		// Initialize TX buffers
899 		tx_buffer = rte_zmalloc_socket(DPDK_TX_BUF_NAME,
900 				RTE_ETH_TX_BUFFER_SIZE(MAX_PKT_BURST), 0,
901 				rte_eth_dev_socket_id(portid));
902 		if (tx_buffer == NULL)
903 		{
904 			snprintf(p->errbuf, PCAP_ERRBUF_SIZE,
905 			    "dpdk error: Cannot allocate buffer for tx on port %u", portid);
906 			ret = PCAP_ERROR;
907 			break;
908 		}
909 		rte_eth_tx_buffer_init(tx_buffer, MAX_PKT_BURST);
910 		// Start device
911 		ret = rte_eth_dev_start(portid);
912 		if (ret < 0)
913 		{
914 			dpdk_fmt_errmsg_for_rte_errno(p->errbuf,
915 			    PCAP_ERRBUF_SIZE, -ret,
916 			    "dpdk error: rte_eth_dev_start:port=%u",
917 			    portid);
918 			ret = PCAP_ERROR;
919 			break;
920 		}
921 		// set promiscuous mode
922 		if (p->opt.promisc){
923 			pd->must_clear_promisc=1;
924 			rte_eth_promiscuous_enable(portid);
925 		}
926 		// check link status
927 		is_port_up = check_link_status(portid, &link);
928 		if (!is_port_up){
929 			snprintf(p->errbuf, PCAP_ERRBUF_SIZE,
930 			    "dpdk error: link is down, port=%u",portid);
931 			ret = PCAP_ERROR_IFACE_NOT_UP;
932 			break;
933 		}
934 		// reset statistics
935 		rte_eth_stats_reset(pd->portid);
936 		calculate_timestamp(&(pd->ts_helper), &(pd->prev_ts));
937 		rte_eth_stats_get(pd->portid,&(pd->prev_stats));
938 		// format pcap_t
939 		pd->portid = portid;
940 		p->fd = pd->portid;
941 		if (p->snapshot <=0 || p->snapshot> MAXIMUM_SNAPLEN)
942 		{
943 			p->snapshot = MAXIMUM_SNAPLEN;
944 		}
945 		p->linktype = DLT_EN10MB; // Ethernet, the 10MB is historical.
946 		p->selectable_fd = p->fd;
947 		p->read_op = pcap_dpdk_dispatch;
948 		p->inject_op = pcap_dpdk_inject;
949 		// using pcapint_filter currently, though DPDK provides their own BPF function. Because DPDK BPF needs load a ELF file as a filter.
950 		p->setfilter_op = pcapint_install_bpf_program;
951 		p->setdirection_op = NULL;
952 		p->set_datalink_op = NULL;
953 		p->getnonblock_op = pcap_dpdk_getnonblock;
954 		p->setnonblock_op = pcap_dpdk_setnonblock;
955 		p->stats_op = pcap_dpdk_stats;
956 		p->cleanup_op = pcap_dpdk_close;
957 		p->breakloop_op = pcapint_breakloop_common;
958 		// set default timeout
959 		pd->required_select_timeout.tv_sec = 0;
960 		pd->required_select_timeout.tv_usec = DPDK_DEF_MIN_SLEEP_MS*1000;
961 		p->required_select_timeout = &pd->required_select_timeout;
962 		ret = 0; // OK
963 	}while(0);
964 
965 	if (ret <= PCAP_ERROR) // all kinds of error code
966 	{
967 		pcapint_cleanup_live_common(p);
968 	}else{
969 		rte_eth_dev_get_name_by_port(portid,pd->pci_addr);
970 		RTE_LOG(INFO, USER1,"Port %d device: %s, MAC:%s, PCI:%s\n", portid, p->opt.device, pd->mac_addr, pd->pci_addr);
971 		RTE_LOG(INFO, USER1,"Port %d Link Up. Speed %u Mbps - %s\n",
972 							portid, link.link_speed,
973 					(link.link_duplex == ETH_LINK_FULL_DUPLEX) ?
974 						("full-duplex") : ("half-duplex\n"));
975 	}
976 	return ret;
977 }
978 
979 // device name for dpdk should be in the form as dpdk:number, such as dpdk:0
pcap_dpdk_create(const char * device,char * ebuf,int * is_ours)980 pcap_t * pcap_dpdk_create(const char *device, char *ebuf, int *is_ours)
981 {
982 	pcap_t *p=NULL;
983 	*is_ours = 0;
984 
985 	*is_ours = !strncmp(device, "dpdk:", 5);
986 	if (! *is_ours)
987 		return NULL;
988 	//memset will happen
989 	p = PCAP_CREATE_COMMON(ebuf, struct pcap_dpdk);
990 
991 	if (p == NULL)
992 		return NULL;
993 	p->activate_op = pcap_dpdk_activate;
994 	return p;
995 }
996 
pcap_dpdk_findalldevs(pcap_if_list_t * devlistp,char * ebuf)997 int pcap_dpdk_findalldevs(pcap_if_list_t *devlistp, char *ebuf)
998 {
999 	int ret=0;
1000 	unsigned int nb_ports = 0;
1001 	char dpdk_name[DPDK_DEV_NAME_MAX];
1002 	char dpdk_desc[DPDK_DEV_DESC_MAX];
1003 	ETHER_ADDR_TYPE eth_addr;
1004 	char mac_addr[DPDK_MAC_ADDR_SIZE];
1005 	char pci_addr[DPDK_PCI_ADDR_SIZE];
1006 	do{
1007 		// init EAL; return "DPDK not available" if we
1008 		// have insufficient permission
1009 		char dpdk_pre_init_errbuf[PCAP_ERRBUF_SIZE];
1010 		ret = dpdk_pre_init(dpdk_pre_init_errbuf, 1);
1011 		if (ret < 0)
1012 		{
1013 			// This returns a negative value on an error.
1014 			snprintf(ebuf, PCAP_ERRBUF_SIZE,
1015 			    "Can't look for DPDK devices: %s",
1016 			    dpdk_pre_init_errbuf);
1017 			ret = PCAP_ERROR;
1018 			break;
1019 		}
1020 		if (ret == 0)
1021 		{
1022 			// This means DPDK isn't available on this machine.
1023 			// That just means "don't return any devices".
1024 			break;
1025 		}
1026 		nb_ports = rte_eth_dev_count_avail();
1027 		if (nb_ports == 0)
1028 		{
1029 			// That just means "don't return any devices".
1030 			ret = 0;
1031 			break;
1032 		}
1033 		for (unsigned int i=0; i<nb_ports; i++){
1034 			snprintf(dpdk_name, DPDK_DEV_NAME_MAX-1,
1035 			    "%s%u", DPDK_PREFIX, i);
1036 			// mac addr
1037 			rte_eth_macaddr_get(i, &eth_addr);
1038 			eth_addr_str(&eth_addr,mac_addr,DPDK_MAC_ADDR_SIZE);
1039 			// PCI addr
1040 			rte_eth_dev_get_name_by_port(i,pci_addr);
1041 			snprintf(dpdk_desc,DPDK_DEV_DESC_MAX-1,"%s %s, MAC:%s, PCI:%s", DPDK_DESC, dpdk_name, mac_addr, pci_addr);
1042 			if (pcapint_add_dev(devlistp, dpdk_name, 0, dpdk_desc, ebuf)==NULL){
1043 				ret = PCAP_ERROR;
1044 				break;
1045 			}
1046 		}
1047 	}while(0);
1048 	return ret;
1049 }
1050 
1051 #ifdef DPDK_ONLY
1052 /*
1053  * This libpcap build supports only DPDK, not regular network interfaces.
1054  */
1055 
1056 /*
1057  * There are no regular interfaces, just DPDK interfaces.
1058  */
1059 int
pcapint_platform_finddevs(pcap_if_list_t * devlistp _U_,char * errbuf)1060 pcapint_platform_finddevs(pcap_if_list_t *devlistp _U_, char *errbuf)
1061 {
1062 	return (0);
1063 }
1064 
1065 /*
1066  * Attempts to open a regular interface fail.
1067  */
1068 pcap_t *
pcapint_create_interface(const char * device,char * errbuf)1069 pcapint_create_interface(const char *device, char *errbuf)
1070 {
1071 	snprintf(errbuf, PCAP_ERRBUF_SIZE,
1072 	    "This version of libpcap only supports DPDK");
1073 	return NULL;
1074 }
1075 
1076 /*
1077  * Libpcap version string.
1078  */
1079 const char *
pcap_lib_version(void)1080 pcap_lib_version(void)
1081 {
1082 	return (PCAP_VERSION_STRING " (DPDK-only)");
1083 }
1084 #endif
1085