X-Git-Url: https://git.tcpdump.org/libpcap/blobdiff_plain/e648c9e593e7a7f0c3d031eb2e2c146de9efc67e..cc3ca65d6519faf3a0e4609b5150757c14af36e9:/pcap-nit.c diff --git a/pcap-nit.c b/pcap-nit.c index 1c0117f0..1b626e21 100644 --- a/pcap-nit.c +++ b/pcap-nit.c @@ -18,10 +18,6 @@ * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. */ -#ifndef lint -static const char rcsid[] = - "@(#) $Header: /tcpdump/master/libpcap/pcap-nit.c,v 1.45 2003-07-25 03:25:46 guy Exp $ (LBL)"; -#endif #ifdef HAVE_CONFIG_H #include "config.h" @@ -71,9 +67,17 @@ 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 NIT devices. + */ +struct pcap_nit { + struct pcap_stat stat; +}; + +static int +pcap_stats_nit(pcap_t *p, struct pcap_stat *ps) { + struct pcap_nit *pn = p->priv; /* * "ps_recv" counts packets handed to the filter, not packets @@ -91,15 +95,15 @@ pcap_stats(pcap_t *p, struct pcap_stat *ps) * kernel by libpcap or packets not yet read from libpcap by the * application. */ - *ps = p->md.stat; + *ps = pn->stat; return (0); } -int -pcap_read(pcap_t *p, int cnt, pcap_handler callback, u_char *user) +static int +pcap_read_nit(pcap_t *p, int cnt, pcap_handler callback, u_char *user) { + struct pcap_nit *pn = p->priv; register int cc, n; - register struct bpf_insn *fcode = p->fcode.bf_insns; register u_char *bp, *cp, *ep; register struct nit_hdr *nh; register int caplen; @@ -110,11 +114,11 @@ pcap_read(pcap_t *p, int cnt, pcap_handler callback, u_char *user) if (cc < 0) { if (errno == EWOULDBLOCK) return (0); - snprintf(p->errbuf, sizeof(p->errbuf), "pcap_read: %s", + pcap_snprintf(p->errbuf, sizeof(p->errbuf), "pcap_read: %s", pcap_strerror(errno)); return (-1); } - bp = p->buffer; + bp = (u_char *)p->buffer; } else bp = p->bp; @@ -126,6 +130,26 @@ pcap_read(pcap_t *p, int cnt, pcap_handler callback, u_char *user) n = 0; ep = bp + cc; while (bp < ep) { + /* + * 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->cc = ep - bp; + p->bp = bp; + return (n); + } + } + nh = (struct nit_hdr *)bp; cp = bp + sizeof(*nh); @@ -137,31 +161,31 @@ pcap_read(pcap_t *p, int cnt, pcap_handler callback, u_char *user) case NIT_NOMBUF: case NIT_NOCLUSTER: case NIT_NOSPACE: - p->md.stat.ps_drop = nh->nh_dropped; + pn->stat.ps_drop = nh->nh_dropped; continue; case NIT_SEQNO: continue; default: - snprintf(p->errbuf, sizeof(p->errbuf), + pcap_snprintf(p->errbuf, sizeof(p->errbuf), "bad nit state %d", nh->nh_state); return (-1); } - ++p->md.stat.ps_recv; + ++pn->stat.ps_recv; bp += ((sizeof(struct nit_hdr) + nh->nh_datalen + sizeof(int) - 1) & ~(sizeof(int) - 1)); caplen = nh->nh_wirelen; if (caplen > p->snapshot) caplen = p->snapshot; - if (bpf_filter(fcode, cp, nh->nh_wirelen, caplen)) { + if (bpf_filter(p->fcode.bf_insns, cp, nh->nh_wirelen, caplen)) { struct pcap_pkthdr h; h.ts = nh->nh_timestamp; h.len = nh->nh_wirelen; 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); @@ -173,80 +197,110 @@ 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_nit(pcap_t *p, const void *buf, size_t size) +{ + struct sockaddr sa; + int ret; + + memset(&sa, 0, sizeof(sa)); + strncpy(sa.sa_data, device, sizeof(sa.sa_data)); + ret = sendto(p->fd, buf, size, 0, &sa, sizeof(sa)); + if (ret == -1) { + pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "send: %s", + pcap_strerror(errno)); + return (-1); + } + return (ret); +} + +static int +nit_setflags(pcap_t *p) { struct nit_ioc nioc; memset(&nioc, 0, sizeof(nioc)); - nioc.nioc_bufspace = BUFSPACE; - nioc.nioc_chunksize = CHUNKSIZE; nioc.nioc_typetomatch = NT_ALLTYPES; nioc.nioc_snaplen = p->snapshot; nioc.nioc_bufalign = sizeof(int); nioc.nioc_bufoffset = 0; - if (to_ms != 0) { + if (p->opt.buffer_size != 0) + nioc.nioc_bufspace = p->opt.buffer_size; + else { + /* Default buffer size */ + nioc.nioc_bufspace = BUFSPACE; + } + + if (p->opt.immediate) { + /* + * XXX - will this cause packets to be delivered immediately? + * XXX - given that this is for SunOS prior to 4.0, do + * we care? + */ + nioc.nioc_chunksize = 0; + } else + nioc.nioc_chunksize = CHUNKSIZE; + if (p->opt.timeout != 0) { nioc.nioc_flags |= NF_TIMEOUT; - nioc.nioc_timeout.tv_sec = to_ms / 1000; - nioc.nioc_timeout.tv_usec = (to_ms * 1000) % 1000000; + nioc.nioc_timeout.tv_sec = p->opt.timeout / 1000; + nioc.nioc_timeout.tv_usec = (p->opt.timeout * 1000) % 1000000; } - if (promisc) + if (p->opt.promisc) nioc.nioc_flags |= NF_PROMISC; - if (ioctl(fd, SIOCSNIT, &nioc) < 0) { - snprintf(ebuf, PCAP_ERRBUF_SIZE, "SIOCSNIT: %s", + if (ioctl(p->fd, SIOCSNIT, &nioc) < 0) { + pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "SIOCSNIT: %s", pcap_strerror(errno)); return (-1); } return (0); } -static void -pcap_close_nit(pcap_t *p) -{ - if (p->buffer != NULL) - free(p->buffer); - if (p->fd >= 0) - close(p->fd); -} - -pcap_t * -pcap_open_live(const char *device, int snaplen, int promisc, int to_ms, - char *ebuf) +static int +pcap_activate_nit(pcap_t *p) { int fd; struct sockaddr_nit snit; - register pcap_t *p; - p = (pcap_t *)malloc(sizeof(*p)); - if (p == NULL) { - strlcpy(ebuf, pcap_strerror(errno), PCAP_ERRBUF_SIZE); - return (NULL); + if (p->opt.rfmon) { + /* + * No monitor mode on SunOS 3.x or earlier (no + * Wi-Fi *devices* for the hardware that supported + * them!). + */ + return (PCAP_ERROR_RFMON_NOTSUP); } - if (snaplen < 96) + if (p->snapshot < 96) /* * NIT requires a snapshot length of at least 96. */ - snaplen = 96; + p->snapshot = 96; memset(p, 0, sizeof(*p)); p->fd = fd = socket(AF_NIT, SOCK_RAW, NITPROTO_RAW); if (fd < 0) { - snprintf(ebuf, PCAP_ERRBUF_SIZE, + pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "socket: %s", pcap_strerror(errno)); goto bad; } snit.snit_family = AF_NIT; - (void)strncpy(snit.snit_ifname, device, NITIFSIZ); + (void)strncpy(snit.snit_ifname, p->opt.device, NITIFSIZ); if (bind(fd, (struct sockaddr *)&snit, sizeof(snit))) { - snprintf(ebuf, PCAP_ERRBUF_SIZE, + /* + * XXX - there's probably a particular bind error that + * means "there's no such device" and a particular bind + * error that means "that device doesn't support NIT"; + * they might be the same error, if they both end up + * meaning "NIT doesn't know about that device". + */ + pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "bind: %s: %s", snit.snit_ifname, pcap_strerror(errno)); goto bad; } - p->snapshot = snaplen; - nit_setflags(p->fd, promisc, to_ms, ebuf); + if (nit_setflags(p) < 0) + goto bad; /* * NIT supports only ethernets. @@ -254,39 +308,77 @@ pcap_open_live(const char *device, int snaplen, int promisc, int to_ms, p->linktype = DLT_EN10MB; p->bufsize = BUFSPACE; - p->buffer = (u_char *)malloc(p->bufsize); + p->buffer = malloc(p->bufsize); if (p->buffer == NULL) { - strlcpy(ebuf, pcap_strerror(errno), PCAP_ERRBUF_SIZE); + strlcpy(p->errbuf, pcap_strerror(errno), PCAP_ERRBUF_SIZE); goto bad; } - p->close_op = pcap_close_nit; + /* + * "p->fd" is a socket, so "select()" should work on it. + */ + p->selectable_fd = p->fd; - return (p); + /* + * 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_nit; + p->inject_op = pcap_inject_nit; + 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_nit; + + return (0); bad: - if (fd >= 0) - close(fd); - free(p); - return (NULL); + pcap_cleanup_live_common(p); + return (PCAP_ERROR); } -int -pcap_platform_finddevs(pcap_if_t **alldevsp, char *errbuf) +pcap_t * +pcap_create_interface(const char *device _U_, char *ebuf) { - return (0); + pcap_t *p; + + p = pcap_create_common(ebuf, sizeof (struct pcap_nit)); + if (p == NULL) + return (NULL); + + p->activate_op = pcap_activate_nit; + return (p); } -int -pcap_setfilter(pcap_t *p, struct bpf_program *fp) +/* + * XXX - there's probably a particular bind error that means "that device + * doesn't support NIT"; if so, we should try a bind and use that. + */ +static int +can_be_bound(const char *name _U_) { - - if (install_bpf_program(p, fp) < 0) - return (-1); - return (0); + return (1); } int -pcap_set_datalink_platform(pcap_t *p, int dlt) +pcap_platform_finddevs(pcap_if_t **alldevsp, char *errbuf) { - return (0); + return (pcap_findalldevs_interfaces(alldevsp, errbuf, can_be_bound)); }