xref: /xnu-12377.1.9/bsd/skywalk/nexus/nexus_traffic_rule.c (revision f6217f891ac0bb64f3d375211650a4c1ff8ca1ea)
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