kpi_interface.c   [plain text]


/*
 * Copyright (c) 2004-2007 Apple Inc. All rights reserved.
 *
 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
 * 
 * This file contains Original Code and/or Modifications of Original Code
 * as defined in and that are subject to the Apple Public Source License
 * Version 2.0 (the 'License'). You may not use this file except in
 * compliance with the License. The rights granted to you under the License
 * may not be used to create, or enable the creation or redistribution of,
 * unlawful or unlicensed copies of an Apple operating system, or to
 * circumvent, violate, or enable the circumvention or violation of, any
 * terms of an Apple operating system software license agreement.
 * 
 * Please obtain a copy of the License at
 * http://www.opensource.apple.com/apsl/ and read it before using this file.
 * 
 * The Original Code and all software distributed under the License are
 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
 * Please see the License for the specific language governing rights and
 * limitations under the License.
 * 
 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
 */

#include "kpi_interface.h"

#include <sys/queue.h>
#include <sys/param.h>	/* for definition of NULL */
#include <kern/debug.h> /* for panic */
#include <sys/errno.h>
#include <sys/socket.h>
#include <sys/kern_event.h>
#include <sys/kernel.h>
#include <sys/malloc.h>
#include <sys/kpi_mbuf.h>
#include <net/if_var.h>
#include <net/if_dl.h>
#include <net/dlil.h>
#include <net/if_types.h>
#include <net/if_dl.h>
#include <net/if_arp.h>
#include <libkern/libkern.h>
#include <libkern/OSAtomic.h>
#include <kern/locks.h>

#if IF_LASTCHANGEUPTIME
#define TOUCHLASTCHANGE(__if_lastchange) microuptime(__if_lastchange)
#else
#define TOUCHLASTCHANGE(__if_lastchange) microtime(__if_lastchange)
#endif

extern struct dlil_threading_info *dlil_lo_thread_ptr;
extern int dlil_multithreaded_input;

static errno_t
ifnet_list_get_common(ifnet_family_t, boolean_t, ifnet_t **, u_int32_t *);

/*
	Temporary work around until we have real reference counting
	
	We keep the bits about calling dlil_if_release (which should be
	called recycle) transparent by calling it from our if_free function
	pointer. We have to keep the client's original detach function
	somewhere so we can call it.
 */
static void
ifnet_kpi_free(
	ifnet_t ifp)
{
	ifnet_detached_func	detach_func = ifp->if_kpi_storage;
	
	if (detach_func)
		detach_func(ifp);
	
	if (ifp->if_broadcast.length > sizeof(ifp->if_broadcast.u.buffer)) {
		FREE(ifp->if_broadcast.u.ptr, M_IFADDR);
		ifp->if_broadcast.u.ptr = NULL;
	}
	
	dlil_if_release(ifp);
}

static __inline__ void*
_cast_non_const(const void * ptr) {
	union {
		const void*		cval;
		void*			val;
	} ret;
	
	ret.cval = ptr;
	return (ret.val);
}

