summaryrefslogtreecommitdiffstats
path: root/contrib/libpcap/inet.c
diff options
context:
space:
mode:
Diffstat (limited to 'contrib/libpcap/inet.c')
-rw-r--r--contrib/libpcap/inet.c949
1 files changed, 842 insertions, 107 deletions
diff --git a/contrib/libpcap/inet.c b/contrib/libpcap/inet.c
index 0b5b696..fa97aaf 100644
--- a/contrib/libpcap/inet.c
+++ b/contrib/libpcap/inet.c
@@ -1,3 +1,4 @@
+/* -*- Mode: c; tab-width: 8; indent-tabs-mode: 1; c-basic-offset: 8; -*- */
/*
* Copyright (c) 1994, 1995, 1996, 1997, 1998
* The Regents of the University of California. All rights reserved.
@@ -33,7 +34,7 @@
#ifndef lint
static const char rcsid[] =
- "@(#) $Header: /tcpdump/master/libpcap/inet.c,v 1.36 2000/09/20 15:10:29 torsten Exp $ (LBL)";
+ "@(#) $Header: /tcpdump/master/libpcap/inet.c,v 1.45 2001/10/28 20:40:43 guy Exp $ (LBL)";
#endif
#ifdef HAVE_CONFIG_H
@@ -61,6 +62,11 @@ struct rtentry;
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
+#ifdef HAVE_LIMITS_H
+#include <limits.h>
+#else
+#define INT_MAX 2147483647
+#endif
#ifdef HAVE_IFADDRS_H
#include <ifaddrs.h>
#endif
@@ -73,189 +79,918 @@ struct rtentry;
/* Not all systems have IFF_LOOPBACK */
#ifdef IFF_LOOPBACK
-#define ISLOOPBACK(p) ((p)->ifr_flags & IFF_LOOPBACK)
-#define ISLOOPBACK_IFA(p) ((p)->ifa_flags & IFF_LOOPBACK)
+#define ISLOOPBACK(name, flags) ((flags) & IFF_LOOPBACK)
#else
-#define ISLOOPBACK(p) ((p)->ifr_name[0] == 'l' && (p)->ifr_name[1] == 'o' && \
- (isdigit((p)->ifr_name[2]) || (p)->ifr_name[2] == '\0'))
-#define ISLOOPBACK_IFA(p) ((p)->ifa_name[0] == 'l' && (p)->ifa_name[1] == 'o' && \
- (isdigit((p)->ifa_name[2]) || (p)->ifa_name[2] == '\0'))
+#define ISLOOPBACK(name, flags) ((name)[0] == 'l' && (name)[1] == 'o' && \
+ (isdigit((unsigned char)((name)[2])) || (name)[2] == '\0'))
#endif
/*
- * Return the name of a network interface attached to the system, or NULL
- * if none can be found. The interface must be configured up; the
- * lowest unit number is preferred; loopback is ignored.
+ * This is fun.
+ *
+ * In older BSD systems, socket addresses were fixed-length, and
+ * "sizeof (struct sockaddr)" gave the size of the structure.
+ * All addresses fit within a "struct sockaddr".
+ *
+ * In newer BSD systems, the socket address is variable-length, and
+ * there's an "sa_len" field giving the length of the structure;
+ * this allows socket addresses to be longer than 2 bytes of family
+ * and 14 bytes of data.
+ *
+ * Some commercial UNIXes use the old BSD scheme, and some might use
+ * the new BSD scheme.
+ *
+ * GNU libc uses neither scheme, but has an "SA_LEN()" macro that
+ * determines the size based on the address family.
*/
-char *
-pcap_lookupdev(errbuf)
- register char *errbuf;
+#ifndef SA_LEN
+#ifdef HAVE_SOCKADDR_SA_LEN
+#define SA_LEN(addr) ((addr)->sa_len)
+#else /* HAVE_SOCKADDR_SA_LEN */
+#define SA_LEN(addr) (sizeof (struct sockaddr))
+#endif /* HAVE_SOCKADDR_SA_LEN */
+#endif /* SA_LEN */
+
+/*
+ * Description string for the "any" device.
+ */
+static const char any_descr[] = "Pseudo-device that captures on all interfaces";
+
+static struct sockaddr *
+dup_sockaddr(struct sockaddr *sa)
+{
+ struct sockaddr *newsa;
+ unsigned int size;
+
+ size = SA_LEN(sa);
+ if ((newsa = malloc(size)) == NULL)
+ return (NULL);
+ return (memcpy(newsa, sa, size));
+}
+
+static int
+get_instance(char *name)
+{
+ char *cp, *endcp;
+ int n;
+
+ if (strcmp(name, "any") == 0) {
+ /*
+ * Give the "any" device an artificially high instance
+ * number, so it shows up after all other non-loopback
+ * interfaces.
+ */
+ return INT_MAX;
+ }
+
+ endcp = name + strlen(name);
+ for (cp = name; cp < endcp && !isdigit((unsigned char)*cp); ++cp)
+ continue;
+
+ if (isdigit((unsigned char)*cp))
+ n = atoi(cp);
+ else
+ n = 0;
+ return (n);
+}
+
+static int
+add_or_find_if(pcap_if_t **curdev_ret, pcap_if_t **alldevs, char *name,
+ u_int flags, const char *description, char *errbuf)
+{
+ pcap_t *p;
+ pcap_if_t *curdev, *prevdev, *nextdev;
+ int this_instance;
+
+ /*
+ * Can we open this interface for live capture?
+ */
+ p = pcap_open_live(name, 68, 0, 0, errbuf);
+ if (p == NULL) {
+ /*
+ * No. Don't bother including it.
+ * Don't treat this as an error, though.
+ */
+ *curdev_ret = NULL;
+ return (0);
+ }
+ pcap_close(p);
+
+ /*
+ * Is there already an entry in the list for this interface?
+ */
+ for (curdev = *alldevs; curdev != NULL; curdev = curdev->next) {
+ if (strcmp(name, curdev->name) == 0)
+ break; /* yes, we found it */
+ }
+ if (curdev == NULL) {
+ /*
+ * No, we didn't find it.
+ * Allocate a new entry.
+ */
+ curdev = malloc(sizeof(pcap_if_t));
+ if (curdev == NULL) {
+ (void)snprintf(errbuf, PCAP_ERRBUF_SIZE,
+ "malloc: %s", pcap_strerror(errno));
+ return (-1);
+ }
+
+ /*
+ * Fill in the entry.
+ */
+ curdev->next = NULL;
+ curdev->name = malloc(strlen(name) + 1);
+ strcpy(curdev->name, name);
+ if (description != NULL) {
+ /*
+ * We have a description for this interface.
+ */
+ curdev->description = malloc(strlen(description) + 1);
+ strcpy(curdev->description, description);
+ } else {
+ /*
+ * We don't.
+ */
+ curdev->description = NULL;
+ }
+ curdev->addresses = NULL; /* list starts out as empty */
+ curdev->flags = 0;
+ if (ISLOOPBACK(name, flags))
+ curdev->flags |= PCAP_IF_LOOPBACK;
+
+ /*
+ * Add it to the list, in the appropriate location.
+ * First, get the instance number of this interface.
+ */
+ this_instance = get_instance(name);
+
+ /*
+ * Now look for the last interface with an instance number
+ * less than or equal to the new interface's instance
+ * number - except that non-loopback interfaces are
+ * arbitrarily treated as having interface numbers less
+ * than those of loopback interfaces, so the loopback
+ * interfaces are put at the end of the list.
+ *
+ * We start with "prevdev" being NULL, meaning we're before
+ * the first element in the list.
+ */
+ prevdev = NULL;
+ for (;;) {
+ /*
+ * Get the interface after this one.
+ */
+ if (prevdev == NULL) {
+ /*
+ * The next element is the first element.
+ */
+ nextdev = *alldevs;
+ } else
+ nextdev = prevdev->next;
+
+ /*
+ * Are we at the end of the list?
+ */
+ if (nextdev == NULL) {
+ /*
+ * Yes - we have to put the new entry
+ * after "prevdev".
+ */
+ break;
+ }
+
+ /*
+ * Is the new interface a non-loopback interface
+ * and the next interface a loopback interface?
+ */
+ if (!(curdev->flags & PCAP_IF_LOOPBACK) &&
+ (nextdev->flags & PCAP_IF_LOOPBACK)) {
+ /*
+ * Yes, we should put the new entry
+ * before "nextdev", i.e. after "prevdev".
+ */
+ break;
+ }
+
+ /*
+ * Is the new interface's instance number less
+ * than the next interface's instance number,
+ * and is it the case that the new interface is a
+ * non-loopback interface or the next interface is
+ * a loopback interface?
+ *
+ * (The goal of both loopback tests is to make
+ * sure that we never put a loopback interface
+ * before any non-loopback interface and that we
+ * always put a non-loopback interface before all
+ * loopback interfaces.)
+ */
+ if (this_instance < get_instance(nextdev->name) &&
+ (!(curdev->flags & PCAP_IF_LOOPBACK) ||
+ (nextdev->flags & PCAP_IF_LOOPBACK))) {
+ /*
+ * Yes - we should put the new entry
+ * before "nextdev", i.e. after "prevdev".
+ */
+ break;
+ }
+
+ prevdev = nextdev;
+ }
+
+ /*
+ * Insert before "nextdev".
+ */
+ curdev->next = nextdev;
+
+ /*
+ * Insert after "prevdev" - unless "prevdev" is null,
+ * in which case this is the first interface.
+ */
+ if (prevdev == NULL) {
+ /*
+ * This is the first interface. Pass back a
+ * pointer to it, and put "curdev" before
+ * "nextdev".
+ */
+ *alldevs = curdev;
+ } else
+ prevdev->next = curdev;
+ }
+
+ *curdev_ret = curdev;
+ return (0);
+}
+
+static int
+add_addr_to_iflist(pcap_if_t **alldevs, char *name, u_int flags,
+ struct sockaddr *addr, struct sockaddr *netmask,
+ struct sockaddr *broadaddr, struct sockaddr *dstaddr, char *errbuf)
{
+ pcap_if_t *curdev;
+ pcap_addr_t *curaddr, *prevaddr, *nextaddr;
+
+ if (add_or_find_if(&curdev, alldevs, name, flags, NULL, errbuf) == -1) {
+ /*
+ * Error - give up.
+ */
+ return (-1);
+ }
+ if (curdev == NULL) {
+ /*
+ * Device wasn't added because it can't be opened.
+ * Not a fatal error.
+ */
+ return (0);
+ }
+
+ /*
+ * "curdev" is an entry for this interface; add an entry for this
+ * address to its list of addresses.
+ *
+ * Allocate the new entry and fill it in.
+ */
+ curaddr = malloc(sizeof(pcap_addr_t));
+ if (curaddr == NULL) {
+ (void)snprintf(errbuf, PCAP_ERRBUF_SIZE,
+ "malloc: %s", pcap_strerror(errno));
+ return (-1);
+ }
+
+ curaddr->next = NULL;
+ if (addr != NULL) {
+ curaddr->addr = dup_sockaddr(addr);
+ if (curaddr->addr == NULL) {
+ (void)snprintf(errbuf, PCAP_ERRBUF_SIZE,
+ "malloc: %s", pcap_strerror(errno));
+ free(curaddr);
+ return (-1);
+ }
+ } else
+ curaddr->addr = NULL;
+
+ if (netmask != NULL) {
+ curaddr->netmask = dup_sockaddr(netmask);
+ if (curaddr->netmask == NULL) {
+ (void)snprintf(errbuf, PCAP_ERRBUF_SIZE,
+ "malloc: %s", pcap_strerror(errno));
+ free(curaddr);
+ return (-1);
+ }
+ } else
+ curaddr->netmask = NULL;
+
+ if (broadaddr != NULL) {
+ curaddr->broadaddr = dup_sockaddr(broadaddr);
+ if (curaddr->broadaddr == NULL) {
+ (void)snprintf(errbuf, PCAP_ERRBUF_SIZE,
+ "malloc: %s", pcap_strerror(errno));
+ free(curaddr);
+ return (-1);
+ }
+ } else
+ curaddr->broadaddr = NULL;
+
+ if (dstaddr != NULL) {
+ curaddr->dstaddr = dup_sockaddr(dstaddr);
+ if (curaddr->dstaddr == NULL) {
+ (void)snprintf(errbuf, PCAP_ERRBUF_SIZE,
+ "malloc: %s", pcap_strerror(errno));
+ free(curaddr);
+ return (-1);
+ }
+ } else
+ curaddr->dstaddr = NULL;
+
+ /*
+ * Find the end of the list of addresses.
+ */
+ for (prevaddr = curdev->addresses; prevaddr != NULL; prevaddr = nextaddr) {
+ nextaddr = prevaddr->next;
+ if (nextaddr == NULL) {
+ /*
+ * This is the end of the list.
+ */
+ break;
+ }
+ }
+
+ if (prevaddr == NULL) {
+ /*
+ * The list was empty; this is the first member.
+ */
+ curdev->addresses = curaddr;
+ } else {
+ /*
+ * "prevaddr" is the last member of the list; append
+ * this member to it.
+ */
+ prevaddr->next = curaddr;
+ }
+
+ return (0);
+}
+
+static int
+pcap_add_if(pcap_if_t **devlist, char *name, u_int flags,
+ const char *description, char *errbuf)
+{
+ pcap_if_t *curdev;
+
+ return (add_or_find_if(&curdev, devlist, name, flags, description,
+ errbuf));
+}
+
+/*
+ * Get a list of all interfaces that are up and that we can open.
+ * Returns -1 on error, 0 otherwise.
+ * The list, as returned through "alldevsp", may be null if no interfaces
+ * were up and could be opened.
+ */
#ifdef HAVE_IFADDRS_H
- struct ifaddrs *ifap, *ifa, *mp;
- int n, minunit;
- char *cp;
- static char device[IF_NAMESIZE + 1];
+int
+pcap_findalldevs(pcap_if_t **alldevsp, char *errbuf)
+{
+ pcap_if_t *devlist = NULL;
+ struct ifaddrs *ifap, *ifa;
+ struct sockaddr *broadaddr, *dstaddr;
+ int ret = 0;
+ /*
+ * Get the list of interface addresses.
+ *
+ * Note: this won't return information about interfaces
+ * with no addresses; are there any such interfaces
+ * that would be capable of receiving packets?
+ * (Interfaces incapable of receiving packets aren't
+ * very interesting from libpcap's point of view.)
+ *
+ * LAN interfaces will probably have link-layer
+ * addresses; I don't know whether all implementations
+ * of "getifaddrs()" now, or in the future, will return
+ * those.
+ */
if (getifaddrs(&ifap) != 0) {
(void)snprintf(errbuf, PCAP_ERRBUF_SIZE,
"getifaddrs: %s", pcap_strerror(errno));
- return NULL;
+ return (-1);
+ }
+ for (ifa = ifap; ifa != NULL; ifa = ifa->ifa_next) {
+ /*
+ * Is this interface up?
+ */
+ if (!(ifa->ifa_flags & IFF_UP)) {
+ /*
+ * No, so don't add it to the list.
+ */
+ continue;
+ }
+
+ /*
+ * "ifa_broadaddr" may be non-null even on
+ * non-broadcast interfaces; "ifa_dstaddr"
+ * was, on at least one FreeBSD 4.1 system,
+ * non-null on a non-point-to-point
+ * interface.
+ */
+ if (ifa->ifa_flags & IFF_BROADCAST)
+ broadaddr = ifa->ifa_broadaddr;
+ else
+ broadaddr = NULL;
+ if (ifa->ifa_flags & IFF_POINTOPOINT)
+ dstaddr = ifa->ifa_dstaddr;
+ else
+ dstaddr = NULL;
+
+ /*
+ * Add information for this address to the list.
+ */
+ if (add_addr_to_iflist(&devlist, ifa->ifa_name,
+ ifa->ifa_flags, ifa->ifa_addr, ifa->ifa_netmask,
+ broadaddr, dstaddr, errbuf) < 0) {
+ ret = -1;
+ break;
+ }
}
- mp = NULL;
- minunit = 666;
- for (ifa = ifap; ifa; ifa = ifa->ifa_next) {
- const char *endcp;
+ freeifaddrs(ifap);
- if ((ifa->ifa_flags & IFF_UP) == 0 || ISLOOPBACK_IFA(ifa))
- continue;
+ if (ret != -1) {
+ /*
+ * We haven't had any errors yet; add the "any" device,
+ * if we can open it.
+ */
+ if (pcap_add_if(&devlist, "any", 0, any_descr, errbuf) < 0)
+ ret = -1;
+ }
+
+ if (ret == -1) {
+ /*
+ * We had an error; free the list we've been constructing.
+ */
+ if (devlist != NULL) {
+ pcap_freealldevs(devlist);
+ devlist = NULL;
+ }
+ }
+
+ *alldevsp = devlist;
+ return (ret);
+}
+#else /* HAVE_IFADDRS_H */
+#ifdef HAVE_PROC_NET_DEV
+/*
+ * Get from "/proc/net/dev" all interfaces listed there; if they're
+ * already in the list of interfaces we have, that won't add another
+ * instance, but if they're not, that'll add them.
+ *
+ * We don't bother getting any addresses for them; it appears you can't
+ * use SIOCGIFADDR on Linux to get IPv6 addresses for interfaces, and,
+ * although some other types of addresses can be fetched with SIOCGIFADDR,
+ * we don't bother with them for now.
+ *
+ * We also don't fail if we couldn't open "/proc/net/dev"; we just leave
+ * the list of interfaces as is.
+ */
+static int
+scan_proc_net_dev(pcap_if_t **devlistp, int fd, char *errbuf)
+{
+ FILE *proc_net_f;
+ char linebuf[512];
+ int linenum;
+ unsigned char *p;
+ char name[512]; /* XXX - pick a size */
+ char *q, *saveq;
+ struct ifreq ifrflags;
+ int ret = 0;
- endcp = ifa->ifa_name + strlen(ifa->ifa_name);
- for (cp = ifa->ifa_name; cp < endcp && !isdigit(*cp); ++cp)
+ proc_net_f = fopen("/proc/net/dev", "r");
+ if (proc_net_f == NULL)
+ return (0);
+
+ for (linenum = 1;
+ fgets(linebuf, sizeof linebuf, proc_net_f) != NULL; linenum++) {
+ /*
+ * Skip the first two lines - they're headers.
+ */
+ if (linenum <= 2)
continue;
- if (isdigit (*cp)) {
- n = atoi(cp);
- } else {
- n = 0;
+ p = &linebuf[0];
+
+ /*
+ * Skip leading white space.
+ */
+ while (*p != '\0' && isspace(*p))
+ p++;
+ if (*p == '\0' || *p == '\n')
+ continue; /* blank line */
+
+ /*
+ * Get the interface name.
+ */
+ q = &name[0];
+ while (*p != '\0' && !isspace(*p)) {
+ if (*p == ':') {
+ /*
+ * This could be the separator between a
+ * name and an alias number, or it could be
+ * the separator between a name with no
+ * alias number and the next field.
+ *
+ * If there's a colon after digits, it
+ * separates the name and the alias number,
+ * otherwise it separates the name and the
+ * next field.
+ */
+ saveq = q;
+ while (isdigit(*p))
+ *q++ = *p++;
+ if (*p != ':') {
+ /*
+ * That was the next field,
+ * not the alias number.
+ */
+ q = saveq;
+ }
+ break;
+ } else
+ *q++ = *p++;
+ }
+ *q = '\0';
+
+ /*
+ * Get the flags for this interface, and skip it if
+ * it's not up.
+ */
+ strncpy(ifrflags.ifr_name, name, sizeof(ifrflags.ifr_name));
+ if (ioctl(fd, SIOCGIFFLAGS, (char *)&ifrflags) < 0) {
+ if (errno == ENXIO)
+ continue;
+ (void)snprintf(errbuf, PCAP_ERRBUF_SIZE,
+ "SIOCGIFFLAGS: %.*s: %s",
+ (int)sizeof(ifrflags.ifr_name),
+ ifrflags.ifr_name,
+ pcap_strerror(errno));
+ ret = -1;
+ break;
}
- if (n < minunit) {
- minunit = n;
- mp = ifa;
+ if (!(ifrflags.ifr_flags & IFF_UP))
+ continue;
+
+ /*
+ * Add an entry for this interface, with no addresses.
+ */
+ if (pcap_add_if(devlistp, name, ifrflags.ifr_flags, NULL,
+ errbuf) == -1) {
+ /*
+ * Failure.
+ */
+ ret = -1;
+ break;
}
}
- if (mp == NULL) {
- (void)strlcpy(errbuf, "no suitable device found",
- PCAP_ERRBUF_SIZE);
-#ifdef HAVE_FREEIFADDRS
- freeifaddrs(ifap);
-#else
- free(ifap);
-#endif
- return (NULL);
+ if (ret != -1) {
+ /*
+ * Well, we didn't fail for any other reason; did we
+ * fail due to an error reading the file?
+ */
+ if (ferror(proc_net_f)) {
+ (void)snprintf(errbuf, PCAP_ERRBUF_SIZE,
+ "Error reading /proc/net/dev: %s",
+ pcap_strerror(errno));
+ ret = -1;
+ }
}
- (void)strlcpy(device, mp->ifa_name, sizeof(device));
-#ifdef HAVE_FREEIFADDRS
- freeifaddrs(ifap);
-#else
- free(ifap);
-#endif
- return (device);
-#else
- register int fd, minunit, n;
- register char *cp;
- register struct ifreq *ifrp, *ifend, *ifnext, *mp;
+ (void)fclose(proc_net_f);
+ return (ret);
+}
+#endif /* HAVE_PROC_NET_DEV */
+
+int
+pcap_findalldevs(pcap_if_t **alldevsp, char *errbuf)
+{
+ pcap_if_t *devlist = NULL;
+ register int fd;
+ register struct ifreq *ifrp, *ifend, *ifnext;
+ int n;
struct ifconf ifc;
- char *buf;
- struct ifreq ifr;
- static char device[sizeof(ifrp->ifr_name) + 1];
+ char *buf = NULL;
unsigned buf_size;
+ struct ifreq ifrflags, ifrnetmask, ifrbroadaddr, ifrdstaddr;
+ struct sockaddr *netmask, *broadaddr, *dstaddr;
+ int ret = 0;
+ /*
+ * Create a socket from which to fetch the list of interfaces.
+ */
fd = socket(AF_INET, SOCK_DGRAM, 0);
if (fd < 0) {
(void)snprintf(errbuf, PCAP_ERRBUF_SIZE,
"socket: %s", pcap_strerror(errno));
- return (NULL);
+ return (-1);
}
+ /*
+ * Start with an 8K buffer, and keep growing the buffer until
+ * we get the entire interface list or fail to get it for some
+ * reason other than EINVAL (which is presumed here to mean
+ * "buffer is too small").
+ */
buf_size = 8192;
-
for (;;) {
- buf = malloc (buf_size);
+ buf = malloc(buf_size);
if (buf == NULL) {
- close (fd);
(void)snprintf(errbuf, PCAP_ERRBUF_SIZE,
- "out of memory");
- return (NULL);
+ "malloc: %s", pcap_strerror(errno));
+ (void)close(fd);
+ return (-1);
}
ifc.ifc_len = buf_size;
ifc.ifc_buf = buf;
- memset (buf, 0, buf_size);
+ memset(buf, 0, buf_size);
if (ioctl(fd, SIOCGIFCONF, (char *)&ifc) < 0
&& errno != EINVAL) {
- free (buf);
(void)snprintf(errbuf, PCAP_ERRBUF_SIZE,
"SIOCGIFCONF: %s", pcap_strerror(errno));
(void)close(fd);
- return (NULL);
+ free(buf);
+ return (-1);
}
if (ifc.ifc_len < buf_size)
break;
- free (buf);
+ free(buf);
buf_size *= 2;
}
ifrp = (struct ifreq *)buf;
ifend = (struct ifreq *)(buf + ifc.ifc_len);
- mp = NULL;
- minunit = 666;
for (; ifrp < ifend; ifrp = ifnext) {
- const char *endcp;
-
-#ifdef HAVE_SOCKADDR_SA_LEN
- n = ifrp->ifr_addr.sa_len + sizeof(ifrp->ifr_name);
+ n = SA_LEN(&ifrp->ifr_addr) + sizeof(ifrp->ifr_name);
if (n < sizeof(*ifrp))
ifnext = ifrp + 1;
else
ifnext = (struct ifreq *)((char *)ifrp + n);
- if (ifrp->ifr_addr.sa_family != AF_INET)
- continue;
-#else
- ifnext = ifrp + 1;
-#endif
+
/*
- * Need a template to preserve address info that is
- * used below to locate the next entry. (Otherwise,
- * SIOCGIFFLAGS stomps over it because the requests
- * are returned in a union.)
+ * Get the flags for this interface, and skip it if it's
+ * not up.
*/
- strncpy(ifr.ifr_name, ifrp->ifr_name, sizeof(ifr.ifr_name));
- if (ioctl(fd, SIOCGIFFLAGS, (char *)&ifr) < 0) {
+ strncpy(ifrflags.ifr_name, ifrp->ifr_name,
+ sizeof(ifrflags.ifr_name));
+ if (ioctl(fd, SIOCGIFFLAGS, (char *)&ifrflags) < 0) {
if (errno == ENXIO)
continue;
(void)snprintf(errbuf, PCAP_ERRBUF_SIZE,
"SIOCGIFFLAGS: %.*s: %s",
- (int)sizeof(ifr.ifr_name), ifr.ifr_name,
+ (int)sizeof(ifrflags.ifr_name),
+ ifrflags.ifr_name,
pcap_strerror(errno));
- (void)close(fd);
- free (buf);
- return (NULL);
+ ret = -1;
+ break;
}
-
- /* Must be up and not the loopback */
- if ((ifr.ifr_flags & IFF_UP) == 0 || ISLOOPBACK(&ifr))
+ if (!(ifrflags.ifr_flags & IFF_UP))
continue;
- endcp = ifrp->ifr_name + strlen(ifrp->ifr_name);
- for (cp = ifrp->ifr_name; cp < endcp && !isdigit(*cp); ++cp)
- continue;
-
- if (isdigit (*cp)) {
- n = atoi(cp);
+ /*
+ * Get the netmask for this address on this interface.
+ */
+ strncpy(ifrnetmask.ifr_name, ifrp->ifr_name,
+ sizeof(ifrnetmask.ifr_name));
+ memcpy(&ifrnetmask.ifr_addr, &ifrp->ifr_addr,
+ sizeof(ifrnetmask.ifr_addr));
+ if (ioctl(fd, SIOCGIFNETMASK, (char *)&ifrnetmask) < 0) {
+ if (errno == EADDRNOTAVAIL) {
+ /*
+ * Not available.
+ */
+ netmask = NULL;
+ } else {
+ (void)snprintf(errbuf, PCAP_ERRBUF_SIZE,
+ "SIOCGIFNETMASK: %.*s: %s",
+ (int)sizeof(ifrnetmask.ifr_name),
+ ifrnetmask.ifr_name,
+ pcap_strerror(errno));
+ ret = -1;
+ break;
+ }
+ } else
+ netmask = &ifrnetmask.ifr_addr;
+
+ /*
+ * Get the broadcast address for this address on this
+ * interface (if any).
+ */
+ if (ifrflags.ifr_flags & IFF_BROADCAST) {
+ strncpy(ifrbroadaddr.ifr_name, ifrp->ifr_name,
+ sizeof(ifrbroadaddr.ifr_name));
+ memcpy(&ifrbroadaddr.ifr_addr, &ifrp->ifr_addr,
+ sizeof(ifrbroadaddr.ifr_addr));
+ if (ioctl(fd, SIOCGIFBRDADDR,
+ (char *)&ifrbroadaddr) < 0) {
+ if (errno == EADDRNOTAVAIL) {
+ /*
+ * Not available.
+ */
+ broadaddr = NULL;
+ } else {
+ (void)snprintf(errbuf, PCAP_ERRBUF_SIZE,
+ "SIOCGIFBRDADDR: %.*s: %s",
+ (int)sizeof(ifrbroadaddr.ifr_name),
+ ifrbroadaddr.ifr_name,
+ pcap_strerror(errno));
+ ret = -1;
+ break;
+ }
+ } else
+ broadaddr = &ifrbroadaddr.ifr_broadaddr;
} else {
- n = 0;
+ /*
+ * Not a broadcast interface, so no broadcast
+ * address.
+ */
+ broadaddr = NULL;
}
- if (n < minunit) {
- minunit = n;
- mp = ifrp;
+
+ /*
+ * Get the destination address for this address on this
+ * interface (if any).
+ */
+ if (ifrflags.ifr_flags & IFF_POINTOPOINT) {
+ strncpy(ifrdstaddr.ifr_name, ifrp->ifr_name,
+ sizeof(ifrdstaddr.ifr_name));
+ memcpy(&ifrdstaddr.ifr_addr, &ifrp->ifr_addr,
+ sizeof(ifrdstaddr.ifr_addr));
+ if (ioctl(fd, SIOCGIFDSTADDR,
+ (char *)&ifrdstaddr) < 0) {
+ if (errno == EADDRNOTAVAIL) {
+ /*
+ * Not available.
+ */
+ dstaddr = NULL;
+ } else {
+ (void)snprintf(errbuf, PCAP_ERRBUF_SIZE,
+ "SIOCGIFDSTADDR: %.*s: %s",
+ (int)sizeof(ifrdstaddr.ifr_name),
+ ifrdstaddr.ifr_name,
+ pcap_strerror(errno));
+ ret = -1;
+ break;
+ }
+ } else
+ dstaddr = &ifrdstaddr.ifr_dstaddr;
+ } else
+ dstaddr = NULL;
+
+ /*
+ * Add information for this address to the list.
+ */
+ if (add_addr_to_iflist(&devlist, ifrp->ifr_name,
+ ifrflags.ifr_flags, &ifrp->ifr_addr,
+ netmask, broadaddr, dstaddr, errbuf) < 0) {
+ ret = -1;
+ break;
}
}
+ free(buf);
+
+#ifdef HAVE_PROC_NET_DEV
+ if (ret != -1) {
+ /*
+ * We haven't had any errors yet; now read "/proc/net/dev",
+ * and add to the list of interfaces all interfaces listed
+ * there that we don't already have, because, on Linux,
+ * SIOCGIFCONF reports only interfaces with IPv4 addresses,
+ * so you need to read "/proc/net/dev" to get the names of
+ * the rest of the interfaces.
+ */
+ ret = scan_proc_net_dev(&devlist, fd, errbuf);
+ }
+#endif
(void)close(fd);
- if (mp == NULL) {
+
+ if (ret != -1) {
+ /*
+ * We haven't had any errors yet; add the "any" device,
+ * if we can open it.
+ */
+ if (pcap_add_if(&devlist, "any", 0, any_descr, errbuf) < 0) {
+ /*
+ * Oops, we had a fatal error.
+ */
+ ret = -1;
+ }
+ }
+
+ if (ret == -1) {
+ /*
+ * We had an error; free the list we've been constructing.
+ */
+ if (devlist != NULL) {
+ pcap_freealldevs(devlist);
+ devlist = NULL;
+ }
+ }
+
+ *alldevsp = devlist;
+ return (ret);
+}
+#endif /* HAVE_IFADDRS_H */
+
+/*
+ * Free a list of interfaces.
+ */
+void
+pcap_freealldevs(pcap_if_t *alldevs)
+{
+ pcap_if_t *curdev, *nextdev;
+ pcap_addr_t *curaddr, *nextaddr;
+
+ for (curdev = alldevs; curdev != NULL; curdev = nextdev) {
+ nextdev = curdev->next;
+
+ /*
+ * Free all addresses.
+ */
+ for (curaddr = curdev->addresses; curaddr != NULL; curaddr = nextaddr) {
+ nextaddr = curaddr->next;
+ if (curaddr->addr)
+ free(curaddr->addr);
+ if (curaddr->netmask)
+ free(curaddr->netmask);
+ if (curaddr->broadaddr)
+ free(curaddr->broadaddr);
+ if (curaddr->dstaddr)
+ free(curaddr->dstaddr);
+ free(curaddr);
+ }
+
+ /*
+ * Free the name string.
+ */
+ free(curdev->name);
+
+ /*
+ * Free the description string, if any.
+ */
+ if (curdev->description != NULL)
+ free(curdev->description);
+
+ /*
+ * Free the interface.
+ */
+ free(curdev);
+ }
+}
+
+/*
+ * Return the name of a network interface attached to the system, or NULL
+ * if none can be found. The interface must be configured up; the
+ * lowest unit number is preferred; loopback is ignored.
+ */
+char *
+pcap_lookupdev(errbuf)
+ register char *errbuf;
+{
+ pcap_if_t *alldevs;
+/* for old BSD systems, including bsdi3 */
+#ifndef IF_NAMESIZE
+#define IF_NAMESIZE IFNAMSIZ
+#endif
+ static char device[IF_NAMESIZE + 1];
+ char *ret;
+
+ if (pcap_findalldevs(&alldevs, errbuf) == -1)
+ return (NULL);
+
+ if (alldevs == NULL || (alldevs->flags & PCAP_IF_LOOPBACK)) {
+ /*
+ * There are no devices on the list, or the first device
+ * on the list is a loopback device, which means there
+ * are no non-loopback devices on the list. This means
+ * we can't return any device.
+ *
+ * XXX - why not return a loopback device? If we can't
+ * capture on it, it won't be on the list, and if it's
+ * on the list, there aren't any non-loopback devices,
+ * so why not just supply it as the default device?
+ */
(void)strlcpy(errbuf, "no suitable device found",
PCAP_ERRBUF_SIZE);
- free(buf);
- return (NULL);
+ ret = NULL;
+ } else {
+ /*
+ * Return the name of the first device on the list.
+ */
+ (void)strlcpy(device, alldevs->name, sizeof(device));
+ ret = device;
}
- (void)strlcpy(device, mp->ifr_name, sizeof(device));
- free(buf);
- return (device);
-#endif
+ pcap_freealldevs(alldevs);
+ return (ret);
}
int
OpenPOWER on IntegriCloud