+ return EINVAL;
+ }
+ return dlil_output(interface, protocol_family, m, NULL, NULL, 1, NULL);
+}
+
+errno_t
+ifnet_set_mtu(ifnet_t interface, u_int32_t mtu)
+{
+ if (interface == NULL) {
+ return EINVAL;
+ }
+
+ interface->if_mtu = mtu;
+ return 0;
+}
+
+u_int32_t
+ifnet_mtu(ifnet_t interface)
+{
+ return (interface == NULL) ? 0 : interface->if_mtu;
+}
+
+u_char
+ifnet_type(ifnet_t interface)
+{
+ return (interface == NULL) ? 0 : interface->if_data.ifi_type;
+}
+
+errno_t
+ifnet_set_addrlen(ifnet_t interface, u_char addrlen)
+{
+ if (interface == NULL) {
+ return EINVAL;
+ }
+
+ interface->if_data.ifi_addrlen = addrlen;
+ return 0;
+}
+
+u_char
+ifnet_addrlen(ifnet_t interface)
+{
+ return (interface == NULL) ? 0 : interface->if_data.ifi_addrlen;
+}
+
+errno_t
+ifnet_set_hdrlen(ifnet_t interface, u_char hdrlen)
+{
+ if (interface == NULL) {
+ return EINVAL;
+ }
+
+ interface->if_data.ifi_hdrlen = hdrlen;
+ return 0;
+}
+
+u_char
+ifnet_hdrlen(ifnet_t interface)
+{
+ return (interface == NULL) ? 0 : interface->if_data.ifi_hdrlen;
+}
+
+errno_t
+ifnet_set_metric(ifnet_t interface, u_int32_t metric)
+{
+ if (interface == NULL) {
+ return EINVAL;
+ }
+
+ interface->if_data.ifi_metric = metric;
+ return 0;
+}
+
+u_int32_t
+ifnet_metric(ifnet_t interface)
+{
+ return (interface == NULL) ? 0 : interface->if_data.ifi_metric;
+}
+
+errno_t
+ifnet_set_baudrate(struct ifnet *ifp, u_int64_t baudrate)
+{
+ if (ifp == NULL) {
+ return EINVAL;
+ }
+
+ ifp->if_output_bw.max_bw = ifp->if_input_bw.max_bw =
+ ifp->if_output_bw.eff_bw = ifp->if_input_bw.eff_bw = baudrate;
+
+ /* Pin if_baudrate to 32 bits until we can change the storage size */
+ ifp->if_baudrate = (baudrate > 0xFFFFFFFF) ? 0xFFFFFFFF : baudrate;
+
+ return 0;
+}
+
+u_int64_t
+ifnet_baudrate(struct ifnet *ifp)
+{
+ return (ifp == NULL) ? 0 : ifp->if_baudrate;
+}
+
+errno_t
+ifnet_set_bandwidths(struct ifnet *ifp, struct if_bandwidths *output_bw,
+ struct if_bandwidths *input_bw)
+{
+ if (ifp == NULL) {
+ return EINVAL;
+ }
+
+ /* set input values first (if any), as output values depend on them */
+ if (input_bw != NULL) {
+ (void) ifnet_set_input_bandwidths(ifp, input_bw);
+ }
+
+ if (output_bw != NULL) {
+ (void) ifnet_set_output_bandwidths(ifp, output_bw, FALSE);
+ }
+
+ return 0;
+}
+
+static void
+ifnet_set_link_status_outbw(struct ifnet *ifp)
+{
+ struct if_wifi_status_v1 *sr;
+ sr = &ifp->if_link_status->ifsr_u.ifsr_wifi.if_wifi_u.if_status_v1;
+ if (ifp->if_output_bw.eff_bw != 0) {
+ sr->valid_bitmask |=
+ IF_WIFI_UL_EFFECTIVE_BANDWIDTH_VALID;
+ sr->ul_effective_bandwidth =
+ ifp->if_output_bw.eff_bw;
+ }
+ if (ifp->if_output_bw.max_bw != 0) {
+ sr->valid_bitmask |=
+ IF_WIFI_UL_MAX_BANDWIDTH_VALID;
+ sr->ul_max_bandwidth =
+ ifp->if_output_bw.max_bw;
+ }
+}
+
+errno_t
+ifnet_set_output_bandwidths(struct ifnet *ifp, struct if_bandwidths *bw,
+ boolean_t locked)
+{
+ struct if_bandwidths old_bw;
+ struct ifclassq *ifq;
+ u_int64_t br;
+
+ VERIFY(ifp != NULL && bw != NULL);
+
+ ifq = &ifp->if_snd;
+ if (!locked) {
+ IFCQ_LOCK(ifq);
+ }
+ IFCQ_LOCK_ASSERT_HELD(ifq);
+
+ old_bw = ifp->if_output_bw;
+ if (bw->eff_bw != 0) {
+ ifp->if_output_bw.eff_bw = bw->eff_bw;
+ }
+ if (bw->max_bw != 0) {
+ ifp->if_output_bw.max_bw = bw->max_bw;
+ }
+ if (ifp->if_output_bw.eff_bw > ifp->if_output_bw.max_bw) {
+ ifp->if_output_bw.max_bw = ifp->if_output_bw.eff_bw;
+ } else if (ifp->if_output_bw.eff_bw == 0) {
+ ifp->if_output_bw.eff_bw = ifp->if_output_bw.max_bw;
+ }
+
+ /* Pin if_baudrate to 32 bits */
+ br = MAX(ifp->if_output_bw.max_bw, ifp->if_input_bw.max_bw);
+ if (br != 0) {
+ ifp->if_baudrate = (br > 0xFFFFFFFF) ? 0xFFFFFFFF : br;
+ }
+
+ /* Adjust queue parameters if needed */
+ if (old_bw.eff_bw != ifp->if_output_bw.eff_bw ||
+ old_bw.max_bw != ifp->if_output_bw.max_bw) {
+ ifnet_update_sndq(ifq, CLASSQ_EV_LINK_BANDWIDTH);
+ }
+
+ if (!locked) {
+ IFCQ_UNLOCK(ifq);
+ }
+
+ /*
+ * If this is a Wifi interface, update the values in
+ * if_link_status structure also.
+ */
+ if (IFNET_IS_WIFI(ifp) && ifp->if_link_status != NULL) {
+ lck_rw_lock_exclusive(&ifp->if_link_status_lock);
+ ifnet_set_link_status_outbw(ifp);
+ lck_rw_done(&ifp->if_link_status_lock);
+ }
+
+ return 0;
+}
+
+static void
+ifnet_set_link_status_inbw(struct ifnet *ifp)
+{
+ struct if_wifi_status_v1 *sr;
+
+ sr = &ifp->if_link_status->ifsr_u.ifsr_wifi.if_wifi_u.if_status_v1;
+ if (ifp->if_input_bw.eff_bw != 0) {
+ sr->valid_bitmask |=
+ IF_WIFI_DL_EFFECTIVE_BANDWIDTH_VALID;
+ sr->dl_effective_bandwidth =
+ ifp->if_input_bw.eff_bw;
+ }
+ if (ifp->if_input_bw.max_bw != 0) {
+ sr->valid_bitmask |=
+ IF_WIFI_DL_MAX_BANDWIDTH_VALID;
+ sr->dl_max_bandwidth = ifp->if_input_bw.max_bw;
+ }
+}
+
+errno_t
+ifnet_set_input_bandwidths(struct ifnet *ifp, struct if_bandwidths *bw)
+{
+ struct if_bandwidths old_bw;
+
+ VERIFY(ifp != NULL && bw != NULL);
+
+ old_bw = ifp->if_input_bw;
+ if (bw->eff_bw != 0) {
+ ifp->if_input_bw.eff_bw = bw->eff_bw;
+ }
+ if (bw->max_bw != 0) {
+ ifp->if_input_bw.max_bw = bw->max_bw;
+ }
+ if (ifp->if_input_bw.eff_bw > ifp->if_input_bw.max_bw) {
+ ifp->if_input_bw.max_bw = ifp->if_input_bw.eff_bw;
+ } else if (ifp->if_input_bw.eff_bw == 0) {
+ ifp->if_input_bw.eff_bw = ifp->if_input_bw.max_bw;
+ }
+
+ if (IFNET_IS_WIFI(ifp) && ifp->if_link_status != NULL) {
+ lck_rw_lock_exclusive(&ifp->if_link_status_lock);
+ ifnet_set_link_status_inbw(ifp);
+ lck_rw_done(&ifp->if_link_status_lock);
+ }
+
+ if (old_bw.eff_bw != ifp->if_input_bw.eff_bw ||
+ old_bw.max_bw != ifp->if_input_bw.max_bw) {
+ ifnet_update_rcv(ifp, CLASSQ_EV_LINK_BANDWIDTH);
+ }
+
+ return 0;
+}
+
+u_int64_t
+ifnet_output_linkrate(struct ifnet *ifp)
+{
+ struct ifclassq *ifq = &ifp->if_snd;
+ u_int64_t rate;
+
+ IFCQ_LOCK_ASSERT_HELD(ifq);
+
+ rate = ifp->if_output_bw.eff_bw;
+ if (IFCQ_TBR_IS_ENABLED(ifq)) {
+ u_int64_t tbr_rate = ifp->if_snd.ifcq_tbr.tbr_rate_raw;
+ VERIFY(tbr_rate > 0);
+ rate = MIN(rate, ifp->if_snd.ifcq_tbr.tbr_rate_raw);
+ }
+
+ return rate;
+}
+
+u_int64_t
+ifnet_input_linkrate(struct ifnet *ifp)
+{
+ return ifp->if_input_bw.eff_bw;
+}
+
+errno_t
+ifnet_bandwidths(struct ifnet *ifp, struct if_bandwidths *output_bw,
+ struct if_bandwidths *input_bw)
+{
+ if (ifp == NULL) {
+ return EINVAL;
+ }
+
+ if (output_bw != NULL) {
+ *output_bw = ifp->if_output_bw;
+ }
+ if (input_bw != NULL) {
+ *input_bw = ifp->if_input_bw;
+ }
+
+ return 0;
+}
+
+errno_t
+ifnet_set_latencies(struct ifnet *ifp, struct if_latencies *output_lt,
+ struct if_latencies *input_lt)
+{
+ if (ifp == NULL) {
+ return EINVAL;
+ }
+
+ if (output_lt != NULL) {
+ (void) ifnet_set_output_latencies(ifp, output_lt, FALSE);
+ }
+
+ if (input_lt != NULL) {
+ (void) ifnet_set_input_latencies(ifp, input_lt);
+ }
+
+ return 0;
+}
+
+errno_t
+ifnet_set_output_latencies(struct ifnet *ifp, struct if_latencies *lt,
+ boolean_t locked)
+{
+ struct if_latencies old_lt;
+ struct ifclassq *ifq;
+
+ VERIFY(ifp != NULL && lt != NULL);
+
+ ifq = &ifp->if_snd;
+ if (!locked) {
+ IFCQ_LOCK(ifq);
+ }
+ IFCQ_LOCK_ASSERT_HELD(ifq);
+
+ old_lt = ifp->if_output_lt;
+ if (lt->eff_lt != 0) {
+ ifp->if_output_lt.eff_lt = lt->eff_lt;
+ }
+ if (lt->max_lt != 0) {
+ ifp->if_output_lt.max_lt = lt->max_lt;
+ }
+ if (ifp->if_output_lt.eff_lt > ifp->if_output_lt.max_lt) {
+ ifp->if_output_lt.max_lt = ifp->if_output_lt.eff_lt;
+ } else if (ifp->if_output_lt.eff_lt == 0) {
+ ifp->if_output_lt.eff_lt = ifp->if_output_lt.max_lt;
+ }
+
+ /* Adjust queue parameters if needed */
+ if (old_lt.eff_lt != ifp->if_output_lt.eff_lt ||
+ old_lt.max_lt != ifp->if_output_lt.max_lt) {
+ ifnet_update_sndq(ifq, CLASSQ_EV_LINK_LATENCY);
+ }
+
+ if (!locked) {
+ IFCQ_UNLOCK(ifq);
+ }
+
+ return 0;
+}
+
+errno_t
+ifnet_set_input_latencies(struct ifnet *ifp, struct if_latencies *lt)
+{
+ struct if_latencies old_lt;
+
+ VERIFY(ifp != NULL && lt != NULL);
+
+ old_lt = ifp->if_input_lt;
+ if (lt->eff_lt != 0) {
+ ifp->if_input_lt.eff_lt = lt->eff_lt;
+ }
+ if (lt->max_lt != 0) {
+ ifp->if_input_lt.max_lt = lt->max_lt;
+ }
+ if (ifp->if_input_lt.eff_lt > ifp->if_input_lt.max_lt) {
+ ifp->if_input_lt.max_lt = ifp->if_input_lt.eff_lt;
+ } else if (ifp->if_input_lt.eff_lt == 0) {
+ ifp->if_input_lt.eff_lt = ifp->if_input_lt.max_lt;
+ }
+
+ if (old_lt.eff_lt != ifp->if_input_lt.eff_lt ||
+ old_lt.max_lt != ifp->if_input_lt.max_lt) {
+ ifnet_update_rcv(ifp, CLASSQ_EV_LINK_LATENCY);
+ }
+
+ return 0;
+}
+
+errno_t
+ifnet_latencies(struct ifnet *ifp, struct if_latencies *output_lt,
+ struct if_latencies *input_lt)
+{
+ if (ifp == NULL) {
+ return EINVAL;
+ }
+
+ if (output_lt != NULL) {
+ *output_lt = ifp->if_output_lt;
+ }
+ if (input_lt != NULL) {
+ *input_lt = ifp->if_input_lt;
+ }
+
+ return 0;
+}
+
+errno_t
+ifnet_set_poll_params(struct ifnet *ifp, struct ifnet_poll_params *p)
+{
+ errno_t err;
+
+ if (ifp == NULL) {
+ return EINVAL;
+ } else if (!ifnet_is_attached(ifp, 1)) {
+ return ENXIO;
+ }
+
+ err = dlil_rxpoll_set_params(ifp, p, FALSE);
+
+ /* Release the io ref count */
+ ifnet_decr_iorefcnt(ifp);
+
+ return err;
+}
+
+errno_t
+ifnet_poll_params(struct ifnet *ifp, struct ifnet_poll_params *p)
+{
+ errno_t err;
+
+ if (ifp == NULL || p == NULL) {
+ return EINVAL;
+ } else if (!ifnet_is_attached(ifp, 1)) {
+ return ENXIO;
+ }
+
+ err = dlil_rxpoll_get_params(ifp, p);
+
+ /* Release the io ref count */
+ ifnet_decr_iorefcnt(ifp);
+
+ return err;
+}
+
+errno_t
+ifnet_stat_increment(struct ifnet *ifp,
+ const struct ifnet_stat_increment_param *s)
+{
+ if (ifp == NULL) {
+ return EINVAL;
+ }
+
+ if (s->packets_in != 0) {
+ atomic_add_64(&ifp->if_data.ifi_ipackets, s->packets_in);
+ }
+ if (s->bytes_in != 0) {
+ atomic_add_64(&ifp->if_data.ifi_ibytes, s->bytes_in);
+ }
+ if (s->errors_in != 0) {
+ atomic_add_64(&ifp->if_data.ifi_ierrors, s->errors_in);
+ }
+
+ if (s->packets_out != 0) {
+ atomic_add_64(&ifp->if_data.ifi_opackets, s->packets_out);
+ }
+ if (s->bytes_out != 0) {
+ atomic_add_64(&ifp->if_data.ifi_obytes, s->bytes_out);
+ }
+ if (s->errors_out != 0) {
+ atomic_add_64(&ifp->if_data.ifi_oerrors, s->errors_out);
+ }
+
+ if (s->collisions != 0) {
+ atomic_add_64(&ifp->if_data.ifi_collisions, s->collisions);
+ }
+ if (s->dropped != 0) {
+ atomic_add_64(&ifp->if_data.ifi_iqdrops, s->dropped);
+ }
+
+ /* Touch the last change time. */
+ TOUCHLASTCHANGE(&ifp->if_lastchange);
+
+ if (ifp->if_data_threshold != 0) {
+ ifnet_notify_data_threshold(ifp);
+ }
+
+ return 0;
+}
+
+errno_t
+ifnet_stat_increment_in(struct ifnet *ifp, u_int32_t packets_in,
+ u_int32_t bytes_in, u_int32_t errors_in)
+{
+ if (ifp == NULL) {
+ return EINVAL;
+ }
+
+ if (packets_in != 0) {
+ atomic_add_64(&ifp->if_data.ifi_ipackets, packets_in);
+ }
+ if (bytes_in != 0) {
+ atomic_add_64(&ifp->if_data.ifi_ibytes, bytes_in);
+ }
+ if (errors_in != 0) {
+ atomic_add_64(&ifp->if_data.ifi_ierrors, errors_in);
+ }
+
+ TOUCHLASTCHANGE(&ifp->if_lastchange);
+
+ if (ifp->if_data_threshold != 0) {
+ ifnet_notify_data_threshold(ifp);
+ }
+
+ return 0;
+}
+
+errno_t
+ifnet_stat_increment_out(struct ifnet *ifp, u_int32_t packets_out,
+ u_int32_t bytes_out, u_int32_t errors_out)
+{
+ if (ifp == NULL) {
+ return EINVAL;
+ }
+
+ if (packets_out != 0) {
+ atomic_add_64(&ifp->if_data.ifi_opackets, packets_out);
+ }
+ if (bytes_out != 0) {
+ atomic_add_64(&ifp->if_data.ifi_obytes, bytes_out);
+ }
+ if (errors_out != 0) {
+ atomic_add_64(&ifp->if_data.ifi_oerrors, errors_out);
+ }
+
+ TOUCHLASTCHANGE(&ifp->if_lastchange);
+
+ if (ifp->if_data_threshold != 0) {
+ ifnet_notify_data_threshold(ifp);
+ }
+
+ return 0;
+}
+
+errno_t
+ifnet_set_stat(struct ifnet *ifp, const struct ifnet_stats_param *s)
+{
+ if (ifp == NULL) {
+ return EINVAL;
+ }
+
+ atomic_set_64(&ifp->if_data.ifi_ipackets, s->packets_in);
+ atomic_set_64(&ifp->if_data.ifi_ibytes, s->bytes_in);
+ atomic_set_64(&ifp->if_data.ifi_imcasts, s->multicasts_in);
+ atomic_set_64(&ifp->if_data.ifi_ierrors, s->errors_in);
+
+ atomic_set_64(&ifp->if_data.ifi_opackets, s->packets_out);
+ atomic_set_64(&ifp->if_data.ifi_obytes, s->bytes_out);
+ atomic_set_64(&ifp->if_data.ifi_omcasts, s->multicasts_out);
+ atomic_set_64(&ifp->if_data.ifi_oerrors, s->errors_out);
+
+ atomic_set_64(&ifp->if_data.ifi_collisions, s->collisions);
+ atomic_set_64(&ifp->if_data.ifi_iqdrops, s->dropped);
+ atomic_set_64(&ifp->if_data.ifi_noproto, s->no_protocol);
+
+ /* Touch the last change time. */
+ TOUCHLASTCHANGE(&ifp->if_lastchange);
+
+ if (ifp->if_data_threshold != 0) {
+ ifnet_notify_data_threshold(ifp);
+ }
+
+ return 0;
+}
+
+errno_t
+ifnet_stat(struct ifnet *ifp, struct ifnet_stats_param *s)
+{
+ if (ifp == NULL) {
+ return EINVAL;
+ }
+
+ atomic_get_64(s->packets_in, &ifp->if_data.ifi_ipackets);
+ atomic_get_64(s->bytes_in, &ifp->if_data.ifi_ibytes);
+ atomic_get_64(s->multicasts_in, &ifp->if_data.ifi_imcasts);
+ atomic_get_64(s->errors_in, &ifp->if_data.ifi_ierrors);
+
+ atomic_get_64(s->packets_out, &ifp->if_data.ifi_opackets);
+ atomic_get_64(s->bytes_out, &ifp->if_data.ifi_obytes);
+ atomic_get_64(s->multicasts_out, &ifp->if_data.ifi_omcasts);
+ atomic_get_64(s->errors_out, &ifp->if_data.ifi_oerrors);
+
+ atomic_get_64(s->collisions, &ifp->if_data.ifi_collisions);
+ atomic_get_64(s->dropped, &ifp->if_data.ifi_iqdrops);
+ atomic_get_64(s->no_protocol, &ifp->if_data.ifi_noproto);
+
+ if (ifp->if_data_threshold != 0) {
+ ifnet_notify_data_threshold(ifp);
+ }
+
+ return 0;
+}
+
+errno_t
+ifnet_touch_lastchange(ifnet_t interface)
+{
+ if (interface == NULL) {
+ return EINVAL;
+ }
+
+ TOUCHLASTCHANGE(&interface->if_lastchange);
+
+ return 0;
+}
+
+errno_t
+ifnet_lastchange(ifnet_t interface, struct timeval *last_change)
+{
+ if (interface == NULL) {
+ return EINVAL;
+ }
+
+ *last_change = interface->if_data.ifi_lastchange;
+ /* Crude conversion from uptime to calendar time */
+ last_change->tv_sec += boottime_sec();
+
+ return 0;
+}
+
+errno_t
+ifnet_touch_lastupdown(ifnet_t interface)
+{
+ if (interface == NULL) {
+ return EINVAL;
+ }
+
+ TOUCHLASTCHANGE(&interface->if_lastupdown);
+
+ return 0;
+}
+
+errno_t
+ifnet_updown_delta(ifnet_t interface, struct timeval *updown_delta)
+{
+ if (interface == NULL) {
+ return EINVAL;
+ }
+
+ /* Calculate the delta */
+ updown_delta->tv_sec = net_uptime();
+ if (updown_delta->tv_sec > interface->if_data.ifi_lastupdown.tv_sec) {
+ updown_delta->tv_sec -= interface->if_data.ifi_lastupdown.tv_sec;
+ }
+ updown_delta->tv_usec = 0;
+
+ return 0;
+}
+
+errno_t
+ifnet_get_address_list(ifnet_t interface, ifaddr_t **addresses)
+{
+ return addresses == NULL ? EINVAL :
+ ifnet_get_address_list_family(interface, addresses, 0);
+}
+
+struct ifnet_addr_list {
+ SLIST_ENTRY(ifnet_addr_list) ifal_le;
+ struct ifaddr *ifal_ifa;
+};
+
+errno_t
+ifnet_get_address_list_family(ifnet_t interface, ifaddr_t **addresses,
+ sa_family_t family)
+{
+ return ifnet_get_address_list_family_internal(interface, addresses,
+ family, 0, M_NOWAIT, 0);
+}
+
+errno_t
+ifnet_get_inuse_address_list(ifnet_t interface, ifaddr_t **addresses)
+{
+ return addresses == NULL ? EINVAL :
+ ifnet_get_address_list_family_internal(interface, addresses,
+ 0, 0, M_NOWAIT, 1);
+}
+
+extern uint32_t tcp_find_anypcb_byaddr(struct ifaddr *ifa);
+
+extern uint32_t udp_find_anypcb_byaddr(struct ifaddr *ifa);
+
+__private_extern__ errno_t
+ifnet_get_address_list_family_internal(ifnet_t interface, ifaddr_t **addresses,
+ sa_family_t family, int detached, int how, int return_inuse_addrs)
+{
+ SLIST_HEAD(, ifnet_addr_list) ifal_head;
+ struct ifnet_addr_list *ifal, *ifal_tmp;
+ struct ifnet *ifp;
+ int count = 0;
+ errno_t err = 0;
+ int usecount = 0;
+ int index = 0;
+
+ SLIST_INIT(&ifal_head);
+
+ if (addresses == NULL) {
+ err = EINVAL;
+ goto done;
+ }
+ *addresses = NULL;
+
+ if (detached) {
+ /*
+ * Interface has been detached, so skip the lookup
+ * at ifnet_head and go directly to inner loop.
+ */
+ ifp = interface;
+ if (ifp == NULL) {
+ err = EINVAL;
+ goto done;
+ }
+ goto one;
+ }
+
+ ifnet_head_lock_shared();
+ TAILQ_FOREACH(ifp, &ifnet_head, if_link) {
+ if (interface != NULL && ifp != interface) {
+ continue;
+ }
+one:
+ ifnet_lock_shared(ifp);
+ if (interface == NULL || interface == ifp) {
+ struct ifaddr *ifa;
+ TAILQ_FOREACH(ifa, &ifp->if_addrhead, ifa_link) {
+ IFA_LOCK(ifa);
+ if (family != 0 &&
+ ifa->ifa_addr->sa_family != family) {
+ IFA_UNLOCK(ifa);
+ continue;
+ }
+ MALLOC(ifal, struct ifnet_addr_list *,
+ sizeof(*ifal), M_TEMP, how);
+ if (ifal == NULL) {
+ IFA_UNLOCK(ifa);
+ ifnet_lock_done(ifp);
+ if (!detached) {
+ ifnet_head_done();
+ }
+ err = ENOMEM;
+ goto done;
+ }
+ ifal->ifal_ifa = ifa;
+ IFA_ADDREF_LOCKED(ifa);
+ SLIST_INSERT_HEAD(&ifal_head, ifal, ifal_le);
+ ++count;
+ IFA_UNLOCK(ifa);
+ }
+ }
+ ifnet_lock_done(ifp);
+ if (detached) {
+ break;
+ }
+ }
+ if (!detached) {
+ ifnet_head_done();
+ }
+
+ if (count == 0) {
+ err = ENXIO;
+ goto done;
+ }
+ MALLOC(*addresses, ifaddr_t *, sizeof(ifaddr_t) * (count + 1),
+ M_TEMP, how);
+ if (*addresses == NULL) {
+ err = ENOMEM;
+ goto done;
+ }
+ bzero(*addresses, sizeof(ifaddr_t) * (count + 1));
+
+done:
+ SLIST_FOREACH_SAFE(ifal, &ifal_head, ifal_le, ifal_tmp) {
+ SLIST_REMOVE(&ifal_head, ifal, ifnet_addr_list, ifal_le);
+ if (err == 0) {
+ if (return_inuse_addrs) {
+ usecount = tcp_find_anypcb_byaddr(ifal->ifal_ifa);
+ usecount += udp_find_anypcb_byaddr(ifal->ifal_ifa);
+ if (usecount) {
+ (*addresses)[index] = ifal->ifal_ifa;
+ index++;
+ } else {
+ IFA_REMREF(ifal->ifal_ifa);
+ }
+ } else {
+ (*addresses)[--count] = ifal->ifal_ifa;
+ }
+ } else {
+ IFA_REMREF(ifal->ifal_ifa);
+ }
+ FREE(ifal, M_TEMP);
+ }
+
+ VERIFY(err == 0 || *addresses == NULL);
+ if ((err == 0) && (count) && ((*addresses)[0] == NULL)) {
+ VERIFY(return_inuse_addrs == 1);
+ FREE(*addresses, M_TEMP);
+ err = ENXIO;
+ }
+ return err;
+}
+
+void
+ifnet_free_address_list(ifaddr_t *addresses)
+{
+ int i;
+
+ if (addresses == NULL) {
+ return;
+ }
+
+ for (i = 0; addresses[i] != NULL; i++) {
+ IFA_REMREF(addresses[i]);
+ }
+
+ FREE(addresses, M_TEMP);
+}
+
+void *
+ifnet_lladdr(ifnet_t interface)
+{
+ struct ifaddr *ifa;
+ void *lladdr;
+
+ if (interface == NULL) {
+ return NULL;
+ }
+
+ /*
+ * if_lladdr points to the permanent link address of
+ * the interface and it never gets deallocated; internal
+ * code should simply use IF_LLADDR() for performance.
+ */
+ ifa = interface->if_lladdr;
+ IFA_LOCK_SPIN(ifa);
+ lladdr = LLADDR(SDL((void *)ifa->ifa_addr));
+ IFA_UNLOCK(ifa);
+
+ return lladdr;
+}
+
+errno_t
+ifnet_llbroadcast_copy_bytes(ifnet_t interface, void *addr, size_t buffer_len,
+ size_t *out_len)
+{
+ if (interface == NULL || addr == NULL || out_len == NULL) {
+ return EINVAL;
+ }
+
+ *out_len = interface->if_broadcast.length;
+
+ if (buffer_len < interface->if_broadcast.length) {
+ return EMSGSIZE;
+ }
+
+ if (interface->if_broadcast.length == 0) {
+ return ENXIO;
+ }
+
+ if (interface->if_broadcast.length <=
+ sizeof(interface->if_broadcast.u.buffer)) {
+ bcopy(interface->if_broadcast.u.buffer, addr,
+ interface->if_broadcast.length);
+ } else {
+ bcopy(interface->if_broadcast.u.ptr, addr,
+ interface->if_broadcast.length);
+ }
+
+ return 0;
+}
+
+static errno_t
+ifnet_lladdr_copy_bytes_internal(ifnet_t interface, void *lladdr,
+ size_t lladdr_len, kauth_cred_t *credp)
+{
+ const u_int8_t *bytes;
+ size_t bytes_len;
+ struct ifaddr *ifa;
+ uint8_t sdlbuf[SOCK_MAXADDRLEN + 1];
+ errno_t error = 0;
+
+ /*
+ * Make sure to accomodate the largest possible
+ * size of SA(if_lladdr)->sa_len.
+ */
+ _CASSERT(sizeof(sdlbuf) == (SOCK_MAXADDRLEN + 1));
+
+ if (interface == NULL || lladdr == NULL) {
+ return EINVAL;
+ }
+
+ ifa = interface->if_lladdr;
+ IFA_LOCK_SPIN(ifa);
+ bcopy(ifa->ifa_addr, &sdlbuf, SDL(ifa->ifa_addr)->sdl_len);
+ IFA_UNLOCK(ifa);
+
+ bytes = dlil_ifaddr_bytes(SDL(&sdlbuf), &bytes_len, credp);
+ if (bytes_len != lladdr_len) {
+ bzero(lladdr, lladdr_len);
+ error = EMSGSIZE;
+ } else {
+ bcopy(bytes, lladdr, bytes_len);
+ }
+
+ return error;
+}
+
+errno_t
+ifnet_lladdr_copy_bytes(ifnet_t interface, void *lladdr, size_t length)
+{
+ return ifnet_lladdr_copy_bytes_internal(interface, lladdr, length,
+ NULL);
+}
+
+errno_t
+ifnet_guarded_lladdr_copy_bytes(ifnet_t interface, void *lladdr, size_t length)
+{
+#if CONFIG_MACF
+ kauth_cred_t cred;
+ net_thread_marks_t marks;
+#endif
+ kauth_cred_t *credp;
+ errno_t error;
+
+ credp = NULL;
+#if CONFIG_MACF
+ marks = net_thread_marks_push(NET_THREAD_CKREQ_LLADDR);
+ cred = kauth_cred_proc_ref(current_proc());
+ credp = &cred;
+#else
+ credp = NULL;
+#endif
+
+ error = ifnet_lladdr_copy_bytes_internal(interface, lladdr, length,
+ credp);
+
+#if CONFIG_MACF
+ kauth_cred_unref(credp);
+ net_thread_marks_pop(marks);
+#endif
+
+ return error;
+}
+
+static errno_t
+ifnet_set_lladdr_internal(ifnet_t interface, const void *lladdr,
+ size_t lladdr_len, u_char new_type, int apply_type)
+{
+ struct ifaddr *ifa;
+ errno_t error = 0;
+
+ if (interface == NULL) {
+ return EINVAL;
+ }
+
+ ifnet_head_lock_shared();
+ ifnet_lock_exclusive(interface);
+ if (lladdr_len != 0 &&
+ (lladdr_len != interface->if_addrlen || lladdr == 0)) {
+ ifnet_lock_done(interface);
+ ifnet_head_done();
+ return EINVAL;
+ }
+ ifa = ifnet_addrs[interface->if_index - 1];
+ if (ifa != NULL) {
+ struct sockaddr_dl *sdl;
+
+ IFA_LOCK_SPIN(ifa);
+ sdl = (struct sockaddr_dl *)(void *)ifa->ifa_addr;
+ if (lladdr_len != 0) {
+ bcopy(lladdr, LLADDR(sdl), lladdr_len);
+ } else {
+ bzero(LLADDR(sdl), interface->if_addrlen);
+ }
+ sdl->sdl_alen = lladdr_len;
+
+ if (apply_type) {
+ sdl->sdl_type = new_type;
+ }
+ IFA_UNLOCK(ifa);
+ } else {
+ error = ENXIO;
+ }
+ ifnet_lock_done(interface);
+ ifnet_head_done();
+
+ /* Generate a kernel event */
+ if (error == 0) {
+ intf_event_enqueue_nwk_wq_entry(interface, NULL,
+ INTF_EVENT_CODE_LLADDR_UPDATE);
+ dlil_post_msg(interface, KEV_DL_SUBCLASS,
+ KEV_DL_LINK_ADDRESS_CHANGED, NULL, 0);
+ }
+
+ return error;
+}
+
+errno_t
+ifnet_set_lladdr(ifnet_t interface, const void* lladdr, size_t lladdr_len)
+{
+ return ifnet_set_lladdr_internal(interface, lladdr, lladdr_len, 0, 0);
+}
+
+errno_t
+ifnet_set_lladdr_and_type(ifnet_t interface, const void* lladdr,
+ size_t lladdr_len, u_char type)
+{
+ return ifnet_set_lladdr_internal(interface, lladdr,
+ lladdr_len, type, 1);
+}
+
+errno_t
+ifnet_add_multicast(ifnet_t interface, const struct sockaddr *maddr,
+ ifmultiaddr_t *ifmap)
+{
+ if (interface == NULL || maddr == NULL) {
+ return EINVAL;
+ }
+
+ /* Don't let users screw up protocols' entries. */
+ switch (maddr->sa_family) {
+ case AF_LINK: {
+ const struct sockaddr_dl *sdl =
+ (const struct sockaddr_dl *)(uintptr_t)maddr;
+ if (sdl->sdl_len < sizeof(struct sockaddr_dl) ||
+ (sdl->sdl_nlen + sdl->sdl_alen + sdl->sdl_slen +
+ offsetof(struct sockaddr_dl, sdl_data) > sdl->sdl_len)) {
+ return EINVAL;
+ }
+ break;
+ }
+ case AF_UNSPEC:
+ if (maddr->sa_len < ETHER_ADDR_LEN +
+ offsetof(struct sockaddr, sa_data)) {
+ return EINVAL;
+ }
+ break;
+ default:
+ return EINVAL;
+ }
+
+ return if_addmulti_anon(interface, maddr, ifmap);
+}
+
+errno_t
+ifnet_remove_multicast(ifmultiaddr_t ifma)
+{
+ struct sockaddr *maddr;
+
+ if (ifma == NULL) {
+ return EINVAL;
+ }
+
+ maddr = ifma->ifma_addr;
+ /* Don't let users screw up protocols' entries. */
+ if (maddr->sa_family != AF_UNSPEC && maddr->sa_family != AF_LINK) {
+ return EINVAL;
+ }
+
+ return if_delmulti_anon(ifma->ifma_ifp, maddr);
+}
+
+errno_t
+ifnet_get_multicast_list(ifnet_t ifp, ifmultiaddr_t **addresses)
+{
+ int count = 0;
+ int cmax = 0;
+ struct ifmultiaddr *addr;
+
+ if (ifp == NULL || addresses == NULL) {
+ return EINVAL;
+ }
+
+ ifnet_lock_shared(ifp);
+ LIST_FOREACH(addr, &ifp->if_multiaddrs, ifma_link) {
+ cmax++;
+ }
+
+ MALLOC(*addresses, ifmultiaddr_t *, sizeof(ifmultiaddr_t) * (cmax + 1),
+ M_TEMP, M_NOWAIT);
+ if (*addresses == NULL) {
+ ifnet_lock_done(ifp);
+ return ENOMEM;
+ }
+
+ LIST_FOREACH(addr, &ifp->if_multiaddrs, ifma_link) {
+ if (count + 1 > cmax) {
+ break;
+ }
+ (*addresses)[count] = (ifmultiaddr_t)addr;
+ ifmaddr_reference((*addresses)[count]);
+ count++;
+ }
+ (*addresses)[cmax] = NULL;
+ ifnet_lock_done(ifp);
+
+ return 0;
+}
+
+void
+ifnet_free_multicast_list(ifmultiaddr_t *addresses)
+{
+ int i;
+
+ if (addresses == NULL) {
+ return;
+ }
+
+ for (i = 0; addresses[i] != NULL; i++) {
+ ifmaddr_release(addresses[i]);
+ }
+
+ FREE(addresses, M_TEMP);
+}
+
+errno_t
+ifnet_find_by_name(const char *ifname, ifnet_t *ifpp)
+{
+ struct ifnet *ifp;
+ int namelen;
+
+ if (ifname == NULL) {
+ return EINVAL;
+ }
+
+ namelen = strlen(ifname);
+
+ *ifpp = NULL;
+
+ ifnet_head_lock_shared();
+ TAILQ_FOREACH(ifp, &ifnet_head, if_link) {
+ struct ifaddr *ifa;
+ struct sockaddr_dl *ll_addr;
+
+ ifa = ifnet_addrs[ifp->if_index - 1];
+ if (ifa == NULL) {
+ continue;
+ }
+
+ IFA_LOCK(ifa);
+ ll_addr = (struct sockaddr_dl *)(void *)ifa->ifa_addr;
+
+ if (namelen == ll_addr->sdl_nlen && strncmp(ll_addr->sdl_data,
+ ifname, ll_addr->sdl_nlen) == 0) {
+ IFA_UNLOCK(ifa);
+ *ifpp = ifp;
+ ifnet_reference(*ifpp);
+ break;
+ }
+ IFA_UNLOCK(ifa);
+ }
+ ifnet_head_done();
+
+ return (ifp == NULL) ? ENXIO : 0;
+}
+
+errno_t
+ifnet_list_get(ifnet_family_t family, ifnet_t **list, u_int32_t *count)
+{
+ return ifnet_list_get_common(family, FALSE, list, count);
+}
+
+__private_extern__ errno_t
+ifnet_list_get_all(ifnet_family_t family, ifnet_t **list, u_int32_t *count)
+{
+ return ifnet_list_get_common(family, TRUE, list, count);
+}
+
+struct ifnet_list {
+ SLIST_ENTRY(ifnet_list) ifl_le;
+ struct ifnet *ifl_ifp;
+};
+
+static errno_t
+ifnet_list_get_common(ifnet_family_t family, boolean_t get_all, ifnet_t **list,
+ u_int32_t *count)
+{
+#pragma unused(get_all)
+ SLIST_HEAD(, ifnet_list) ifl_head;
+ struct ifnet_list *ifl, *ifl_tmp;
+ struct ifnet *ifp;
+ int cnt = 0;
+ errno_t err = 0;
+
+ SLIST_INIT(&ifl_head);
+
+ if (list == NULL || count == NULL) {
+ err = EINVAL;
+ goto done;
+ }
+ *count = 0;
+ *list = NULL;
+
+ ifnet_head_lock_shared();
+ TAILQ_FOREACH(ifp, &ifnet_head, if_link) {
+ if (family == IFNET_FAMILY_ANY || ifp->if_family == family) {
+ MALLOC(ifl, struct ifnet_list *, sizeof(*ifl),
+ M_TEMP, M_NOWAIT);
+ if (ifl == NULL) {
+ ifnet_head_done();
+ err = ENOMEM;
+ goto done;
+ }
+ ifl->ifl_ifp = ifp;
+ ifnet_reference(ifp);
+ SLIST_INSERT_HEAD(&ifl_head, ifl, ifl_le);
+ ++cnt;
+ }
+ }
+ ifnet_head_done();
+
+ if (cnt == 0) {
+ err = ENXIO;
+ goto done;
+ }
+
+ MALLOC(*list, ifnet_t *, sizeof(ifnet_t) * (cnt + 1),
+ M_TEMP, M_NOWAIT);
+ if (*list == NULL) {
+ err = ENOMEM;
+ goto done;
+ }
+ bzero(*list, sizeof(ifnet_t) * (cnt + 1));
+ *count = cnt;
+
+done:
+ SLIST_FOREACH_SAFE(ifl, &ifl_head, ifl_le, ifl_tmp) {
+ SLIST_REMOVE(&ifl_head, ifl, ifnet_list, ifl_le);
+ if (err == 0) {
+ (*list)[--cnt] = ifl->ifl_ifp;
+ } else {
+ ifnet_release(ifl->ifl_ifp);
+ }
+ FREE(ifl, M_TEMP);
+ }
+
+ return err;
+}
+
+void
+ifnet_list_free(ifnet_t *interfaces)
+{
+ int i;
+
+ if (interfaces == NULL) {
+ return;
+ }
+
+ for (i = 0; interfaces[i]; i++) {
+ ifnet_release(interfaces[i]);
+ }
+
+ FREE(interfaces, M_TEMP);
+}
+
+/*************************************************************************/
+/* ifaddr_t accessors */
+/*************************************************************************/
+
+errno_t
+ifaddr_reference(ifaddr_t ifa)
+{
+ if (ifa == NULL) {
+ return EINVAL;
+ }
+
+ IFA_ADDREF(ifa);
+ return 0;
+}
+
+errno_t
+ifaddr_release(ifaddr_t ifa)
+{
+ if (ifa == NULL) {
+ return EINVAL;
+ }
+
+ IFA_REMREF(ifa);
+ return 0;
+}
+
+sa_family_t
+ifaddr_address_family(ifaddr_t ifa)
+{
+ sa_family_t family = 0;
+
+ if (ifa != NULL) {
+ IFA_LOCK_SPIN(ifa);
+ if (ifa->ifa_addr != NULL) {
+ family = ifa->ifa_addr->sa_family;
+ }
+ IFA_UNLOCK(ifa);
+ }
+ return family;
+}
+
+errno_t
+ifaddr_address(ifaddr_t ifa, struct sockaddr *out_addr, u_int32_t addr_size)
+{
+ u_int32_t copylen;
+
+ if (ifa == NULL || out_addr == NULL) {
+ return EINVAL;
+ }
+
+ IFA_LOCK_SPIN(ifa);
+ if (ifa->ifa_addr == NULL) {
+ IFA_UNLOCK(ifa);
+ return ENOTSUP;
+ }
+
+ copylen = (addr_size >= ifa->ifa_addr->sa_len) ?
+ ifa->ifa_addr->sa_len : addr_size;
+ bcopy(ifa->ifa_addr, out_addr, copylen);
+
+ if (ifa->ifa_addr->sa_len > addr_size) {
+ IFA_UNLOCK(ifa);
+ return EMSGSIZE;
+ }
+
+ IFA_UNLOCK(ifa);
+ return 0;
+}
+
+errno_t
+ifaddr_dstaddress(ifaddr_t ifa, struct sockaddr *out_addr, u_int32_t addr_size)
+{
+ u_int32_t copylen;
+
+ if (ifa == NULL || out_addr == NULL) {
+ return EINVAL;
+ }
+
+ IFA_LOCK_SPIN(ifa);
+ if (ifa->ifa_dstaddr == NULL) {
+ IFA_UNLOCK(ifa);
+ return ENOTSUP;
+ }
+
+ copylen = (addr_size >= ifa->ifa_dstaddr->sa_len) ?
+ ifa->ifa_dstaddr->sa_len : addr_size;
+ bcopy(ifa->ifa_dstaddr, out_addr, copylen);
+
+ if (ifa->ifa_dstaddr->sa_len > addr_size) {
+ IFA_UNLOCK(ifa);
+ return EMSGSIZE;
+ }
+
+ IFA_UNLOCK(ifa);
+ return 0;
+}
+
+errno_t
+ifaddr_netmask(ifaddr_t ifa, struct sockaddr *out_addr, u_int32_t addr_size)
+{
+ u_int32_t copylen;
+
+ if (ifa == NULL || out_addr == NULL) {
+ return EINVAL;
+ }
+
+ IFA_LOCK_SPIN(ifa);
+ if (ifa->ifa_netmask == NULL) {
+ IFA_UNLOCK(ifa);
+ return ENOTSUP;
+ }
+
+ copylen = addr_size >= ifa->ifa_netmask->sa_len ?
+ ifa->ifa_netmask->sa_len : addr_size;
+ bcopy(ifa->ifa_netmask, out_addr, copylen);
+
+ if (ifa->ifa_netmask->sa_len > addr_size) {
+ IFA_UNLOCK(ifa);
+ return EMSGSIZE;
+ }
+
+ IFA_UNLOCK(ifa);
+ return 0;
+}
+
+ifnet_t
+ifaddr_ifnet(ifaddr_t ifa)
+{
+ struct ifnet *ifp;
+
+ if (ifa == NULL) {
+ return NULL;
+ }
+
+ /* ifa_ifp is set once at creation time; it is never changed */
+ ifp = ifa->ifa_ifp;
+
+ return ifp;
+}
+
+ifaddr_t
+ifaddr_withaddr(const struct sockaddr *address)
+{
+ if (address == NULL) {
+ return NULL;
+ }
+
+ return ifa_ifwithaddr(address);
+}
+
+ifaddr_t
+ifaddr_withdstaddr(const struct sockaddr *address)
+{
+ if (address == NULL) {
+ return NULL;
+ }
+
+ return ifa_ifwithdstaddr(address);
+}
+
+ifaddr_t
+ifaddr_withnet(const struct sockaddr *net)
+{
+ if (net == NULL) {
+ return NULL;
+ }
+
+ return ifa_ifwithnet(net);
+}
+
+ifaddr_t
+ifaddr_withroute(int flags, const struct sockaddr *destination,
+ const struct sockaddr *gateway)
+{
+ if (destination == NULL || gateway == NULL) {
+ return NULL;
+ }
+
+ return ifa_ifwithroute(flags, destination, gateway);
+}
+
+ifaddr_t
+ifaddr_findbestforaddr(const struct sockaddr *addr, ifnet_t interface)
+{
+ if (addr == NULL || interface == NULL) {
+ return NULL;
+ }
+
+ return ifaof_ifpforaddr_select(addr, interface);
+}
+
+errno_t
+ifmaddr_reference(ifmultiaddr_t ifmaddr)
+{
+ if (ifmaddr == NULL) {
+ return EINVAL;
+ }
+
+ IFMA_ADDREF(ifmaddr);
+ return 0;
+}
+
+errno_t
+ifmaddr_release(ifmultiaddr_t ifmaddr)
+{
+ if (ifmaddr == NULL) {
+ return EINVAL;
+ }
+
+ IFMA_REMREF(ifmaddr);
+ return 0;
+}
+
+errno_t
+ifmaddr_address(ifmultiaddr_t ifma, struct sockaddr *out_addr,
+ u_int32_t addr_size)
+{
+ u_int32_t copylen;
+
+ if (ifma == NULL || out_addr == NULL) {
+ return EINVAL;
+ }
+
+ IFMA_LOCK(ifma);
+ if (ifma->ifma_addr == NULL) {
+ IFMA_UNLOCK(ifma);
+ return ENOTSUP;
+ }
+
+ copylen = (addr_size >= ifma->ifma_addr->sa_len ?
+ ifma->ifma_addr->sa_len : addr_size);
+ bcopy(ifma->ifma_addr, out_addr, copylen);
+
+ if (ifma->ifma_addr->sa_len > addr_size) {
+ IFMA_UNLOCK(ifma);
+ return EMSGSIZE;
+ }
+ IFMA_UNLOCK(ifma);
+ return 0;
+}
+
+errno_t
+ifmaddr_lladdress(ifmultiaddr_t ifma, struct sockaddr *out_addr,
+ u_int32_t addr_size)
+{
+ struct ifmultiaddr *ifma_ll;
+
+ if (ifma == NULL || out_addr == NULL) {
+ return EINVAL;
+ }
+ if ((ifma_ll = ifma->ifma_ll) == NULL) {
+ return ENOTSUP;
+ }
+
+ return ifmaddr_address(ifma_ll, out_addr, addr_size);
+}
+
+ifnet_t
+ifmaddr_ifnet(ifmultiaddr_t ifma)
+{
+ return (ifma == NULL) ? NULL : ifma->ifma_ifp;
+}
+
+/**************************************************************************/
+/* interface cloner */
+/**************************************************************************/
+
+errno_t
+ifnet_clone_attach(struct ifnet_clone_params *cloner_params,
+ if_clone_t *ifcloner)
+{
+ errno_t error = 0;
+ struct if_clone *ifc = NULL;
+ size_t namelen;
+
+ if (cloner_params == NULL || ifcloner == NULL ||
+ cloner_params->ifc_name == NULL ||
+ cloner_params->ifc_create == NULL ||
+ cloner_params->ifc_destroy == NULL ||
+ (namelen = strlen(cloner_params->ifc_name)) >= IFNAMSIZ) {
+ error = EINVAL;
+ goto fail;
+ }
+
+ if (if_clone_lookup(cloner_params->ifc_name, NULL) != NULL) {
+ printf("%s: already a cloner for %s\n", __func__,
+ cloner_params->ifc_name);
+ error = EEXIST;
+ goto fail;
+ }
+
+ /* Make room for name string */
+ ifc = _MALLOC(sizeof(struct if_clone) + IFNAMSIZ + 1, M_CLONE,
+ M_WAITOK | M_ZERO);
+ if (ifc == NULL) {
+ printf("%s: _MALLOC failed\n", __func__);
+ error = ENOBUFS;
+ goto fail;
+ }
+ strlcpy((char *)(ifc + 1), cloner_params->ifc_name, IFNAMSIZ + 1);
+ ifc->ifc_name = (char *)(ifc + 1);
+ ifc->ifc_namelen = namelen;
+ ifc->ifc_maxunit = IF_MAXUNIT;
+ ifc->ifc_create = cloner_params->ifc_create;
+ ifc->ifc_destroy = cloner_params->ifc_destroy;
+
+ error = if_clone_attach(ifc);
+ if (error != 0) {
+ printf("%s: if_clone_attach failed %d\n", __func__, error);
+ goto fail;
+ }
+ *ifcloner = ifc;
+
+ return 0;
+fail:
+ if (ifc != NULL) {
+ FREE(ifc, M_CLONE);
+ }
+ return error;
+}
+
+errno_t
+ifnet_clone_detach(if_clone_t ifcloner)
+{
+ errno_t error = 0;
+ struct if_clone *ifc = ifcloner;
+
+ if (ifc == NULL || ifc->ifc_name == NULL) {
+ return EINVAL;
+ }
+
+ if ((if_clone_lookup(ifc->ifc_name, NULL)) == NULL) {
+ printf("%s: no cloner for %s\n", __func__, ifc->ifc_name);
+ error = EINVAL;
+ goto fail;
+ }
+
+ if_clone_detach(ifc);
+
+ FREE(ifc, M_CLONE);
+
+fail:
+ return error;
+}
+
+/**************************************************************************/
+/* misc */
+/**************************************************************************/
+
+errno_t
+ifnet_get_local_ports_extended(ifnet_t ifp, protocol_family_t protocol,
+ u_int32_t flags, u_int8_t *bitfield)
+{
+ u_int32_t ifindex;
+
+ if (bitfield == NULL) {
+ return EINVAL;
+ }
+
+ switch (protocol) {
+ case PF_UNSPEC:
+ case PF_INET:
+ case PF_INET6:
+ break;
+ default:
+ return EINVAL;
+ }
+
+ /* bit string is long enough to hold 16-bit port values */
+ bzero(bitfield, bitstr_size(IP_PORTRANGE_SIZE));
+
+ if_ports_used_update_wakeuuid(ifp);
+
+
+ ifindex = (ifp != NULL) ? ifp->if_index : 0;
+
+ if (!(flags & IFNET_GET_LOCAL_PORTS_TCPONLY)) {
+ udp_get_ports_used(ifindex, protocol, flags,
+ bitfield);
+ }
+
+ if (!(flags & IFNET_GET_LOCAL_PORTS_UDPONLY)) {
+ tcp_get_ports_used(ifindex, protocol, flags,
+ bitfield);
+ }
+
+ return 0;
+}
+
+errno_t
+ifnet_get_local_ports(ifnet_t ifp, u_int8_t *bitfield)
+{
+ u_int32_t flags = IFNET_GET_LOCAL_PORTS_WILDCARDOK;
+ return ifnet_get_local_ports_extended(ifp, PF_UNSPEC, flags,
+ bitfield);
+}
+
+errno_t
+ifnet_notice_node_presence(ifnet_t ifp, struct sockaddr *sa, int32_t rssi,
+ int lqm, int npm, u_int8_t srvinfo[48])
+{
+ if (ifp == NULL || sa == NULL || srvinfo == NULL) {
+ return EINVAL;
+ }
+ if (sa->sa_len > sizeof(struct sockaddr_storage)) {
+ return EINVAL;
+ }
+ if (sa->sa_family != AF_LINK && sa->sa_family != AF_INET6) {
+ return EINVAL;
+ }
+
+ return dlil_node_present(ifp, sa, rssi, lqm, npm, srvinfo);
+}
+
+errno_t
+ifnet_notice_node_presence_v2(ifnet_t ifp, struct sockaddr *sa, struct sockaddr_dl *sdl,
+ int32_t rssi, int lqm, int npm, u_int8_t srvinfo[48])
+{
+ /* Support older version if sdl is NULL */
+ if (sdl == NULL) {
+ return ifnet_notice_node_presence(ifp, sa, rssi, lqm, npm, srvinfo);
+ }
+
+ if (ifp == NULL || sa == NULL || srvinfo == NULL) {
+ return EINVAL;