errno_t
ifnet_allocate(
	const struct ifnet_init_params *init,
	ifnet_t *interface)
{
	int error;
	struct ifnet *ifp = NULL;
	
	if (init->family == 0)
		return EINVAL;
	if (init->name == NULL ||
		init->output == NULL)
		return EINVAL;
	if (strlen(init->name) >= IFNAMSIZ)
		return EINVAL;
	if ((init->type & 0xFFFFFF00) != 0 || init->type == 0)
		return EINVAL;
	
	error = dlil_if_acquire(init->family, init->uniqueid, init->uniqueid_len, &ifp);
	if (error == 0)
	{
		/*
		 * Cast ifp->if_name as non const. dlil_if_acquire sets it up
		 * to point to storage of at least IFNAMSIZ bytes. It is safe
		 * to write to this.
		 */
		strncpy(_cast_non_const(ifp->if_name), init->name, IFNAMSIZ);
		ifp->if_type = init->type;
		ifp->if_family = init->family;
		ifp->if_unit = init->unit;
		ifp->if_output = init->output;
		ifp->if_demux = init->demux;
		ifp->if_add_proto = init->add_proto;
		ifp->if_del_proto = init->del_proto;
		ifp->if_check_multi = init->check_multi;
		ifp->if_framer = init->framer;
		ifp->if_softc = init->softc;
		ifp->if_ioctl = init->ioctl;
		ifp->if_set_bpf_tap = init->set_bpf_tap;
		ifp->if_free = ifnet_kpi_free;
		ifp->if_event = init->event;
		ifp->if_kpi_storage = init->detach;
		ifp->if_eflags |= IFEF_USEKPI;
		
		if (init->broadcast_len && init->broadcast_addr) {
			if (init->broadcast_len > sizeof(ifp->if_broadcast.u.buffer)) {
				MALLOC(ifp->if_broadcast.u.ptr, u_char*, init->broadcast_len, M_IFADDR, M_NOWAIT);
				if (ifp->if_broadcast.u.ptr == NULL) {
					error = ENOMEM;
				}
				else {
					bcopy(init->broadcast_addr, ifp->if_broadcast.u.ptr, init->broadcast_len);
				}
			}
			else {
				bcopy(init->broadcast_addr, ifp->if_broadcast.u.buffer, init->broadcast_len);
			}
			ifp->if_broadcast.length = init->broadcast_len;
		}
		else {
			bzero(&ifp->if_broadcast, sizeof(ifp->if_broadcast));
		}
		
		if (error == 0) {
			*interface = ifp;
			ifnet_reference(ifp); // temporary - this should be done in dlil_if_acquire
		}
		else {
			dlil_if_release(ifp);
			*interface = 0;
		}
	}
	
	/*
	  Note: We should do something here to indicate that we haven't been
	  attached yet. By doing so, we can catch the case in ifnet_release
	  where the reference count reaches zero and call the recycle
	  function. If the interface is attached, the interface will be
	  recycled when the interface's if_free function is called. If the
	  interface is never attached, the if_free function will never be
	  called and the interface will never be recycled.
	*/
	
	return error;
}

errno_t
ifnet_reference(
	ifnet_t	ifp)
{
	int	oldval;
	
	if (ifp == NULL) return EINVAL;
	
	oldval = OSIncrementAtomic((SInt32 *)&ifp->if_refcnt);
	
	return 0;
}

errno_t
ifnet_release(
	ifnet_t	ifp)
{
	int	oldval;
	
	if (ifp == NULL) return EINVAL;
	
	oldval = OSDecrementAtomic((SInt32*)&ifp->if_refcnt);
	if (oldval == 0)
		panic("ifnet_release - refcount decremented past zero!");
	
	return 0;
}

void*
ifnet_softc(
	ifnet_t interface)
{
	return interface == NULL ? NULL : interface->if_softc;
}

const char*
ifnet_name(
	ifnet_t interface)
{
	return interface == NULL ? NULL : interface->if_name;
}

ifnet_family_t
ifnet_family(
	ifnet_t interface)
{
	return interface == NULL ? 0 : interface->if_family;
}

u_int32_t
ifnet_unit(
	ifnet_t interface)
{
	return interface == NULL ? (u_int32_t)0xffffffff : (u_int32_t)interface->if_unit;
}

u_int32_t
ifnet_index(
	ifnet_t interface)
{
	return interface == NULL ? (u_int32_t)0xffffffff : interface->if_index;
}

errno_t
ifnet_set_flags(
	ifnet_t interface,
	u_int16_t new_flags,
	u_int16_t mask)
{
	int lock;
	
	if (interface == NULL) return EINVAL;
	lock = (interface->if_lock != 0);
	
	if (lock) ifnet_lock_exclusive(interface);
	
	/* If we are modifying the up/down state, call if_updown */
	if (lock && (mask & IFF_UP) != 0) {
		if_updown(interface, (new_flags & IFF_UP) == IFF_UP);
	}
	
	interface->if_flags = (new_flags & mask) | (interface->if_flags & ~mask);
	if (lock) ifnet_lock_done(interface);
	
	return 0;
}

