/* * see <linux-kernel-source>/Documentation/usb/usbmon.txt and * <linux-kernel-source>/drivers/usb/mon/mon_bin.c binary ABI */ static int usb_read_linux_bin(pcap_t *handle, int max_packets, pcap_handler callback, u_char *user) { struct pcap_usb_linux *handlep = handle->priv; struct mon_bin_get info; int ret; struct pcap_pkthdr pkth; u_int clen = handle->snapshot - sizeof(pcap_usb_header); /* the usb header is going to be part of 'packet' data*/ info.hdr = (pcap_usb_header*) handle->buffer; info.data = (u_char *)handle->buffer + sizeof(pcap_usb_header); info.data_len = clen; /* ignore interrupt system call errors */ do { ret = ioctl(handle->fd, MON_IOCX_GET, &info); if (handle->break_loop) { handle->break_loop = 0; return -2; } } while ((ret == -1) && (errno == EINTR)); if (ret < 0) { if (errno == EAGAIN) return 0; /* no data there */ pcap_fmt_errmsg_for_errno(handle->errbuf, PCAP_ERRBUF_SIZE, errno, "Can't read from fd %d", handle->fd); return -1; } /* we can get less that than really captured from kernel, depending on * snaplen, so adjust header accordingly */ if (info.hdr->data_len < clen) clen = info.hdr->data_len; info.hdr->data_len = clen; pkth.caplen = clen + sizeof(pcap_usb_header); pkth.len = info.hdr->data_len + sizeof(pcap_usb_header); pkth.ts.tv_sec = info.hdr->ts_sec; pkth.ts.tv_usec = info.hdr->ts_usec; if (handle->fcode.bf_insns == NULL || bpf_filter(handle->fcode.bf_insns, handle->buffer, pkth.len, pkth.caplen)) { handlep->packets_read++; callback(user, &pkth, handle->buffer); return 1; } return 0; /* didn't pass filter */ }
int acn_parse_hosts_file(char *errbuf) { /* returns: -1 = error, 0 = OK */ FILE *fp; char buf[MAX_LINE_SIZE]; char *ptr, *ptr2; int pos; int chassis, geoslot; unit_t *u; empty_unit_table(); if ((fp = fopen("/etc/hosts", "r")) == NULL) { /* try to open the hosts file and if it fails */ pcap_snprintf(errbuf, PCAP_ERRBUF_SIZE, "Cannot open '/etc/hosts' for reading."); /* return the nohostsfile error response */ return -1; } while (fgets(buf, MAX_LINE_SIZE-1, fp)) { /* while looping over the file */ pos = strcspn(buf, "#\n\r"); /* find the first comment character or EOL */ *(buf + pos) = '\0'; /* and clobber it and anything that follows it */ pos = strspn(buf, " \t"); /* then find the first non-white space */ if (pos == strlen(buf)) /* if there is nothing but white space on the line */ continue; /* ignore that empty line */ ptr = buf + pos; /* and skip over any of that leading whitespace */ if ((ptr2 = strstr(ptr, "_I_")) == NULL) /* skip any lines that don't have names that look like they belong to IOPs */ continue; if (*(ptr2 + 4) != '_') /* and skip other lines that have names that don't look like ACN components */ continue; *(ptr + strcspn(ptr, " \t")) = '\0'; /* null terminate the IP address so its a standalone string */ chassis = *(ptr2 + 3) - '0'; /* extract the chassis number */ geoslot = *(ptr2 + 5) - '0'; /* and geo-slot number */ if (chassis < 1 || chassis > MAX_CHASSIS || geoslot < 1 || geoslot > MAX_GEOSLOT) { /* if the chassis and/or slot numbers appear to be bad... */ pcap_snprintf(errbuf, PCAP_ERRBUF_SIZE, "Invalid ACN name in '/etc/hosts'."); /* warn the user */ continue; /* and ignore the entry */ } if ((ptr2 = (char *)malloc(strlen(ptr) + 1)) == NULL) { pcap_fmt_errmsg_for_errno(errbuf, PCAP_ERRBUF_SIZE, errno, "malloc"); continue; } strcpy(ptr2, ptr); /* copy the IP address into our malloc'ed memory */ u = &units[chassis][geoslot]; u->ip = ptr2; /* and remember the whole shebang */ u->chassis = chassis; u->geoslot = geoslot; } fclose(fp); if (*errbuf) return -1; else return 0; }
static int 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_fmt_errmsg_for_errno(p->errbuf, PCAP_ERRBUF_SIZE, errno, "send"); return (-1); } return (ret); }
static int usb_stats_linux_bin(pcap_t *handle, struct pcap_stat *stats) { struct pcap_usb_linux *handlep = handle->priv; int ret; struct mon_bin_stats st; ret = ioctl(handle->fd, MON_IOCG_STATS, &st); if (ret < 0) { pcap_fmt_errmsg_for_errno(handle->errbuf, PCAP_ERRBUF_SIZE, errno, "Can't read stats from fd %d", handle->fd); return -1; } stats->ps_recv = handlep->packets_read + st.queued; stats->ps_drop = st.dropped; stats->ps_ifdrop = 0; return 0; }
static int nit_setflags(pcap_t *p) { struct nit_ioc nioc; memset(&nioc, 0, sizeof(nioc)); nioc.nioc_typetomatch = NT_ALLTYPES; nioc.nioc_snaplen = p->snapshot; nioc.nioc_bufalign = sizeof(int); nioc.nioc_bufoffset = 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 = p->opt.timeout / 1000; nioc.nioc_timeout.tv_usec = (p->opt.timeout * 1000) % 1000000; } if (p->opt.promisc) nioc.nioc_flags |= NF_PROMISC; if (ioctl(p->fd, SIOCSNIT, &nioc) < 0) { pcap_fmt_errmsg_for_errno(p->errbuf, PCAP_ERRBUF_SIZE, errno, "SIOCSNIT"); return (-1); } return (0); }
static int read_bytes(FILE *fp, void *buf, size_t bytes_to_read, int fail_on_eof, char *errbuf) { size_t amt_read; amt_read = fread(buf, 1, bytes_to_read, fp); if (amt_read != bytes_to_read) { if (ferror(fp)) { pcap_fmt_errmsg_for_errno(errbuf, PCAP_ERRBUF_SIZE, errno, "error reading dump file"); } else { if (amt_read == 0 && !fail_on_eof) return (0); /* EOF */ pcap_snprintf(errbuf, PCAP_ERRBUF_SIZE, "truncated dump file; tried to read %" PRIsize " bytes, only got %" PRIsize, bytes_to_read, amt_read); } return (-1); } return (1); }
static int pcap_netmap_dispatch(pcap_t *p, int cnt, pcap_handler cb, u_char *user) { int ret; struct pcap_netmap *pn = p->priv; struct nm_desc *d = pn->d; struct pollfd pfd = { .fd = p->fd, .events = POLLIN, .revents = 0 }; pn->cb = cb; pn->cb_arg = user; for (;;) { if (p->break_loop) { p->break_loop = 0; return PCAP_ERROR_BREAK; } /* nm_dispatch won't run forever */ ret = nm_dispatch((void *)d, cnt, (void *)pcap_netmap_filter, (void *)p); if (ret != 0) break; errno = 0; ret = poll(&pfd, 1, p->opt.timeout); } return ret; } /* XXX need to check the NIOCTXSYNC/poll */ static int pcap_netmap_inject(pcap_t *p, const void *buf, size_t size) { struct pcap_netmap *pn = p->priv; struct nm_desc *d = pn->d; return nm_inject(d, buf, size); } static int pcap_netmap_ioctl(pcap_t *p, u_long what, uint32_t *if_flags) { struct pcap_netmap *pn = p->priv; struct nm_desc *d = pn->d; struct ifreq ifr; int error, fd = d->fd; #ifdef linux fd = socket(AF_INET, SOCK_DGRAM, 0); if (fd < 0) { fprintf(stderr, "Error: cannot get device control socket.\n"); return -1; } #endif /* linux */ bzero(&ifr, sizeof(ifr)); strncpy(ifr.ifr_name, d->req.nr_name, sizeof(ifr.ifr_name)); switch (what) { case SIOCSIFFLAGS: /* * The flags we pass in are 32-bit and unsigned. * * On most if not all UN*Xes, ifr_flags is 16-bit and * signed, and the result of assigning a longer * unsigned value to a shorter signed value is * implementation-defined (even if, in practice, it'll * do what's intended on all platforms we support * result of assigning a 32-bit unsigned value). * So we mask out the upper 16 bits. */ ifr.ifr_flags = *if_flags & 0xffff; #ifdef __FreeBSD__ /* * In FreeBSD, we need to set the high-order flags, * as we're using IFF_PPROMISC, which is in those bits. * * XXX - DragonFly BSD? */ ifr.ifr_flagshigh = *if_flags >> 16; #endif /* __FreeBSD__ */ break; } error = ioctl(fd, what, &ifr); if (!error) { switch (what) { case SIOCGIFFLAGS: /* * The flags we return are 32-bit. * * On most if not all UN*Xes, ifr_flags is * 16-bit and signed, and will get sign- * extended, so that the upper 16 bits of * those flags will be forced on. So we * mask out the upper 16 bits of the * sign-extended value. */ *if_flags = ifr.ifr_flags & 0xffff; #ifdef __FreeBSD__ /* * In FreeBSD, we need to return the * high-order flags, as we're using * IFF_PPROMISC, which is in those bits. * * XXX - DragonFly BSD? */ *if_flags |= (ifr.ifr_flagshigh << 16); #endif /* __FreeBSD__ */ } } #ifdef linux close(fd); #endif /* linux */ return error ? -1 : 0; } static void pcap_netmap_close(pcap_t *p) { struct pcap_netmap *pn = p->priv; struct nm_desc *d = pn->d; uint32_t if_flags = 0; if (pn->must_clear_promisc) { pcap_netmap_ioctl(p, SIOCGIFFLAGS, &if_flags); /* fetch flags */ if (if_flags & IFF_PPROMISC) { if_flags &= ~IFF_PPROMISC; pcap_netmap_ioctl(p, SIOCSIFFLAGS, &if_flags); } } nm_close(d); pcap_cleanup_live_common(p); } static int pcap_netmap_activate(pcap_t *p) { struct pcap_netmap *pn = p->priv; struct nm_desc *d; uint32_t if_flags = 0; d = nm_open(p->opt.device, NULL, 0, NULL); if (d == NULL) { pcap_fmt_errmsg_for_errno(p->errbuf, PCAP_ERRBUF_SIZE, errno, "netmap open: cannot access %s", p->opt.device); pcap_cleanup_live_common(p); return (PCAP_ERROR); } if (0) fprintf(stderr, "%s device %s priv %p fd %d ports %d..%d\n", __FUNCTION__, p->opt.device, d, d->fd, d->first_rx_ring, d->last_rx_ring); pn->d = d; p->fd = d->fd; /* * 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->opt.promisc && !(d->req.nr_ringid & NETMAP_SW_RING)) { pcap_netmap_ioctl(p, SIOCGIFFLAGS, &if_flags); /* fetch flags */ if (!(if_flags & IFF_PPROMISC)) { pn->must_clear_promisc = 1; if_flags |= IFF_PPROMISC; pcap_netmap_ioctl(p, SIOCSIFFLAGS, &if_flags); } } p->linktype = DLT_EN10MB; p->selectable_fd = p->fd; p->read_op = pcap_netmap_dispatch; p->inject_op = pcap_netmap_inject, p->setfilter_op = install_bpf_program; p->setdirection_op = NULL; p->set_datalink_op = NULL; p->getnonblock_op = pcap_getnonblock_fd; p->setnonblock_op = pcap_setnonblock_fd; p->stats_op = pcap_netmap_stats; p->cleanup_op = pcap_netmap_close; return (0); } pcap_t * pcap_netmap_create(const char *device, char *ebuf, int *is_ours) { pcap_t *p; *is_ours = (!strncmp(device, "netmap:", 7) || !strncmp(device, "vale", 4)); if (! *is_ours) return NULL; p = pcap_create_common(ebuf, sizeof (struct pcap_netmap)); if (p == NULL) return (NULL); p->activate_op = pcap_netmap_activate; return (p); }
static int usb_activate(pcap_t* handle) { struct pcap_usb_linux *handlep = handle->priv; char full_path[USB_LINE_LEN]; /* * 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 (handle->snapshot <= 0 || handle->snapshot > MAXIMUM_SNAPLEN) handle->snapshot = MAXIMUM_SNAPLEN; /* Initialize some components of the pcap structure. */ handle->bufsize = handle->snapshot; handle->offset = 0; handle->linktype = DLT_USB_LINUX; handle->inject_op = usb_inject_linux; handle->setfilter_op = install_bpf_program; /* no kernel filtering */ handle->setdirection_op = usb_setdirection_linux; handle->set_datalink_op = NULL; /* can't change data link type */ handle->getnonblock_op = pcap_getnonblock_fd; handle->setnonblock_op = pcap_setnonblock_fd; /*get usb bus index from device name */ if (sscanf(handle->opt.device, USB_IFACE"%d", &handlep->bus_index) != 1) { pcap_snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, "Can't get USB bus index from %s", handle->opt.device); return PCAP_ERROR; } if (have_binary_usbmon()) { /* * We have binary-mode support. * Try to open the binary interface. */ pcap_snprintf(full_path, USB_LINE_LEN, LINUX_USB_MON_DEV"%d", handlep->bus_index); handle->fd = open(full_path, O_RDONLY, 0); if (handle->fd < 0) { /* * The attempt failed; why? */ switch (errno) { case ENOENT: /* * The device doesn't exist. * That could either mean that there's * no support for monitoring USB buses * (which probably means "the usbmon * module isn't loaded") or that there * is but that *particular* device * doesn't exist (no "scan all buses" * device if the bus index is 0, no * such bus if the bus index isn't 0). */ return PCAP_ERROR_NO_SUCH_DEVICE; case EACCES: /* * We didn't have permission to open it. */ return PCAP_ERROR_PERM_DENIED; default: /* * Something went wrong. */ pcap_fmt_errmsg_for_errno(handle->errbuf, PCAP_ERRBUF_SIZE, errno, "Can't open USB bus file %s", full_path); return PCAP_ERROR; } } if (handle->opt.rfmon) { /* * Monitor mode doesn't apply to USB devices. */ close(handle->fd); return PCAP_ERROR_RFMON_NOTSUP; } /* try to use fast mmap access */ if (usb_mmap(handle)) { handle->linktype = DLT_USB_LINUX_MMAPPED; handle->stats_op = usb_stats_linux_bin; handle->read_op = usb_read_linux_mmap; handle->cleanup_op = usb_cleanup_linux_mmap; #ifdef HAVE_LINUX_USBDEVICE_FS_H probe_devices(handlep->bus_index); #endif /* * "handle->fd" is a real file, so * "select()" and "poll()" work on it. */ handle->selectable_fd = handle->fd; return 0; } /* can't mmap, use plain binary interface access */ handle->stats_op = usb_stats_linux_bin; handle->read_op = usb_read_linux_bin; #ifdef HAVE_LINUX_USBDEVICE_FS_H probe_devices(handlep->bus_index); #endif } else { /* * We don't have binary mode support. * Try opening the text-mode device. */ pcap_snprintf(full_path, USB_LINE_LEN, USB_TEXT_DIR"/%dt", handlep->bus_index); handle->fd = open(full_path, O_RDONLY, 0); if (handle->fd < 0) { if (errno == ENOENT) { /* * Not found at the new location; try * the old location. */ pcap_snprintf(full_path, USB_LINE_LEN, USB_TEXT_DIR_OLD"/%dt", handlep->bus_index); handle->fd = open(full_path, O_RDONLY, 0); } if (handle->fd < 0) { /* * Is the problem that we didn't have * sufficient permission to open it? */ if (errno == EACCES) { /* * Yes - return that error. */ return PCAP_ERROR_PERM_DENIED; } /* * No - was the problem something other * than "it doesn't exist"? */ if (errno != ENOENT) { /* * Yes - return *that* error. */ pcap_fmt_errmsg_for_errno(handle->errbuf, PCAP_ERRBUF_SIZE, errno, "Can't open USB bus file %s", full_path); return PCAP_ERROR; } /* * No. Report that as "no such device". * (That could mean "no such USB bus" * or "monitoring not supported".) */ return PCAP_ERROR_NO_SUCH_DEVICE; } } if (handle->opt.rfmon) { /* * Monitor mode doesn't apply to USB devices. */ close(handle->fd); return PCAP_ERROR_RFMON_NOTSUP; } handle->stats_op = usb_stats_linux; handle->read_op = usb_read_linux; } /* * "handle->fd" is a real file, so "select()" and "poll()" * work on it. */ handle->selectable_fd = handle->fd; /* for plain binary access and text access we need to allocate the read * buffer */ handle->buffer = malloc(handle->bufsize); if (!handle->buffer) { pcap_fmt_errmsg_for_errno(handle->errbuf, PCAP_ERRBUF_SIZE, errno, "malloc"); close(handle->fd); return PCAP_ERROR; } return 0; }
static int pcap_activate_nit(pcap_t *p) { int fd; struct sockaddr_nit snit; 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); } /* * 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. */ p->snapshot = 96; memset(p, 0, sizeof(*p)); p->fd = fd = socket(AF_NIT, SOCK_RAW, NITPROTO_RAW); if (fd < 0) { pcap_fmt_errmsg_for_errno(p->errbuf, PCAP_ERRBUF_SIZE, errno, "socket"); goto bad; } snit.snit_family = AF_NIT; (void)strncpy(snit.snit_ifname, p->opt.device, NITIFSIZ); if (bind(fd, (struct sockaddr *)&snit, sizeof(snit))) { /* * 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_fmt_errmsg_for_errno(p->errbuf, PCAP_ERRBUF_SIZE, errno, "bind: %s", snit.snit_ifname); goto bad; } if (nit_setflags(p) < 0) goto bad; /* * NIT supports only ethernets. */ p->linktype = DLT_EN10MB; p->bufsize = BUFSPACE; p->buffer = malloc(p->bufsize); if (p->buffer == NULL) { pcap_fmt_errmsg_for_errno(p->errbuf, PCAP_ERRBUF_SIZE, errno, "malloc"); goto bad; } /* * "p->fd" is a socket, so "select()" 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_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: pcap_cleanup_live_common(p); return (PCAP_ERROR); }
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 u_char *bp, *cp, *ep; register struct nit_hdr *nh; register int caplen; cc = p->cc; if (cc == 0) { cc = read(p->fd, (char *)p->buffer, p->bufsize); if (cc < 0) { if (errno == EWOULDBLOCK) return (0); pcap_fmt_errmsg_for_errno(p->errbuf, sizeof(p->errbuf), errno, "pcap_read"); return (-1); } bp = (u_char *)p->buffer; } else bp = p->bp; /* * Loop through each packet. The increment expression * rounds up to the next int boundary past the end of * the previous packet. */ 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); switch (nh->nh_state) { case NIT_CATCH: break; case NIT_NOMBUF: case NIT_NOCLUSTER: case NIT_NOSPACE: pn->stat.ps_drop = nh->nh_dropped; continue; case NIT_SEQNO: continue; default: pcap_snprintf(p->errbuf, sizeof(p->errbuf), "bad nit state %d", nh->nh_state); return (-1); } ++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(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 && !PACKET_COUNT_IS_UNLIMITED(cnt)) { p->cc = ep - bp; p->bp = bp; return (n); } } } p->cc = 0; return (n); }
/* * see <linux-kernel-source>/Documentation/usb/usbmon.txt and * <linux-kernel-source>/drivers/usb/mon/mon_text.c for urb string * format description */ static int usb_read_linux(pcap_t *handle, int max_packets, pcap_handler callback, u_char *user) { /* see: * /usr/src/linux/Documentation/usb/usbmon.txt * for message format */ struct pcap_usb_linux *handlep = handle->priv; unsigned timestamp; int tag, cnt, ep_num, dev_addr, dummy, ret, urb_len, data_len; char etype, pipeid1, pipeid2, status[16], urb_tag, line[USB_LINE_LEN]; char *string = line; u_char * rawdata = handle->buffer; struct pcap_pkthdr pkth; pcap_usb_header* uhdr = (pcap_usb_header*)handle->buffer; u_char urb_transfer=0; int incoming=0; /* ignore interrupt system call errors */ do { ret = read(handle->fd, line, USB_LINE_LEN - 1); if (handle->break_loop) { handle->break_loop = 0; return -2; } } while ((ret == -1) && (errno == EINTR)); if (ret < 0) { if (errno == EAGAIN) return 0; /* no data there */ pcap_fmt_errmsg_for_errno(handle->errbuf, PCAP_ERRBUF_SIZE, errno, "Can't read from fd %d", handle->fd); return -1; } /* read urb header; %n argument may increment return value, but it's * not mandatory, so does not count on it*/ string[ret] = 0; ret = sscanf(string, "%x %d %c %c%c:%d:%d %s%n", &tag, ×tamp, &etype, &pipeid1, &pipeid2, &dev_addr, &ep_num, status, &cnt); if (ret < 8) { pcap_snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, "Can't parse USB bus message '%s', too few tokens (expected 8 got %d)", string, ret); return -1; } uhdr->id = tag; uhdr->device_address = dev_addr; uhdr->bus_id = handlep->bus_index; uhdr->status = 0; string += cnt; /* don't use usbmon provided timestamp, since it have low precision*/ if (gettimeofday(&pkth.ts, NULL) < 0) { pcap_fmt_errmsg_for_errno(handle->errbuf, PCAP_ERRBUF_SIZE, errno, "Can't get timestamp for message '%s'", string); return -1; } uhdr->ts_sec = pkth.ts.tv_sec; uhdr->ts_usec = pkth.ts.tv_usec; /* parse endpoint information */ if (pipeid1 == 'C') urb_transfer = URB_CONTROL; else if (pipeid1 == 'Z') urb_transfer = URB_ISOCHRONOUS; else if (pipeid1 == 'I') urb_transfer = URB_INTERRUPT; else if (pipeid1 == 'B') urb_transfer = URB_BULK; if (pipeid2 == 'i') { ep_num |= URB_TRANSFER_IN; incoming = 1; } if (etype == 'C') incoming = !incoming; /* direction check*/ if (incoming) { if (handle->direction == PCAP_D_OUT) return 0; } else if (handle->direction == PCAP_D_IN) return 0; uhdr->event_type = etype; uhdr->transfer_type = urb_transfer; uhdr->endpoint_number = ep_num; pkth.caplen = sizeof(pcap_usb_header); rawdata += sizeof(pcap_usb_header); /* check if this is a setup packet */ ret = sscanf(status, "%d", &dummy); if (ret != 1) { /* this a setup packet, setup data can be filled with underscore if * usbmon has not been able to read them, so we must parse this fields as * strings */ pcap_usb_setup* shdr; char str1[3], str2[3], str3[5], str4[5], str5[5]; ret = sscanf(string, "%s %s %s %s %s%n", str1, str2, str3, str4, str5, &cnt); if (ret < 5) { pcap_snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, "Can't parse USB bus message '%s', too few tokens (expected 5 got %d)", string, ret); return -1; } string += cnt; /* try to convert to corresponding integer */ shdr = &uhdr->setup; shdr->bmRequestType = strtoul(str1, 0, 16); shdr->bRequest = strtoul(str2, 0, 16); shdr->wValue = htols(strtoul(str3, 0, 16)); shdr->wIndex = htols(strtoul(str4, 0, 16)); shdr->wLength = htols(strtoul(str5, 0, 16)); uhdr->setup_flag = 0; } else uhdr->setup_flag = 1; /* read urb data */ ret = sscanf(string, " %d%n", &urb_len, &cnt); if (ret < 1) { pcap_snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, "Can't parse urb length from '%s'", string); return -1; } string += cnt; /* urb tag is not present if urb length is 0, so we can stop here * text parsing */ pkth.len = urb_len+pkth.caplen; uhdr->urb_len = urb_len; uhdr->data_flag = 1; data_len = 0; if (uhdr->urb_len == 0) goto got; /* check for data presence; data is present if and only if urb tag is '=' */ if (sscanf(string, " %c", &urb_tag) != 1) { pcap_snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, "Can't parse urb tag from '%s'", string); return -1; } if (urb_tag != '=') goto got; /* skip urb tag and following space */ string += 3; /* if we reach this point we got some urb data*/ uhdr->data_flag = 0; /* read all urb data; if urb length is greater then the usbmon internal * buffer length used by the kernel to spool the URB, we get only * a partial information. * At least until linux 2.6.17 there is no way to set usbmon intenal buffer * length and default value is 130. */ while ((string[0] != 0) && (string[1] != 0) && (pkth.caplen < (bpf_u_int32)handle->snapshot)) { rawdata[0] = ascii_to_int(string[0]) * 16 + ascii_to_int(string[1]); rawdata++; string+=2; if (string[0] == ' ') string++; pkth.caplen++; data_len++; } got: uhdr->data_len = data_len; if (pkth.caplen > (bpf_u_int32)handle->snapshot) pkth.caplen = (bpf_u_int32)handle->snapshot; if (handle->fcode.bf_insns == NULL || bpf_filter(handle->fcode.bf_insns, handle->buffer, pkth.len, pkth.caplen)) { handlep->packets_read++; callback(user, &pkth, handle->buffer); return 1; } return 0; /* didn't pass filter */ }
static int usb_stats_linux(pcap_t *handle, struct pcap_stat *stats) { struct pcap_usb_linux *handlep = handle->priv; int dummy, ret, consumed, cnt; char string[USB_LINE_LEN]; char token[USB_LINE_LEN]; char * ptr = string; int fd; pcap_snprintf(string, USB_LINE_LEN, USB_TEXT_DIR"/%ds", handlep->bus_index); fd = open(string, O_RDONLY, 0); if (fd < 0) { if (errno == ENOENT) { /* * Not found at the new location; try the old * location. */ pcap_snprintf(string, USB_LINE_LEN, USB_TEXT_DIR_OLD"/%ds", handlep->bus_index); fd = open(string, O_RDONLY, 0); } if (fd < 0) { pcap_fmt_errmsg_for_errno(handle->errbuf, PCAP_ERRBUF_SIZE, errno, "Can't open USB stats file %s", string); return -1; } } /* read stats line */ do { ret = read(fd, string, USB_LINE_LEN-1); } while ((ret == -1) && (errno == EINTR)); close(fd); if (ret < 0) { pcap_snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, "Can't read stats from fd %d ", fd); return -1; } string[ret] = 0; /* extract info on dropped urbs */ for (consumed=0; consumed < ret; ) { /* from the sscanf man page: * The C standard says: "Execution of a %n directive does * not increment the assignment count returned at the completion * of execution" but the Corrigendum seems to contradict this. * Do not make any assumptions on the effect of %n conversions * on the return value and explicitly check for cnt assignmet*/ int ntok; cnt = -1; ntok = sscanf(ptr, "%s%n", token, &cnt); if ((ntok < 1) || (cnt < 0)) break; consumed += cnt; ptr += cnt; if (strcmp(token, "nreaders") == 0) ret = sscanf(ptr, "%d", &stats->ps_drop); else ret = sscanf(ptr, "%d", &dummy); if (ntok != 1) break; consumed += cnt; ptr += cnt; } stats->ps_recv = handlep->packets_read; stats->ps_ifdrop = 0; return 0; }
static int process_client_data (char *errbuf) { /* returns: -1 = error, 0 = OK */ int chassis, geoslot; unit_t *u; pcap_if_t *iff, *prev_iff; pcap_addr_t *addr, *prev_addr; char *ptr; int address_count; struct sockaddr_in *s; char *newname; bpf_u_int32 interfaceType; unsigned char flags; void *bigger_buffer; prev_iff = 0; for (chassis = 0; chassis <= MAX_CHASSIS; chassis++) { for (geoslot = 0; geoslot <= MAX_GEOSLOT; geoslot++) { /* now loop over all the devices */ u = &units[chassis][geoslot]; empty_unit_iface(u); ptr = u->imsg; /* point to the start of the msg for this IOP */ while (ptr < (u->imsg + u->len)) { if ((iff = malloc(sizeof(pcap_if_t))) == NULL) { pcap_fmt_errmsg_for_errno(errbuf, PCAP_ERRBUF_SIZE, errno, "malloc"); return -1; } memset((char *)iff, 0, sizeof(pcap_if_t)); /* bzero() is deprecated, replaced with memset() */ if (acn_if_list == 0) acn_if_list = iff; /* remember the head of the list */ if (prev_iff) prev_iff->next = iff; /* insert a forward link */ if (*ptr) { /* if there is a count for the name */ if ((iff->name = malloc(*ptr + 1)) == NULL) { /* get that amount of space */ pcap_fmt_errmsg_for_errno(errbuf, PCAP_ERRBUF_SIZE, errno, "malloc"); return -1; } memcpy(iff->name, (ptr + 1), *ptr); /* copy the name into the malloc'ed space */ *(iff->name + *ptr) = 0; /* and null terminate the string */ ptr += *ptr; /* now move the pointer forwards by the length of the count plus the length of the string */ } ptr++; if (*ptr) { /* if there is a count for the description */ if ((iff->description = malloc(*ptr + 1)) == NULL) { /* get that amount of space */ pcap_fmt_errmsg_for_errno(errbuf, PCAP_ERRBUF_SIZE, errno, "malloc"); return -1; } memcpy(iff->description, (ptr + 1), *ptr); /* copy the name into the malloc'ed space */ *(iff->description + *ptr) = 0; /* and null terminate the string */ ptr += *ptr; /* now move the pointer forwards by the length of the count plus the length of the string */ } ptr++; interfaceType = ntohl(*(bpf_u_int32 *)ptr); ptr += 4; /* skip over the interface type */ flags = *ptr++; if (flags) iff->flags = PCAP_IF_LOOPBACK; /* if this is a loopback style interface, lets mark it as such */ address_count = *ptr++; prev_addr = 0; while (address_count--) { if ((addr = malloc(sizeof(pcap_addr_t))) == NULL) { pcap_fmt_errmsg_for_errno(errbuf, PCAP_ERRBUF_SIZE, errno, "malloc"); return -1; } memset((char *)addr, 0, sizeof(pcap_addr_t)); /* bzero() is deprecated, replaced with memset() */ if (iff->addresses == 0) iff->addresses = addr; if (prev_addr) prev_addr->next = addr; /* insert a forward link */ if (*ptr) { /* if there is a count for the address */ if ((s = malloc(sizeof(struct sockaddr_in))) == NULL) { /* get that amount of space */ pcap_fmt_errmsg_for_errno(errbuf, PCAP_ERRBUF_SIZE, errno, "malloc"); return -1; } memset((char *)s, 0, sizeof(struct sockaddr_in)); /* bzero() is deprecated, replaced with memset() */ addr->addr = (struct sockaddr *)s; s->sin_family = AF_INET; s->sin_addr.s_addr = *(bpf_u_int32 *)(ptr + 1); /* copy the address in */ ptr += *ptr; /* now move the pointer forwards according to the specified length of the address */ } ptr++; /* then forwards one more for the 'length of the address' field */ if (*ptr) { /* process any netmask */ if ((s = malloc(sizeof(struct sockaddr_in))) == NULL) { pcap_fmt_errmsg_for_errno(errbuf, PCAP_ERRBUF_SIZE, errno, "malloc"); return -1; } /* bzero() is deprecated, replaced with memset() */ memset((char *)s, 0, sizeof(struct sockaddr_in)); addr->netmask = (struct sockaddr *)s; s->sin_family = AF_INET; s->sin_addr.s_addr = *(bpf_u_int32*)(ptr + 1); ptr += *ptr; } ptr++; if (*ptr) { /* process any broadcast address */ if ((s = malloc(sizeof(struct sockaddr_in))) == NULL) { pcap_fmt_errmsg_for_errno(errbuf, PCAP_ERRBUF_SIZE, errno, "malloc"); return -1; } /* bzero() is deprecated, replaced with memset() */ memset((char *)s, 0, sizeof(struct sockaddr_in)); addr->broadaddr = (struct sockaddr *)s; s->sin_family = AF_INET; s->sin_addr.s_addr = *(bpf_u_int32*)(ptr + 1); ptr += *ptr; } ptr++; if (*ptr) { /* process any destination address */ if ((s = malloc(sizeof(struct sockaddr_in))) == NULL) { pcap_fmt_errmsg_for_errno(errbuf, PCAP_ERRBUF_SIZE, errno, "malloc"); return -1; } /* bzero() is deprecated, replaced with memset() */ memset((char *)s, 0, sizeof(struct sockaddr_in)); addr->dstaddr = (struct sockaddr *)s; s->sin_family = AF_INET; s->sin_addr.s_addr = *(bpf_u_int32*)(ptr + 1); ptr += *ptr; } ptr++; prev_addr = addr; } prev_iff = iff; newname = translate_IOP_to_pcap_name(u, iff->name, interfaceType); /* add a translation entry and get a point to the mangled name */ bigger_buffer = realloc(iff->name, strlen(newname) + 1)); if (bigger_buffer == NULL) { /* we now re-write the name stored in the interface list */ pcap_fmt_errmsg_for_errno(errbuf, PCAP_ERRBUF_SIZE, errno, "realloc"); return -1; } iff->name = bigger_buffer; strcpy(iff->name, newname); /* to this new name */ } } } return 0; }
/* * Check whether this is a pcap savefile and, if it is, extract the * relevant information from the header. */ pcap_t * pcap_check_header(bpf_u_int32 magic, FILE *fp, u_int precision, char *errbuf, int *err) { struct pcap_file_header hdr; size_t amt_read; pcap_t *p; int swapped = 0; struct pcap_sf *ps; /* * Assume no read errors. */ *err = 0; /* * Check whether the first 4 bytes of the file are the magic * number for a pcap savefile, or for a byte-swapped pcap * savefile. */ if (magic != TCPDUMP_MAGIC && magic != KUZNETZOV_TCPDUMP_MAGIC && magic != NSEC_TCPDUMP_MAGIC) { magic = SWAPLONG(magic); if (magic != TCPDUMP_MAGIC && magic != KUZNETZOV_TCPDUMP_MAGIC && magic != NSEC_TCPDUMP_MAGIC) return (NULL); /* nope */ swapped = 1; } /* * They are. Put the magic number in the header, and read * the rest of the header. */ hdr.magic = magic; amt_read = fread(((char *)&hdr) + sizeof hdr.magic, 1, sizeof(hdr) - sizeof(hdr.magic), fp); if (amt_read != sizeof(hdr) - sizeof(hdr.magic)) { if (ferror(fp)) { pcap_fmt_errmsg_for_errno(errbuf, PCAP_ERRBUF_SIZE, errno, "error reading dump file"); } else { pcap_snprintf(errbuf, PCAP_ERRBUF_SIZE, "truncated dump file; tried to read %lu file header bytes, only got %lu", (unsigned long)sizeof(hdr), (unsigned long)amt_read); } *err = 1; return (NULL); } /* * If it's a byte-swapped capture file, byte-swap the header. */ if (swapped) { hdr.version_major = SWAPSHORT(hdr.version_major); hdr.version_minor = SWAPSHORT(hdr.version_minor); hdr.thiszone = SWAPLONG(hdr.thiszone); hdr.sigfigs = SWAPLONG(hdr.sigfigs); hdr.snaplen = SWAPLONG(hdr.snaplen); hdr.linktype = SWAPLONG(hdr.linktype); } if (hdr.version_major < PCAP_VERSION_MAJOR) { pcap_snprintf(errbuf, PCAP_ERRBUF_SIZE, "archaic pcap savefile format"); *err = 1; return (NULL); } /* * currently only versions 2.[0-4] are supported with * the exception of 543.0 for DG/UX tcpdump. */ if (! ((hdr.version_major == PCAP_VERSION_MAJOR && hdr.version_minor <= PCAP_VERSION_MINOR) || (hdr.version_major == 543 && hdr.version_minor == 0))) { pcap_snprintf(errbuf, PCAP_ERRBUF_SIZE, "unsupported pcap savefile version %u.%u", hdr.version_major, hdr.version_minor); *err = 1; return NULL; } /* * OK, this is a good pcap file. * Allocate a pcap_t for it. */ p = pcap_open_offline_common(errbuf, sizeof (struct pcap_sf)); if (p == NULL) { /* Allocation failed. */ *err = 1; return (NULL); } p->swapped = swapped; p->version_major = hdr.version_major; p->version_minor = hdr.version_minor; p->tzoff = hdr.thiszone; p->snapshot = hdr.snaplen; if (p->snapshot <= 0) { /* * Bogus snapshot length; use the maximum for this * link-layer type as a fallback. * * XXX - the only reason why snapshot is signed is * that pcap_snapshot() returns an int, not an * unsigned int. */ p->snapshot = max_snaplen_for_dlt(hdr.linktype); } p->linktype = linktype_to_dlt(LT_LINKTYPE(hdr.linktype)); p->linktype_ext = LT_LINKTYPE_EXT(hdr.linktype); p->next_packet_op = pcap_next_packet; ps = p->priv; p->opt.tstamp_precision = precision; /* * Will we need to scale the timestamps to match what the * user wants? */ switch (precision) { case PCAP_TSTAMP_PRECISION_MICRO: if (magic == NSEC_TCPDUMP_MAGIC) { /* * The file has nanoseconds, the user * wants microseconds; scale the * precision down. */ ps->scale_type = SCALE_DOWN; } else { /* * The file has microseconds, the * user wants microseconds; nothing to do. */ ps->scale_type = PASS_THROUGH; } break; case PCAP_TSTAMP_PRECISION_NANO: if (magic == NSEC_TCPDUMP_MAGIC) { /* * The file has nanoseconds, the * user wants nanoseconds; nothing to do. */ ps->scale_type = PASS_THROUGH; } else { /* * The file has microoseconds, the user * wants nanoseconds; scale the * precision up. */ ps->scale_type = SCALE_UP; } break; default: pcap_snprintf(errbuf, PCAP_ERRBUF_SIZE, "unknown time stamp resolution %u", precision); free(p); *err = 1; return (NULL); } /* * We interchanged the caplen and len fields at version 2.3, * in order to match the bpf header layout. But unfortunately * some files were written with version 2.3 in their headers * but without the interchanged fields. * * In addition, DG/UX tcpdump writes out files with a version * number of 543.0, and with the caplen and len fields in the * pre-2.3 order. */ switch (hdr.version_major) { case 2: if (hdr.version_minor < 3) ps->lengths_swapped = SWAPPED; else if (hdr.version_minor == 3) ps->lengths_swapped = MAYBE_SWAPPED; else ps->lengths_swapped = NOT_SWAPPED; break; case 543: ps->lengths_swapped = SWAPPED; break; default: ps->lengths_swapped = NOT_SWAPPED; break; } if (magic == KUZNETZOV_TCPDUMP_MAGIC) { /* * XXX - the patch that's in some versions of libpcap * changes the packet header but not the magic number, * and some other versions with this magic number have * some extra debugging information in the packet header; * we'd have to use some hacks^H^H^H^H^Hheuristics to * detect those variants. * * Ethereal does that, but it does so by trying to read * the first two packets of the file with each of the * record header formats. That currently means it seeks * backwards and retries the reads, which doesn't work * on pipes. We want to be able to read from a pipe, so * that strategy won't work; we'd have to buffer some * data ourselves and read from that buffer in order to * make that work. */ ps->hdrsize = sizeof(struct pcap_sf_patched_pkthdr); if (p->linktype == DLT_EN10MB) { /* * This capture might have been done in raw mode * or cooked mode. * * If it was done in cooked mode, p->snapshot was * passed to recvfrom() as the buffer size, meaning * that the most packet data that would be copied * would be p->snapshot. However, a faked Ethernet * header would then have been added to it, so the * most data that would be in a packet in the file * would be p->snapshot + 14. * * We can't easily tell whether the capture was done * in raw mode or cooked mode, so we'll assume it was * cooked mode, and add 14 to the snapshot length. * That means that, for a raw capture, the snapshot * length will be misleading if you use it to figure * out why a capture doesn't have all the packet data, * but there's not much we can do to avoid that. */ p->snapshot += 14; } } else ps->hdrsize = sizeof(struct pcap_sf_pkthdr); /* * Allocate a buffer for the packet data. * Choose the minimum of the file's snapshot length and 2K bytes; * that should be enough for most network packets - we'll grow it * if necessary. That way, we don't allocate a huge chunk of * memory just because there's a huge snapshot length, as the * snapshot length might be larger than the size of the largest * packet. */ p->bufsize = p->snapshot; if (p->bufsize > 2048) p->bufsize = 2048; p->buffer = malloc(p->bufsize); if (p->buffer == NULL) { pcap_snprintf(errbuf, PCAP_ERRBUF_SIZE, "out of memory"); free(p); *err = 1; return (NULL); } p->cleanup_op = sf_cleanup; return (p); }
/* * Read and return the next packet from the savefile. Return the header * in hdr and a pointer to the contents in data. Return 0 on success, 1 * if there were no more packets, and -1 on an error. */ static int pcap_next_packet(pcap_t *p, struct pcap_pkthdr *hdr, u_char **data) { struct pcap_sf *ps = p->priv; struct pcap_sf_patched_pkthdr sf_hdr; FILE *fp = p->rfile; size_t amt_read; bpf_u_int32 t; /* * Read the packet header; the structure we use as a buffer * is the longer structure for files generated by the patched * libpcap, but if the file has the magic number for an * unpatched libpcap we only read as many bytes as the regular * header has. */ amt_read = fread(&sf_hdr, 1, ps->hdrsize, fp); if (amt_read != ps->hdrsize) { if (ferror(fp)) { pcap_fmt_errmsg_for_errno(p->errbuf, PCAP_ERRBUF_SIZE, errno, "error reading dump file"); return (-1); } else { if (amt_read != 0) { pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "truncated dump file; tried to read %lu header bytes, only got %lu", (unsigned long)ps->hdrsize, (unsigned long)amt_read); return (-1); } /* EOF */ return (1); } } if (p->swapped) { /* these were written in opposite byte order */ hdr->caplen = SWAPLONG(sf_hdr.caplen); hdr->len = SWAPLONG(sf_hdr.len); hdr->ts.tv_sec = SWAPLONG(sf_hdr.ts.tv_sec); hdr->ts.tv_usec = SWAPLONG(sf_hdr.ts.tv_usec); } else { hdr->caplen = sf_hdr.caplen; hdr->len = sf_hdr.len; hdr->ts.tv_sec = sf_hdr.ts.tv_sec; hdr->ts.tv_usec = sf_hdr.ts.tv_usec; } switch (ps->scale_type) { case PASS_THROUGH: /* * Just pass the time stamp through. */ break; case SCALE_UP: /* * File has microseconds, user wants nanoseconds; convert * it. */ hdr->ts.tv_usec = hdr->ts.tv_usec * 1000; break; case SCALE_DOWN: /* * File has nanoseconds, user wants microseconds; convert * it. */ hdr->ts.tv_usec = hdr->ts.tv_usec / 1000; break; } /* Swap the caplen and len fields, if necessary. */ switch (ps->lengths_swapped) { case NOT_SWAPPED: break; case MAYBE_SWAPPED: if (hdr->caplen <= hdr->len) { /* * The captured length is <= the actual length, * so presumably they weren't swapped. */ break; } /* FALLTHROUGH */ case SWAPPED: t = hdr->caplen; hdr->caplen = hdr->len; hdr->len = t; break; } /* * Is the packet bigger than we consider sane? */ if (hdr->caplen > max_snaplen_for_dlt(p->linktype)) { /* * Yes. This may be a damaged or fuzzed file. * * Is it bigger than the snapshot length? * (We don't treat that as an error if it's not * bigger than the maximum we consider sane; see * below.) */ if (hdr->caplen > (bpf_u_int32)p->snapshot) { pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "invalid packet capture length %u, bigger than " "snaplen of %d", hdr->caplen, p->snapshot); } else { pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "invalid packet capture length %u, bigger than " "maximum of %u", hdr->caplen, max_snaplen_for_dlt(p->linktype)); } return (-1); } if (hdr->caplen > (bpf_u_int32)p->snapshot) { /* * The packet is bigger than the snapshot length * for this file. * * This can happen due to Solaris 2.3 systems tripping * over the BUFMOD problem and not setting the snapshot * length correctly in the savefile header. * * libpcap 0.4 and later on Solaris 2.3 should set the * snapshot length correctly in the pcap file header, * even though they don't set a snapshot length in bufmod * (the buggy bufmod chops off the *beginning* of the * packet if a snapshot length is specified); they should * also reduce the captured length, as supplied to the * per-packet callback, to the snapshot length if it's * greater than the snapshot length, so the code using * libpcap should see the packet cut off at the snapshot * length, even though the full packet is copied up to * userland. * * However, perhaps some versions of libpcap failed to * set the snapshot length currectly in the file header * or the per-packet header, or perhaps this is a * corrupted safefile or a savefile built/modified by a * fuzz tester, so we check anyway. */ size_t bytes_to_discard; size_t bytes_to_read, bytes_read; char discard_buf[4096]; if (hdr->caplen > p->bufsize) { /* * Grow the buffer to the snapshot length. */ if (!grow_buffer(p, p->snapshot)) return (-1); } /* * Read the first p->bufsize bytes into the buffer. */ amt_read = fread(p->buffer, 1, p->bufsize, fp); if (amt_read != p->bufsize) { if (ferror(fp)) { pcap_fmt_errmsg_for_errno(p->errbuf, PCAP_ERRBUF_SIZE, errno, "error reading dump file"); } else { /* * Yes, this uses hdr->caplen; technically, * it's true, because we would try to read * and discard the rest of those bytes, and * that would fail because we got EOF before * the read finished. */ pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "truncated dump file; tried to read %u captured bytes, only got %lu", hdr->caplen, (unsigned long)amt_read); } return (-1); } /* * Now read and discard what's left. */ bytes_to_discard = hdr->caplen - p->bufsize; bytes_read = amt_read; while (bytes_to_discard != 0) { bytes_to_read = bytes_to_discard; if (bytes_to_read > sizeof (discard_buf)) bytes_to_read = sizeof (discard_buf); amt_read = fread(discard_buf, 1, bytes_to_read, fp); bytes_read += amt_read; if (amt_read != bytes_to_read) { if (ferror(fp)) { pcap_fmt_errmsg_for_errno(p->errbuf, PCAP_ERRBUF_SIZE, errno, "error reading dump file"); } else { pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "truncated dump file; tried to read %u captured bytes, only got %lu", hdr->caplen, (unsigned long)bytes_read); } return (-1); } bytes_to_discard -= amt_read; } /* * Adjust caplen accordingly, so we don't get confused later * as to how many bytes we have to play with. */ hdr->caplen = p->bufsize; } else { if (hdr->caplen > p->bufsize) { /* * Grow the buffer to the next power of 2, or * the snaplen, whichever is lower. */ u_int new_bufsize; new_bufsize = hdr->caplen; /* * http://graphics.stanford.edu/~seander/bithacks.html#RoundUpPowerOf2 */ new_bufsize--; new_bufsize |= new_bufsize >> 1; new_bufsize |= new_bufsize >> 2; new_bufsize |= new_bufsize >> 4; new_bufsize |= new_bufsize >> 8; new_bufsize |= new_bufsize >> 16; new_bufsize++; if (new_bufsize > (u_int)p->snapshot) new_bufsize = p->snapshot; if (!grow_buffer(p, new_bufsize)) return (-1); } /* read the packet itself */ amt_read = fread(p->buffer, 1, hdr->caplen, fp); if (amt_read != hdr->caplen) { if (ferror(fp)) { pcap_fmt_errmsg_for_errno(p->errbuf, PCAP_ERRBUF_SIZE, errno, "error reading dump file"); } else { pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "truncated dump file; tried to read %u captured bytes, only got %lu", hdr->caplen, (unsigned long)amt_read); } return (-1); } }
static int usb_read_linux_mmap(pcap_t *handle, int max_packets, pcap_handler callback, u_char *user) { struct pcap_usb_linux *handlep = handle->priv; struct mon_bin_mfetch fetch; int32_t vec[VEC_SIZE]; struct pcap_pkthdr pkth; pcap_usb_header* hdr; int nflush = 0; int packets = 0; u_int clen, max_clen; max_clen = handle->snapshot - sizeof(pcap_usb_header); for (;;) { int i, ret; int limit = max_packets - packets; if (limit <= 0) limit = VEC_SIZE; if (limit > VEC_SIZE) limit = VEC_SIZE; /* try to fetch as many events as possible*/ fetch.offvec = vec; fetch.nfetch = limit; fetch.nflush = nflush; /* ignore interrupt system call errors */ do { ret = ioctl(handle->fd, MON_IOCX_MFETCH, &fetch); if (handle->break_loop) { handle->break_loop = 0; return -2; } } while ((ret == -1) && (errno == EINTR)); if (ret < 0) { if (errno == EAGAIN) return 0; /* no data there */ pcap_fmt_errmsg_for_errno(handle->errbuf, PCAP_ERRBUF_SIZE, errno, "Can't mfetch fd %d", handle->fd); return -1; } /* keep track of processed events, we will flush them later */ nflush = fetch.nfetch; for (i=0; i<fetch.nfetch; ++i) { /* discard filler */ hdr = (pcap_usb_header*) &handlep->mmapbuf[vec[i]]; if (hdr->event_type == '@') continue; /* we can get less that than really captured from kernel, depending on * snaplen, so adjust header accordingly */ clen = max_clen; if (hdr->data_len < clen) clen = hdr->data_len; /* get packet info from header*/ pkth.caplen = clen + sizeof(pcap_usb_header_mmapped); pkth.len = hdr->data_len + sizeof(pcap_usb_header_mmapped); pkth.ts.tv_sec = hdr->ts_sec; pkth.ts.tv_usec = hdr->ts_usec; if (handle->fcode.bf_insns == NULL || bpf_filter(handle->fcode.bf_insns, (u_char*) hdr, pkth.len, pkth.caplen)) { handlep->packets_read++; callback(user, &pkth, (u_char*) hdr); packets++; } } /* with max_packets specifying "unlimited" we stop afer the first chunk*/ if (PACKET_COUNT_IS_UNLIMITED(max_packets) || (packets == max_packets)) break; } /* flush pending events*/ if (ioctl(handle->fd, MON_IOCH_MFLUSH, nflush) == -1) { pcap_fmt_errmsg_for_errno(handle->errbuf, PCAP_ERRBUF_SIZE, errno, "Can't mflush fd %d", handle->fd); return -1; } return packets; }
/* * Check whether this is a pcapng savefile and, if it is, extract the * relevant information from the header. */ pcap_t * pcap_ng_check_header(bpf_u_int32 magic, FILE *fp, u_int precision, char *errbuf, int *err) { size_t amt_read; bpf_u_int32 total_length; bpf_u_int32 byte_order_magic; struct block_header *bhdrp; struct section_header_block *shbp; pcap_t *p; int swapped = 0; struct pcap_ng_sf *ps; int status; struct block_cursor cursor; struct interface_description_block *idbp; /* * Assume no read errors. */ *err = 0; /* * Check whether the first 4 bytes of the file are the block * type for a pcapng savefile. */ if (magic != BT_SHB) { /* * XXX - check whether this looks like what the block * type would be after being munged by mapping between * UN*X and DOS/Windows text file format and, if it * does, look for the byte-order magic number in * the appropriate place and, if we find it, report * this as possibly being a pcapng file transferred * between UN*X and Windows in text file format? */ return (NULL); /* nope */ } /* * OK, they are. However, that's just \n\r\r\n, so it could, * conceivably, be an ordinary text file. * * It could not, however, conceivably be any other type of * capture file, so we can read the rest of the putative * Section Header Block; put the block type in the common * header, read the rest of the common header and the * fixed-length portion of the SHB, and look for the byte-order * magic value. */ amt_read = fread(&total_length, 1, sizeof(total_length), fp); if (amt_read < sizeof(total_length)) { if (ferror(fp)) { pcap_fmt_errmsg_for_errno(errbuf, PCAP_ERRBUF_SIZE, errno, "error reading dump file"); *err = 1; return (NULL); /* fail */ } /* * Possibly a weird short text file, so just say * "not pcapng". */ return (NULL); } amt_read = fread(&byte_order_magic, 1, sizeof(byte_order_magic), fp); if (amt_read < sizeof(byte_order_magic)) { if (ferror(fp)) { pcap_fmt_errmsg_for_errno(errbuf, PCAP_ERRBUF_SIZE, errno, "error reading dump file"); *err = 1; return (NULL); /* fail */ } /* * Possibly a weird short text file, so just say * "not pcapng". */ return (NULL); } if (byte_order_magic != BYTE_ORDER_MAGIC) { byte_order_magic = SWAPLONG(byte_order_magic); if (byte_order_magic != BYTE_ORDER_MAGIC) { /* * Not a pcapng file. */ return (NULL); } swapped = 1; total_length = SWAPLONG(total_length); } /* * Check the sanity of the total length. */ if (total_length < sizeof(*bhdrp) + sizeof(*shbp) + sizeof(struct block_trailer)) { pcap_snprintf(errbuf, PCAP_ERRBUF_SIZE, "Section Header Block in pcapng dump file has a length of %u < %" PRIsize, total_length, sizeof(*bhdrp) + sizeof(*shbp) + sizeof(struct block_trailer)); *err = 1; return (NULL); } /* * Make sure it's not too big. */ if (total_length > INITIAL_MAX_BLOCKSIZE) { pcap_snprintf(errbuf, PCAP_ERRBUF_SIZE, "pcapng block size %u > maximum %u", total_length, INITIAL_MAX_BLOCKSIZE); *err = 1; return (NULL); } /* * OK, this is a good pcapng file. * Allocate a pcap_t for it. */ p = pcap_open_offline_common(errbuf, sizeof (struct pcap_ng_sf)); if (p == NULL) { /* Allocation failed. */ *err = 1; return (NULL); } p->swapped = swapped; ps = p->priv; /* * What precision does the user want? */ switch (precision) { case PCAP_TSTAMP_PRECISION_MICRO: ps->user_tsresol = 1000000; break; case PCAP_TSTAMP_PRECISION_NANO: ps->user_tsresol = 1000000000; break; default: pcap_snprintf(errbuf, PCAP_ERRBUF_SIZE, "unknown time stamp resolution %u", precision); free(p); *err = 1; return (NULL); } p->opt.tstamp_precision = precision; /* * Allocate a buffer into which to read blocks. We default to * the maximum of: * * the total length of the SHB for which we read the header; * * 2K, which should be more than large enough for an Enhanced * Packet Block containing a full-size Ethernet frame, and * leaving room for some options. * * If we find a bigger block, we reallocate the buffer, up to * the maximum size. We start out with a maximum size of * INITIAL_MAX_BLOCKSIZE; if we see any link-layer header types * with a maximum snapshot that results in a larger maximum * block length, we boost the maximum. */ p->bufsize = 2048; if (p->bufsize < total_length) p->bufsize = total_length; p->buffer = malloc(p->bufsize); if (p->buffer == NULL) { pcap_snprintf(errbuf, PCAP_ERRBUF_SIZE, "out of memory"); free(p); *err = 1; return (NULL); } ps->max_blocksize = INITIAL_MAX_BLOCKSIZE; /* * Copy the stuff we've read to the buffer, and read the rest * of the SHB. */ bhdrp = (struct block_header *)p->buffer; shbp = (struct section_header_block *)((u_char *)p->buffer + sizeof(struct block_header)); bhdrp->block_type = magic; bhdrp->total_length = total_length; shbp->byte_order_magic = byte_order_magic; if (read_bytes(fp, (u_char *)p->buffer + (sizeof(magic) + sizeof(total_length) + sizeof(byte_order_magic)), total_length - (sizeof(magic) + sizeof(total_length) + sizeof(byte_order_magic)), 1, errbuf) == -1) goto fail; if (p->swapped) { /* * Byte-swap the fields we've read. */ shbp->major_version = SWAPSHORT(shbp->major_version); shbp->minor_version = SWAPSHORT(shbp->minor_version); /* * XXX - we don't care about the section length. */ } /* currently only SHB version 1.0 is supported */ if (! (shbp->major_version == PCAP_NG_VERSION_MAJOR && shbp->minor_version == PCAP_NG_VERSION_MINOR)) { pcap_snprintf(errbuf, PCAP_ERRBUF_SIZE, "unsupported pcapng savefile version %u.%u", shbp->major_version, shbp->minor_version); goto fail; } p->version_major = shbp->major_version; p->version_minor = shbp->minor_version; /* * Save the time stamp resolution the user requested. */ p->opt.tstamp_precision = precision; /* * Now start looking for an Interface Description Block. */ for (;;) { /* * Read the next block. */ status = read_block(fp, p, &cursor, errbuf); if (status == 0) { /* EOF - no IDB in this file */ pcap_snprintf(errbuf, PCAP_ERRBUF_SIZE, "the capture file has no Interface Description Blocks"); goto fail; } if (status == -1) goto fail; /* error */ switch (cursor.block_type) { case BT_IDB: /* * Get a pointer to the fixed-length portion of the * IDB. */ idbp = get_from_block_data(&cursor, sizeof(*idbp), errbuf); if (idbp == NULL) goto fail; /* error */ /* * Byte-swap it if necessary. */ if (p->swapped) { idbp->linktype = SWAPSHORT(idbp->linktype); idbp->snaplen = SWAPLONG(idbp->snaplen); } /* * Try to add this interface. */ if (!add_interface(p, &cursor, errbuf)) goto fail; goto done; case BT_EPB: case BT_SPB: case BT_PB: /* * Saw a packet before we saw any IDBs. That's * not valid, as we don't know what link-layer * encapsulation the packet has. */ pcap_snprintf(errbuf, PCAP_ERRBUF_SIZE, "the capture file has a packet block before any Interface Description Blocks"); goto fail; default: /* * Just ignore it. */ break; } } done: p->snapshot = idbp->snaplen; if (p->snapshot <= 0) { /* * Bogus snapshot length; use the maximum for this * link-layer type as a fallback. * * XXX - the only reason why snapshot is signed is * that pcap_snapshot() returns an int, not an * unsigned int. */ p->snapshot = max_snaplen_for_dlt(idbp->linktype); } p->linktype = linktype_to_dlt(idbp->linktype); p->linktype_ext = 0; /* * If the maximum block size for a packet with the maximum * snapshot length for this DLT_ is bigger than the current * maximum block size, increase the maximum. */ if (MAX_BLOCKSIZE_FOR_SNAPLEN(max_snaplen_for_dlt(p->linktype)) > ps->max_blocksize) ps->max_blocksize = MAX_BLOCKSIZE_FOR_SNAPLEN(max_snaplen_for_dlt(p->linktype)); p->next_packet_op = pcap_ng_next_packet; p->cleanup_op = pcap_ng_cleanup; return (p); fail: free(ps->ifaces); free(p->buffer); free(p); *err = 1; return (NULL); }
int bt_findalldevs(pcap_if_list_t *devlistp, char *err_str) { struct hci_dev_list_req *dev_list; struct hci_dev_req *dev_req; int i, sock; int ret = 0; sock = socket(AF_BLUETOOTH, SOCK_RAW, BTPROTO_HCI); if (sock < 0) { /* if bluetooth is not supported this this is not fatal*/ if (errno == EAFNOSUPPORT) return 0; pcap_fmt_errmsg_for_errno(err_str, PCAP_ERRBUF_SIZE, errno, "Can't open raw Bluetooth socket"); return -1; } dev_list = malloc(HCI_MAX_DEV * sizeof(*dev_req) + sizeof(*dev_list)); if (!dev_list) { pcap_snprintf(err_str, PCAP_ERRBUF_SIZE, "Can't allocate %zu bytes for Bluetooth device list", HCI_MAX_DEV * sizeof(*dev_req) + sizeof(*dev_list)); ret = -1; goto done; } dev_list->dev_num = HCI_MAX_DEV; if (ioctl(sock, HCIGETDEVLIST, (void *) dev_list) < 0) { pcap_fmt_errmsg_for_errno(err_str, PCAP_ERRBUF_SIZE, errno, "Can't get Bluetooth device list via ioctl"); ret = -1; goto free; } dev_req = dev_list->dev_req; for (i = 0; i < dev_list->dev_num; i++, dev_req++) { char dev_name[20], dev_descr[30]; pcap_snprintf(dev_name, 20, BT_IFACE"%d", dev_req->dev_id); pcap_snprintf(dev_descr, 30, "Bluetooth adapter number %d", i); /* * Bluetooth is a wireless technology. * XXX - if there's the notion of associating with a * network, and we can determine whether the interface * is associated with a network, check that and set * the status to PCAP_IF_CONNECTION_STATUS_CONNECTED * or PCAP_IF_CONNECTION_STATUS_DISCONNECTED. */ if (add_dev(devlistp, dev_name, PCAP_IF_WIRELESS, dev_descr, err_str) == NULL) { ret = -1; break; } } free: free(dev_list); done: close(sock); return ret; }
static int bt_read_linux(pcap_t *handle, int max_packets _U_, pcap_handler callback, u_char *user) { struct cmsghdr *cmsg; struct msghdr msg; struct iovec iv; ssize_t ret; struct pcap_pkthdr pkth; pcap_bluetooth_h4_header* bthdr; u_char *pktd; int in = 0; pktd = (u_char *)handle->buffer + BT_CTRL_SIZE; bthdr = (pcap_bluetooth_h4_header*)(void *)pktd; iv.iov_base = pktd + sizeof(pcap_bluetooth_h4_header); iv.iov_len = handle->snapshot; memset(&msg, 0, sizeof(msg)); msg.msg_iov = &iv; msg.msg_iovlen = 1; msg.msg_control = handle->buffer; msg.msg_controllen = BT_CTRL_SIZE; /* ignore interrupt system call error */ do { ret = recvmsg(handle->fd, &msg, 0); if (handle->break_loop) { handle->break_loop = 0; return -2; } } while ((ret == -1) && (errno == EINTR)); if (ret < 0) { pcap_fmt_errmsg_for_errno(handle->errbuf, PCAP_ERRBUF_SIZE, errno, "Can't receive packet"); return -1; } pkth.caplen = ret; /* get direction and timestamp*/ cmsg = CMSG_FIRSTHDR(&msg); while (cmsg) { switch (cmsg->cmsg_type) { case HCI_CMSG_DIR: memcpy(&in, CMSG_DATA(cmsg), sizeof in); break; case HCI_CMSG_TSTAMP: memcpy(&pkth.ts, CMSG_DATA(cmsg), sizeof pkth.ts); break; } cmsg = CMSG_NXTHDR(&msg, cmsg); } if ((in && (handle->direction == PCAP_D_OUT)) || ((!in) && (handle->direction == PCAP_D_IN))) return 0; bthdr->direction = htonl(in != 0); pkth.caplen+=sizeof(pcap_bluetooth_h4_header); pkth.len = pkth.caplen; if (handle->fcode.bf_insns == NULL || bpf_filter(handle->fcode.bf_insns, pktd, pkth.len, pkth.caplen)) { callback(user, &pkth, pktd); return 1; } return 0; /* didn't pass filter */ }
static int bt_activate(pcap_t* handle) { struct pcap_bt *handlep = handle->priv; struct sockaddr_hci addr; int opt; int dev_id; struct hci_filter flt; int err = PCAP_ERROR; /* get bt interface id */ if (sscanf(handle->opt.device, BT_IFACE"%d", &dev_id) != 1) { pcap_snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, "Can't get Bluetooth device index from %s", handle->opt.device); return PCAP_ERROR; } /* * 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 (handle->snapshot <= 0 || handle->snapshot > MAXIMUM_SNAPLEN) handle->snapshot = MAXIMUM_SNAPLEN; /* Initialize some components of the pcap structure. */ handle->bufsize = BT_CTRL_SIZE+sizeof(pcap_bluetooth_h4_header)+handle->snapshot; handle->linktype = DLT_BLUETOOTH_HCI_H4_WITH_PHDR; handle->read_op = bt_read_linux; handle->inject_op = bt_inject_linux; handle->setfilter_op = install_bpf_program; /* no kernel filtering */ handle->setdirection_op = bt_setdirection_linux; handle->set_datalink_op = NULL; /* can't change data link type */ handle->getnonblock_op = pcap_getnonblock_fd; handle->setnonblock_op = pcap_setnonblock_fd; handle->stats_op = bt_stats_linux; handlep->dev_id = dev_id; /* Create HCI socket */ handle->fd = socket(AF_BLUETOOTH, SOCK_RAW, BTPROTO_HCI); if (handle->fd < 0) { pcap_fmt_errmsg_for_errno(handle->errbuf, PCAP_ERRBUF_SIZE, errno, "Can't create raw socket"); return PCAP_ERROR; } handle->buffer = malloc(handle->bufsize); if (!handle->buffer) { pcap_fmt_errmsg_for_errno(handle->errbuf, PCAP_ERRBUF_SIZE, errno, "Can't allocate dump buffer"); goto close_fail; } opt = 1; if (setsockopt(handle->fd, SOL_HCI, HCI_DATA_DIR, &opt, sizeof(opt)) < 0) { pcap_fmt_errmsg_for_errno(handle->errbuf, PCAP_ERRBUF_SIZE, errno, "Can't enable data direction info"); goto close_fail; } opt = 1; if (setsockopt(handle->fd, SOL_HCI, HCI_TIME_STAMP, &opt, sizeof(opt)) < 0) { pcap_fmt_errmsg_for_errno(handle->errbuf, PCAP_ERRBUF_SIZE, errno, "Can't enable time stamp"); goto close_fail; } /* Setup filter, do not call hci function to avoid dependence on * external libs */ memset(&flt, 0, sizeof(flt)); memset((void *) &flt.type_mask, 0xff, sizeof(flt.type_mask)); memset((void *) &flt.event_mask, 0xff, sizeof(flt.event_mask)); if (setsockopt(handle->fd, SOL_HCI, HCI_FILTER, &flt, sizeof(flt)) < 0) { pcap_fmt_errmsg_for_errno(handle->errbuf, PCAP_ERRBUF_SIZE, errno, "Can't set filter"); goto close_fail; } /* Bind socket to the HCI device */ addr.hci_family = AF_BLUETOOTH; addr.hci_dev = handlep->dev_id; #ifdef HAVE_STRUCT_SOCKADDR_HCI_HCI_CHANNEL addr.hci_channel = HCI_CHANNEL_RAW; #endif if (bind(handle->fd, (struct sockaddr *) &addr, sizeof(addr)) < 0) { pcap_fmt_errmsg_for_errno(handle->errbuf, PCAP_ERRBUF_SIZE, errno, "Can't attach to device %d", handlep->dev_id); goto close_fail; } if (handle->opt.rfmon) { /* * Monitor mode doesn't apply to Bluetooth devices. */ err = PCAP_ERROR_RFMON_NOTSUP; goto close_fail; } if (handle->opt.buffer_size != 0) { /* * Set the socket buffer size to the specified value. */ if (setsockopt(handle->fd, SOL_SOCKET, SO_RCVBUF, &handle->opt.buffer_size, sizeof(handle->opt.buffer_size)) == -1) { pcap_fmt_errmsg_for_errno(handle->errbuf, errno, PCAP_ERRBUF_SIZE, "SO_RCVBUF"); goto close_fail; } } handle->selectable_fd = handle->fd; return 0; close_fail: pcap_cleanup_live_common(handle); return err; }
static int netfilter_read_linux(pcap_t *handle, int max_packets, pcap_handler callback, u_char *user) { struct pcap_netfilter *handlep = handle->priv; register u_char *bp, *ep; int count = 0; ssize_t len; /* * Has "pcap_breakloop()" been called? */ if (handle->break_loop) { /* * Yes - clear the flag that indicates that it * has, and return PCAP_ERROR_BREAK to indicate * that we were told to break out of the loop. */ handle->break_loop = 0; return PCAP_ERROR_BREAK; } len = handle->cc; if (len == 0) { /* * The buffer is empty; refill it. * * We ignore EINTR, as that might just be due to a signal * being delivered - if the signal should interrupt the * loop, the signal handler should call pcap_breakloop() * to set handle->break_loop (we ignore it on other * platforms as well). */ do { len = recv(handle->fd, handle->buffer, handle->bufsize, 0); if (handle->break_loop) { handle->break_loop = 0; return PCAP_ERROR_BREAK; } if (errno == ENOBUFS) handlep->packets_nobufs++; } while ((len == -1) && (errno == EINTR || errno == ENOBUFS)); if (len < 0) { pcap_fmt_errmsg_for_errno(handle->errbuf, PCAP_ERRBUF_SIZE, errno, "Can't receive packet"); return PCAP_ERROR; } bp = (unsigned char *)handle->buffer; } else bp = handle->bp; ep = bp + len; while (bp < ep) { const struct nlmsghdr *nlh = (const struct nlmsghdr *) bp; uint32_t msg_len; nftype_t type = OTHER; /* * Has "pcap_breakloop()" been called? * If so, return immediately - if we haven't read any * packets, clear the flag and return PCAP_ERROR_BREAK * 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 (handle->break_loop) { handle->bp = bp; handle->cc = (int)(ep - bp); if (count == 0) { handle->break_loop = 0; return PCAP_ERROR_BREAK; } else return count; } if (ep - bp < NLMSG_SPACE(0)) { /* * There's less than one netlink message left * in the buffer. Give up. */ break; } if (nlh->nlmsg_len < sizeof(struct nlmsghdr) || (u_int)len < nlh->nlmsg_len) { pcap_snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, "Message truncated: (got: %zd) (nlmsg_len: %u)", len, nlh->nlmsg_len); return -1; } if (NFNL_SUBSYS_ID(nlh->nlmsg_type) == NFNL_SUBSYS_ULOG && NFNL_MSG_TYPE(nlh->nlmsg_type) == NFULNL_MSG_PACKET) type = NFLOG; else if (NFNL_SUBSYS_ID(nlh->nlmsg_type) == NFNL_SUBSYS_QUEUE && NFNL_MSG_TYPE(nlh->nlmsg_type) == NFQNL_MSG_PACKET) type = NFQUEUE; if (type != OTHER) { const unsigned char *payload = NULL; struct pcap_pkthdr pkth; const struct nfgenmsg *nfg = NULL; int id = 0; if (handle->linktype != DLT_NFLOG) { const struct nfattr *payload_attr = NULL; if (nlh->nlmsg_len < HDR_LENGTH) { pcap_snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, "Malformed message: (nlmsg_len: %u)", nlh->nlmsg_len); return -1; } nfg = NLMSG_DATA(nlh); if (nlh->nlmsg_len > HDR_LENGTH) { struct nfattr *attr = NFM_NFA(nfg); int attr_len = nlh->nlmsg_len - NLMSG_ALIGN(HDR_LENGTH); while (NFA_OK(attr, attr_len)) { if (type == NFQUEUE) { switch (NFA_TYPE(attr)) { case NFQA_PACKET_HDR: { const struct nfqnl_msg_packet_hdr *pkt_hdr = (const struct nfqnl_msg_packet_hdr *) NFA_DATA(attr); id = ntohl(pkt_hdr->packet_id); break; } case NFQA_PAYLOAD: payload_attr = attr; break; } } else if (type == NFLOG) { switch (NFA_TYPE(attr)) { case NFULA_PAYLOAD: payload_attr = attr; break; } } attr = NFA_NEXT(attr, attr_len); } } if (payload_attr) { payload = NFA_DATA(payload_attr); pkth.len = pkth.caplen = NFA_PAYLOAD(payload_attr); } } else { payload = NLMSG_DATA(nlh); pkth.caplen = pkth.len = nlh->nlmsg_len-NLMSG_ALIGN(sizeof(struct nlmsghdr)); } if (payload) { /* pkth.caplen = min (payload_len, handle->snapshot); */ gettimeofday(&pkth.ts, NULL); if (handle->fcode.bf_insns == NULL || pcap_filter(handle->fcode.bf_insns, payload, pkth.len, pkth.caplen)) { handlep->packets_read++; callback(user, &pkth, payload); count++; } } if (type == NFQUEUE) { /* XXX, possible responses: NF_DROP, NF_ACCEPT, NF_STOLEN, NF_QUEUE, NF_REPEAT, NF_STOP */ /* if type == NFQUEUE, handle->linktype is always != DLT_NFLOG, so nfg is always initialized to NLMSG_DATA(nlh). */ if (nfg != NULL) nfqueue_send_verdict(handle, ntohs(nfg->res_id), id, NF_ACCEPT); } } msg_len = NLMSG_ALIGN(nlh->nlmsg_len); /* * If the message length would run past the end of the * buffer, truncate it to the remaining space in the * buffer. */ if (msg_len > ep - bp) msg_len = (uint32_t)(ep - bp); bp += msg_len; if (count >= max_packets && !PACKET_COUNT_IS_UNLIMITED(max_packets)) { handle->bp = bp; handle->cc = (int)(ep - bp); if (handle->cc < 0) handle->cc = 0; return count; } } handle->cc = 0; return count; }
int pcap_findalldevs_ex(const char *source, struct pcap_rmtauth *auth, pcap_if_t **alldevs, char *errbuf) { int type; char name[PCAP_BUF_SIZE], path[PCAP_BUF_SIZE], filename[PCAP_BUF_SIZE]; size_t pathlen; size_t stringlen; pcap_t *fp; char tmpstring[PCAP_BUF_SIZE + 1]; /* Needed to convert names and descriptions from 'old' syntax to the 'new' one */ pcap_if_t *lastdev; /* Last device in the pcap_if_t list */ pcap_if_t *dev; /* Device we're adding to the pcap_if_t list */ /* List starts out empty. */ (*alldevs) = NULL; lastdev = NULL; if (strlen(source) > PCAP_BUF_SIZE) { pcap_snprintf(errbuf, PCAP_ERRBUF_SIZE, "The source string is too long. Cannot handle it correctly."); return -1; } /* * Determine the type of the source (file, local, remote) * There are some differences if pcap_findalldevs_ex() is called to list files and remote adapters. * In the first case, the name of the directory we have to look into must be present (therefore * the 'name' parameter of the pcap_parsesrcstr() is present). * In the second case, the name of the adapter is not required (we need just the host). So, we have * to use a first time this function to get the source type, and a second time to get the appropriate * info, which depends on the source type. */ if (pcap_parsesrcstr(source, &type, NULL, NULL, NULL, errbuf) == -1) return -1; switch (type) { case PCAP_SRC_IFLOCAL: if (pcap_parsesrcstr(source, &type, NULL, NULL, NULL, errbuf) == -1) return -1; /* Initialize temporary string */ tmpstring[PCAP_BUF_SIZE] = 0; /* The user wants to retrieve adapters from a local host */ if (pcap_findalldevs(alldevs, errbuf) == -1) return -1; if (*alldevs == NULL) { pcap_snprintf(errbuf, PCAP_ERRBUF_SIZE, "No interfaces found! Make sure libpcap/Npcap is properly installed" " on the local machine."); return -1; } /* Scan all the interfaces and modify name and description */ /* This is a trick in order to avoid the re-implementation of the pcap_findalldevs here */ dev = *alldevs; while (dev) { char *localdesc, *desc; /* Create the new device identifier */ if (pcap_createsrcstr(tmpstring, PCAP_SRC_IFLOCAL, NULL, NULL, dev->name, errbuf) == -1) return -1; /* Delete the old pointer */ free(dev->name); /* Make a copy of the new device identifier */ dev->name = strdup(tmpstring); if (dev->name == NULL) { pcap_fmt_errmsg_for_errno(errbuf, PCAP_ERRBUF_SIZE, errno, "malloc() failed"); pcap_freealldevs(*alldevs); return -1; } /* * Create the description. */ if ((dev->description == NULL) || (dev->description[0] == 0)) localdesc = dev->name; else localdesc = dev->description; if (pcap_asprintf(&desc, "%s '%s' %s", PCAP_TEXT_SOURCE_ADAPTER, localdesc, PCAP_TEXT_SOURCE_ON_LOCAL_HOST) == -1) { pcap_fmt_errmsg_for_errno(errbuf, PCAP_ERRBUF_SIZE, errno, "malloc() failed"); pcap_freealldevs(*alldevs); return -1; } /* Now overwrite the description */ free(dev->description); dev->description = desc; dev = dev->next; } return 0; case PCAP_SRC_FILE: { #ifdef _WIN32 WIN32_FIND_DATA filedata; HANDLE filehandle; #else struct dirent *filedata; DIR *unixdir; #endif if (pcap_parsesrcstr(source, &type, NULL, NULL, name, errbuf) == -1) return -1; /* Check that the filename is correct */ stringlen = strlen(name); /* The directory must end with '\' in Win32 and '/' in UNIX */ #ifdef _WIN32 #define ENDING_CHAR '\\' #else #define ENDING_CHAR '/' #endif if (name[stringlen - 1] != ENDING_CHAR) { name[stringlen] = ENDING_CHAR; name[stringlen + 1] = 0; stringlen++; } /* Save the path for future reference */ pcap_snprintf(path, sizeof(path), "%s", name); pathlen = strlen(path); #ifdef _WIN32 /* To perform directory listing, Win32 must have an 'asterisk' as ending char */ if (name[stringlen - 1] != '*') { name[stringlen] = '*'; name[stringlen + 1] = 0; } filehandle = FindFirstFile(name, &filedata); if (filehandle == INVALID_HANDLE_VALUE) { pcap_snprintf(errbuf, PCAP_ERRBUF_SIZE, "Error when listing files: does folder '%s' exist?", path); return -1; } #else /* opening the folder */ unixdir= opendir(path); /* get the first file into it */ filedata= readdir(unixdir); if (filedata == NULL) { pcap_snprintf(errbuf, PCAP_ERRBUF_SIZE, "Error when listing files: does folder '%s' exist?", path); return -1; } #endif /* Add all files we find to the list. */ do { #ifdef _WIN32 /* Skip the file if the pathname won't fit in the buffer */ if (pathlen + strlen(filedata.cFileName) >= sizeof(filename)) continue; pcap_snprintf(filename, sizeof(filename), "%s%s", path, filedata.cFileName); #else if (pathlen + strlen(filedata->d_name) >= sizeof(filename)) continue; pcap_snprintf(filename, sizeof(filename), "%s%s", path, filedata->d_name); #endif fp = pcap_open_offline(filename, errbuf); if (fp) { /* allocate the main structure */ dev = (pcap_if_t *)malloc(sizeof(pcap_if_t)); if (dev == NULL) { pcap_fmt_errmsg_for_errno(errbuf, PCAP_ERRBUF_SIZE, errno, "malloc() failed"); pcap_freealldevs(*alldevs); return -1; } /* Initialize the structure to 'zero' */ memset(dev, 0, sizeof(pcap_if_t)); /* Append it to the list. */ if (lastdev == NULL) { /* * List is empty, so it's also * the first device. */ *alldevs = dev; } else { /* * Append after the last device. */ lastdev->next = dev; } /* It's now the last device. */ lastdev = dev; /* Create the new source identifier */ if (pcap_createsrcstr(tmpstring, PCAP_SRC_FILE, NULL, NULL, filename, errbuf) == -1) { pcap_freealldevs(*alldevs); return -1; } dev->name = strdup(tmpstring); if (dev->name == NULL) { pcap_fmt_errmsg_for_errno(errbuf, PCAP_ERRBUF_SIZE, errno, "malloc() failed"); pcap_freealldevs(*alldevs); return -1; } /* * Create the description. */ if (pcap_asprintf(&dev->description, "%s '%s' %s", PCAP_TEXT_SOURCE_FILE, filename, PCAP_TEXT_SOURCE_ON_LOCAL_HOST) == -1) { pcap_fmt_errmsg_for_errno(errbuf, PCAP_ERRBUF_SIZE, errno, "malloc() failed"); pcap_freealldevs(*alldevs); return -1; } pcap_close(fp); } } #ifdef _WIN32 while (FindNextFile(filehandle, &filedata) != 0); #else while ( (filedata= readdir(unixdir)) != NULL); #endif #ifdef _WIN32 /* Close the search handle. */ FindClose(filehandle); #endif return 0; } case PCAP_SRC_IFREMOTE: return pcap_findalldevs_ex_remote(source, auth, alldevs, errbuf); default: pcap_strlcpy(errbuf, "Source type not supported", PCAP_ERRBUF_SIZE); return -1; } }