xref: /freebsd/tests/sys/netinet/libalias/perf.c (revision 4543ef516683042d46f3bd3bb8a4f3f746e00499)
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 
41 static void usage(void) __dead2;
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 
60 static void
61 usage(void) {
62 	printf("Usage: perf [max_seconds [batch_size [random_size [attack_size [redir_size]]]]]\n");
63 	exit(1);
64 }
65 
66 int 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 	}
277 out:
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