Home | History | Annotate | Line # | Download | only in libnpftest
npf_gc_test.c revision 1.1.2.3
      1 /*
      2  * NPF connection tests.
      3  *
      4  * Public Domain.
      5  */
      6 
      7 #ifdef _KERNEL
      8 #include <sys/types.h>
      9 #include <sys/kernel.h>
     10 #include <sys/kmem.h>
     11 #endif
     12 
     13 #include "npf.h"
     14 #include "npf_impl.h"
     15 #include "npf_conn.h"
     16 #include "npf_test.h"
     17 
     18 static bool	lverbose = false;
     19 
     20 static unsigned
     21 count_conns(npf_conndb_t *cd)
     22 {
     23 	npf_conn_t *head = npf_conndb_getlist(cd), *conn = head;
     24 	unsigned n = 0;
     25 
     26 	while (conn) {
     27 		n++;
     28 		conn = npf_conndb_getnext(cd, conn);
     29 		if (conn == head) {
     30 			break;
     31 		}
     32 	}
     33 	return n;
     34 }
     35 
     36 static struct mbuf *
     37 get_packet(unsigned i)
     38 {
     39 	struct mbuf *m;
     40 	struct ip *ip;
     41 
     42 	m = mbuf_get_pkt(AF_INET, IPPROTO_UDP,
     43 	    "10.0.0.1", "172.16.0.1", 9000, 9000);
     44 	(void)mbuf_return_hdrs(m, false, &ip);
     45 	ip->ip_src.s_addr += i;
     46 	return m;
     47 }
     48 
     49 static bool
     50 enqueue_connection(unsigned i, bool expire)
     51 {
     52 	struct mbuf *m = get_packet(i);
     53 	npf_cache_t *npc = get_cached_pkt(m, NULL);
     54 	npf_conn_t *con;
     55 
     56 	con = npf_conn_establish(npc, PFIL_IN, true);
     57 	CHECK_TRUE(con != NULL);
     58 	if (expire) {
     59 		npf_conn_expire(con);
     60 	}
     61 	npf_conn_release(con);
     62 	put_cached_pkt(npc);
     63 	return true;
     64 }
     65 
     66 static bool
     67 run_conn_gc(unsigned active, unsigned expired, unsigned expected)
     68 {
     69 	npf_t *npf = npf_getkernctx();
     70 	npf_conndb_t *cd = npf_conndb_create();
     71 	unsigned total, n = 0;
     72 
     73 	npf->conn_db = cd;
     74 
     75 	/*
     76 	 * Insert the given number of active and expired connections..
     77 	 */
     78 	total = active + expired;
     79 
     80 	while (active || expired) {
     81 		if (active) {
     82 			enqueue_connection(n++, false);
     83 			active--;
     84 		}
     85 		if (expired) {
     86 			enqueue_connection(n++, true);
     87 			expired--;
     88 		}
     89 	}
     90 
     91 	/* Verify the count. */
     92 	n = count_conns(cd);
     93 	CHECK_TRUE(n == total);
     94 
     95 	/*
     96 	 * Run G/C.  Check the remaining.
     97 	 */
     98 	npf_conndb_gc(npf, cd, false, false);
     99 	n = count_conns(cd);
    100 	if (lverbose) {
    101 		printf("in conndb -- %u (expected %u)\n", n, expected);
    102 	}
    103 	CHECK_TRUE(n == expected);
    104 
    105 	/* Flush and destroy. */
    106 	npf_conndb_gc(npf, cd, true, false);
    107 	npf_conndb_destroy(cd);
    108 	npf->conn_db = NULL;
    109 	return true;
    110 }
    111 
    112 static bool
    113 run_gc_tests(void)
    114 {
    115 	bool ok;
    116 	int val;
    117 
    118 	/* Check the default value. */
    119 	npfk_param_get(npf_getkernctx(), "gc.step", &val);
    120 	CHECK_TRUE(val == 256);
    121 
    122 	/* Empty => GC => 0 in conndb. */
    123 	ok = run_conn_gc(0, 0, 0);
    124 	CHECK_TRUE(ok);
    125 
    126 	/* 1 active => GC => 1 in conndb. */
    127 	ok = run_conn_gc(1, 0, 1);
    128 	CHECK_TRUE(ok);
    129 
    130 	/* 1 expired => GC => 0 in conndb. */
    131 	ok = run_conn_gc(0, 1, 0);
    132 	CHECK_TRUE(ok);
    133 
    134 	/* 1 active and 1 expired => GC => 1 in conndb. */
    135 	ok = run_conn_gc(1, 1, 1);
    136 	CHECK_TRUE(ok);
    137 
    138 	/* 2 expired => GC => 0 in conndb. */
    139 	ok = run_conn_gc(0, 2, 0);
    140 	CHECK_TRUE(ok);
    141 
    142 	/* 128 expired => GC => 0 in conndb. */
    143 	ok = run_conn_gc(0, 128, 0);
    144 	CHECK_TRUE(ok);
    145 
    146 	/* 512 expired => GC => 256 in conndb. */
    147 	ok = run_conn_gc(0, 512, 256);
    148 	CHECK_TRUE(ok);
    149 
    150 	/* 512 expired => GC => 127 in conndb. */
    151 	npfk_param_set(npf_getkernctx(), "gc.step", 128);
    152 	ok = run_conn_gc(0, 512, 384);
    153 	CHECK_TRUE(ok);
    154 
    155 	return true;
    156 }
    157 
    158 static bool
    159 run_conndb_tests(npf_t *npf)
    160 {
    161 	npf_conndb_t *orig_cd = npf->conn_db;
    162 	bool ok;
    163 
    164 	npf_config_enter(npf);
    165 	npf_conn_tracking(npf, true);
    166 	npf_config_exit(npf);
    167 
    168 	ok = run_gc_tests();
    169 
    170 	/* We *MUST* restore the valid conndb. */
    171 	npf->conn_db = orig_cd;
    172 	return ok;
    173 }
    174 
    175 
    176 static void
    177 worker_test_task(npf_t *npf)
    178 {
    179 	bool *done = atomic_load_acquire(&npf->arg);
    180 	atomic_store_release(done, true);
    181 }
    182 
    183 static bool
    184 run_worker_tests(npf_t *npf)
    185 {
    186 	unsigned n = 100;
    187 	int error;
    188 
    189 	/* Spawn a worker thread. */
    190 	error = npf_worker_sysinit(1);
    191 	assert(error == 0);
    192 
    193 	/*
    194 	 * Enlist/discharge an instance, trying to trigger a race.
    195 	 */
    196 	while (n--) {
    197 		bool task_done = false;
    198 		unsigned retry = 100;
    199 		npf_t *test_npf;
    200 
    201 		/*
    202 		 * Initialize a dummy NPF instance and add a test task.
    203 		 * We will (ab)use npf_t::arg here.
    204 		 *
    205 		 * XXX/TODO: We should use:
    206 		 *
    207 		 *	npfk_create(NPF_NO_GC, &npftest_mbufops,
    208 		 *	    &npftest_ifops, &task_done);
    209 		 *
    210 		 * However, it resets the interface state and breaks
    211 		 * other tests; to be refactor.
    212 		 */
    213 		test_npf = kmem_zalloc(sizeof(npf_t), KM_SLEEP);
    214 		atomic_store_release(&test_npf->arg, &task_done);
    215 		test_npf->ebr = npf_ebr_create();
    216 
    217 		error = npf_worker_addfunc(test_npf, worker_test_task);
    218 		assert(error == 0);
    219 
    220 		/* Enlist the NPF instance. */
    221 		npf_worker_enlist(test_npf);
    222 
    223 		/* Wait for the task to be done. */
    224 		while (!atomic_load_acquire(&task_done) && retry--) {
    225 			npf_worker_signal(test_npf);
    226 			kpause("gctest", false, mstohz(1), NULL);
    227 		}
    228 
    229 		CHECK_TRUE(atomic_load_acquire(&task_done));
    230 		npf_worker_discharge(test_npf);
    231 
    232 		/* Clear the parameter and signal again. */
    233 		atomic_store_release(&test_npf->arg, NULL);
    234 		npf_worker_signal(test_npf);
    235 
    236 		npf_ebr_destroy(test_npf->ebr);
    237 		kmem_free(test_npf, sizeof(npf_t)); // npfk_destroy()
    238 	}
    239 
    240 	/*
    241 	 * Destroy the worker.
    242 	 *
    243 	 * Attempts to enlist, discharge or signal should have no effect.
    244 	 */
    245 
    246 	npf_worker_sysfini();
    247 	npf_worker_enlist(npf);
    248 	npf_worker_signal(npf);
    249 	npf_worker_discharge(npf);
    250 	return true;
    251 }
    252 
    253 bool
    254 npf_gc_test(bool verbose)
    255 {
    256 	npf_t *npf = npf_getkernctx();
    257 	bool ok;
    258 
    259 	lverbose = verbose;
    260 
    261 	ok = run_conndb_tests(npf);
    262 	CHECK_TRUE(ok);
    263 
    264 	ok = run_worker_tests(npf);
    265 	CHECK_TRUE(ok);
    266 
    267 	return ok;
    268 }
    269