Branch data Line data Source code
1 : : /* SPDX-License-Identifier: BSD-3-Clause
2 : : * Copyright(c) 2010-2016 Intel Corporation
3 : : */
4 : : #include <stdint.h>
5 : : #include <string.h>
6 : :
7 : : #include <rte_mbuf.h>
8 : : #include <rte_malloc.h>
9 : : #include <rte_memcpy.h>
10 : :
11 : : #ifdef RTE_PORT_PCAP
12 : : #include <rte_ether.h>
13 : : #include <pcap.h>
14 : : #endif
15 : :
16 : : #include "rte_port_source_sink.h"
17 : :
18 : : #include "port_log.h"
19 : :
20 : : /*
21 : : * Port SOURCE
22 : : */
23 : : #ifdef RTE_PORT_STATS_COLLECT
24 : :
25 : : #define RTE_PORT_SOURCE_STATS_PKTS_IN_ADD(port, val) \
26 : : port->stats.n_pkts_in += val
27 : : #define RTE_PORT_SOURCE_STATS_PKTS_DROP_ADD(port, val) \
28 : : port->stats.n_pkts_drop += val
29 : :
30 : : #else
31 : :
32 : : #define RTE_PORT_SOURCE_STATS_PKTS_IN_ADD(port, val)
33 : : #define RTE_PORT_SOURCE_STATS_PKTS_DROP_ADD(port, val)
34 : :
35 : : #endif
36 : :
37 : : struct rte_port_source {
38 : : struct rte_port_in_stats stats;
39 : :
40 : : struct rte_mempool *mempool;
41 : :
42 : : /* PCAP buffers and indices */
43 : : uint8_t **pkts;
44 : : uint8_t *pkt_buff;
45 : : uint32_t *pkt_len;
46 : : uint32_t n_pkts;
47 : : uint32_t pkt_index;
48 : : };
49 : :
50 : : #ifdef RTE_PORT_PCAP
51 : :
52 : : static int
53 : 0 : pcap_source_load(struct rte_port_source *port,
54 : : const char *file_name,
55 : : uint32_t n_bytes_per_pkt,
56 : : int socket_id)
57 : : {
58 : : uint32_t n_pkts = 0;
59 : : uint32_t i;
60 : : uint32_t *pkt_len_aligns = NULL;
61 : : size_t total_buff_len = 0;
62 : : pcap_t *pcap_handle;
63 : : char pcap_errbuf[PCAP_ERRBUF_SIZE];
64 : : uint32_t max_len;
65 : : struct pcap_pkthdr pcap_hdr;
66 : : const uint8_t *pkt;
67 : : uint8_t *buff = NULL;
68 : 0 : uint32_t pktmbuf_maxlen = (uint32_t)
69 [ # # ]: 0 : (rte_pktmbuf_data_room_size(port->mempool) -
70 : : RTE_PKTMBUF_HEADROOM);
71 : :
72 [ # # ]: 0 : if (n_bytes_per_pkt == 0)
73 : : max_len = pktmbuf_maxlen;
74 : : else
75 : 0 : max_len = RTE_MIN(n_bytes_per_pkt, pktmbuf_maxlen);
76 : :
77 : : /* first time open, get packet number */
78 : 0 : pcap_handle = pcap_open_offline(file_name, pcap_errbuf);
79 [ # # ]: 0 : if (pcap_handle == NULL) {
80 : 0 : PORT_LOG(ERR, "Failed to open pcap file "
81 : : "'%s' for reading", file_name);
82 : 0 : goto error_exit;
83 : : }
84 : :
85 [ # # ]: 0 : while ((pkt = pcap_next(pcap_handle, &pcap_hdr)) != NULL)
86 : 0 : n_pkts++;
87 : :
88 : 0 : pcap_close(pcap_handle);
89 : :
90 : 0 : port->pkt_len = rte_zmalloc_socket("PCAP",
91 : : (sizeof(*port->pkt_len) * n_pkts), 0, socket_id);
92 [ # # ]: 0 : if (port->pkt_len == NULL) {
93 : 0 : PORT_LOG(ERR, "No enough memory");
94 : 0 : goto error_exit;
95 : : }
96 : :
97 : 0 : pkt_len_aligns = rte_malloc("PCAP",
98 : : (sizeof(*pkt_len_aligns) * n_pkts), 0);
99 [ # # ]: 0 : if (pkt_len_aligns == NULL) {
100 : 0 : PORT_LOG(ERR, "No enough memory");
101 : 0 : goto error_exit;
102 : : }
103 : :
104 : 0 : port->pkts = rte_zmalloc_socket("PCAP",
105 : : (sizeof(*port->pkts) * n_pkts), 0, socket_id);
106 [ # # ]: 0 : if (port->pkts == NULL) {
107 : 0 : PORT_LOG(ERR, "No enough memory");
108 : 0 : goto error_exit;
109 : : }
110 : :
111 : : /* open 2nd time, get pkt_len */
112 : 0 : pcap_handle = pcap_open_offline(file_name, pcap_errbuf);
113 [ # # ]: 0 : if (pcap_handle == NULL) {
114 : 0 : PORT_LOG(ERR, "Failed to open pcap file "
115 : : "'%s' for reading", file_name);
116 : 0 : goto error_exit;
117 : : }
118 : :
119 [ # # ]: 0 : for (i = 0; i < n_pkts; i++) {
120 : 0 : pcap_next(pcap_handle, &pcap_hdr);
121 : 0 : port->pkt_len[i] = RTE_MIN(max_len, pcap_hdr.len);
122 : 0 : pkt_len_aligns[i] = RTE_CACHE_LINE_ROUNDUP(
123 : : port->pkt_len[i]);
124 : 0 : total_buff_len += pkt_len_aligns[i];
125 : : }
126 : :
127 : 0 : pcap_close(pcap_handle);
128 : :
129 : : /* allocate a big trunk of data for pcap file load */
130 : 0 : buff = rte_zmalloc_socket("PCAP",
131 : : total_buff_len, 0, socket_id);
132 [ # # ]: 0 : if (buff == NULL) {
133 : 0 : PORT_LOG(ERR, "No enough memory");
134 : 0 : goto error_exit;
135 : : }
136 : :
137 : 0 : port->pkt_buff = buff;
138 : :
139 : : /* open file one last time to copy the pkt content */
140 : 0 : pcap_handle = pcap_open_offline(file_name, pcap_errbuf);
141 [ # # ]: 0 : if (pcap_handle == NULL) {
142 : 0 : PORT_LOG(ERR, "Failed to open pcap file "
143 : : "'%s' for reading", file_name);
144 : 0 : goto error_exit;
145 : : }
146 : :
147 [ # # ]: 0 : for (i = 0; i < n_pkts; i++) {
148 : 0 : pkt = pcap_next(pcap_handle, &pcap_hdr);
149 [ # # ]: 0 : rte_memcpy(buff, pkt, port->pkt_len[i]);
150 : 0 : port->pkts[i] = buff;
151 : 0 : buff += pkt_len_aligns[i];
152 : : }
153 : :
154 : 0 : pcap_close(pcap_handle);
155 : :
156 : 0 : port->n_pkts = n_pkts;
157 : :
158 : 0 : rte_free(pkt_len_aligns);
159 : :
160 : 0 : PORT_LOG(INFO, "Successfully load pcap file "
161 : : "'%s' with %u pkts",
162 : : file_name, port->n_pkts);
163 : :
164 : 0 : return 0;
165 : :
166 : 0 : error_exit:
167 : 0 : rte_free(pkt_len_aligns);
168 : 0 : rte_free(port->pkt_len);
169 : 0 : rte_free(port->pkts);
170 : 0 : rte_free(port->pkt_buff);
171 : :
172 : 0 : return -1;
173 : : }
174 : :
175 : : #define PCAP_SOURCE_LOAD(port, file_name, n_bytes, socket_id) \
176 : : pcap_source_load(port, file_name, n_bytes, socket_id)
177 : :
178 : : #else /* RTE_PORT_PCAP */
179 : :
180 : : #define PCAP_SOURCE_LOAD(port, file_name, n_bytes, socket_id) \
181 : : __extension__ ({ \
182 : : int _ret = 0; \
183 : : \
184 : : if (file_name) { \
185 : : PORT_LOG(ERR, "Source port field " \
186 : : "\"file_name\" is not NULL."); \
187 : : _ret = -1; \
188 : : } \
189 : : \
190 : : _ret; \
191 : : })
192 : :
193 : : #endif /* RTE_PORT_PCAP */
194 : :
195 : : static void *
196 : 0 : rte_port_source_create(void *params, int socket_id)
197 : : {
198 : : struct rte_port_source_params *p =
199 : : params;
200 : : struct rte_port_source *port;
201 : :
202 : : /* Check input arguments*/
203 [ # # # # ]: 0 : if ((p == NULL) || (p->mempool == NULL)) {
204 : 0 : PORT_LOG(ERR, "%s: Invalid params", __func__);
205 : 0 : return NULL;
206 : : }
207 : :
208 : : /* Memory allocation */
209 : 0 : port = rte_zmalloc_socket("PORT", sizeof(*port),
210 : : RTE_CACHE_LINE_SIZE, socket_id);
211 [ # # ]: 0 : if (port == NULL) {
212 : 0 : PORT_LOG(ERR, "%s: Failed to allocate port", __func__);
213 : 0 : return NULL;
214 : : }
215 : :
216 : : /* Initialization */
217 : 0 : port->mempool = (struct rte_mempool *) p->mempool;
218 : :
219 [ # # ]: 0 : if (p->file_name) {
220 : 0 : int status = PCAP_SOURCE_LOAD(port, p->file_name,
221 : : p->n_bytes_per_pkt, socket_id);
222 : :
223 [ # # ]: 0 : if (status < 0) {
224 : 0 : rte_free(port);
225 : : port = NULL;
226 : : }
227 : : }
228 : :
229 : : return port;
230 : : }
231 : :
232 : : static int
233 : 0 : rte_port_source_free(void *port)
234 : : {
235 : : struct rte_port_source *p =
236 : : port;
237 : :
238 : : /* Check input parameters */
239 [ # # ]: 0 : if (p == NULL)
240 : : return 0;
241 : :
242 : 0 : rte_free(p->pkt_len);
243 : 0 : rte_free(p->pkts);
244 : 0 : rte_free(p->pkt_buff);
245 : :
246 : 0 : rte_free(p);
247 : :
248 : 0 : return 0;
249 : : }
250 : :
251 : : static int
252 : 0 : rte_port_source_rx(void *port, struct rte_mbuf **pkts, uint32_t n_pkts)
253 : : {
254 : : struct rte_port_source *p = port;
255 : : uint32_t i;
256 : :
257 [ # # ]: 0 : if (rte_pktmbuf_alloc_bulk(p->mempool, pkts, n_pkts) != 0)
258 : : return 0;
259 : :
260 [ # # ]: 0 : if (p->pkt_buff != NULL) {
261 [ # # ]: 0 : for (i = 0; i < n_pkts; i++) {
262 : 0 : uint8_t *pkt_data = rte_pktmbuf_mtod(pkts[i],
263 : : uint8_t *);
264 : :
265 : 0 : rte_memcpy(pkt_data, p->pkts[p->pkt_index],
266 [ # # ]: 0 : p->pkt_len[p->pkt_index]);
267 : 0 : pkts[i]->data_len = p->pkt_len[p->pkt_index];
268 : 0 : pkts[i]->pkt_len = pkts[i]->data_len;
269 : :
270 : 0 : p->pkt_index++;
271 [ # # ]: 0 : if (p->pkt_index >= p->n_pkts)
272 : 0 : p->pkt_index = 0;
273 : : }
274 : : }
275 : :
276 : : RTE_PORT_SOURCE_STATS_PKTS_IN_ADD(p, n_pkts);
277 : :
278 : 0 : return n_pkts;
279 : : }
280 : :
281 : : static int
282 : 0 : rte_port_source_stats_read(void *port,
283 : : struct rte_port_in_stats *stats, int clear)
284 : : {
285 : : struct rte_port_source *p =
286 : : port;
287 : :
288 [ # # ]: 0 : if (stats != NULL)
289 : 0 : memcpy(stats, &p->stats, sizeof(p->stats));
290 : :
291 [ # # ]: 0 : if (clear)
292 : 0 : memset(&p->stats, 0, sizeof(p->stats));
293 : :
294 : 0 : return 0;
295 : : }
296 : :
297 : : /*
298 : : * Port SINK
299 : : */
300 : : #ifdef RTE_PORT_STATS_COLLECT
301 : :
302 : : #define RTE_PORT_SINK_STATS_PKTS_IN_ADD(port, val) \
303 : : (port->stats.n_pkts_in += val)
304 : : #define RTE_PORT_SINK_STATS_PKTS_DROP_ADD(port, val) \
305 : : (port->stats.n_pkts_drop += val)
306 : :
307 : : #else
308 : :
309 : : #define RTE_PORT_SINK_STATS_PKTS_IN_ADD(port, val)
310 : : #define RTE_PORT_SINK_STATS_PKTS_DROP_ADD(port, val)
311 : :
312 : : #endif
313 : :
314 : : struct rte_port_sink {
315 : : struct rte_port_out_stats stats;
316 : :
317 : : /* PCAP dumper handle and pkts number */
318 : : void *dumper;
319 : : uint32_t max_pkts;
320 : : uint32_t pkt_index;
321 : : uint32_t dump_finish;
322 : : };
323 : :
324 : : #ifdef RTE_PORT_PCAP
325 : :
326 : : static int
327 : 0 : pcap_sink_open(struct rte_port_sink *port,
328 : : const char *file_name,
329 : : uint32_t max_n_pkts)
330 : : {
331 : : pcap_t *tx_pcap;
332 : : pcap_dumper_t *pcap_dumper;
333 : :
334 : : /** Open a dead pcap handler for opening dumper file */
335 : 0 : tx_pcap = pcap_open_dead(DLT_EN10MB, 65535);
336 [ # # ]: 0 : if (tx_pcap == NULL) {
337 : 0 : PORT_LOG(ERR, "Cannot open pcap dead handler");
338 : 0 : return -1;
339 : : }
340 : :
341 : : /* The dumper is created using the previous pcap_t reference */
342 : 0 : pcap_dumper = pcap_dump_open(tx_pcap, file_name);
343 [ # # ]: 0 : if (pcap_dumper == NULL) {
344 : 0 : PORT_LOG(ERR, "Failed to open pcap file "
345 : : "\"%s\" for writing", file_name);
346 : 0 : return -1;
347 : : }
348 : :
349 : 0 : port->dumper = pcap_dumper;
350 : 0 : port->max_pkts = max_n_pkts;
351 : 0 : port->pkt_index = 0;
352 : 0 : port->dump_finish = 0;
353 : :
354 : 0 : PORT_LOG(INFO, "Ready to dump packets to file \"%s\"",
355 : : file_name);
356 : :
357 : 0 : return 0;
358 : : }
359 : :
360 : : static void
361 : 0 : pcap_sink_write_pkt(struct rte_port_sink *port, struct rte_mbuf *mbuf)
362 : : {
363 : 0 : uint8_t *pcap_dumper = (port->dumper);
364 : : struct pcap_pkthdr pcap_hdr;
365 : : uint8_t jumbo_pkt_buf[RTE_ETHER_MAX_JUMBO_FRAME_LEN];
366 : : uint8_t *pkt;
367 : :
368 : : /* Maximum num packets already reached */
369 [ # # ]: 0 : if (port->dump_finish)
370 : 0 : return;
371 : :
372 : 0 : pkt = rte_pktmbuf_mtod(mbuf, uint8_t *);
373 : :
374 : 0 : pcap_hdr.len = mbuf->pkt_len;
375 : 0 : pcap_hdr.caplen = pcap_hdr.len;
376 : 0 : gettimeofday(&(pcap_hdr.ts), NULL);
377 : :
378 [ # # ]: 0 : if (mbuf->nb_segs > 1) {
379 : : struct rte_mbuf *jumbo_mbuf;
380 : : uint32_t pkt_index = 0;
381 : :
382 : : /* if packet size longer than RTE_ETHER_MAX_JUMBO_FRAME_LEN,
383 : : * ignore it.
384 : : */
385 [ # # ]: 0 : if (mbuf->pkt_len > RTE_ETHER_MAX_JUMBO_FRAME_LEN)
386 : : return;
387 : :
388 [ # # ]: 0 : for (jumbo_mbuf = mbuf; jumbo_mbuf != NULL;
389 : 0 : jumbo_mbuf = jumbo_mbuf->next) {
390 : 0 : rte_memcpy(&jumbo_pkt_buf[pkt_index],
391 : 0 : rte_pktmbuf_mtod(jumbo_mbuf, uint8_t *),
392 [ # # ]: 0 : jumbo_mbuf->data_len);
393 : 0 : pkt_index += jumbo_mbuf->data_len;
394 : : }
395 : :
396 : 0 : jumbo_pkt_buf[pkt_index] = '\0';
397 : :
398 : : pkt = jumbo_pkt_buf;
399 : : }
400 : :
401 : 0 : pcap_dump(pcap_dumper, &pcap_hdr, pkt);
402 : :
403 : 0 : port->pkt_index++;
404 : :
405 [ # # # # ]: 0 : if ((port->max_pkts != 0) && (port->pkt_index >= port->max_pkts)) {
406 : 0 : port->dump_finish = 1;
407 : 0 : PORT_LOG(INFO, "Dumped %u packets to file",
408 : : port->pkt_index);
409 : : }
410 : :
411 : : }
412 : :
413 : : #define PCAP_SINK_OPEN(port, file_name, max_n_pkts) \
414 : : pcap_sink_open(port, file_name, max_n_pkts)
415 : :
416 : : #define PCAP_SINK_WRITE_PKT(port, mbuf) \
417 : : pcap_sink_write_pkt(port, mbuf)
418 : :
419 : : #define PCAP_SINK_FLUSH_PKT(dumper) \
420 : : do { \
421 : : if (dumper) \
422 : : pcap_dump_flush((pcap_dumper_t *)dumper); \
423 : : } while (0)
424 : :
425 : : #define PCAP_SINK_CLOSE(dumper) \
426 : : do { \
427 : : if (dumper) \
428 : : pcap_dump_close((pcap_dumper_t *)dumper); \
429 : : } while (0)
430 : :
431 : : #else
432 : :
433 : : #define PCAP_SINK_OPEN(port, file_name, max_n_pkts) \
434 : : __extension__ ({ \
435 : : int _ret = 0; \
436 : : \
437 : : if (file_name) { \
438 : : PORT_LOG(ERR, "Sink port field " \
439 : : "\"file_name\" is not NULL."); \
440 : : _ret = -1; \
441 : : } \
442 : : \
443 : : _ret; \
444 : : })
445 : :
446 : : #define PCAP_SINK_WRITE_PKT(port, mbuf) {}
447 : :
448 : : #define PCAP_SINK_FLUSH_PKT(dumper)
449 : :
450 : : #define PCAP_SINK_CLOSE(dumper)
451 : :
452 : : #endif
453 : :
454 : : static void *
455 : 0 : rte_port_sink_create(void *params, int socket_id)
456 : : {
457 : : struct rte_port_sink *port;
458 : : struct rte_port_sink_params *p = params;
459 : :
460 : : /* Memory allocation */
461 : 0 : port = rte_zmalloc_socket("PORT", sizeof(*port),
462 : : RTE_CACHE_LINE_SIZE, socket_id);
463 [ # # ]: 0 : if (port == NULL) {
464 : 0 : PORT_LOG(ERR, "%s: Failed to allocate port", __func__);
465 : 0 : return NULL;
466 : : }
467 : :
468 [ # # ]: 0 : if (!p)
469 : : return port;
470 : :
471 [ # # ]: 0 : if (p->file_name) {
472 : 0 : int status = PCAP_SINK_OPEN(port, p->file_name,
473 : : p->max_n_pkts);
474 : :
475 [ # # ]: 0 : if (status < 0) {
476 : 0 : rte_free(port);
477 : : port = NULL;
478 : : }
479 : : }
480 : :
481 : : return port;
482 : : }
483 : :
484 : : static int
485 : 0 : rte_port_sink_tx(void *port, struct rte_mbuf *pkt)
486 : : {
487 : : struct rte_port_sink *p = port;
488 : :
489 : : RTE_PORT_SINK_STATS_PKTS_IN_ADD(p, 1);
490 [ # # ]: 0 : if (p->dumper != NULL)
491 : 0 : PCAP_SINK_WRITE_PKT(p, pkt);
492 : 0 : rte_pktmbuf_free(pkt);
493 : : RTE_PORT_SINK_STATS_PKTS_DROP_ADD(p, 1);
494 : :
495 : 0 : return 0;
496 : : }
497 : :
498 : : static int
499 : 0 : rte_port_sink_tx_bulk(void *port, struct rte_mbuf **pkts,
500 : : uint64_t pkts_mask)
501 : : {
502 : : struct rte_port_sink *p = port;
503 : :
504 [ # # ]: 0 : if ((pkts_mask & (pkts_mask + 1)) == 0) {
505 : : uint64_t n_pkts = rte_popcount64(pkts_mask);
506 : : uint32_t i;
507 : :
508 : : RTE_PORT_SINK_STATS_PKTS_IN_ADD(p, n_pkts);
509 : : RTE_PORT_SINK_STATS_PKTS_DROP_ADD(p, n_pkts);
510 : :
511 [ # # ]: 0 : if (p->dumper) {
512 [ # # ]: 0 : for (i = 0; i < n_pkts; i++)
513 : 0 : PCAP_SINK_WRITE_PKT(p, pkts[i]);
514 : : }
515 : :
516 [ # # ]: 0 : for (i = 0; i < n_pkts; i++) {
517 : 0 : struct rte_mbuf *pkt = pkts[i];
518 : :
519 : 0 : rte_pktmbuf_free(pkt);
520 : : }
521 : :
522 : : } else {
523 [ # # ]: 0 : if (p->dumper) {
524 : : uint64_t dump_pkts_mask = pkts_mask;
525 : : uint32_t pkt_index;
526 : :
527 [ # # ]: 0 : for ( ; dump_pkts_mask; ) {
528 : : pkt_index = rte_ctz64(
529 : : dump_pkts_mask);
530 : 0 : PCAP_SINK_WRITE_PKT(p, pkts[pkt_index]);
531 : 0 : dump_pkts_mask &= ~(1LLU << pkt_index);
532 : : }
533 : : }
534 : :
535 [ # # ]: 0 : for ( ; pkts_mask; ) {
536 : : uint32_t pkt_index = rte_ctz64(pkts_mask);
537 : 0 : uint64_t pkt_mask = 1LLU << pkt_index;
538 : 0 : struct rte_mbuf *pkt = pkts[pkt_index];
539 : :
540 : : RTE_PORT_SINK_STATS_PKTS_IN_ADD(p, 1);
541 : : RTE_PORT_SINK_STATS_PKTS_DROP_ADD(p, 1);
542 : 0 : rte_pktmbuf_free(pkt);
543 : 0 : pkts_mask &= ~pkt_mask;
544 : : }
545 : : }
546 : :
547 : 0 : return 0;
548 : : }
549 : :
550 : : static int
551 : 0 : rte_port_sink_flush(void *port)
552 : : {
553 : : struct rte_port_sink *p =
554 : : port;
555 : :
556 [ # # ]: 0 : if (p == NULL)
557 : : return 0;
558 : :
559 [ # # ]: 0 : PCAP_SINK_FLUSH_PKT(p->dumper);
560 : :
561 : : return 0;
562 : : }
563 : :
564 : : static int
565 : 0 : rte_port_sink_free(void *port)
566 : : {
567 : : struct rte_port_sink *p =
568 : : port;
569 : :
570 [ # # ]: 0 : if (p == NULL)
571 : : return 0;
572 : :
573 [ # # ]: 0 : PCAP_SINK_CLOSE(p->dumper);
574 : :
575 : 0 : rte_free(p);
576 : :
577 : 0 : return 0;
578 : : }
579 : :
580 : : static int
581 : 0 : rte_port_sink_stats_read(void *port, struct rte_port_out_stats *stats,
582 : : int clear)
583 : : {
584 : : struct rte_port_sink *p =
585 : : port;
586 : :
587 [ # # ]: 0 : if (stats != NULL)
588 : 0 : memcpy(stats, &p->stats, sizeof(p->stats));
589 : :
590 [ # # ]: 0 : if (clear)
591 : 0 : memset(&p->stats, 0, sizeof(p->stats));
592 : :
593 : 0 : return 0;
594 : : }
595 : :
596 : : /*
597 : : * Summary of port operations
598 : : */
599 : : struct rte_port_in_ops rte_port_source_ops = {
600 : : .f_create = rte_port_source_create,
601 : : .f_free = rte_port_source_free,
602 : : .f_rx = rte_port_source_rx,
603 : : .f_stats = rte_port_source_stats_read,
604 : : };
605 : :
606 : : struct rte_port_out_ops rte_port_sink_ops = {
607 : : .f_create = rte_port_sink_create,
608 : : .f_free = rte_port_sink_free,
609 : : .f_tx = rte_port_sink_tx,
610 : : .f_tx_bulk = rte_port_sink_tx_bulk,
611 : : .f_flush = rte_port_sink_flush,
612 : : .f_stats = rte_port_sink_stats_read,
613 : : };
|