xref: /netbsd-src/usr.sbin/npf/npftest/libnpftest/npf_gc_test.c (revision b13ab56848f10cbfb8f1264128574cde18e39cf5)
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
count_conns(npf_conndb_t * cd)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 *
get_packet(unsigned i)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
enqueue_connection(unsigned i,bool expire)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
run_conn_gc(unsigned active,unsigned expired,unsigned expected)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
run_gc_tests(void)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
run_conndb_tests(npf_t * npf)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
worker_test_task(npf_t * npf)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
run_worker_tests(npf_t * npf)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
npf_gc_test(bool verbose)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