1/*
2 * SPDX-License-Identifier: BSD-3-Clause
3 *
4 * Copyright 2021 Lutz Donnerhacke
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 *
10 * 1. Redistributions of source code must retain the above copyright
11 *    notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above
13 *    copyright notice, this list of conditions and the following
14 *    disclaimer in the documentation and/or other materials provided
15 *    with the distribution.
16 * 3. Neither the name of the copyright holder nor the names of its
17 *    contributors may be used to endorse or promote products derived
18 *    from this software without specific prior written permission.
19 *
20 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
21 * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
22 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
23 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
24 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS
25 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
26 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
27 * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
28 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
29 * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
30 * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
31 * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32 * SUCH DAMAGE.
33 */
34#include <stdio.h>
35#include <stdlib.h>
36#include <strings.h>
37#include <sys/time.h>
38#include "util.h"
39#include <alias.h>
40
41static void usage(void);
42
43#define	timevalcmp(tv, uv, cmp)			\
44	(((tv).tv_sec == (uv).tv_sec)		\
45	 ? ((tv).tv_usec cmp (uv).tv_usec)	\
46	 : ((tv).tv_sec cmp (uv).tv_sec))
47
48#define timevaldiff(n, o) (float)		\
49	(((n).tv_sec - (o).tv_sec)*1000000l +	\
50	 ((n).tv_usec - (o).tv_usec))
51
52#define check_timeout()	do {				\
53	if (check_timeout_cnt++ > 1000) {		\
54		check_timeout_cnt = 0;			\
55		gettimeofday(&now, NULL);		\
56		if (timevalcmp(now, timeout, >=))	\
57		    goto out;				\
58	} } while(0)
59
60static void
61usage(void) {
62	printf("Usage: perf [max_seconds [batch_size [random_size [attack_size [redir_size]]]]]\n");
63	exit(1);
64}
65
66int main(int argc, char ** argv)
67{
68	struct libalias *la;
69	struct timeval timeout, now, start;
70	struct ip *p;
71	struct udphdr *u;
72	struct {
73		struct in_addr src, dst;
74		uint16_t sport, dport, aport;
75	} *batch;
76	struct {
77		unsigned long ok, fail;
78	} nat, usenat, unnat, random, attack;
79	int i, round, check_timeout_cnt = 0;
80	int max_seconds = 90, batch_size = 2000,
81	    random_size = 1000, attack_size = 1000,
82	    redir_size = 2000;
83
84	if (argc >= 2) {
85		char * end;
86
87		max_seconds = strtol(argv[1], &end, 10);
88		if (max_seconds < 2 || end[0] != '\0')
89			usage();
90	}
91	if (argc > 2 && (batch_size  = atoi(argv[2])) < 0)	usage();
92	if (argc > 3 && (random_size = atoi(argv[3])) < 0)	usage();
93	if (argc > 4 && (attack_size = atoi(argv[4])) < 0)	usage();
94	if (argc > 5 && (redir_size  = atoi(argv[5])) < 0)	usage();
95
96	printf("Running perfomance test with parameters:\n");
97	printf("  Maximum Runtime (max_seconds) = %d\n", max_seconds);
98	printf("  Amount of valid connections (batch_size) = %d\n", batch_size);
99	printf("  Amount of random, incoming packets (batch_size) = %d\n", random_size);
100	printf("  Repeat count of a random, incoming packet (attack_size) = %d\n", attack_size);
101	printf("  Amount of open port forwardings (redir_size) = %d\n", redir_size);
102	printf("\n");
103
104	if (NULL == (la = LibAliasInit(NULL))) {
105		perror("LibAliasInit");
106		return -1;
107	}
108
109	bzero(&nat, sizeof(nat));
110	bzero(&usenat, sizeof(usenat));
111	bzero(&unnat, sizeof(unnat));
112	bzero(&random, sizeof(random));
113	bzero(&attack, sizeof(attack));
114
115	LibAliasSetAddress(la, masq);
116	LibAliasSetMode(la, PKT_ALIAS_SAME_PORTS | PKT_ALIAS_DENY_INCOMING, ~0);
117
118	prv1.s_addr &= htonl(0xffff0000);
119	ext.s_addr &= htonl(0xffff0000);
120
121	for (i = 0; i < redir_size; i++) {
122		int aport = htons(rand_range(1000, 2000));
123		int sport = htons(rand_range(1000, 2000));
124
125		prv2.s_addr &= htonl(0xffff0000);
126		prv2.s_addr |= rand_range(0, 0xffff);
127		LibAliasRedirectPort(la, prv2, sport, ANY_ADDR, 0, masq, aport, IPPROTO_UDP);
128	}
129
130	p = ip_packet(0, 64);
131	u = set_udp(p, 0, 0);
132
133	if (NULL == (batch = calloc(batch_size, sizeof(*batch)))) {
134		perror("calloc(batch)");
135		return -1;
136	}
137
138	gettimeofday(&timeout, NULL);
139	timeout.tv_sec += max_seconds;
140
141	printf("RND SECOND newNAT RANDOM ATTACK useNAT\n");
142	for (round = 0; ; round++) {
143		int res, cnt;
144
145		printf("%3d ", round+1);
146
147		gettimeofday(&start, NULL);
148		printf("%6.1f ", max_seconds - timevaldiff(timeout, start)/1000000.0f);
149		for (cnt = i = 0; i < batch_size; i++, cnt++) {
150			batch[i].src.s_addr = prv1.s_addr | htonl(rand_range(0, 0xffff));
151			batch[i].dst.s_addr = ext.s_addr | htonl(rand_range(0, 0xffff));
152			batch[i].sport = rand_range(1000, 60000);
153			batch[i].dport = rand_range(1000, 60000);
154
155			p->ip_src = batch[i].src;
156			p->ip_dst = batch[i].dst;
157			u = set_udp(p, batch[i].sport, batch[i].dport);
158
159			res = LibAliasOut(la, p, 64);
160			batch[i].aport = htons(u->uh_sport);
161
162			if (res == PKT_ALIAS_OK &&
163			    u->uh_dport == htons(batch[i].dport) &&
164			    addr_eq(p->ip_dst, batch[i].dst) &&
165			    addr_eq(p->ip_src, masq))
166				nat.ok++;
167			else
168				nat.fail++;
169
170			check_timeout();
171		}
172		gettimeofday(&now, NULL);
173		if (cnt > 0)
174			printf("%6.2f ", timevaldiff(now, start) / cnt);
175		else
176			printf("------ ");
177
178		start = now;
179		for (cnt = i = 0; i < random_size; i++, cnt++) {
180			p->ip_src.s_addr = ext.s_addr & htonl(0xfff00000);
181			p->ip_src.s_addr |= htonl(rand_range(0, 0xffff));
182			p->ip_dst = masq;
183			u = set_udp(p, rand_range(1, 0xffff), rand_range(1, 0xffff));
184
185			res = LibAliasIn(la, p, 64);
186
187			if (res == PKT_ALIAS_OK)
188				random.ok++;
189			else
190				random.fail++;
191
192			check_timeout();
193		}
194		gettimeofday(&now, NULL);
195		if (cnt > 0)
196			printf("%6.2f ", timevaldiff(now, start) / cnt);
197		else
198			printf("------ ");
199
200		start = now;
201		p->ip_src.s_addr = ext.s_addr & htonl(0xfff00000);
202		p->ip_src.s_addr |= htonl(rand_range(0, 0xffff));
203		p->ip_dst = masq;
204		u = set_udp(p, rand_range(1, 0xffff), rand_range(1, 0xffff));
205		for (cnt = i = 0; i < attack_size; i++, cnt++) {
206			res = LibAliasIn(la, p, 64);
207
208			if (res == PKT_ALIAS_OK)
209				attack.ok++;
210			else
211				attack.fail++;
212
213			check_timeout();
214		}
215		gettimeofday(&now, NULL);
216		if (cnt > 0)
217			printf("%6.2f ", timevaldiff(now, start) / cnt);
218		else
219			printf("------ ");
220
221		qsort(batch, batch_size, sizeof(*batch), randcmp);
222
223		gettimeofday(&start, NULL);
224		for (cnt = i = 0; i < batch_size; i++) {
225			int j;
226
227			/* random communication length */
228			for(j = rand_range(1, 150); j-- > 0; cnt++) {
229				int k;
230
231				/* a random flow out of rolling window */
232				k = rand_range(i, i + 25);
233				if (k >= batch_size)
234					k = i;
235
236				/* 10% outgoing, 90% incoming */
237				if (rand_range(0, 100) > 10) {
238					p->ip_src = batch[k].dst;
239					p->ip_dst = masq;
240					u = set_udp(p, batch[k].dport, batch[k].aport);
241
242					res = LibAliasIn(la, p, 64);
243					if (res == PKT_ALIAS_OK &&
244					    u->uh_sport == htons(batch[k].dport) &&
245					    u->uh_dport == htons(batch[k].sport) &&
246					    addr_eq(p->ip_dst, batch[k].src) &&
247					    addr_eq(p->ip_src, batch[k].dst))
248						unnat.ok++;
249					else
250						unnat.fail++;
251				} else {
252					p->ip_src = batch[k].src;
253					p->ip_dst = batch[k].dst;
254					u = set_udp(p, batch[k].sport, batch[k].dport);
255
256					res = LibAliasOut(la, p, 64);
257					if (res == PKT_ALIAS_OK &&
258					    u->uh_sport == htons(batch[k].aport) &&
259					    u->uh_dport == htons(batch[k].dport) &&
260					    addr_eq(p->ip_dst, batch[k].dst) &&
261					    addr_eq(p->ip_src, masq))
262						usenat.ok++;
263					else
264						usenat.fail++;
265				}
266				check_timeout();
267			}
268		}
269		gettimeofday(&now, NULL);
270		if (cnt > 0)
271			printf("%6.2f ", timevaldiff(now, start) / cnt);
272		else
273			printf("------ ");
274
275		printf("\n");
276	}
277out:
278	printf("\n\n");
279	free(batch);
280	free(p);
281
282	printf("Results\n");
283	printf("   Rounds  : %9u\n", round);
284	printf("newNAT ok  : %9lu\n", nat.ok);
285	printf("newNAT fail: %9lu\n", nat.fail);
286	printf("useNAT ok  : %9lu (out)\n", usenat.ok);
287	printf("useNAT fail: %9lu (out)\n", usenat.fail);
288	printf("useNAT ok  : %9lu (in)\n", unnat.ok);
289	printf("useNAT fail: %9lu (in)\n", unnat.fail);
290	printf("RANDOM ok  : %9lu\n", random.ok);
291	printf("RANDOM fail: %9lu\n", random.fail);
292	printf("ATTACK ok  : %9lu\n", attack.ok);
293	printf("ATTACK fail: %9lu\n", attack.fail);
294	printf("             ---------\n");
295	printf("      Total: %9lu\n",
296	       nat.ok + nat.fail +
297	       unnat.ok + unnat.fail +
298	       usenat.ok + usenat.fail +
299	       random.ok + random.fail +
300	       attack.ok + attack.fail);
301
302	gettimeofday(&start, NULL);
303	printf("\n  Cleanup  : ");
304	LibAliasUninit(la);
305	gettimeofday(&now, NULL);
306	printf("%.2fs\n", timevaldiff(now, start)/1000000l);
307	return (0);
308}
309