]> The Tcpdump Group git mirrors - libpcap/blobdiff - inet.c
From Scott Gifford:
[libpcap] / inet.c
diff --git a/inet.c b/inet.c
index a3375b067106ab86c00edb9f9190135811942de5..5fb4aacd82694847c26cf5aa2fce92d83ee526c5 100644 (file)
--- a/inet.c
+++ b/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.38 2001-07-28 22:56:34 guy Exp $ (LBL)";
+    "@(#) $Header: /tcpdump/master/libpcap/inet.c,v 1.39 2001-10-08 01:06:20 guy Exp $ (LBL)";
 #endif
 
 #ifdef HAVE_CONFIG_H
@@ -73,194 +74,730 @@ 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 */
+
+static struct sockaddr *
+dup_sockaddr(struct sockaddr *sa)
 {
-#ifdef HAVE_IFADDRS_H
-       struct ifaddrs *ifap, *ifa, *mp;
-       int n, minunit;
-       char *cp;
-/* for old BSD systems, including bsdi3 */
-#ifndef IF_NAMESIZE
-#define IF_NAMESIZE IFNAMSIZ
-#endif
-       static char device[IF_NAMESIZE + 1];
+       struct sockaddr *newsa;
+       unsigned int size;
+       
+       size = SA_LEN(sa);
+       if ((newsa = malloc(size)) == NULL)
+               return (NULL);
+       return (memcpy(newsa, sa, size));
+}
 
-       if (getifaddrs(&ifap) != 0) {
-               (void)snprintf(errbuf, PCAP_ERRBUF_SIZE,
-                   "getifaddrs: %s", pcap_strerror(errno));
-               return NULL;
+static int
+get_instance(char *name)
+{
+       char *cp, *endcp;
+       int n;
+
+       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, 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);
 
-       mp = NULL;
-       minunit = 666;
-       for (ifa = ifap; ifa; ifa = ifa->ifa_next) {
-               const char *endcp;
+       /*
+        * 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);
+               curdev->description = NULL;     /* not available */
+               curdev->addresses = NULL;       /* list starts out as empty */
+               curdev->is_loopback = ISLOOPBACK(name, flags);
 
-               if ((ifa->ifa_flags & IFF_UP) == 0 || ISLOOPBACK_IFA(ifa))
-                       continue;
+               /*
+                * Add it to the list, in the appropriate location.
+                * First, get the instance number of this interface.
+                */
+               this_instance = get_instance(name);
 
-               endcp = ifa->ifa_name + strlen(ifa->ifa_name);
-               for (cp = ifa->ifa_name; cp < endcp && !isdigit(*cp); ++cp)
-                       continue;
+               /*
+                * 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;
 
-               if (isdigit (*cp)) {
-                       n = atoi(cp);
-               } else {
-                       n = 0;
+                       /*
+                        * 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->is_loopback && nextdev->is_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->is_loopback || nextdev->is_loopback)) {
+                               /*
+                                * Yes - we should put the new entry
+                                * before "nextdev", i.e. after "prevdev".
+                                */
+                               break;
+                       }
+
+                       prevdev = nextdev;
                }
-               if (n < minunit) {
-                       minunit = n;
-                       mp = ifa;
+
+               /*
+                * 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, 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 (mp == NULL) {
-               (void)strlcpy(errbuf, "no suitable device found",
-                   PCAP_ERRBUF_SIZE);
-#ifdef HAVE_FREEIFADDRS
-               freeifaddrs(ifap);
-#else
-               free(ifap);
-#endif
-               return (NULL);
+
+       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_addanydev(pcap_if_t **devlist, char *errbuf)
+{
+       pcap_if_t *curdev;
+
+       return (add_or_find_if(&curdev, devlist, "any", 0, 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
+int
+pcap_findalldevs(alldevsp, errbuf)
+       pcap_if_t **alldevsp;
+       register char *errbuf;
+{
+       pcap_if_t *devlist = NULL;
+       struct ifaddrs *ifap, *ifa;
+       int ret = 0;
+
+
+       /*
+        * Get the list of interface addresses.
+        */
+       if (getifaddrs(&ifap) != 0) {
+               (void)snprintf(errbuf, PCAP_ERRBUF_SIZE,
+                   "getifaddrs: %s", pcap_strerror(errno));
+               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;
+               }
+
+               /*
+                * 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,
+                   ifa->ifa_broadaddr, ifa->ifa_dstaddr, errbuf) < 0) {
+                       /*
+                        * Oops, we had a fatal error.
+                        * Free the list we've been constructing, and fail.
+                        */
+                       if (devlist != NULL) {
+                               pcap_freealldevs(devlist);
+                               devlist = NULL;
+                       }
+                       ret = -1;
+                       break;
+               }
        }
 
-       (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;
+
+       if (pcap_addanydev(&devlist, errbuf) < 0) {
+               if (devlist != NULL) {
+                       pcap_freealldevs(devlist);
+                       devlist = NULL;
+               }
+               return -1;
+       }
+
+       *alldevsp = devlist;
+       return (ret);
+}
+#else /* HAVE_IFADDRS_H */
+int
+pcap_findalldevs(alldevsp, errbuf)
+       pcap_if_t **alldevsp;
+       register 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.
+        *
+        * XXX - on Linux, SIOCGIFCONF reports only interfaces with
+        * IPv4 addresses; you need to read "/proc/net/dev" to get
+        * the names of all the 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));
+                       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);
+                       close(fd);
+                       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);
+                       if (devlist != NULL) {
+                               pcap_freealldevs(devlist);
+                               devlist = 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((unsigned char)*cp); ++cp)
-                       continue;
-               
-               if (isdigit ((unsigned char)*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));
+                               if (devlist != NULL) {
+                                       pcap_freealldevs(devlist);
+                                       devlist = NULL;
+                               }
+                               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));
+                                       if (devlist != NULL) {
+                                               pcap_freealldevs(devlist);
+                                               devlist = NULL;
+                                       }
+                                       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));
+                                       if (devlist != NULL) {
+                                               pcap_freealldevs(devlist);
+                                               devlist = NULL;
+                                       }
+                                       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) {
+                       /*
+                        * Oops, we had a fatal error.
+                        * Free the list we've been constructing, and fail.
+                        */
+                       if (devlist != NULL) {
+                               pcap_freealldevs(devlist);
+                               devlist = NULL;
+                       }
+                       ret = -1;
+                       break;
                }
        }
+
        (void)close(fd);
-       if (mp == NULL) {
+       free(buf);
+
+       if (pcap_addanydev(&devlist, errbuf) < 0) {
+               if (devlist != NULL) {
+                       pcap_freealldevs(devlist);
+                       devlist = NULL;
+               }
+               return -1;
+       }
+
+       *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->is_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