port: add pcap file dump
[dpdk.git] / lib / librte_port / rte_port_source_sink.c
1 /*-
2  *   BSD LICENSE
3  *
4  *   Copyright(c) 2010-2016 Intel Corporation. All rights reserved.
5  *   All rights reserved.
6  *
7  *   Redistribution and use in source and binary forms, with or without
8  *   modification, are permitted provided that the following conditions
9  *   are met:
10  *
11  *     * Redistributions of source code must retain the above copyright
12  *       notice, this list of conditions and the following disclaimer.
13  *     * Redistributions in binary form must reproduce the above copyright
14  *       notice, this list of conditions and the following disclaimer in
15  *       the documentation and/or other materials provided with the
16  *       distribution.
17  *     * Neither the name of Intel Corporation nor the names of its
18  *       contributors may be used to endorse or promote products derived
19  *       from this software without specific prior written permission.
20  *
21  *   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22  *   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23  *   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
24  *   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
25  *   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
26  *   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
27  *   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
28  *   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
29  *   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
30  *   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
31  *   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32  */
33 #include <stdint.h>
34 #include <string.h>
35
36 #include <rte_mbuf.h>
37 #include <rte_mempool.h>
38 #include <rte_malloc.h>
39
40 #ifdef RTE_NEXT_ABI
41
42 #include <rte_memcpy.h>
43
44 #ifdef RTE_PORT_PCAP
45 #include <rte_ether.h>
46 #include <pcap.h>
47 #endif
48
49 #else
50 #undef RTE_PORT_PCAP
51 #endif
52
53 #include "rte_port_source_sink.h"
54
55 /*
56  * Port SOURCE
57  */
58 #ifdef RTE_PORT_STATS_COLLECT
59
60 #define RTE_PORT_SOURCE_STATS_PKTS_IN_ADD(port, val) \
61         port->stats.n_pkts_in += val
62 #define RTE_PORT_SOURCE_STATS_PKTS_DROP_ADD(port, val) \
63         port->stats.n_pkts_drop += val
64
65 #else
66
67 #define RTE_PORT_SOURCE_STATS_PKTS_IN_ADD(port, val)
68 #define RTE_PORT_SOURCE_STATS_PKTS_DROP_ADD(port, val)
69
70 #endif
71
72 struct rte_port_source {
73         struct rte_port_in_stats stats;
74
75         struct rte_mempool *mempool;
76
77 #ifdef RTE_NEXT_ABI
78         /* PCAP buffers and indexes */
79         uint8_t **pkts;
80         uint8_t *pkt_buff;
81         uint32_t *pkt_len;
82         uint32_t n_pkts;
83         uint32_t pkt_index;
84 #endif
85 };
86
87 #ifdef RTE_NEXT_ABI
88
89 #ifdef RTE_PORT_PCAP
90
91 /**
92  * Load PCAP file, allocate and copy packets in the file to memory
93  *
94  * @param p
95  *   Parameters for source port
96  * @param port
97  *   Handle to source port
98  * @param socket_id
99  *   Socket id where the memory is created
100  * @return
101  *   0 on SUCCESS
102  *   error code otherwise
103  */
104 static int
105 pcap_source_load(struct rte_port_source_params *p,
106                 struct rte_port_source *port,
107                 int socket_id)
108 {
109         uint32_t status = 0;
110         uint32_t n_pkts = 0;
111         uint32_t i;
112         uint32_t *pkt_len_aligns = NULL;
113         size_t total_buff_len = 0;
114         pcap_t *pcap_handle;
115         char pcap_errbuf[PCAP_ERRBUF_SIZE];
116         uint32_t max_len;
117         struct pcap_pkthdr pcap_hdr;
118         const uint8_t *pkt;
119         uint8_t *buff = NULL;
120         uint32_t pktmbuf_maxlen = (uint32_t)
121                         (rte_pktmbuf_data_room_size(port->mempool) -
122                         RTE_PKTMBUF_HEADROOM);
123
124         if (p->file_name == NULL)
125                 return 0;
126
127         if (p->n_bytes_per_pkt == 0)
128                 max_len = pktmbuf_maxlen;
129         else
130                 max_len = RTE_MIN(p->n_bytes_per_pkt, pktmbuf_maxlen);
131
132         /* first time open, get packet number */
133         pcap_handle = pcap_open_offline(p->file_name, pcap_errbuf);
134         if (pcap_handle == NULL) {
135                 status = -ENOENT;
136                 goto error_exit;
137         }
138
139         while ((pkt = pcap_next(pcap_handle, &pcap_hdr)) != NULL)
140                 n_pkts++;
141
142         pcap_close(pcap_handle);
143
144         port->pkt_len = rte_zmalloc_socket("PCAP",
145                 (sizeof(*port->pkt_len) * n_pkts), 0, socket_id);
146         if (port->pkt_len == NULL) {
147                 status = -ENOMEM;
148                 goto error_exit;
149         }
150
151         pkt_len_aligns = rte_malloc("PCAP",
152                 (sizeof(*pkt_len_aligns) * n_pkts), 0);
153         if (pkt_len_aligns == NULL) {
154                 status = -ENOMEM;
155                 goto error_exit;
156         }
157
158         port->pkts = rte_zmalloc_socket("PCAP",
159                 (sizeof(*port->pkts) * n_pkts), 0, socket_id);
160         if (port->pkts == NULL) {
161                 status = -ENOMEM;
162                 goto error_exit;
163         }
164
165         /* open 2nd time, get pkt_len */
166         pcap_handle = pcap_open_offline(p->file_name, pcap_errbuf);
167         if (pcap_handle == NULL) {
168                 status = -ENOENT;
169                 goto error_exit;
170         }
171
172         for (i = 0; i < n_pkts; i++) {
173                 pkt = pcap_next(pcap_handle, &pcap_hdr);
174                 port->pkt_len[i] = RTE_MIN(max_len, pcap_hdr.len);
175                 pkt_len_aligns[i] = RTE_CACHE_LINE_ROUNDUP(
176                         port->pkt_len[i]);
177                 total_buff_len += pkt_len_aligns[i];
178         }
179
180         pcap_close(pcap_handle);
181
182         /* allocate a big trunk of data for pcap file load */
183         buff = rte_zmalloc_socket("PCAP",
184                 total_buff_len, 0, socket_id);
185         if (buff == NULL) {
186                 status = -ENOMEM;
187                 goto error_exit;
188         }
189
190         port->pkt_buff = buff;
191
192         /* open file one last time to copy the pkt content */
193         pcap_handle = pcap_open_offline(p->file_name, pcap_errbuf);
194         if (pcap_handle == NULL) {
195                 status = -ENOENT;
196                 goto error_exit;
197         }
198
199         for (i = 0; i < n_pkts; i++) {
200                 pkt = pcap_next(pcap_handle, &pcap_hdr);
201                 rte_memcpy(buff, pkt, port->pkt_len[i]);
202                 port->pkts[i] = buff;
203                 buff += pkt_len_aligns[i];
204         }
205
206         pcap_close(pcap_handle);
207
208         port->n_pkts = n_pkts;
209
210         rte_free(pkt_len_aligns);
211
212         return 0;
213
214 error_exit:
215         if (pkt_len_aligns)
216                 rte_free(pkt_len_aligns);
217         if (port->pkt_len)
218                 rte_free(port->pkt_len);
219         if (port->pkts)
220                 rte_free(port->pkts);
221         if (port->pkt_buff)
222                 rte_free(port->pkt_buff);
223
224         return status;
225 }
226
227 #else
228 static int
229 pcap_source_load(__rte_unused struct rte_port_source_params *p,
230                 struct rte_port_source *port,
231                 __rte_unused int socket_id)
232 {
233         port->pkt_buff = NULL;
234         port->pkt_len = NULL;
235         port->pkts = NULL;
236         port->pkt_index = 0;
237
238         return -ENOTSUP;
239 }
240 #endif /* RTE_PORT_PCAP */
241
242 #endif
243
244 static void *
245 rte_port_source_create(void *params, int socket_id)
246 {
247         struct rte_port_source_params *p =
248                         (struct rte_port_source_params *) params;
249         struct rte_port_source *port;
250
251         /* Check input arguments*/
252         if ((p == NULL) || (p->mempool == NULL)) {
253                 RTE_LOG(ERR, PORT, "%s: Invalid params\n", __func__);
254                 return NULL;
255         }
256
257         /* Memory allocation */
258         port = rte_zmalloc_socket("PORT", sizeof(*port),
259                         RTE_CACHE_LINE_SIZE, socket_id);
260         if (port == NULL) {
261                 RTE_LOG(ERR, PORT, "%s: Failed to allocate port\n", __func__);
262                 return NULL;
263         }
264
265         /* Initialization */
266         port->mempool = (struct rte_mempool *) p->mempool;
267
268 #ifdef RTE_NEXT_ABI
269
270         /* pcap file load and initialization */
271         int status = pcap_source_load(p, port, socket_id);
272
273         if (status == 0) {
274                 if (port->pkt_buff != NULL) {
275                         RTE_LOG(INFO, PORT, "Successfully load pcap file "
276                                 "'%s' with %u pkts\n",
277                                 p->file_name, port->n_pkts);
278                 }
279         } else if (status != -ENOTSUP) {
280                 /* ENOTSUP is not treated as error */
281                 switch (status) {
282                 case -ENOENT:
283                         RTE_LOG(ERR, PORT, "%s: Failed to open pcap file "
284                                 "'%s' for reading\n",
285                                 __func__, p->file_name);
286                         break;
287                 case -ENOMEM:
288                         RTE_LOG(ERR, PORT, "%s: Not enough memory\n",
289                                 __func__);
290                         break;
291                 default:
292                         RTE_LOG(ERR, PORT, "%s: Failed to enable PCAP "
293                                 "support for unknown reason\n",
294                                 __func__);
295                         break;
296                 }
297
298                 rte_free(port);
299                 port = NULL;
300         }
301
302 #endif
303
304         return port;
305 }
306
307 static int
308 rte_port_source_free(void *port)
309 {
310         struct rte_port_source *p =
311                         (struct rte_port_source *)port;
312
313         /* Check input parameters */
314         if (p == NULL)
315                 return 0;
316
317 #ifdef RTE_NEXT_ABI
318
319         if (p->pkt_len)
320                 rte_free(p->pkt_len);
321         if (p->pkts)
322                 rte_free(p->pkts);
323         if (p->pkt_buff)
324                 rte_free(p->pkt_buff);
325 #endif
326
327         rte_free(p);
328
329         return 0;
330 }
331
332 static int
333 rte_port_source_rx(void *port, struct rte_mbuf **pkts, uint32_t n_pkts)
334 {
335         struct rte_port_source *p = (struct rte_port_source *) port;
336         uint32_t i;
337
338         if (rte_mempool_get_bulk(p->mempool, (void **) pkts, n_pkts) != 0)
339                 return 0;
340
341         for (i = 0; i < n_pkts; i++) {
342                 rte_mbuf_refcnt_set(pkts[i], 1);
343                 rte_pktmbuf_reset(pkts[i]);
344         }
345
346 #ifdef RTE_NEXT_ABI
347
348         if (p->pkt_buff != NULL) {
349                 for (i = 0; i < n_pkts; i++) {
350                         uint8_t *pkt_data = rte_pktmbuf_mtod(pkts[i],
351                                 uint8_t *);
352
353                         rte_memcpy(pkt_data, p->pkts[p->pkt_index],
354                                         p->pkt_len[p->pkt_index]);
355                         pkts[i]->data_len = p->pkt_len[p->pkt_index];
356                         pkts[i]->pkt_len = pkts[i]->data_len;
357
358                         p->pkt_index++;
359                         if (p->pkt_index >= p->n_pkts)
360                                 p->pkt_index = 0;
361                 }
362         }
363
364 #endif
365
366         RTE_PORT_SOURCE_STATS_PKTS_IN_ADD(p, n_pkts);
367
368         return n_pkts;
369 }
370
371 static int
372 rte_port_source_stats_read(void *port,
373                 struct rte_port_in_stats *stats, int clear)
374 {
375         struct rte_port_source *p =
376                 (struct rte_port_source *) port;
377
378         if (stats != NULL)
379                 memcpy(stats, &p->stats, sizeof(p->stats));
380
381         if (clear)
382                 memset(&p->stats, 0, sizeof(p->stats));
383
384         return 0;
385 }
386
387 /*
388  * Port SINK
389  */
390 #ifdef RTE_PORT_STATS_COLLECT
391
392 #define RTE_PORT_SINK_STATS_PKTS_IN_ADD(port, val) \
393         (port->stats.n_pkts_in += val)
394 #define RTE_PORT_SINK_STATS_PKTS_DROP_ADD(port, val) \
395         (port->stats.n_pkts_drop += val)
396
397 #else
398
399 #define RTE_PORT_SINK_STATS_PKTS_IN_ADD(port, val)
400 #define RTE_PORT_SINK_STATS_PKTS_DROP_ADD(port, val)
401
402 #endif
403
404 struct rte_port_sink {
405         struct rte_port_out_stats stats;
406
407         /* PCAP dumper handle and pkts number */
408         void *dumper;
409         uint32_t max_pkts;
410         uint32_t pkt_index;
411         uint32_t dump_finish;
412 };
413
414 #ifdef RTE_PORT_PCAP
415
416 /**
417  * Open PCAP file for dumping packets to the file later
418  *
419  * @param port
420  *   Handle to sink port
421  * @param p
422  *   Sink port parameter
423  * @return
424  *   0 on SUCCESS
425  *   error code otherwise
426  */
427 static int
428 pcap_sink_open(struct rte_port_sink *port,
429                 __rte_unused struct rte_port_sink_params *p)
430 {
431         pcap_t *tx_pcap;
432         pcap_dumper_t *pcap_dumper;
433
434         if (p->file_name == NULL) {
435                 port->dumper = NULL;
436                 port->max_pkts = 0;
437                 port->pkt_index = 0;
438                 port->dump_finish = 0;
439                 return 0;
440         }
441
442         /** Open a dead pcap handler for opening dumper file */
443         tx_pcap = pcap_open_dead(DLT_EN10MB, 65535);
444         if (tx_pcap == NULL)
445                 return -ENOENT;
446
447         /* The dumper is created using the previous pcap_t reference */
448         pcap_dumper = pcap_dump_open(tx_pcap, p->file_name);
449         if (pcap_dumper == NULL)
450                 return -ENOENT;
451
452         port->dumper = pcap_dumper;
453         port->max_pkts = p->max_n_pkts;
454         port->pkt_index = 0;
455         port->dump_finish = 0;
456
457         return 0;
458 }
459
460 uint8_t jumbo_pkt_buf[ETHER_MAX_JUMBO_FRAME_LEN];
461
462 /**
463  * Dump a packet to PCAP dumper
464  *
465  * @param p
466  *   Handle to sink port
467  * @param mbuf
468  *   Handle to mbuf structure holding the packet
469  */
470 static void
471 pcap_sink_dump_pkt(struct rte_port_sink *port, struct rte_mbuf *mbuf)
472 {
473         uint8_t *pcap_dumper = (uint8_t *)(port->dumper);
474         struct pcap_pkthdr pcap_hdr;
475         uint8_t *pkt;
476
477         /* Maximum num packets already reached */
478         if (port->dump_finish)
479                 return;
480
481         pkt = rte_pktmbuf_mtod(mbuf, uint8_t *);
482
483         pcap_hdr.len = mbuf->pkt_len;
484         pcap_hdr.caplen = pcap_hdr.len;
485         gettimeofday(&(pcap_hdr.ts), NULL);
486
487         if (mbuf->nb_segs > 1) {
488                 struct rte_mbuf *jumbo_mbuf;
489                 uint32_t pkt_index = 0;
490
491                 /* if packet size longer than ETHER_MAX_JUMBO_FRAME_LEN,
492                  * ignore it.
493                  */
494                 if (mbuf->pkt_len > ETHER_MAX_JUMBO_FRAME_LEN)
495                         return;
496
497                 for (jumbo_mbuf = mbuf; jumbo_mbuf != NULL;
498                                 jumbo_mbuf = jumbo_mbuf->next) {
499                         rte_memcpy(&jumbo_pkt_buf[pkt_index],
500                                 rte_pktmbuf_mtod(jumbo_mbuf, uint8_t *),
501                                 jumbo_mbuf->data_len);
502                         pkt_index += jumbo_mbuf->data_len;
503                 }
504
505                 jumbo_pkt_buf[pkt_index] = '\0';
506
507                 pkt = jumbo_pkt_buf;
508         }
509
510         pcap_dump(pcap_dumper, &pcap_hdr, pkt);
511
512         port->pkt_index++;
513
514         if ((port->max_pkts != 0) && (port->pkt_index >= port->max_pkts)) {
515                 port->dump_finish = 1;
516                 RTE_LOG(INFO, PORT, "Dumped %u packets to file\n",
517                                 port->pkt_index);
518         }
519
520 }
521
522 /**
523  * Flush pcap dumper
524  *
525  * @param dumper
526  *   Handle to pcap dumper
527  */
528
529 static void
530 pcap_sink_flush_pkt(void *dumper)
531 {
532         pcap_dumper_t *pcap_dumper = (pcap_dumper_t *)dumper;
533
534         pcap_dump_flush(pcap_dumper);
535 }
536
537 /**
538  * Close a PCAP dumper handle
539  *
540  * @param dumper
541  *   Handle to pcap dumper
542  */
543 static void
544 pcap_sink_close(void *dumper)
545 {
546         pcap_dumper_t *pcap_dumper = (pcap_dumper_t *)dumper;
547
548         pcap_dump_close(pcap_dumper);
549 }
550
551 #else
552
553 static int
554 pcap_sink_open(struct rte_port_sink *port,
555                 __rte_unused struct rte_port_sink_params *p)
556 {
557         port->dumper = NULL;
558         port->max_pkts = 0;
559         port->pkt_index = 0;
560         port->dump_finish = 0;
561
562         return -ENOTSUP;
563 }
564
565 static void
566 pcap_sink_dump_pkt(__rte_unused struct rte_port_sink *port,
567                 __rte_unused struct rte_mbuf *mbuf) {}
568
569 static void
570 pcap_sink_flush_pkt(__rte_unused void *dumper) {}
571
572 static void
573 pcap_sink_close(__rte_unused void *dumper) {}
574
575 #endif
576
577 static void *
578 rte_port_sink_create(__rte_unused void *params, int socket_id)
579 {
580         struct rte_port_sink *port;
581         struct rte_port_sink_params *p = params;
582         int status;
583
584         /* Memory allocation */
585         port = rte_zmalloc_socket("PORT", sizeof(*port),
586                         RTE_CACHE_LINE_SIZE, socket_id);
587         if (port == NULL) {
588                 RTE_LOG(ERR, PORT, "%s: Failed to allocate port\n", __func__);
589                 return NULL;
590         }
591
592         /* Try to open PCAP file for dumping, if possible */
593         status = pcap_sink_open(port, p);
594         if (status == 0) {
595                 if (port->dumper != NULL)
596                         RTE_LOG(INFO, PORT, "Ready to dump packets "
597                                 "to file %s\n", p->file_name);
598
599         } else if (status != -ENOTSUP) {
600                 if (status == -ENOENT)
601                         RTE_LOG(ERR, PORT, "%s: Failed to open pcap file "
602                                 "%s for writing\n", __func__,
603                                 p->file_name);
604                 else
605                         RTE_LOG(ERR, PORT, "%s: Failed to enable pcap "
606                                 "support for unknown reason\n", __func__);
607
608                 rte_free(port);
609                 port = NULL;
610         }
611
612         return port;
613 }
614
615 static int
616 rte_port_sink_tx(void *port, struct rte_mbuf *pkt)
617 {
618         __rte_unused struct rte_port_sink *p = (struct rte_port_sink *) port;
619
620         RTE_PORT_SINK_STATS_PKTS_IN_ADD(p, 1);
621         if (p->dumper != NULL)
622                 pcap_sink_dump_pkt(p, pkt);
623         rte_pktmbuf_free(pkt);
624         RTE_PORT_SINK_STATS_PKTS_DROP_ADD(p, 1);
625
626         return 0;
627 }
628
629 static int
630 rte_port_sink_tx_bulk(void *port, struct rte_mbuf **pkts,
631         uint64_t pkts_mask)
632 {
633         __rte_unused struct rte_port_sink *p = (struct rte_port_sink *) port;
634
635         if ((pkts_mask & (pkts_mask + 1)) == 0) {
636                 uint64_t n_pkts = __builtin_popcountll(pkts_mask);
637                 uint32_t i;
638
639                 RTE_PORT_SINK_STATS_PKTS_IN_ADD(p, n_pkts);
640                 RTE_PORT_SINK_STATS_PKTS_DROP_ADD(p, n_pkts);
641
642                 if (p->dumper) {
643                         for (i = 0; i < n_pkts; i++) {
644                                 struct rte_mbuf *pkt = pkts[i];
645
646                                 pcap_sink_dump_pkt(p, pkt);
647                         }
648                 }
649
650                 for (i = 0; i < n_pkts; i++) {
651                         struct rte_mbuf *pkt = pkts[i];
652
653                         rte_pktmbuf_free(pkt);
654                 }
655
656         } else {
657                 if (p->dumper) {
658                         uint64_t dump_pkts_mask = pkts_mask;
659                         uint32_t pkt_index;
660
661                         for ( ; dump_pkts_mask; ) {
662                                 pkt_index = __builtin_ctzll(
663                                         dump_pkts_mask);
664                                 pcap_sink_dump_pkt(p, pkts[pkt_index]);
665                                 dump_pkts_mask &= ~(1LLU << pkt_index);
666                         }
667                 }
668
669                 for ( ; pkts_mask; ) {
670                         uint32_t pkt_index = __builtin_ctzll(pkts_mask);
671                         uint64_t pkt_mask = 1LLU << pkt_index;
672                         struct rte_mbuf *pkt = pkts[pkt_index];
673
674                         RTE_PORT_SINK_STATS_PKTS_IN_ADD(p, 1);
675                         RTE_PORT_SINK_STATS_PKTS_DROP_ADD(p, 1);
676                         rte_pktmbuf_free(pkt);
677                         pkts_mask &= ~pkt_mask;
678                 }
679         }
680
681         return 0;
682 }
683
684 static int
685 rte_port_sink_flush(void *port)
686 {
687         struct rte_port_sink *p = (struct rte_port_sink *)port;
688
689         if (p->dumper != NULL)
690                 pcap_sink_flush_pkt(p->dumper);
691
692         return 0;
693 }
694
695 static int
696 rte_port_sink_free(void *port)
697 {
698         struct rte_port_sink *p =
699                         (struct rte_port_sink *)port;
700         /* Check input parameters */
701         if (p == NULL)
702                 return 0;
703
704         if (p->dumper != NULL)
705                 pcap_sink_close(p->dumper);
706
707         rte_free(p);
708
709         return 0;
710 }
711
712 static int
713 rte_port_sink_stats_read(void *port, struct rte_port_out_stats *stats,
714                 int clear)
715 {
716         struct rte_port_sink *p =
717                 (struct rte_port_sink *) port;
718
719         if (stats != NULL)
720                 memcpy(stats, &p->stats, sizeof(p->stats));
721
722         if (clear)
723                 memset(&p->stats, 0, sizeof(p->stats));
724
725         return 0;
726 }
727
728 /*
729  * Summary of port operations
730  */
731 struct rte_port_in_ops rte_port_source_ops = {
732         .f_create = rte_port_source_create,
733         .f_free = rte_port_source_free,
734         .f_rx = rte_port_source_rx,
735         .f_stats = rte_port_source_stats_read,
736 };
737
738 struct rte_port_out_ops rte_port_sink_ops = {
739         .f_create = rte_port_sink_create,
740         .f_free = rte_port_sink_free,
741         .f_tx = rte_port_sink_tx,
742         .f_tx_bulk = rte_port_sink_tx_bulk,
743         .f_flush = rte_port_sink_flush,
744         .f_stats = rte_port_sink_stats_read,
745 };