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