freebsd-dev/lib/libc/net/ifname.c
Jonathan Lemon c479a8493c Use the new SIOCGIFINDEX ioctl to efficiently map a name to an index.
If the syscall fails, fall back on the old method as a compatability
measure.
2001-10-17 20:08:15 +00:00

233 lines
5.7 KiB
C

/*
* Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the project nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON 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 ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* $FreeBSD$
*/
/*
* TODO:
* - prototype defs into arpa/inet.h, not net/if.h (bsd-api-new-02)
*/
#include <sys/param.h>
#include <sys/socket.h>
#include <sys/sockio.h>
#include <sys/sysctl.h>
#include <net/if.h>
#include <net/route.h>
#include <net/if_dl.h>
#include <errno.h>
#include <stdlib.h>
#include <string.h>
#define ROUNDUP(a) \
((a) > 0 ? (1 + (((a) - 1) | (sizeof(long) - 1))) : sizeof(long))
#define ADVANCE(x, n) (x += ROUNDUP((n)->sa_len))
static unsigned int
if_onametoindex(ifname)
const char *ifname;
{
struct if_nameindex *iff = if_nameindex(), *ifx;
int ret;
if (iff == NULL) return 0;
ifx = iff;
while (ifx->if_name != NULL) {
if (strcmp(ifx->if_name, ifname) == 0) {
ret = ifx->if_index;
if_freenameindex(iff);
return ret;
}
ifx++;
}
if_freenameindex(iff);
errno = ENXIO;
return 0;
}
unsigned int
if_nametoindex(ifname)
const char *ifname;
{
int s;
struct ifreq ifr;
s = socket(AF_INET, SOCK_DGRAM, 0);
if (s == -1)
return (0);
strlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
if (ioctl(s, SIOCGIFINDEX, &ifr) == -1) {
close (s);
return (if_onametoindex(ifname));
}
close(s);
return (ifr.ifr_index);
}
char *
if_indextoname(ifindex, ifname)
unsigned int ifindex;
char *ifname; /* at least IF_NAMESIZE */
{
struct if_nameindex *iff = if_nameindex(), *ifx;
char *cp, *dp;
if (iff == NULL) return NULL;
ifx = iff;
while (ifx->if_index != 0) {
if (ifx->if_index == ifindex) {
cp = ifname;
dp = ifx->if_name;
while ((*cp++ = *dp++)) ;
if_freenameindex(iff);
return (ifname);
}
ifx++;
}
if_freenameindex(iff);
errno = ENXIO;
return NULL;
}
struct if_nameindex *
if_nameindex()
{
size_t needed;
int mib[6], i, ifn = 0, off = 0, hlen;
char *buf = NULL, *lim, *next, *cp, *ifbuf = NULL;
struct rt_msghdr *rtm;
struct if_msghdr *ifm;
struct sockaddr *sa;
struct sockaddr_dl *sdl;
struct if_nameindex *ret = NULL;
static int ifxs = 64; /* initial upper limit */
struct _ifx {
int if_index;
int if_off;
} *ifx = NULL;
mib[0] = CTL_NET;
mib[1] = PF_ROUTE;
mib[2] = 0; /* protocol */
mib[3] = 0; /* wildcard address family */
mib[4] = NET_RT_IFLIST;
mib[5] = 0; /* no flags */
if (sysctl(mib, 6, NULL, &needed, NULL, 0) < 0)
return NULL;
if ((buf = malloc(needed)) == NULL) {
errno = ENOMEM;
goto end;
}
/* XXX: we may have allocated too much than necessary */
if ((ifbuf = malloc(needed)) == NULL) {
errno = ENOMEM;
goto end;
}
if ((ifx = (struct _ifx *)malloc(sizeof(*ifx) * ifxs)) == NULL) {
errno = ENOMEM;
goto end;
}
if (sysctl(mib, 6, buf, &needed, NULL, 0) < 0) {
/* sysctl has set errno */
goto end;
}
lim = buf + needed;
for (next = buf; next < lim; next += rtm->rtm_msglen) {
rtm = (struct rt_msghdr *)next;
if (rtm->rtm_version != RTM_VERSION) {
errno = EPROTONOSUPPORT;
goto end;
}
switch (rtm->rtm_type) {
case RTM_IFINFO:
ifm = (struct if_msghdr *)rtm;
ifx[ifn].if_index = ifm->ifm_index;
ifx[ifn].if_off = off;
cp = (char *)(ifm + 1);
for (i = 1; i; i <<= 1) {
if (i & ifm->ifm_addrs) {
sa = (struct sockaddr *)cp;
if (i == RTA_IFP &&
sa->sa_family == AF_LINK) {
sdl = (struct sockaddr_dl *)sa;
memcpy(ifbuf + off,
sdl->sdl_data,
sdl->sdl_nlen);
off += sdl->sdl_nlen;
*(ifbuf + off) = '\0';
off++;
}
ADVANCE(cp, sa);
}
}
if (++ifn == ifxs) {
/* we need more memory */
struct _ifx *newifx;
ifxs *= 2;
if ((newifx = (struct _ifx *)malloc(sizeof(*newifx) * ifxs)) == NULL) {
errno = ENOMEM;
goto end;
}
/* copy and free old data */
memcpy(newifx, ifx, (sizeof(*ifx) * ifxs) / 2);
free(ifx);
ifx = newifx;
}
}
}
hlen = sizeof(struct if_nameindex) * (ifn + 1);
if ((cp = (char *)malloc(hlen + off)) == NULL) {
errno = ENOMEM;
goto end;
}
bcopy(ifbuf, cp + hlen, off);
ret = (struct if_nameindex *)cp;
for (i = 0; i < ifn; i++) {
ret[i].if_index = ifx[i].if_index;
ret[i].if_name = cp + hlen + ifx[i].if_off;
}
ret[ifn].if_index = 0;
ret[ifn].if_name = NULL;
end:
if (buf) free(buf);
if (ifbuf) free(ifbuf);
if (ifx) free(ifx);
return ret;
}
void if_freenameindex(ptr)
struct if_nameindex *ptr;
{
free(ptr);
}