test_tle_udp_stream_gen.h revision 5c795f7b
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[] = {VDEV_NAME",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	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_eth_dev_attach(vdevargs[0], portid) != 0)
318		return -1;
319	cb = rte_eth_add_rx_callback(*portid, 0,
320		typen_rx_callback, nullptr);
321	if (port_init(*portid, mbuf_pool) != 0)
322		return -1;
323
324	return 0;
325}
326
327int
328test_tle_udp_gen_base::cleanup_devices(dpdk_port_t portid)
329{
330	/* release mbufs + detach device */
331	char name[RTE_ETH_NAME_MAX_LEN];
332
333	rte_eth_dev_stop(portid);
334	rte_eth_dev_close(portid);
335	rte_eth_dev_detach(portid, name);
336
337	return 0;
338}
339
340int
341test_tle_udp_gen_base::prepare_pcaps(string l_ip, string r_ip, int l_port,
342	int r_port, int nb_pkts, int l3_chksum, int l4_chksum,
343	string rx_pcap_dest)
344{
345	string py_cmd;
346
347	/* generate pcap rx & tx files * for tests using scapy */
348	py_cmd = "python " + string(binpath) + "/test_scapy_gen.py ";
349	py_cmd = py_cmd + " " + l_ip + " " + r_ip + " " +
350			to_string(l_port) + " " + to_string(r_port) + " " +
351			to_string(nb_pkts);
352
353	if (l3_chksum > 0)
354		py_cmd = py_cmd + " -bc3 " + to_string(l3_chksum);
355	if (l4_chksum > 0)
356		py_cmd = py_cmd + " -bc4 " + to_string(l4_chksum);
357	py_cmd = py_cmd + " " + rx_pcap_dest;
358	system(py_cmd.c_str());
359	return 0;
360}
361
362int
363test_tle_udp_gen_base::cleanup_pcaps(const char *file)
364{
365	if (remove(file) != 0)
366		perror("Error deleting pcap file");
367
368	return 0;
369}
370
371tle_ctx *
372test_tle_udp_gen_base::setup_ctx(void)
373{
374
375	struct tle_ctx *ctx;
376	struct tle_ctx_param ctx_prm;
377
378	memset(&ctx_prm, 0, sizeof(ctx_prm));
379	ctx_prm.socket_id = SOCKET_ID_ANY;
380	ctx_prm.max_streams = 0x10;
381	ctx_prm.max_stream_rbufs = CTX_MAX_RBUFS;
382	ctx_prm.max_stream_sbufs = CTX_MAX_SBUFS;
383	ctx_prm.lookup4 = lookup4_function;
384	ctx_prm.lookup6 = lookup6_function;
385	ctx_prm.lookup4_data = &routes4;
386	ctx_prm.lookup6_data = &routes6;
387
388	ctx = tle_ctx_create(&ctx_prm);
389
390	return ctx;
391}
392
393struct tle_dev *
394test_tle_udp_gen_base::setup_dev(struct tle_ctx *ctx, uint64_t rx_offload,
395	uint64_t tx_offload, const char *l_ipv4, const char *l_ipv6)
396{
397	struct tle_dev *dev;
398	struct tle_dev_param dev_prm;
399
400	memset(&dev_prm, 0, sizeof(dev_prm));
401	dev_prm.rx_offload = RX_NO_OFFLOAD;
402	dev_prm.tx_offload = TX_NO_OFFLOAD;
403	if (l_ipv4 != NULL)
404		inet_pton(AF_INET, l_ipv4, &(dev_prm).local_addr4);
405	if (l_ipv6 != NULL)
406		inet_pton(AF_INET6, l_ipv6, &(dev_prm).local_addr6);
407
408	dev = tle_add_dev(ctx, &dev_prm);
409
410	return dev;
411}
412
413struct tle_evq *
414test_tle_udp_gen_base::setup_evq()
415{
416	uint32_t socket_id;
417	uint32_t max_events;
418	struct tle_evq_param evq_params;
419	struct tle_evq *evq;
420
421	socket_id = SOCKET_ID_ANY;
422	max_events = 10;
423	memset(&evq_params, 0, sizeof(struct tle_evq_param));
424
425	evq_params.socket_id = socket_id;
426	evq_params.max_events = max_events;
427	evq = tle_evq_create(&evq_params);
428	return evq;
429}
430
431struct tle_stream *
432test_tle_udp_gen_base::setup_stream(struct tle_ctx *ctx, int family,
433	const char *l_ip, const char *r_ip, int l_port, int r_port)
434{
435	struct tle_stream *stream;
436	struct tle_udp_stream_param stream_prm;
437	struct sockaddr_in *ip4_addr;
438	struct sockaddr_in6 *ip6_addr;
439	int32_t ret;
440
441	memset(&stream_prm, 0, sizeof(stream_prm));
442
443	if (family == AF_INET) {
444		ip4_addr = (struct sockaddr_in *) &stream_prm.local_addr;
445		ip4_addr->sin_family = AF_INET;
446		ip4_addr->sin_port = htons(l_port);
447		ip4_addr->sin_addr.s_addr = inet_addr(l_ip);
448
449		ip4_addr = (struct sockaddr_in *) &stream_prm.remote_addr;
450		ip4_addr->sin_family = AF_INET;
451		ip4_addr->sin_port = htons(r_port);
452		ip4_addr->sin_addr.s_addr = inet_addr(r_ip);
453	} else if (family == AF_INET6) {
454		ip6_addr = (struct sockaddr_in6 *) &stream_prm.local_addr;
455		ip6_addr->sin6_family = AF_INET6;
456		inet_pton(AF_INET6, l_ip, &ip6_addr->sin6_addr);
457		ip6_addr->sin6_port = htons(l_port);
458
459		ip6_addr = (struct sockaddr_in6 *) &stream_prm.remote_addr;
460		ip6_addr->sin6_family = AF_INET6;
461		inet_pton(AF_INET6, r_ip, &ip6_addr->sin6_addr);
462		ip6_addr->sin6_port = htons(r_port);
463	} else {
464		printf("Invalid address family, stream not created\n");
465		return NULL;
466	}
467
468	/* Not supporting callbacks and events at the moment */
469	/* TODO: Add tests which use cb's and events. */
470	stream_prm.recv_ev = tle_event_alloc(evq, nullptr);
471	stream_prm.send_ev = tle_event_alloc(evq, nullptr);
472
473	stream = tle_udp_stream_open(ctx,
474			(const struct tle_udp_stream_param *) &stream_prm);
475
476	return stream;
477}
478
479int
480test_tle_udp_gen_base::close_streams(vector<struct stream_s> streams)
481{
482	int rc;
483
484	for (auto &s : streams) {
485		rc = tle_udp_stream_close(s.ptr);
486		if (rc != 0)
487			return -1;
488	}
489
490	return 0;
491}
492
493int
494test_tle_udp_gen_base::del_devs(vector<struct dev_s> devs)
495{
496	int rc;
497
498	for (auto &d : devs) {
499		rc = tle_del_dev(d.ptr);
500		if (rc != 0)
501			return -1;
502	}
503
504	return 0;
505}
506
507class tle_rx_test : public test_tle_udp_gen_base {
508public:
509	virtual void SetUp(void)
510	{
511		/* Generate RX pcap file, for RX tests, then
512		 * follow setup steps as in base class */
513		tp = GetParam();
514
515		for(auto &s : tp.gen_streams) {
516			prepare_pcaps(s.src_ip.c_str(), s.dst_ip.c_str(),
517				s.src_port, s.dst_port, s.nb_pkts,
518				s.bad_chksum_l3, s.bad_chksum_l4, RX_PCAP);
519		}
520		test_tle_udp_gen_base::SetUp();
521	}
522};
523
524class tle_rx_enobufs: public tle_rx_test { };
525
526class tle_tx_test: public test_tle_udp_gen_base {
527public:
528	virtual void SetUp(void)
529	{
530		/* Generate 1-packet PCAP RX file so that virtual device can be
531		 * initialized (needs a pcap file present during init), then
532		 * follow setup steps as in base class
533		 */
534		prepare_pcaps("10.0.0.1", "10.0.0.1", 100, 100, 1, 0, 0,
535			RX_PCAP);
536		test_tle_udp_gen_base::SetUp();
537	}
538};
539
540#endif /* TEST_TLE_UDP_STREAM_GEN_H_ */
541