npf_test_subr.c revision 1.18 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(npf_t *, ifnet_t *);
47 static ifnet_t * npftest_ifop_lookup(npf_t *, const char *);
48 static void npftest_ifop_flush(npf_t *, void *);
49 static void * npftest_ifop_getmeta(npf_t *, const ifnet_t *);
50 static void npftest_ifop_setmeta(npf_t *, ifnet_t *, void *);
51
52 const npf_ifops_t npftest_ifops = {
53 .getname = npftest_ifop_getname,
54 .lookup = npftest_ifop_lookup,
55 .flush = npftest_ifop_flush,
56 .getmeta = npftest_ifop_getmeta,
57 .setmeta = npftest_ifop_setmeta,
58 };
59
60 void
61 npf_test_init(int (*pton_func)(int, const char *, void *),
62 const char *(*ntop_func)(int, const void *, char *, socklen_t),
63 long (*rndfunc)(void))
64 {
65 npf_t *npf;
66
67 #ifdef __NetBSD__
68 // XXX: Workaround for npf_init()
69 if ((npf = npf_getkernctx()) != NULL) {
70 npf_worker_discharge(npf);
71 npf_worker_sysfini();
72 }
73 #endif
74 npfk_sysinit(0);
75 npf = npfk_create(0, &npftest_mbufops, &npftest_ifops, NULL);
76 npfk_thread_register(npf);
77 npf_setkernctx(npf);
78
79 npf_state_setsampler(npf_state_sample);
80 _pton_func = pton_func;
81 _ntop_func = ntop_func;
82 _random_func = rndfunc;
83
84 (void)npf_test_addif(IFNAME_DUMMY, false, false);
85 }
86
87 void
88 npf_test_fini(void)
89 {
90 npf_t *npf = npf_getkernctx();
91 npfk_thread_unregister(npf);
92 npfk_destroy(npf);
93 npfk_sysfini();
94 }
95
96 int
97 npf_test_load(const void *buf, size_t len, bool verbose)
98 {
99 nvlist_t *npf_dict;
100 npf_error_t error;
101 int ret;
102
103 npf_dict = nvlist_unpack(buf, len, 0);
104 if (!npf_dict) {
105 printf("%s: could not unpack the nvlist\n", __func__);
106 return EINVAL;
107 }
108 load_npf_config_ifs(npf_dict, verbose);
109 ret = npfk_load(npf_getkernctx(), npf_dict, &error);
110 nvlist_destroy(npf_dict);
111 return ret;
112 }
113
114 ifnet_t *
115 npf_test_addif(const char *ifname, bool reg, bool verbose)
116 {
117 npf_t *npf = npf_getkernctx();
118 ifnet_t *ifp = kmem_zalloc(sizeof(*ifp), KM_SLEEP);
119
120 /*
121 * This is a "fake" interface with explicitly set index.
122 * Note: test modules may not setup pfil(9) hooks and if_attach()
123 * may not trigger npf_ifmap_attach(), so we call it manually.
124 */
125 strlcpy(ifp->if_xname, ifname, sizeof(ifp->if_xname));
126 TAILQ_INSERT_TAIL(&npftest_ifnet_list, ifp, if_list);
127
128 npfk_ifmap_attach(npf, ifp);
129 if (reg) {
130 npf_ifmap_register(npf, ifname);
131 }
132
133 if (verbose) {
134 printf("+ Interface %s\n", ifname);
135 }
136 return ifp;
137 }
138
139 ifnet_t *
140 npf_test_getif(const char *ifname)
141 {
142 ifnet_t *ifp;
143
144 TAILQ_FOREACH(ifp, &npftest_ifnet_list, if_list) {
145 if (!strcmp(ifp->if_xname, ifname))
146 return ifp;
147 }
148 return NULL;
149 }
150
151 static void
152 load_npf_config_ifs(nvlist_t *npf_dict, bool verbose)
153 {
154 const nvlist_t * const *iflist;
155 const nvlist_t *dbg_dict;
156 size_t nitems;
157
158 dbg_dict = dnvlist_get_nvlist(npf_dict, "debug", NULL);
159 if (!dbg_dict) {
160 return;
161 }
162 if (!nvlist_exists_nvlist_array(dbg_dict, "interfaces")) {
163 return;
164 }
165 iflist = nvlist_get_nvlist_array(dbg_dict, "interfaces", &nitems);
166 for (unsigned i = 0; i < nitems; i++) {
167 const nvlist_t *ifdict = iflist[i];
168 const char *ifname;
169
170 if ((ifname = nvlist_get_string(ifdict, "name")) != NULL) {
171 (void)npf_test_addif(ifname, true, verbose);
172 }
173 }
174 }
175
176 static const char *
177 npftest_ifop_getname(npf_t *npf __unused, ifnet_t *ifp)
178 {
179 return ifp->if_xname;
180 }
181
182 static ifnet_t *
183 npftest_ifop_lookup(npf_t *npf __unused, const char *ifname)
184 {
185 return npf_test_getif(ifname);
186 }
187
188 static void
189 npftest_ifop_flush(npf_t *npf __unused, void *arg)
190 {
191 ifnet_t *ifp;
192
193 TAILQ_FOREACH(ifp, &npftest_ifnet_list, if_list)
194 ifp->if_softc = arg;
195 }
196
197 static void *
198 npftest_ifop_getmeta(npf_t *npf __unused, const ifnet_t *ifp)
199 {
200 return ifp->if_softc;
201 }
202
203 static void
204 npftest_ifop_setmeta(npf_t *npf __unused, ifnet_t *ifp, void *arg)
205 {
206 ifp->if_softc = arg;
207 }
208
209 /*
210 * State sampler - this routine is called from inside of NPF state engine.
211 */
212 static void
213 npf_state_sample(npf_state_t *nst, bool retval)
214 {
215 /* Pointer will serve as an ID. */
216 cstream_ptr = nst;
217 memcpy(&cstream_state, nst, sizeof(npf_state_t));
218 cstream_retval = retval;
219 }
220
221 int
222 npf_test_statetrack(const void *data, size_t len, ifnet_t *ifp,
223 bool forw, int64_t *result)
224 {
225 npf_t *npf = npf_getkernctx();
226 struct mbuf *m;
227 int i = 0, error;
228
229 m = mbuf_getwithdata(data, len);
230 error = npfk_packet_handler(npf, &m, ifp, forw ? PFIL_OUT : PFIL_IN);
231 if (error) {
232 assert(m == NULL);
233 return error;
234 }
235 assert(m != NULL);
236 m_freem(m);
237
238 const int di = forw ? NPF_FLOW_FORW : NPF_FLOW_BACK;
239 npf_tcpstate_t *fstate = &cstream_state.nst_tcpst[di];
240 npf_tcpstate_t *tstate = &cstream_state.nst_tcpst[!di];
241
242 result[i++] = (intptr_t)cstream_ptr;
243 result[i++] = cstream_retval;
244 result[i++] = cstream_state.nst_state;
245
246 result[i++] = fstate->nst_end;
247 result[i++] = fstate->nst_maxend;
248 result[i++] = fstate->nst_maxwin;
249 result[i++] = fstate->nst_wscale;
250
251 result[i++] = tstate->nst_end;
252 result[i++] = tstate->nst_maxend;
253 result[i++] = tstate->nst_maxwin;
254 result[i++] = tstate->nst_wscale;
255
256 return 0;
257 }
258
259 int
260 npf_inet_pton(int af, const char *src, void *dst)
261 {
262 return _pton_func(af, src, dst);
263 }
264
265 const char *
266 npf_inet_ntop(int af, const void *src, char *dst, socklen_t size)
267 {
268 return _ntop_func(af, src, dst, size);
269 }
270
271 #ifdef _KERNEL
272 /*
273 * Need to override cprng_fast32() -- we need deterministic PRNG.
274 */
275 uint32_t
276 cprng_fast32(void)
277 {
278 return (uint32_t)(_random_func ? _random_func() : random());
279 }
280 #endif
281