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