xref: /netbsd-src/usr.sbin/npf/npftest/libnpftest/npf_test_subr.c (revision 404ee5b9334f618040b6cdef96a0ff35a6fc4636)
1 /*
2  * NPF initialisation and handler routines.
3  *
4  * Public Domain.
5  */
6 
7 #ifdef _KERNEL
8 #include <sys/types.h>
9 #include <sys/cprng.h>
10 #include <sys/kmem.h>
11 #include <net/if.h>
12 #include <net/if_types.h>
13 #endif
14 
15 #include "npf_impl.h"
16 #include "npf_test.h"
17 
18 /* State of the current stream. */
19 static npf_state_t	cstream_state;
20 static void *		cstream_ptr;
21 static bool		cstream_retval;
22 
23 static long		(*_random_func)(void);
24 static int		(*_pton_func)(int, const char *, void *);
25 static const char *	(*_ntop_func)(int, const void *, char *, socklen_t);
26 
27 static void		npf_state_sample(npf_state_t *, bool);
28 
29 static void		load_npf_config_ifs(nvlist_t *, bool);
30 
31 #ifndef __NetBSD__
32 /*
33  * Standalone NPF: we define the same struct ifnet members
34  * to reduce the npf_ifops_t implementation differences.
35  */
36 struct ifnet {
37 	char		if_xname[32];
38 	void *		if_softc;
39 	TAILQ_ENTRY(ifnet) if_list;
40 };
41 #endif
42 
43 static TAILQ_HEAD(, ifnet) npftest_ifnet_list =
44     TAILQ_HEAD_INITIALIZER(npftest_ifnet_list);
45 
46 static const char *	npftest_ifop_getname(ifnet_t *);
47 static void		npftest_ifop_flush(void *);
48 static void *		npftest_ifop_getmeta(const ifnet_t *);
49 static void		npftest_ifop_setmeta(ifnet_t *, void *);
50 
51 const npf_ifops_t npftest_ifops = {
52 	.getname	= npftest_ifop_getname,
53 	.lookup		= npf_test_getif,
54 	.flush		= npftest_ifop_flush,
55 	.getmeta	= npftest_ifop_getmeta,
56 	.setmeta	= npftest_ifop_setmeta,
57 };
58 
59 void
60 npf_test_init(int (*pton_func)(int, const char *, void *),
61     const char *(*ntop_func)(int, const void *, char *, socklen_t),
62     long (*rndfunc)(void))
63 {
64 	npf_t *npf;
65 
66 	npfk_sysinit(0);
67 	npf = npfk_create(0, &npftest_mbufops, &npftest_ifops);
68 	npfk_thread_register(npf);
69 	npf_setkernctx(npf);
70 
71 	npf_state_setsampler(npf_state_sample);
72 	_pton_func = pton_func;
73 	_ntop_func = ntop_func;
74 	_random_func = rndfunc;
75 
76 	(void)npf_test_addif(IFNAME_DUMMY, false, false);
77 }
78 
79 void
80 npf_test_fini(void)
81 {
82 	npf_t *npf = npf_getkernctx();
83 	npfk_thread_unregister(npf);
84 	npfk_destroy(npf);
85 	npfk_sysfini();
86 }
87 
88 int
89 npf_test_load(const void *buf, size_t len, bool verbose)
90 {
91 	nvlist_t *npf_dict;
92 	npf_error_t error;
93 
94 	npf_dict = nvlist_unpack(buf, len, 0);
95 	if (!npf_dict) {
96 		printf("%s: could not unpack the nvlist\n", __func__);
97 		return EINVAL;
98 	}
99 	load_npf_config_ifs(npf_dict, verbose);
100 
101 	// Note: npf_dict will be consumed by npf_load().
102 	return npfk_load(npf_getkernctx(), npf_dict, &error);
103 }
104 
105 ifnet_t *
106 npf_test_addif(const char *ifname, bool reg, bool verbose)
107 {
108 	npf_t *npf = npf_getkernctx();
109 	ifnet_t *ifp = kmem_zalloc(sizeof(*ifp), KM_SLEEP);
110 
111 	/*
112 	 * This is a "fake" interface with explicitly set index.
113 	 * Note: test modules may not setup pfil(9) hooks and if_attach()
114 	 * may not trigger npf_ifmap_attach(), so we call it manually.
115 	 */
116 	strlcpy(ifp->if_xname, ifname, sizeof(ifp->if_xname));
117 	TAILQ_INSERT_TAIL(&npftest_ifnet_list, ifp, if_list);
118 
119 	npfk_ifmap_attach(npf, ifp);
120 	if (reg) {
121 		npf_ifmap_register(npf, ifname);
122 	}
123 
124 	if (verbose) {
125 		printf("+ Interface %s\n", ifname);
126 	}
127 	return ifp;
128 }
129 
130 static void
131 load_npf_config_ifs(nvlist_t *npf_dict, bool verbose)
132 {
133 	const nvlist_t * const *iflist;
134 	const nvlist_t *dbg_dict;
135 	size_t nitems;
136 
137 	dbg_dict = dnvlist_get_nvlist(npf_dict, "debug", NULL);
138 	if (!dbg_dict) {
139 		return;
140 	}
141 	if (!nvlist_exists_nvlist_array(dbg_dict, "interfaces")) {
142 		return;
143 	}
144 	iflist = nvlist_get_nvlist_array(dbg_dict, "interfaces", &nitems);
145 	for (unsigned i = 0; i < nitems; i++) {
146 		const nvlist_t *ifdict = iflist[i];
147 		const char *ifname;
148 
149 		if ((ifname = nvlist_get_string(ifdict, "name")) != NULL) {
150 			(void)npf_test_addif(ifname, true, verbose);
151 		}
152 	}
153 }
154 
155 static const char *
156 npftest_ifop_getname(ifnet_t *ifp)
157 {
158 	return ifp->if_xname;
159 }
160 
161 ifnet_t *
162 npf_test_getif(const char *ifname)
163 {
164 	ifnet_t *ifp;
165 
166 	TAILQ_FOREACH(ifp, &npftest_ifnet_list, if_list) {
167 		if (!strcmp(ifp->if_xname, ifname))
168 			return ifp;
169 	}
170 	return NULL;
171 }
172 
173 static void
174 npftest_ifop_flush(void *arg)
175 {
176 	ifnet_t *ifp;
177 
178 	TAILQ_FOREACH(ifp, &npftest_ifnet_list, if_list)
179 		ifp->if_softc = arg;
180 }
181 
182 static void *
183 npftest_ifop_getmeta(const ifnet_t *ifp)
184 {
185 	return ifp->if_softc;
186 }
187 
188 static void
189 npftest_ifop_setmeta(ifnet_t *ifp, void *arg)
190 {
191 	ifp->if_softc = arg;
192 }
193 
194 /*
195  * State sampler - this routine is called from inside of NPF state engine.
196  */
197 static void
198 npf_state_sample(npf_state_t *nst, bool retval)
199 {
200 	/* Pointer will serve as an ID. */
201 	cstream_ptr = nst;
202 	memcpy(&cstream_state, nst, sizeof(npf_state_t));
203 	cstream_retval = retval;
204 }
205 
206 int
207 npf_test_statetrack(const void *data, size_t len, ifnet_t *ifp,
208     bool forw, int64_t *result)
209 {
210 	npf_t *npf = npf_getkernctx();
211 	struct mbuf *m;
212 	int i = 0, error;
213 
214 	m = mbuf_getwithdata(data, len);
215 	error = npfk_packet_handler(npf, &m, ifp, forw ? PFIL_OUT : PFIL_IN);
216 	if (error) {
217 		assert(m == NULL);
218 		return error;
219 	}
220 	assert(m != NULL);
221 	m_freem(m);
222 
223 	const int di = forw ? NPF_FLOW_FORW : NPF_FLOW_BACK;
224 	npf_tcpstate_t *fstate = &cstream_state.nst_tcpst[di];
225 	npf_tcpstate_t *tstate = &cstream_state.nst_tcpst[!di];
226 
227 	result[i++] = (intptr_t)cstream_ptr;
228 	result[i++] = cstream_retval;
229 	result[i++] = cstream_state.nst_state;
230 
231 	result[i++] = fstate->nst_end;
232 	result[i++] = fstate->nst_maxend;
233 	result[i++] = fstate->nst_maxwin;
234 	result[i++] = fstate->nst_wscale;
235 
236 	result[i++] = tstate->nst_end;
237 	result[i++] = tstate->nst_maxend;
238 	result[i++] = tstate->nst_maxwin;
239 	result[i++] = tstate->nst_wscale;
240 
241 	return 0;
242 }
243 
244 int
245 npf_inet_pton(int af, const char *src, void *dst)
246 {
247 	return _pton_func(af, src, dst);
248 }
249 
250 const char *
251 npf_inet_ntop(int af, const void *src, char *dst, socklen_t size)
252 {
253 	return _ntop_func(af, src, dst, size);
254 }
255 
256 #ifdef _KERNEL
257 /*
258  * Need to override cprng_fast32() -- we need deterministic PRNG.
259  */
260 uint32_t
261 cprng_fast32(void)
262 {
263 	return (uint32_t)(_random_func ? _random_func() : random());
264 }
265 #endif
266