File:  [ELWIX - Embedded LightWeight unIX -] / embedaddon / libpdel / net / if_iface.c
Revision 1.1.1.1 (vendor branch): download - view: text, annotated - select for diffs - revision graph
Tue Feb 21 23:25:53 2012 UTC (12 years, 10 months ago) by misho
Branches: libpdel, MAIN
CVS tags: v0_5_3, HEAD
libpdel


/*
 * Copyright (c) 2001-2002 Packet Design, LLC.
 * All rights reserved.
 * 
 * Subject to the following obligations and disclaimer of warranty,
 * use and redistribution of this software, in source or object code
 * forms, with or without modifications are expressly permitted by
 * Packet Design; provided, however, that:
 * 
 *    (i)  Any and all reproductions of the source or object code
 *         must include the copyright notice above and the following
 *         disclaimer of warranties; and
 *    (ii) No rights are granted, in any manner or form, to use
 *         Packet Design trademarks, including the mark "PACKET DESIGN"
 *         on advertising, endorsements, or otherwise except as such
 *         appears in the above copyright notice or in the software.
 * 
 * THIS SOFTWARE IS BEING PROVIDED BY PACKET DESIGN "AS IS", AND
 * TO THE MAXIMUM EXTENT PERMITTED BY LAW, PACKET DESIGN MAKES NO
 * REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED, REGARDING
 * THIS SOFTWARE, INCLUDING WITHOUT LIMITATION, ANY AND ALL IMPLIED
 * WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE,
 * OR NON-INFRINGEMENT.  PACKET DESIGN DOES NOT WARRANT, GUARANTEE,
 * OR MAKE ANY REPRESENTATIONS REGARDING THE USE OF, OR THE RESULTS
 * OF THE USE OF THIS SOFTWARE IN TERMS OF ITS CORRECTNESS, ACCURACY,
 * RELIABILITY OR OTHERWISE.  IN NO EVENT SHALL PACKET DESIGN BE
 * LIABLE FOR ANY DAMAGES RESULTING FROM OR ARISING OUT OF ANY USE
 * OF THIS SOFTWARE, INCLUDING WITHOUT LIMITATION, ANY DIRECT,
 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, PUNITIVE, OR CONSEQUENTIAL
 * DAMAGES, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, LOSS OF
 * USE, DATA OR PROFITS, HOWEVER CAUSED AND UNDER ANY THEORY OF
 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
 * THE USE OF THIS SOFTWARE, EVEN IF PACKET DESIGN IS ADVISED OF
 * THE POSSIBILITY OF SUCH DAMAGE.
 *
 * Author: Archie Cobbs <archie@freebsd.org>
 */

#include <sys/types.h>
#include <sys/param.h>
#include <sys/socket.h>
#include <sys/sysctl.h>
#include <sys/sockio.h>

#include <net/if.h>
#include <net/if_dl.h>
#include <net/route.h>

#include <netinet/in.h>
#include <arpa/inet.h>

#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <err.h>

#include "structs/structs.h"
#include "structs/type/array.h"

#include "util/typed_mem.h"
#include "net/if_util.h"

#define ROUNDUP(a) \
	((a) > 0 ? (1 + (((a) - 1) | (sizeof(long) - 1))) : sizeof(long))
#define ADVANCE(x, n) (x += ROUNDUP((n)->sa_len))

static struct	if_msghdr *if_find(const char *ifname,
			u_char **bufp, const char *mtype);
static int	if_info(u_char **bufp, const char *mtype);

/*
 * Get list of IP addresses and netmasks configured on an interface.
 */
int
if_get_ip_addrs(const char *ifname, struct in_addr **iplistp,
	struct in_addr **nmlistp, const char *mtype)
{
	struct if_msghdr *ifm;
	struct ifa_msghdr *ifam;
	struct in_addr *iplist = NULL;
	struct in_addr *nmlist = NULL;
	int errno_save;
	int num = 0;
	u_char *buf;

	/* Find interface info */
	if ((ifm = if_find(ifname, &buf, TYPED_MEM_TEMP)) == NULL)
		return (-1);

	/* Search for first IP address */
	for (ifam = (struct ifa_msghdr *)(void *)
	      ((u_char *)ifm + ifm->ifm_msglen);
	    ifam->ifam_type == RTM_NEWADDR;
	    ifam = (struct ifa_msghdr *)(void *)
	      ((u_char *)ifam + ifam->ifam_msglen))
		num++;

	/* Allocate arrays */
	if ((iplist = MALLOC(mtype, num * sizeof(*iplist))) == NULL
	    || (nmlist = MALLOC(mtype, num * sizeof(*nmlist))) == NULL) {
		errno_save = errno;
		FREE(mtype, iplist);				/* ok if NULL */
		FREE(TYPED_MEM_TEMP, buf);
		errno = errno_save;
		return (-1);
	}