u_int16_t
ifnet_flags(
	ifnet_t interface)
{
	return interface == NULL ? 0 : interface->if_flags;
}

errno_t
ifnet_set_eflags(
	ifnet_t interface,
	u_int32_t new_flags,
	u_int32_t mask)
{
	int lock;
	
	if (interface == NULL) return EINVAL;
	lock = (interface->if_lock != 0);
	
	if (lock) ifnet_lock_exclusive(interface);
	interface->if_eflags = (new_flags & mask) | (interface->if_eflags & ~mask);
	if (lock) ifnet_lock_done(interface);
	
	return 0;
}

u_int32_t
ifnet_eflags(
	ifnet_t interface)
{
	return interface == NULL ? 0 : interface->if_eflags;
}

static const ifnet_offload_t offload_mask = IFNET_CSUM_IP | IFNET_CSUM_TCP |
			IFNET_CSUM_UDP | IFNET_CSUM_FRAGMENT | IFNET_IP_FRAGMENT |
			IFNET_CSUM_SUM16 | IFNET_VLAN_TAGGING | IFNET_VLAN_MTU |
			IFNET_MULTIPAGES;

errno_t
ifnet_set_offload(
	ifnet_t interface,
	ifnet_offload_t offload)
{
	int lock;
	
	if (interface == NULL) return EINVAL;
	lock = (interface->if_lock != 0);
	
	if (lock) ifnet_lock_exclusive(interface);
	interface->if_hwassist = (offload & offload_mask);
	if (lock) ifnet_lock_done(interface);
	
	return 0;
}

ifnet_offload_t
ifnet_offload(
	ifnet_t interface)
{
	return interface == NULL ? 0 : (interface->if_hwassist & offload_mask);
}

/*
 * Should MIB data store a copy?
 */
errno_t
ifnet_set_link_mib_data(
	ifnet_t interface,
	void* mibData,
	u_int32_t mibLen)
{
	int lock;
	
	if (interface == NULL) return EINVAL;
	lock = (interface->if_lock != 0);
	
	if (lock) ifnet_lock_exclusive(interface);
	interface->if_linkmib = (void*)mibData;
	interface->if_linkmiblen = mibLen;
	if (lock) ifnet_lock_done(interface);
	return 0;
}

errno_t
ifnet_get_link_mib_data(
	ifnet_t interface,
	void *mibData,
	u_int32_t *mibLen)
{
	errno_t	result = 0;
	int lock;
	
	if (interface == NULL) return EINVAL;
	lock = (interface->if_lock != NULL);
	
	if (lock) ifnet_lock_shared(interface);
	if (*mibLen < interface->if_linkmiblen)
		result = EMSGSIZE;
	if (result == 0 && interface->if_linkmib == NULL)
		result = ENOTSUP;
	
	if (result == 0) {
		*mibLen = interface->if_linkmiblen;
		bcopy(interface->if_linkmib, mibData, *mibLen);
	}
	if (lock) ifnet_lock_done(interface);
	
	return result;
}

u_int32_t
ifnet_get_link_mib_data_length(
	ifnet_t interface)
{
	return interface == NULL ? 0 : interface->if_linkmiblen;
}

errno_t
ifnet_output(
	ifnet_t interface,
	protocol_family_t protocol_family,
	mbuf_t m,
	void *route,
	const struct sockaddr *dest)
{
	if (interface == NULL || protocol_family == 0 || m == NULL) {
		if (m)
			mbuf_freem_list(m);
		return EINVAL;
	}
	return dlil_output(interface, protocol_family, m, route, dest, 0);
}

errno_t
ifnet_output_raw(
	ifnet_t interface,
	protocol_family_t protocol_family,
	mbuf_t m)
{
	if (interface == NULL || m == NULL) {
		if (m)
			mbuf_freem_list(m);
		return EINVAL;
	}
	return dlil_output(interface, protocol_family, m, NULL, NULL, 1);
}

