From 23ba01423ed0134c33457ca251d0eab6c392b2e5 Mon Sep 17 00:00:00 2001 From: Jun-ichiro itojun Hagino Date: Wed, 5 Jul 2000 02:13:17 +0000 Subject: [PATCH] add getifaddrs(3) from bsdi. this is a magic function which lets you grab interface addresses in a portable manner, without headache of SIOCGIFCONF or sysctl. it is in bsdi/openbsd/netbsd already. from kame tree (actually, mandatory for latest kame tree). --- include/Makefile | 2 +- include/ifaddrs.h | 56 ++++++ lib/libc/net/Makefile.inc | 5 +- lib/libc/net/getifaddrs.3 | 163 ++++++++++++++++ lib/libc/net/getifaddrs.c | 379 ++++++++++++++++++++++++++++++++++++++ 5 files changed, 602 insertions(+), 3 deletions(-) create mode 100644 include/ifaddrs.h create mode 100644 lib/libc/net/getifaddrs.3 create mode 100644 lib/libc/net/getifaddrs.c diff --git a/include/Makefile b/include/Makefile index c6fd25e44d9d..6119e24cef0f 100644 --- a/include/Makefile +++ b/include/Makefile @@ -10,7 +10,7 @@ CLEANFILES= osreldate.h version vers.c SUBDIR= rpcsvc FILES= a.out.h ar.h assert.h bitstring.h ctype.h db.h dirent.h disktab.h \ dlfcn.h elf.h err.h fnmatch.h fstab.h \ - fts.h glob.h grp.h strhash.h histedit.h ieeefp.h iso646.h \ + fts.h glob.h grp.h strhash.h histedit.h ieeefp.h ifaddrs.h iso646.h \ limits.h link.h locale.h malloc.h memory.h mpool.h \ ndbm.h netdb.h nl_types.h nlist.h objformat.h \ paths.h pthread.h pthread_np.h pwd.h \ diff --git a/include/ifaddrs.h b/include/ifaddrs.h new file mode 100644 index 000000000000..6ebf288c2733 --- /dev/null +++ b/include/ifaddrs.h @@ -0,0 +1,56 @@ +/* $FreeBSD$ */ + +/* + * Copyright (c) 1995, 1999 + * Berkeley Software Design, Inc. 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. + * + * THIS SOFTWARE IS PROVIDED BY Berkeley Software Design, Inc. ``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 Berkeley Software Design, Inc. 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. + * + * BSDI ifaddrs.h,v 2.5 2000/02/23 14:51:59 dab Exp + */ + +#ifndef _IFADDRS_H_ +#define _IFADDRS_H_ + +struct ifaddrs { + struct ifaddrs *ifa_next; + char *ifa_name; + u_int ifa_flags; + struct sockaddr *ifa_addr; + struct sockaddr *ifa_netmask; + struct sockaddr *ifa_dstaddr; + void *ifa_data; +}; + +/* + * This may have been defined in . Note that if is + * to be included it must be included before this header file. + */ +#ifndef ifa_broadaddr +#define ifa_broadaddr ifa_dstaddr /* broadcast address interface */ +#endif + +#include + +__BEGIN_DECLS +extern int getifaddrs __P((struct ifaddrs **)); +extern void freeifaddrs __P((struct ifaddrs *)); +__END_DECLS + +#endif diff --git a/lib/libc/net/Makefile.inc b/lib/libc/net/Makefile.inc index 22c40109b8a2..1d3ab660da73 100644 --- a/lib/libc/net/Makefile.inc +++ b/lib/libc/net/Makefile.inc @@ -6,7 +6,7 @@ SRCS+= addr2ascii.c ascii2addr.c base64.c ether_addr.c getaddrinfo.c \ gethostbydns.c gethostbyht.c gethostbynis.c gethostnamadr.c \ - getnameinfo.c \ + getifaddrs.c getnameinfo.c \ getnetbydns.c getnetbyht.c getnetbynis.c getnetnamadr.c \ getproto.c getprotoent.c getprotoname.c getservbyname.c \ getservbyport.c getservent.c herror.c inet_addr.c ifname.c \ @@ -28,7 +28,7 @@ CFLAGS+=-DINET6 .if ${LIB} == "c" MAN3+= addr2ascii.3 byteorder.3 ethers.3 getaddrinfo.3 gethostbyname.3 \ - getipnodebyname.3 \ + getifaddrs.3 getipnodebyname.3 \ getnameinfo.3 getnetent.3 getprotoent.3 getservent.3 if_indextoname.3 \ inet.3 inet6_option_space.3 inet6_rthdr_space.3 linkaddr.3 \ rcmd.3 resolver.3 @@ -44,6 +44,7 @@ MLINKS+=gethostbyname.3 endhostent.3 gethostbyname.3 gethostbyaddr.3 \ gethostbyname.3 gethostbyname2.3 gethostbyname.3 gethostent.3 \ gethostbyname.3 herror.3 gethostbyname.3 hstrerror.3 \ gethostbyname.3 sethostent.3 +MLINKS+=getifaddrs.3 freeifaddrs.3 MLINKS+=getipnodebyname.3 getipnodebyaddr.3 getipnodebyname.3 freehostent.3 MLINKS+=getnetent.3 endnetent.3 getnetent.3 getnetbyaddr.3 \ getnetent.3 getnetbyname.3 getnetent.3 setnetent.3 diff --git a/lib/libc/net/getifaddrs.3 b/lib/libc/net/getifaddrs.3 new file mode 100644 index 000000000000..f2fa74902960 --- /dev/null +++ b/lib/libc/net/getifaddrs.3 @@ -0,0 +1,163 @@ +.\" $FreeBSD$ +.\" $KAME: getifaddrs.3,v 1.4 2000/05/17 14:13:14 itojun Exp $ +.\" BSDI getifaddrs.3,v 2.5 2000/02/23 14:51:59 dab Exp +.\" +.\" Copyright (c) 1995, 1999 +.\" Berkeley Software Design, Inc. 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. +.\" +.\" THIS SOFTWARE IS PROVIDED BY Berkeley Software Design, Inc. ``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 Berkeley Software Design, Inc. 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. +.Dd "October 12, 1995" +.Dt GETIFADDRS 3 +.Sh NAME +.Nm getifaddrs +.Nd get interface addresses +.Sh SYNOPSIS +.Fd #include +.Fd #include +.Fd #include +.Ft int +.Fn getifaddrs "struct ifaddrs **ifap" +.Ft void +.Fn freeifaddrs "struct ifaddrs *ifp" +.Sh DESCRIPTION +The +.Fn getifaddrs +function stores a reference to a linked list of the network interfaces +on the local machine in the memory referenced by +.Fa ifap . +The list consists of +.Nm ifaddrs +structures, as defined in the include file +.Aq Pa ifaddrs.h . +The +.Nm ifaddrs +structure contains at least the following entries: +.Bd -literal + struct ifaddrs *ifa_next; /* Pointer to next struct */ + char *ifa_name; /* Interface name */ + u_int ifa_flags; /* Interface flags */ + struct sockaddr *ifa_addr; /* Interface address */ + struct sockaddr *ifa_netmask; /* Interface netmask */ + struct sockaddr *ifa_broadaddr; /* Interface broadcast address */ + struct sockaddr *ifa_dstaddr; /* P2P interface destination */ + void *ifa_data; /* Address specific data */ +.Ed +.Pp +The +.Li ifa_next +field contains a pointer to the next structure on the list. +This field is +.Dv NULL +in last structure on the list. +.Pp +The +.Li ifa_name +field contains the interface name. +.Pp +The +.Li ifa_flags +field contains the interface flags, as set by +.Xr ifconfig 8 +utility. +.Pp +The +.Li ifa_addr +field references either the address of the interface or the link level +address of the interface, if one exists, otherwise it is NULL. +(The +.Li sa_family +field of the +.Li ifa_addr +field should be consulted to determine the format of the +.Li ifa_addr +address.) +.Pp +The +.Li ifa_netmask +field references the netmask associated with +.Li ifa_addr , +if one is set, otherwise it is NULL. +.Pp +The +.Li ifa_broadaddr +field, +which should only be referenced for non-P2P interfaces, +references the broadcast address associated with +.Li ifa_addr , +if one exists, otherwise it is NULL. +.Pp +The +.Li ifa_dstaddr +field references the destination address on a P2P interface, +if one exists, otherwise it is NULL. +.Pp +The +.Li ifa_data +field references address family specific data. For +.Dv AF_LINK +addresses it contains a pointer to the +.Fa struct if_data +.Pq as defined in include file Aq Pa net/if.h +which contains various interface attributes and statistics. +For all other address families, it contains a pointer to the +.Fa struct ifa_data +.Pq as defined in include file Aq Pa net/if.h +which contains per-address interface statistics. +.Pp +The data returned by +.Fn getifaddrs +is dynamically allocated and should be freed using +.Fn freeifaddrs +when no longer needed. +.Sh RETURN VALUES +Upon successful completion, a value of 0 is returned. +Otherwise, a value of -1 is returned and +.Va errno +is set to indicate the error. +.Sh ERRORS +The +.Fn getifaddrs +may fail and set +.Va errno +for any of the errors specified for the library routines +.Xr ioctl 2 , +.Xr socket 2 , +.Xr malloc 3 +or +.Xr sysctl 3 . +.Sh BUGS +If both +.Aq Pa net/if.h +and +.Aq Pa ifaddrs.h +are being included, +.Aq Pa net/if.h +.Em must +be included before +.Aq Pa ifaddrs.h . +.Sh SEE ALSO +.Xr ioctl 2 , +.Xr socket 2 , +.Xr sysctl 3 , +.Xr networking 4 , +.Xr ifconfig 8 +.Sh HISTORY +The +.Nm +implementation first appeared in BSDI BSD/OS. diff --git a/lib/libc/net/getifaddrs.c b/lib/libc/net/getifaddrs.c new file mode 100644 index 000000000000..573aadad189b --- /dev/null +++ b/lib/libc/net/getifaddrs.c @@ -0,0 +1,379 @@ +/* $FreeBSD$ */ + +/* + * Copyright (c) 1995, 1999 + * Berkeley Software Design, Inc. 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. + * + * THIS SOFTWARE IS PROVIDED BY Berkeley Software Design, Inc. ``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 Berkeley Software Design, Inc. 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. + * + * BSDI getifaddrs.c,v 2.12 2000/02/23 14:51:59 dab Exp + */ +/* + * NOTE: SIOCGIFCONF case is not LP64 friendly. it also does not perform + * try-and-error for region size. + */ +#include +#include +#include +#include +#ifdef NET_RT_IFLIST +#include +#include +#include +#include +#endif + +#include +#include +#include +#include + +#if !defined(AF_LINK) +#define SA_LEN(sa) sizeof(struct sockaddr) +#endif + +#if !defined(SA_LEN) +#define SA_LEN(sa) (sa)->sa_len +#endif + +#define SALIGN (sizeof(long) - 1) +#define SA_RLEN(sa) ((sa)->sa_len ? (((sa)->sa_len + SALIGN) & ~SALIGN) : (SALIGN + 1)) + +#ifndef ALIGNBYTES +/* + * On systems with a routing socket, ALIGNBYTES should match the value + * that the kernel uses when building the messages. + */ +#define ALIGNBYTES XXX +#endif +#ifndef ALIGN +#define ALIGN(p) (((u_long)(p) + ALIGNBYTES) &~ ALIGNBYTES) +#endif + +#if _BSDI_VERSION >= 199701 +#define HAVE_IFM_DATA +#endif + +#if _BSDI_VERSION >= 199802 +#define HAVE_IFAM_DATA +#endif + +int +getifaddrs(struct ifaddrs **pif) +{ + int icnt = 1; + int dcnt = 0; + int ncnt = 0; +#ifdef NET_RT_IFLIST + int mib[6]; + size_t needed; + char *buf; + char *next; + struct ifaddrs *cif = 0; + char *p, *p0; + struct rt_msghdr *rtm; + struct if_msghdr *ifm; + struct ifa_msghdr *ifam; + struct sockaddr_dl *dl; + struct sockaddr *sa; + struct ifaddrs *ifa, *ift; + u_short index = 0; +#else /* NET_RT_IFLIST */ + char buf[1024]; + int m, sock; + struct ifconf ifc; + struct ifreq *ifr; + struct ifreq *lifr; +#endif /* NET_RT_IFLIST */ + int i; + size_t len, alen; + char *data; + char *names; + +#ifdef NET_RT_IFLIST + 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 (-1); + if ((buf = malloc(needed)) == NULL) + return (-1); + if (sysctl(mib, 6, buf, &needed, NULL, 0) < 0) { + free(buf); + return (-1); + } + + for (next = buf; next < buf + needed; next += rtm->rtm_msglen) { + rtm = (struct rt_msghdr *)next; + if (rtm->rtm_version != RTM_VERSION) + continue; + switch (rtm->rtm_type) { + case RTM_IFINFO: + ifm = (struct if_msghdr *)rtm; + if (ifm->ifm_addrs & RTA_IFP) { + index = ifm->ifm_index; + ++icnt; + dl = (struct sockaddr_dl *)(ifm + 1); + dcnt += SA_RLEN((struct sockaddr *)dl) + + ALIGNBYTES; +#ifdef HAVE_IFM_DATA + dcnt += sizeof(ifm->ifm_data); +#endif /* HAVE_IFM_DATA */ + ncnt += dl->sdl_nlen + 1; + } else + index = 0; + break; + + case RTM_NEWADDR: + ifam = (struct ifa_msghdr *)rtm; + if (index && ifam->ifam_index != index) + abort(); /* this cannot happen */ + +#define RTA_MASKS (RTA_NETMASK | RTA_IFA | RTA_BRD) + if (index == 0 || (ifam->ifam_addrs & RTA_MASKS) == 0) + break; + p = (char *)(ifam + 1); + ++icnt; +#ifdef HAVE_IFAM_DATA + dcnt += sizeof(ifam->ifam_data) + ALIGNBYTES; +#endif /* HAVE_IFAM_DATA */ + /* Scan to look for length of address */ + alen = 0; + for (p0 = p, i = 0; i < RTAX_MAX; i++) { + if ((RTA_MASKS & ifam->ifam_addrs & (1 << i)) + == 0) + continue; + sa = (struct sockaddr *)p; + len = SA_RLEN(sa); + if (i == RTAX_IFA) { + alen = len; + break; + } + p += len; + } + for (p = p0, i = 0; i < RTAX_MAX; i++) { + if ((RTA_MASKS & ifam->ifam_addrs & (1 << i)) + == 0) + continue; + sa = (struct sockaddr *)p; + len = SA_RLEN(sa); + if (i == RTAX_NETMASK && SA_LEN(sa) == 0) + dcnt += alen; + else + dcnt += len; + p += len; + } + break; + } + } +#else /* NET_RT_IFLIST */ + ifc.ifc_buf = buf; + ifc.ifc_len = sizeof(buf); + + if ((sock = socket(AF_INET, SOCK_STREAM, 0)) < 0) + return (-1); + i = ioctl(sock, SIOCGIFCONF, (char *)&ifc); + close(sock); + if (i < 0) + return (-1); + + ifr = ifc.ifc_req; + lifr = (struct ifreq *)&ifc.ifc_buf[ifc.ifc_len]; + + while (ifr < lifr) { + struct sockaddr *sa; + + sa = &ifr->ifr_addr; + ++icnt; + dcnt += SA_RLEN(sa); + ncnt += sizeof(ifr->ifr_name) + 1; + + ifr = (struct ifreq *)(((char *)sa) + SA_LEN(sa)); + } +#endif /* NET_RT_IFLIST */ + + if (icnt + dcnt + ncnt == 1) { + *pif = NULL; + free(buf); + return (0); + } + data = malloc(sizeof(struct ifaddrs) * icnt + dcnt + ncnt); + if (data == NULL) { + free(buf); + return(-1); + } + + ifa = (struct ifaddrs *)data; + data += sizeof(struct ifaddrs) * icnt; + names = data + dcnt; + + memset(ifa, 0, sizeof(struct ifaddrs) * icnt); + ift = ifa; + +#ifdef NET_RT_IFLIST + index = 0; + for (next = buf; next < buf + needed; next += rtm->rtm_msglen) { + rtm = (struct rt_msghdr *)next; + if (rtm->rtm_version != RTM_VERSION) + continue; + switch (rtm->rtm_type) { + case RTM_IFINFO: + ifm = (struct if_msghdr *)rtm; + if (ifm->ifm_addrs & RTA_IFP) { + index = ifm->ifm_index; + dl = (struct sockaddr_dl *)(ifm + 1); + + cif = ift; + ift->ifa_name = names; + ift->ifa_flags = (int)ifm->ifm_flags; + memcpy(names, dl->sdl_data, dl->sdl_nlen); + names[dl->sdl_nlen] = 0; + names += dl->sdl_nlen + 1; + + ift->ifa_addr = (struct sockaddr *)data; + memcpy(data, dl, SA_LEN((struct sockaddr *)dl)); + data += SA_RLEN((struct sockaddr *)dl); + +#ifdef HAVE_IFM_DATA + /* ifm_data needs to be aligned */ + ift->ifa_data = data = (void *)ALIGN(data); + memcpy(data, &ifm->ifm_data, sizeof(ifm->ifm_data)); + data += sizeof(ifm->ifm_data); +#else /* HAVE_IFM_DATA */ + ift->ifa_data = NULL; +#endif /* HAVE_IFM_DATA */ + + ift = (ift->ifa_next = ift + 1); + } else + index = 0; + break; + + case RTM_NEWADDR: + ifam = (struct ifa_msghdr *)rtm; + if (index && ifam->ifam_index != index) + abort(); /* this cannot happen */ + + if (index == 0 || (ifam->ifam_addrs & RTA_MASKS) == 0) + break; + ift->ifa_name = cif->ifa_name; + ift->ifa_flags = cif->ifa_flags; + ift->ifa_data = NULL; + p = (char *)(ifam + 1); + /* Scan to look for length of address */ + alen = 0; + for (p0 = p, i = 0; i < RTAX_MAX; i++) { + if ((RTA_MASKS & ifam->ifam_addrs & (1 << i)) + == 0) + continue; + sa = (struct sockaddr *)p; + len = SA_RLEN(sa); + if (i == RTAX_IFA) { + alen = len; + break; + } + p += len; + } + for (p = p0, i = 0; i < RTAX_MAX; i++) { + if ((RTA_MASKS & ifam->ifam_addrs & (1 << i)) + == 0) + continue; + sa = (struct sockaddr *)p; + len = SA_RLEN(sa); + switch (i) { + case RTAX_IFA: + ift->ifa_addr = (struct sockaddr *)data; + memcpy(data, p, len); + data += len; + break; + + case RTAX_NETMASK: + ift->ifa_netmask = + (struct sockaddr *)data; + if (SA_LEN(sa) == 0) { + memset(data, 0, alen); + data += alen; + break; + } + memcpy(data, p, len); + data += len; + break; + + case RTAX_BRD: + ift->ifa_broadaddr = + (struct sockaddr *)data; + memcpy(data, p, len); + data += len; + break; + } + p += len; + } + +#ifdef HAVE_IFAM_DATA + /* ifam_data needs to be aligned */ + ift->ifa_data = data = (void *)ALIGN(data); + memcpy(data, &ifam->ifam_data, sizeof(ifam->ifam_data)); + data += sizeof(ifam->ifam_data); +#endif /* HAVE_IFAM_DATA */ + + ift = (ift->ifa_next = ift + 1); + break; + } + } + + free(buf); +#else /* NET_RT_IFLIST */ + ifr = ifc.ifc_req; + lifr = (struct ifreq *)&ifc.ifc_buf[ifc.ifc_len]; + + while (ifr < lifr) { + struct sockaddr *sa; + + ift->ifa_name = names; + names[sizeof(ifr->ifr_name)] = 0; + strncpy(names, ifr->ifr_name, sizeof(ifr->ifr_name)); + while (*names++) + ; + + ift->ifa_addr = (struct sockaddr *)data; + sa = &ifr->ifr_addr; + memcpy(data, sa, SA_LEN(sa)); + data += SA_RLEN(sa); + + ifr = (struct ifreq *)(((char *)sa) + SA_LEN(sa)); + ift = (ift->ifa_next = ift + 1); + } +#endif /* NET_RT_IFLIST */ + if (--ift >= ifa) { + ift->ifa_next = NULL; + *pif = ifa; + } else { + *pif = NULL; + free(ifa); + } + return (0); +} + +void +freeifaddrs(struct ifaddrs *ifp) +{ + free(ifp); +}