	/* Search for IP address/netmask combinations */
	num = 0;
	for (ifam = (struct ifa_msghdr *)(void *)
	     ((u_char *)ifm + ifm->ifm_msglen);
	    ifam->ifam_type == RTM_NEWADDR;
	    ifam = (struct ifa_msghdr *)(void *)
	      ((u_char *)ifam + ifam->ifam_msglen)) {
		char *cp = (char *)(ifam + 1);
		int need = RTA_IFA | RTA_NETMASK;
		int i;

		/* Find IP address and netmask, if any */
		if ((ifam->ifam_addrs & need) != need)
			continue;
		for (i = 1; i != 0 && need != 0; i <<= 1) {
			if ((ifam->ifam_addrs & i) == 0)
				continue;
			if (i == RTA_IFA
			    && ((struct sockaddr *)(void *)cp)->sa_family
			      == AF_INET) {
				iplist[num] = ((struct sockaddr_in *)
				    (void *)cp)->sin_addr;
			} else if (i == RTA_NETMASK) {
				nmlist[num] = ((struct sockaddr_in *)
				    (void *)cp)->sin_addr;
			}
			need &= ~i;
			ADVANCE(cp, (struct sockaddr *)cp);
		}
		if (need == 0)
			num++;
	}

	/* Done */
	FREE(TYPED_MEM_TEMP, buf);
	*iplistp = iplist;
	*nmlistp = nmlist;
	return (num);
}

/*
 * Get the first IP address on an interface.
 */
int
if_get_ip_addr(const char *ifname, struct in_addr *ipp, struct in_addr *nmp)
{
	struct in_addr *iplist;
	struct in_addr *nmlist;
	int nip;

	if ((nip = if_get_ip_addrs(ifname,
	    &iplist, &nmlist, TYPED_MEM_TEMP)) == -1)
		return (-1);
	if (nip == 0) {
		FREE(TYPED_MEM_TEMP, iplist);
		FREE(TYPED_MEM_TEMP, nmlist);
		errno = ENOENT;
		return (-1);
	}
	if (ipp != NULL)
		*ipp = iplist[0];
	if (nmp != NULL)
		*nmp = nmlist[0];
	FREE(TYPED_MEM_TEMP, iplist);
	FREE(TYPED_MEM_TEMP, nmlist);
	return (0);
}

/*
 * Get the flags associated with an interface.
 */
int
if_get_flags(const char *ifname)
{
	struct if_msghdr *ifm;
	u_char *buf;
	int rtn;

	if ((ifm = if_find(ifname, &buf, TYPED_MEM_TEMP)) == NULL)
		return (-1);
	rtn = ifm->ifm_flags;
	FREE(TYPED_MEM_TEMP, buf);
	return (rtn);
}

/*
 * Set the flags associated with an interface.
 */
int
if_set_flags(const char *ifname, int flags)
{
	struct ifreq ifr;
	int r;
	int s;

	if ((s = socket(PF_INET, SOCK_DGRAM, 0)) == -1)
		return (-1);
	memset(&ifr, 0, sizeof(ifr));
	strncpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
	ifr.ifr_flags = flags;
	r = ioctl(s, SIOCSIFFLAGS, (char *)&ifr);
	(void)close(s);
	return (r);
}

/*
 * Get the MTU associated with an interface.
 */
int
if_get_mtu(const char *ifname)
{
	struct if_msghdr *ifm;
	u_char *buf;
	int rtn;

	if ((ifm = if_find(ifname, &buf, TYPED_MEM_TEMP)) == NULL)
		return (-1);
	rtn = ifm->ifm_data.ifi_mtu;
	FREE(TYPED_MEM_TEMP, buf);
	return (rtn);
}

/*
 * Set the MTU associated with an interface.
 */
int
if_set_mtu(const char *ifname, u_int mtu)
{
	struct ifreq ifr;
	int r;
	int s;

	if ((s = socket(PF_INET, SOCK_DGRAM, 0)) == -1)
		return (-1);
	memset(&ifr, 0, sizeof(ifr));
	strncpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
	ifr.ifr_mtu = mtu;
	r = ioctl(s, SIOCSIFMTU, (char *)&ifr);
	(void)close(s);
	return (r);
}

/*
 * Get the interface type.
 */
int
if_get_type(const char *ifname)
{
	struct if_msghdr *ifm;
	u_char *buf;
	int rtn;

	if ((ifm = if_find(ifname, &buf, TYPED_MEM_TEMP)) == NULL)
		return (-1);
	rtn = ifm->ifm_data.ifi_type;
	FREE(TYPED_MEM_TEMP, buf);
	return (rtn);
}

/*
 * Get the link address for an interface.
 */
