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