1/*
2 * Copyright (c) 2016  Intel Corporation.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at:
6 *
7 *     http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16#ifndef TEST_TLE_UDP_STREAM_GEN_H_
17#define TEST_TLE_UDP_STREAM_GEN_H_
18
19#include <sys/types.h>
20
21#include <stdio.h>
22#include <map>
23#include <string>
24#include <algorithm>
25#include <arpa/inet.h>
26#include <netinet/ip6.h>
27#include <sys/socket.h>
28#include <netdb.h>
29#include <gtest/gtest.h>
30
31#include <rte_version.h>
32
33#include <tle_udp.h>
34#include <tle_event.h>
35
36#include "test_common.h"
37
38#define RX_NO_OFFLOAD 0
39#define TX_NO_OFFLOAD 0
40#define CTX_MAX_RBUFS 0x100
41#define CTX_MAX_SBUFS 0x100
42
43#define RX_PCAP "rx_pcap.cap"
44#define TX_PCAP "tx_pcap.cap"
45
46/*
47 * Check DPDK version:
48 * Previous "eth_pcap" was changed to "net_pcap" after DPDK 16.07.
49 * Use correct vdev name depending on version.
50 */
51#if (RTE_VERSION_NUM(16, 7, 0, 0) < \
52	RTE_VERSION_NUM(RTE_VER_YEAR, RTE_VER_MONTH, 0, 0))
53	#define VDEV_NAME "net_pcap0"
54#else
55	#define VDEV_NAME "eth_pcap0"
56#endif
57
58using namespace std;
59
60extern struct rte_mempool *mbuf_pool;
61
62/* Dummy lookup functions, TX operations are not performed in these tests */
63
64static int
65lookup4_function(void *opaque, const struct in_addr *addr, struct tle_dest *res)
66{
67	struct in_addr route;
68	struct ether_hdr *eth;
69	struct ipv4_hdr *ip4h;
70	auto routes = static_cast<map<string, tle_dev *> *>(opaque);
71
72	/* Check all routes added in map for a match with dest *addr */
73	for (auto it = routes->begin(); it != routes->end(); ++it) {
74		inet_pton(AF_INET, it->first.c_str(), &route);
75
76		/* If it matches then fill *res and return with 0 code */
77		if (memcmp(&route, addr, sizeof(struct in_addr)) == 0) {
78			memset(res, 0, sizeof(*res));
79			res->dev = it->second;
80			res->mtu = 1500;
81			res->l2_len = sizeof(*eth);
82			res->l3_len = sizeof(*ip4h);
83			res->head_mp = mbuf_pool;
84			eth = (struct ether_hdr *)res->hdr;
85			eth->ether_type = rte_cpu_to_be_16(ETHER_TYPE_IPv4);
86			ip4h = (struct ipv4_hdr *)(eth + 1);
87			ip4h->version_ihl = (4 << 4) |
88				(sizeof(*ip4h) / IPV4_IHL_MULTIPLIER);
89			ip4h->time_to_live = 64;
90			ip4h->next_proto_id = IPPROTO_UDP;
91			ip4h->fragment_offset = 0;
92
93			return 0;
94		}
95	}
96
97	return -ENOENT;
98}
99
100static int
101lookup6_function(void *opaque, const struct in6_addr *addr,
102	struct tle_dest *res)
103{
104	struct ether_hdr *eth;
105	struct ipv6_hdr *ip6h;
106	struct in6_addr route;
107	auto routes = static_cast<map<string, tle_dev *> *>(opaque);
108
109	/* Check all routes added in map for a match with dest *addr */
110	for (auto it = routes->begin(); it != routes->end(); ++it) {
111		inet_pton(AF_INET6, it->first.c_str(), &route);
112
113		/* If it matches then fill *res and return with 0 code */
114		if (memcmp(&route, addr, sizeof(struct in6_addr)) == 0) {
115			memset(res, 0, sizeof(*res));
116			res->dev = it->second;
117			res->mtu = 1500;
118			res->l2_len = sizeof(*eth);
119			res->l3_len = sizeof(*ip6h);
120			res->head_mp = mbuf_pool;
121			eth = (struct ether_hdr *)res->hdr;
122			eth->ether_type = rte_cpu_to_be_16(ETHER_TYPE_IPv6);
123			ip6h = (struct ipv6_hdr *)(eth + 1);
124			ip6h->vtc_flow = 6 << 4;
125			ip6h->proto = IPPROTO_UDP;
126			ip6h->hop_limits = 64;
127
128			return 0;
129		}
130	}
131	return -ENOENT;
132}
133
134/*
135 * Structures used to describe test instances:
136 * test_str - main structure for describing test case instance; contains
137 *            instance description, and vectors with information about
138 *            devices, streams & streams to be generated for RX/TX path.
139 * dev_s    - structure describing single device; contains device addresses,
140 *            checksum offload information and expected number of received /
141 *            transmitted packets.
142 *            packets for that device in scenario.
143 * stream_s - structure describing single stream to be created; contains
144 *            information on local & remote IP's and port numbers, expected
145 *            number of received and transmitted packets.
146 * stream_g - structure describing a stream which to generate via scapy script;
147 *            Contains information on IP addresses and port numbers and if
148 *            L3/L4 checksums should be incorrectly calculated.
149 *            In future: if packet should be fragmented.
150 */
151
152struct stream_g {
153	int family;
154	string src_ip;
155	string dst_ip;
156	int src_port;
157	int dst_port;
158	int nb_pkts;
159	bool bad_chksum_l3;
160	bool bad_chksum_l4;
161	bool fragment;
162};
163
164struct stream_s {
165	int family;
166	int l_port;
167	int r_port;
168	string l_ip;
169	string r_ip;
170	int exp_pkts_rx;
171	int exp_pkts_tx;
172	int act_pkts_rx;
173	int act_pkts_tx;
174	tle_stream *ptr;
175};
176
177struct dev_s {
178	string l_ipv4;
179	string l_ipv6;
180	uint64_t rx_offload;
181	uint64_t tx_offload;
182	int exp_pkts_bulk_rx;
183	int exp_pkts_bulk_tx;
184	int exp_pkts_enoent;
185	int act_pkts_bulk_rx;
186	int act_pkts_bulk_tx;
187	int act_pkts_enoent;
188	tle_dev *ptr;
189};
190
191struct test_str {
192	string test_desc;
193	vector<dev_s> devs;
194	vector<stream_s> streams;
195	vector<stream_g> gen_streams;
196};
197
198const char *vdevargs = "rx_pcap=" RX_PCAP ",tx_pcap=" TX_PCAP;
199
200class test_tle_udp_gen_base : public testing::TestWithParam<test_str> {
201public:
202
203	tle_ctx *setup_ctx(void);
204	tle_dev *setup_dev(tle_ctx *ctx, uint64_t rx_offload,
205		uint64_t tx_offload, const char *local_ipv4,
206		const char *local_ipv6);
207	tle_evq *setup_evq(void);
208	tle_event *setup_event(void);
209	tle_stream *setup_stream(struct tle_ctx *ctx, int family,
210		const char *l_ip, const char *r_ip, int l_port, int r_port);
211	int setup_devices(dpdk_port_t *portid);
212	int cleanup_devices(dpdk_port_t portid);
213	int prepare_pcaps(string l_ip, string r_ip, int l_port, int r_port,
214		int nb_pkts, int l3_chksum, int l4_chksum, string rx_pcap_dest);
215
216	int cleanup_pcaps(const char *file);
217	int close_streams(vector<struct stream_s> streams);
218	int del_devs(vector<struct dev_s> devs);
219
220	virtual void SetUp(void)
221	{
222		nb_ports = 1;
223		tp = GetParam();
224
225		/* Usual tldk stuff below -> ctx, dev, events etc. */
226		ctx = setup_ctx();
227		ASSERT_NE(ctx, nullptr);
228
229		evq = setup_evq();
230		ASSERT_NE(evq, nullptr);
231
232		for (auto &d : tp.devs) {
233			dev = setup_dev(ctx, d.rx_offload, d.tx_offload,
234				d.l_ipv4.c_str(), d.l_ipv6.c_str());
235			ASSERT_NE(dev, nullptr);
236
237			/* Initialize counters for verifying results */
238			d.act_pkts_bulk_rx = 0;
239			d.act_pkts_bulk_tx = 0;
240			d.act_pkts_enoent = 0;
241
242			/* Save pointer to device */
243			d.ptr = dev;
244		}
245
246		for (auto &s : tp.streams) {
247			stream = setup_stream(ctx, s.family,
248					s.l_ip.c_str(), s.r_ip.c_str(),
249					s.l_port, s.r_port);
250			ASSERT_NE(stream, nullptr);
251
252			/* Initialize counters for verifying results */
253			s.act_pkts_rx = 0;
254			s.act_pkts_tx = 0;
255
256			/* Save pointer to stream */
257			s.ptr = stream;
258
259			/* Find which dev has the same address as streams
260			 * local address and save destination for later use
261			 * in lookup functions
262			 */
263			if (s.family == AF_INET) {
264				for (auto &d : tp.devs) {
265					if (s.l_ip.compare(d.l_ipv4) == 0)
266						routes4.insert(pair<string,
267							tle_dev *>(s.r_ip,
268								d.ptr));
269				}
270			} else if (s.family == AF_INET6) {
271				for (auto &d : tp.devs) {
272					if (s.l_ip.compare(d.l_ipv6) == 0)
273						routes6.insert(pair<string,
274							tle_dev *>(s.r_ip,
275								d.ptr));
276				}
277			}
278		}
279
280		/* setup pcap/eth devices */
281		setup_devices(&portid);
282	}
283
284	virtual void TearDown(void)
285	{
286		/*
287		 * Remember to shutdown & detach rte devices
288		 * and clean / delete .pcap files so not to
289		 * interfere with next test
290		 */
291		close_streams(tp.streams);
292		del_devs(tp.devs);
293		tle_ctx_destroy(ctx);
294		cleanup_devices(portid);
295		cleanup_pcaps(RX_PCAP);
296		cleanup_pcaps(TX_PCAP);
297	}
298
299	dpdk_port_t nb_ports;
300	dpdk_port_t portid;
301	uint32_t socket_id;
302	uint32_t max_events;
303	struct tle_ctx *ctx;
304	struct tle_dev *dev;
305	struct tle_evq *evq;
306	struct tle_stream *stream;
307	map<string, tle_dev *> routes4;
308	map<string, tle_dev *> routes6;
309	test_str tp;
310	const void *cb;
311};
312
313int
314test_tle_udp_gen_base::setup_devices(dpdk_port_t *portid)
315{
316	/* attach + configure + start pmd device */
317	if (rte_eal_hotplug_add("vdev", VDEV_NAME, vdevargs) < 0 ||
318			rte_eth_dev_get_port_by_name(VDEV_NAME, portid) != 0)
319		return -1;
320	cb = rte_eth_add_rx_callback(*portid, 0,
321		typen_rx_callback, nullptr);
322	if (port_init(*portid, mbuf_pool) != 0)
323		return -1;
324
325	return 0;
326}
327
328int
329test_tle_udp_gen_base::cleanup_devices(dpdk_port_t portid)
330{
331	/* release mbufs + detach device */
332	char name[RTE_ETH_NAME_MAX_LEN];
333
334	rte_eth_dev_stop(portid);
335	rte_eth_dev_close(portid);
336	rte_eal_hotplug_remove("vdev", VDEV_NAME);
337
338	return 0;
339}
340
341int
342test_tle_udp_gen_base::prepare_pcaps(string l_ip, string r_ip, int l_port,
343	int r_port, int nb_pkts, int l3_chksum, int l4_chksum,
344	string rx_pcap_dest)
345{
346	string py_cmd;
347
348	/* generate pcap rx & tx files * for tests using scapy */
349	py_cmd = "python " + string(binpath) + "/test_scapy_gen.py ";
350	py_cmd = py_cmd + " " + l_ip + " " + r_ip + " " +
351			to_string(l_port) + " " + to_string(r_port) + " " +
352			to_string(nb_pkts);
353
354	if (l3_chksum > 0)
355		py_cmd = py_cmd + " -bc3 " + to_string(l3_chksum);
356	if (l4_chksum > 0)
357		py_cmd = py_cmd + " -bc4 " + to_string(l4_chksum);
358	py_cmd = py_cmd + " " + rx_pcap_dest;
359	system(py_cmd.c_str());
360	return 0;
361}
362
363int
364test_tle_udp_gen_base::cleanup_pcaps(const char *file)
365{
366	if (remove(file) != 0)
367		perror("Error deleting pcap file");
368
369	return 0;
370}
371
372tle_ctx *
373test_tle_udp_gen_base::setup_ctx(void)
374{
375
376	struct tle_ctx *ctx;
377	struct tle_ctx_param ctx_prm;
378
379	memset(&ctx_prm, 0, sizeof(ctx_prm));
380	ctx_prm.socket_id = SOCKET_ID_ANY;
381	ctx_prm.max_streams = 0x10;
382	ctx_prm.max_stream_rbufs = CTX_MAX_RBUFS;
383	ctx_prm.max_stream_sbufs = CTX_MAX_SBUFS;
384	ctx_prm.lookup4 = lookup4_function;
385	ctx_prm.lookup6 = lookup6_function;
386	ctx_prm.lookup4_data = &routes4;
387	ctx_prm.lookup6_data = &routes6;
388
389	ctx = tle_ctx_create(&ctx_prm);
390
391	return ctx;
392}
393
394struct tle_dev *
395test_tle_udp_gen_base::setup_dev(struct tle_ctx *ctx, uint64_t rx_offload,
396	uint64_t tx_offload, const char *l_ipv4, const char *l_ipv6)
397{
398	struct tle_dev *dev;
399	struct tle_dev_param dev_prm;
400
401	memset(&dev_prm, 0, sizeof(dev_prm));
402	dev_prm.rx_offload = RX_NO_OFFLOAD;
403	dev_prm.tx_offload = TX_NO_OFFLOAD;
404	if (l_ipv4 != NULL)
405		inet_pton(AF_INET, l_ipv4, &(dev_prm).local_addr4);
406	if (l_ipv6 != NULL)
407		inet_pton(AF_INET6, l_ipv6, &(dev_prm).local_addr6);
408
409	dev = tle_add_dev(ctx, &dev_prm);
410
411	return dev;
412}
413
414struct tle_evq *
415test_tle_udp_gen_base::setup_evq()
416{
417	uint32_t socket_id;
418	uint32_t max_events;
419	struct tle_evq_param evq_params;
420	struct tle_evq *evq;
421
422	socket_id = SOCKET_ID_ANY;
423	max_events = 10;
424	memset(&evq_params, 0, sizeof(struct tle_evq_param));
425
426	evq_params.socket_id = socket_id;
427	evq_params.max_events = max_events;
428	evq = tle_evq_create(&evq_params);
429	return evq;
430}
431
432struct tle_stream *
433test_tle_udp_gen_base::setup_stream(struct tle_ctx *ctx, int family,
434	const char *l_ip, const char *r_ip, int l_port, int r_port)
435{
436	struct tle_stream *stream;
437	struct tle_udp_stream_param stream_prm;
438	struct sockaddr_in *ip4_addr;
439	struct sockaddr_in6 *ip6_addr;
440	int32_t ret;
441
442	memset(&stream_prm, 0, sizeof(stream_prm));
443
444	if (family == AF_INET) {
445		ip4_addr = (struct sockaddr_in *) &stream_prm.local_addr;
446		ip4_addr->sin_family = AF_INET;
447		ip4_addr->sin_port = htons(l_port);
448		ip4_addr->sin_addr.s_addr = inet_addr(l_ip);
449
450		ip4_addr = (struct sockaddr_in *) &stream_prm.remote_addr;
451		ip4_addr->sin_family = AF_INET;
452		ip4_addr->sin_port = htons(r_port);
453		ip4_addr->sin_addr.s_addr = inet_addr(r_ip);
454	} else if (family == AF_INET6) {
455		ip6_addr = (struct sockaddr_in6 *) &stream_prm.local_addr;
456		ip6_addr->sin6_family = AF_INET6;
457		inet_pton(AF_INET6, l_ip, &ip6_addr->sin6_addr);
458		ip6_addr->sin6_port = htons(l_port);
459
460		ip6_addr = (struct sockaddr_in6 *) &stream_prm.remote_addr;
461		ip6_addr->sin6_family = AF_INET6;
462		inet_pton(AF_INET6, r_ip, &ip6_addr->sin6_addr);
463		ip6_addr->sin6_port = htons(r_port);
464	} else {
465		printf("Invalid address family, stream not created\n");
466		return NULL;
467	}
468
469	/* Not supporting callbacks and events at the moment */
470	/* TODO: Add tests which use cb's and events. */
471	stream_prm.recv_ev = tle_event_alloc(evq, nullptr);
472	stream_prm.send_ev = tle_event_alloc(evq, nullptr);
473
474	stream = tle_udp_stream_open(ctx,
475			(const struct tle_udp_stream_param *) &stream_prm);
476
477	return stream;
478}
479
480int
481test_tle_udp_gen_base::close_streams(vector<struct stream_s> streams)
482{
483	int rc;
484
485	for (auto &s : streams) {
486		rc = tle_udp_stream_close(s.ptr);
487		if (rc != 0)
488			return -1;
489	}
490
491	return 0;
492}
493
494int
495test_tle_udp_gen_base::del_devs(vector<struct dev_s> devs)
496{
497	int rc;
498
499	for (auto &d : devs) {
500		rc = tle_del_dev(d.ptr);
501		if (rc != 0)
502			return -1;
503	}
504
505	return 0;
506}
507
508class tle_rx_test : public test_tle_udp_gen_base {
509public:
510	virtual void SetUp(void)
511	{
512		/* Generate RX pcap file, for RX tests, then
513		 * follow setup steps as in base class */
514		tp = GetParam();
515
516		for(auto &s : tp.gen_streams) {
517			prepare_pcaps(s.src_ip.c_str(), s.dst_ip.c_str(),
518				s.src_port, s.dst_port, s.nb_pkts,
519				s.bad_chksum_l3, s.bad_chksum_l4, RX_PCAP);
520		}
521		test_tle_udp_gen_base::SetUp();
522	}
523};
524
525class tle_rx_enobufs: public tle_rx_test { };
526
527class tle_tx_test: public test_tle_udp_gen_base {
528public:
529	virtual void SetUp(void)
530	{
531		/* Generate 1-packet PCAP RX file so that virtual device can be
532		 * initialized (needs a pcap file present during init), then
533		 * follow setup steps as in base class
534		 */
535		prepare_pcaps("10.0.0.1", "10.0.0.1", 100, 100, 1, 0, 0,
536			RX_PCAP);
537		test_tle_udp_gen_base::SetUp();
538	}
539};
540
541#endif /* TEST_TLE_UDP_STREAM_GEN_H_ */
542