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