1 /*
2 * This file and its contents are supplied under the terms of the
3 * Common Development and Distribution License ("CDDL"), version 1.0.
4 * You may only use this file in accordance with the terms of version
5 * 1.0 of the CDDL.
6 *
7 * A full copy of the text of the CDDL should have accompanied this
8 * source. A copy of the CDDL is also available via the Internet at
9 * http://www.illumos.org/license/CDDL.
10 *
11 * Copyright 2019 Joyent, Inc.
12 */
13
14 #include <sys/hook.h>
15 #include <sys/hook_event.h>
16
17 #include "viona_impl.h"
18
19
20 /*
21 * Global linked list of viona_neti_ts. Access is protected by viona_neti_lock
22 */
23 static list_t viona_neti_list;
24 static kmutex_t viona_neti_lock;
25
26 /*
27 * viona_neti is allocated and initialized during attach, and read-only
28 * until detach (where it's also freed)
29 */
30 static net_instance_t *viona_neti;
31
32
33 /*
34 * Generate a hook event for the packet in *mpp headed in the direction
35 * indicated by 'out'. If the packet is accepted, 0 is returned. If the
36 * packet is rejected, an error is returned. The hook function may or may not
37 * alter or even free *mpp. The caller is expected to deal with either
38 * situation.
39 */
40 int
viona_hook(viona_link_t * link,viona_vring_t * ring,mblk_t ** mpp,boolean_t out)41 viona_hook(viona_link_t *link, viona_vring_t *ring, mblk_t **mpp, boolean_t out)
42 {
43 viona_neti_t *nip = link->l_neti;
44 viona_nethook_t *vnh = &nip->vni_nethook;
45 hook_pkt_event_t info;
46 hook_event_t he;
47 hook_event_token_t het;
48 int ret;
49
50 he = out ? vnh->vnh_event_out : vnh->vnh_event_in;
51 het = out ? vnh->vnh_token_out : vnh->vnh_token_in;
52
53 if (!he.he_interested)
54 return (0);
55
56 info.hpe_protocol = vnh->vnh_neti;
57 info.hpe_ifp = (phy_if_t)link;
58 info.hpe_ofp = (phy_if_t)link;
59 info.hpe_mp = mpp;
60 info.hpe_flags = 0;
61
62 ret = hook_run(vnh->vnh_neti->netd_hooks, het, (hook_data_t)&info);
63 if (ret == 0)
64 return (0);
65
66 if (out) {
67 VIONA_PROBE3(tx_hook_drop, viona_vring_t *, ring,
68 mblk_t *, *mpp, int, ret);
69 VIONA_RING_STAT_INCR(ring, tx_hookdrop);
70 } else {
71 VIONA_PROBE3(rx_hook_drop, viona_vring_t *, ring,
72 mblk_t *, *mpp, int, ret);
73 VIONA_RING_STAT_INCR(ring, rx_hookdrop);
74 }
75 return (ret);
76 }
77
78 /*
79 * netinfo stubs - required by the nethook framework, but otherwise unused
80 *
81 * Currently, all ipf rules are applied against all interfaces in a given
82 * netstack (e.g. all interfaces in a zone). In the future if we want to
83 * support being able to apply different rules to different interfaces, I
84 * believe we would need to implement some of these stubs to map an interface
85 * name in a rule (e.g. 'net0', back to an index or viona_link_t);
86 */
87 static int
viona_neti_getifname(net_handle_t neti __unused,phy_if_t phy __unused,char * buf __unused,const size_t len __unused)88 viona_neti_getifname(net_handle_t neti __unused, phy_if_t phy __unused,
89 char *buf __unused, const size_t len __unused)
90 {
91 return (-1);
92 }
93
94 static int
viona_neti_getmtu(net_handle_t neti __unused,phy_if_t phy __unused,lif_if_t ifdata __unused)95 viona_neti_getmtu(net_handle_t neti __unused, phy_if_t phy __unused,
96 lif_if_t ifdata __unused)
97 {
98 return (-1);
99 }
100
101 static int
viona_neti_getptmue(net_handle_t neti __unused)102 viona_neti_getptmue(net_handle_t neti __unused)
103 {
104 return (-1);
105 }
106
107 static int
viona_neti_getlifaddr(net_handle_t neti __unused,phy_if_t phy __unused,lif_if_t ifdata __unused,size_t nelem __unused,net_ifaddr_t type[]__unused,void * storage __unused)108 viona_neti_getlifaddr(net_handle_t neti __unused, phy_if_t phy __unused,
109 lif_if_t ifdata __unused, size_t nelem __unused,
110 net_ifaddr_t type[] __unused, void *storage __unused)
111 {
112 return (-1);
113 }
114
115 static int
viona_neti_getlifzone(net_handle_t neti __unused,phy_if_t phy __unused,lif_if_t ifdata __unused,zoneid_t * zid __unused)116 viona_neti_getlifzone(net_handle_t neti __unused, phy_if_t phy __unused,
117 lif_if_t ifdata __unused, zoneid_t *zid __unused)
118 {
119 return (-1);
120 }
121
122 static int
viona_neti_getlifflags(net_handle_t neti __unused,phy_if_t phy __unused,lif_if_t ifdata __unused,uint64_t * flags __unused)123 viona_neti_getlifflags(net_handle_t neti __unused, phy_if_t phy __unused,
124 lif_if_t ifdata __unused, uint64_t *flags __unused)
125 {
126 return (-1);
127 }
128
129 static phy_if_t
viona_neti_phygetnext(net_handle_t neti __unused,phy_if_t phy __unused)130 viona_neti_phygetnext(net_handle_t neti __unused, phy_if_t phy __unused)
131 {
132 return ((phy_if_t)-1);
133 }
134
135 static phy_if_t
viona_neti_phylookup(net_handle_t neti __unused,const char * name __unused)136 viona_neti_phylookup(net_handle_t neti __unused, const char *name __unused)
137 {
138 return ((phy_if_t)-1);
139 }
140
141 static lif_if_t
viona_neti_lifgetnext(net_handle_t neti __unused,phy_if_t phy __unused,lif_if_t ifdata __unused)142 viona_neti_lifgetnext(net_handle_t neti __unused, phy_if_t phy __unused,
143 lif_if_t ifdata __unused)
144 {
145 return (-1);
146 }
147
148 static int
viona_neti_inject(net_handle_t neti __unused,inject_t style __unused,net_inject_t * packet __unused)149 viona_neti_inject(net_handle_t neti __unused, inject_t style __unused,
150 net_inject_t *packet __unused)
151 {
152 return (-1);
153 }
154
155 static phy_if_t
viona_neti_route(net_handle_t neti __unused,struct sockaddr * address __unused,struct sockaddr * next __unused)156 viona_neti_route(net_handle_t neti __unused, struct sockaddr *address __unused,
157 struct sockaddr *next __unused)
158 {
159 return ((phy_if_t)-1);
160 }
161
162 static int
viona_neti_ispchksum(net_handle_t neti __unused,mblk_t * mp __unused)163 viona_neti_ispchksum(net_handle_t neti __unused, mblk_t *mp __unused)
164 {
165 return (-1);
166 }
167
168 static int
viona_neti_isvchksum(net_handle_t neti __unused,mblk_t * mp __unused)169 viona_neti_isvchksum(net_handle_t neti __unused, mblk_t *mp __unused)
170 {
171 return (-1);
172 }
173
174 static net_protocol_t viona_netinfo = {
175 NETINFO_VERSION,
176 NHF_VIONA,
177 viona_neti_getifname,
178 viona_neti_getmtu,
179 viona_neti_getptmue,
180 viona_neti_getlifaddr,
181 viona_neti_getlifzone,
182 viona_neti_getlifflags,
183 viona_neti_phygetnext,
184 viona_neti_phylookup,
185 viona_neti_lifgetnext,
186 viona_neti_inject,
187 viona_neti_route,
188 viona_neti_ispchksum,
189 viona_neti_isvchksum
190 };
191
192 /*
193 * Create/register our nethooks
194 */
195 static int
viona_nethook_init(netid_t nid,viona_nethook_t * vnh,char * nh_name,net_protocol_t * netip)196 viona_nethook_init(netid_t nid, viona_nethook_t *vnh, char *nh_name,
197 net_protocol_t *netip)
198 {
199 int ret;
200
201 if ((vnh->vnh_neti = net_protocol_register(nid, netip)) == NULL) {
202 cmn_err(CE_NOTE, "%s: net_protocol_register failed "
203 "(netid=%d name=%s)", __func__, nid, nh_name);
204 goto fail_init_proto;
205 }
206
207 HOOK_FAMILY_INIT(&vnh->vnh_family, nh_name);
208 if ((ret = net_family_register(vnh->vnh_neti, &vnh->vnh_family)) != 0) {
209 cmn_err(CE_NOTE, "%s: net_family_register failed "
210 "(netid=%d name=%s err=%d)", __func__,
211 nid, nh_name, ret);
212 goto fail_init_family;
213 }
214
215 HOOK_EVENT_INIT(&vnh->vnh_event_in, NH_PHYSICAL_IN);
216 if ((vnh->vnh_token_in = net_event_register(vnh->vnh_neti,
217 &vnh->vnh_event_in)) == NULL) {
218 cmn_err(CE_NOTE, "%s: net_event_register %s failed "
219 "(netid=%d name=%s)", __func__, NH_PHYSICAL_IN, nid,
220 nh_name);
221 goto fail_init_event_in;
222 }
223
224 HOOK_EVENT_INIT(&vnh->vnh_event_out, NH_PHYSICAL_OUT);
225 if ((vnh->vnh_token_out = net_event_register(vnh->vnh_neti,
226 &vnh->vnh_event_out)) == NULL) {
227 cmn_err(CE_NOTE, "%s: net_event_register %s failed "
228 "(netid=%d name=%s)", __func__, NH_PHYSICAL_OUT, nid,
229 nh_name);
230 goto fail_init_event_out;
231 }
232 return (0);
233
234 /*
235 * On failure, we undo all the steps that succeeded in the
236 * reverse order of initialization, starting at the last
237 * successful step (the labels denoting the failing step).
238 */
239 fail_init_event_out:
240 VERIFY0(net_event_shutdown(vnh->vnh_neti, &vnh->vnh_event_in));
241 VERIFY0(net_event_unregister(vnh->vnh_neti, &vnh->vnh_event_in));
242 vnh->vnh_token_in = NULL;
243
244 fail_init_event_in:
245 VERIFY0(net_family_shutdown(vnh->vnh_neti, &vnh->vnh_family));
246 VERIFY0(net_family_unregister(vnh->vnh_neti, &vnh->vnh_family));
247
248 fail_init_family:
249 VERIFY0(net_protocol_unregister(vnh->vnh_neti));
250 vnh->vnh_neti = NULL;
251
252 fail_init_proto:
253 return (1);
254 }
255
256 /*
257 * Shutdown the nethooks for a protocol family. This triggers notification
258 * callbacks to anything that has registered interest to allow hook consumers
259 * to unhook prior to the removal of the hooks as well as makes them unavailable
260 * to any future consumers as the first step of removal.
261 */
262 static void
viona_nethook_shutdown(viona_nethook_t * vnh)263 viona_nethook_shutdown(viona_nethook_t *vnh)
264 {
265 VERIFY0(net_event_shutdown(vnh->vnh_neti, &vnh->vnh_event_out));
266 VERIFY0(net_event_shutdown(vnh->vnh_neti, &vnh->vnh_event_in));
267 VERIFY0(net_family_shutdown(vnh->vnh_neti, &vnh->vnh_family));
268 }
269
270 /*
271 * Remove the nethooks for a protocol family.
272 */
273 static void
viona_nethook_fini(viona_nethook_t * vnh)274 viona_nethook_fini(viona_nethook_t *vnh)
275 {
276 VERIFY0(net_event_unregister(vnh->vnh_neti, &vnh->vnh_event_out));
277 VERIFY0(net_event_unregister(vnh->vnh_neti, &vnh->vnh_event_in));
278 VERIFY0(net_family_unregister(vnh->vnh_neti, &vnh->vnh_family));
279 VERIFY0(net_protocol_unregister(vnh->vnh_neti));
280 vnh->vnh_neti = NULL;
281 }
282
283 /*
284 * Callback invoked by the neti module. This creates/registers our hooks
285 * {IPv4,IPv6}{in,out} with the nethook framework so they are available to
286 * interested consumers (e.g. ipf).
287 *
288 * During attach, viona_neti_create is called once for every netstack
289 * present on the system at the time of attach. Thereafter, it is called
290 * during the creation of additional netstack instances (i.e. zone boot). As a
291 * result, the viona_neti_t that is created during this call always occurs
292 * prior to any viona instances that will use it to send hook events.
293 *
294 * It should never return NULL. If we cannot register our hooks, we do not
295 * set vnh_hooked of the respective protocol family, which will prevent the
296 * creation of any viona instances on this netstack (see viona_ioc_create).
297 * This can only occur if after a shutdown event (which means destruction is
298 * imminent) we are trying to create a new instance.
299 */
300 static void *
viona_neti_create(const netid_t netid)301 viona_neti_create(const netid_t netid)
302 {
303 viona_neti_t *nip;
304
305 VERIFY(netid != -1);
306
307 nip = kmem_zalloc(sizeof (*nip), KM_SLEEP);
308 nip->vni_netid = netid;
309 nip->vni_zid = net_getzoneidbynetid(netid);
310 mutex_init(&nip->vni_lock, NULL, MUTEX_DRIVER, NULL);
311 list_create(&nip->vni_dev_list, sizeof (viona_soft_state_t),
312 offsetof(viona_soft_state_t, ss_node));
313
314 if (viona_nethook_init(netid, &nip->vni_nethook, Hn_VIONA,
315 &viona_netinfo) == 0)
316 nip->vni_nethook.vnh_hooked = B_TRUE;
317
318 mutex_enter(&viona_neti_lock);
319 list_insert_tail(&viona_neti_list, nip);
320 mutex_exit(&viona_neti_lock);
321
322 return (nip);
323 }
324
325 /*
326 * Called during netstack teardown by the neti module. During teardown, all
327 * the shutdown callbacks are invoked, allowing consumers to release any holds
328 * and otherwise quiesce themselves prior to destruction, followed by the
329 * actual destruction callbacks.
330 */
331 static void
viona_neti_shutdown(netid_t nid,void * arg)332 viona_neti_shutdown(netid_t nid, void *arg)
333 {
334 viona_neti_t *nip = arg;
335
336 ASSERT(nip != NULL);
337 VERIFY(nid == nip->vni_netid);
338
339 mutex_enter(&viona_neti_lock);
340 list_remove(&viona_neti_list, nip);
341 mutex_exit(&viona_neti_lock);
342
343 if (nip->vni_nethook.vnh_hooked)
344 viona_nethook_shutdown(&nip->vni_nethook);
345 }
346
347 /*
348 * Called during netstack teardown by the neti module. Destroys the viona
349 * netinst data. This is invoked after all the netstack and neti shutdown
350 * callbacks have been invoked.
351 */
352 static void
viona_neti_destroy(netid_t nid,void * arg)353 viona_neti_destroy(netid_t nid, void *arg)
354 {
355 viona_neti_t *nip = arg;
356
357 ASSERT(nip != NULL);
358 VERIFY(nid == nip->vni_netid);
359
360 mutex_enter(&nip->vni_lock);
361 while (nip->vni_ref != 0)
362 cv_wait(&nip->vni_ref_change, &nip->vni_lock);
363 mutex_exit(&nip->vni_lock);
364
365 VERIFY(!list_link_active(&nip->vni_node));
366
367 if (nip->vni_nethook.vnh_hooked)
368 viona_nethook_fini(&nip->vni_nethook);
369
370 mutex_destroy(&nip->vni_lock);
371 list_destroy(&nip->vni_dev_list);
372 kmem_free(nip, sizeof (*nip));
373 }
374
375 /*
376 * Find the viona netinst data by zone id. This is only used during
377 * viona instance creation (and thus is only called by a zone that is running).
378 */
379 viona_neti_t *
viona_neti_lookup_by_zid(zoneid_t zid)380 viona_neti_lookup_by_zid(zoneid_t zid)
381 {
382 viona_neti_t *nip;
383
384 mutex_enter(&viona_neti_lock);
385 for (nip = list_head(&viona_neti_list); nip != NULL;
386 nip = list_next(&viona_neti_list, nip)) {
387 if (nip->vni_zid == zid) {
388 mutex_enter(&nip->vni_lock);
389 nip->vni_ref++;
390 mutex_exit(&nip->vni_lock);
391 mutex_exit(&viona_neti_lock);
392 return (nip);
393 }
394 }
395 mutex_exit(&viona_neti_lock);
396 return (NULL);
397 }
398
399 void
viona_neti_rele(viona_neti_t * nip)400 viona_neti_rele(viona_neti_t *nip)
401 {
402 mutex_enter(&nip->vni_lock);
403 VERIFY3S(nip->vni_ref, >, 0);
404 nip->vni_ref--;
405 mutex_exit(&nip->vni_lock);
406 cv_broadcast(&nip->vni_ref_change);
407 }
408
409 void
viona_neti_attach(void)410 viona_neti_attach(void)
411 {
412 mutex_init(&viona_neti_lock, NULL, MUTEX_DRIVER, NULL);
413 list_create(&viona_neti_list, sizeof (viona_neti_t),
414 offsetof(viona_neti_t, vni_node));
415
416 /* This can only fail if NETINFO_VERSION is wrong */
417 viona_neti = net_instance_alloc(NETINFO_VERSION);
418 VERIFY(viona_neti != NULL);
419
420 viona_neti->nin_name = "viona";
421 viona_neti->nin_create = viona_neti_create;
422 viona_neti->nin_shutdown = viona_neti_shutdown;
423 viona_neti->nin_destroy = viona_neti_destroy;
424 /* This can only fail if we've registered ourselves multiple times */
425 VERIFY3S(net_instance_register(viona_neti), ==, DDI_SUCCESS);
426 }
427
428 void
viona_neti_detach(void)429 viona_neti_detach(void)
430 {
431 /* This can only fail if we've not registered previously */
432 VERIFY3S(net_instance_unregister(viona_neti), ==, DDI_SUCCESS);
433 net_instance_free(viona_neti);
434 viona_neti = NULL;
435
436 list_destroy(&viona_neti_list);
437 mutex_destroy(&viona_neti_lock);
438 }
439