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