]> git.ipfire.org Git - thirdparty/unbound.git/commitdiff
- Add unittest for tcp_reuse functions.
authorGeorge Thessalonikefs <george@nlnetlabs.nl>
Fri, 23 Jul 2021 23:15:00 +0000 (01:15 +0200)
committerGeorge Thessalonikefs <george@nlnetlabs.nl>
Fri, 23 Jul 2021 23:15:00 +0000 (01:15 +0200)
Makefile.in
services/outside_network.c
services/outside_network.h
testcode/unitmain.c
testcode/unitmain.h
testcode/unittcpreuse.c [new file with mode: 0644]

index 9fbb8f8e043b7829f5c79a0c5300778ff5619e8a..476545ea90411d92c96316ce2e25dbe73ddb5b89 100644 (file)
@@ -175,10 +175,12 @@ UNITTEST_SRC=testcode/unitanchor.c testcode/unitdname.c \
 testcode/unitlruhash.c testcode/unitmain.c testcode/unitmsgparse.c \
 testcode/unitneg.c testcode/unitregional.c testcode/unitslabhash.c \
 testcode/unitverify.c testcode/readhex.c testcode/testpkts.c testcode/unitldns.c \
-testcode/unitecs.c testcode/unitauth.c testcode/unitzonemd.c
+testcode/unitecs.c testcode/unitauth.c testcode/unitzonemd.c \
+testcode/unittcpreuse.c
 UNITTEST_OBJ=unitanchor.lo unitdname.lo unitlruhash.lo unitmain.lo \
 unitmsgparse.lo unitneg.lo unitregional.lo unitslabhash.lo unitverify.lo \
-readhex.lo testpkts.lo unitldns.lo unitecs.lo unitauth.lo unitzonemd.lo
+readhex.lo testpkts.lo unitldns.lo unitecs.lo unitauth.lo unitzonemd.lo \
+unittcpreuse.lo
 UNITTEST_OBJ_LINK=$(UNITTEST_OBJ) worker_cb.lo $(COMMON_OBJ) $(SLDNS_OBJ) \
 $(COMPAT_OBJ)
 DAEMON_SRC=daemon/acl_list.c daemon/cachedump.c daemon/daemon.c \
@@ -1223,6 +1225,8 @@ unitzonemd.lo unitzonemd.o: $(srcdir)/testcode/unitzonemd.c config.h $(srcdir)/u
  $(srcdir)/sldns/sbuffer.h $(srcdir)/util/config_file.h $(srcdir)/daemon/stats.h $(srcdir)/util/timehist.h \
  $(srcdir)/libunbound/unbound.h $(srcdir)/respip/respip.h $(srcdir)/util/data/dname.h $(srcdir)/util/regional.h \
  $(srcdir)/validator/val_anchor.h
+unittcpreuse.lo unittcpreuse.o: $(srcdir)/testcode/unittcpreuse.c config.h $(srcdir)/services/outside_network.h \
+$(srcdir)/util/random.h
 acl_list.lo acl_list.o: $(srcdir)/daemon/acl_list.c config.h $(srcdir)/daemon/acl_list.h \
  $(srcdir)/util/storage/dnstree.h $(srcdir)/util/rbtree.h $(srcdir)/services/view.h $(srcdir)/util/locks.h \
  $(srcdir)/util/log.h $(srcdir)/util/regional.h $(srcdir)/util/config_file.h $(srcdir)/util/net_help.h \
index 340489ba9f1b5874593c2aba1ac175cff83ae0e8..5207a704278ed5c122b7ef36bf4eda3e72198d7e 100644 (file)
@@ -90,10 +90,6 @@ static int randomize_and_send_udp(struct pending* pend, sldns_buffer* packet,
 static void waiting_list_remove(struct outside_network* outnet,
        struct waiting_tcp* w);
 
-/** remove reused element from tree and lru list */
-static void reuse_tcp_remove_tree_list(struct outside_network* outnet,
-       struct reuse_tcp* reuse);
-
 /** select a DNS ID for a TCP stream */
 static uint16_t tcp_select_id(struct outside_network* outnet,
        struct reuse_tcp* reuse);
@@ -459,7 +455,7 @@ tree_by_id_get_id(rbnode_type* node)
 }
 
 /** insert into reuse tcp tree and LRU, false on failure (duplicate) */
