]> The Tcpdump Group git mirrors - libpcap/commitdiff
From Scott Gifford:
authorguy <guy>
Mon, 8 Oct 2001 01:06:20 +0000 (01:06 +0000)
committerguy <guy>
Mon, 8 Oct 2001 01:06:20 +0000 (01:06 +0000)
Add a new "pcap_findalldevs()" routine to get a list of all
interfaces that can be opened with "pcap_open_live()", and a
"pcap_freealldevs()" routine to free the list.

Make "pcap_lookupdev()" use it, which also arranges that it will
not return a device that cannot be opened by "pcap_open_live()".

Allow the "any" device to be opened, on Linux, with "promisc"
non-zero; ignore the request for promiscuity, and return a
warning message indicating that promiscuous mode isn't supported
on the "any" device.

Document "pcap_findalldevs()" and "pcap_lookupdev()", and clean up some
items in the libpcap man page.

CREDITS
inet.c
pcap-linux.c
pcap.3
pcap.h

diff --git a/CREDITS b/CREDITS
index 7e6b7740f1c29a42feaae4a915bebb379aaf83b8..66d139d86943ea12c3888e3dbb18f103ebf75430 100644 (file)
--- a/CREDITS
+++ b/CREDITS
@@ -39,6 +39,7 @@ Additional people who have contributed patches:
        Rafal Maszkowski                <[email protected]>
        Rick Jones                      <[email protected]>
        Scott Barron                    <[email protected]>
+       Scott Gifford                   <[email protected]>
        Stefan Hudson                   <[email protected]>
        Tony Li                         <[email protected]>
        Uns Lider                       <[email protected]>
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
index 3e179a80904213d7528468f1dbc5a06a758d13e7..0211c2d297983e969bd8824b479316fac43c56da 100644 (file)
@@ -26,7 +26,7 @@
  */
 #ifndef lint
 static const char rcsid[] =
-    "@(#) $Header: /tcpdump/master/libpcap/pcap-linux.c,v 1.67 2001-09-23 22:43:57 guy Exp $ (LBL)";
+    "@(#) $Header: /tcpdump/master/libpcap/pcap-linux.c,v 1.68 2001-10-08 01:06:21 guy Exp $ (LBL)";
 #endif
 
 /*
@@ -237,6 +237,13 @@ pcap_open_live(char *device, int snaplen, int promisc, int to_ms, char *ebuf)
        if (!device || strcmp(device, "any") == 0) {
                device                  = NULL;
                handle->md.device       = strdup("any");
+               if (promisc) {
+                       promisc = 0;
+                       /* Just a warning. */
+                       snprintf(ebuf, PCAP_ERRBUF_SIZE,
+                           "Promiscuous mode not supported on the \"any\" device");
+               }
+       
        } else
                handle->md.device       = strdup(device);
 
diff --git a/pcap.3 b/pcap.3
index ea62a6ba001de21236751417041230f25455b865..64b97c762124f76405a5b8d373e766698c3c2b49 100644 (file)
--- a/pcap.3
+++ b/pcap.3
@@ -1,4 +1,4 @@
-.\" @(#) $Header: /tcpdump/master/libpcap/Attic/pcap.3,v 1.22 2001-07-04 07:34:50 guy Exp $
+.\" @(#) $Header: /tcpdump/master/libpcap/Attic/pcap.3,v 1.23 2001-10-08 01:06:21 guy Exp $
 .\"
 .\" Copyright (c) 1994, 1996, 1997
 .\"    The Regents of the University of California.  All rights reserved.
@@ -28,17 +28,23 @@ pcap \- Packet Capture library
 #include <pcap.h>
 .ft
 .LP
+.nf
+.ft B
+char errbuf[PCAP_ERRBUF_SIZE];
+.ft
+.LP
 .ft B
 pcap_t *pcap_open_live(char *device, int snaplen,
 .ti +8
-int promisc, int to_ms, char *ebuf)
+int promisc, int to_ms, char *errbuf)
 pcap_t *pcap_open_dead(int linktype, int snaplen)
-pcap_t *pcap_open_offline(char *fname, char *ebuf)
+pcap_t *pcap_open_offline(char *fname, char *errbuf)
 pcap_dumper_t *pcap_dump_open(pcap_t *p, char *fname)
 .ft
 .LP
 .ft B
