1 /*
2  * testcode/unittcpreuse.c - unit test for tcp_reuse.
3  *
4  * Copyright (c) 2021, NLnet Labs. All rights reserved.
5  *
6  * This software is open source.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  *
12  * Redistributions of source code must retain the above copyright notice,
13  * this list of conditions and the following disclaimer.
14  *
15  * Redistributions in binary form must reproduce the above copyright notice,
16  * this list of conditions and the following disclaimer in the documentation
17  * and/or other materials provided with the distribution.
18  *
19  * Neither the name of the NLNET LABS nor the names of its contributors may
20  * be used to endorse or promote products derived from this software without
21  * specific prior written permission.
22  *
23  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
24  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
25  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
26  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
27  * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
28  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
29  * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
30  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
31  * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
32  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
33  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
34  *
35  */
36 /**
37  * \file
38  * Tests the tcp_reuse functionality.
39  */
40 
41 #include "config.h"
42 #include "testcode/unitmain.h"
43 #include "util/log.h"
44 #include "util/random.h"
45 #include "services/outside_network.h"
46 
47 /** add number of new IDs to the reuse tree, randomly chosen */
48 static void tcpid_addmore(struct reuse_tcp* reuse,
49 	struct outside_network* outnet, unsigned int addnum)
50 {
51 	unsigned int i;
52 	struct waiting_tcp* w;
53 	for(i=0; i<addnum; i++) {
54 		uint16_t id = reuse_tcp_select_id(reuse, outnet);
55 		unit_assert(!reuse_tcp_by_id_find(reuse, id));
56 		w = calloc(1, sizeof(*w));
57 		unit_assert(w);
58 		w->id = id;
59 		w->outnet = outnet;
60 		w->next_waiting = (void*)reuse->pending;
61 		reuse_tree_by_id_insert(reuse, w);
62 	}
63 }
64 
65 /** fill up the reuse ID tree and test assertions */
66 static void tcpid_fillup(struct reuse_tcp* reuse,
67 	struct outside_network* outnet)
68 {
69 	int t, numtest=3;
70 	for(t=0; t<numtest; t++) {
71 		rbtree_init(&reuse->tree_by_id, reuse_id_cmp);
72 		tcpid_addmore(reuse, outnet, 65535);
73 		reuse_del_readwait(&reuse->tree_by_id);
74 	}
75 }
76 
77 /** test TCP ID selection */
78 static void tcpid_test(void)
79 {
80 	struct pending_tcp pend;
81 	struct outside_network outnet;
82 	unit_show_func("services/outside_network.c", "reuse_tcp_select_id");
83 	memset(&pend, 0, sizeof(pend));
84 	pend.reuse.pending = &pend;
85 	memset(&outnet, 0, sizeof(outnet));
86 	outnet.rnd = ub_initstate(NULL);
87 	rbtree_init(&pend.reuse.tree_by_id, reuse_id_cmp);
88 	tcpid_fillup(&pend.reuse, &outnet);
89 	ub_randfree(outnet.rnd);
90 }
91 
92 /** check that the tree has present number of nodes and the LRU is linked
93  * properly. */
94 static void check_tree_and_list(struct outside_network* outnet, int present)
95 {
96 	int i;
97 	struct reuse_tcp *reuse, *next_reuse;
98 	unit_assert(present == (int)outnet->tcp_reuse.count);
99 	if(present < 1) {
100 		unit_assert(outnet->tcp_reuse_first == NULL);
101 		unit_assert(outnet->tcp_reuse_last == NULL);
102 		return;
103 	}
104 	unit_assert(outnet->tcp_reuse_first->item_on_lru_list);
105 	unit_assert(!outnet->tcp_reuse_first->lru_prev);
106 	reuse = outnet->tcp_reuse_first;
107 	for(i=0; i<present-1; i++) {
108 		unit_assert(reuse->item_on_lru_list);
109 		unit_assert(reuse->lru_next);
110 		unit_assert(reuse->lru_next != reuse);
111 		next_reuse = reuse->lru_next;
112 		unit_assert(next_reuse->lru_prev == reuse);
113 		reuse = next_reuse;
114 	}
115 	unit_assert(!reuse->lru_next);
116 	unit_assert(outnet->tcp_reuse_last->item_on_lru_list);
117 	unit_assert(outnet->tcp_reuse_last == reuse);
118 }
119 
120 /** creates pending_tcp. Copy of outside_network.c:create_pending_tcp without
121  *  the comm_point creation */
122 static int create_pending_tcp(struct outside_network* outnet)
123 {
124 	size_t i;
125 	if(outnet->num_tcp == 0)
126 		return 1; /* no tcp needed, nothing to do */
127 	if(!(outnet->tcp_conns = (struct pending_tcp **)calloc(
128 			outnet->num_tcp, sizeof(struct pending_tcp*))))
129 		return 0;
130 	for(i=0; i<outnet->num_tcp; i++) {
131 		if(!(outnet->tcp_conns[i] = (struct pending_tcp*)calloc(1,
132 			sizeof(struct pending_tcp))))
133 			return 0;
134 		outnet->tcp_conns[i]->next_free = outnet->tcp_free;
135 		outnet->tcp_free = outnet->tcp_conns[i];
136 	}
137 	return 1;
138 }
139 
140 /** empty the tcp_reuse tree and LRU list */
141 static void empty_tree(struct outside_network* outnet)
142 {
143 	size_t i;
144 	struct reuse_tcp* reuse;
145 	reuse = outnet->tcp_reuse_first;
146 	i = outnet->tcp_reuse.count;
147 	while(reuse) {
148 		reuse_tcp_remove_tree_list(outnet, reuse);
149 		check_tree_and_list(outnet, --i);
150 		reuse = outnet->tcp_reuse_first;
151 	}
152 }
153 
154 /** check removal of the LRU element on the given position of total elements */
155 static void check_removal(struct outside_network* outnet, int position, int total)
156 {
157 	int i;
158 	struct reuse_tcp* reuse;
159 	empty_tree(outnet);
160 	for(i=0; i<total; i++) {
161 		reuse_tcp_insert(outnet, outnet->tcp_conns[i]);
162 	}
163 	check_tree_and_list(outnet, total);
164 	reuse = outnet->tcp_reuse_first;
165 	for(i=0; i<position; i++) reuse = reuse->lru_next;
166 	reuse_tcp_remove_tree_list(outnet, reuse);
167 	check_tree_and_list(outnet, total-1);
168 }
169 
170 /** check snipping off the last element of the LRU with total elements */
171 static void check_snip(struct outside_network* outnet, int total)
172 {
173 	int i;
174 	struct reuse_tcp* reuse;
175 	empty_tree(outnet);
176 	for(i=0; i<total; i++) {
177 		reuse_tcp_insert(outnet, outnet->tcp_conns[i]);
178 	}
179 	check_tree_and_list(outnet, total);
180 	reuse = reuse_tcp_lru_snip(outnet);
181 	while(reuse) {
182 		reuse_tcp_remove_tree_list(outnet, reuse);
183 		check_tree_and_list(outnet, --total);
184 		reuse = reuse_tcp_lru_snip(outnet);
185 	}
186 	unit_assert(outnet->tcp_reuse_first == NULL);
187 	unit_assert(outnet->tcp_reuse_last == NULL);
188 	unit_assert(outnet->tcp_reuse.count == 0);
189 }
190 
191 /** test tcp_reuse tree and LRU list functions */
192 static void tcp_reuse_tree_list_test(void)
193 {
194 	size_t i;
195 	struct outside_network outnet;
196 	struct reuse_tcp* reuse;
197 	memset(&outnet, 0, sizeof(outnet));
198 	rbtree_init(&outnet.tcp_reuse, reuse_cmp);
199 	outnet.num_tcp = 5;
200 	outnet.tcp_reuse_max = outnet.num_tcp;
201 	if(!create_pending_tcp(&outnet)) fatal_exit("out of memory");
202 	/* add all to the tree */
203 	unit_show_func("services/outside_network.c", "reuse_tcp_insert");
204 	for(i=0; i<outnet.num_tcp; i++) {
205 		reuse_tcp_insert(&outnet, outnet.tcp_conns[i]);
206 		check_tree_and_list(&outnet, i+1);
207 	}
208 	/* check touching */
209 	unit_show_func("services/outside_network.c", "reuse_tcp_lru_touch");
210 	for(i=0; i<outnet.tcp_reuse.count; i++) {
211 		for(reuse = outnet.tcp_reuse_first; reuse->lru_next; reuse = reuse->lru_next);
212 		reuse_tcp_lru_touch(&outnet, reuse);
213 		check_tree_and_list(&outnet, outnet.num_tcp);
214 	}
215 	/* check removal */
216 	unit_show_func("services/outside_network.c", "reuse_tcp_remove_tree_list");
217 	check_removal(&outnet, 2, 5);
218 	check_removal(&outnet, 1, 3);
219 	check_removal(&outnet, 1, 2);
220 	/* check snip */
221 	unit_show_func("services/outside_network.c", "reuse_tcp_lru_snip");
222 	check_snip(&outnet, 4);
223 
224 	for(i=0; i<outnet.num_tcp; i++)
225 		if(outnet.tcp_conns[i]) {
226 			free(outnet.tcp_conns[i]);
227 		}
228 	free(outnet.tcp_conns);
229 }
230 
231 void tcpreuse_test(void)
232 {
233     unit_show_feature("tcp_reuse");
234     tcpid_test();
235     tcp_reuse_tree_list_test();
236 }
237