errno_t
ifnet_set_mtu(
	ifnet_t interface,
	u_int32_t mtu)
{
	if (interface == NULL) return EINVAL;
	interface->if_data.ifi_mtu = mtu;
	return 0;
}

u_int32_t
ifnet_mtu(
	ifnet_t interface)
{
	u_int32_t retval;
	retval = interface == NULL ? 0 : interface->if_data.ifi_mtu;
	return retval;
}

u_char
ifnet_type(
	ifnet_t interface)
{
	u_char retval;
	
	retval = interface == NULL ? 0 : interface->if_data.ifi_type;
	return retval;
}

#if 0
errno_t
ifnet_set_typelen(
	ifnet_t interface,
	u_char typelen)
{
	int lock = (interface->if_lock != 0);
	if (lock) ifnet_lock_exclusive(interface);
	interface->if_data.ifi_typelen = typelen;
	if (lock) ifnet_lock_done(interface);
	return 0;
}

u_char
ifnet_typelen(
	ifnet_t interface)
{
	u_char retval;
	retval = interface == NULL ? 0 : interface->if_data.ifi_typelen;
	return retval;
}
#endif

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)
{
	u_char retval;
	retval = interface == NULL ? 0 : interface->if_data.ifi_addrlen;
	return retval;
}

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)
{
	u_char retval;
	retval = interface == NULL ? 0 : interface->if_data.ifi_hdrlen;
	return retval;
}

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)
{
	u_int32_t retval;
	retval = interface == NULL ? 0 : interface->if_data.ifi_metric;
	return retval;
}

errno_t
ifnet_set_baudrate(
	ifnet_t interface,
	u_int64_t baudrate)
{
	if (interface == NULL) return EINVAL;
	/* Pin baudrate to 32 bits until we can change the storage size */
	interface->if_data.ifi_baudrate = baudrate > 0xFFFFFFFF ? 0xFFFFFFFF : baudrate;
	return 0;
}

u_int64_t
ifnet_baudrate(
	ifnet_t interface)
{
	u_int64_t retval;
	retval = interface == NULL ? 0 : interface->if_data.ifi_baudrate;
	return retval;
}

errno_t
ifnet_stat_increment(
	ifnet_t interface,
	const struct ifnet_stat_increment_param *counts)
{
	struct dlil_threading_info *thread;
	if (interface == NULL) return EINVAL;

       	if ((thread = interface->if_input_thread) == NULL || (dlil_multithreaded_input == 0))
		thread = dlil_lo_thread_ptr;

	lck_mtx_lock(thread->input_lck);

	interface->if_data.ifi_ipackets += counts->packets_in;
	interface->if_data.ifi_ibytes += counts->bytes_in;
	interface->if_data.ifi_ierrors += counts->errors_in;

	interface->if_data.ifi_opackets += counts->packets_out;
	interface->if_data.ifi_obytes += counts->bytes_out;
	interface->if_data.ifi_oerrors += counts->errors_out;

	interface->if_data.ifi_collisions += counts->collisions;
	interface->if_data.ifi_iqdrops += counts->dropped;
	
	/* Touch the last change time. */
	TOUCHLASTCHANGE(&interface->if_lastchange);

	lck_mtx_unlock(thread->input_lck);
	
	return 0;
}

errno_t
ifnet_stat_increment_in(
	ifnet_t interface,
	u_int32_t packets_in,
	u_int32_t bytes_in,
	u_int32_t errors_in)
{
	struct dlil_threading_info *thread;

	if (interface == NULL) return EINVAL;
	
       	if ((thread = interface->if_input_thread) == NULL || (dlil_multithreaded_input == 0))
		thread = dlil_lo_thread_ptr;

	lck_mtx_lock(thread->input_lck);

	interface->if_data.ifi_ipackets += packets_in;
	interface->if_data.ifi_ibytes += bytes_in;
	interface->if_data.ifi_ierrors += errors_in;

	TOUCHLASTCHANGE(&interface->if_lastchange);

	lck_mtx_unlock(thread->input_lck);
	
	return 0;
}