-char errbuf[PCAP_ERRBUF_SIZE];
+int pcap_findalldevs(pcap_if_t **alldevsp, char *errbuf)
+void pcap_freealldevs(pcap_if_t *)
 char *pcap_lookupdev(char *errbuf)
 int pcap_lookupnet(char *device, bpf_u_int32 *netp,
 .ti +8
@@ -99,7 +105,9 @@ NOTE:
 .I errbuf
 in
 .B pcap_open_live(),
+.B pcap_open_dead(),
 .B pcap_open_offline(),
+.B pcap_findalldevs(),
 .B pcap_lookupdev(),
 and
 .B pcap_lookupnet()
@@ -134,20 +142,20 @@ is seen, but that it wait for some amount of time to allow more packets
 to arrive and to read multiple packets from the OS kernel in one
 operation.  Not all platforms support a read timeout; on platforms that
 don't, the read timeout is ignored.
-.I ebuf
+.I errbuf
 is used to return error or warning text.  It will be set to error text when
 .B pcap_open_live()
 fails and returns
 .BR NULL .
-.I ebuf
+.I errbuf
 may also be set to warning text when
 .B pcap_open_live()
 succeds; to detect this case the caller should store a zero-length string in
-.I ebuf
+.I errbuf
 before calling
 .B pcap_open_live()
 and display the warning to the user if
-.I ebuf
+.I errbuf
 is no longer a zero-length string.
 .PP
 .B pcap_open_dead()
@@ -166,7 +174,7 @@ and
 .BR tcpslice(1) .
 The name "-" in a synonym for
 .BR stdin .
-.I ebuf
+.I errbuf
 is used to return error text and is only set when
 .B pcap_open_offline()
 fails and returns
@@ -193,6 +201,97 @@ is returned,
 .B pcap_geterr()
 can be used to get the error text.
 .PP
+.B pcap_findalldevs()
+constructs a list of network devices that can be opened with
+.BR pcap_open_live() .
+(Note that there may be network devices that cannot be opened with
+.BR pcap_open_live()
+by the
+process calling
+.BR pcap_findalldevs() ,
+because, for example, that process might not have sufficient privileges
+to open them for capturing; if so, those devices will not appear on the
+list.)
+.I alldevsp
+is set to point to the first element of the list; each element of the
+list is of type
+.BR pcap_if_t ,
+and has the following members:
+.RS
+.TP
+.B next
+if not
+.BR NULL ,
+a pointer to the next element in the list;
+.B NULL
+for the last element of the list
+.TP
+.B name
+a pointer to a string giving a name for the device to pass to
+.B pcap_open_live()
+.TP
+.B description
+if not
+.BR NULL ,
+a pointer to a string giving a human-readable description of the device
+.TP
+.B addresses
+a pointer to the first element of a list of addresses for the interface
+.TP
+.B is_loopback
+non-zero if the interface is a loopback interface
+.RE
+.PP
+Each element of the list of addresses is of type
+.BR pcap_addr_t ,
+and has the following members:
+.RS
+.TP
+.B next
+if not
+.BR NULL ,
+a pointer to the next element in the list;
+.B NULL
+for the last element of the list
+.TP
+.B addr
+a pointer to a
+.B "struct sockaddr"
+containing an address
+.TP
+.B netmask
+if not
+.BR NULL ,
+a pointer to a
+.B "struct sockaddr"
+that contains the netmask corresponding to the address pointed to by
+.B addr
+.TP
+.B broadaddr
+if not
+.BR NULL ,
+a pointer to a
+.B "struct sockaddr"
+that contains the broadcast address corresponding to the address pointed
+to by
+.BR addr ;
+may be null if the interface doesn't support broadcasts
+.TP
+.B dstaddr
+if not
+.BR NULL ,
+a pointer to a
+.B "struct sockaddr"
+that contains the destination address corresponding to the address pointed
+to by
+.BR addr ;
+may be null if the interface isn't a point-to-point interface
+.RE
+.PP
+.B pcap_freealldevs()
+is used to free a list allocated by
+.BR pcap_findalldevs() .
+.PP
 .B pcap_lookupdev()
 returns a pointer to a network device suitable for use with
 .B pcap_open_live()
diff --git a/pcap.h b/pcap.h
index 7d62728f587a2a5363734f573adf46374442d27b..88ccdcfdf2e1c9475a8018593c6deb056a83fc37 100644 (file)
--- a/pcap.h
+++ b/pcap.h
@@ -1,3 +1,4 @@
+/* -*- Mode: c; tab-width: 8; indent-tabs-mode: 1; c-basic-offset: 8; -*- */
 /*
  * Copyright (c) 1993, 1994, 1995, 1996, 1997
  *     The Regents of the University of California.  All rights reserved.
@@ -30,7 +31,7 @@
  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  * SUCH DAMAGE.
  *
- * @(#) $Header: /tcpdump/master/libpcap/pcap.h,v 1.31 2000-10-28 00:01:31 guy Exp $ (LBL)
+ * @(#) $Header: /tcpdump/master/libpcap/pcap.h,v 1.32 2001-10-08 01:06:22 guy Exp $ (LBL)
  */
 
 #ifndef lib_pcap_h
@@ -63,6 +64,8 @@ typedef       u_int bpf_u_int32;
 
 typedef struct pcap pcap_t;
 typedef struct pcap_dumper pcap_dumper_t;
+typedef struct pcap_if pcap_if_t;
+typedef struct pcap_addr pcap_addr_t;
 
 /*
  * The first record in the file contains saved values for some
@@ -128,6 +131,28 @@ struct pcap_stat {
        u_int ps_ifdrop;        /* drops by interface XXX not yet supported */
 };
 
+/*
+ * Item in a list of interfaces.
+ */
+struct pcap_if {
+       struct pcap_if *next;
+       char *name;             /* name to hand to "pcap_open_live()" */
+       char *description;      /* textual description of interface, or NULL */
+       struct pcap_addr *addresses;
+       u_int is_loopback;      /* non-0 if interface is loopback */
+};
+
+/*
+ * Representation of an interface address.
+ */
+struct pcap_addr {
+       struct pcap_addr *next;
+       struct sockaddr *addr;          /* address */
+       struct sockaddr *netmask;       /* netmask for that address */
+       struct sockaddr *broadaddr;     /* broadcast address for that address */
+       struct sockaddr *dstaddr;       /* P2P destination address for that address */
+};
+
 typedef void (*pcap_handler)(u_char *, const struct pcap_pkthdr *,
                             const u_char *);
 
@@ -165,6 +190,9 @@ pcap_dumper_t *pcap_dump_open(pcap_t *, const char *);
 void   pcap_dump_close(pcap_dumper_t *);
 void   pcap_dump(u_char *, const struct pcap_pkthdr *, const u_char *);
 
+int    pcap_findalldevs(pcap_if_t **, char *);
+void   pcap_freealldevs(pcap_if_t *);
+
 /* XXX this guy lives in the bpf tree */
 u_int  bpf_filter(struct bpf_insn *, u_char *, u_int, u_int);
 int    bpf_validate(struct bpf_insn *f, int len);