X-Git-Url: https://git.tcpdump.org/libpcap/blobdiff_plain/b11ddf8a9b0d30bf759abf01afcf2894e79857b1..09b51d326c38ea8e10ce4da09c09d50e08c5aeb8:/pcap-snit.c diff --git a/pcap-snit.c b/pcap-snit.c index 6971d3b6..3f4e69d7 100644 --- a/pcap-snit.c +++ b/pcap-snit.c @@ -23,9 +23,8 @@ * This module now handles the STREAMS based NIT. */ -#ifndef lint -static const char rcsid[] = - "@(#) $Header: /tcpdump/master/libpcap/pcap-snit.c,v 1.45 1999-10-07 23:46:40 mcr Exp $ (LBL)"; +#ifdef HAVE_CONFIG_H +#include #endif #include @@ -54,18 +53,13 @@ static const char rcsid[] = #include #include -#include #include -#ifdef HAVE_MALLOC_H -#include -#endif #include #include #include #include "pcap-int.h" -#include "gnuc.h" #ifdef HAVE_OS_PROTO_H #include "os-proto.h" #endif @@ -84,19 +78,44 @@ static const char rcsid[] = /* Forwards */ static int nit_setflags(int, int, int, char *); -int -pcap_stats(pcap_t *p, struct pcap_stat *ps) +/* + * Private data for capturing on STREAMS NIT devices. + */ +struct pcap_snit { + struct pcap_stat stat; +}; + +static int +pcap_stats_snit(pcap_t *p, struct pcap_stat *ps) { + struct pcap_snit *psn = p->priv; - *ps = p->md.stat; + /* + * "ps_recv" counts packets handed to the filter, not packets + * that passed the filter. As filtering is done in userland, + * this does not include packets dropped because we ran out + * of buffer space. + * + * "ps_drop" counts packets dropped inside the "/dev/nit" + * device because of flow control requirements or resource + * exhaustion; it doesn't count packets dropped by the + * interface driver, or packets dropped upstream. As filtering + * is done in userland, it counts packets regardless of whether + * they would've passed the filter. + * + * These statistics don't include packets not yet read from the + * kernel by libpcap or packets not yet read from libpcap by the + * application. + */ + *ps = psn->stat; return (0); } -int -pcap_read(pcap_t *p, int cnt, pcap_handler callback, u_char *user) +static int +pcap_read_snit(pcap_t *p, int cnt, pcap_handler callback, u_char *user) { + struct pcap_snit *psn = p->priv; register int cc, n; - register struct bpf_insn *fcode = p->fcode.bf_insns; register u_char *bp, *cp, *ep; register struct nit_bufhdr *hdrp; register struct nit_iftime *ntp; @@ -110,21 +129,44 @@ pcap_read(pcap_t *p, int cnt, pcap_handler callback, u_char *user) if (cc < 0) { if (errno == EWOULDBLOCK) return (0); - sprintf(p->errbuf, "pcap_read: %s", - pcap_strerror(errno)); + pcap_fmt_errmsg_for_errno(p->errbuf, sizeof(p->errbuf), + errno, "pcap_read"); return (-1); } - bp = p->buffer; + bp = (u_char *)p->buffer; } else bp = p->bp; /* * loop through each snapshot in the chunk + * + * This assumes that a single buffer of packets will have + * <= INT_MAX packets, so the packet count doesn't overflow. */ n = 0; ep = bp + cc; while (bp < ep) { - ++p->md.stat.ps_recv; + /* + * 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); + } + } + + ++psn->stat.ps_recv; cp = bp; /* get past NIT buffer */ @@ -136,7 +178,7 @@ pcap_read(pcap_t *p, int cnt, pcap_handler callback, u_char *user) cp += sizeof(*ntp); ndp = (struct nit_ifdrops *)cp; - p->md.stat.ps_drop = ndp->nh_drops; + psn->stat.ps_drop = ndp->nh_drops; cp += sizeof *ndp; /* get past packet len */ @@ -150,13 +192,13 @@ pcap_read(pcap_t *p, int cnt, pcap_handler callback, u_char *user) if (caplen > p->snapshot) caplen = p->snapshot; - if (bpf_filter(fcode, cp, nlp->nh_pktlen, caplen)) { + if (pcap_filter(p->fcode.bf_insns, cp, nlp->nh_pktlen, caplen)) { struct pcap_pkthdr h; h.ts = ntp->nh_timestamp; h.len = nlp->nh_pktlen; h.caplen = caplen; (*callback)(user, &h, cp); - if (++n >= cnt && cnt >= 0) { + if (++n >= cnt && !PACKET_COUNT_IS_UNLIMITED(cnt)) { p->cc = ep - bp; p->bp = bp; return (n); @@ -168,73 +210,154 @@ pcap_read(pcap_t *p, int cnt, pcap_handler callback, u_char *user) } static int -nit_setflags(int fd, int promisc, int to_ms, char *ebuf) +pcap_inject_snit(pcap_t *p, const void *buf, int size) +{ + struct strbuf ctl, data; + + /* + * XXX - can we just do + * + ret = write(pd->f, buf, size); + */ + ctl.len = sizeof(*sa); /* XXX - what was this? */ + ctl.buf = (char *)sa; + data.buf = buf; + data.len = size; + ret = putmsg(p->fd, &ctl, &data); + if (ret == -1) { + pcap_fmt_errmsg_for_errno(p->errbuf, PCAP_ERRBUF_SIZE, + errno, "send"); + return (-1); + } + return (ret); +} + +static int +nit_setflags(pcap_t *p) { bpf_u_int32 flags; struct strioctl si; + u_int zero = 0; struct timeval timeout; + if (p->opt.immediate) { + /* + * Set the chunk size to zero, so that chunks get sent + * up immediately. + */ + si.ic_cmd = NIOCSCHUNK; + si.ic_len = sizeof(zero); + si.ic_dp = (char *)&zero; + if (ioctl(p->fd, I_STR, (char *)&si) < 0) { + pcap_fmt_errmsg_for_errno(p->errbuf, PCAP_ERRBUF_SIZE, + errno, "NIOCSCHUNK"); + return (-1); + } + } si.ic_timout = INFTIM; - if (to_ms != 0) { - timeout.tv_sec = to_ms / 1000; - timeout.tv_usec = (to_ms * 1000) % 1000000; + if (p->opt.timeout != 0) { + timeout.tv_sec = p->opt.timeout / 1000; + timeout.tv_usec = (p->opt.timeout * 1000) % 1000000; si.ic_cmd = NIOCSTIME; si.ic_len = sizeof(timeout); si.ic_dp = (char *)&timeout; - if (ioctl(fd, I_STR, (char *)&si) < 0) { - sprintf(ebuf, "NIOCSTIME: %s", pcap_strerror(errno)); + if (ioctl(p->fd, I_STR, (char *)&si) < 0) { + pcap_fmt_errmsg_for_errno(p->errbuf, PCAP_ERRBUF_SIZE, + errno, "NIOCSTIME"); return (-1); } } flags = NI_TIMESTAMP | NI_LEN | NI_DROPS; - if (promisc) + if (p->opt.promisc) flags |= NI_PROMISC; si.ic_cmd = NIOCSFLAGS; si.ic_len = sizeof(flags); si.ic_dp = (char *)&flags; - if (ioctl(fd, I_STR, (char *)&si) < 0) { - sprintf(ebuf, "NIOCSFLAGS: %s", pcap_strerror(errno)); + if (ioctl(p->fd, I_STR, (char *)&si) < 0) { + pcap_fmt_errmsg_for_errno(p->errbuf, PCAP_ERRBUF_SIZE, + errno, "NIOCSFLAGS"); return (-1); } return (0); } -pcap_t * -pcap_open_live(char *device, int snaplen, int promisc, int to_ms, char *ebuf) +static int +pcap_activate_snit(pcap_t *p) { struct strioctl si; /* struct for ioctl() */ struct ifreq ifr; /* interface request struct */ int chunksize = CHUNKSIZE; int fd; - static char dev[] = "/dev/nit"; - register pcap_t *p; + static const char dev[] = "/dev/nit"; + int err; - p = (pcap_t *)malloc(sizeof(*p)); - if (p == NULL) { - strcpy(ebuf, pcap_strerror(errno)); - return (NULL); + if (p->opt.rfmon) { + /* + * No monitor mode on SunOS 4.x (no Wi-Fi devices on + * hardware supported by SunOS 4.x). + */ + return (PCAP_ERROR_RFMON_NOTSUP); } - if (snaplen < 96) + /* + * Turn a negative snapshot value (invalid), a snapshot value of + * 0 (unspecified), or a value bigger than the normal maximum + * value, into the maximum allowed value. + * + * If some application really *needs* a bigger snapshot + * length, we should just increase MAXIMUM_SNAPLEN. + */ + if (p->snapshot <= 0 || p->snapshot > MAXIMUM_SNAPLEN) + p->snapshot = MAXIMUM_SNAPLEN; + + if (p->snapshot < 96) /* * NIT requires a snapshot length of at least 96. */ - snaplen = 96; + p->snapshot = 96; - bzero(p, sizeof(*p)); - p->fd = fd = open(dev, O_RDONLY); + /* + * Initially try a read/write open (to allow the inject + * method to work). If that fails due to permission + * issues, fall back to read-only. This allows a + * non-root user to be granted specific access to pcap + * capabilities via file permissions. + * + * XXX - we should have an API that has a flag that + * controls whether to open read-only or read-write, + * so that denial of permission to send (or inability + * to send, if sending packets isn't supported on + * the device in question) can be indicated at open + * time. + */ + p->fd = fd = open(dev, O_RDWR); + if (fd < 0 && errno == EACCES) + p->fd = fd = open(dev, O_RDONLY); if (fd < 0) { - sprintf(ebuf, "%s: %s", dev, pcap_strerror(errno)); + if (errno == EACCES) { + err = PCAP_ERROR_PERM_DENIED; + snprintf(p->errbuf, PCAP_ERRBUF_SIZE, + "Attempt to open %s failed with EACCES - root privileges may be required", + dev); + } else { + err = PCAP_ERROR; + pcap_fmt_errmsg_for_errno(p->errbuf, PCAP_ERRBUF_SIZE, + errno, "%s", dev); + } goto bad; } /* arrange to get discrete messages from the STREAM and use NIT_BUF */ if (ioctl(fd, I_SRDOPT, (char *)RMSGD) < 0) { - sprintf(ebuf, "I_SRDOPT: %s", pcap_strerror(errno)); + pcap_fmt_errmsg_for_errno(p->errbuf, PCAP_ERRBUF_SIZE, + errno, "I_SRDOPT"); + err = PCAP_ERROR; goto bad; } if (ioctl(fd, I_PUSH, "nbuf") < 0) { - sprintf(ebuf, "push nbuf: %s", pcap_strerror(errno)); + pcap_fmt_errmsg_for_errno(p->errbuf, PCAP_ERRBUF_SIZE, + errno, "push nbuf"); + err = PCAP_ERROR; goto bad; } /* set the chunksize */ @@ -243,33 +366,44 @@ pcap_open_live(char *device, int snaplen, int promisc, int to_ms, char *ebuf) si.ic_len = sizeof(chunksize); si.ic_dp = (char *)&chunksize; if (ioctl(fd, I_STR, (char *)&si) < 0) { - sprintf(ebuf, "NIOCSCHUNK: %s", pcap_strerror(errno)); + pcap_fmt_errmsg_for_errno(p->errbuf, PCAP_ERRBUF_SIZE, + errno, "NIOCSCHUNK"); + err = PCAP_ERROR; goto bad; } /* request the interface */ - strncpy(ifr.ifr_name, device, sizeof(ifr.ifr_name)); - ifr.ifr_name[sizeof(ifr.ifr_name) - 1] = ' '; + strncpy(ifr.ifr_name, p->opt.device, sizeof(ifr.ifr_name)); + ifr.ifr_name[sizeof(ifr.ifr_name) - 1] = '\0'; si.ic_cmd = NIOCBIND; si.ic_len = sizeof(ifr); si.ic_dp = (char *)𝔦 if (ioctl(fd, I_STR, (char *)&si) < 0) { - sprintf(ebuf, "NIOCBIND: %s: %s", - ifr.ifr_name, pcap_strerror(errno)); + /* + * XXX - is there an error that means "no such device"? + * Is there one that means "that device doesn't support + * STREAMS NIT"? + */ + pcap_fmt_errmsg_for_errno(p->errbuf, PCAP_ERRBUF_SIZE, + errno, "NIOCBIND: %s", ifr.ifr_name); + err = PCAP_ERROR; goto bad; } /* set the snapshot length */ si.ic_cmd = NIOCSSNAP; - si.ic_len = sizeof(snaplen); - si.ic_dp = (char *)&snaplen; + si.ic_len = sizeof(p->snapshot); + si.ic_dp = (char *)&p->snapshot; if (ioctl(fd, I_STR, (char *)&si) < 0) { - sprintf(ebuf, "NIOCSSNAP: %s", pcap_strerror(errno)); + pcap_fmt_errmsg_for_errno(p->errbuf, PCAP_ERRBUF_SIZE, + errno, "NIOCSSNAP"); + err = PCAP_ERROR; goto bad; } - p->snapshot = snaplen; - if (nit_setflags(p->fd, promisc, to_ms, ebuf) < 0) + if (nit_setflags(p) < 0) { + err = PCAP_ERROR; goto bad; + } (void)ioctl(fd, I_FLUSH, (char *)FLUSHR); /* @@ -278,23 +412,101 @@ pcap_open_live(char *device, int snaplen, int promisc, int to_ms, char *ebuf) p->linktype = DLT_EN10MB; p->bufsize = BUFSPACE; - p->buffer = (u_char *)malloc(p->bufsize); + p->buffer = malloc(p->bufsize); if (p->buffer == NULL) { - strcpy(ebuf, pcap_strerror(errno)); + pcap_fmt_errmsg_for_errno(p->errbuf, PCAP_ERRBUF_SIZE, + errno, "malloc"); + err = PCAP_ERROR; goto bad; } - return (p); + + /* + * "p->fd" is an FD for a STREAMS device, so "select()" and + * "poll()" should work on it. + */ + p->selectable_fd = p->fd; + + /* + * This is (presumably) a real Ethernet capture; give it a + * link-layer-type list with DLT_EN10MB and DLT_DOCSIS, so + * that an application can let you choose it, in case you're + * capturing DOCSIS traffic that a Cisco Cable Modem + * Termination System is putting out onto an Ethernet (it + * doesn't put an Ethernet header onto the wire, it puts raw + * DOCSIS frames out on the wire inside the low-level + * Ethernet framing). + */ + p->dlt_list = (u_int *) malloc(sizeof(u_int) * 2); + /* + * If that fails, just leave the list empty. + */ + if (p->dlt_list != NULL) { + p->dlt_list[0] = DLT_EN10MB; + p->dlt_list[1] = DLT_DOCSIS; + p->dlt_count = 2; + } + + p->read_op = pcap_read_snit; + p->inject_op = pcap_inject_snit; + p->setfilter_op = install_bpf_program; /* no kernel filtering */ + p->setdirection_op = NULL; /* Not implemented. */ + p->set_datalink_op = NULL; /* can't change data link type */ + p->getnonblock_op = pcap_getnonblock_fd; + p->setnonblock_op = pcap_setnonblock_fd; + p->stats_op = pcap_stats_snit; + + return (0); bad: - if (fd >= 0) - close(fd); - free(p); - return (NULL); + pcap_cleanup_live_common(p); + return (err); } -int -pcap_setfilter(pcap_t *p, struct bpf_program *fp) +pcap_t * +pcap_create_interface(const char *device _U_, char *ebuf) { + pcap_t *p; - p->fcode = *fp; + p = PCAP_CREATE_COMMON(ebuf, struct pcap_snit); + if (p == NULL) + return (NULL); + + p->activate_op = pcap_activate_snit; + return (p); +} + +/* + * XXX - there's probably a NIOCBIND error that means "that device + * doesn't support NIT"; if so, we should try an NIOCBIND and use that. + */ +static int +can_be_bound(const char *name _U_) +{ + return (1); +} + +static int +get_if_flags(const char *name _U_, bpf_u_int32 *flags _U_, char *errbuf _U_) +{ + /* + * Nothing we can do. + * XXX - is there a way to find out whether an adapter has + * something plugged into it? + */ return (0); } + +int +pcap_platform_finddevs(pcap_if_list_t *devlistp, char *errbuf) +{ + return (pcap_findalldevs_interfaces(devlistp, errbuf, can_be_bound, + get_if_flags)); +} + +/* + * Libpcap version string. + */ +const char * +pcap_lib_version(void) +{ + return (PCAP_VERSION_STRING); +}