errno_t
ifnet_stat_increment_out(
	ifnet_t interface,
	u_int32_t packets_out,
	u_int32_t bytes_out,
	u_int32_t errors_out)
{
	struct dlil_threading_info *thread;
	if (interface == NULL) return EINVAL;
	
       	if ((thread = interface->if_input_thread) == NULL || (dlil_multithreaded_input == 0))
		thread = dlil_lo_thread_ptr;

	lck_mtx_lock(thread->input_lck);

	interface->if_data.ifi_opackets += packets_out;
	interface->if_data.ifi_obytes += bytes_out;
	interface->if_data.ifi_oerrors += errors_out;

	TOUCHLASTCHANGE(&interface->if_lastchange);

	lck_mtx_unlock(thread->input_lck);
	
	return 0;
}

errno_t
ifnet_set_stat(
	ifnet_t interface,
	const struct ifnet_stats_param *stats)
{
	struct dlil_threading_info *thread;

	if (interface == NULL) return EINVAL;
	
       	if ((thread = interface->if_input_thread) == NULL || (dlil_multithreaded_input == 0))
		thread = dlil_lo_thread_ptr;

	lck_mtx_lock(thread->input_lck);

	interface->if_data.ifi_ipackets = stats->packets_in;
	interface->if_data.ifi_ibytes = stats->bytes_in;
	interface->if_data.ifi_imcasts = stats->multicasts_in;
	interface->if_data.ifi_ierrors = stats->errors_in;
	
	interface->if_data.ifi_opackets = stats->packets_out;
	interface->if_data.ifi_obytes = stats->bytes_out;
	interface->if_data.ifi_omcasts = stats->multicasts_out;
	interface->if_data.ifi_oerrors = stats->errors_out;
	
	interface->if_data.ifi_collisions = stats->collisions;
	interface->if_data.ifi_iqdrops = stats->dropped;
	interface->if_data.ifi_noproto = stats->no_protocol;

	/* Touch the last change time. */
	TOUCHLASTCHANGE(&interface->if_lastchange);

	lck_mtx_unlock(thread->input_lck);
	
	return 0;
}

errno_t
ifnet_stat(
	ifnet_t interface,
	struct ifnet_stats_param *stats)
{
	struct dlil_threading_info *thread;

	if (interface == NULL) return EINVAL;
	
       	if ((thread = interface->if_input_thread) == NULL || (dlil_multithreaded_input == 0))
		thread = dlil_lo_thread_ptr;

	lck_mtx_lock(thread->input_lck);

	stats->packets_in = interface->if_data.ifi_ipackets;
	stats->bytes_in = interface->if_data.ifi_ibytes;
	stats->multicasts_in = interface->if_data.ifi_imcasts;
	stats->errors_in = interface->if_data.ifi_ierrors;

	stats->packets_out = interface->if_data.ifi_opackets;
	stats->bytes_out = interface->if_data.ifi_obytes;
	stats->multicasts_out = interface->if_data.ifi_omcasts;
	stats->errors_out = interface->if_data.ifi_oerrors;

	stats->collisions = interface->if_data.ifi_collisions;
	stats->dropped = interface->if_data.ifi_iqdrops;
	stats->no_protocol = interface->if_data.ifi_noproto;

	lck_mtx_unlock(thread->input_lck);
	
	return 0;
}

errno_t
ifnet_touch_lastchange(
	ifnet_t interface)
{
	struct dlil_threading_info *thread;

	if (interface == NULL) return EINVAL;
	
       	if ((thread = interface->if_input_thread) == NULL || (dlil_multithreaded_input == 0))
		thread = dlil_lo_thread_ptr;

	lck_mtx_lock(thread->input_lck);

	TOUCHLASTCHANGE(&interface->if_lastchange);

	lck_mtx_unlock(thread->input_lck);
	
	return 0;
}

