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/*
30 * if_redirect.c
31 * Virtual network interface that redirects traffic to a delegate interface.
32 */
33
34#include <sys/sysctl.h>
35#include <net/dlil.h>
36#include <net/ethernet.h>
37#include <net/kpi_interface.h>
38#include <net/bpf.h>
39#include <net/if_media.h>
40#include <net/if_ether.h>
41#include <net/if_redirect.h>
42#include <os/log.h>
43
44#include <skywalk/os_skywalk_private.h>
45#include <skywalk/nexus/netif/nx_netif.h>
46
47#define RD_NAME "rd"
48#define RD_MAXUNIT IF_MAXUNIT
49#define RD_ZONE_MAX_ELEM MIN(IFNETS_MAX, RD_MAXUNIT)
50#define RD_MAX_MTU 2048
51
52#define RD_MAX_TX_RINGS 1
53#define RD_MAX_RX_RINGS 1
54#define RD_POOL_SIZE 1024
55
56static uint8_t default_mac[ETHER_ADDR_LEN] = {0x0, 0x1, 0x2, 0x3, 0x4, 0x5};
57
58SYSCTL_DECL(_net_link);
59SYSCTL_NODE(_net_link, OID_AUTO, redirect, CTLFLAG_RW | CTLFLAG_LOCKED, 0,
60 "Redirect interface");
61
62static int if_redirect_debug = 0;
63SYSCTL_INT(_net_link_redirect, OID_AUTO, debug, CTLFLAG_RW | CTLFLAG_LOCKED,
64 &if_redirect_debug, 0, "Redirect interface debug logs");
65
66os_log_t redirect_log_handle = NULL;
67
68#define RDLOG(level, format, ...) do { \
69 if (level == LOG_ERR) { \
70 os_log_error(redirect_log_handle, "%s: " format "\n", \
71 __FUNCTION__, ##__VA_ARGS__); \
72 } else { \
73 if (__probable(if_redirect_debug == 0)) { \
74 break; \
75 } \
76 if (level == LOG_DEBUG) { \
77 os_log_debug(redirect_log_handle, "%s: " format "\n", \
78 __FUNCTION__, ##__VA_ARGS__); \
79 } else if (level == LOG_INFO) { \
80 os_log_info(redirect_log_handle, "%s: " format "\n", \
81 __FUNCTION__, ##__VA_ARGS__); \
82 } \
83 } \
84} while (0)
85
86#define RDLOG_ERR(format, ...) RDLOG(LOG_ERR, format, ##__VA_ARGS__)
87#define RDLOG_DBG(format, ...) RDLOG(LOG_DEBUG, format, ##__VA_ARGS__)
88#define RDLOG_INFO(format, ...) RDLOG(LOG_INFO, format, ##__VA_ARGS__)
89
90#define RD_MEDIA_LIST_MAX 27
91
92typedef struct {
93 uuid_t rnx_provider;
94 uuid_t rnx_instance;
95} redirect_nx, *redirect_nx_t;
96
97typedef struct {
98 char rd_name[IFNAMSIZ]; /* our unique id */
99 lck_mtx_t rd_lock;
100 uint32_t rd_ftype;
101 ifnet_t rd_ifp;
102 ifnet_t rd_delegate_ifp;
103
104 /* General state of the interface */
105 boolean_t rd_detaching;
106 boolean_t rd_connected;
107
108 /* Used for tracking delegate related state info */
109 boolean_t rd_self_ref;
110 boolean_t rd_delegate_parent_set;
111 boolean_t rd_delegate_ref;
112 boolean_t rd_fsw_rx_cb_set;
113 boolean_t rd_delegate_set;
114 boolean_t rd_mac_addr_set;
115 boolean_t rd_detach_notify_set;
116
117 unsigned int rd_max_mtu;
118 uint32_t rd_retain_count;
119 kern_pbufpool_t rd_pp;
120 kern_channel_ring_t rd_rx_ring[RD_MAX_RX_RINGS];
121 kern_channel_ring_t rd_tx_ring[RD_MAX_TX_RINGS];
122 redirect_nx rd_nx;
123 struct netif_stats *rd_nifs;
124 void *rd_intf_adv_kern_ctx;
125 thread_call_t rd_doorbell_tcall;
126 boolean_t rd_doorbell_tcall_active;
127 boolean_t rd_waiting_for_tcall;
128 bool rd_intf_adv_enabled;
129 kern_nexus_capab_interface_advisory_notify_fn_t rd_intf_adv_notify;
130} if_redirect, *if_redirect_t;
131
132static if_redirect_t ifnet_get_if_redirect(ifnet_t);
133static int redirect_clone_create(struct if_clone *, uint32_t, void *);
134static int redirect_clone_destroy(ifnet_t);
135static int redirect_ioctl(ifnet_t, u_long, void *);
136static void redirect_if_free(ifnet_t);
137static void redirect_free(if_redirect_t);
138static errno_t redirect_demux(ifnet_t, mbuf_t, char *, protocol_family_t *);
139static errno_t redirect_add_proto(ifnet_t, protocol_family_t,
140 const struct ifnet_demux_desc *, uint32_t);
141static errno_t redirect_del_proto(ifnet_t, protocol_family_t);
142static void redirect_clear_delegate_locked(if_redirect_t);
143static void redirect_clear_delegate(if_redirect_t);
144
145static struct if_clone
146 redirect_cloner = IF_CLONE_INITIALIZER(RD_NAME,
147 redirect_clone_create,
148 redirect_clone_destroy,
149 0,
150 RD_MAXUNIT);
151static void interface_link_event(ifnet_t ifp, uint32_t event_code);
152
153static LCK_GRP_DECLARE(redirect_lock_group, "redirect");
154static LCK_ATTR_DECLARE(redirect_lock_attr, 0, 0);
155
156#define RD_LOCK_INIT(rd) \
157 lck_mtx_init(&(rd)->rd_lock, &redirect_lock_group, &redirect_lock_attr)
158#define RD_LOCK(rd) \
159 lck_mtx_lock(&(rd)->rd_lock)
160#define RD_UNLOCK(rd) \
161 lck_mtx_unlock(&(rd)->rd_lock)
162#define RD_LOCK_DESTROY(rd) \
163 lck_mtx_destroy(&(rd)->rd_lock, &redirect_lock_group)
164
165static inline boolean_t
166redirect_is_usable(if_redirect_t rd)
167{
168 return !rd->rd_detaching && rd->rd_connected;
169}
170
171static inline unsigned int
172redirect_max_mtu(ifnet_t ifp)
173{
174 if_redirect_t rd;
175 unsigned int max_mtu = ETHERMTU;
176
177 rd = ifnet_get_if_redirect(ifp);
178 if (rd == NULL) {
179 RDLOG_ERR("rd is NULL");
180 goto done;
181 }
182 max_mtu = rd->rd_max_mtu;
183done:
184 return max_mtu;
185}
186
187static void
188redirect_free(if_redirect_t rd)
189{
190 VERIFY(rd->rd_retain_count == 0);
191
192 if (rd->rd_pp != NULL) {
193 pp_release(rd->rd_pp);
194 rd->rd_pp = NULL;
195 }
196 RD_LOCK_DESTROY(rd);
197 RDLOG_DBG("%s", rd->rd_name);
198 kfree_type(if_redirect, rd);
199}
200
201static void
202redirect_release(if_redirect_t rd)
203{
204 uint32_t old_retain_count;
205
206 old_retain_count = OSDecrementAtomic(&rd->rd_retain_count);
207 switch (old_retain_count) {
208 case 0:
209 VERIFY(old_retain_count != 0);
210 break;
211 case 1:
212 redirect_free(rd);
213 break;
214 default:
215 break;
216 }
217 return;
218}
219
220static void
221redirect_retain(if_redirect_t rd)
222{
223 OSIncrementAtomic(&rd->rd_retain_count);
224}
225
226static void
227redirect_bpf_tap(ifnet_t ifp, kern_packet_t pkt, bool input)
228{
229 uint32_t dlt;
230
231 switch (ifp->if_family) {
232 case IFNET_FAMILY_ETHERNET:
233 dlt = DLT_EN10MB;
234 break;
235 case IFNET_FAMILY_CELLULAR:
236 case IFNET_FAMILY_UTUN:
237 case IFNET_FAMILY_IPSEC:
238 dlt = DLT_RAW;
239 break;
240 default:
241 DTRACE_SKYWALK1(invalid__family, ifnet_t, ifp);
242 return;
243 }
244
245 if (input) {
246 bpf_tap_packet_in(interface: ifp, dlt, packet: pkt, NULL, header_len: 0);
247 } else {
248 bpf_tap_packet_out(interface: ifp, dlt, packet: pkt, NULL, header_len: 0);
249 }
250}
251
252static void
253redirect_packet_pool_init_prepare(if_redirect_t rd,
254 struct kern_pbufpool_init *pp_init)
255{
256 uint32_t max_mtu = rd->rd_max_mtu;
257
258 bzero(s: pp_init, n: sizeof(*pp_init));
259 pp_init->kbi_version = KERN_PBUFPOOL_CURRENT_VERSION;
260 pp_init->kbi_flags |= KBIF_VIRTUAL_DEVICE;
261 pp_init->kbi_packets = RD_POOL_SIZE;
262 pp_init->kbi_bufsize = max_mtu;
263 pp_init->kbi_max_frags = 1;
264 pp_init->kbi_buflets = (2 * pp_init->kbi_packets); /* Tx/Rx pool */
265 pp_init->kbi_buf_seg_size = skmem_usr_buf_seg_size;
266 pp_init->kbi_ctx = NULL;
267 pp_init->kbi_ctx_retain = NULL;
268 pp_init->kbi_ctx_release = NULL;
269}
270
271static errno_t
272redirect_packet_pool_make(if_redirect_t rd)
273{
274 struct kern_pbufpool_init pp_init;
275 errno_t err;
276
277 redirect_packet_pool_init_prepare(rd, pp_init: &pp_init);
278 (void)snprintf((char *)pp_init.kbi_name, count: sizeof(pp_init.kbi_name),
279 "%s pp", rd->rd_name);
280
281 err = kern_pbufpool_create(&pp_init, &rd->rd_pp, NULL);
282 return err;
283}
284
285static int
286redirect_enqueue_pkt(struct nx_netif *nif, struct __kern_packet *pkt,
287 boolean_t flush, boolean_t *drop)
288{
289 ifnet_t ifp = nif->nif_ifp;
290 uint64_t qset_id;
291 int err;
292
293 if (NX_LLINK_PROV(nif->nif_nx) &&
294 ifp->if_traffic_rule_count > 0 &&
295 nxctl_inet_traffic_rule_find_qset_id_with_pkt(ifp->if_xname,
296 pkt, &qset_id) == 0) {
297 struct netif_qset *qset;
298
299 /*
300 * This always returns a qset because if the qset id is invalid the
301 * default qset is returned.
302 */
303 qset = nx_netif_find_qset(nif, qset_id);
304 ASSERT(qset != NULL);
305 pkt->pkt_qset_idx = qset->nqs_idx;
306 err = ifnet_enqueue_ifcq_pkt(ifp, qset->nqs_ifcq, pkt, flush, drop);
307 nx_netif_qset_release(&qset);
308 } else {
309 /* callee consumes packet */
310 err = ifnet_enqueue_pkt(ifp, pkt, flush, drop);
311 }
312 return err;
313}
314
315static int
316redirect_enqueue_mbuf(struct nx_netif *nif, struct mbuf *m,
317 boolean_t flush, boolean_t *drop)
318{
319 return ifnet_enqueue_mbuf(nif->nif_ifp, m, flush, drop);
320}
321
322static int
323redirect_tx_submit(ifnet_t delegate_ifp, struct pktq *spktq, uint32_t if_flowhash)
324{
325 struct __kern_packet *spkt, *pkt;
326 struct nx_netif *nif;
327 struct netif_stats *nifs;
328 struct nexus_netif_adapter *dev_nifna;
329 struct mbuf *m;
330 boolean_t drop, native, compat;
331 errno_t err;
332 int cnt = 0;
333
334 if (!ifnet_datamov_begin(delegate_ifp)) {
335 RDLOG_ERR("delegate interface is being detached");
336 DTRACE_SKYWALK1(delegate__detached, ifnet_t, delegate_ifp);
337 return ENXIO;
338 }
339 if (NA(delegate_ifp) == NULL) {
340 RDLOG_ERR("nexus adapter is not present");
341 DTRACE_SKYWALK1(no__nexus, ifnet_t, delegate_ifp);
342 err = ENXIO;
343 goto done;
344 }
345 dev_nifna = NA(delegate_ifp);
346 nif = dev_nifna->nifna_netif;
347 nifs = &nif->nif_stats;
348
349 native = (dev_nifna->nifna_up.na_type == NA_NETIF_DEV);
350 compat = (dev_nifna->nifna_up.na_type == NA_NETIF_COMPAT_DEV);
351
352 while (KPKTQ_LEN(spktq) > 0) {
353 KPKTQ_DEQUEUE(spktq, spkt);
354 ASSERT(spkt != NULL);
355 drop = FALSE;
356
357 if (__probable(native)) {
358 pkt = nx_netif_pkt_to_pkt(dev_nifna, spkt, NETIF_CONVERT_TX);
359 if (pkt == NULL) {
360 continue;
361 }
362 pkt->pkt_flowsrc_type = FLOWSRC_IFNET;
363 pkt->pkt_flow_token = if_flowhash;
364 pkt->pkt_pflags |= (PKT_F_FLOW_ADV | PKTF_FLOW_ID);
365
366 netif_ifp_inc_traffic_class_out_pkt(delegate_ifp,
367 pkt->pkt_svc_class, 1, pkt->pkt_length);
368
369 err = redirect_enqueue_pkt(nif, pkt, FALSE, drop: &drop);
370 } else {
371 ASSERT(compat);
372 m = nx_netif_pkt_to_mbuf(dev_nifna, spkt, NETIF_CONVERT_TX);
373 if (m == NULL) {
374 continue;
375 }
376 m->m_pkthdr.pkt_flowsrc = FLOWSRC_IFNET;
377 m->m_pkthdr.pkt_mpriv_srcid = if_flowhash;
378 m->m_pkthdr.pkt_flags =
379 (PKTF_FLOW_ID | PKTF_FLOW_ADV | PKTF_FLOW_LOCALSRC);
380
381 ifp_inc_traffic_class_out(ifp: delegate_ifp, m);
382
383 err = redirect_enqueue_mbuf(nif, m, FALSE, drop: &drop);
384 }
385 if (__probable(err == 0)) {
386 cnt++;
387 } else {
388 RDLOG_ERR("enqueue failed: %d", err);
389 if (drop) {
390 STATS_INC(nifs, NETIF_STATS_TX_DROP_ENQ_AQM);
391 STATS_INC(nifs, NETIF_STATS_DROP);
392 }
393 DTRACE_SKYWALK3(enqueue__failed,
394 ifnet_t, delegate_ifp, boolean_t, drop, int, err);
395 break;
396 }
397 }
398done:
399 if (cnt > 0) {
400 netif_transmit(delegate_ifp, NETIF_XMIT_FLAG_REDIRECT);
401 }
402 ifnet_datamov_end(delegate_ifp);
403 return err;
404}
405
406/*
407 * nexus netif domain provider
408 */
409static errno_t
410redirect_nxdp_init(kern_nexus_domain_provider_t domprov)
411{
412#pragma unused(domprov)
413 return 0;
414}
415
416static void
417redirect_nxdp_fini(kern_nexus_domain_provider_t domprov)
418{
419#pragma unused(domprov)
420}
421
422static uuid_t redirect_nx_dom_prov;
423
424static errno_t
425redirect_register_nexus_domain_provider(void)
426{
427 const struct kern_nexus_domain_provider_init dp_init = {
428 .nxdpi_version = KERN_NEXUS_DOMAIN_PROVIDER_CURRENT_VERSION,
429 .nxdpi_flags = 0,
430 .nxdpi_init = redirect_nxdp_init,
431 .nxdpi_fini = redirect_nxdp_fini
432 };
433
434 errno_t err = 0;
435
436 /* redirect_nxdp_init() is called before this function returns */
437 err = kern_nexus_register_domain_provider(type: NEXUS_TYPE_NET_IF,
438 name: (const uint8_t *)
439 "com.apple.redirect",
440 init: &dp_init, init_len: sizeof(dp_init),
441 dom_prov_uuid: &redirect_nx_dom_prov);
442 if (err != 0) {
443 RDLOG_ERR("failed to register domain provider");
444 return err;
445 }
446 return 0;
447}
448
449/*
450 * netif nexus routines
451 */
452static if_redirect_t
453redirect_nexus_context(kern_nexus_t nexus)
454{
455 if_redirect_t rd;
456
457 rd = (if_redirect_t)kern_nexus_get_context(nexus);
458 assert(rd != NULL);
459 return rd;
460}
461
462static errno_t
463redirect_nx_ring_init(kern_nexus_provider_t nxprov, kern_nexus_t nexus,
464 kern_channel_t channel, kern_channel_ring_t ring, boolean_t is_tx_ring,
465 void **ring_ctx)
466{
467#pragma unused(nxprov, channel, ring_ctx)
468 if_redirect_t rd;
469
470 rd = redirect_nexus_context(nexus);
471 RD_LOCK(rd);
472 if (rd->rd_detaching) {
473 DTRACE_SKYWALK1(detaching, if_redirect_t, rd);
474 RD_UNLOCK(rd);
475 return ENXIO;
476 }
477 if (is_tx_ring) {
478 _CASSERT(RD_MAX_TX_RINGS == 1);
479 VERIFY(rd->rd_tx_ring[0] == NULL);
480 rd->rd_tx_ring[0] = ring;
481 } else {
482 _CASSERT(RD_MAX_RX_RINGS == 1);
483 VERIFY(rd->rd_rx_ring[0] == NULL);
484 rd->rd_rx_ring[0] = ring;
485 }
486
487 rd->rd_nifs = &NX_NETIF_PRIVATE(nexus)->nif_stats;
488 RD_UNLOCK(rd);
489 RDLOG_INFO("%s: %s ring init", rd->rd_name,
490 is_tx_ring ? "TX" : "RX");
491 return 0;
492}
493
494static void
495redirect_nx_ring_fini(kern_nexus_provider_t nxprov, kern_nexus_t nexus,
496 kern_channel_ring_t ring)
497{
498#pragma unused(nxprov, ring)
499 if_redirect_t rd;
500 thread_call_t tcall = NULL;
501
502 rd = redirect_nexus_context(nexus);
503 RD_LOCK(rd);
504 if (rd->rd_rx_ring[0] == ring) {
505 RDLOG_INFO("%s: RX ring fini", rd->rd_name);
506 rd->rd_rx_ring[0] = NULL;
507 } else if (rd->rd_tx_ring[0] == ring) {
508 RDLOG_INFO("%s: TX ring fini", rd->rd_name);
509 tcall = rd->rd_doorbell_tcall;
510 rd->rd_doorbell_tcall = NULL;
511 rd->rd_tx_ring[0] = NULL;
512 }
513 rd->rd_nifs = NULL;
514 RD_UNLOCK(rd);
515
516 if (tcall != NULL) {
517 boolean_t success;
518
519 success = thread_call_cancel_wait(call: tcall);
520 RDLOG_INFO("%s: thread_call_cancel %s",
521 rd->rd_name, success ? "SUCCESS" : "FAILURE");
522 if (!success) {
523 RD_LOCK(rd);
524 if (rd->rd_doorbell_tcall_active) {
525 rd->rd_waiting_for_tcall = TRUE;
526 RDLOG_INFO("%s: *waiting for threadcall",
527 rd->rd_name);
528 do {
529 msleep(chan: rd, mtx: &rd->rd_lock,
530 PZERO, wmesg: "redirect threadcall", ts: 0);
531 } while (rd->rd_doorbell_tcall_active);
532 RDLOG_INFO("%s: threadcall done",
533 rd->rd_name);
534 rd->rd_waiting_for_tcall = FALSE;
535 }
536 RD_UNLOCK(rd);
537 }
538 success = thread_call_free(call: tcall);
539 RDLOG_INFO("%s: thread_call_free %s",
540 rd->rd_name, success ? "SUCCESS" : "FAILURE");
541 redirect_release(rd);
542 VERIFY(success == TRUE);
543 }
544}
545
546static errno_t
547redirect_nx_pre_connect(kern_nexus_provider_t nxprov,
548 proc_t proc, kern_nexus_t nexus, nexus_port_t port,
549 kern_channel_t channel, void **channel_context)
550{
551#pragma unused(nxprov, proc, nexus, port, channel, channel_context)
552 return 0;
553}
554
555static errno_t
556redirect_nx_connected(kern_nexus_provider_t nxprov, kern_nexus_t nexus,
557 kern_channel_t channel)
558{
559#pragma unused(nxprov, channel)
560 if_redirect_t rd = NULL;
561
562 rd = redirect_nexus_context(nexus);
563 RD_LOCK(rd);
564 if (rd->rd_detaching) {
565 DTRACE_SKYWALK1(detaching, if_redirect_t, rd);
566 RD_UNLOCK(rd);
567 return EBUSY;
568 }
569 redirect_retain(rd);
570 rd->rd_connected = TRUE;
571 RD_UNLOCK(rd);
572
573 RDLOG_DBG("%s: connected channel %p", rd->rd_name, channel);
574 return 0;
575}
576
577static void
578redirect_nx_pre_disconnect(kern_nexus_provider_t nxprov, kern_nexus_t nexus,
579 kern_channel_t channel)
580{
581#pragma unused(nxprov, channel)
582 if_redirect_t rd;
583
584 rd = redirect_nexus_context(nexus);
585 RDLOG_INFO("%s: pre-disconnect channel %p", rd->rd_name, channel);
586 /* Quiesce the interface and flush any pending outbound packets */
587 if_down(rd->rd_ifp);
588 RD_LOCK(rd);
589 rd->rd_connected = FALSE;
590 RD_UNLOCK(rd);
591}
592
593static void
594redirect_nx_disconnected(kern_nexus_provider_t nxprov, kern_nexus_t nexus,
595 kern_channel_t channel)
596{
597#pragma unused(nxprov, channel)
598 if_redirect_t rd;
599
600 rd = redirect_nexus_context(nexus);
601 RDLOG_INFO("%s: disconnected channel %p", rd->rd_name, channel);
602 redirect_release(rd);
603}
604
605static errno_t
606redirect_nx_slot_init(kern_nexus_provider_t nxprov, kern_nexus_t nexus,
607 kern_channel_ring_t ring, kern_channel_slot_t slot, uint32_t slot_index,
608 struct kern_slot_prop **slot_prop_addr, void **slot_context)
609{
610#pragma unused(nxprov, nexus, ring, slot, slot_index, slot_prop_addr, slot_context)
611 return 0;
612}
613
614static void
615redirect_nx_slot_fini(kern_nexus_provider_t nxprov, kern_nexus_t nexus,
616 kern_channel_ring_t ring, kern_channel_slot_t slot, uint32_t slot_index)
617{
618#pragma unused(nxprov, nexus, ring, slot, slot_index)
619}
620
621static errno_t
622redirect_nx_sync_tx(kern_nexus_provider_t nxprov, kern_nexus_t nexus,
623 kern_channel_ring_t tx_ring, uint32_t flags)
624{
625#pragma unused(nxprov)
626 if_redirect_t rd;
627 ifnet_t ifp;
628 kern_channel_slot_t last_tx_slot = NULL;
629 ifnet_t delegate_ifp;
630 struct kern_channel_ring_stat_increment stats;
631 kern_channel_slot_t tx_slot = NULL;
632 struct netif_stats *nifs = &NX_NETIF_PRIVATE(nexus)->nif_stats;
633 struct pktq tx_pktq;
634 uint32_t n_pkts = 0;
635 int error = 0;
636
637 bzero(s: &stats, n: sizeof(stats));
638 STATS_INC(nifs, NETIF_STATS_TX_SYNC);
639 rd = redirect_nexus_context(nexus);
640 RDLOG_INFO("%s ring %d flags 0x%x", rd->rd_name, tx_ring->ckr_ring_id, flags);
641
642 if (__improbable(!redirect_is_usable(rd))) {
643 RDLOG_INFO("%s is not usable", rd->rd_name);
644 DTRACE_SKYWALK1(unusable, if_redirect_t, rd);
645 return ENOENT;
646 }
647 ifp = rd->rd_ifp;
648 delegate_ifp = rd->rd_delegate_ifp;
649
650 KPKTQ_INIT(&tx_pktq);
651 while ((tx_slot = kern_channel_get_next_slot(kring: tx_ring, slot: tx_slot, NULL)) != NULL) {
652 kern_packet_t sph;
653
654 /* detach the packet from the TX ring */
655 sph = kern_channel_slot_get_packet(ring: tx_ring, slot: tx_slot);
656 VERIFY(sph != 0);
657 kern_channel_slot_detach_packet(ring: tx_ring, slot: tx_slot, packet: sph);
658
659 /* bpf tap output */
660 redirect_bpf_tap(ifp, pkt: sph, false);
661
662 ASSERT(sph != 0);
663 STATS_INC(nifs, NETIF_STATS_TX_COPY_DIRECT);
664 STATS_INC(nifs, NETIF_STATS_TX_PACKETS);
665
666 stats.kcrsi_slots_transferred++;
667 stats.kcrsi_bytes_transferred += kern_packet_get_data_length(sph);
668
669 KPKTQ_ENQUEUE(&tx_pktq, SK_PTR_ADDR_KPKT(sph));
670 n_pkts++;
671
672 last_tx_slot = tx_slot;
673 }
674 if (last_tx_slot != NULL) {
675 kern_channel_advance_slot(kring: tx_ring, slot: last_tx_slot);
676 kern_channel_increment_ring_net_stats(ring: tx_ring, ifp, stats: &stats);
677 }
678 if (__improbable(delegate_ifp == NULL)) {
679 RDLOG_INFO("%s has no delegate", rd->rd_name);
680 DTRACE_SKYWALK1(no__delegate, if_redirect_t, rd);
681 error = ENXIO;
682 goto done;
683 }
684 if (n_pkts > 0) {
685 redirect_tx_submit(delegate_ifp, spktq: &tx_pktq, if_flowhash: ifp->if_flowhash);
686 }
687done:
688 /*
689 * Packets not enqueued into delegate interface AQM
690 */
691 if (KPKTQ_LEN(&tx_pktq) > 0) {
692 DTRACE_SKYWALK2(unsent, if_redirect_t, rd, struct pktq *, &tx_pktq);
693 STATS_ADD(nifs, NETIF_STATS_DROP_NO_DELEGATE, KPKTQ_LEN(&tx_pktq));
694 pp_free_pktq(&tx_pktq);
695 }
696 return error;
697}
698
699static void
700redirect_rx_cb(void *arg, struct pktq *spktq)
701{
702 if_redirect_t rd = arg;
703 struct __kern_packet *spkt, *pkt;
704 kern_packet_t ph;
705 kern_channel_ring_t rx_ring = NULL;
706 kern_channel_slot_t rx_slot = NULL, last_rx_slot = NULL;
707 struct kern_channel_ring_stat_increment stats;
708 int err;
709
710 /*
711 * The ring cannot disappear before the callback is finished and removed.
712 */
713 rx_ring = rd->rd_rx_ring[0];
714 if (rx_ring == NULL) {
715 DTRACE_SKYWALK2(no__ring__drop, if_redirect_t, rd, struct pktq *, spktq);
716 pp_free_pktq(spktq);
717 return;
718 }
719 bzero(s: &stats, n: sizeof(stats));
720 kr_enter(rx_ring, TRUE);
721 kern_channel_reclaim(rx_ring);
722
723 while (KPKTQ_LEN(spktq) > 0) {
724 KPKTQ_DEQUEUE(spktq, spkt);
725
726 rx_slot = kern_channel_get_next_slot(kring: rx_ring, slot: last_rx_slot, NULL);
727 if (rx_slot == NULL) {
728 DTRACE_SKYWALK2(no__slot__drop, if_redirect_t, rd,
729 struct __kern_packet *, spkt);
730 pp_free_packet_single(spkt);
731 continue;
732 }
733 pkt = nx_netif_pkt_to_pkt(rd->rd_ifp->if_na, spkt, NETIF_CONVERT_RX);
734 if (pkt == NULL) {
735 DTRACE_SKYWALK1(copy__drop, if_redirect_t, rd);
736 continue;
737 }
738 ph = SK_PKT2PH(pkt);
739 stats.kcrsi_slots_transferred++;
740 stats.kcrsi_bytes_transferred += kern_packet_get_data_length(ph);
741
742 redirect_bpf_tap(ifp: rd->rd_ifp, pkt: ph, true);
743
744 err = kern_channel_slot_attach_packet(ring: rx_ring, slot: rx_slot, packet: ph);
745 VERIFY(err == 0);
746 last_rx_slot = rx_slot;
747 }
748 ASSERT(KPKTQ_EMPTY(spktq));
749 if (last_rx_slot != NULL) {
750 kern_channel_advance_slot(kring: rx_ring, slot: last_rx_slot);
751 kern_channel_increment_ring_net_stats(ring: rx_ring, rd->rd_ifp, stats: &stats);
752 }
753 kr_exit(rx_ring);
754 if (last_rx_slot != NULL) {
755 kern_channel_notify(rx_ring, flags: 0);
756 }
757}
758
759static errno_t
760redirect_nx_sync_rx(kern_nexus_provider_t nxprov, kern_nexus_t nexus,
761 kern_channel_ring_t ring, uint32_t flags)
762{
763#pragma unused(nxprov, nexus, ring, flags)
764 return 0;
765}
766
767static void
768redirect_async_doorbell(thread_call_param_t arg0, thread_call_param_t arg1)
769{
770#pragma unused(arg1)
771 errno_t error;
772 if_redirect_t rd = (if_redirect_t)arg0;
773 kern_channel_ring_t ring;
774 boolean_t more;
775
776 RD_LOCK(rd);
777 ring = rd->rd_tx_ring[0];
778 if (__improbable(!redirect_is_usable(rd) || ring == NULL)) {
779 DTRACE_SKYWALK2(unusable, if_redirect_t, rd, kern_channel_ring_t, ring);
780 goto done;
781 }
782 rd->rd_doorbell_tcall_active = TRUE;
783 RD_UNLOCK(rd);
784
785 error = kern_channel_tx_refill(ring, UINT32_MAX, UINT32_MAX, FALSE,
786 pkts_pending: &more);
787 if (error != 0 && error != EAGAIN) {
788 RDLOG_ERR("%s: Tx refill failed %d", rd->rd_name, error);
789 } else {
790 RDLOG_DBG("%s: Tx refilled", rd->rd_name);
791 }
792
793 RD_LOCK(rd);
794done:
795 rd->rd_doorbell_tcall_active = FALSE;
796 if (rd->rd_waiting_for_tcall) {
797 RDLOG_INFO("%s: threadcall waking up waiter", rd->rd_name);
798 wakeup(chan: (caddr_t)rd);
799 }
800 RD_UNLOCK(rd);
801}
802
803static void
804redirect_schedule_async_doorbell(if_redirect_t rd)
805{
806 thread_call_t tcall;
807
808 RD_LOCK(rd);
809 if (__improbable(!redirect_is_usable(rd))) {
810 DTRACE_SKYWALK1(unusable, if_redirect_t, rd);
811 RD_UNLOCK(rd);
812 return;
813 }
814 tcall = rd->rd_doorbell_tcall;
815 if (tcall != NULL) {
816 thread_call_enter(call: tcall);
817 } else {
818 tcall = thread_call_allocate_with_options(func: redirect_async_doorbell,
819 param0: (thread_call_param_t)rd,
820 pri: THREAD_CALL_PRIORITY_KERNEL,
821 options: THREAD_CALL_OPTIONS_ONCE);
822 if (tcall == NULL) {
823 RDLOG_ERR("%s: tcall alloc failed", rd->rd_name);
824 } else {
825 rd->rd_doorbell_tcall = tcall;
826 redirect_retain(rd);
827 thread_call_enter(call: tcall);
828 }
829 }
830 RD_UNLOCK(rd);
831}
832
833static errno_t
834redirect_nx_tx_doorbell(kern_nexus_provider_t nxprov, kern_nexus_t nexus,
835 kern_channel_ring_t ring, uint32_t flags)
836{
837#pragma unused(nxprov, ring, flags)
838 errno_t error;
839 if_redirect_t rd;
840
841 rd = redirect_nexus_context(nexus);
842 RDLOG_DBG("%s", rd->rd_name);
843
844 if ((flags & KERN_NEXUS_TXDOORBELLF_ASYNC_REFILL) == 0) {
845 boolean_t more;
846 /* synchronous tx refill */
847 error = kern_channel_tx_refill(ring, UINT32_MAX, UINT32_MAX,
848 TRUE, pkts_pending: &more);
849 if (error != 0 && error != EAGAIN) {
850 RDLOG_ERR("%s: Tx refill (sync) %d", rd->rd_name, error);
851 } else {
852 RDLOG_DBG("%s: Tx refilled (sync)", rd->rd_name);
853 }
854 } else {
855 RDLOG_DBG("%s: schedule async refill", rd->rd_name);
856 redirect_schedule_async_doorbell(rd);
857 }
858 return 0;
859}
860
861static errno_t
862redirect_netif_prepare(kern_nexus_t nexus, ifnet_t ifp)
863{
864 if_redirect_t rd;
865
866 rd = (if_redirect_t)kern_nexus_get_context(nexus);
867
868 (void)ifnet_set_capabilities_enabled(interface: ifp, new_caps: 0, mask: -1);
869 ifnet_set_baudrate(interface: ifp, baudrate: 0);
870 ifnet_set_mtu(interface: ifp, ETHERMTU);
871 ifnet_set_offload(interface: ifp, offload: 0);
872
873 if (rd->rd_ftype == IFRTYPE_FAMILY_ETHERNET) {
874 ifnet_set_flags(interface: ifp,
875 IFF_BROADCAST | IFF_MULTICAST | IFF_SIMPLEX, mask: 0xffff);
876 ifnet_set_addrlen(interface: ifp, ETHER_ADDR_LEN);
877 ifnet_set_hdrlen(interface: ifp, hdrlen: sizeof(struct ether_header));
878 } else {
879 ifnet_set_flags(interface: ifp, IFF_MULTICAST | IFF_POINTOPOINT, mask: 0xffff);
880 }
881 return 0;
882}
883
884static void
885redirect_delegate_adv_config(ifnet_t delegate_ifp, bool enable)
886{
887 struct nx_netif *delegate_nif;
888
889 ASSERT(delegate_ifp != NULL);
890 if (!SKYWALK_NATIVE(delegate_ifp)) {
891 RDLOG_ERR("%s is not skywalk native", if_name(delegate_ifp));
892 DTRACE_SKYWALK1(not__native, ifnet_t, delegate_ifp);
893 return;
894 }
895 delegate_nif = NA(delegate_ifp)->nifna_netif;
896 nx_netif_config_interface_advisory(delegate_nif->nif_nx, enable);
897}
898
899static errno_t
900redirect_nx_intf_adv_config(void *prov_ctx, bool enable)
901{
902 if_redirect_t rd = (if_redirect_t)prov_ctx;
903
904 RD_LOCK(rd);
905 if (!redirect_is_usable(rd)) {
906 RDLOG_ERR("cannot %s advisory on %s because it is not usable",
907 enable ? "enable" : "disable", if_name(rd->rd_ifp));
908 DTRACE_SKYWALK1(unusable, if_redirect_t, rd);
909 RD_UNLOCK(rd);
910 return ENXIO;
911 }
912 if (rd->rd_intf_adv_enabled == enable) {
913 RDLOG_ERR("advisory is already %s on %s",
914 enable ? "enable" : "disable", if_name(rd->rd_ifp));
915 DTRACE_SKYWALK1(advisory__already__set, if_redirect_t, rd);
916 RD_UNLOCK(rd);
917 return ENXIO;
918 }
919 if (!rd->rd_delegate_set) {
920 RDLOG_ERR("delegate is not set on %s", if_name(rd->rd_ifp));
921 DTRACE_SKYWALK1(no__delegate, if_redirect_t, rd);
922 RD_UNLOCK(rd);
923 return ENXIO;
924 }
925 redirect_delegate_adv_config(delegate_ifp: rd->rd_delegate_ifp, enable);
926 rd->rd_intf_adv_enabled = enable;
927 RD_UNLOCK(rd);
928 return 0;
929}
930
931static errno_t
932fill_capab_interface_advisory(if_redirect_t rd, void *contents,
933 uint32_t *len)
934{
935 struct kern_nexus_capab_interface_advisory *capab = contents;
936
937 if (*len != sizeof(*capab)) {
938 DTRACE_SKYWALK2(invalid__len, uint32_t, *len, size_t, sizeof(*capab));
939 return EINVAL;
940 }
941 if (capab->kncia_version !=
942 KERN_NEXUS_CAPAB_INTERFACE_ADVISORY_VERSION_1) {
943 DTRACE_SKYWALK2(invalid__ver, uint32_t, capab->kncia_version,
944 uint32_t, KERN_NEXUS_CAPAB_INTERFACE_ADVISORY_VERSION_1);
945 return EINVAL;
946 }
947 VERIFY(capab->kncia_notify != NULL);
948 rd->rd_intf_adv_kern_ctx = capab->kncia_kern_context;
949 rd->rd_intf_adv_notify = capab->kncia_notify;
950 capab->kncia_provider_context = rd;
951 capab->kncia_config = redirect_nx_intf_adv_config;
952 return 0;
953}
954
955static errno_t
956redirect_nx_capab_config(kern_nexus_provider_t nxprov, kern_nexus_t nx,
957 kern_nexus_capab_t capab, void *contents, uint32_t *len)
958{
959#pragma unused(nxprov)
960 errno_t error;
961 if_redirect_t rd;
962
963 rd = redirect_nexus_context(nexus: nx);
964
965 switch (capab) {
966 case KERN_NEXUS_CAPAB_INTERFACE_ADVISORY:
967 error = fill_capab_interface_advisory(rd, contents, len);
968 break;
969 default:
970 error = ENOTSUP;
971 break;
972 }
973 return error;
974}
975
976static errno_t
977create_netif_provider_and_instance(if_redirect_t rd,
978 struct ifnet_init_eparams *init_params, ifnet_t *ifp,
979 uuid_t *provider, uuid_t *instance)
980{
981 errno_t err = 0;
982 nexus_controller_t controller = kern_nexus_shared_controller();
983 struct kern_nexus_net_init net_init = {};
984 nexus_name_t provider_name = {};
985 nexus_attr_t nexus_attr = NULL;
986
987 struct kern_nexus_provider_init prov_init = {
988 .nxpi_version = KERN_NEXUS_DOMAIN_PROVIDER_CURRENT_VERSION,
989 .nxpi_flags = NXPIF_VIRTUAL_DEVICE,
990 .nxpi_pre_connect = redirect_nx_pre_connect,
991 .nxpi_connected = redirect_nx_connected,
992 .nxpi_pre_disconnect = redirect_nx_pre_disconnect,
993 .nxpi_disconnected = redirect_nx_disconnected,
994 .nxpi_ring_init = redirect_nx_ring_init,
995 .nxpi_ring_fini = redirect_nx_ring_fini,
996 .nxpi_slot_init = redirect_nx_slot_init,
997 .nxpi_slot_fini = redirect_nx_slot_fini,
998 .nxpi_sync_tx = redirect_nx_sync_tx,
999 .nxpi_sync_rx = redirect_nx_sync_rx,
1000 .nxpi_tx_doorbell = redirect_nx_tx_doorbell,
1001 .nxpi_config_capab = redirect_nx_capab_config,
1002 };
1003
1004 err = kern_nexus_attr_create(&nexus_attr);
1005 if (err != 0) {
1006 RDLOG_ERR("%s nexus attribution creation failed, error: %d",
1007 rd->rd_name, err);
1008 DTRACE_SKYWALK2(attr__create__failed, if_redirect_t, rd, int, err);
1009 goto failed;
1010 }
1011
1012 snprintf((char *)provider_name, count: sizeof(provider_name),
1013 "com.apple.netif.%s", rd->rd_name);
1014 err = kern_nexus_controller_register_provider(ctl: controller,
1015 dom_prov_uuid: redirect_nx_dom_prov,
1016 provider_name,
1017 init: &prov_init,
1018 init_len: sizeof(prov_init),
1019 nxa: nexus_attr,
1020 nx_prov_uuid: provider);
1021 if (err != 0) {
1022 RDLOG_ERR("%s register provider failed, error %d", rd->rd_name, err);
1023 DTRACE_SKYWALK2(register__failed, if_redirect_t, rd, int, err);
1024 goto failed;
1025 }
1026
1027 net_init.nxneti_version = KERN_NEXUS_NET_CURRENT_VERSION;
1028 net_init.nxneti_flags = 0;
1029 net_init.nxneti_eparams = init_params;
1030 net_init.nxneti_lladdr = NULL;
1031 net_init.nxneti_prepare = redirect_netif_prepare;
1032 net_init.nxneti_rx_pbufpool = rd->rd_pp;
1033 net_init.nxneti_tx_pbufpool = rd->rd_pp;
1034 err = kern_nexus_controller_alloc_net_provider_instance(ctl: controller,
1035 nx_prov_uuid: *provider, nexus_context: rd, NULL, nx_uuid: instance, init: &net_init, ifp);
1036 if (err != 0) {
1037 RDLOG_ERR("%s alloc net provider instance failed %d", rd->rd_name, err);
1038 DTRACE_SKYWALK2(alloc__provider__instance__failed, if_redirect_t, rd, int, err);
1039 kern_nexus_controller_deregister_provider(ctl: controller, nx_prov_uuid: *provider);
1040 uuid_clear(uu: *provider);
1041 goto failed;
1042 }
1043failed:
1044 if (nexus_attr != NULL) {
1045 kern_nexus_attr_destroy(attr: nexus_attr);
1046 }
1047 return err;
1048}
1049
1050static errno_t
1051redirect_attach_netif_nexus(if_redirect_t rd,
1052 struct ifnet_init_eparams *init_params, ifnet_t *ifp)
1053{
1054 errno_t error = 0;
1055 redirect_nx_t nx = &rd->rd_nx;
1056
1057 error = redirect_packet_pool_make(rd);
1058 if (error != 0) {
1059 RDLOG_ERR("%s packet pool make failed: %d", rd->rd_name, error);
1060 DTRACE_SKYWALK2(pool__make__failed, if_redirect_t, rd, int, error);
1061 return error;
1062 }
1063
1064 return create_netif_provider_and_instance(rd, init_params, ifp,
1065 provider: &nx->rnx_provider, instance: &nx->rnx_instance);
1066}
1067
1068static void
1069detach_provider_and_instance(uuid_t provider, uuid_t instance)
1070{
1071 nexus_controller_t controller = kern_nexus_shared_controller();
1072 errno_t err;
1073
1074 if (!uuid_is_null(uu: instance)) {
1075 err = kern_nexus_controller_free_provider_instance(ctl: controller,
1076 nx_uuid: instance);
1077 if (err != 0) {
1078 RDLOG_ERR("free_provider_instance failed %d", err);
1079 }
1080 uuid_clear(uu: instance);
1081 }
1082 if (!uuid_is_null(uu: provider)) {
1083 err = kern_nexus_controller_deregister_provider(ctl: controller,
1084 nx_prov_uuid: provider);
1085 if (err != 0) {
1086 RDLOG_ERR("deregister_provider failed %d", err);
1087 }
1088 uuid_clear(uu: provider);
1089 }
1090 return;
1091}
1092
1093static void
1094redirect_detach_netif_nexus(if_redirect_t rd)
1095{
1096 redirect_nx_t rnx = &rd->rd_nx;
1097 detach_provider_and_instance(provider: rnx->rnx_provider, instance: rnx->rnx_instance);
1098}
1099
1100static void
1101interface_link_event(ifnet_t ifp, uint32_t event_code)
1102{
1103 struct event {
1104 uint32_t ifnet_family;
1105 uint32_t unit;
1106 char if_name[IFNAMSIZ];
1107 };
1108 _Alignas(struct kern_event_msg) char message[sizeof(struct kern_event_msg) + sizeof(struct event)] = { 0 };
1109 struct kern_event_msg *header = (struct kern_event_msg *)message;
1110 struct event *data = (struct event *)(header + 1);
1111
1112 header->total_size = sizeof(message);
1113 header->vendor_code = KEV_VENDOR_APPLE;
1114 header->kev_class = KEV_NETWORK_CLASS;
1115 header->kev_subclass = KEV_DL_SUBCLASS;
1116 header->event_code = event_code;
1117 data->ifnet_family = ifnet_family(interface: ifp);
1118 data->unit = (uint32_t)ifnet_unit(interface: ifp);
1119 strlcpy(dst: data->if_name, src: ifnet_name(interface: ifp), IFNAMSIZ);
1120 ifnet_event(interface: ifp, event_ptr: header);
1121}
1122
1123static if_redirect_t
1124ifnet_get_if_redirect(ifnet_t ifp)
1125{
1126 return (if_redirect_t)ifnet_softc(interface: ifp);
1127}
1128
1129static int
1130redirect_clone_create(struct if_clone *ifc, uint32_t unit, void *param)
1131{
1132 int error;
1133 if_redirect_t rd;
1134 struct ifnet_init_eparams rd_init;
1135 struct if_redirect_create_params params;
1136 user_addr_t param_addr = (user_addr_t)param;
1137 ifnet_t ifp;
1138
1139 if (param_addr == USER_ADDR_NULL) {
1140 RDLOG_ERR("create params not specified");
1141 DTRACE_SKYWALK2(no__param, struct if_clone *, ifc, uint32_t, unit);
1142 return EINVAL;
1143 }
1144 error = copyin(param_addr, &params, sizeof(params));
1145 if (error != 0) {
1146 RDLOG_ERR("copyin failed: error %d", error);
1147 DTRACE_SKYWALK1(copyin__failed, int, error);
1148 return error;
1149 }
1150 if ((params.ircp_type != RD_CREATE_PARAMS_TYPE &&
1151 params.ircp_type != RD_CREATE_PARAMS_TYPE_NOATTACH) ||
1152 params.ircp_len != sizeof(params)) {
1153 RDLOG_ERR("invalid type(0x%x) or len(0x%d)", params.ircp_type,
1154 params.ircp_len);
1155 DTRACE_SKYWALK2(invalid__params, uint16_t, params.ircp_type,
1156 uint16_t, params.ircp_len);
1157 return EINVAL;
1158 }
1159 if (params.ircp_ftype != IFRTYPE_FAMILY_ETHERNET &&
1160 params.ircp_ftype != IFRTYPE_FAMILY_CELLULAR) {
1161 RDLOG_ERR("functional type(0x%x) not supported", params.ircp_ftype);
1162 DTRACE_SKYWALK1(invalid__ftype, uint32_t, params.ircp_ftype);
1163 return ENOTSUP;
1164 }
1165
1166 rd = kalloc_type(if_redirect, Z_WAITOK | Z_ZERO | Z_NOFAIL);
1167 RD_LOCK_INIT(rd);
1168 rd->rd_ftype = params.ircp_ftype;
1169 rd->rd_retain_count = 1;
1170 rd->rd_max_mtu = RD_MAX_MTU;
1171
1172 /* use the interface name as the unique id for ifp recycle */
1173 if ((unsigned int)
1174 snprintf(rd->rd_name, count: sizeof(rd->rd_name), "%s%d",
1175 ifc->ifc_name, unit) >= sizeof(rd->rd_name)) {
1176 redirect_release(rd);
1177 RDLOG_ERR("invalid ifc_name(%s) or unit(%d)", ifc->ifc_name, unit);
1178 DTRACE_SKYWALK2(invalid__name__or__unit, char *, ifc->ifc_name,
1179 uint32_t, unit);
1180 return EINVAL;
1181 }
1182
1183 bzero(s: &rd_init, n: sizeof(rd_init));
1184 rd_init.ver = IFNET_INIT_CURRENT_VERSION;
1185 rd_init.len = sizeof(rd_init);
1186 rd_init.flags |= (IFNET_INIT_SKYWALK_NATIVE | IFNET_INIT_IF_ADV);
1187 if (params.ircp_type == RD_CREATE_PARAMS_TYPE_NOATTACH) {
1188 rd_init.flags |= IFNET_INIT_NX_NOAUTO;
1189 }
1190 rd_init.uniqueid = rd->rd_name;
1191 rd_init.uniqueid_len = (uint32_t)strlen(s: rd->rd_name);
1192 rd_init.name = ifc->ifc_name;
1193 rd_init.unit = unit;
1194 rd_init.softc = rd;
1195 rd_init.ioctl = redirect_ioctl;
1196 rd_init.detach = redirect_if_free;
1197 rd_init.subfamily = IFNET_SUBFAMILY_REDIRECT;
1198
1199 if (rd->rd_ftype == IFRTYPE_FAMILY_ETHERNET) {
1200 rd_init.family = IFNET_FAMILY_ETHERNET;
1201 rd_init.type = IFT_ETHER;
1202 rd_init.demux = ether_demux;
1203 rd_init.add_proto = ether_add_proto;
1204 rd_init.del_proto = ether_del_proto;
1205 rd_init.check_multi = ether_check_multi;
1206 rd_init.framer_extended = ether_frameout_extended;
1207 rd_init.broadcast_addr = etherbroadcastaddr;
1208 rd_init.broadcast_len = ETHER_ADDR_LEN;
1209 } else {
1210 rd_init.family = IFNET_FAMILY_CELLULAR;
1211 rd_init.type = IFT_CELLULAR;
1212 rd_init.demux = redirect_demux;
1213 rd_init.add_proto = redirect_add_proto;
1214 rd_init.del_proto = redirect_del_proto;
1215 }
1216 error = redirect_attach_netif_nexus(rd, init_params: &rd_init, ifp: &ifp);
1217 if (error != 0) {
1218 redirect_release(rd);
1219 RDLOG_ERR("attach netif nexus failed: error %d", error);
1220 DTRACE_SKYWALK1(attach__nexus__failed, int, error);
1221 return error;
1222 }
1223
1224 /* take an additional reference for nexus controller */
1225 redirect_retain(rd);
1226 rd->rd_ifp = ifp;
1227
1228 if (rd->rd_ftype == IFRTYPE_FAMILY_ETHERNET) {
1229 /* mac address will be set after delegate is configured */
1230 (void) ifnet_set_lladdr(interface: ifp, lladdr: default_mac, ETHER_ADDR_LEN);
1231 bpfattach(interface: ifp, DLT_EN10MB, header_length: sizeof(struct ether_header));
1232 } else {
1233 bpfattach(interface: ifp, DLT_RAW, header_length: 0);
1234 }
1235 return 0;
1236}
1237
1238/*
1239 * This function is meant for cleaning up everything, not just delegate
1240 * related info.
1241 */
1242static void
1243redirect_cleanup(if_redirect_t rd)
1244{
1245 redirect_clear_delegate(rd);
1246 rd->rd_intf_adv_enabled = false;
1247}
1248
1249static int
1250redirect_clone_destroy(ifnet_t ifp)
1251{
1252 if_redirect_t rd;
1253
1254 rd = ifnet_get_if_redirect(ifp);
1255 if (rd == NULL) {
1256 RDLOG_ERR("rd is NULL");
1257 DTRACE_SKYWALK1(null__rd, ifnet_t, ifp);
1258 return ENXIO;
1259 }
1260 RD_LOCK(rd);
1261 if (rd->rd_detaching) {
1262 RDLOG_ERR("%s is detaching", rd->rd_name);
1263 DTRACE_SKYWALK1(detaching, if_redirect_t, rd);
1264 RD_UNLOCK(rd);
1265 return 0;
1266 }
1267 rd->rd_detaching = TRUE;
1268 RD_UNLOCK(rd);
1269
1270 redirect_cleanup(rd);
1271 redirect_detach_netif_nexus(rd);
1272 /*
1273 * Releasing reference held for nexus controller
1274 */
1275 redirect_release(rd);
1276 interface_link_event(ifp, KEV_DL_LINK_OFF);
1277 ifnet_detach(interface: ifp);
1278 return 0;
1279}
1280
1281static int
1282if_redirect_request_copyin(user_addr_t user_addr,
1283 struct if_redirect_request *ifrr, uint64_t len)
1284{
1285 int error;
1286
1287 if (user_addr == USER_ADDR_NULL || len < sizeof(*ifrr)) {
1288 RDLOG_ERR("user_addr(0x%llx) or len(%llu) < %lu",
1289 user_addr, len, sizeof(*ifrr));
1290 error = EINVAL;
1291 goto done;
1292 }
1293 error = copyin(user_addr, ifrr, sizeof(*ifrr));
1294 if (error != 0) {
1295 RDLOG_ERR("copyin failed: %d", error);
1296 goto done;
1297 }
1298 if (ifrr->ifrr_reserved[0] != 0 || ifrr->ifrr_reserved[1] != 0 ||
1299 ifrr->ifrr_reserved[2] != 0 || ifrr->ifrr_reserved[3] != 0) {
1300 RDLOG_ERR("reserved[0]=0x%llu, reserved[1]=0x%llu"
1301 "reserved[2]=0x%llu, reserved[3]=0x%llu", ifrr->ifrr_reserved[0],
1302 ifrr->ifrr_reserved[1], ifrr->ifrr_reserved[2],
1303 ifrr->ifrr_reserved[3]);
1304 error = EINVAL;
1305 goto done;
1306 }
1307done:
1308 return error;
1309}
1310
1311static void
1312redirect_detach_notify(void *arg)
1313{
1314 if_redirect_t rd = arg;
1315
1316 redirect_clear_delegate(rd);
1317}
1318
1319static int
1320redirect_set_delegate(if_redirect_t rd, ifnet_t delegate_ifp)
1321{
1322 ifnet_t ifp = rd->rd_ifp;
1323 int error;
1324
1325 RD_LOCK(rd);
1326 if (rd->rd_detaching) {
1327 RDLOG_ERR("%s is detaching", rd->rd_name);
1328 DTRACE_SKYWALK2(detaching, if_redirect_t, rd, ifnet_t, delegate_ifp);
1329 RD_UNLOCK(rd);
1330 return ENXIO;
1331 }
1332 if (rd->rd_delegate_ifp != NULL) {
1333 if (rd->rd_delegate_ifp == delegate_ifp) {
1334 RDLOG_ERR("cannot configure the same delegate");
1335 DTRACE_SKYWALK2(same__ifp, if_redirect_t, rd,
1336 ifnet_t, delegate_ifp);
1337 RD_UNLOCK(rd);
1338 return EALREADY;
1339 } else {
1340 redirect_clear_delegate_locked(rd);
1341 }
1342 }
1343 ASSERT(rd->rd_delegate_ifp == NULL);
1344
1345 if (!ifnet_is_attached(ifp, refio: 1)) {
1346 RDLOG_ERR("failed to get self reference");
1347 DTRACE_SKYWALK2(ifp__detaching, if_redirect_t, rd, ifnet_t, ifp);
1348 error = ENXIO;
1349 goto fail;
1350 }
1351 ASSERT(!rd->rd_self_ref);
1352 rd->rd_self_ref = TRUE;
1353
1354 /* This saves the reference taken above */
1355 error = ifnet_set_delegate_parent(difp: delegate_ifp, parent: ifp);
1356 if (error != 0) {
1357 RDLOG_ERR("failed to set delegate parent");
1358 DTRACE_SKYWALK4(set__delegate__parent__failed, if_redirect_t, rd,
1359 ifnet_t, delegate_ifp, ifnet_t, ifp, int, error);
1360 goto fail;
1361 }
1362 ASSERT(!rd->rd_delegate_parent_set);
1363 rd->rd_delegate_parent_set = TRUE;
1364
1365 if (!ifnet_is_attached(delegate_ifp, refio: 1)) {
1366 RDLOG_ERR("failed to get delegate reference");
1367 DTRACE_SKYWALK2(delegate__detaching, if_redirect_t, rd,
1368 ifnet_t, delegate_ifp);
1369 error = ENXIO;
1370 goto fail;
1371 }
1372 ASSERT(rd->rd_delegate_ifp == NULL);
1373 rd->rd_delegate_ifp = delegate_ifp;
1374 ASSERT(!rd->rd_delegate_ref);
1375 rd->rd_delegate_ref = TRUE;
1376
1377 error = ifnet_set_flowswitch_rx_callback(ifp: delegate_ifp, cb: redirect_rx_cb, arg: rd);
1378 if (error != 0) {
1379 RDLOG_ERR("failed to set fsw rx callback: %d", error);
1380 DTRACE_SKYWALK3(set__fsw__rx__cb__fail, if_redirect_t, rd, ifnet_t,
1381 delegate_ifp, int, error);
1382 goto fail;
1383 }
1384 ASSERT(!rd->rd_fsw_rx_cb_set);
1385 rd->rd_fsw_rx_cb_set = TRUE;
1386
1387 error = ifnet_set_delegate(ifp, delegated_ifp: delegate_ifp);
1388 if (error != 0) {
1389 RDLOG_ERR("failed to set delegate ifp: %d", error);
1390 DTRACE_SKYWALK4(set__delegate__fail, if_redirect_t, rd, ifnet_t, ifp,
1391 ifnet_t, delegate_ifp, int, error);
1392 goto fail;
1393 }
1394 ASSERT(!rd->rd_delegate_set);
1395 rd->rd_delegate_set = TRUE;
1396
1397 if (rd->rd_ftype == IFRTYPE_FAMILY_ETHERNET) {
1398 uint8_t mac_addr[ETHER_ADDR_LEN];
1399
1400 error = ifnet_lladdr_copy_bytes(interface: delegate_ifp, lladdr: mac_addr,
1401 ETHER_ADDR_LEN);
1402 if (error != 0) {
1403 RDLOG_ERR("failed to get mac addr from %s, error %d",
1404 if_name(delegate_ifp), error);
1405 DTRACE_SKYWALK3(lladdr__copy__fail, if_redirect_t, rd,
1406 ifnet_t, delegate_ifp, int, error);
1407 goto fail;
1408 }
1409 error = ifnet_set_lladdr(interface: ifp, lladdr: mac_addr, ETHER_ADDR_LEN);
1410 if (error != 0) {
1411 RDLOG_ERR("failed to set mac addr for %s, error %d",
1412 if_name(ifp), error);
1413 DTRACE_SKYWALK3(set__lladdr__fail, if_redirect_t, rd,
1414 ifnet_t, ifp, int, error);
1415 goto fail;
1416 }
1417 ASSERT(!rd->rd_mac_addr_set);
1418 rd->rd_mac_addr_set = TRUE;
1419 }
1420 /*
1421 * This is enabled out-of-band from redirect_set_delegate() but we should do
1422 * this here in case we move to a different delegate.
1423 */
1424 if (rd->rd_intf_adv_enabled) {
1425 redirect_delegate_adv_config(delegate_ifp, true);
1426 }
1427 ifnet_set_detach_notify(ifp: delegate_ifp, cb: redirect_detach_notify, arg: rd);
1428 rd->rd_detach_notify_set = TRUE;
1429
1430 /*
1431 * Check that the delegate is still attached. If not, the detach notify above
1432 * could've been missed and we would have to cleanup everything here.
1433 */
1434 if (!ifnet_is_attached(delegate_ifp, refio: 0)) {
1435 RDLOG_ERR("delegate %s detached during setup", if_name(delegate_ifp));
1436 DTRACE_SKYWALK2(delegate__detached, if_redirect_t, rd,
1437 ifnet_t, delegate_ifp);
1438 error = ENXIO;
1439 goto fail;
1440 }
1441 RD_UNLOCK(rd);
1442 return 0;
1443
1444fail:
1445 redirect_clear_delegate_locked(rd);
1446 RD_UNLOCK(rd);
1447 return error;
1448}
1449
1450static void
1451redirect_clear_delegate_locked(if_redirect_t rd)
1452{
1453 ifnet_t ifp = rd->rd_ifp;
1454 ifnet_t delegate_ifp = rd->rd_delegate_ifp;
1455 int error;
1456
1457 if (rd->rd_detach_notify_set) {
1458 ASSERT(delegate_ifp != NULL);
1459 ifnet_set_detach_notify(ifp: delegate_ifp, NULL, NULL);
1460 rd->rd_detach_notify_set = FALSE;
1461 }
1462 if (rd->rd_intf_adv_enabled && delegate_ifp != NULL) {
1463 redirect_delegate_adv_config(delegate_ifp, false);
1464 /*
1465 * We don't clear rd_intf_adv_enabled because we want to reenable
1466 * advisory after moving to a different delegate.
1467 */
1468 }
1469 if (rd->rd_ftype == IFRTYPE_FAMILY_ETHERNET && rd->rd_mac_addr_set) {
1470 ASSERT(delegate_ifp != NULL);
1471 error = ifnet_set_lladdr(interface: ifp, lladdr: default_mac, ETHER_ADDR_LEN);
1472 if (error != 0) {
1473 RDLOG_ERR("failed to set mac addr for %s, error %d",
1474 if_name(ifp), error);
1475 DTRACE_SKYWALK3(set__lladdr__fail, if_redirect_t, rd,
1476 ifnet_t, ifp, int, error);
1477 }
1478 rd->rd_mac_addr_set = FALSE;
1479 }
1480 if (rd->rd_delegate_set) {
1481 ASSERT(delegate_ifp != NULL);
1482 (void) ifnet_set_delegate(ifp, NULL);
1483 rd->rd_delegate_set = FALSE;
1484 }
1485 if (rd->rd_fsw_rx_cb_set) {
1486 ASSERT(delegate_ifp != NULL);
1487 (void) ifnet_set_flowswitch_rx_callback(ifp: delegate_ifp, NULL, NULL);
1488 rd->rd_fsw_rx_cb_set = FALSE;
1489 }
1490 if (rd->rd_delegate_ref) {
1491 ASSERT(delegate_ifp != NULL);
1492 rd->rd_delegate_ifp = NULL;
1493 ifnet_decr_iorefcnt(delegate_ifp);
1494 rd->rd_delegate_ref = FALSE;
1495 }
1496 if (rd->rd_delegate_parent_set) {
1497 ASSERT(delegate_ifp != NULL);
1498 ifnet_set_delegate_parent(difp: delegate_ifp, NULL);
1499 rd->rd_delegate_parent_set = FALSE;
1500 }
1501 if (rd->rd_self_ref) {
1502 ifnet_decr_iorefcnt(ifp);
1503 rd->rd_self_ref = FALSE;
1504 }
1505}
1506
1507static void
1508redirect_clear_delegate(if_redirect_t rd)
1509{
1510 RD_LOCK(rd);
1511 redirect_clear_delegate_locked(rd);
1512 RD_UNLOCK(rd);
1513}
1514
1515static int
1516redirect_ioctl_set_delegate(ifnet_t ifp, user_addr_t user_addr, uint64_t len)
1517{
1518 if_redirect_t rd = NULL;
1519 struct if_redirect_request ifrr;
1520 ifnet_t delegate_ifp = NULL;
1521 int error;
1522
1523 error = if_redirect_request_copyin(user_addr, ifrr: &ifrr, len);
1524 if (error != 0) {
1525 RDLOG_ERR("if_redirect_request_copyin failed: error %d", error);
1526 DTRACE_SKYWALK4(copyin__failed, ifnet_t, ifp, user_addr_t, user_addr,
1527 uint64_t, len, int, error);
1528 goto done;
1529 }
1530 if (ifrr.ifrr_delegate_name[0] == '\0') {
1531 RDLOG_ERR("NULL delegate name");
1532 DTRACE_SKYWALK1(null__delegate, ifnet_t, ifp);
1533 error = EINVAL;
1534 goto done;
1535 }
1536 /* ensure null termination */
1537 ifrr.ifrr_delegate_name[IFNAMSIZ - 1] = '\0';
1538 delegate_ifp = ifunit_ref(ifrr.ifrr_delegate_name);
1539 if (delegate_ifp == NULL) {
1540 RDLOG_ERR("delegate %s not found", ifrr.ifrr_delegate_name);
1541 DTRACE_SKYWALK2(invalid__name, ifnet_t, ifp, char *,
1542 ifrr.ifrr_delegate_name);
1543 error = ENOENT;
1544 goto done;
1545 }
1546 rd = ifnet_get_if_redirect(ifp);
1547 if (rd == NULL) {
1548 RDLOG_ERR("rd is NULL");
1549 DTRACE_SKYWALK1(null__rd, ifnet_t, ifp);
1550 error = ENOENT;
1551 goto done;
1552 }
1553 /* Verify that the delegate type is supported */
1554 if (rd->rd_ftype == IFRTYPE_FAMILY_ETHERNET) {
1555 if (delegate_ifp->if_family != IFNET_FAMILY_ETHERNET) {
1556 RDLOG_ERR("%s's family %d not compatible "
1557 "with ethernet functional type", if_name(delegate_ifp),
1558 delegate_ifp->if_family);
1559 DTRACE_SKYWALK2(delegate__incompatible__ether, if_redirect_t, rd,
1560 ifnet_t, delegate_ifp);
1561 error = EINVAL;
1562 goto done;
1563 }
1564 if (ifnet_is_low_latency(ifp: delegate_ifp)) {
1565 RDLOG_ERR("low latency %s cannot be a delegate",
1566 if_name(delegate_ifp));
1567 DTRACE_SKYWALK2(delegate__is__ll, if_redirect_t, rd,
1568 ifnet_t, delegate_ifp);
1569 error = EINVAL;
1570 goto done;
1571 }
1572 } else {
1573 ASSERT(rd->rd_ftype == IFRTYPE_FAMILY_CELLULAR);
1574 if (delegate_ifp->if_family != IFNET_FAMILY_CELLULAR &&
1575 delegate_ifp->if_family != IFNET_FAMILY_UTUN &&
1576 delegate_ifp->if_family != IFNET_FAMILY_IPSEC) {
1577 RDLOG_ERR("%s's family %d not compatible "
1578 "with cellular functional type", if_name(delegate_ifp),
1579 delegate_ifp->if_family);
1580 DTRACE_SKYWALK2(delegate__incompatible__cell, if_redirect_t, rd,
1581 ifnet_t, delegate_ifp);
1582 error = EINVAL;
1583 goto done;
1584 }
1585 }
1586 if (delegate_ifp->if_subfamily == IFNET_SUBFAMILY_REDIRECT) {
1587 RDLOG_ERR("delegate %s cannot be redirect", if_name(delegate_ifp));
1588 DTRACE_SKYWALK2(delegate__is__redirect, if_redirect_t, rd,
1589 ifnet_t, delegate_ifp);
1590 error = EINVAL;
1591 goto done;
1592 }
1593 error = redirect_set_delegate(rd, delegate_ifp);
1594done:
1595 if (delegate_ifp != NULL) {
1596 ifnet_decr_iorefcnt(delegate_ifp);
1597 }
1598 return error;
1599}
1600
1601static int
1602redirect_set_drvspec(ifnet_t ifp, uint64_t cmd, uint64_t len,
1603 user_addr_t user_addr)
1604{
1605 int error;
1606
1607 switch (cmd) {
1608 case RD_S_CMD_SET_DELEGATE:
1609 error = redirect_ioctl_set_delegate(ifp, user_addr, len);
1610 break;
1611 default:
1612 error = EOPNOTSUPP;
1613 break;
1614 }
1615 return error;
1616}
1617
1618static int
1619redirect_get_drvspec(ifnet_t ifp, uint64_t cmd, uint64_t len,
1620 user_addr_t user_addr)
1621{
1622#pragma unused(ifp, cmd, len, user_addr)
1623 return 0;
1624}
1625
1626union ifdrvu {
1627 struct ifdrv32 *ifdrvu_32;
1628 struct ifdrv64 *ifdrvu_64;
1629 void *ifdrvu_p;
1630};
1631
1632static errno_t
1633redirect_ioctl(ifnet_t ifp, u_long cmd, void *data)
1634{
1635 if_redirect_t rd = NULL;
1636 struct ifreq *ifr = NULL;
1637 union ifdrvu drv;
1638 uint64_t drv_cmd;
1639 uint64_t drv_len;
1640 boolean_t drv_set_command = FALSE;
1641 user_addr_t user_addr;
1642 int error = 0;
1643
1644 rd = ifnet_get_if_redirect(ifp);
1645 if (rd == NULL) {
1646 RDLOG_ERR("rd is NULL");
1647 DTRACE_SKYWALK1(null__rd, ifnet_t, ifp);
1648 return ENXIO;
1649 }
1650 RD_LOCK(rd);
1651 if (rd->rd_detaching) {
1652 RDLOG_ERR("%s is detaching", rd->rd_name);
1653 DTRACE_SKYWALK1(detaching, if_redirect_t, rd);
1654 RD_UNLOCK(rd);
1655 return ENXIO;
1656 }
1657 RD_UNLOCK(rd);
1658
1659 ifr = (struct ifreq *)data;
1660
1661 switch (cmd) {
1662 case SIOCSIFADDR:
1663 ifnet_set_flags(interface: ifp, IFF_UP, IFF_UP);
1664 break;
1665 case SIOCGIFMEDIA32:
1666 case SIOCGIFMEDIA64: {
1667 struct ifmediareq *ifmr;
1668
1669 RD_LOCK(rd);
1670 if (rd->rd_ftype != IFRTYPE_FAMILY_ETHERNET) {
1671 DTRACE_SKYWALK1(not__ether, if_redirect_t, rd);
1672 RD_UNLOCK(rd);
1673 return EOPNOTSUPP;
1674 }
1675 ifmr = (struct ifmediareq *)data;
1676 ifmr->ifm_current = IFM_ETHER;
1677 ifmr->ifm_mask = 0;
1678 ifmr->ifm_status = (IFM_AVALID | IFM_ACTIVE);
1679 ifmr->ifm_active = IFM_ETHER;
1680 ifmr->ifm_count = 1;
1681
1682 user_addr = (cmd == SIOCGIFMEDIA64) ?
1683 ((struct ifmediareq64 *)ifmr)->ifmu_ulist :
1684 CAST_USER_ADDR_T(((struct ifmediareq32 *)ifmr)->ifmu_ulist);
1685 if (user_addr != USER_ADDR_NULL) {
1686 error = copyout(&ifmr->ifm_current, user_addr, sizeof(int));
1687 }
1688 RD_UNLOCK(rd);
1689 break;
1690 }
1691 case SIOCGIFDEVMTU: {
1692 struct ifdevmtu *devmtu_p;
1693
1694 devmtu_p = &ifr->ifr_devmtu;
1695 devmtu_p->ifdm_current = ifnet_mtu(interface: ifp);
1696 devmtu_p->ifdm_max = redirect_max_mtu(ifp);
1697 devmtu_p->ifdm_min = IF_MINMTU;
1698 break;
1699 }
1700 case SIOCSIFMTU:
1701 if ((unsigned int)ifr->ifr_mtu > redirect_max_mtu(ifp) ||
1702 ifr->ifr_mtu < IF_MINMTU) {
1703 error = EINVAL;
1704 } else {
1705 error = ifnet_set_mtu(interface: ifp, mtu: ifr->ifr_mtu);
1706 }
1707 break;
1708 case SIOCSIFFLAGS:
1709 if ((ifp->if_flags & IFF_UP) != 0) {
1710 /* marked up, set running if not already set */
1711 if ((ifp->if_flags & IFF_RUNNING) == 0) {
1712 /* set running */
1713 error = ifnet_set_flags(interface: ifp, IFF_RUNNING,
1714 IFF_RUNNING);
1715 }
1716 } else if ((ifp->if_flags & IFF_RUNNING) != 0) {
1717 /* marked down, clear running */
1718 error = ifnet_set_flags(interface: ifp, new_flags: 0, IFF_RUNNING);
1719 }
1720 break;
1721 case SIOCSDRVSPEC32:
1722 case SIOCSDRVSPEC64:
1723 error = proc_suser(p: current_proc());
1724 if (error != 0) {
1725 break;
1726 }
1727 drv_set_command = TRUE;
1728 OS_FALLTHROUGH;
1729 case SIOCGDRVSPEC32:
1730 case SIOCGDRVSPEC64:
1731 drv.ifdrvu_p = data;
1732 if (cmd == SIOCGDRVSPEC32 || cmd == SIOCSDRVSPEC32) {
1733 drv_cmd = drv.ifdrvu_32->ifd_cmd;
1734 drv_len = drv.ifdrvu_32->ifd_len;
1735 user_addr = CAST_USER_ADDR_T(drv.ifdrvu_32->ifd_data);
1736 } else {
1737 drv_cmd = drv.ifdrvu_64->ifd_cmd;
1738 drv_len = drv.ifdrvu_64->ifd_len;
1739 user_addr = drv.ifdrvu_64->ifd_data;
1740 }
1741 if (drv_set_command) {
1742 error = redirect_set_drvspec(ifp, cmd: drv_cmd, len: drv_len,
1743 user_addr);
1744 } else {
1745 error = redirect_get_drvspec(ifp, cmd: drv_cmd, len: drv_len,
1746 user_addr);
1747 }
1748 break;
1749 case SIOCADDMULTI:
1750 case SIOCDELMULTI:
1751 error = 0;
1752 break;
1753
1754 default:
1755 error = EOPNOTSUPP;
1756 break;
1757 }
1758
1759 return error;
1760}
1761
1762static void
1763redirect_if_free(ifnet_t ifp)
1764{
1765 if_redirect_t rd = NULL;
1766
1767 if (ifp == NULL) {
1768 RDLOG_ERR("ifp is NULL");
1769 DTRACE_SKYWALK(null__ifp);
1770 return;
1771 }
1772 rd = ifnet_get_if_redirect(ifp);
1773 if (rd == NULL) {
1774 RDLOG_ERR("rd is NULL");
1775 DTRACE_SKYWALK1(null__rd, ifnet_t, ifp);
1776 return;
1777 }
1778 RD_LOCK(rd);
1779 ifp->if_softc = NULL;
1780 VERIFY(rd->rd_doorbell_tcall == NULL);
1781 RD_UNLOCK(rd);
1782 redirect_release(rd);
1783 ifnet_release(interface: ifp);
1784 return;
1785}
1786
1787/*
1788 * Network interface functions
1789 */
1790static errno_t
1791redirect_demux(__unused ifnet_t ifp, mbuf_t data, __unused char *frame_header,
1792 protocol_family_t *protocol)
1793{
1794 struct ip *ip;
1795 u_int ip_version;
1796
1797 while (data != NULL && mbuf_len(mbuf: data) < 1) {
1798 data = mbuf_next(mbuf: data);
1799 }
1800
1801 if (data == NULL) {
1802 RDLOG_DBG("data is NULL");
1803 DTRACE_SKYWALK(null__data);
1804 return ENOENT;
1805 }
1806
1807 ip = mtod(data, struct ip *);
1808 ip_version = ip->ip_v;
1809
1810 switch (ip_version) {
1811 case 4:
1812 *protocol = PF_INET;
1813 return 0;
1814 case 6:
1815 *protocol = PF_INET6;
1816 return 0;
1817 default:
1818 *protocol = PF_UNSPEC;
1819 break;
1820 }
1821
1822 return 0;
1823}
1824
1825static errno_t
1826redirect_add_proto(__unused ifnet_t interface, protocol_family_t protocol,
1827 __unused const struct ifnet_demux_desc *demux_array,
1828 __unused uint32_t demux_count)
1829{
1830 switch (protocol) {
1831 case PF_INET:
1832 return 0;
1833 case PF_INET6:
1834 return 0;
1835 default:
1836 break;
1837 }
1838
1839 return ENOPROTOOPT;
1840}
1841
1842static errno_t
1843redirect_del_proto(__unused ifnet_t interface,
1844 __unused protocol_family_t protocol)
1845{
1846 return 0;
1847}
1848
1849__private_extern__ void
1850if_redirect_init(void)
1851{
1852 int error;
1853
1854 redirect_log_handle = os_log_create(subsystem: "com.apple.xnu.net.redirect", category: "redirect");
1855 (void)redirect_register_nexus_domain_provider();
1856 error = if_clone_attach(&redirect_cloner);
1857 if (error != 0) {
1858 return;
1859 }
1860 return;
1861}
1862