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, ð_type, ð_raddr);
173 if (err != 0) {
174 return err;
175 }
176 return nxctl_eth_traffic_rule_find_qset_id(ifname, eth_type, ð_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