errno_t
ifnet_lastchange(
	ifnet_t interface,
	struct timeval *last_change)
{
	struct dlil_threading_info *thread;

	if (interface == NULL) return EINVAL;
	
       	if ((thread = interface->if_input_thread) == NULL || (dlil_multithreaded_input == 0))
		thread = dlil_lo_thread_ptr;

	lck_mtx_lock(thread->input_lck);

	*last_change = interface->if_data.ifi_lastchange;
	
	lck_mtx_unlock(thread->input_lck);
	
#if IF_LASTCHANGEUPTIME
	/* Crude conversion from uptime to calendar time */
	last_change->tv_sec += boottime_sec();
#endif

	return 0;
}

errno_t
ifnet_get_address_list(
	ifnet_t interface,
	ifaddr_t **addresses)
{
	if (addresses == NULL) return EINVAL;
	return ifnet_get_address_list_family(interface, addresses, 0);
}

errno_t
ifnet_get_address_list_family(
	ifnet_t interface,
	ifaddr_t **addresses,
	sa_family_t	family)
{
	struct ifnet *ifp;
	int count = 0;
	int cmax = 0;
	
	if (addresses == NULL) return EINVAL;
	*addresses = NULL;
	
	ifnet_head_lock_shared();
	TAILQ_FOREACH(ifp, &ifnet, if_link)
	{
		if (interface && ifp != interface) continue;
		
		ifnet_lock_shared(ifp);
		if ((ifp->if_eflags & IFEF_DETACHING) == 0) {
			if (interface == NULL || interface == ifp)
			{
				struct ifaddr *addr;
				TAILQ_FOREACH(addr, &ifp->if_addrhead, ifa_link)
				{
					if (family == 0 || addr->ifa_addr->sa_family == family)
						cmax++;
				}
			}
		}
		else if (interface != NULL) {
			ifnet_lock_done(ifp);
			ifnet_head_done();
			return ENXIO;
		}
		ifnet_lock_done(ifp);
	}
	
	MALLOC(*addresses, ifaddr_t*, sizeof(ifaddr_t) * (cmax + 1), M_TEMP, M_NOWAIT);
	if (*addresses == NULL) {
		ifnet_head_done();
		return ENOMEM;
	}
	
	TAILQ_FOREACH(ifp, &ifnet, if_link)
	{
		if (interface && ifp != interface) continue;
		
		ifnet_lock_shared(ifp);
		if ((ifp->if_eflags & IFEF_DETACHING) == 0) {
			if (interface == NULL || (struct ifnet*)interface == ifp)
			{
				struct ifaddr *addr;
				TAILQ_FOREACH(addr, &ifp->if_addrhead, ifa_link)
				{
					if (count + 1 > cmax) break;
					if (family == 0 || addr->ifa_addr->sa_family == family) {
						(*addresses)[count] = (ifaddr_t)addr;
						ifaddr_reference((*addresses)[count]);
						count++;
					}
				}
			}
		}
		ifnet_lock_done(ifp);
		if (interface || count == cmax)
			break;
	}
	ifnet_head_done();
	(*addresses)[cmax] = 0;
	
	return 0;
}

void
ifnet_free_address_list(
	ifaddr_t *addresses)
{
	int i;
	
	if (addresses == NULL) return;
	
	for (i = 0; addresses[i] != NULL; i++)
	{
		ifaddr_release(addresses[i]);
	}
	
	FREE(addresses, M_TEMP);
}

void*
ifnet_lladdr(
	ifnet_t	interface)
{
	if (interface == NULL) return NULL;
	return LLADDR(SDL(interface->if_addrhead.tqh_first->ifa_addr));
}

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;
}

