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