1 /*
2 * Copyright (c) 2022 Apple Inc. All rights reserved.
3 *
4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. The rights granted to you under the License
10 * may not be used to create, or enable the creation or redistribution of,
11 * unlawful or unlicensed copies of an Apple operating system, or to
12 * circumvent, violate, or enable the circumvention or violation of, any
13 * terms of an Apple operating system software license agreement.
14 *
15 * Please obtain a copy of the License at
16 * http://www.opensource.apple.com/apsl/ and read it before using this file.
17 *
18 * The Original Code and all software distributed under the License are
19 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
20 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
21 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
22 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
23 * Please see the License for the specific language governing rights and
24 * limitations under the License.
25 *
26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
27 */
28
29 #include <skywalk/nexus/nexus_traffic_rule_inet.h>
30 #include <skywalk/nexus/nexus_traffic_rule_eth.h>
31
32 static struct nxctl_traffic_rule_type nxctl_rule_types[] = {
33 {
34 .ntrt_type = IFNET_TRAFFIC_DESCRIPTOR_TYPE_INET,
35 .ntrt_validate = inet_traffic_rule_validate,
36 .ntrt_find = inet_traffic_rule_find,
37 .ntrt_find_by_uuid = inet_traffic_rule_find_by_uuid,
38 .ntrt_link = inet_traffic_rule_link,
39 .ntrt_unlink = inet_traffic_rule_unlink,
40 .ntrt_notify = inet_traffic_rule_notify,
41 .ntrt_create = inet_traffic_rule_create,
42 .ntrt_destroy = inet_traffic_rule_destroy,
43 .ntrt_get_all = inet_traffic_rule_get_all,
44 .ntrt_get_count = inet_traffic_rule_get_count,
45 },
46 {
47 .ntrt_type = IFNET_TRAFFIC_DESCRIPTOR_TYPE_ETH,
48 .ntrt_validate = eth_traffic_rule_validate,
49 .ntrt_find = eth_traffic_rule_find,
50 .ntrt_find_by_uuid = eth_traffic_rule_find_by_uuid,
51 .ntrt_link = eth_traffic_rule_link,
52 .ntrt_unlink = eth_traffic_rule_unlink,
53 .ntrt_notify = eth_traffic_rule_notify,
54 .ntrt_create = eth_traffic_rule_create,
55 .ntrt_destroy = eth_traffic_rule_destroy,
56 .ntrt_get_all = eth_traffic_rule_get_all,
57 .ntrt_get_count = eth_traffic_rule_get_count,
58 },
59 };
60 #define NRULETYPES \
61 (sizeof(nxctl_rule_types)/sizeof(struct nxctl_traffic_rule_type))
62
63 /* Per-fd list kept at the nxctl */
64 SLIST_HEAD(nxctl_traffic_rule_head, nxctl_traffic_rule);
65 struct nxctl_traffic_rule_storage {
66 struct nxctl_traffic_rule_head rs_list;
67 uint32_t rs_count;
68 };
69
70 static LCK_RW_DECLARE_ATTR(nxctl_traffic_rule_lock, &sk_lock_group, &sk_lock_attr);
71
72 SK_INLINE_ATTRIBUTE
73 void
nxtr_wlock(void)74 nxtr_wlock(void)
75 {
76 lck_rw_lock_exclusive(&nxctl_traffic_rule_lock);
77 }
78
79 SK_INLINE_ATTRIBUTE
80 void
nxtr_wunlock(void)81 nxtr_wunlock(void)
82 {
83 lck_rw_unlock_exclusive(&nxctl_traffic_rule_lock);
84 }
85
86 SK_INLINE_ATTRIBUTE
87 void
nxtr_rlock(void)88 nxtr_rlock(void)
89 {
90 lck_rw_lock_shared(&nxctl_traffic_rule_lock);
91 }
92
93 SK_INLINE_ATTRIBUTE
94 void
nxtr_runlock(void)95 nxtr_runlock(void)
96 {
97 lck_rw_unlock_shared(&nxctl_traffic_rule_lock);
98 }
99
100 static struct nxctl_traffic_rule_type *find_traffic_rule_type(uint8_t type);
101 static int remove_traffic_rule(struct nxctl *nxctl, uuid_t uuid,
102 struct nxctl_traffic_rule **ntrp);
103 static int notify_traffic_rule(struct nxctl_traffic_rule *ntr, uint32_t flags);
104
105 #define NXCTL_TRAFFIC_RULE_TAG "com.apple.skywalk.nexus.traffic_rule"
106 static kern_allocation_name_t nxctl_traffic_rule_tag;
107 static struct nxctl_traffic_rule_type *inet_traffic_rule_type = NULL;
108 static struct nxctl_traffic_rule_type *eth_traffic_rule_type = NULL;
109
110 void
nxctl_traffic_rule_init(void)111 nxctl_traffic_rule_init(void)
112 {
113 ASSERT(nxctl_traffic_rule_tag == NULL);
114 nxctl_traffic_rule_tag =
115 kern_allocation_name_allocate(NXCTL_TRAFFIC_RULE_TAG, 0);
116 ASSERT(nxctl_traffic_rule_tag != NULL);
117
118 ASSERT(inet_traffic_rule_type == NULL);
119 inet_traffic_rule_type =
120 find_traffic_rule_type(IFNET_TRAFFIC_DESCRIPTOR_TYPE_INET);
121 ASSERT(inet_traffic_rule_type != NULL);
122
123 ASSERT(eth_traffic_rule_type == NULL);
124 eth_traffic_rule_type =
125 find_traffic_rule_type(IFNET_TRAFFIC_DESCRIPTOR_TYPE_ETH);
126 ASSERT(eth_traffic_rule_type != NULL);
127
128 inet_traffic_rule_init(nxctl_traffic_rule_tag);
129 eth_traffic_rule_init(nxctl_traffic_rule_tag);
130 }
131
132 void
nxctl_traffic_rule_fini(void)133 nxctl_traffic_rule_fini(void)
134 {
135 if (nxctl_traffic_rule_tag != NULL) {
136 kern_allocation_name_release(nxctl_traffic_rule_tag);
137 nxctl_traffic_rule_tag = NULL;
138 }
139 inet_traffic_rule_type = NULL;
140 eth_traffic_rule_type = NULL;
141 }
142
143 SK_NO_INLINE_ATTRIBUTE
144 static struct nxctl_traffic_rule_storage *
nxctl_traffic_rule_storage_create(void)145 nxctl_traffic_rule_storage_create(void)
146 {
147 struct nxctl_traffic_rule_storage *rs;
148
149 rs = sk_alloc_type(struct nxctl_traffic_rule_storage,
150 Z_WAITOK | Z_NOFAIL, nxctl_traffic_rule_tag);
151 SLIST_INIT(&rs->rs_list);
152 rs->rs_count = 0;
153 return rs;
154 }
155
156 SK_NO_INLINE_ATTRIBUTE
157 static void
nxctl_traffic_rule_storage_destroy(struct nxctl_traffic_rule_storage * rs)158 nxctl_traffic_rule_storage_destroy(struct nxctl_traffic_rule_storage *rs)
159 {
160 ASSERT(rs->rs_count == 0);
161 ASSERT(SLIST_EMPTY(&rs->rs_list));
162 sk_free_type(struct nxctl_traffic_rule_storage, rs);
163 }
164
165 /*
166 * This is meant to be called during closure of the nxctl's fd.
167 * This will cleanup all rules linked to this nxctl. Rules that
168 * are marked persistent won't be added to the nxctl list.
169 */
170 void
nxctl_traffic_rule_clean(struct nxctl * nxctl)171 nxctl_traffic_rule_clean(struct nxctl *nxctl)
172 {
173 struct nxctl_traffic_rule_storage *rs;
174 struct nxctl_traffic_rule *ntr, *next;
175 int err;
176
177 lck_mtx_lock(&nxctl->nxctl_lock);
178 if ((rs = nxctl->nxctl_traffic_rule_storage) == NULL) {
179 lck_mtx_unlock(&nxctl->nxctl_lock);
180 return;
181 }
182 ntr = SLIST_FIRST(&rs->rs_list);
183 SLIST_INIT(&rs->rs_list);
184 rs->rs_count = 0;
185 nxctl_traffic_rule_storage_destroy(rs);
186 nxctl->nxctl_traffic_rule_storage = NULL;
187 lck_mtx_unlock(&nxctl->nxctl_lock);
188
189 while (ntr != NULL) {
190 next = SLIST_NEXT(ntr, ntr_storage_link);
191 /*
192 * Clearing the flag to tell remove_traffic_rule() not to
193 * remove from the nxctl list again.
194 */
195 ntr->ntr_flags &= ~NTR_FLAG_ON_NXCTL_LIST;
196
197 /* Passing NULL because we already hold a reference */
198 err = remove_traffic_rule(nxctl, ntr->ntr_uuid, NULL);
199 if (err == 0) {
200 (void) notify_traffic_rule(ntr, NTR_NOTIFY_FLAG_REMOVE);
201 }
202 release_traffic_rule(ntr);
203 ntr = next;
204 }
205 }
206
207 SK_NO_INLINE_ATTRIBUTE
208 static void
add_traffic_rule_to_nxctl(struct nxctl * nxctl,struct nxctl_traffic_rule * ntr)209 add_traffic_rule_to_nxctl(struct nxctl *nxctl, struct nxctl_traffic_rule *ntr)
210 {
211 struct nxctl_traffic_rule_storage *rs;
212
213 lck_mtx_lock(&nxctl->nxctl_lock);
214 if ((rs = nxctl->nxctl_traffic_rule_storage) == NULL) {
215 rs = nxctl_traffic_rule_storage_create();
216 nxctl->nxctl_traffic_rule_storage = rs;
217 }
218 ntr->ntr_flags |= NTR_FLAG_ON_NXCTL_LIST;
219 retain_traffic_rule(ntr);
220 SLIST_INSERT_HEAD(&rs->rs_list, ntr, ntr_storage_link);
221 rs->rs_count++;
222 lck_mtx_unlock(&nxctl->nxctl_lock);
223 }
224
225 SK_NO_INLINE_ATTRIBUTE
226 static void
remove_traffic_rule_from_nxctl(struct nxctl * nxctl,struct nxctl_traffic_rule * ntr)227 remove_traffic_rule_from_nxctl(struct nxctl *nxctl,
228 struct nxctl_traffic_rule *ntr)
229 {
230 struct nxctl_traffic_rule_storage *rs;
231
232 lck_mtx_lock(&nxctl->nxctl_lock);
233 if ((ntr->ntr_flags & NTR_FLAG_ON_NXCTL_LIST) == 0) {
234 lck_mtx_unlock(&nxctl->nxctl_lock);
235 return;
236 }
237 rs = nxctl->nxctl_traffic_rule_storage;
238 SLIST_REMOVE(&rs->rs_list, ntr, nxctl_traffic_rule, ntr_storage_link);
239 rs->rs_count--;
240 ntr->ntr_flags &= ~NTR_FLAG_ON_NXCTL_LIST;
241 release_traffic_rule(ntr);
242 if (rs->rs_count == 0) {
243 nxctl_traffic_rule_storage_destroy(rs);
244 nxctl->nxctl_traffic_rule_storage = NULL;
245 }
246 lck_mtx_unlock(&nxctl->nxctl_lock);
247 }
248
249 SK_NO_INLINE_ATTRIBUTE
250 void
retain_traffic_rule(struct nxctl_traffic_rule * ntr)251 retain_traffic_rule(struct nxctl_traffic_rule *ntr)
252 {
253 #if (DEVELOPMENT || DEBUG)
254 os_ref_count_t count = os_ref_get_count(&ntr->ntr_refcnt);
255 DTRACE_SKYWALK2(ntr__retain, struct nxctl_traffic_rule *, ntr,
256 os_ref_count_t, count);
257 #endif
258 os_ref_retain(&ntr->ntr_refcnt);
259 }
260
261 SK_NO_INLINE_ATTRIBUTE
262 void
release_traffic_rule(struct nxctl_traffic_rule * ntr)263 release_traffic_rule(struct nxctl_traffic_rule *ntr)
264 {
265 #if (DEVELOPMENT || DEBUG)
266 os_ref_count_t count = os_ref_get_count(&ntr->ntr_refcnt);
267 DTRACE_SKYWALK2(ntr__release, struct nxctl_traffic_rule *, ntr,
268 os_ref_count_t, count);
269 #endif
270 if (os_ref_release(&ntr->ntr_refcnt) == 0) {
271 struct nxctl_traffic_rule_type *type;
272
273 type = find_traffic_rule_type(ntr->ntrt_type);
274 ASSERT(type);
275
276 type->ntrt_destroy(ntr);
277 }
278 }
279
280 SK_NO_INLINE_ATTRIBUTE
281 static int
notify_traffic_rule(struct nxctl_traffic_rule * ntr,uint32_t flags)282 notify_traffic_rule(struct nxctl_traffic_rule *ntr, uint32_t flags)
283 {
284 struct nxctl_traffic_rule_type *type;
285
286 type = find_traffic_rule_type(ntr->ntrt_type);
287 ASSERT(type);
288
289 return type->ntrt_notify(ntr, flags);
290 }
291
292 static void
link_traffic_rule(struct nxctl * nxctl,struct nxctl_traffic_rule * ntr)293 link_traffic_rule(struct nxctl *nxctl, struct nxctl_traffic_rule *ntr)
294 {
295 /*
296 * The persist flag means: do not clean up rule upon nxctl fd close.
297 * This means we only add the rule to the nxctl list if persist
298 * is not set.
299 */
300 if ((ntr->ntr_flags & NTR_FLAG_PERSIST) == 0) {
301 add_traffic_rule_to_nxctl(nxctl, ntr);
302 }
303
304 struct nxctl_traffic_rule_type *type;
305
306 type = find_traffic_rule_type(ntr->ntrt_type);
307 ASSERT(type);
308
309 type->ntrt_link(ntr);
310 }
311
312 static void
unlink_traffic_rule(struct nxctl * nxctl,struct nxctl_traffic_rule * ntr)313 unlink_traffic_rule(struct nxctl *nxctl, struct nxctl_traffic_rule *ntr)
314 {
315 if ((ntr->ntr_flags & NTR_FLAG_PERSIST) == 0) {
316 remove_traffic_rule_from_nxctl(nxctl, ntr);
317 }
318
319 struct nxctl_traffic_rule_type *type;
320
321 type = find_traffic_rule_type(ntr->ntrt_type);
322 ASSERT(type);
323
324 type->ntrt_unlink(ntr);
325 }
326
327 static int
find_traffic_rule_by_uuid(uuid_t uuid,struct nxctl_traffic_rule ** ntrp)328 find_traffic_rule_by_uuid(uuid_t uuid, struct nxctl_traffic_rule **ntrp)
329 {
330 int i, err;
331 struct nxctl_traffic_rule_type *ntrt;
332 struct nxctl_traffic_rule *__single ntr = NULL;
333
334 for (i = 0; i < NRULETYPES; i++) {
335 ntrt = &nxctl_rule_types[i];
336 err = ntrt->ntrt_find_by_uuid(uuid, &ntr);
337 if (err == 0) {
338 ASSERT(ntr != NULL);
339 *ntrp = ntr;
340 return 0;
341 }
342 }
343 return ENOENT;
344 }
345
346 static struct nxctl_traffic_rule_type *
find_traffic_rule_type(uint8_t type)347 find_traffic_rule_type(uint8_t type)
348 {
349 int i;
350 struct nxctl_traffic_rule_type *ntrt;
351
352 for (i = 0; i < NRULETYPES; i++) {
353 ntrt = &nxctl_rule_types[i];
354 if (ntrt->ntrt_type == type) {
355 return ntrt;
356 }
357 }
358 return NULL;
359 }
360
361 SK_NO_INLINE_ATTRIBUTE
362 static int
add_traffic_rule(struct nxctl * nxctl,const char * ifname,struct ifnet_traffic_descriptor_common * td,struct ifnet_traffic_rule_action * ra,uint32_t flags,struct nxctl_traffic_rule ** ntrp)363 add_traffic_rule(struct nxctl *nxctl, const char *ifname,
364 struct ifnet_traffic_descriptor_common *td,
365 struct ifnet_traffic_rule_action *ra,
366 uint32_t flags,
367 struct nxctl_traffic_rule **ntrp)
368 {
369 struct nxctl_traffic_rule_type *type = NULL;
370 struct nxctl_traffic_rule *__single ntr = NULL;
371 int err;
372
373 NXTR_WLOCK();
374 type = find_traffic_rule_type(td->itd_type);
375 if (type == NULL) {
376 SK_ERR("rule type %x not found", td->itd_type);
377 err = EINVAL;
378 goto fail;
379 }
380 for (int i = 0; i < NRULETYPES; i++) {
381 if (&nxctl_rule_types[i] != type) {
382 uint32_t count = 0;
383 err = nxctl_rule_types[i].ntrt_get_count(ifname, &count);
384 if (!(err == ENOENT || (err == 0 && count == 0))) {
385 SK_ERR("other types of rules are added to the same ifname");
386 err = EINVAL;
387 goto fail;
388 }
389 }
390 }
391 err = type->ntrt_validate(ifname, td, ra);
392 if (err != 0) {
393 SK_ERR("rule validate failed: %d", err);
394 goto fail;
395 }
396 err = type->ntrt_find(ifname, td, NTR_FIND_FLAG_EXACT, &ntr);
397 if (err == 0) {
398 SK_ERR("rule already exists");
399 ASSERT(ntr != NULL);
400 err = EEXIST;
401 goto fail;
402 } else if (err != ENOENT) {
403 SK_ERR("rule find failed: %d", err);
404 goto fail;
405 }
406 err = type->ntrt_create(ifname, td, ra, flags, &ntr);
407 if (err != 0) {
408 SK_ERR("rule create failed: %d", err);
409 goto fail;
410 }
411 link_traffic_rule(nxctl, ntr);
412 if (ntrp != NULL) {
413 retain_traffic_rule(ntr);
414 *ntrp = ntr;
415 }
416 NXTR_WUNLOCK();
417 return 0;
418 fail:
419 NXTR_WUNLOCK();
420 return err;
421 }
422
423
424 SK_NO_INLINE_ATTRIBUTE
425 static int
remove_traffic_rule(struct nxctl * nxctl,uuid_t uuid,struct nxctl_traffic_rule ** ntrp)426 remove_traffic_rule(struct nxctl *nxctl, uuid_t uuid,
427 struct nxctl_traffic_rule **ntrp)
428 {
429 struct nxctl_traffic_rule *__single ntr;
430 int err;
431
432 NXTR_WLOCK();
433 err = find_traffic_rule_by_uuid(uuid, &ntr);
434 if (err != 0) {
435 SK_ERR("traffic rule not found");
436 NXTR_WUNLOCK();
437 return err;
438 }
439 if (ntrp != NULL) {
440 retain_traffic_rule(ntr);
441 *ntrp = ntr;
442 }
443 unlink_traffic_rule(nxctl, ntr);
444 /* release initial reference */
445 release_traffic_rule(ntr);
446 NXTR_WUNLOCK();
447 return 0;
448 }
449
450 static uint32_t
convert_traffic_rule_ioc_flags(uint32_t flags)451 convert_traffic_rule_ioc_flags(uint32_t flags)
452 {
453 uint32_t f = 0;
454
455 if ((flags & NXIOC_ADD_TRAFFIC_RULE_FLAG_PERSIST) != 0) {
456 f |= NTR_FLAG_PERSIST;
457 }
458 return f;
459 }
460
461 SK_NO_INLINE_ATTRIBUTE
462 static int
add_traffic_rule_generic(struct nxctl * nxctl,const char * ifname,struct ifnet_traffic_descriptor_common * td,struct ifnet_traffic_rule_action * ra,uint32_t flags,uuid_t * uuid)463 add_traffic_rule_generic(struct nxctl *nxctl, const char *ifname,
464 struct ifnet_traffic_descriptor_common *td,
465 struct ifnet_traffic_rule_action *ra, uint32_t flags, uuid_t *uuid)
466 {
467 struct nxctl_traffic_rule *__single ntr;
468 int err;
469
470 err = add_traffic_rule(nxctl, ifname, td, ra, flags, &ntr);
471 if (err != 0) {
472 return err;
473 }
474 (void) notify_traffic_rule(ntr, NTR_NOTIFY_FLAG_ADD);
475 uuid_copy(*uuid, ntr->ntr_uuid);
476 release_traffic_rule(ntr);
477 return 0;
478 }
479
480 int
nxioctl_add_traffic_rule_inet(struct nxctl * nxctl,caddr_t data,proc_t procp)481 nxioctl_add_traffic_rule_inet(struct nxctl *nxctl, caddr_t data, proc_t procp)
482 {
483 #pragma unused(procp)
484 struct nxctl_add_traffic_rule_inet_iocargs *args =
485 (struct nxctl_add_traffic_rule_inet_iocargs *)(void *)data;
486 char *__null_terminated atri_ifname = NULL;
487
488 atri_ifname = __unsafe_null_terminated_from_indexable(args->atri_ifname);
489
490 return add_traffic_rule_generic(nxctl, atri_ifname,
491 &args->atri_td.inet_common,
492 &args->atri_ra.ras_common,
493 convert_traffic_rule_ioc_flags(args->atri_flags),
494 &args->atri_uuid);
495 }
496
497 int
nxioctl_add_traffic_rule_eth(struct nxctl * nxctl,caddr_t data,proc_t procp)498 nxioctl_add_traffic_rule_eth(struct nxctl *nxctl, caddr_t data, proc_t procp)
499 {
500 #pragma unused(procp)
501 struct nxctl_add_traffic_rule_eth_iocargs *args =
502 (struct nxctl_add_traffic_rule_eth_iocargs *)(void *)data;
503 char *__null_terminated atre_ifname = NULL;
504
505 atre_ifname = __unsafe_null_terminated_from_indexable(args->atre_ifname);
506
507 return add_traffic_rule_generic(nxctl, atre_ifname,
508 &args->atre_td.eth_common,
509 &args->atre_ra.ras_common,
510 convert_traffic_rule_ioc_flags(args->atre_flags),
511 &args->atre_uuid);
512 }
513
514 int
nxioctl_remove_traffic_rule(struct nxctl * nxctl,caddr_t data,proc_t procp)515 nxioctl_remove_traffic_rule(struct nxctl *nxctl, caddr_t data, proc_t procp)
516 {
517 #pragma unused(procp)
518 struct nxctl_remove_traffic_rule_iocargs *args =
519 (struct nxctl_remove_traffic_rule_iocargs *)(void *)data;
520 struct nxctl_traffic_rule *__single ntr;
521 int err;
522
523 err = remove_traffic_rule(nxctl, args->rtr_uuid, &ntr);
524 if (err != 0) {
525 return err;
526 }
527 (void) notify_traffic_rule(ntr, NTR_NOTIFY_FLAG_REMOVE);
528 release_traffic_rule(ntr);
529 return 0;
530 }
531
532 int
nxioctl_get_traffic_rules(struct nxctl * nxctl,caddr_t data,proc_t procp)533 nxioctl_get_traffic_rules(struct nxctl *nxctl, caddr_t data, proc_t procp)
534 {
535 #pragma unused(nxctl)
536 struct nxctl_get_traffic_rules_iocargs *args =
537 (struct nxctl_get_traffic_rules_iocargs *)(void *)data;
538 struct nxctl_traffic_rule_type *type;
539 user_addr_t uaddr;
540 int err;
541
542 NXTR_RLOCK();
543 type = find_traffic_rule_type(args->gtr_type);
544 if (type == NULL) {
545 SK_ERR("rule type %x not found", args->gtr_type);
546 err = EINVAL;
547 goto fail;
548 }
549 uaddr = proc_is64bit(procp) ? args->gtr_buf64 :
550 CAST_USER_ADDR_T(args->gtr_buf);
551 err = type->ntrt_get_all(args->gtr_size, &args->gtr_count, uaddr);
552 if (err != 0) {
553 goto fail;
554 }
555 NXTR_RUNLOCK();
556 return 0;
557 fail:
558 NXTR_RUNLOCK();
559 return err;
560 }
561