int
if_get_link_addr(const char *ifname,
	struct sockaddr_dl **sdlp, const char *mtype)
{
	struct if_msghdr *ifm;
	struct sockaddr_dl *sdl;
	int errno_save;
	u_char *buf;

	if ((ifm = if_find(ifname, &buf, TYPED_MEM_TEMP)) == NULL)
		return (-1);
	sdl = (struct sockaddr_dl *)(ifm + 1);
	if ((*sdlp = MALLOC(mtype, sdl->sdl_len)) == NULL) {
		errno_save = errno;
		FREE(TYPED_MEM_TEMP, buf);
		errno = errno_save;
		return (-1);
	}
	memcpy(*sdlp, sdl, sdl->sdl_len);
	FREE(TYPED_MEM_TEMP, buf);
	return (0);
}

/*
 * Gets the names of all system interfaces.
 */
int
if_get_list(char ***listp, const char *mtype)
{
	char **list = NULL;
	int errno_save;
	int num = 0;
	u_char *buf;
	u_char *ptr;
	int len;

	/* Get raw data from kernel */
	if ((len = if_info(&buf, TYPED_MEM_TEMP)) == -1)
		return (-1);

	/* Scan interfaces */
	for (ptr = buf;
	    ptr < buf + len;
	    ptr += ((struct if_msghdr *)(void *)ptr)->ifm_msglen) {
		struct if_msghdr *const ifm = (struct if_msghdr *)(void *)ptr;

		/* Sanity check version */
		if (ifm->ifm_version != RTM_VERSION) {
			errno = EIO;
			goto fail;
		}

		/* Next interface? */
		if (ifm->ifm_type == RTM_IFINFO) {
			struct sockaddr_dl *const sdl
			    = (struct sockaddr_dl *)(ifm + 1);
			char nbuf[IF_NAMESIZE + 1];
			void *new_list;

			/* Get name */
			if (sdl->sdl_nlen > sizeof(nbuf) - 1)
				continue;
			memcpy(nbuf, sdl->sdl_data, sdl->sdl_nlen);
			nbuf[sdl->sdl_nlen] = '\0';

			/* Add name to list */
			if ((new_list = REALLOC(mtype, list,
			    (num + 1) * sizeof(*list))) == NULL)
				goto fail;
			list = new_list;
			if ((list[num] = STRDUP(mtype, nbuf)) == NULL)
				goto fail;
			num++;
		}
	}

	/* Done */
	FREE(TYPED_MEM_TEMP, buf);
	*listp = list;
	return (num);

fail:
	errno_save = errno;
	while (num > 0)
		FREE(mtype, list[--num]);
	FREE(mtype, list);
	FREE(TYPED_MEM_TEMP, buf);
	errno = errno_save;
	return (-1);
}

/*
 * Get the flags or type associated with an interface.
 * Caller must free *bufp.
 *
 * Returns -1 and sets errno if there was a problem.
 */
static struct if_msghdr *
if_find(const char *ifname, u_char **bufp, const char *mtype)
{
	u_char *buf;
	u_char *ptr;
	size_t len;

	/* Get raw data from kernel */
	if ((len = if_info(&buf, mtype)) == -1)
		return (NULL);

	/* Scan for desired interface */
	for (ptr = buf;
	    ptr < buf + len;
	    ptr += ((struct if_msghdr *)(void *)ptr)->ifm_msglen) {
		struct if_msghdr *const ifm = (struct if_msghdr *)(void *)ptr;
		struct sockaddr_dl *sdl;

		/* Sanity check version */
		if (ifm->ifm_version != RTM_VERSION) {
			FREE(mtype, buf);
			errno = EIO;
			return (NULL);
		}

		/* Next interface or previous interface address? */
		if (ifm->ifm_type != RTM_IFINFO)
			continue;

		/* Compare interface name */
		sdl = (struct sockaddr_dl *)(ifm + 1);
		if (strlen(ifname) != sdl->sdl_nlen ||
		    strncmp(ifname, sdl->sdl_data, sdl->sdl_nlen) != 0)
			continue;

		/* Found it */
		*bufp = buf;
		return (ifm);
	}

	/* Not found */
	FREE(mtype, buf);
	errno = ENOENT;
	return (NULL);
}

/*
 * Get the interface list. Caller must free *bufp.
 */
static int
if_info(u_char **bufp, const char *mtype)
{
	int mib[6] = { CTL_NET, PF_ROUTE, 0, AF_INET, NET_RT_IFLIST, 0 };
	size_t length;
	u_char *buf;

	if (sysctl(mib, 6, NULL, &length, NULL, 0) == -1)
		return (-1);
	length += 256;
	if ((buf = MALLOC(mtype, length)) == NULL)
		return (-1);
	if (sysctl(mib, 6, buf, &length, NULL, 0) == -1)
		return (-1);
	*bufp = buf;
	return (length);
}


FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>