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