]> The Tcpdump Group git mirrors - libpcap/commitdiff
Move code from fad-helpers.c to pcap.c.
authorGuy Harris <[email protected]>
Wed, 18 Jan 2017 04:29:06 +0000 (20:29 -0800)
committerGuy Harris <[email protected]>
Wed, 18 Jan 2017 04:29:06 +0000 (20:29 -0800)
Other helpers for pcap modules are in pcap.c; move those there as well.

Makefile.in
fad-helpers.c [deleted file]
pcap.c

index e71d973ca448c6aeab1a71e87a8a255074724536..7044f04385ed5d948326e31215ccb036ffba6896 100644 (file)
@@ -82,7 +82,7 @@ YACC = @YACC@
 PSRC = pcap-@[email protected] @USB_SRC@ @BT_SRC@ @BT_MONITOR_SRC@ @NETFILTER_SRC@ @DBUS_SRC@
 FSRC =  @V_FINDALLDEVS@
 SSRC =  @SSRC@
-CSRC = pcap.c inet.c fad-helpers.c gencode.c optimize.c nametoaddr.c \
+CSRC = pcap.c inet.c gencode.c optimize.c nametoaddr.c \
        etherent.c savefile.c sf-pcap.c sf-pcap-ng.c pcap-common.c \
        bpf_image.c bpf_dump.c
 GENSRC = scanner.c grammar.c bpf_filter.c version.c
@@ -273,7 +273,6 @@ EXTRA_DIST = \
        fad-getad.c \
        fad-gifc.c \
        fad-glifc.c \
-       fad-helpers.c \
        gen_version_c.sh \
        gen_version_header.sh \
        grammar.y \
