+ 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) {
+ 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. */
+ if (maddr->sa_family != AF_UNSPEC && maddr->sa_family != AF_LINK)
+ 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);
+}
+
+void
+ifnet_transmit_burst_start(ifnet_t ifp, mbuf_t pkt)
+{
+#if MEASURE_BW
+ uint32_t orig_flags;
+
+ if (ifp == NULL || !(pkt->m_flags & M_PKTHDR))
+ return;
+
+ orig_flags = OSBitOrAtomic(IF_MEASURED_BW_INPROGRESS,
+ &ifp->if_bw.flags);
+ if (orig_flags & IF_MEASURED_BW_INPROGRESS) {
+ /* There is already a measurement in progress; skip this one */
+ return;
+ }
+
+ ifp->if_bw.start_seq = pkt->m_pkthdr.pkt_bwseq;
+ ifp->if_bw.start_ts = mach_absolute_time();
+#else /* !MEASURE_BW */
+#pragma unused(ifp, pkt)
+#endif /* !MEASURE_BW */
+}
+
+void
+ifnet_transmit_burst_end(ifnet_t ifp, mbuf_t pkt)
+{
+#if MEASURE_BW
+ uint64_t oseq, ots, bytes, ts, t;
+ uint32_t flags;
+
+ if (ifp == NULL || !(pkt->m_flags & M_PKTHDR))
+ return;
+
+ flags = OSBitOrAtomic(IF_MEASURED_BW_CALCULATION, &ifp->if_bw.flags);
+
+ /* If a calculation is already in progress, just return */
+ if (flags & IF_MEASURED_BW_CALCULATION)
+ return;
+
+ /* Check if a measurement was started at all */
+ if (!(flags & IF_MEASURED_BW_INPROGRESS)) {
+ /*
+ * It is an error to call burst_end before burst_start.
+ * Reset the calculation flag and return.
+ */
+ goto done;
+ }
+
+ oseq = pkt->m_pkthdr.pkt_bwseq;
+ ots = mach_absolute_time();
+
+ if (ifp->if_bw.start_seq > 0 && oseq > ifp->if_bw.start_seq) {
+ ts = ots - ifp->if_bw.start_ts;
+ if (ts > 0) {
+ absolutetime_to_nanoseconds(ts, &t);
+ bytes = oseq - ifp->if_bw.start_seq;
+ ifp->if_bw.bytes = bytes;
+ ifp->if_bw.ts = ts;
+
+ if (t > 0) {
+ uint64_t bw = 0;
+
+ /* Compute bandwidth as bytes/ms */
+ bw = (bytes * NSEC_PER_MSEC) / t;
+ if (bw > 0) {
+ if (ifp->if_bw.bw > 0) {
+ u_int32_t shft;
+
+ shft = if_bw_smoothing_val;
+ /* Compute EWMA of bw */
+ ifp->if_bw.bw = (bw +
+ ((ifp->if_bw.bw << shft) -
+ ifp->if_bw.bw)) >> shft;
+ } else {
+ ifp->if_bw.bw = bw;
+ }
+ }
+ }
+ ifp->if_bw.last_seq = oseq;
+ ifp->if_bw.last_ts = ots;
+ }
+ }
+
+done:
+ flags = ~(IF_MEASURED_BW_INPROGRESS | IF_MEASURED_BW_CALCULATION);
+ OSBitAndAtomic(flags, &ifp->if_bw.flags);
+#else /* !MEASURE_BW */
+#pragma unused(ifp, pkt)
+#endif /* !MEASURE_BW */
+}
+
+/*************************************************************************/
+/* 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);