diff options
author | Damien Miller <djm@cvs.openbsd.org> | 2005-11-18 11:05:40 +0000 |
---|---|---|
committer | Damien Miller <djm@cvs.openbsd.org> | 2005-11-18 11:05:40 +0000 |
commit | b3d796352917fe2a0f9021b3d370e7fa278210f1 (patch) | |
tree | 0c943c5097ed32c71ced43194d854fcb75db9446 /lib | |
parent | 237b9b47f2326dabd4a6bddc33123e3a52be6179 (diff) |
pull in the good bits of libpcap-0.9.4's API without the cross-platform
cruft. help from deraadt@, mpf@, jmc@; ok mpf@a
nb. this will break tcpdump unless it updated too
Diffstat (limited to 'lib')
-rw-r--r-- | lib/libpcap/Makefile | 13 | ||||
-rw-r--r-- | lib/libpcap/fad-getad.c | 498 | ||||
-rw-r--r-- | lib/libpcap/inet.c | 68 | ||||
-rw-r--r-- | lib/libpcap/pcap-bpf.c | 113 | ||||
-rw-r--r-- | lib/libpcap/pcap-int.h | 19 | ||||
-rw-r--r-- | lib/libpcap/pcap-namedb.h | 6 | ||||
-rw-r--r-- | lib/libpcap/pcap.3 | 83 | ||||
-rw-r--r-- | lib/libpcap/pcap.c | 182 | ||||
-rw-r--r-- | lib/libpcap/pcap.h | 45 | ||||
-rw-r--r-- | lib/libpcap/shlib_version | 4 |
10 files changed, 997 insertions, 34 deletions
diff --git a/lib/libpcap/Makefile b/lib/libpcap/Makefile index 12d178175ac..3e0d2a560ac 100644 --- a/lib/libpcap/Makefile +++ b/lib/libpcap/Makefile @@ -1,4 +1,4 @@ -# $OpenBSD: Makefile,v 1.15 2003/02/20 03:20:09 deraadt Exp $ +# $OpenBSD: Makefile,v 1.16 2005/11/18 11:05:38 djm Exp $ # $NetBSD: Makefile,v 1.3 1996/05/10 21:54:24 cgd Exp $ LIB= pcap @@ -13,7 +13,14 @@ MLINKS= pcap.3 pcap_open_live.3 pcap.3 pcap_open_offline.3 \ pcap_stats.3 pcap.3 pcap_file.3 pcap.3 pcap_fileno.3 pcap.3 \ pcap_perror.3 pcap.3 pcap_geterr.3 pcap.3 pcap_strerror.3 \ pcap.3 pcap_close.3 pcap.3 pcap_dump_close.3 \ - pcap.3 pcap_freecode.3 + pcap.3 pcap_freecode.3 pcap.3 pcap_breakloop.3 \ + pcap.3 pcap_findalldevs.3 pcap.3 pcap_freealldevs.3 \ + pcap.3 pcap_getnonblock.3 pcap.3 pcap_setnonblock.3 \ + pcap.3 pcap_set_datalink.3 pcap.3 pcap_list_datalinks.3 \ + pcap.3 pcap_open_dead.3 pcap.3 pcap_lib_version.3 \ + pcap.3 pcap_datalink_val_to_name.3 \ + pcap.3 pcap_datalink_val_to_description.3 \ + pcap.3 pcap_datalink_name_to_val.3 DEFS= -DHAVE_SYS_IOCCOM_H -DHAVE_SYS_SOCKIO_H -DHAVE_ETHER_HOSTTON \ -DHAVE_STRERROR -DHAVE_SOCKADDR_SA_LEN -DLBL_ALIGN -DHAVE_IFADDRS_H \ @@ -24,7 +31,7 @@ CFLAGS+=-I. -I${.CURDIR} -Dyylval=pcap_yylval ${DEFS} HDRS= pcap.h pcap-int.h pcap-namedb.h SRCS= pcap.c inet.c gencode.c optimize.c nametoaddr.c etherent.c \ savefile.c bpf_filter.c bpf_image.c grammar.y scanner.l \ - pcap-bpf.c version.c + pcap-bpf.c version.c fad-getad.c .PATH: ${.CURDIR}/../../sys/net CLEANFILES+= grammar.c scanner.c grammar.tab.h version.c diff --git a/lib/libpcap/fad-getad.c b/lib/libpcap/fad-getad.c new file mode 100644 index 00000000000..94783071dfc --- /dev/null +++ b/lib/libpcap/fad-getad.c @@ -0,0 +1,498 @@ +/* -*- 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. + */ + +#include <sys/types.h> +#include <sys/socket.h> +#include <netinet/in.h> + +#include <net/if.h> + +#include <ctype.h> +#include <errno.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <ifaddrs.h> +#include <limits.h> + +#include "pcap-int.h" + +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)); +} + +static int +get_instance(const char *name) +{ + const char *cp, *endcp; + int n; + + if (strcmp(name, "any") == 0) { + /* + * Give the "any" device an artificially high instance + * number, so it shows up after all other non-loopback + * interfaces. + */ + return INT_MAX; + } + + endcp = name + strlen(name); + for (cp = name; cp < endcp && !isdigit((unsigned char)*cp); ++cp) + continue; + + if (isdigit((unsigned char)*cp)) + n = atoi(cp); + else + n = 0; + return (n); +} + +static int +add_or_find_if(pcap_if_t **curdev_ret, pcap_if_t **alldevs, const char *name, + u_int flags, const char *description, char *errbuf) +{ + pcap_t *p; + pcap_if_t *curdev, *prevdev, *nextdev; + int this_instance; + size_t len; + + /* + * Can we open this interface for live capture? + * + * We do this check so that interfaces that are supplied + * by the interface enumeration mechanism we're using + * but that don't support packet capture aren't included + * in the list. An example of this is loopback interfaces + * on Solaris; we don't just omit loopback interfaces + * becaue you *can* capture on loopback interfaces on some + * OSes. + */ + p = pcap_open_live(name, 68, 0, 0, errbuf); + if (p == NULL) { + /* + * No. Don't bother including it. + * Don't treat this as an error, though. + */ + *curdev_ret = NULL; + return (0); + } + pcap_close(p); + + /* + * Is there already an entry in the list for this interface? + */ + for (curdev = *alldevs; curdev != NULL; curdev = curdev->next) { + if (strcmp(name, curdev->name) == 0) + break; /* yes, we found it */ + } + if (curdev == NULL) { + /* + * No, we didn't find it. + * Allocate a new entry. + */ + curdev = malloc(sizeof(pcap_if_t)); + if (curdev == NULL) { + (void)snprintf(errbuf, PCAP_ERRBUF_SIZE, + "malloc: %s", pcap_strerror(errno)); + return (-1); + } + + /* + * Fill in the entry. + */ + curdev->next = NULL; + len = strlen(name) + 1; + curdev->name = malloc(len); + strlcpy(curdev->name, name, len); + if (description != NULL) { + /* + * We have a description for this interface. + */ + len = strlen(description) + 1; + curdev->description = malloc(len); + strlcpy(curdev->description, description, len); + } else { + /* + * We don't. + */ + curdev->description = NULL; + } + curdev->addresses = NULL; /* list starts out as empty */ + curdev->flags = 0; + if (ISLOOPBACK(name, flags)) + curdev->flags |= PCAP_IF_LOOPBACK; + + /* + * Add it to the list, in the appropriate location. + * First, get the instance number of this interface. + */ + this_instance = get_instance(name); + + /* + * Now look for the last interface with an instance number + * less than or equal to the new interface's instance + * number - except that non-loopback interfaces are + * arbitrarily treated as having interface numbers less + * than those of loopback interfaces, so the loopback + * interfaces are put at the end of the list. + * + * We start with "prevdev" being NULL, meaning we're before + * the first element in the list. + */ + prevdev = NULL; + for (;;) { + /* + * Get the interface after this one. + */ + if (prevdev == NULL) { + /* + * The next element is the first element. + */ + nextdev = *alldevs; + } else + nextdev = prevdev->next; + + /* + * Are we at the end of the list? + */ + if (nextdev == NULL) { + /* + * Yes - we have to put the new entry + * after "prevdev". + */ + break; + } + + /* + * Is the new interface a non-loopback interface + * and the next interface a loopback interface? + */ + if (!(curdev->flags & PCAP_IF_LOOPBACK) && + (nextdev->flags & PCAP_IF_LOOPBACK)) { + /* + * Yes, we should put the new entry + * before "nextdev", i.e. after "prevdev". + */ + break; + } + + /* + * Is the new interface's instance number less + * than the next interface's instance number, + * and is it the case that the new interface is a + * non-loopback interface or the next interface is + * a loopback interface? + * + * (The goal of both loopback tests is to make + * sure that we never put a loopback interface + * before any non-loopback interface and that we + * always put a non-loopback interface before all + * loopback interfaces.) + */ + if (this_instance < get_instance(nextdev->name) && + (!(curdev->flags & PCAP_IF_LOOPBACK) || + (nextdev->flags & PCAP_IF_LOOPBACK))) { + /* + * Yes - we should put the new entry + * before "nextdev", i.e. after "prevdev". + */ + break; + } + + prevdev = nextdev; + } + + /* + * Insert before "nextdev". + */ + curdev->next = nextdev; + + /* + * Insert after "prevdev" - unless "prevdev" is null, + * in which case this is the first interface. + */ + if (prevdev == NULL) { + /* + * This is the first interface. Pass back a + * pointer to it, and put "curdev" before + * "nextdev". + */ + *alldevs = curdev; + } else + prevdev->next = curdev; + } + + *curdev_ret = curdev; + return (0); +} + +static int +add_addr_to_iflist(pcap_if_t **alldevs, const char *name, u_int 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; + pcap_addr_t *curaddr, *prevaddr, *nextaddr; + + if (add_or_find_if(&curdev, alldevs, name, flags, NULL, errbuf) == -1) { + /* + * Error - give up. + */ + return (-1); + } + if (curdev == NULL) { + /* + * Device wasn't added because it can't be opened. + * Not a fatal error. + */ + return (0); + } + + /* + * "curdev" is an entry for this interface; add an entry for this + * address to its list of addresses. + * + * Allocate the new entry and fill it in. + */ + curaddr = malloc(sizeof(pcap_addr_t)); + if (curaddr == NULL) { + (void)snprintf(errbuf, PCAP_ERRBUF_SIZE, + "malloc: %s", pcap_strerror(errno)); + return (-1); + } + + curaddr->next = NULL; + if (addr != NULL) { + curaddr->addr = dup_sockaddr(addr, addr_size); + 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, netmask_size); + 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, broadaddr_size); + 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, dstaddr_size); + if (curaddr->dstaddr == NULL) { + (void)snprintf(errbuf, PCAP_ERRBUF_SIZE, + "malloc: %s", pcap_strerror(errno)); + free(curaddr); + return (-1); + } + } else + curaddr->dstaddr = NULL; + + /* + * Find the end of the list of addresses. + */ + for (prevaddr = curdev->addresses; prevaddr != NULL; + prevaddr = nextaddr) { + nextaddr = prevaddr->next; + if (nextaddr == NULL) { + /* + * This is the end of the list. + */ + break; + } + } + + if (prevaddr == NULL) { + /* + * The list was empty; this is the first member. + */ + curdev->addresses = curaddr; + } else { + /* + * "prevaddr" is the last member of the list; append + * this member to it. + */ + prevaddr->next = curaddr; + } + + return (0); +} + +/* + * 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. + * + * This is the implementation used on platforms that have "getifaddrs()". + */ +int +pcap_findalldevs(pcap_if_t **alldevsp, char *errbuf) +{ + pcap_if_t *devlist = NULL; + struct ifaddrs *ifap, *ifa; + struct sockaddr *addr, *netmask, *broadaddr, *dstaddr; + size_t addr_size, broadaddr_size, dstaddr_size; + int ret = 0; + + /* + * Get the list of interface addresses. + * + * Note: this won't return information about interfaces + * with no addresses; are there any such interfaces + * that would be capable of receiving packets? + * (Interfaces incapable of receiving packets aren't + * very interesting from libpcap's point of view.) + * + * LAN interfaces will probably have link-layer + * addresses; I don't know whether all implementations + * of "getifaddrs()" now, or in the future, will return + * those. + */ + if (getifaddrs(&ifap) != 0) { + (void)snprintf(errbuf, PCAP_ERRBUF_SIZE, + "getifaddrs: %s", pcap_strerror(errno)); + return (-1); + } + for (ifa = ifap; ifa != NULL; ifa = ifa->ifa_next) { + /* + * Is this interface up? + */ + if (!(ifa->ifa_flags & IFF_UP)) { + /* + * No, so don't add it to the list. + */ + continue; + } + + /* + * "ifa_addr" was apparently null on at least one + * interface on some system. + * + * "ifa_broadaddr" may be non-null even on + * non-broadcast interfaces, and was null on + * at least one OpenBSD 3.4 system on at least + * one interface with IFF_BROADCAST set. + * + * "ifa_dstaddr" was, on at least one FreeBSD 4.1 + * system, non-null on a non-point-to-point + * interface. + * + * Therefore, we supply the address and netmask only + * if "ifa_addr" is non-null (if there's no address, + * there's obviously no netmask), and supply the + * broadcast and destination addresses if the appropriate + * flag is set *and* the appropriate "ifa_" entry doesn't + * evaluate to a null pointer. + */ + if (ifa->ifa_addr != NULL) { + addr = ifa->ifa_addr; + addr_size = SA_LEN(addr); + netmask = ifa->ifa_netmask; + } else { + addr = NULL; + addr_size = 0; + netmask = NULL; + } + if (ifa->ifa_flags & IFF_BROADCAST && + ifa->ifa_broadaddr != NULL) { + broadaddr = ifa->ifa_broadaddr; + broadaddr_size = SA_LEN(broadaddr); + } else { + broadaddr = NULL; + broadaddr_size = 0; + } + if (ifa->ifa_flags & IFF_POINTOPOINT && + ifa->ifa_dstaddr != NULL) { + dstaddr = ifa->ifa_dstaddr; + dstaddr_size = SA_LEN(ifa->ifa_dstaddr); + } else { + dstaddr = NULL; + dstaddr_size = 0; + } + + /* + * Add information for this address to the list. + */ + if (add_addr_to_iflist(&devlist, ifa->ifa_name, + ifa->ifa_flags, addr, addr_size, netmask, addr_size, + broadaddr, broadaddr_size, dstaddr, dstaddr_size, + errbuf) < 0) { + ret = -1; + break; + } + } + + freeifaddrs(ifap); + + if (ret == -1) { + /* + * We had an error; free the list we've been constructing. + */ + if (devlist != NULL) { + pcap_freealldevs(devlist); + devlist = NULL; + } + } + + *alldevsp = devlist; + return (ret); +} diff --git a/lib/libpcap/inet.c b/lib/libpcap/inet.c index 983275446d5..23bb811e04d 100644 --- a/lib/libpcap/inet.c +++ b/lib/libpcap/inet.c @@ -1,4 +1,4 @@ -/* $OpenBSD: inet.c,v 1.16 2004/01/27 06:58:03 tedu Exp $ */ +/* $OpenBSD: inet.c,v 1.17 2005/11/18 11:05:39 djm Exp $ */ /* * Copyright (c) 1994, 1995, 1996, 1997, 1998 @@ -66,13 +66,52 @@ struct rtentry; #include "os-proto.h" #endif -/* Not all systems have IFF_LOOPBACK */ -#ifdef IFF_LOOPBACK -#define ISLOOPBACK(p) ((p)->ifr_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')) -#endif +/* + * 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 @@ -100,16 +139,8 @@ pcap_lookupdev(errbuf) for (ifa = ifap; ifa; ifa = ifa->ifa_next) { if ((ifa->ifa_flags & IFF_UP) == 0) continue; -#ifdef IFF_LOOPBACK - if ((ifa->ifa_flags & IFF_LOOPBACK) != 0) - continue; -#else - if (strncmp(ifa->ifa_name, "lo", 2) == 0 && - (ifa->ifa_name[2] == '\0' || isdigit(ifa->ifa_name[2]))) { + if (ISLOOPBACK(ifa->ifa_name, ifa->ifa_flags)) continue; - } -#endif - for (cp = ifa->ifa_name; !isdigit(*cp); ++cp) continue; n = atoi(cp); @@ -190,7 +221,8 @@ pcap_lookupdev(errbuf) } /* Must be up and not the loopback */ - if ((ifr.ifr_flags & IFF_UP) == 0 || ISLOOPBACK(&ifr)) + if ((ifr.ifr_flags & IFF_UP) == 0 || + ISLOOPBACK(ifr.ifr_name, ifr.ifr_flags) continue; for (cp = ifrp->ifr_name; !isdigit(*cp); ++cp) diff --git a/lib/libpcap/pcap-bpf.c b/lib/libpcap/pcap-bpf.c index f81375e0203..ac72b43f703 100644 --- a/lib/libpcap/pcap-bpf.c +++ b/lib/libpcap/pcap-bpf.c @@ -1,4 +1,4 @@ -/* $OpenBSD: pcap-bpf.c,v 1.16 2004/02/06 22:41:24 tedu Exp $ */ +/* $OpenBSD: pcap-bpf.c,v 1.17 2005/11/18 11:05:39 djm Exp $ */ /* * Copyright (c) 1993, 1994, 1995, 1996, 1998 @@ -70,6 +70,19 @@ pcap_read(pcap_t *p, int cnt, pcap_handler callback, u_char *user) register u_char *bp, *ep; again: + /* + * Has "pcap_breakloop()" been called? + */ + if (p->break_loop) { + /* + * Yes - clear the flag that indicates that it + * has, and return -2 to indicate that we were + * told to break out of the loop. + */ + p->break_loop = 0; + return (-2); + } + cc = p->cc; if (p->cc == 0) { cc = read(p->fd, (char *)p->buffer, p->bufsize); @@ -112,6 +125,27 @@ pcap_read(pcap_t *p, int cnt, pcap_handler callback, u_char *user) ep = bp + cc; while (bp < ep) { register int caplen, hdrlen; + + /* + * Has "pcap_breakloop()" been called? + * If so, return immediately - if we haven't read any + * packets, clear the flag and return -2 to indicate + * that we were told to break out of the loop, otherwise + * leave the flag set, so that the *next* call will break + * out of the loop without having read any packets, and + * return the number of packets we've processed so far. + */ + if (p->break_loop) { + if (n == 0) { + p->break_loop = 0; + return (-2); + } else { + p->bp = bp; + p->cc = ep - bp; + return (n); + } + } + caplen = bhp->bh_caplen; hdrlen = bhp->bh_hdrlen; /* @@ -164,13 +198,15 @@ bpf_open(pcap_t *p, char *errbuf) } pcap_t * -pcap_open_live(char *device, int snaplen, int promisc, int to_ms, char *ebuf) +pcap_open_live(const char *device, int snaplen, int promisc, int to_ms, + char *ebuf) { int fd; struct ifreq ifr; struct bpf_version bv; u_int v; pcap_t *p; + struct bpf_dltlist bdl; p = (pcap_t *)malloc(sizeof(*p)); if (p == NULL) { @@ -235,6 +271,37 @@ pcap_open_live(char *device, int snaplen, int promisc, int to_ms, char *ebuf) #endif p->linktype = v; + /* + * We know the default link type -- now determine all the DLTs + * this interface supports. If this fails with EINVAL, it's + * not fatal; we just don't get to use the feature later. + */ + bzero(&bdl, sizeof(bdl)); + if (ioctl(fd, BIOCGDLTLIST, (caddr_t)&bdl) == 0) { + bdl.bfl_list = (u_int *) malloc(sizeof(u_int) * + bdl.bfl_len + 1); + if (bdl.bfl_list == NULL) { + (void)snprintf(ebuf, PCAP_ERRBUF_SIZE, "malloc: %s", + pcap_strerror(errno)); + goto bad; + } + + if (ioctl(fd, BIOCGDLTLIST, (caddr_t)&bdl) < 0) { + (void)snprintf(ebuf, PCAP_ERRBUF_SIZE, + "BIOCGDLTLIST: %s", pcap_strerror(errno)); + free(bdl.bfl_list); + goto bad; + } + p->dlt_count = bdl.bfl_len; + p->dlt_list = bdl.bfl_list; + } else { + if (errno != EINVAL) { + (void)snprintf(ebuf, PCAP_ERRBUF_SIZE, + "BIOCGDLTLIST: %s", pcap_strerror(errno)); + goto bad; + } + } + /* set timeout */ if (to_ms != 0) { struct timeval to; @@ -298,3 +365,45 @@ pcap_setfilter(pcap_t *p, struct bpf_program *fp) } return (0); } + +int +pcap_set_datalink(pcap_t *p, int dlt) +{ + int i; + + if (p->dlt_count == 0) { + /* + * We couldn't fetch the list of DLTs, or we don't + * have a "set datalink" operation, which means + * this platform doesn't support changing the + * DLT for an interface. Check whether the new + * DLT is the one this interface supports. + */ + if (p->linktype != dlt) + goto unsupported; + + /* + * It is, so there's nothing we need to do here. + */ + return (0); + } + for (i = 0; i < p->dlt_count; i++) + if (p->dlt_list[i] == dlt) + break; + if (i >= p->dlt_count) + goto unsupported; + if (ioctl(p->fd, BIOCSDLT, &dlt) == -1) { + (void) snprintf(p->errbuf, sizeof(p->errbuf), + "Cannot set DLT %d: %s", dlt, strerror(errno)); + return (-1); + } + p->linktype = dlt; + return (0); + +unsupported: + (void) snprintf(p->errbuf, sizeof(p->errbuf), + "DLT %d is not one of the DLTs supported by this device", + dlt); + return (-1); +} + diff --git a/lib/libpcap/pcap-int.h b/lib/libpcap/pcap-int.h index a3997831619..e5d2e204b33 100644 --- a/lib/libpcap/pcap-int.h +++ b/lib/libpcap/pcap-int.h @@ -1,4 +1,4 @@ -/* $OpenBSD: pcap-int.h,v 1.9 2001/10/02 18:04:35 deraadt Exp $ */ +/* $OpenBSD: pcap-int.h,v 1.10 2005/11/18 11:05:39 djm Exp $ */ /* * Copyright (c) 1994, 1995, 1996 @@ -32,7 +32,7 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#) $Header: /cvs/OpenBSD/src/lib/libpcap/pcap-int.h,v 1.9 2001/10/02 18:04:35 deraadt Exp $ (LBL) + * @(#) $Header: /cvs/OpenBSD/src/lib/libpcap/pcap-int.h,v 1.10 2005/11/18 11:05:39 djm Exp $ (LBL) */ #ifndef pcap_int_h @@ -73,6 +73,7 @@ struct pcap { int linktype; int tzoff; /* timezone offset */ int offset; /* offset for proper alignment */ + int break_loop; /* force break from packet-reading loop */ struct pcap_sf sf; struct pcap_md md; @@ -96,6 +97,12 @@ struct pcap { */ struct bpf_program fcode; + /* + * Datalink types supported on underlying fd + */ + int dlt_count; + u_int *dlt_list; + char errbuf[PCAP_ERRBUF_SIZE]; }; @@ -115,6 +122,14 @@ int yylex(void); #define min(a, b) ((a) > (b) ? (b) : (a)) #endif +/* 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 + /* XXX should these be in pcap.h? */ int pcap_offline_read(pcap_t *, int, pcap_handler, u_char *); int pcap_read(pcap_t *, int cnt, pcap_handler, u_char *); diff --git a/lib/libpcap/pcap-namedb.h b/lib/libpcap/pcap-namedb.h index 5b50bc77867..5e336b28d4f 100644 --- a/lib/libpcap/pcap-namedb.h +++ b/lib/libpcap/pcap-namedb.h @@ -1,4 +1,4 @@ -/* $OpenBSD: pcap-namedb.h,v 1.6 2000/04/26 21:25:53 jakob Exp $ */ +/* $OpenBSD: pcap-namedb.h,v 1.7 2005/11/18 11:05:39 djm Exp $ */ /* * Copyright (c) 1994, 1996 @@ -32,7 +32,7 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#) $Header: /cvs/OpenBSD/src/lib/libpcap/pcap-namedb.h,v 1.6 2000/04/26 21:25:53 jakob Exp $ (LBL) + * @(#) $Header: /cvs/OpenBSD/src/lib/libpcap/pcap-namedb.h,v 1.7 2005/11/18 11:05:39 djm Exp $ (LBL) */ #ifndef lib_pcap_ethers_h @@ -65,6 +65,8 @@ bpf_u_int32 pcap_nametonetaddr(const char *); int pcap_nametoport(const char *, int *, int *); int pcap_nametoproto(const char *); int pcap_nametoeproto(const char *); +int pcap_nametollc(const char *); + /* * If a protocol is unknown, PROTO_UNDEF is returned. * Also, pcap_nametoport() returns the protocol along with the port number. diff --git a/lib/libpcap/pcap.3 b/lib/libpcap/pcap.3 index 666ac9ab549..e144afd32cb 100644 --- a/lib/libpcap/pcap.3 +++ b/lib/libpcap/pcap.3 @@ -1,4 +1,4 @@ -.\" $OpenBSD: pcap.3,v 1.25 2003/03/06 20:13:15 jmc Exp $ +.\" $OpenBSD: pcap.3,v 1.26 2005/11/18 11:05:39 djm Exp $ .\" .\" Copyright (c) 1994, 1996, 1997 .\" The Regents of the University of California. All rights reserved. @@ -79,6 +79,30 @@ .Fn pcap_close "pcap_t *p" .Ft void .Fn pcap_dump_close "pcap_dumper_t *p" +.Ft int +.Fn pcap_breakloop "pcap_t *p" +.Ft int +.Fn pcap_findalldevs "pcap_if_t **alldevsp" "char *errbuf" +.Ft void +.Fn pcap_freealldevs "pcap_if_t *alldevs" +.Ft int +.Fn pcap_getnonblock "pcap_t *p" "char *errbuf" +.Ft int +.Fn pcap_setnonblock "pcap_t *p" "int nonblock" "char *errbuf" +.Ft int +.Fn pcap_set_datalink "pcap_t *" "int dlt" +.Ft int +.Fn pcap_list_datalinks "pcap_t *p" "int **dlts" +.Ft pcap_t +.Fn pcap_open_dead "int linktype" "int snaplen" +.Ft const char * +.Fn pcap_lib_version "void" +.Ft const char * +.Fn pcap_datalink_val_to_name "int" +.Ft const char * +.Fn pcap_datalink_val_to_description "int" +.Ft int +.Fn pcap_datalink_name_to_val "const char *" .Sh DESCRIPTION .Nm provides a high level interface to packet capture systems. @@ -91,6 +115,7 @@ through this mechanism. in .Fn pcap_open_live , .Fn pcap_open_offline , +.Fn pcap_findalldevs , .Fn pcap_lookupdev , and .Fn pcap_lookupnet @@ -287,6 +312,12 @@ A negative causes .Fn pcap_loop to loop forever (or at least until an error occurs). +.Fn pcap_loop +may be terminated early through an explicit call to +.Fn pcap_breakloop . +In this case, the return value of +.Fn pcap_loop +will be \-2. .Pp .Fn pcap_next returns a @@ -352,6 +383,56 @@ and deallocates resources. .Pp .Fn pcap_dump_close closes the savefile. +.Pp +.Fn pcap_findalldevs +constructs a linked list of network devices that are suitable for +opening with +.Fn pcap_open_live . +.Pp +.Fn pcap_freealldevs +frees a list of interfaces built by +.Fn pcap_findalldevs . +.Pp +.Fn pcap_getnonblock +returns 1 if the capture file descriptor is in non-blocking mode, 0 +if it is in blocking mode, or \-1 on error. +.Pp +.Fn pcap_setnonblock +sets or resets non-blocking mode on a capture file descriptor. +.Pp +.Fn pcap_set_datalink +sets the datalink type on a live capture device that supports multiple +datalink types. +.Pp +.Fn pcap_list_datalinks +returns an array of the supported datalink types for an opened live capture +device as a \-1 terminated array. +It is the caller's responsibility to free this list. +.Pp +.Fn pcap_breakloop +safely breaks out of a +.Fn pcap_loop . +This function sets an internal flag and is safe to be called from inside a +signal handler. +.Pp +.Fn pcap_open_dead +is used for creating a pcap_t structure to use when calling the +other functions in libpcap. +It is typically used when just using libpcap for compiling BPF code. +.Pp +.Fn pcap_lib_version +returns a string describing the version of +.Xr pcap 3 . +.Fn pcap_datalink_val_to_name +and +.Fn pcap_datalink_val_to_description +lookup the name or description of a datalink type by number. +These functions return +.Dv NULL +if the specified datalink type is not known. +.Fn pcap_datalink_name_to_val +finds the datalink number for a given datalink name. +Returns \-1 if the name is not known. .Sh SEE ALSO .Xr tcpdump 8 .\" , tcpslice(1) diff --git a/lib/libpcap/pcap.c b/lib/libpcap/pcap.c index 3eab3051cef..30f58292718 100644 --- a/lib/libpcap/pcap.c +++ b/lib/libpcap/pcap.c @@ -1,4 +1,4 @@ -/* $OpenBSD: pcap.c,v 1.8 2004/01/27 06:58:03 tedu Exp $ */ +/* $OpenBSD: pcap.c,v 1.9 2005/11/18 11:05:39 djm Exp $ */ /* * Copyright (c) 1993, 1994, 1995, 1996, 1997, 1998 @@ -39,6 +39,8 @@ #include <stdlib.h> #include <string.h> #include <unistd.h> +#include <errno.h> +#include <fcntl.h> #ifdef HAVE_OS_PROTO_H #include "os-proto.h" @@ -46,6 +48,8 @@ #include "pcap-int.h" +static const char pcap_version_string[] = "OpenBSD libpcap"; + int pcap_dispatch(pcap_t *p, int cnt, pcap_handler callback, u_char *user) { @@ -107,6 +111,15 @@ pcap_next(pcap_t *p, struct pcap_pkthdr *h) return (s.pkt); } +/* + * Force the loop in "pcap_read()" or "pcap_read_offline()" to terminate. + */ +void +pcap_breakloop(pcap_t *p) +{ + p->break_loop = 1; +} + int pcap_datalink(pcap_t *p) { @@ -114,6 +127,109 @@ pcap_datalink(pcap_t *p) } int +pcap_list_datalinks(pcap_t *p, int **dlt_buffer) +{ + if (p->dlt_count == 0) { + /* + * We couldn't fetch the list of DLTs, which means + * this platform doesn't support changing the + * DLT for an interface. Return a list of DLTs + * containing only the DLT this device supports. + */ + *dlt_buffer = (int*)malloc(sizeof(**dlt_buffer)); + if (*dlt_buffer == NULL) { + (void)snprintf(p->errbuf, sizeof(p->errbuf), + "malloc: %s", pcap_strerror(errno)); + return (-1); + } + **dlt_buffer = p->linktype; + return (1); + } else { + *dlt_buffer = (int*)malloc(sizeof(**dlt_buffer) * p->dlt_count); + if (*dlt_buffer == NULL) { + (void)snprintf(p->errbuf, sizeof(p->errbuf), + "malloc: %s", pcap_strerror(errno)); + return (-1); + } + (void)memcpy(*dlt_buffer, p->dlt_list, + sizeof(**dlt_buffer) * p->dlt_count); + return (p->dlt_count); + } +} + +struct dlt_choice { + const char *name; + const char *description; + int dlt; +}; + +static struct dlt_choice dlts[] = { +#define DLT_CHOICE(code, description) { #code, description, code } +DLT_CHOICE(DLT_NULL, "no link-layer encapsulation"), +DLT_CHOICE(DLT_EN10MB, "Ethernet (10Mb)"), +DLT_CHOICE(DLT_EN3MB, "Experimental Ethernet (3Mb)"), +DLT_CHOICE(DLT_AX25, "Amateur Radio AX.25"), +DLT_CHOICE(DLT_PRONET, "Proteon ProNET Token Ring"), +DLT_CHOICE(DLT_CHAOS, "Chaos"), +DLT_CHOICE(DLT_IEEE802, "IEEE 802 Networks"), +DLT_CHOICE(DLT_ARCNET, "ARCNET"), +DLT_CHOICE(DLT_SLIP, "Serial Line IP"), +DLT_CHOICE(DLT_PPP, "Point-to-point Protocol"), +DLT_CHOICE(DLT_FDDI, "FDDI"), +DLT_CHOICE(DLT_ATM_RFC1483, "LLC/SNAP encapsulated atm"), +DLT_CHOICE(DLT_LOOP, "loopback type (af header)"), +DLT_CHOICE(DLT_ENC, "IPSEC enc type (af header, spi, flags)"), +DLT_CHOICE(DLT_RAW, "raw IP"), +DLT_CHOICE(DLT_SLIP_BSDOS, "BSD/OS Serial Line IP"), +DLT_CHOICE(DLT_PPP_BSDOS, "BSD/OS Point-to-point Protocol"), +DLT_CHOICE(DLT_OLD_PFLOG, "Packet filter logging, old (XXX remove?)"), +DLT_CHOICE(DLT_PFSYNC, "Packet filter state syncing"), +DLT_CHOICE(DLT_PPP_ETHER, "PPP over Ethernet; session only w/o ether header"), +DLT_CHOICE(DLT_IEEE802_11, "IEEE 802.11 wireless"), +DLT_CHOICE(DLT_PFLOG, "Packet filter logging, by pcap people"), +DLT_CHOICE(DLT_IEEE802_11_RADIO, "IEEE 802.11 plus WLAN header"), +#undef DLT_CHOICE + { NULL, NULL, -1} +}; + +int +pcap_datalink_name_to_val(const char *name) +{ + int i; + + for (i = 0; dlts[i].name != NULL; i++) { + /* Skip leading "DLT_" */ + if (strcasecmp(dlts[i].name + 4, name) == 0) + return (dlts[i].dlt); + } + return (-1); +} + +const char * +pcap_datalink_val_to_name(int dlt) +{ + int i; + + for (i = 0; dlts[i].name != NULL; i++) { + if (dlts[i].dlt == dlt) + return (dlts[i].name + 4); /* Skip leading "DLT_" */ + } + return (NULL); +} + +const char * +pcap_datalink_val_to_description(int dlt) +{ + int i; + + for (i = 0; dlts[i].name != NULL; i++) { + if (dlts[i].dlt == dlt) + return (dlts[i].description); + } + return (NULL); +} + +int pcap_snapshot(pcap_t *p) { return (p->snapshot); @@ -161,6 +277,46 @@ pcap_geterr(pcap_t *p) return (p->errbuf); } +int +pcap_getnonblock(pcap_t *p, char *errbuf) +{ + int fdflags; + + fdflags = fcntl(p->fd, F_GETFL, 0); + if (fdflags == -1) { + snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "F_GETFL: %s", + pcap_strerror(errno)); + return (-1); + } + if (fdflags & O_NONBLOCK) + return (1); + else + return (0); +} + +int +pcap_setnonblock(pcap_t *p, int nonblock, char *errbuf) +{ + int fdflags; + + fdflags = fcntl(p->fd, F_GETFL, 0); + if (fdflags == -1) { + snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "F_GETFL: %s", + pcap_strerror(errno)); + return (-1); + } + if (nonblock) + fdflags |= O_NONBLOCK; + else + fdflags &= ~O_NONBLOCK; + if (fcntl(p->fd, F_SETFL, fdflags) == -1) { + snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "F_SETFL: %s", + pcap_strerror(errno)); + return (-1); + } + return (0); +} + /* * Not all systems have strerror(). */ @@ -181,6 +337,21 @@ pcap_strerror(int errnum) #endif } +pcap_t * +pcap_open_dead(int linktype, int snaplen) +{ + pcap_t *p; + + p = malloc(sizeof(*p)); + if (p == NULL) + return NULL; + memset (p, 0, sizeof(*p)); + p->snapshot = snaplen; + p->linktype = linktype; + p->fd = -1; + return p; +} + void pcap_close(pcap_t *p) { @@ -198,5 +369,14 @@ pcap_close(pcap_t *p) free(p->md.device); #endif pcap_freecode(&p->fcode); + if (p->dlt_list != NULL) + free(p->dlt_list); free(p); } + +const char * +pcap_lib_version(void) +{ + return (pcap_version_string); +} + diff --git a/lib/libpcap/pcap.h b/lib/libpcap/pcap.h index 0404f31f92e..f4c0e2eade6 100644 --- a/lib/libpcap/pcap.h +++ b/lib/libpcap/pcap.h @@ -1,4 +1,4 @@ -/* $OpenBSD: pcap.h,v 1.12 2004/06/24 18:29:38 naddy Exp $ */ +/* $OpenBSD: pcap.h,v 1.13 2005/11/18 11:05:39 djm Exp $ */ /* * Copyright (c) 1993, 1994, 1995, 1996, 1997 @@ -32,7 +32,7 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#) $Header: /cvs/OpenBSD/src/lib/libpcap/pcap.h,v 1.12 2004/06/24 18:29:38 naddy Exp $ (LBL) + * @(#) $Header: /cvs/OpenBSD/src/lib/libpcap/pcap.h,v 1.13 2005/11/18 11:05:39 djm Exp $ (LBL) */ #ifndef lib_pcap_h @@ -60,6 +60,8 @@ typedef u_int bpf_u_int32; #endif typedef struct pcap pcap_t; +typedef struct pcap_if pcap_if_t; +typedef struct pcap_addr pcap_addr_t; typedef struct pcap_dumper pcap_dumper_t; /* @@ -98,14 +100,39 @@ 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; + bpf_u_int32 flags; /* PCAP_IF_ interface flags */ +}; + +#define PCAP_IF_LOOPBACK 0x00000001 /* 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 *); __BEGIN_DECLS char *pcap_lookupdev(char *); int pcap_lookupnet(char *, bpf_u_int32 *, bpf_u_int32 *, char *); -pcap_t *pcap_open_live(char *, int, int, int, char *); +pcap_t *pcap_open_live(const char *, int, int, int, char *); pcap_t *pcap_open_offline(const char *, char *); +pcap_t *pcap_open_dead(int, int); void pcap_close(pcap_t *); int pcap_loop(pcap_t *, int, pcap_handler, u_char *); int pcap_dispatch(pcap_t *, int, pcap_handler, u_char *); @@ -114,6 +141,8 @@ const u_char* int pcap_stats(pcap_t *, struct pcap_stat *); int pcap_inject(pcap_t *, const void *, size_t); int pcap_setfilter(pcap_t *, struct bpf_program *); +int pcap_getnonblock(pcap_t *, char *); +int pcap_setnonblock(pcap_t *, int, char *); void pcap_perror(pcap_t *, char *); char *pcap_strerror(int); char *pcap_geterr(pcap_t *); @@ -122,7 +151,14 @@ int pcap_compile(pcap_t *, struct bpf_program *, char *, int, int pcap_compile_nopcap(int, int, struct bpf_program *, char *, int, bpf_u_int32); void pcap_freecode(struct bpf_program *); +void pcap_breakloop(pcap_t *); int pcap_datalink(pcap_t *); +int pcap_list_datalinks(pcap_t *, int **); +int pcap_set_datalink(pcap_t *, int); +int pcap_datalink_name_to_val(const char *); +const char *pcap_datalink_val_to_name(int); +const char *pcap_datalink_val_to_description(int); +const char *pcap_lib_version(void); int pcap_snapshot(pcap_t *); int pcap_is_swapped(pcap_t *); int pcap_major_version(pcap_t *); @@ -136,6 +172,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); char *bpf_image(struct bpf_insn *, int); diff --git a/lib/libpcap/shlib_version b/lib/libpcap/shlib_version index 3f0196ebf4a..d9961ea9fef 100644 --- a/lib/libpcap/shlib_version +++ b/lib/libpcap/shlib_version @@ -1,2 +1,2 @@ -major=3 -minor=1 +major=4 +minor=0 |