errno_t
ifnet_lladdr_copy_bytes(
	ifnet_t	interface,
	void*	lladdr,
	size_t	lladdr_len)
{
	struct sockaddr_dl *sdl;
	if (interface == NULL || lladdr == NULL) return EINVAL;
	
	sdl = SDL(interface->if_addrhead.tqh_first->ifa_addr);
	
	while (1) {
		if (lladdr_len != sdl->sdl_alen) {
			bzero(lladdr, lladdr_len);
			return EMSGSIZE;
		}
		bcopy(LLADDR(sdl), lladdr, lladdr_len);
		if (bcmp(lladdr, LLADDR(sdl), lladdr_len) == 0 &&
			lladdr_len == sdl->sdl_alen)
			break;
	}
	return 0;
}

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;
	struct sockaddr_dl	*sdl;
	errno_t	error = 0;
	
	if (interface == NULL) return EINVAL;
	
	if (lladdr_len != 0 && (lladdr_len != interface->if_addrlen || lladdr == 0))
		return EINVAL;
	
	ifnet_head_lock_shared();
	ifa = ifnet_addrs[interface->if_index - 1];
	if (ifa != NULL) {
		sdl = (struct sockaddr_dl*)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;
		}
	}
	else {
		error = ENXIO;
	}
	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 *address)
{
	if (interface == NULL || maddr == NULL) return EINVAL;
	return if_addmulti(interface, maddr, address);
}

errno_t
ifnet_remove_multicast(
	ifmultiaddr_t address)
{
	if (address == NULL) return EINVAL;
	return if_delmultiaddr(address, 0);
}

errno_t ifnet_get_multicast_list(ifnet_t interface, ifmultiaddr_t **addresses)
{
	int count = 0;
	int cmax = 0;
	struct ifmultiaddr *addr;
	int lock;
	
	if (interface == NULL || addresses == NULL)
		return EINVAL;
	
	lock = (interface->if_lock != 0);
	if (lock) ifnet_lock_shared(interface);
	if ((interface->if_eflags & IFEF_DETACHING) == 0) {
		LIST_FOREACH(addr, &interface->if_multiaddrs, ifma_link)
		{
			cmax++;
		}
	}
	else {
		if (lock) ifnet_lock_done(interface);
		return ENXIO;
	}
	
	MALLOC(*addresses, ifmultiaddr_t*, sizeof(ifmultiaddr_t) * (cmax + 1), M_TEMP, M_NOWAIT);
	if (*addresses == NULL) {
		if (lock) ifnet_lock_done(interface);
		return ENOMEM;
	}
	
	LIST_FOREACH(addr, &interface->if_multiaddrs, ifma_link)
	{
		if (count + 1 > cmax)
			break;
		(*addresses)[count] = (ifmultiaddr_t)addr;
		ifmaddr_reference((*addresses)[count]);
		count++;
	}
	(*addresses)[cmax] = 0;
	if (lock) ifnet_lock_done(interface);
	
	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 *interface)
{
	struct ifnet *ifp;
	int	namelen;
	
	if (ifname == NULL) return EINVAL;
	
	namelen = strlen(ifname);
	
	*interface = NULL;
	
	ifnet_head_lock_shared();
	TAILQ_FOREACH(ifp, &ifnet, if_link)
	{
		struct ifaddr *ifa = ifnet_addrs[ifp->if_index - 1];
		struct sockaddr_dl *ll_addr;

		if (!ifa || !ifa->ifa_addr)
			continue;

		ll_addr = (struct sockaddr_dl *)ifa->ifa_addr;

		if ((ifp->if_eflags & IFEF_DETACHING) == 0 &&
			namelen == ll_addr->sdl_nlen &&
			(strncmp(ll_addr->sdl_data, ifname, ll_addr->sdl_nlen) == 0))
		{
			break;
		}
	}
	if (ifp) {
		*interface = ifp;
		ifnet_reference(*interface);
	}
	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));
}