-static int
+int
 reuse_tcp_insert(struct outside_network* outnet, struct pending_tcp* pend_tcp)
 {
        log_reuse_tcp(VERB_CLIENT, "reuse_tcp_insert", &pend_tcp->reuse);
@@ -733,7 +729,7 @@ outnet_tcp_take_into_use(struct waiting_tcp* w)
 /** Touch the lru of a reuse_tcp element, it is in use.
  * This moves it to the front of the list, where it is not likely to
  * be closed.  Items at the back of the list are closed to make space. */
-static void
+void
 reuse_tcp_lru_touch(struct outside_network* outnet, struct reuse_tcp* reuse)
 {
        if(!reuse->item_on_lru_list) {
@@ -771,6 +767,29 @@ reuse_tcp_lru_touch(struct outside_network* outnet, struct reuse_tcp* reuse)
                (outnet->tcp_reuse_first && outnet->tcp_reuse_last));
 }
 
+/** Snip the last reuse_tcp element off of the LRU list */
+struct reuse_tcp*
+reuse_tcp_lru_snip(struct outside_network* outnet)
+{
+       struct reuse_tcp* reuse = outnet->tcp_reuse_last;
+       if(!reuse) return NULL;
+       /* snip off of LRU */
+       log_assert(reuse->lru_next == NULL);
+       if(reuse->lru_prev) {
+               outnet->tcp_reuse_last = reuse->lru_prev;
+               reuse->lru_prev->lru_next = NULL;
+       } else {
+               outnet->tcp_reuse_last = NULL;
+               outnet->tcp_reuse_first = NULL;
+       }
+       log_assert((!outnet->tcp_reuse_first && !outnet->tcp_reuse_last) ||
+               (outnet->tcp_reuse_first && outnet->tcp_reuse_last));
+       reuse->item_on_lru_list = 0;
+       reuse->lru_next = NULL;
+       reuse->lru_prev = NULL;
+       return reuse;
+}
+
 /** call callback on waiting_tcp, if not NULL */
 static void
 waiting_tcp_callback(struct waiting_tcp* w, struct comm_point* c, int error,
@@ -955,7 +974,7 @@ reuse_move_writewait_away(struct outside_network* outnet,
 }
 
 /** remove reused element from tree and lru list */
-static void
+void
 reuse_tcp_remove_tree_list(struct outside_network* outnet,
        struct reuse_tcp* reuse)
 {
@@ -2153,28 +2172,12 @@ outnet_tcptimer(void* arg)
 static void
 reuse_tcp_close_oldest(struct outside_network* outnet)
 {
-       struct pending_tcp* pend;
+       struct reuse_tcp* reuse;
        verbose(VERB_CLIENT, "reuse_tcp_close_oldest");
-       if(!outnet->tcp_reuse_last) return;
-       pend = outnet->tcp_reuse_last->pending;
-
-       /* snip off of LRU */
-       log_assert(pend->reuse.lru_next == NULL);
-       if(pend->reuse.lru_prev) {
-               outnet->tcp_reuse_last = pend->reuse.lru_prev;
-               pend->reuse.lru_prev->lru_next = NULL;
-       } else {
-               outnet->tcp_reuse_last = NULL;
-               outnet->tcp_reuse_first = NULL;
-       }
-       log_assert((!outnet->tcp_reuse_first && !outnet->tcp_reuse_last) ||
-               (outnet->tcp_reuse_first && outnet->tcp_reuse_last));
-       pend->reuse.item_on_lru_list = 0;
-       pend->reuse.lru_next = NULL;
-       pend->reuse.lru_prev = NULL;
-
+       reuse = reuse_tcp_lru_snip(outnet);
+       if(!reuse) return;
        /* free up */
-       reuse_cb_and_decommission(outnet, pend, NETEVENT_CLOSED);
+       reuse_cb_and_decommission(outnet, reuse->pending, NETEVENT_CLOSED);
 }
 
 static uint16_t
index 67403f7368c40dd366facd9e9739e8cb160c12d4..d0d532e6425f1f5946629528057cf73e8997dde6 100644 (file)
@@ -682,12 +682,28 @@ struct waiting_tcp* reuse_tcp_by_id_find(struct reuse_tcp* reuse, uint16_t id);
 /** insert element in tree by id */
 void reuse_tree_by_id_insert(struct reuse_tcp* reuse, struct waiting_tcp* w);
 
+/** insert element in tcp_reuse tree and LRU list */
+int reuse_tcp_insert(struct outside_network* outnet,
+       struct pending_tcp* pend_tcp);
+
+/** touch the LRU of the element */
+void reuse_tcp_lru_touch(struct outside_network* outnet,
+       struct reuse_tcp* reuse);
+
+/** remove element from tree and LRU list */
+void reuse_tcp_remove_tree_list(struct outside_network* outnet,
+       struct reuse_tcp* reuse);
+
+/** snip the last reuse_tcp element off of the LRU list if any */
+struct reuse_tcp* reuse_tcp_lru_snip(struct outside_network* outnet);
+
 /** delete readwait waiting_tcp elements, deletes the elements in the list */
 void reuse_del_readwait(rbtree_type* tree_by_id);
 
 /** get TCP file descriptor for address, returns -1 on failure,
  * tcp_mss is 0 or maxseg size to set for TCP packets. */
-int outnet_get_tcp_fd(struct sockaddr_storage* addr, socklen_t addrlen, int tcp_mss, int dscp);
+int outnet_get_tcp_fd(struct sockaddr_storage* addr, socklen_t addrlen,
+       int tcp_mss, int dscp);
 
 /**
  * Create udp commpoint suitable for sending packets to the destination.
index 30562af116e0a2f23750006a3585f904bbf030f9..c18be7be3a089ccc575b769ada954bac6637d537 100644 (file)
@@ -839,52 +839,6 @@ static void respip_test(void)
        respip_conf_actions_test();
 }
 
-#include "services/outside_network.h"
-/** add number of new IDs to the reuse tree, randomly chosen */
-static void tcpid_addmore(struct reuse_tcp* reuse,
-       struct outside_network* outnet, unsigned int addnum)
-{
-       unsigned int i;
-       struct waiting_tcp* w;
-       for(i=0; i<addnum; i++) {
-               uint16_t id = reuse_tcp_select_id(reuse, outnet);
-               unit_assert(!reuse_tcp_by_id_find(reuse, id));
-               w = calloc(1, sizeof(*w));
-               unit_assert(w);
-               w->id = id;
-               w->outnet = outnet;
-               w->next_waiting = (void*)reuse->pending;
-               reuse_tree_by_id_insert(reuse, w);
-       }
-}
-
-/** fill up the reuse ID tree and test assertions */
-static void tcpid_fillup(struct reuse_tcp* reuse,
-       struct outside_network* outnet)
-{
-       int t, numtest=3;
-       for(t=0; t<numtest; t++) {
-               rbtree_init(&reuse->tree_by_id, reuse_id_cmp);
-               tcpid_addmore(reuse, outnet, 65535);
-               reuse_del_readwait(&reuse->tree_by_id);
-       }
-}
-
-/** test TCP ID selection */
-static void tcpid_test(void)
-{
-       struct pending_tcp pend;
-       struct outside_network outnet;
-       unit_show_func("services/outside_network.c", "reuse_tcp_select_id");
-       memset(&pend, 0, sizeof(pend));
-       pend.reuse.pending = &pend;
-       memset(&outnet, 0, sizeof(outnet));
-       outnet.rnd = ub_initstate(NULL);
-       rbtree_init(&pend.reuse.tree_by_id, reuse_id_cmp);
-       tcpid_fillup(&pend.reuse, &outnet);
-       ub_randfree(outnet.rnd);
-}
-
 void unit_show_func(const char* file, const char* func)
 {
        printf("test %s:%s\n", file, func);
@@ -953,8 +907,8 @@ main(int argc, char* argv[])
        infra_test();
        ldns_test();
        zonemd_test();
+       tcpreuse_test();
        msgparse_test();
-       tcpid_test();
 #ifdef CLIENT_SUBNET
        ecs_test();
 #endif /* CLIENT_SUBNET */
index 66d1322f2a16df56559951a3452549be2301f5ae..adcd74f77b523a96b3a8be878bc3dbd67ff031a0 100644 (file)
@@ -82,5 +82,7 @@ void ldns_test(void);
 void authzone_test(void);
 /** unit test for zonemd functions */
 void zonemd_test(void);
+/** unit test for tcp_reuse functions */
+void tcpreuse_test(void);
 
 #endif /* TESTCODE_UNITMAIN_H */
diff --git a/testcode/unittcpreuse.c b/testcode/unittcpreuse.c
new file mode 100644 (file)
index 0000000..ddb865b
--- /dev/null
@@ -0,0 +1,236 @@
+/*
+ * testcode/unittcpreuse.c - unit test for tcp_reuse.
+ *
+ * Copyright (c) 2021, NLnet Labs. All rights reserved.
+ *
+ * This software is open source.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ *
+ * Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * Neither the name of the NLNET LABS nor the names of its contributors may
+ * be used to endorse or promote products derived from this software without
+ * specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
+ * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+/**
+ * \file
+ * Tests the tcp_reuse functionality.
+ */
+
+#include "config.h"
+#include "testcode/unitmain.h"
+#include "util/log.h"
+#include "util/random.h"
+#include "services/outside_network.h"
+
+/** add number of new IDs to the reuse tree, randomly chosen */
+static void tcpid_addmore(struct reuse_tcp* reuse,
+       struct outside_network* outnet, unsigned int addnum)
+{
+       unsigned int i;
+       struct waiting_tcp* w;
+       for(i=0; i<addnum; i++) {
+               uint16_t id = reuse_tcp_select_id(reuse, outnet);
+               unit_assert(!reuse_tcp_by_id_find(reuse, id));
+               w = calloc(1, sizeof(*w));
+               unit_assert(w);
+               w->id = id;
+               w->outnet = outnet;
+               w->next_waiting = (void*)reuse->pending;
+               reuse_tree_by_id_insert(reuse, w);
+       }
+}
+
+/** fill up the reuse ID tree and test assertions */
+static void tcpid_fillup(struct reuse_tcp* reuse,
+       struct outside_network* outnet)
+{
+       int t, numtest=3;
+       for(t=0; t<numtest; t++) {
+               rbtree_init(&reuse->tree_by_id, reuse_id_cmp);
+               tcpid_addmore(reuse, outnet, 65535);
+               reuse_del_readwait(&reuse->tree_by_id);
+       }
+}
+
+/** test TCP ID selection */
+static void tcpid_test(void)
+{
+       struct pending_tcp pend;
+       struct outside_network outnet;
+       unit_show_func("services/outside_network.c", "reuse_tcp_select_id");
+       memset(&pend, 0, sizeof(pend));
+       pend.reuse.pending = &pend;
+       memset(&outnet, 0, sizeof(outnet));
+       outnet.rnd = ub_initstate(NULL);
+       rbtree_init(&pend.reuse.tree_by_id, reuse_id_cmp);
+       tcpid_fillup(&pend.reuse, &outnet);
+       ub_randfree(outnet.rnd);
+}
+
+/** check that the tree has present number of nodes and the LRU is linked
+ * properly. */
+static void check_tree_and_list(struct outside_network* outnet, int present)
+{
+       int i;
+       struct reuse_tcp *reuse, *next_reuse;
+       unit_assert(present == (int)outnet->tcp_reuse.count);
+       if(present < 1) {
+               unit_assert(outnet->tcp_reuse_first == NULL);
+               unit_assert(outnet->tcp_reuse_last == NULL);
+               return;
+       }
+       unit_assert(outnet->tcp_reuse_first->item_on_lru_list);
+       unit_assert(!outnet->tcp_reuse_first->lru_prev);
+       reuse = outnet->tcp_reuse_first;
+       for(i=0; i<present-1; i++) {
+               unit_assert(reuse->item_on_lru_list);
+               unit_assert(reuse->lru_next);
+               unit_assert(reuse->lru_next != reuse);
+               next_reuse = reuse->lru_next;
+               unit_assert(next_reuse->lru_prev == reuse);
+               reuse = next_reuse;
+       }
+       unit_assert(!reuse->lru_next);
+       unit_assert(outnet->tcp_reuse_last->item_on_lru_list);
+       unit_assert(outnet->tcp_reuse_last == reuse);
+}
+
+/** creates pending_tcp. Copy of outside_network.c:create_pending_tcp without
+ *  the comm_point creation */
+static int create_pending_tcp(struct outside_network* outnet)
+{
+       size_t i;
+       if(outnet->num_tcp == 0)
+               return 1; /* no tcp needed, nothing to do */
+       if(!(outnet->tcp_conns = (struct pending_tcp **)calloc(
+                       outnet->num_tcp, sizeof(struct pending_tcp*))))
+               return 0;
+       for(i=0; i<outnet->num_tcp; i++) {
+               if(!(outnet->tcp_conns[i] = (struct pending_tcp*)calloc(1, 
+                       sizeof(struct pending_tcp))))
+                       return 0;
+               outnet->tcp_conns[i]->next_free = outnet->tcp_free;
+               outnet->tcp_free = outnet->tcp_conns[i];
+       }
+       return 1;
+}
+
+/** empty the tcp_reuse tree and LRU list */
+static void empty_tree(struct outside_network* outnet)
+{
+       size_t i;
+       struct reuse_tcp* reuse;
+       reuse = outnet->tcp_reuse_first;
+       i = outnet->tcp_reuse.count;
+       while(reuse) {
+               reuse_tcp_remove_tree_list(outnet, reuse);
+               check_tree_and_list(outnet, --i);
+               reuse = outnet->tcp_reuse_first;
+       }
+}
+
+/** check removal of the LRU element on the given position of total elements */
+static void check_removal(struct outside_network* outnet, int position, int total)
+{
+       int i;
+       struct reuse_tcp* reuse;
+       empty_tree(outnet);
+       for(i=0; i<total; i++) {
+               reuse_tcp_insert(outnet, outnet->tcp_conns[i]);
+       }
+       check_tree_and_list(outnet, total);
+       reuse = outnet->tcp_reuse_first;
+       for(i=0; i<position; i++) reuse = reuse->lru_next;
+       reuse_tcp_remove_tree_list(outnet, reuse);
+       check_tree_and_list(outnet, total-1);
+}
+
+/** check snipping off the last element of the LRU with total elements */
+static void check_snip(struct outside_network* outnet, int total)
+{
+       int i;
+       struct reuse_tcp* reuse;
+       empty_tree(outnet);
+       for(i=0; i<total; i++) {
+               reuse_tcp_insert(outnet, outnet->tcp_conns[i]);
+       }
+       check_tree_and_list(outnet, total);
+       reuse = reuse_tcp_lru_snip(outnet);
+       while(reuse) {
+               reuse_tcp_remove_tree_list(outnet, reuse);
+               check_tree_and_list(outnet, --total);
+               reuse = reuse_tcp_lru_snip(outnet);
+       }
+       unit_assert(outnet->tcp_reuse_first == NULL);
+       unit_assert(outnet->tcp_reuse_last == NULL);
+       unit_assert(outnet->tcp_reuse.count == 0);
+}
+
+/** test tcp_reuse tree and LRU list functions */
+static void tcp_reuse_tree_list_test(void)
+{
+       size_t i;
+       struct outside_network outnet;
+       struct reuse_tcp* reuse;
+       rbtree_init(&outnet.tcp_reuse, reuse_cmp);
+       outnet.num_tcp = 5;
+       outnet.tcp_reuse_max = outnet.num_tcp;
+       if(!create_pending_tcp(&outnet)) fatal_exit("out of memory");
+       /* add all to the tree */
+       unit_show_func("services/outside_network.c", "reuse_tcp_insert");
+       for(i=0; i<outnet.num_tcp; i++) {
+               reuse_tcp_insert(&outnet, outnet.tcp_conns[i]);
+               check_tree_and_list(&outnet, i+1);
+       }
+       /* check touching */
+       unit_show_func("services/outside_network.c", "reuse_tcp_lru_touch");
+       for(i=0; i<outnet.tcp_reuse.count; i++) {
+               for(reuse = outnet.tcp_reuse_first; reuse->lru_next; reuse = reuse->lru_next);
+               reuse_tcp_lru_touch(&outnet, reuse);
+               check_tree_and_list(&outnet, outnet.num_tcp);
+       }
+       /* check removal */
+       unit_show_func("services/outside_network.c", "reuse_tcp_remove_tree_list");
+       check_removal(&outnet, 2, 5);
+       check_removal(&outnet, 1, 3);
+       check_removal(&outnet, 1, 2);
+       
+       /* check snip */
+       unit_show_func("services/outside_network.c", "reuse_tcp_lru_snip");
+       check_snip(&outnet, 4);
+
+       for(i=0; i<outnet.num_tcp; i++)
+               if(outnet.tcp_conns[i]) {
+                       free(outnet.tcp_conns[i]);
+               }
+       free(outnet.tcp_conns);
+}
+
+void tcpreuse_test(void)
+{
+    unit_show_feature("tcp_reuse");
+    tcpid_test();
+    tcp_reuse_tree_list_test();
+}