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); 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