static errno_t
ifnet_list_get_common(ifnet_family_t family, boolean_t get_all, ifnet_t **list,
    u_int32_t *count)
{
	struct ifnet *ifp;
	u_int32_t cmax = 0;
	*count = 0;
	errno_t	result = 0;

	if (list == NULL || count == NULL)
		return (EINVAL);

	ifnet_head_lock_shared();
	TAILQ_FOREACH(ifp, &ifnet, if_link) {
		if ((ifp->if_eflags & IFEF_DETACHING) && !get_all)
			continue;
		if (family == IFNET_FAMILY_ANY || ifp->if_family == family)
			cmax++;
	}

	if (cmax == 0)
		result = ENXIO;

	if (result == 0) {
		MALLOC(*list, ifnet_t*, sizeof(ifnet_t) * (cmax + 1),
		    M_TEMP, M_NOWAIT);
		if (*list == NULL)
			result = ENOMEM;
	}

	if (result == 0) {
		TAILQ_FOREACH(ifp, &ifnet, if_link) {
			if ((ifp->if_eflags & IFEF_DETACHING) && !get_all)
				continue;
			if (*count + 1 > cmax)
				break;
			if (family == IFNET_FAMILY_ANY ||
			    ((ifnet_family_t)ifp->if_family) == family) {
				(*list)[*count] = (ifnet_t)ifp;
				ifnet_reference((*list)[*count]);
				(*count)++;
			}
		}
		(*list)[*count] = NULL;
	}
	ifnet_head_done();

	return (result);
}

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;
	ifaref(ifa);
	return 0;
}

errno_t
ifaddr_release(
	ifaddr_t ifa)
{
	if (ifa == NULL) return EINVAL;
	ifafree(ifa);
	return 0;
}

sa_family_t
ifaddr_address_family(
	ifaddr_t ifa)
{
	if (ifa && ifa->ifa_addr)
		return ifa->ifa_addr->sa_family;
	
	return 0;
}

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;
	if (ifa->ifa_addr == NULL) 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) return EMSGSIZE;
	
	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;
	if (ifa->ifa_dstaddr == NULL) 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) return EMSGSIZE;
	
	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;
	if (ifa->ifa_netmask == NULL) 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) return EMSGSIZE;
	
	return 0;
}

ifnet_t
ifaddr_ifnet(
	ifaddr_t ifa)
{
	struct ifnet *ifp;
	if (ifa == NULL) return NULL;
	ifp = ifa->ifa_ifp;
	
	return (ifnet_t)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(addr, interface);
}

errno_t
ifmaddr_reference(
	ifmultiaddr_t ifmaddr)
{
	if (ifmaddr == NULL) return EINVAL;
	ifma_reference(ifmaddr);
	return 0;
}

errno_t
ifmaddr_release(
	ifmultiaddr_t ifmaddr)
{
	if (ifmaddr == NULL) return EINVAL;
	ifma_release(ifmaddr);	
	return 0;
}

errno_t
ifmaddr_address(
	ifmultiaddr_t ifmaddr,
	struct sockaddr *out_addr,
	u_int32_t addr_size)
{
	u_int32_t copylen;
	
	if (ifmaddr == NULL || out_addr == NULL) return EINVAL;
	if (ifmaddr->ifma_addr == NULL) return ENOTSUP;
	
	copylen = addr_size >= ifmaddr->ifma_addr->sa_len ? ifmaddr->ifma_addr->sa_len : addr_size;
	bcopy(ifmaddr->ifma_addr, out_addr, copylen);
	
	if (ifmaddr->ifma_addr->sa_len > addr_size) return EMSGSIZE;
	
	return 0;
}

errno_t
ifmaddr_lladdress(
	ifmultiaddr_t ifmaddr,
	struct sockaddr *out_addr,
	u_int32_t addr_size)
{
	if (ifmaddr == NULL || out_addr == NULL) return EINVAL;
	if (ifmaddr->ifma_ll == NULL) return ENOTSUP;
	
	return ifmaddr_address(ifmaddr->ifma_ll, out_addr, addr_size);
}

ifnet_t
ifmaddr_ifnet(
	ifmultiaddr_t ifmaddr)
{
	if (ifmaddr == NULL || ifmaddr->ifma_ifp == NULL) return NULL;
	return ifmaddr->ifma_ifp;
}