- Add unittest for tcp_reuse functions.

This commit is contained in:
George Thessalonikefs 2021-07-24 01:15:00 +02:00
parent 1b7358e85c
commit ab318a8b95
6 changed files with 292 additions and 77 deletions

View 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 \

View 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

View 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.

View 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 */

View 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 */

236
testcode/unittcpreuse.c Normal file
View File

@ -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();
}