xref: /xnu-12377.1.9/bsd/skywalk/nexus/nexus_traffic_rule_eth.c (revision f6217f891ac0bb64f3d375211650a4c1ff8ca1ea)
1 /*
2  * Copyright (c) 2024 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 #include <skywalk/nexus/nexus_traffic_rule_eth.h>
29 #include <skywalk/nexus/netif/nx_netif.h>
30 #include <net/ethernet.h>
31 
32 /*
33  * Eth-specific traffic rule.
34  */
35 struct nxctl_traffic_rule_eth {
36 	struct nxctl_traffic_rule ntre_common;
37 	SLIST_ENTRY(nxctl_traffic_rule_eth) ntre_storage_link;
38 	struct ifnet_traffic_descriptor_eth ntre_td;
39 	struct ifnet_traffic_rule_action_steer ntre_ra;
40 };
41 
42 /*
43  * Currently supported tuple types.
44  */
45 #define ETRM(type, raddr) \
46 	ITDBIT(type,  IFNET_TRAFFIC_DESCRIPTOR_ETH_MASK_ETHER_TYPE) | \
47 	ITDBIT(raddr, IFNET_TRAFFIC_DESCRIPTOR_ETH_MASK_RADDR)
48 
49 static uint8_t nxctl_eth_traffic_rule_masks[] = {
50 	ETRM(1, 0),
51 	ETRM(0, 1),
52 };
53 #define NETHRULEMASKS \
54     (sizeof(nxctl_eth_traffic_rule_masks)/sizeof(uint8_t))
55 
56 /* Per-interface lists of eth traffic rules */
57 SLIST_HEAD(nxctl_traffic_rule_eth_head, nxctl_traffic_rule_eth);
58 struct nxctl_traffic_rule_eth_if {
59 	char rei_ifname[IFNAMSIZ];
60 	struct nxctl_traffic_rule_eth_head rei_lists[NETHRULEMASKS];
61 	uint32_t rei_count;
62 	SLIST_ENTRY(nxctl_traffic_rule_eth_if) rei_link;
63 };
64 
65 /* List of per-interface lists */
66 SLIST_HEAD(nxctl_traffic_rule_eth_if_head, nxctl_traffic_rule_eth_if);
67 struct nxctl_traffic_rule_eth_storage {
68 	struct nxctl_traffic_rule_eth_if_head res_if_list;
69 	uint32_t res_count;
70 };
71 
72 static struct nxctl_traffic_rule_eth_storage *rs = NULL;
73 static kern_allocation_name_t nxctl_traffic_rule_tag = NULL;
74 
75 /*
76  * If an interface attaches after rule(s) are added, this function is used
77  * retrieve the current rule count for that interface.
78  */
79 int
nxctl_eth_traffic_rule_get_count(const char * ifname,uint32_t * count)80 nxctl_eth_traffic_rule_get_count(const char *ifname, uint32_t *count)
81 {
82 	int err;
83 
84 	NXTR_RLOCK();
85 	err = eth_traffic_rule_get_count(ifname, count);
86 	NXTR_RUNLOCK();
87 
88 	return err;
89 }
90 
91 /*
92  * Used for finding the qset id associated with a ether type and ether remote addr.
93  */
94 int
nxctl_eth_traffic_rule_find_qset_id(const char * ifname,uint16_t eth_type,ether_addr_t * eth_raddr,uint64_t * qset_id)95 nxctl_eth_traffic_rule_find_qset_id(const char *ifname,
96     uint16_t eth_type, ether_addr_t *eth_raddr, uint64_t *qset_id)
97 {
98 	struct nxctl_traffic_rule_eth *__single ntre = NULL;
99 	struct nxctl_traffic_rule *__single ntr = NULL;
100 	struct ifnet_traffic_descriptor_eth td = {0};
101 	int err;
102 
103 	td.eth_common.itd_type = IFNET_TRAFFIC_DESCRIPTOR_TYPE_ETH;
104 	td.eth_common.itd_len = sizeof(td);
105 	td.eth_common.itd_flags = IFNET_TRAFFIC_DESCRIPTOR_FLAG_INBOUND |
106 	    IFNET_TRAFFIC_DESCRIPTOR_FLAG_OUTBOUND;
107 
108 	td.eth_type = eth_type;
109 	td.eth_mask = IFNET_TRAFFIC_DESCRIPTOR_ETH_MASK_ETHER_TYPE;
110 
111 	if (eth_raddr) {
112 		bcopy(eth_raddr, &td.eth_raddr, ETHER_ADDR_LEN);
113 		td.eth_mask |= IFNET_TRAFFIC_DESCRIPTOR_ETH_MASK_RADDR;
114 	}
115 
116 	NXTR_RLOCK();
117 	err = eth_traffic_rule_find(ifname, &td.eth_common, 0, &ntr);
118 	if (err != 0) {
119 		goto fail;
120 	}
121 	ntre = __container_of(ntr, struct nxctl_traffic_rule_eth, ntre_common);
122 	*qset_id = ntre->ntre_ra.ras_qset_id;
123 	NXTR_RUNLOCK();
124 	return 0;
125 fail:
126 	NXTR_RUNLOCK();
127 	return err;
128 }
129 
130 static int
parse_eth_hdr(struct __kern_packet * pkt,uint16_t * eth_type,ether_addr_t * eth_raddr)131 parse_eth_hdr(struct __kern_packet *pkt, uint16_t *eth_type, ether_addr_t *eth_raddr)
132 {
133 	volatile ether_header_t *_l2 = NULL;
134 	uint8_t *pkt_buf, *l2_hdr;
135 	uint32_t bdlen, bdlim, bdoff, cls_len;
136 	int err;
137 
138 	ASSERT(pkt->pkt_l2_len <= pkt->pkt_length);
139 
140 	MD_BUFLET_ADDR_ABS_DLEN(pkt, pkt_buf, bdlen, bdlim, bdoff);
141 	cls_len = bdlim - bdoff;
142 	cls_len = (uint32_t)MIN(cls_len, pkt->pkt_length);
143 	VERIFY(pkt->pkt_length >= cls_len);
144 	if (cls_len == 0) {
145 		SK_ERR("cls_len == 0");
146 		err = EINVAL;
147 		goto fail;
148 	}
149 
150 	l2_hdr = pkt_buf + pkt->pkt_headroom;
151 	_l2 = (volatile ether_header_t *)(void *)l2_hdr;
152 
153 	*eth_type = ntohs(_l2->ether_type);
154 	bcopy(__DECONST(void *, &_l2->ether_dhost), eth_raddr, ETHER_ADDR_LEN);
155 
156 	return 0;
157 
158 fail:
159 	DTRACE_SKYWALK4(classify__failed, ether_header_t *, _l2, size_t, pkt->pkt_length,
160 	    uint8_t, pkt->pkt_l2_len, int, err);
161 	return err;
162 }
163 
164 int
nxctl_eth_traffic_rule_find_qset_id_with_pkt(const char * ifname,struct __kern_packet * pkt,uint64_t * qset_id)165 nxctl_eth_traffic_rule_find_qset_id_with_pkt(const char *ifname,
166     struct __kern_packet *pkt, uint64_t *qset_id)
167 {
168 	ether_addr_t eth_raddr;
169 	uint16_t eth_type;
170 	int err;
171 
172 	err = parse_eth_hdr(pkt, &eth_type, &eth_raddr);
173 	if (err != 0) {
174 		return err;
175 	}
176 	return nxctl_eth_traffic_rule_find_qset_id(ifname, eth_type, &eth_raddr, qset_id);
177 }
178 
179 void
eth_traffic_rule_init(kern_allocation_name_t rule_tag)180 eth_traffic_rule_init(kern_allocation_name_t rule_tag)
181 {
182 	ASSERT(nxctl_traffic_rule_tag == NULL);
183 	nxctl_traffic_rule_tag = rule_tag;
184 }
185 
186 int
eth_traffic_rule_validate(const char * ifname,struct ifnet_traffic_descriptor_common * td,struct ifnet_traffic_rule_action * ra)187 eth_traffic_rule_validate(
188 	const char *ifname,
189 	struct ifnet_traffic_descriptor_common *td,
190 	struct ifnet_traffic_rule_action *ra)
191 {
192 	char buf[IFNAMSIZ];
193 	int unit, i;
194 	struct ifnet_traffic_descriptor_eth *tdi;
195 
196 	if (ifunit_extract(ifname, buf, sizeof(buf), &unit) < 0) {
197 		SK_ERR("invalid ifname: %s", ifname);
198 		return EINVAL;
199 	}
200 	if (td->itd_len != sizeof(*tdi)) {
201 		SK_ERR("invalid td len: expected %lu, actual %d",
202 		    sizeof(*tdi), td->itd_len);
203 		return EINVAL;
204 	}
205 	if (td->itd_flags == 0 ||
206 	    (td->itd_flags &
207 	    ~(IFNET_TRAFFIC_DESCRIPTOR_FLAG_INBOUND |
208 	    IFNET_TRAFFIC_DESCRIPTOR_FLAG_OUTBOUND)) != 0) {
209 		SK_ERR("invalid td flags: 0x%x", td->itd_flags);
210 		return EINVAL;
211 	}
212 	tdi = (struct ifnet_traffic_descriptor_eth *)td;
213 	for (i = 0; i < NETHRULEMASKS; i++) {
214 		if (tdi->eth_mask == nxctl_eth_traffic_rule_masks[i]) {
215 			break;
216 		}
217 	}
218 	if (i == NETHRULEMASKS) {
219 		SK_ERR("invalid eth mask: 0x%x", tdi->eth_mask);
220 		return EINVAL;
221 	}
222 	if ((tdi->eth_mask & IFNET_TRAFFIC_DESCRIPTOR_ETH_MASK_ETHER_TYPE) != 0) {
223 		if (tdi->eth_type != ETHERTYPE_PAE &&
224 		    tdi->eth_type != ETHERTYPE_WAI) {
225 			SK_ERR("invalid eth type 0x%x", tdi->eth_type);
226 			return EINVAL;
227 		}
228 	}
229 	if ((tdi->eth_mask & IFNET_TRAFFIC_DESCRIPTOR_ETH_MASK_RADDR) != 0) {
230 		ether_addr_t mac_zeros_addr = {0};
231 		if (!_ether_cmp(&tdi->eth_raddr, &mac_zeros_addr)) {
232 			SK_ERR("eth raddr cannot be unspecified");
233 			return EINVAL;
234 		}
235 	}
236 	if (ra->ra_len != sizeof(struct ifnet_traffic_rule_action_steer)) {
237 		SK_ERR("invalid ra len: expected %lu, actual %d",
238 		    sizeof(struct ifnet_traffic_rule_action_steer), ra->ra_len);
239 		return EINVAL;
240 	}
241 	return 0;
242 }
243 
244 SK_NO_INLINE_ATTRIBUTE
245 static void
eth_traffic_rule_storage_create(void)246 eth_traffic_rule_storage_create(void)
247 {
248 	rs = sk_alloc_type(struct nxctl_traffic_rule_eth_storage,
249 	    Z_WAITOK | Z_NOFAIL, nxctl_traffic_rule_tag);
250 	SLIST_INIT(&rs->res_if_list);
251 	rs->res_count = 0;
252 	return;
253 }
254 
255 SK_NO_INLINE_ATTRIBUTE
256 static void
eth_traffic_rule_storage_destroy(void)257 eth_traffic_rule_storage_destroy(void)
258 {
259 	ASSERT(rs->res_count == 0);
260 	ASSERT(SLIST_EMPTY(&rs->res_if_list));
261 	sk_free_type(struct nxctl_traffic_rule_eth_storage, rs);
262 }
263 
264 SK_NO_INLINE_ATTRIBUTE
265 static struct nxctl_traffic_rule_eth_if *
eth_traffic_rule_if_create(const char * ifname)266 eth_traffic_rule_if_create(const char *ifname)
267 {
268 	struct nxctl_traffic_rule_eth_if *rif;
269 	int i;
270 
271 	rif = sk_alloc_type(struct nxctl_traffic_rule_eth_if,
272 	    Z_WAITOK | Z_NOFAIL, nxctl_traffic_rule_tag);
273 	for (i = 0; i < NETHRULEMASKS; i++) {
274 		SLIST_INIT(&rif->rei_lists[i]);
275 	}
276 	strlcpy(rif->rei_ifname, ifname, sizeof(rif->rei_ifname));
277 	rif->rei_count = 0;
278 	return rif;
279 }
280 
281 SK_NO_INLINE_ATTRIBUTE
282 static void
eth_traffic_rule_if_destroy(struct nxctl_traffic_rule_eth_if * rif)283 eth_traffic_rule_if_destroy(struct nxctl_traffic_rule_eth_if *rif)
284 {
285 	int i;
286 
287 	for (i = 0; i < NETHRULEMASKS; i++) {
288 		ASSERT(SLIST_EMPTY(&rif->rei_lists[i]));
289 	}
290 	ASSERT(rif->rei_count == 0);
291 	sk_free_type(struct nxctl_traffic_rule_eth_if, rif);
292 }
293 
294 SK_NO_INLINE_ATTRIBUTE
295 static boolean_t
eth_traffic_rule_match(struct nxctl_traffic_rule_eth * ntre,const char * ifname,uint32_t flags,struct ifnet_traffic_descriptor_eth * tdi)296 eth_traffic_rule_match(struct nxctl_traffic_rule_eth *ntre, const char *ifname,
297     uint32_t flags, struct ifnet_traffic_descriptor_eth *tdi)
298 {
299 	struct nxctl_traffic_rule *ntr = (struct nxctl_traffic_rule *)ntre;
300 	struct ifnet_traffic_descriptor_eth *tdi0;
301 	uint8_t mask;
302 	boolean_t exact;
303 
304 	VERIFY(strlcmp(ntr->ntr_ifname, ifname, sizeof(ntr->ntr_ifname)) == 0);
305 	tdi0 = &ntre->ntre_td;
306 
307 	exact = ((flags & NTR_FIND_FLAG_EXACT) != 0);
308 	mask = tdi0->eth_mask & tdi->eth_mask;
309 	if (exact) {
310 		ASSERT(tdi0->eth_mask == tdi->eth_mask);
311 	}
312 	if ((mask & IFNET_TRAFFIC_DESCRIPTOR_ETH_MASK_ETHER_TYPE) != 0 &&
313 	    tdi0->eth_type != tdi->eth_type) {
314 		DTRACE_SKYWALK2(eth_type__mismatch,
315 		    uint8_t, tdi0->eth_type, uint8_t, tdi->eth_type);
316 		return FALSE;
317 	}
318 	if ((mask & IFNET_TRAFFIC_DESCRIPTOR_ETH_MASK_RADDR) != 0 &&
319 	    _ether_cmp(&tdi0->eth_raddr, &tdi->eth_raddr)) {
320 		DTRACE_SKYWALK2(eth_raddr__mismatch,
321 		    ether_addr_t *, &tdi0->eth_raddr,
322 		    ether_addr_t *, &tdi->eth_raddr);
323 		return FALSE;
324 	}
325 	return TRUE;
326 }
327 
328 int
eth_traffic_rule_find(const char * ifname,struct ifnet_traffic_descriptor_common * td,uint32_t flags,struct nxctl_traffic_rule ** ntrp)329 eth_traffic_rule_find(const char *ifname,
330     struct ifnet_traffic_descriptor_common *td, uint32_t flags,
331     struct nxctl_traffic_rule **ntrp)
332 {
333 	struct nxctl_traffic_rule_eth *ntre = NULL;
334 	struct nxctl_traffic_rule_eth_if *rif;
335 	struct ifnet_traffic_descriptor_eth *tdi =
336 	    (struct ifnet_traffic_descriptor_eth *)td;
337 	int i;
338 
339 	if (rs == NULL) {
340 		return ENOENT;
341 	}
342 	SLIST_FOREACH(rif, &rs->res_if_list, rei_link) {
343 		if (strlcmp(rif->rei_ifname, ifname, sizeof(rif->rei_ifname)) != 0) {
344 			continue;
345 		}
346 		for (i = 0; i < NETHRULEMASKS; i++) {
347 			if ((flags & NTR_FIND_FLAG_EXACT) != 0 &&
348 			    tdi->eth_mask != nxctl_eth_traffic_rule_masks[i]) {
349 				continue;
350 			}
351 			SLIST_FOREACH(ntre, &rif->rei_lists[i], ntre_storage_link) {
352 				if (eth_traffic_rule_match(ntre, ifname, flags, tdi)) {
353 					*ntrp = (struct nxctl_traffic_rule *)ntre;
354 					return 0;
355 				}
356 			}
357 		}
358 	}
359 	return ENOENT;
360 }
361 
362 int
eth_traffic_rule_find_by_uuid(uuid_t uuid,struct nxctl_traffic_rule ** ntrp)363 eth_traffic_rule_find_by_uuid(
364 	uuid_t uuid, struct nxctl_traffic_rule **ntrp)
365 {
366 	struct nxctl_traffic_rule_eth *ntre;
367 	struct nxctl_traffic_rule *ntr;
368 	struct nxctl_traffic_rule_eth_if *rif;
369 	int i;
370 
371 	if (rs == NULL) {
372 		return ENOENT;
373 	}
374 	SLIST_FOREACH(rif, &rs->res_if_list, rei_link) {
375 		for (i = 0; i < NETHRULEMASKS; i++) {
376 			SLIST_FOREACH(ntre, &rif->rei_lists[i], ntre_storage_link) {
377 				ntr = &ntre->ntre_common;
378 				if (uuid_compare(ntr->ntr_uuid, uuid) == 0) {
379 					*ntrp = ntr;
380 					return 0;
381 				}
382 			}
383 		}
384 	}
385 	return ENOENT;
386 }
387 
388 static void
eth_update_ifnet_traffic_rule_count(const char * ifname,uint32_t count)389 eth_update_ifnet_traffic_rule_count(const char *ifname, uint32_t count)
390 {
391 	struct ifnet *ifp;
392 
393 	ifp = ifunit_ref(ifname);
394 	if (ifp == NULL) {
395 		DTRACE_SKYWALK1(ifname__not__found, char *, ifname);
396 		return;
397 	}
398 	ifnet_update_eth_traffic_rule_count(ifp, count);
399 	ifnet_decr_iorefcnt(ifp);
400 }
401 
402 void
eth_traffic_rule_link(struct nxctl_traffic_rule * ntr)403 eth_traffic_rule_link(struct nxctl_traffic_rule *ntr)
404 {
405 	struct nxctl_traffic_rule_eth_if *rif;
406 	struct nxctl_traffic_rule_eth *ntre =
407 	    (struct nxctl_traffic_rule_eth *)ntr;
408 	struct nxctl_traffic_rule_eth_head *list = NULL;
409 	int i;
410 	char *__null_terminated ntr_ifname = NULL;
411 	char *__null_terminated rei_ifname = NULL;
412 
413 	if (rs == NULL) {
414 		eth_traffic_rule_storage_create();
415 	}
416 	SLIST_FOREACH(rif, &rs->res_if_list, rei_link) {
417 		if (strbufcmp(rif->rei_ifname, ntr->ntr_ifname) == 0) {
418 			break;
419 		}
420 	}
421 	if (rif == NULL) {
422 		ntr_ifname = __unsafe_null_terminated_from_indexable(ntr->ntr_ifname);
423 		rif = eth_traffic_rule_if_create(ntr_ifname);
424 		SLIST_INSERT_HEAD(&rs->res_if_list, rif, rei_link);
425 	}
426 	for (i = 0; i < NETHRULEMASKS; i++) {
427 		if (ntre->ntre_td.eth_mask ==
428 		    nxctl_eth_traffic_rule_masks[i]) {
429 			list = &rif->rei_lists[i];
430 			break;
431 		}
432 	}
433 	retain_traffic_rule(ntr);
434 	ASSERT(list != NULL);
435 	SLIST_INSERT_HEAD(list, ntre, ntre_storage_link);
436 	/* per-interface count */
437 	rif->rei_count++;
438 	rei_ifname = __unsafe_null_terminated_from_indexable(rif->rei_ifname);
439 	eth_update_ifnet_traffic_rule_count(rei_ifname, rif->rei_count);
440 
441 	/* global count */
442 	rs->res_count++;
443 }
444 
445 void
eth_traffic_rule_unlink(struct nxctl_traffic_rule * ntr)446 eth_traffic_rule_unlink(struct nxctl_traffic_rule *ntr)
447 {
448 	struct nxctl_traffic_rule_eth_if *rif;
449 	struct nxctl_traffic_rule_eth *ntre =
450 	    (struct nxctl_traffic_rule_eth *)ntr;
451 	struct nxctl_traffic_rule_eth_head *list = NULL;
452 	int i;
453 	char *__null_terminated rei_ifname = NULL;
454 
455 	ASSERT(rs != NULL);
456 	SLIST_FOREACH(rif, &rs->res_if_list, rei_link) {
457 		if (strbufcmp(rif->rei_ifname, ntr->ntr_ifname) == 0) {
458 			break;
459 		}
460 	}
461 	ASSERT(rif != NULL);
462 	for (i = 0; i < NETHRULEMASKS; i++) {
463 		if (ntre->ntre_td.eth_mask ==
464 		    nxctl_eth_traffic_rule_masks[i]) {
465 			list = &rif->rei_lists[i];
466 			break;
467 		}
468 	}
469 	ASSERT(list != NULL);
470 	SLIST_REMOVE(list, ntre, nxctl_traffic_rule_eth, ntre_storage_link);
471 	rif->rei_count--;
472 	rei_ifname = __unsafe_null_terminated_from_indexable(rif->rei_ifname);
473 	eth_update_ifnet_traffic_rule_count(rei_ifname, rif->rei_count);
474 
475 	rs->res_count--;
476 	release_traffic_rule(ntr);
477 
478 	if (rif->rei_count == 0) {
479 		SLIST_REMOVE(&rs->res_if_list, rif, nxctl_traffic_rule_eth_if, rei_link);
480 		eth_traffic_rule_if_destroy(rif);
481 	}
482 	if (rs->res_count == 0) {
483 		eth_traffic_rule_storage_destroy();
484 		rs = NULL;
485 	}
486 }
487 
488 /*
489  * XXX
490  * This may need additional changes to ensure safety against detach/attach.
491  */
492 int
eth_traffic_rule_notify(struct nxctl_traffic_rule * ntr,uint32_t flags)493 eth_traffic_rule_notify(struct nxctl_traffic_rule *ntr, uint32_t flags)
494 {
495 	struct ifnet *ifp;
496 	struct nx_netif *nif;
497 	struct netif_qset *__single qset = NULL;
498 	struct nxctl_traffic_rule_eth *ntre;
499 	int err = 0;
500 	char *__null_terminated ntr_ifname = NULL;
501 
502 	ntr_ifname = __unsafe_null_terminated_from_indexable(ntr->ntr_ifname);
503 	ifp = ifunit_ref(ntr_ifname);
504 	if (ifp == NULL) {
505 		DTRACE_SKYWALK1(ifname__not__found, char *, ntr->ntr_ifname);
506 		err = ENXIO;
507 		goto done;
508 	}
509 	nif = NA(ifp)->nifna_netif;
510 	if (!NX_LLINK_PROV(nif->nif_nx)) {
511 		DTRACE_SKYWALK1(llink__not__enabled, struct ifnet *, ifp);
512 		err = ENOTSUP;
513 		goto done;
514 	}
515 	ntre = (struct nxctl_traffic_rule_eth *)ntr;
516 	qset = nx_netif_find_qset(nif, ntre->ntre_ra.ras_qset_id);
517 	err = nx_netif_notify_steering_info(nif, qset,
518 	    (struct ifnet_traffic_descriptor_common *)&ntre->ntre_td,
519 	    ((flags & NTR_NOTIFY_FLAG_ADD) != 0));
520 done:
521 	if (qset != NULL) {
522 		nx_netif_qset_release(&qset);
523 	}
524 	if (ifp != NULL) {
525 		ifnet_decr_iorefcnt(ifp);
526 	}
527 	return err;
528 }
529 
530 int
eth_traffic_rule_get_count(const char * ifname,uint32_t * count)531 eth_traffic_rule_get_count(const char *ifname, uint32_t *count)
532 {
533 	struct nxctl_traffic_rule_eth_if *rif;
534 	int err;
535 
536 	if (rs == NULL) {
537 		err = ENOENT;
538 		goto fail;
539 	}
540 	SLIST_FOREACH(rif, &rs->res_if_list, rei_link) {
541 		if (strlcmp(rif->rei_ifname, ifname, sizeof(rif->rei_ifname)) == 0) {
542 			break;
543 		}
544 	}
545 	if (rif == NULL) {
546 		err = ENOENT;
547 		goto fail;
548 	}
549 	*count = rif->rei_count;
550 	return 0;
551 fail:
552 	return err;
553 }
554 
555 int
eth_traffic_rule_create(const char * ifname,struct ifnet_traffic_descriptor_common * td,struct ifnet_traffic_rule_action * ra,uint32_t flags,struct nxctl_traffic_rule ** ntrp)556 eth_traffic_rule_create(
557 	const char *ifname, struct ifnet_traffic_descriptor_common *td,
558 	struct ifnet_traffic_rule_action *ra, uint32_t flags,
559 	struct nxctl_traffic_rule **ntrp)
560 {
561 	struct nxctl_traffic_rule_eth *ntre;
562 	struct nxctl_traffic_rule *ntr;
563 	struct ifnet_traffic_descriptor_eth *tdi;
564 	struct ifnet_traffic_rule_action_steer *ras;
565 
566 	ntre = sk_alloc_type(struct nxctl_traffic_rule_eth,
567 	    Z_WAITOK | Z_NOFAIL, nxctl_traffic_rule_tag);
568 	ntr = &ntre->ntre_common;
569 
570 	ntr->ntrt_type = IFNET_TRAFFIC_DESCRIPTOR_TYPE_ETH;
571 	ntr->ntr_flags = flags;
572 	uuid_generate(ntr->ntr_uuid);
573 	os_ref_init(&ntr->ntr_refcnt, NULL);
574 
575 	strlcpy(ntr->ntr_ifname, ifname, sizeof(ntr->ntr_ifname));
576 	proc_selfname(ntr->ntr_procname, sizeof(ntr->ntr_procname));
577 
578 	tdi = __container_of(td, struct ifnet_traffic_descriptor_eth, eth_common);
579 	ras = __container_of(ra, struct ifnet_traffic_rule_action_steer, ras_common);
580 	bcopy(tdi, &ntre->ntre_td, sizeof(ntre->ntre_td));
581 	bcopy(ras, &ntre->ntre_ra, sizeof(ntre->ntre_ra));
582 
583 	*ntrp = ntr;
584 	return 0;
585 }
586 
587 void
eth_traffic_rule_destroy(struct nxctl_traffic_rule * ntr)588 eth_traffic_rule_destroy(struct nxctl_traffic_rule *ntr)
589 {
590 	struct nxctl_traffic_rule_eth *ntre;
591 
592 	ASSERT(os_ref_get_count(&ntr->ntr_refcnt) == 0);
593 	ntre = (struct nxctl_traffic_rule_eth *)ntr;
594 	sk_free_type(struct nxctl_traffic_rule_eth, ntre);
595 }
596 
597 static void
convert_ntre_to_iocinfo(struct nxctl_traffic_rule_eth * ntre,struct nxctl_traffic_rule_eth_iocinfo * info)598 convert_ntre_to_iocinfo(struct nxctl_traffic_rule_eth *ntre,
599     struct nxctl_traffic_rule_eth_iocinfo *info)
600 {
601 	struct nxctl_traffic_rule *ntr;
602 	struct nxctl_traffic_rule_generic_iocinfo *ginfo;
603 
604 	bzero(info, sizeof(*info));
605 	ntr = &ntre->ntre_common;
606 	ginfo = &info->tre_common;
607 	static_assert(sizeof(ntr->ntr_procname) == sizeof(ginfo->trg_procname));
608 	static_assert(sizeof(ntr->ntr_ifname) == sizeof(ginfo->trg_ifname));
609 	uuid_copy(ginfo->trg_uuid, ntr->ntr_uuid);
610 	strbufcpy(ginfo->trg_procname, ntr->ntr_procname);
611 	strbufcpy(ginfo->trg_ifname, ntr->ntr_ifname);
612 	bcopy(&ntre->ntre_td, &info->tre_td, sizeof(info->tre_td));
613 	bcopy(&ntre->ntre_ra, &info->tre_ra, sizeof(info->tre_ra));
614 }
615 
616 int
eth_traffic_rule_get_all(uint32_t size,uint32_t * count,user_addr_t uaddr)617 eth_traffic_rule_get_all(uint32_t size,
618     uint32_t *count, user_addr_t uaddr)
619 {
620 	struct nxctl_traffic_rule_eth *ntre = NULL;
621 	struct nxctl_traffic_rule_eth_if *rif;
622 	struct nxctl_traffic_rule_eth_iocinfo info;
623 	int i, err;
624 
625 	if (size != sizeof(info)) {
626 		SK_ERR("size: actual %d, expected %lu", size, sizeof(info));
627 		return EINVAL;
628 	}
629 	if (rs == NULL) {
630 		*count = 0;
631 		return 0;
632 	}
633 	if (*count < rs->res_count) {
634 		SK_ERR("count: given %d, require: %d", *count, rs->res_count);
635 		return ENOBUFS;
636 	}
637 	SLIST_FOREACH(rif, &rs->res_if_list, rei_link) {
638 		for (i = 0; i < NETHRULEMASKS; i++) {
639 			SLIST_FOREACH(ntre, &rif->rei_lists[i], ntre_storage_link) {
640 				convert_ntre_to_iocinfo(ntre, &info);
641 				err = copyout(&info, uaddr, sizeof(info));
642 				if (err != 0) {
643 					SK_ERR("copyout failed: %d", err);
644 					return err;
645 				}
646 				uaddr += sizeof(info);
647 			}
648 		}
649 	}
650 	*count = rs->res_count;
651 	return 0;
652 }
653