第17章 ioctl操作
17.6 get_ifi_info函数
#define _GNU_SOURCE
#include <netinet/in.h> /* sockaddr_in{} and other Internet defns */
#include <stdarg.h> /* ANSI C header file */
#include <stdlib.h>
#include <errno.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/ioctl.h>
#include <sys/un.h>
#include <net/if.h>
#include <sys/types.h>
#define MAXLINE 4096 /* max text line length */
#define IFI_ALIAS 1 /* ifi_addr is an alias */
#define IFI_NAME 16 /* same as IFNAMSIZ in <net/if.h> */
#define IFI_HADDR 8 /* allow for 64-bit EUI-64 in future */
struct ifi_info {
char ifi_name[IFI_NAME]; /* interface name, null-terminated */
short ifi_index; /* interface index */
short ifi_mtu; /* interface MTU */
u_char ifi_haddr[IFI_HADDR]; /* hardware address */
u_short ifi_hlen; /* # bytes in hardware address: 0, 6, 8 */
short ifi_flags; /* IFF_xxx constants from <net/if.h> */
short ifi_myflags; /* our own IFI_xxx flags */
struct sockaddr *ifi_addr; /* primary address */
struct sockaddr *ifi_brdaddr; /* broadcast address */
struct sockaddr *ifi_dstaddr; /* destination address */
struct ifi_info *ifi_next; /* next of these structures */
};
void err_doit(int errnoflag, const char *fmt, va_list ap)
{
int errno_save;
char buf[MAXLINE];
errno_save = errno; /* value caller might want printed */
vsprintf(buf, fmt, ap);
if (errnoflag)
sprintf(buf + strlen(buf), ": %s", strerror(errno_save));
strcat(buf, "\n");
fflush(stdout); /* in case stdout and stderr are the same */
fputs(buf, stderr);
fflush(stderr); /* SunOS 4.1.* doesn't grok NULL argument */
return;
}
void err_quit(const char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
err_doit(0, fmt, ap);
va_end(ap);
exit(1);
}
void err_sys(const char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
err_doit(1, fmt, ap);
va_end(ap);
exit(1);
}
int Socket(int family, int type, int protocol)
{
int n;
if ((n = socket(family, type, protocol)) < 0)
err_sys("socket error");
return (n);
}
void *Malloc(size_t size)
{
void *ptr;
if ((ptr = malloc(size)) == NULL)
err_sys("malloc error");
return (ptr);
}
void *Calloc(size_t n, size_t size)
{
void *ptr;
if ((ptr = calloc(n, size)) == NULL)
err_sys("calloc error");
return (ptr);
}
int Ioctl(int fd, int request, void *arg)
{
int n;
if ((n = ioctl(fd, request, arg)) == -1)
err_sys("ioctl error");
return (n); /* streamio of I_LIST returns value */
}
struct ifi_info *get_ifi_info(int family, int doaliases)
{
struct ifi_info *ifi, *ifihead, **ifipnext;
int sockfd, len, lastlen, flags, myflags, idx = 0, hlen = 0;
char *ptr, *buf, lastname[IFNAMSIZ], *cptr, *haddr = 0, *sdlname;
struct ifconf ifc;
struct ifreq *ifr, ifrcopy;
struct sockaddr_in *sinptr;
struct sockaddr_in6 *sin6ptr;
sockfd = Socket(AF_INET, SOCK_DGRAM, 0);
lastlen = 0;
len = 100 * sizeof(struct ifreq); /* initial buffer size guess */
for (;;) {
buf = (char *)Malloc(len);
ifc.ifc_len = len;
ifc.ifc_buf = buf;
if (ioctl(sockfd, SIOCGIFCONF, &ifc) < 0) {
if (errno != EINVAL || lastlen != 0)
err_sys("ioctl error");
} else {
if (ifc.ifc_len == lastlen)
break; /* success, len has not changed */
lastlen = ifc.ifc_len;
}
len += 10 * sizeof(struct ifreq); /* increment */
free(buf);
}
ifihead = NULL;
ifipnext = &ifihead;
lastname[0] = 0;
sdlname = NULL;
/* end get_ifi_info1 */
/* include get_ifi_info2 */
for (ptr = buf; ptr < buf + ifc.ifc_len;) {
ifr = (struct ifreq *)ptr;
#ifdef HAVE_SOCKADDR_SA_LEN
len = max(sizeof(struct sockaddr), ifr->ifr_addr.sa_len);
#else
switch (ifr->ifr_addr.sa_family) {
#ifdef IPV6
case AF_INET6:
len = sizeof(struct sockaddr_in6);
break;
#endif
case AF_INET:
default:
len = sizeof(struct sockaddr);
break;
}
#endif /* HAVE_SOCKADDR_SA_LEN */
ptr += sizeof(ifr->ifr_name) + len; /* for next one in buffer */
#ifdef HAVE_SOCKADDR_DL_STRUCT
/* assumes that AF_LINK precedes AF_INET or AF_INET6 */
if (ifr->ifr_addr.sa_family == AF_LINK) {
struct sockaddr_dl *sdl =
(struct sockaddr_dl *)&ifr->ifr_addr;
sdlname = ifr->ifr_name;
idx = sdl->sdl_index;
haddr = sdl->sdl_data + sdl->sdl_nlen;
hlen = sdl->sdl_alen;
}
#endif
if (ifr->ifr_addr.sa_family != family)
continue; /* ignore if not desired address family */
myflags = 0;
if ((cptr = strchr(ifr->ifr_name, ':')) != NULL)
*cptr = 0; /* replace colon with null */
if (strncmp(lastname, ifr->ifr_name, IFNAMSIZ) == 0) {
if (doaliases == 0)
continue; /* already processed this interface */
myflags = IFI_ALIAS;
}
memcpy(lastname, ifr->ifr_name, IFNAMSIZ);
ifrcopy = *ifr;
Ioctl(sockfd, SIOCGIFFLAGS, &ifrcopy);
flags = ifrcopy.ifr_flags;
if ((flags & IFF_UP) == 0)
continue; /* ignore if interface not up */
/* end get_ifi_info2 */
/* include get_ifi_info3 */
ifi = (struct ifi_info *)Calloc(1, sizeof(struct ifi_info));
*ifipnext = ifi; /* prev points to this new one */
ifipnext = &ifi->ifi_next; /* pointer to next one goes here */
ifi->ifi_flags = flags; /* IFF_xxx values */
ifi->ifi_myflags = myflags; /* IFI_xxx values */
#if defined(SIOCGIFMTU) && defined(HAVE_STRUCT_IFREQ_IFR_MTU)
Ioctl(sockfd, SIOCGIFMTU, &ifrcopy);
ifi->ifi_mtu = ifrcopy.ifr_mtu;
#else
ifi->ifi_mtu = 0;
#endif
memcpy(ifi->ifi_name, ifr->ifr_name, IFI_NAME);
ifi->ifi_name[IFI_NAME - 1] = '\0';
/* If the sockaddr_dl is from a different interface, ignore it */
if (sdlname == NULL || strcmp(sdlname, ifr->ifr_name) != 0)
idx = hlen = 0;
ifi->ifi_index = idx;
ifi->ifi_hlen = hlen;
if (ifi->ifi_hlen > IFI_HADDR)
ifi->ifi_hlen = IFI_HADDR;
if (hlen)
memcpy(ifi->ifi_haddr, haddr, ifi->ifi_hlen);
/* end get_ifi_info3 */
/* include get_ifi_info4 */
switch (ifr->ifr_addr.sa_family) {
case AF_INET:
sinptr = (struct sockaddr_in *)&ifr->ifr_addr;
ifi->ifi_addr =
(struct sockaddr *)Calloc(1,
sizeof(struct
sockaddr_in));
memcpy(ifi->ifi_addr, sinptr,
sizeof(struct sockaddr_in));
#ifdef SIOCGIFBRDADDR
if (flags & IFF_BROADCAST) {
Ioctl(sockfd, SIOCGIFBRDADDR, &ifrcopy);
sinptr = (struct sockaddr_in *)
&ifrcopy.ifr_broadaddr;
ifi->ifi_brdaddr =
(struct sockaddr *)Calloc(1,
sizeof(struct
sockaddr_in));
memcpy(ifi->ifi_brdaddr, sinptr,
sizeof(struct sockaddr_in));
}
#endif
#ifdef SIOCGIFDSTADDR
if (flags & IFF_POINTOPOINT) {
Ioctl(sockfd, SIOCGIFDSTADDR, &ifrcopy);
sinptr =
(struct sockaddr_in *)&ifrcopy.ifr_dstaddr;
ifi->ifi_dstaddr =
Calloc(1, sizeof(struct sockaddr_in));
memcpy(ifi->ifi_dstaddr, sinptr,
sizeof(struct sockaddr_in));
}
#endif
break;
case AF_INET6:
sin6ptr = (struct sockaddr_in6 *)&ifr->ifr_addr;
ifi->ifi_addr = Calloc(1, sizeof(struct sockaddr_in6));
memcpy(ifi->ifi_addr, sin6ptr,
sizeof(struct sockaddr_in6));
#ifdef SIOCGIFDSTADDR
if (flags & IFF_POINTOPOINT) {
Ioctl(sockfd, SIOCGIFDSTADDR, &ifrcopy);
sin6ptr =
(struct sockaddr_in6 *)&ifrcopy.ifr_dstaddr;
ifi->ifi_dstaddr =
Calloc(1, sizeof(struct sockaddr_in6));
memcpy(ifi->ifi_dstaddr, sin6ptr,
sizeof(struct sockaddr_in6));
}
#endif
break;
default:
break;
}
}
free(buf);
return (ifihead); /* pointer to first structure in linked list */
}
/* end get_ifi_info4 */
/* include free_ifi_info */
void free_ifi_info(struct ifi_info *ifihead)
{
struct ifi_info *ifi, *ifinext;
for (ifi = ifihead; ifi != NULL; ifi = ifinext) {
if (ifi->ifi_addr != NULL)
free(ifi->ifi_addr);
if (ifi->ifi_brdaddr != NULL)
free(ifi->ifi_brdaddr);
if (ifi->ifi_dstaddr != NULL)
free(ifi->ifi_dstaddr);
ifinext = ifi->ifi_next; /* can't fetch ifi_next after free() */
free(ifi); /* the ifi_info{} itself */
}
}
/* end free_ifi_info */
struct ifi_info *Get_ifi_info(int family, int doaliases)
{
struct ifi_info *ifi;
if ((ifi = get_ifi_info(family, doaliases)) == NULL)
err_quit("get_ifi_info error");
return (ifi);
}
char *sock_ntop_host(const struct sockaddr *sa, socklen_t salen)
{
static char str[128]; /* Unix domain is largest */
switch (sa->sa_family) {
case AF_INET:{
struct sockaddr_in *sin = (struct sockaddr_in *)sa;
if (inet_ntop(AF_INET, &sin->sin_addr, str, sizeof(str))
== NULL)
return (NULL);
return (str);
}
#ifdef IPV6
case AF_INET6:{
struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)sa;
if (inet_ntop
(AF_INET6, &sin6->sin6_addr, str,
sizeof(str)) == NULL)
return (NULL);
return (str);
}
#endif
#ifdef AF_UNIX
case AF_UNIX:{
struct sockaddr_un *unp = (struct sockaddr_un *)sa;
/* OK to have no pathname bound to the socket: happens on
every connect() unless client calls bind() first. */
if (unp->sun_path[0] == 0)
strcpy(str, "(no pathname bound)");
else
snprintf(str, sizeof(str), "%s", unp->sun_path);
return (str);
}
#endif
#ifdef HAVE_SOCKADDR_DL_STRUCT
case AF_LINK:{
struct sockaddr_dl *sdl = (struct sockaddr_dl *)sa;
if (sdl->sdl_nlen > 0)
snprintf(str, sizeof(str), "%*s",
sdl->sdl_nlen, &sdl->sdl_data[0]);
else
snprintf(str, sizeof(str), "AF_LINK, index=%d",
sdl->sdl_index);
return (str);
}
#endif
default:
snprintf(str, sizeof(str),
"sock_ntop_host: unknown AF_xxx: %d, len %d",
sa->sa_family, salen);
return (str);
}
return (NULL);
}
char *Sock_ntop_host(const struct sockaddr *sa, socklen_t salen)
{
char *ptr;
if ((ptr = sock_ntop_host(sa, salen)) == NULL)
err_sys("sock_ntop_host error"); /* inet_ntop() sets errno */
return (ptr);
}
int main(int argc, char **argv)
{
struct ifi_info *ifi, *ifihead;
struct sockaddr *sa;
u_char *ptr;
int i, family, doaliases;
if (argc != 3)
err_quit("usage: prifinfo <inet4|inet6> <doaliases>");
if (strcmp(argv[1], "inet4") == 0)
family = AF_INET;
#ifdef IPv6
else if (strcmp(argv[1], "inet6") == 0)
family = AF_INET6;
#endif
else
err_quit("invalid <address-family>");
doaliases = atoi(argv[2]);
for (ifihead = ifi = Get_ifi_info(family, doaliases);
ifi != NULL; ifi = ifi->ifi_next) {
printf("%s: ", ifi->ifi_name);
if (ifi->ifi_index != 0)
printf("(%d) ", ifi->ifi_index);
printf("<"); /* *INDENT-OFF* */
if (ifi->ifi_flags & IFF_UP)
printf("UP ");
if (ifi->ifi_flags & IFF_BROADCAST)
printf("BCAST ");
if (ifi->ifi_flags & IFF_MULTICAST)
printf("MCAST ");
if (ifi->ifi_flags & IFF_LOOPBACK)
printf("LOOP ");
if (ifi->ifi_flags & IFF_POINTOPOINT)
printf("P2P ");
printf(">\n");
/* *INDENT-ON* */
if ((i = ifi->ifi_hlen) > 0) {
ptr = ifi->ifi_haddr;
do {
printf("%s%x",
(i == ifi->ifi_hlen) ? " " : ":",
*ptr++);
} while (--i > 0);
printf("\n");
}
if (ifi->ifi_mtu != 0)
printf(" MTU: %d\n", ifi->ifi_mtu);
if ((sa = ifi->ifi_addr) != NULL)
printf(" IP addr: %s\n",
Sock_ntop_host(sa, sizeof(*sa)));
if ((sa = ifi->ifi_brdaddr) != NULL)
printf(" broadcast addr: %s\n",
Sock_ntop_host(sa, sizeof(*sa)));
if ((sa = ifi->ifi_dstaddr) != NULL)
printf(" destination addr: %s\n",
Sock_ntop_host(sa, sizeof(*sa)));
}
free_ifi_info(ifihead);
exit(0);
}