diff --git a/fad-helpers.c b/fad-helpers.c
deleted file mode 100644 (file)
index ea9011f..0000000
+++ /dev/null
@@ -1,765 +0,0 @@
-/* -*- 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.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- *    notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- *    notice, this list of conditions and the following disclaimer in the
- *    documentation and/or other materials provided with the distribution.
- * 3. All advertising materials mentioning features or use of this software
- *    must display the following acknowledgement:
- *     This product includes software developed by the Computer Systems
- *     Engineering Group at Lawrence Berkeley Laboratory.
- * 4. Neither the name of the University nor of the Laboratory may be used
- *    to endorse or promote products derived from this software without
- *    specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
- * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
- * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
- * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
- * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- */
-
-#ifdef HAVE_CONFIG_H
-#include "config.h"
-#endif
-
-#ifdef _WIN32
-#include <pcap-stdinc.h>
-#else /* _WIN32 */
-
-#include <sys/param.h>
-#ifndef MSDOS
-#include <sys/file.h>
-#endif
-#include <sys/ioctl.h>
-#include <sys/socket.h>
-#ifdef HAVE_SYS_SOCKIO_H
-#include <sys/sockio.h>
-#endif
-
-struct mbuf;           /* Squelch compiler warnings on some platforms for */
-struct rtentry;                /* declarations in <net/if.h> */
-#include <net/if.h>
-#include <netinet/in.h>
-#endif /* _WIN32 */
-
-#include <ctype.h>
-#include <errno.h>
-#include <memory.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#if !defined(_WIN32) && !defined(__BORLANDC__)
-#include <unistd.h>
-#endif /* !_WIN32 && !__BORLANDC__ */
-#ifdef HAVE_LIMITS_H
-#include <limits.h>
-#else
-#define INT_MAX                2147483647
-#endif
-
-#include "pcap-int.h"
-
-#ifdef HAVE_OS_PROTO_H
-#include "os-proto.h"
-#endif
-
-#ifndef _WIN32
-/* Not all systems have IFF_LOOPBACK */
-#ifdef IFF_LOOPBACK
-#define ISLOOPBACK(name, flags) ((flags) & IFF_LOOPBACK)
-#else
-#define ISLOOPBACK(name, flags) ((name)[0] == 'l' && (name)[1] == 'o' && \
-    (isdigit((unsigned char)((name)[2])) || (name)[2] == '\0'))
-#endif
-
-#ifdef IFF_UP
-#define ISUP(flags) ((flags) & IFF_UP)
-#else
-#define ISUP(flags) 0
-#endif
-
-#ifdef IFF_RUNNING
-#define ISRUNNING(flags) ((flags) & IFF_RUNNING)
-#else
-#define ISRUNNING(flags) 0
-#endif
-
-/*
- * Map UN*X-style interface flags to libpcap flags.
- */
-bpf_u_int32
-if_flags_to_pcap_flags(const char *name _U_, u_int if_flags)
-{
-       bpf_u_int32 pcap_flags;
-
-       pcap_flags = 0;
-       if (ISLOOPBACK(name, if_flags))
-               pcap_flags |= PCAP_IF_LOOPBACK;
-       if (ISUP(if_flags))
-               pcap_flags |= PCAP_IF_UP;
-       if (ISRUNNING(if_flags))
-               pcap_flags |= PCAP_IF_RUNNING;
-       return (pcap_flags);
-}
-#endif
-
-static struct sockaddr *
-dup_sockaddr(struct sockaddr *sa, size_t sa_length)
-{
-       struct sockaddr *newsa;
-
-       if ((newsa = malloc(sa_length)) == NULL)
-               return (NULL);
-       return (memcpy(newsa, sa, sa_length));
-}
-
-/*
- * Construct a "figure of merit" for an interface, for use when sorting
- * the list of interfaces, in which interfaces that are up are superior
- * to interfaces that aren't up, interfaces that are up and running are
- * superior to interfaces that are up but not running, and non-loopback
- * interfaces that are up and running are superior to loopback interfaces,
- * and interfaces with the same flags have a figure of merit that's higher
- * the lower the instance number.
- *
- * The goal is to try to put the interfaces most likely to be useful for
- * capture at the beginning of the list.
- *
- * The figure of merit, which is lower the "better" the interface is,
- * has the uppermost bit set if the interface isn't running, the bit
- * below that set if the interface isn't up, the bit below that set
- * if the interface is a loopback interface, and the interface index
- * in the 29 bits below that.  (Yes, we assume u_int is 32 bits.)
- */
-static u_int
-get_figure_of_merit(pcap_if_t *dev)
-{
-       const char *cp;
-       u_int n;
-
-       if (strcmp(dev->name, "any") == 0) {
-               /*
-                * Give the "any" device an artificially high instance
-                * number, so it shows up after all other non-loopback
-                * interfaces.
-                */
-               n = 0x1FFFFFFF; /* 29 all-1 bits */
-       } else {
-               /*
-                * A number at the end of the device name string is
-                * assumed to be an instance number.  Add 1 to the
-                * instance number, and use 0 for "no instance
-                * number", so we don't put "no instance number"
-                * devices and "instance 0" devices together.
-                */
-               cp = dev->name + strlen(dev->name) - 1;
-               while (cp-1 >= dev->name && *(cp-1) >= '0' && *(cp-1) <= '9')
-                       cp--;
-               if (*cp >= '0' && *cp <= '9')
-                       n = atoi(cp) + 1;
-               else
-                       n = 0;
-       }
-       if (!(dev->flags & PCAP_IF_RUNNING))
-               n |= 0x80000000;
-       if (!(dev->flags & PCAP_IF_UP))
-               n |= 0x40000000;
-       if (dev->flags & PCAP_IF_LOOPBACK)
-               n |= 0x20000000;
-       return (n);
-}
-
-#ifndef _WIN32
-/*
- * Try to get a description for a given device.
- * Returns a mallocated description if it could and NULL if it couldn't.
- *
- * XXX - on FreeBSDs that support it, should it get the sysctl named
- * "dev.{adapter family name}.{adapter unit}.%desc" to get a description
- * of the adapter?  Note that "dev.an.0.%desc" is "Aironet PC4500/PC4800"
- * with my Cisco 350 card, so the name isn't entirely descriptive.  The
- * "dev.an.0.%pnpinfo" has a better description, although one might argue
- * that the problem is really a driver bug - if it can find out that it's
- * a Cisco 340 or 350, rather than an old Aironet card, it should use
- * that in the description.
- *
- * Do NetBSD, DragonflyBSD, or OpenBSD support this as well?  FreeBSD
- * and OpenBSD let you get a description, but it's not generated by the OS,
- * it's set with another ioctl that ifconfig supports; we use that to get
- * a description in FreeBSD and OpenBSD, but if there is no such
- * description available, it still might be nice to get some description
- * string based on the device type or something such as that.
- *
- * In OS X, the System Configuration framework can apparently return
- * names in 10.4 and later.
- *
- * It also appears that freedesktop.org's HAL offers an "info.product"
- * string, but the HAL specification says it "should not be used in any
- * UI" and "subsystem/capability specific properties" should be used
- * instead and, in any case, I think HAL is being deprecated in
- * favor of other stuff such as DeviceKit.  DeviceKit doesn't appear
- * to have any obvious product information for devices, but maybe
- * I haven't looked hard enough.
- *
- * Using the System Configuration framework, or HAL, or DeviceKit, or
- * whatever, would require that libpcap applications be linked with
- * the frameworks/libraries in question.  That shouldn't be a problem
- * for programs linking with the shared version of libpcap (unless
- * you're running on AIX - which I think is the only UN*X that doesn't
- * support linking a shared library with other libraries on which it
- * depends, and having an executable linked only with the first shared
- * library automatically pick up the other libraries when started -
- * and using HAL or whatever).  Programs linked with the static
- * version of libpcap would have to use pcap-config with the --static
- * flag in order to get the right linker flags in order to pick up
- * the additional libraries/frameworks; those programs need that anyway
- * for libpcap 1.1 and beyond on Linux, as, by default, it requires
- * -lnl.
- *
- * Do any other UN*Xes, or desktop environments support getting a
- * description?
- */
-static char *
-get_if_description(const char *name)
-{
-#ifdef SIOCGIFDESCR
-       char *description = NULL;
-       int s;
-       struct ifreq ifrdesc;
-#ifndef IFDESCRSIZE
-       size_t descrlen = 64;
-#else
-       size_t descrlen = IFDESCRSIZE;
-#endif /* IFDESCRSIZE */
-
-       /*
-        * Get the description for the interface.
-        */
-       memset(&ifrdesc, 0, sizeof ifrdesc);
-       strlcpy(ifrdesc.ifr_name, name, sizeof ifrdesc.ifr_name);
-       s = socket(AF_INET, SOCK_DGRAM, 0);
-       if (s >= 0) {
-#ifdef __FreeBSD__
-               /*
-                * On FreeBSD, if the buffer isn't big enough for the
-                * description, the ioctl succeeds, but the description
-                * isn't copied, ifr_buffer.length is set to the description
-                * length, and ifr_buffer.buffer is set to NULL.
-                */
-               for (;;) {
-                       free(description);
-                       if ((description = malloc(descrlen)) != NULL) {
-                               ifrdesc.ifr_buffer.buffer = description;
-                               ifrdesc.ifr_buffer.length = descrlen;
-                               if (ioctl(s, SIOCGIFDESCR, &ifrdesc) == 0) {
-                                       if (ifrdesc.ifr_buffer.buffer ==
-                                           description)
-                                               break;
-                                       else
-                                               descrlen = ifrdesc.ifr_buffer.length;
-                               } else {
-                                       /*
-                                        * Failed to get interface description.
-                                        */
-                                       free(description);
-                                       description = NULL;
-                                       break;
-                               }
-                       } else
-                               break;
-               }
-#else /* __FreeBSD__ */
-               /*
-                * The only other OS that currently supports
-                * SIOCGIFDESCR is OpenBSD, and it has no way
-                * to get the description length - it's clamped
-                * to a maximum of IFDESCRSIZE.
-                */
-               if ((description = malloc(descrlen)) != NULL) {
-                       ifrdesc.ifr_data = (caddr_t)description;
-                       if (ioctl(s, SIOCGIFDESCR, &ifrdesc) != 0) {
-                               /*
-                                * Failed to get interface description.
-                                */
-                               free(description);
-                               description = NULL;
-                       }
-               }
-#endif /* __FreeBSD__ */
-               close(s);
-               if (description != NULL && strlen(description) == 0) {
-                       /*
-                        * Description is empty, so discard it.
-                        */
-                       free(description);
-                       description = NULL;
-               }
-       }
-
-#ifdef __FreeBSD__
-       /*
-        * For FreeBSD, if we didn't get a description, and this is
-        * a device with a name of the form usbusN, label it as a USB
-        * bus.
-        */
-       if (description == NULL) {
-               if (strncmp(name, "usbus", 5) == 0) {
-                       /*
-                        * OK, it begins with "usbus".
-                        */
-                       long busnum;
-                       char *p;
-
-                       errno = 0;
-                       busnum = strtol(name + 5, &p, 10);
-                       if (errno == 0 && p != name + 5 && *p == '\0' &&
-                           busnum >= 0 && busnum <= INT_MAX) {
-                               /*
-                                * OK, it's a valid number that's not
-                                * bigger than INT_MAX.  Construct
-                                * a description from it.
-                                */
-                               static const char descr_prefix[] = "USB bus number ";
-                               size_t descr_size;
-
-                               /*
-                                * Allow enough room for a 32-bit bus number.
-                                * sizeof (descr_prefix) includes the
-                                * terminating NUL.
-                                */
-                               descr_size = sizeof (descr_prefix) + 10;
-                               description = malloc(descr_size);
-                               if (description != NULL) {
-                                       pcap_snprintf(description, descr_size,
-                                           "%s%ld", descr_prefix, busnum);
-                               }
-                       }
-               }
-       }
-#endif
-       return (description);
-#else /* SIOCGIFDESCR */
-       return (NULL);
-#endif /* SIOCGIFDESCR */
-}
-
-/*
- * Look for a given device in the specified list of devices.
- *
- * If we find it, then, if the specified address isn't null, add it to
- * the list of addresses for the device and return 0.
- *
- * If we don't find it, attempt to add an entry for it, with the specified
- * flags and description, and, if that succeeds, add the specified
- * address to its list of addresses if that address is non-null, and
- * return 0, otherwise return -1 and set errbuf to an error message.
- *
- * (We can get called with a null address because we might get a list
- * of interface name/address combinations from the underlying OS, with
- * the address being absent in some cases, rather than a list of
- * interfaces with each interface having a list of addresses, so this
- * call may be the only call made to add to the list, and we want to
- * add interfaces even if they have no addresses.)
- */
-int
-add_addr_to_iflist(pcap_if_t **alldevs, const char *name, bpf_u_int32 flags,
-    struct sockaddr *addr, size_t addr_size,
-    struct sockaddr *netmask, size_t netmask_size,
-    struct sockaddr *broadaddr, size_t broadaddr_size,
-    struct sockaddr *dstaddr, size_t dstaddr_size,
-    char *errbuf)
-{
-       pcap_if_t *curdev;
-
-       /*
-        * Attempt to find an entry for this device; if we don't find one,
-        * attempt to add one.
-        */
-       curdev = find_or_add_dev(alldevs, name, flags, get_if_description(name),
-           errbuf);
-       if (curdev == NULL) {
-               /*
-                * Error - give up.
-                */
-               return (-1);
-       }
-
-       if (addr == NULL) {
-               /*
-                * There's no address to add; this entry just meant
-                * "here's a new interface".
-                */
-               return (0);
-       }
-
-       /*
-        * "curdev" is an entry for this interface, and we have an
-        * address for it; add an entry for that address to the
-        * interface's list of addresses.
-        *
-        * Allocate the new entry and fill it in.
-        */
-       return (add_addr_to_dev(curdev, addr, addr_size, netmask,
-           netmask_size, broadaddr, broadaddr_size, dstaddr,
-           dstaddr_size, errbuf));
-}
-#endif /* _WIN32 */
-
-/*
- * Add an entry to the list of addresses for an interface.
- * "curdev" is the entry for that interface.
- * If this is the first IP address added to the interface, move it
- * in the list as appropriate.
- */
-int
-add_addr_to_dev(pcap_if_t *curdev,
-    struct sockaddr *addr, size_t addr_size,
-    struct sockaddr *netmask, size_t netmask_size,
-    struct sockaddr *broadaddr, size_t broadaddr_size,
-    struct sockaddr *dstaddr, size_t dstaddr_size,
-    char *errbuf)
-{
-       pcap_addr_t *curaddr, *prevaddr, *nextaddr;
-
-       curaddr = malloc(sizeof(pcap_addr_t));
-       if (curaddr == NULL) {
-               (void)pcap_snprintf(errbuf, PCAP_ERRBUF_SIZE,
-                   "malloc: %s", pcap_strerror(errno));
-               return (-1);
-       }
-
-       curaddr->next = NULL;
-       if (addr != NULL) {
-               curaddr->addr = dup_sockaddr(addr, addr_size);
-               if (curaddr->addr == NULL) {
-                       (void)pcap_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, netmask_size);
-               if (curaddr->netmask == NULL) {
-                       (void)pcap_snprintf(errbuf, PCAP_ERRBUF_SIZE,
-                           "malloc: %s", pcap_strerror(errno));
-                       if (curaddr->addr != NULL)
-                               free(curaddr->addr);
-                       free(curaddr);
-                       return (-1);
-               }
-       } else
-               curaddr->netmask = NULL;
-
-       if (broadaddr != NULL) {
-               curaddr->broadaddr = dup_sockaddr(broadaddr, broadaddr_size);
-               if (curaddr->broadaddr == NULL) {
-                       (void)pcap_snprintf(errbuf, PCAP_ERRBUF_SIZE,
-                           "malloc: %s", pcap_strerror(errno));
-                       if (curaddr->netmask != NULL)
-                               free(curaddr->netmask);
-                       if (curaddr->addr != NULL)
-                               free(curaddr->addr);
-                       free(curaddr);
-                       return (-1);
-               }
-       } else
-               curaddr->broadaddr = NULL;
-
-       if (dstaddr != NULL) {
-               curaddr->dstaddr = dup_sockaddr(dstaddr, dstaddr_size);
-               if (curaddr->dstaddr == NULL) {
-                       (void)pcap_snprintf(errbuf, PCAP_ERRBUF_SIZE,
-                           "malloc: %s", pcap_strerror(errno));
-                       if (curaddr->broadaddr != NULL)
-                               free(curaddr->broadaddr);
-                       if (curaddr->netmask != NULL)
-                               free(curaddr->netmask);
-                       if (curaddr->addr != NULL)
-                               free(curaddr->addr);
-                       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);
-}
-
-/*
- * Look for a given device in the specified list of devices.
- *
- * If we find it, return 0 and set *curdev_ret to point to it.
- *
- * If we don't find it, attempt to add an entry for it, with the specified
- * flags and description, and, if that succeeds, return 0, otherwise
- * return -1 and set errbuf to an error message.
- */
-pcap_if_t *
-find_or_add_dev(pcap_if_t **alldevs, const char *name, bpf_u_int32 flags,
-    const char *description, char *errbuf)
-{
-       pcap_if_t *curdev;
-
-       /*
-        * Is there already an entry in the list for this interface?
-        */
-       curdev = find_dev(alldevs, name);
-       if (curdev != NULL) {
-               /*
-                * Yes, return it.
-                */
-               return (curdev);
-       }
-
-       /*
-        * No, we didn't find it.  Try to add it to the list of devices.
-        */
-       return (add_dev(alldevs, name, flags, description, errbuf));
-}
-
-/*
- * Look for a given device in the specified list of devices, and return
- * the entry for it if we find it or NULL if we don't.
- */
-pcap_if_t *
-find_dev(pcap_if_t **alldevs, const char *name)
-{
-       pcap_if_t *curdev;
-
-       /*
-        * Is there an entry in the list for this device?
-        */
-       for (curdev = *alldevs; curdev != NULL; curdev = curdev->next) {
-               if (strcmp(name, curdev->name) == 0) {
-                       /*
-                        * We found it, so, yes, there is.  No need to
-                        * add it.  Provide the entry we found to our
-                        * caller.
-                        */
-                       return (curdev);
-               }
-       }
-
-       /*
-        * No.
-        */
-       return (NULL);
-}
-
-/*
- * Attempt to add an entry for a device, with the specified flags
- * and description, and, if that succeeds, return 0 and return a pointer
- * to the new entry, otherwise return NULL and set errbuf to an error
- * message.
- *
- * If we weren't given a description, try to get one.
- */
-pcap_if_t *
-add_dev(pcap_if_t **alldevs, const char *name, bpf_u_int32 flags,
-    const char *description, char *errbuf)
-{
-       pcap_if_t *curdev, *prevdev, *nextdev;
-       u_int this_figure_of_merit, nextdev_figure_of_merit;
-
-       curdev = malloc(sizeof(pcap_if_t));
-       if (curdev == NULL) {
-               (void)pcap_snprintf(errbuf, PCAP_ERRBUF_SIZE,
-                   "malloc: %s", pcap_strerror(errno));
-               return (NULL);
-       }
-
-       /*
-        * Fill in the entry.
-        */
-       curdev->next = NULL;
-       curdev->name = strdup(name);
-       if (curdev->name == NULL) {
-               (void)pcap_snprintf(errbuf, PCAP_ERRBUF_SIZE,
-                   "malloc: %s", pcap_strerror(errno));
-               free(curdev);
-               return (NULL);
-       }
-       if (description == NULL) {
-               /*
-                * We weren't handed a description for the interface.
-                */
-               curdev->description = NULL;
-       } else {
-               /*
-                * We were handed a description; make a copy.
-                */
-               curdev->description = strdup(description);
-               if (curdev->description == NULL) {
-                       (void)pcap_snprintf(errbuf, PCAP_ERRBUF_SIZE,
-                           "malloc: %s", pcap_strerror(errno));
-                       free(curdev->name);
-                       free(curdev);
-                       return (NULL);
-               }
-       }
-       curdev->addresses = NULL;       /* list starts out as empty */
-       curdev->flags = flags;
-
-       /*
-        * Add it to the list, in the appropriate location.
-        * First, get the "figure of merit" for this interface.
-        */
-       this_figure_of_merit = get_figure_of_merit(curdev);
-
-       /*
-        * Now look for the last interface with an figure of merit
-        * less than or equal to the new interface's figure of merit.
-        *
-        * 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's figure of merit less
-                * than the next interface's figure of merit,
-                * meaning that the new interface is better
-                * than the next interface?
-                */
-               nextdev_figure_of_merit = get_figure_of_merit(nextdev);
-               if (this_figure_of_merit < nextdev_figure_of_merit) {
-                       /*
-                        * 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;
-       return (curdev);
-}
-
-/*
- * 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);
-       }
-}
diff --git a/pcap.c b/pcap.c
index d93dfe360acf6ea1553c94e5e80148160fd62351..ff47860c5c5711ea7bfa0a88b6a0f53a172e2f5e 100644 (file)
--- a/pcap.c
+++ b/pcap.c
 #ifdef HAVE_SYS_BITYPES_H
 #include <sys/bitypes.h>
 #endif
+#include <sys/param.h>
 #include <sys/types.h>
+#ifndef MSDOS
+#include <sys/file.h>
+#endif
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+#ifdef HAVE_SYS_SOCKIO_H
+#include <sys/sockio.h>
+#endif
+
+struct mbuf;           /* Squelch compiler warnings on some platforms for */
+struct rtentry;                /* declarations in <net/if.h> */
+#include <net/if.h>
+#include <netinet/in.h>
 #endif /* _WIN32 */
 
+#include <ctype.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
 #endif
 #include <fcntl.h>
 #include <errno.h>
+#ifdef HAVE_LIMITS_H
+#include <limits.h>
+#else
+#define INT_MAX                2147483647
+#endif
 
 #ifdef HAVE_OS_PROTO_H
 #include "os-proto.h"
@@ -368,6 +388,693 @@ pcap_findalldevs(pcap_if_t **alldevsp, char *errbuf)
        return (0);
 }
 
+#ifndef _WIN32
+/* Not all systems have IFF_LOOPBACK */
+#ifdef IFF_LOOPBACK
+#define ISLOOPBACK(name, flags) ((flags) & IFF_LOOPBACK)
+#else
+#define ISLOOPBACK(name, flags) ((name)[0] == 'l' && (name)[1] == 'o' && \
+    (isdigit((unsigned char)((name)[2])) || (name)[2] == '\0'))
+#endif
+
+#ifdef IFF_UP
+#define ISUP(flags) ((flags) & IFF_UP)
+#else
+#define ISUP(flags) 0
+#endif
+
+#ifdef IFF_RUNNING
+#define ISRUNNING(flags) ((flags) & IFF_RUNNING)
+#else
+#define ISRUNNING(flags) 0
+#endif
+
+/*
+ * Map UN*X-style interface flags to libpcap flags.
+ */
+bpf_u_int32
+if_flags_to_pcap_flags(const char *name _U_, u_int if_flags)
+{
+       bpf_u_int32 pcap_flags;
+
+       pcap_flags = 0;
+       if (if_flags & IFF_LOOPBACK)
+               pcap_flags |= PCAP_IF_LOOPBACK;
+       if (if_flags & IFF_UP)
+               pcap_flags |= PCAP_IF_UP;
+       if (if_flags & IFF_RUNNING)
+               pcap_flags |= PCAP_IF_RUNNING;
+       return (pcap_flags);
+}
+#endif
+
+static struct sockaddr *
+dup_sockaddr(struct sockaddr *sa, size_t sa_length)
+{
+       struct sockaddr *newsa;
+
+       if ((newsa = malloc(sa_length)) == NULL)
+               return (NULL);
+       return (memcpy(newsa, sa, sa_length));
+}
+
+/*
+ * Construct a "figure of merit" for an interface, for use when sorting
+ * the list of interfaces, in which interfaces that are up are superior
+ * to interfaces that aren't up, interfaces that are up and running are
+ * superior to interfaces that are up but not running, and non-loopback
+ * interfaces that are up and running are superior to loopback interfaces,
+ * and interfaces with the same flags have a figure of merit that's higher
+ * the lower the instance number.
+ *
+ * The goal is to try to put the interfaces most likely to be useful for
+ * capture at the beginning of the list.
+ *
+ * The figure of merit, which is lower the "better" the interface is,
+ * has the uppermost bit set if the interface isn't running, the bit
+ * below that set if the interface isn't up, the bit below that set
+ * if the interface is a loopback interface, and the interface index
+ * in the 29 bits below that.  (Yes, we assume u_int is 32 bits.)
+ */
+static u_int
+get_figure_of_merit(pcap_if_t *dev)
+{
+       const char *cp;
+       u_int n;
+
+       if (strcmp(dev->name, "any") == 0) {
+               /*
+                * Give the "any" device an artificially high instance
+                * number, so it shows up after all other non-loopback
+                * interfaces.
+                */
+               n = 0x1FFFFFFF; /* 29 all-1 bits */
+       } else {
+               /*
+                * A number at the end of the device name string is
+                * assumed to be an instance number.  Add 1 to the
+                * instance number, and use 0 for "no instance
+                * number", so we don't put "no instance number"
+                * devices and "instance 0" devices together.
+                */
+               cp = dev->name + strlen(dev->name) - 1;
+               while (cp-1 >= dev->name && *(cp-1) >= '0' && *(cp-1) <= '9')
+                       cp--;
+               if (*cp >= '0' && *cp <= '9')
+                       n = atoi(cp) + 1;
+               else
+                       n = 0;
+       }
+       if (!(dev->flags & PCAP_IF_RUNNING))
+               n |= 0x80000000;
+       if (!(dev->flags & PCAP_IF_UP))
+               n |= 0x40000000;
+       if (dev->flags & PCAP_IF_LOOPBACK)
+               n |= 0x20000000;
+       return (n);
+}
+
+#ifndef _WIN32
+/*
+ * Try to get a description for a given device.
+ * Returns a mallocated description if it could and NULL if it couldn't.
+ *
+ * XXX - on FreeBSDs that support it, should it get the sysctl named
+ * "dev.{adapter family name}.{adapter unit}.%desc" to get a description
+ * of the adapter?  Note that "dev.an.0.%desc" is "Aironet PC4500/PC4800"
+ * with my Cisco 350 card, so the name isn't entirely descriptive.  The
+ * "dev.an.0.%pnpinfo" has a better description, although one might argue
+ * that the problem is really a driver bug - if it can find out that it's
+ * a Cisco 340 or 350, rather than an old Aironet card, it should use
+ * that in the description.
+ *
+ * Do NetBSD, DragonflyBSD, or OpenBSD support this as well?  FreeBSD
+ * and OpenBSD let you get a description, but it's not generated by the OS,
+ * it's set with another ioctl that ifconfig supports; we use that to get
+ * a description in FreeBSD and OpenBSD, but if there is no such
+ * description available, it still might be nice to get some description
+ * string based on the device type or something such as that.
+ *
+ * In OS X, the System Configuration framework can apparently return
+ * names in 10.4 and later.
+ *
+ * It also appears that freedesktop.org's HAL offers an "info.product"
+ * string, but the HAL specification says it "should not be used in any
+ * UI" and "subsystem/capability specific properties" should be used
+ * instead and, in any case, I think HAL is being deprecated in
+ * favor of other stuff such as DeviceKit.  DeviceKit doesn't appear
+ * to have any obvious product information for devices, but maybe
+ * I haven't looked hard enough.
+ *
+ * Using the System Configuration framework, or HAL, or DeviceKit, or
+ * whatever, would require that libpcap applications be linked with
+ * the frameworks/libraries in question.  That shouldn't be a problem
+ * for programs linking with the shared version of libpcap (unless
+ * you're running on AIX - which I think is the only UN*X that doesn't
+ * support linking a shared library with other libraries on which it
+ * depends, and having an executable linked only with the first shared
+ * library automatically pick up the other libraries when started -
+ * and using HAL or whatever).  Programs linked with the static
+ * version of libpcap would have to use pcap-config with the --static
+ * flag in order to get the right linker flags in order to pick up
+ * the additional libraries/frameworks; those programs need that anyway
+ * for libpcap 1.1 and beyond on Linux, as, by default, it requires
+ * -lnl.
+ *
+ * Do any other UN*Xes, or desktop environments support getting a
+ * description?
+ */
+static char *
+get_if_description(const char *name)
+{
+#ifdef SIOCGIFDESCR
+       char *description = NULL;
+       int s;
+       struct ifreq ifrdesc;
+#ifndef IFDESCRSIZE
+       size_t descrlen = 64;
+#else
+       size_t descrlen = IFDESCRSIZE;
+#endif /* IFDESCRSIZE */
+
+       /*
+        * Get the description for the interface.
+        */
+       memset(&ifrdesc, 0, sizeof ifrdesc);
+       strlcpy(ifrdesc.ifr_name, name, sizeof ifrdesc.ifr_name);
+       s = socket(AF_INET, SOCK_DGRAM, 0);
+       if (s >= 0) {
+#ifdef __FreeBSD__
+               /*
+                * On FreeBSD, if the buffer isn't big enough for the
+                * description, the ioctl succeeds, but the description
+                * isn't copied, ifr_buffer.length is set to the description
+                * length, and ifr_buffer.buffer is set to NULL.
+                */
+               for (;;) {
+                       free(description);
+                       if ((description = malloc(descrlen)) != NULL) {
+                               ifrdesc.ifr_buffer.buffer = description;
+                               ifrdesc.ifr_buffer.length = descrlen;
+                               if (ioctl(s, SIOCGIFDESCR, &ifrdesc) == 0) {
+                                       if (ifrdesc.ifr_buffer.buffer ==
+                                           description)
+                                               break;
+                                       else
+                                               descrlen = ifrdesc.ifr_buffer.length;
+                               } else {
+                                       /*
+                                        * Failed to get interface description.
+                                        */
+                                       free(description);
+                                       description = NULL;
+                                       break;
+                               }
+                       } else
+                               break;
+               }
+#else /* __FreeBSD__ */
+               /*
+                * The only other OS that currently supports
+                * SIOCGIFDESCR is OpenBSD, and it has no way
+                * to get the description length - it's clamped
+                * to a maximum of IFDESCRSIZE.
+                */
+               if ((description = malloc(descrlen)) != NULL) {
+                       ifrdesc.ifr_data = (caddr_t)description;
+                       if (ioctl(s, SIOCGIFDESCR, &ifrdesc) != 0) {
+                               /*
+                                * Failed to get interface description.
+                                */
+                               free(description);
+                               description = NULL;
+                       }
+               }
+#endif /* __FreeBSD__ */
+               close(s);
+               if (description != NULL && strlen(description) == 0) {
+                       /*
+                        * Description is empty, so discard it.
+                        */
+                       free(description);
+                       description = NULL;
+               }
+       }
+
+#ifdef __FreeBSD__
+       /*
+        * For FreeBSD, if we didn't get a description, and this is
+        * a device with a name of the form usbusN, label it as a USB
+        * bus.
+        */
+       if (description == NULL) {
+               if (strncmp(name, "usbus", 5) == 0) {
+                       /*
+                        * OK, it begins with "usbus".
+                        */
+                       long busnum;
+                       char *p;
+
+                       errno = 0;
+                       busnum = strtol(name + 5, &p, 10);
+                       if (errno == 0 && p != name + 5 && *p == '\0' &&
+                           busnum >= 0 && busnum <= INT_MAX) {
+                               /*
+                                * OK, it's a valid number that's not
+                                * bigger than INT_MAX.  Construct
+                                * a description from it.
+                                */
+                               static const char descr_prefix[] = "USB bus number ";
+                               size_t descr_size;
+
+                               /*
+                                * Allow enough room for a 32-bit bus number.
+                                * sizeof (descr_prefix) includes the
+                                * terminating NUL.
+                                */
+                               descr_size = sizeof (descr_prefix) + 10;
+                               description = malloc(descr_size);
+                               if (description != NULL) {
+                                       pcap_snprintf(description, descr_size,
+                                           "%s%ld", descr_prefix, busnum);
+                               }
+                       }
+               }
+       }
+#endif
+       return (description);
+#else /* SIOCGIFDESCR */
+       return (NULL);
+#endif /* SIOCGIFDESCR */
+}
+
+/*
+ * Look for a given device in the specified list of devices.
+ *
+ * If we find it, then, if the specified address isn't null, add it to
+ * the list of addresses for the device and return 0.
+ *
+ * If we don't find it, attempt to add an entry for it, with the specified
+ * flags and description, and, if that succeeds, add the specified
+ * address to its list of addresses if that address is non-null, and
+ * return 0, otherwise return -1 and set errbuf to an error message.
+ *
+ * (We can get called with a null address because we might get a list
+ * of interface name/address combinations from the underlying OS, with
+ * the address being absent in some cases, rather than a list of
+ * interfaces with each interface having a list of addresses, so this
+ * call may be the only call made to add to the list, and we want to
+ * add interfaces even if they have no addresses.)
+ */
+int
+add_addr_to_iflist(pcap_if_t **alldevs, const char *name, bpf_u_int32 flags,
+    struct sockaddr *addr, size_t addr_size,
+    struct sockaddr *netmask, size_t netmask_size,
+    struct sockaddr *broadaddr, size_t broadaddr_size,
+    struct sockaddr *dstaddr, size_t dstaddr_size,
+    char *errbuf)
+{
+       pcap_if_t *curdev;
+
+       /*
+        * Attempt to find an entry for this device; if we don't find one,
+        * attempt to add one.
+        */
+       curdev = find_or_add_dev(alldevs, name, flags, get_if_description(name),
+           errbuf);
+       if (curdev == NULL) {
+               /*
+                * Error - give up.
+                */
+               return (-1);
+       }
+
+       if (addr == NULL) {
+               /*
+                * There's no address to add; this entry just meant
+                * "here's a new interface".
+                */
+               return (0);
+       }
+
+       /*
+        * "curdev" is an entry for this interface, and we have an
+        * address for it; add an entry for that address to the
+        * interface's list of addresses.
+        *
+        * Allocate the new entry and fill it in.
+        */
+       return (add_addr_to_dev(curdev, addr, addr_size, netmask,
+           netmask_size, broadaddr, broadaddr_size, dstaddr,
+           dstaddr_size, errbuf));
+}
+#endif /* _WIN32 */
+
+/*
+ * Add an entry to the list of addresses for an interface.
+ * "curdev" is the entry for that interface.
+ * If this is the first IP address added to the interface, move it
+ * in the list as appropriate.
+ */
+int
+add_addr_to_dev(pcap_if_t *curdev,
+    struct sockaddr *addr, size_t addr_size,
+    struct sockaddr *netmask, size_t netmask_size,
+    struct sockaddr *broadaddr, size_t broadaddr_size,
+    struct sockaddr *dstaddr, size_t dstaddr_size,
+    char *errbuf)
+{
+       pcap_addr_t *curaddr, *prevaddr, *nextaddr;
+
+       curaddr = malloc(sizeof(pcap_addr_t));
+       if (curaddr == NULL) {
+               (void)pcap_snprintf(errbuf, PCAP_ERRBUF_SIZE,
+                   "malloc: %s", pcap_strerror(errno));
+               return (-1);
+       }
+
+       curaddr->next = NULL;
+       if (addr != NULL) {
+               curaddr->addr = dup_sockaddr(addr, addr_size);
+               if (curaddr->addr == NULL) {
+                       (void)pcap_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, netmask_size);
+               if (curaddr->netmask == NULL) {
+                       (void)pcap_snprintf(errbuf, PCAP_ERRBUF_SIZE,
+                           "malloc: %s", pcap_strerror(errno));
+                       if (curaddr->addr != NULL)
+                               free(curaddr->addr);
+                       free(curaddr);
+                       return (-1);
+               }
+       } else
+               curaddr->netmask = NULL;
+
+       if (broadaddr != NULL) {
+               curaddr->broadaddr = dup_sockaddr(broadaddr, broadaddr_size);
+               if (curaddr->broadaddr == NULL) {
+                       (void)pcap_snprintf(errbuf, PCAP_ERRBUF_SIZE,
+                           "malloc: %s", pcap_strerror(errno));
+                       if (curaddr->netmask != NULL)
+                               free(curaddr->netmask);
+                       if (curaddr->addr != NULL)
+                               free(curaddr->addr);
+                       free(curaddr);
+                       return (-1);
+               }
+       } else
+               curaddr->broadaddr = NULL;
+
+       if (dstaddr != NULL) {
+               curaddr->dstaddr = dup_sockaddr(dstaddr, dstaddr_size);
+               if (curaddr->dstaddr == NULL) {
+                       (void)pcap_snprintf(errbuf, PCAP_ERRBUF_SIZE,
+                           "malloc: %s", pcap_strerror(errno));
+                       if (curaddr->broadaddr != NULL)
+                               free(curaddr->broadaddr);
+                       if (curaddr->netmask != NULL)
+                               free(curaddr->netmask);
+                       if (curaddr->addr != NULL)
+                               free(curaddr->addr);
+                       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);
+}
+
+/*
+ * Look for a given device in the specified list of devices.
+ *
+ * If we find it, return 0 and set *curdev_ret to point to it.
+ *
+ * If we don't find it, attempt to add an entry for it, with the specified
+ * flags and description, and, if that succeeds, return 0, otherwise
+ * return -1 and set errbuf to an error message.
+ */
+pcap_if_t *
+find_or_add_dev(pcap_if_t **alldevs, const char *name, bpf_u_int32 flags,
+    const char *description, char *errbuf)
+{
+       pcap_if_t *curdev;
+
+       /*
+        * Is there already an entry in the list for this interface?
+        */
+       curdev = find_dev(alldevs, name);
+       if (curdev != NULL) {
+               /*
+                * Yes, return it.
+                */
+               return (curdev);
+       }
+
+       /*
+        * No, we didn't find it.  Try to add it to the list of devices.
+        */
+       return (add_dev(alldevs, name, flags, description, errbuf));
+}
+
+/*
+ * Look for a given device in the specified list of devices, and return
+ * the entry for it if we find it or NULL if we don't.
+ */
+pcap_if_t *
+find_dev(pcap_if_t **alldevs, const char *name)
+{
+       pcap_if_t *curdev;
+
+       /*
+        * Is there an entry in the list for this device?
+        */
+       for (curdev = *alldevs; curdev != NULL; curdev = curdev->next) {
+               if (strcmp(name, curdev->name) == 0) {
+                       /*
+                        * We found it, so, yes, there is.  No need to
+                        * add it.  Provide the entry we found to our
+                        * caller.
+                        */
+                       return (curdev);
+               }
+       }
+
+       /*
+        * No.
+        */
+       return (NULL);
+}
+
+/*
+ * Attempt to add an entry for a device, with the specified flags
+ * and description, and, if that succeeds, return 0 and return a pointer
+ * to the new entry, otherwise return NULL and set errbuf to an error
+ * message.
+ *
+ * If we weren't given a description, try to get one.
+ */
+pcap_if_t *
+add_dev(pcap_if_t **alldevs, const char *name, bpf_u_int32 flags,
+    const char *description, char *errbuf)
+{
+       pcap_if_t *curdev, *prevdev, *nextdev;
+       u_int this_figure_of_merit, nextdev_figure_of_merit;
+
+       curdev = malloc(sizeof(pcap_if_t));
+       if (curdev == NULL) {
+               (void)pcap_snprintf(errbuf, PCAP_ERRBUF_SIZE,
+                   "malloc: %s", pcap_strerror(errno));
+               return (NULL);
+       }
+
+       /*
+        * Fill in the entry.
+        */
+       curdev->next = NULL;
+       curdev->name = strdup(name);
+       if (curdev->name == NULL) {
+               (void)pcap_snprintf(errbuf, PCAP_ERRBUF_SIZE,
+                   "malloc: %s", pcap_strerror(errno));
+               free(curdev);
+               return (NULL);
+       }
+       if (description == NULL) {
+               /*
+                * We weren't handed a description for the interface.
+                */
+               curdev->description = NULL;
+       } else {
+               /*
+                * We were handed a description; make a copy.
+                */
+               curdev->description = strdup(description);
+               if (curdev->description == NULL) {
+                       (void)pcap_snprintf(errbuf, PCAP_ERRBUF_SIZE,
+                           "malloc: %s", pcap_strerror(errno));
+                       free(curdev->name);
+                       free(curdev);
+                       return (NULL);
+               }
+       }
+       curdev->addresses = NULL;       /* list starts out as empty */
+       curdev->flags = flags;
+
+       /*
+        * Add it to the list, in the appropriate location.
+        * First, get the "figure of merit" for this interface.
+        */
+       this_figure_of_merit = get_figure_of_merit(curdev);
+
+       /*
+        * Now look for the last interface with an figure of merit
+        * less than or equal to the new interface's figure of merit.
+        *
+        * 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's figure of merit less
+                * than the next interface's figure of merit,
+                * meaning that the new interface is better
+                * than the next interface?
+                */
+               nextdev_figure_of_merit = get_figure_of_merit(nextdev);
+               if (this_figure_of_merit < nextdev_figure_of_merit) {
+                       /*
+                        * 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;
+       return (curdev);
+}
+
+/*
+ * 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);
+       }
+}
+
 pcap_t *
 pcap_create(const char *device, char *errbuf)
 {