pcap-bpf.c
来自「Ubuntu packages of security software。 相」· C语言 代码 · 共 1,135 行 · 第 1/2 页
C
1,135 行
return (fd);}/* * We include the OS's <net/bpf.h>, not our "pcap-bpf.h", so we probably * don't get DLT_DOCSIS defined. */#ifndef DLT_DOCSIS#define DLT_DOCSIS 143#endifpcap_t *pcap_open_live(const char *device, int snaplen, int promisc, int to_ms, char *ebuf){ int fd; struct ifreq ifr; struct bpf_version bv;#ifdef BIOCGDLTLIST struct bpf_dltlist bdl;#endif#if defined(BIOCGHDRCMPLT) && defined(BIOCSHDRCMPLT) u_int spoof_eth_src = 1;#endif u_int v; pcap_t *p; struct bpf_insn total_insn; struct bpf_program total_prog; struct utsname osinfo;#ifdef HAVE_DAG_API if (strstr(device, "dag")) { return dag_open_live(device, snaplen, promisc, to_ms, ebuf); }#endif /* HAVE_DAG_API */#ifdef BIOCGDLTLIST memset(&bdl, 0, sizeof(bdl));#endif p = (pcap_t *)malloc(sizeof(*p)); if (p == NULL) { snprintf(ebuf, PCAP_ERRBUF_SIZE, "malloc: %s", pcap_strerror(errno)); return (NULL); } memset(p, 0, sizeof(*p)); fd = bpf_open(p, ebuf); if (fd < 0) goto bad; p->fd = fd; p->snapshot = snaplen; if (ioctl(fd, BIOCVERSION, (caddr_t)&bv) < 0) { snprintf(ebuf, PCAP_ERRBUF_SIZE, "BIOCVERSION: %s", pcap_strerror(errno)); goto bad; } if (bv.bv_major != BPF_MAJOR_VERSION || bv.bv_minor < BPF_MINOR_VERSION) { snprintf(ebuf, PCAP_ERRBUF_SIZE, "kernel bpf filter out of date"); goto bad; } /* * Try finding a good size for the buffer; 32768 may be too * big, so keep cutting it in half until we find a size * that works, or run out of sizes to try. If the default * is larger, don't make it smaller. * * XXX - there should be a user-accessible hook to set the * initial buffer size. */ if ((ioctl(fd, BIOCGBLEN, (caddr_t)&v) < 0) || v < 32768) v = 32768; for ( ; v != 0; v >>= 1) { /* Ignore the return value - this is because the call fails * on BPF systems that don't have kernel malloc. And if * the call fails, it's no big deal, we just continue to * use the standard buffer size. */ (void) ioctl(fd, BIOCSBLEN, (caddr_t)&v); (void)strncpy(ifr.ifr_name, device, sizeof(ifr.ifr_name)); if (ioctl(fd, BIOCSETIF, (caddr_t)&ifr) >= 0) break; /* that size worked; we're done */ if (errno != ENOBUFS) { snprintf(ebuf, PCAP_ERRBUF_SIZE, "BIOCSETIF: %s: %s", device, pcap_strerror(errno)); goto bad; } } if (v == 0) { snprintf(ebuf, PCAP_ERRBUF_SIZE, "BIOCSBLEN: %s: No buffer size worked", device); goto bad; } /* Get the data link layer type. */ if (ioctl(fd, BIOCGDLT, (caddr_t)&v) < 0) { snprintf(ebuf, PCAP_ERRBUF_SIZE, "BIOCGDLT: %s", pcap_strerror(errno)); goto bad; }#ifdef _AIX /* * AIX's BPF returns IFF_ types, not DLT_ types, in BIOCGDLT. */ switch (v) { case IFT_ETHER: case IFT_ISO88023: v = DLT_EN10MB; break; case IFT_FDDI: v = DLT_FDDI; break; case IFT_ISO88025: v = DLT_IEEE802; break; case IFT_LOOP: v = DLT_NULL; break; default: /* * We don't know what to map this to yet. */ snprintf(ebuf, PCAP_ERRBUF_SIZE, "unknown interface type %u", v); goto bad; }#endif#if _BSDI_VERSION - 0 >= 199510 /* The SLIP and PPP link layer header changed in BSD/OS 2.1 */ switch (v) { case DLT_SLIP: v = DLT_SLIP_BSDOS; break; case DLT_PPP: v = DLT_PPP_BSDOS; break; case 11: /*DLT_FR*/ v = DLT_FRELAY; break; case 12: /*DLT_C_HDLC*/ v = DLT_CHDLC; break; }#endif#ifdef PCAP_FDDIPAD if (v == DLT_FDDI) p->fddipad = PCAP_FDDIPAD; else p->fddipad = 0;#endif p->linktype = v;#ifdef BIOCGDLTLIST /* * We know the default link type -- now determine all the DLTs * this interface supports. If this fails with EINVAL, it's * not fatal; we just don't get to use the feature later. */ if (ioctl(fd, BIOCGDLTLIST, (caddr_t)&bdl) == 0) { u_int i; int is_ethernet; bdl.bfl_list = (u_int *) malloc(sizeof(u_int) * bdl.bfl_len + 1); if (bdl.bfl_list == NULL) { (void)snprintf(ebuf, PCAP_ERRBUF_SIZE, "malloc: %s", pcap_strerror(errno)); goto bad; } if (ioctl(fd, BIOCGDLTLIST, (caddr_t)&bdl) < 0) { (void)snprintf(ebuf, PCAP_ERRBUF_SIZE, "BIOCGDLTLIST: %s", pcap_strerror(errno)); free(bdl.bfl_list); goto bad; } /* * OK, for real Ethernet devices, add DLT_DOCSIS to the * list, 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). * * A "real Ethernet device" is defined here as a device * that has a link-layer type of DLT_EN10MB and that has * no alternate link-layer types; that's done to exclude * 802.11 interfaces (which might or might not be the * right thing to do, but I suspect it is - Ethernet <-> * 802.11 bridges would probably badly mishandle frames * that don't have Ethernet headers). */ if (p->linktype == DLT_EN10MB) { is_ethernet = 1; for (i = 0; i < bdl.bfl_len; i++) { if (bdl.bfl_list[i] != DLT_EN10MB) { is_ethernet = 0; break; } } if (is_ethernet) { /* * We reserved one more slot at the end of * the list. */ bdl.bfl_list[bdl.bfl_len] = DLT_DOCSIS; bdl.bfl_len++; } } p->dlt_count = bdl.bfl_len; p->dlt_list = bdl.bfl_list; } else { if (errno != EINVAL) { (void)snprintf(ebuf, PCAP_ERRBUF_SIZE, "BIOCGDLTLIST: %s", pcap_strerror(errno)); goto bad; } }#endif /* * If this is an Ethernet device, and we don't have a DLT_ list, * give it a list with DLT_EN10MB and DLT_DOCSIS. (That'd give * 802.11 interfaces DLT_DOCSIS, which isn't the right thing to * do, but there's not much we can do about that without finding * some other way of determining whether it's an Ethernet or 802.11 * device.) */ if (p->linktype == DLT_EN10MB && p->dlt_count == 0) { 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; } } #if defined(BIOCGHDRCMPLT) && defined(BIOCSHDRCMPLT) /* * Do a BIOCSHDRCMPLT, if defined, to turn that flag on, so * the link-layer source address isn't forcibly overwritten. * (Should we ignore errors? Should we do this only if * we're open for writing?) * * XXX - I seem to remember some packet-sending bug in some * BSDs - check CVS log for "bpf.c"? */ if (ioctl(fd, BIOCSHDRCMPLT, &spoof_eth_src) == -1) { (void)snprintf(ebuf, PCAP_ERRBUF_SIZE, "BIOCSHDRCMPLT: %s", pcap_strerror(errno)); goto bad; }#endif /* set timeout */ if (to_ms != 0) { /* * XXX - is this seconds/nanoseconds in AIX? * (Treating it as such doesn't fix the timeout * problem described below.) */ struct timeval to; to.tv_sec = to_ms / 1000; to.tv_usec = (to_ms * 1000) % 1000000; if (ioctl(p->fd, BIOCSRTIMEOUT, (caddr_t)&to) < 0) { snprintf(ebuf, PCAP_ERRBUF_SIZE, "BIOCSRTIMEOUT: %s", pcap_strerror(errno)); goto bad; } }#ifdef _AIX#ifdef BIOCIMMEDIATE /* * Darren Reed notes that * * On AIX (4.2 at least), if BIOCIMMEDIATE is not set, the * timeout appears to be ignored and it waits until the buffer * is filled before returning. The result of not having it * set is almost worse than useless if your BPF filter * is reducing things to only a few packets (i.e. one every * second or so). * * so we turn BIOCIMMEDIATE mode on if this is AIX. * * We don't turn it on for other platforms, as that means we * get woken up for every packet, which may not be what we want; * in the Winter 1993 USENIX paper on BPF, they say: * * Since a process might want to look at every packet on a * network and the time between packets can be only a few * microseconds, it is not possible to do a read system call * per packet and BPF must collect the data from several * packets and return it as a unit when the monitoring * application does a read. * * which I infer is the reason for the timeout - it means we * wait that amount of time, in the hopes that more packets * will arrive and we'll get them all with one read. * * Setting BIOCIMMEDIATE mode on FreeBSD (and probably other * BSDs) causes the timeout to be ignored. * * On the other hand, some platforms (e.g., Linux) don't support * timeouts, they just hand stuff to you as soon as it arrives; * if that doesn't cause a problem on those platforms, it may * be OK to have BIOCIMMEDIATE mode on BSD as well. * * (Note, though, that applications may depend on the read * completing, even if no packets have arrived, when the timeout * expires, e.g. GUI applications that have to check for input * while waiting for packets to arrive; a non-zero timeout * prevents "select()" from working right on FreeBSD and * possibly other BSDs, as the timer doesn't start until a * "read()" is done, so the timer isn't in effect if the * application is blocked on a "select()", and the "select()" * doesn't get woken up for a BPF device until the buffer * fills up.) */ v = 1; if (ioctl(p->fd, BIOCIMMEDIATE, &v) < 0) { snprintf(ebuf, PCAP_ERRBUF_SIZE, "BIOCIMMEDIATE: %s", pcap_strerror(errno)); goto bad; }#endif /* BIOCIMMEDIATE */#endif /* _AIX */ if (promisc) { /* set promiscuous mode, okay if it fails */ if (ioctl(p->fd, BIOCPROMISC, NULL) < 0) { snprintf(ebuf, PCAP_ERRBUF_SIZE, "BIOCPROMISC: %s", pcap_strerror(errno)); } } if (ioctl(fd, BIOCGBLEN, (caddr_t)&v) < 0) { snprintf(ebuf, PCAP_ERRBUF_SIZE, "BIOCGBLEN: %s", pcap_strerror(errno)); goto bad; } p->bufsize = v; p->buffer = (u_char *)malloc(p->bufsize); if (p->buffer == NULL) { snprintf(ebuf, PCAP_ERRBUF_SIZE, "malloc: %s", pcap_strerror(errno)); goto bad; }#ifdef _AIX /* For some strange reason this seems to prevent the EFAULT * problems we have experienced from AIX BPF. */ memset(p->buffer, 0x0, p->bufsize);#endif /* * If there's no filter program installed, there's * no indication to the kernel of what the snapshot * length should be, so no snapshotting is done. * * Therefore, when we open the device, we install * an "accept everything" filter with the specified * snapshot length. */ total_insn.code = (u_short)(BPF_RET | BPF_K); total_insn.jt = 0; total_insn.jf = 0; total_insn.k = snaplen; total_prog.bf_len = 1; total_prog.bf_insns = &total_insn; if (ioctl(p->fd, BIOCSETF, (caddr_t)&total_prog) < 0) { snprintf(ebuf, PCAP_ERRBUF_SIZE, "BIOCSETF: %s", pcap_strerror(errno)); goto bad; } /* * On most BPF platforms, either you can do a "select()" or * "poll()" on a BPF file descriptor and it works correctly, * or you can do it and it will return "readable" if the * hold buffer is full but not if the timeout expires *and* * a non-blocking read will, if the hold buffer is empty * but the store buffer isn't empty, rotate the buffers * and return what packets are available. * * In the latter case, the fact that a non-blocking read * will give you the available packets means you can work * around the failure of "select()" and "poll()" to wake up * and return "readable" when the timeout expires by using * the timeout as the "select()" or "poll()" timeout, putting * the BPF descriptor into non-blocking mode, and read from * it regardless of whether "select()" reports it as readable * or not. * * However, in FreeBSD 4.3 and 4.4, "select()" and "poll()" * won't wake up and return "readable" if the timer expires * and non-blocking reads return EWOULDBLOCK if the hold * buffer is empty, even if the store buffer is non-empty. * * This means the workaround in question won't work. * * Therefore, on FreeBSD 4.3 and 4.4, we set "p->selectable_fd" * to -1, which means "sorry, you can't use 'select()' or 'poll()' * here". On all other BPF platforms, we set it to the FD for * the BPF device; in NetBSD, OpenBSD, and Darwin, a non-blocking * read will, if the hold buffer is empty and the store buffer * isn't empty, rotate the buffers and return what packets are * there (and in sufficiently recent versions of OpenBSD * "select()" and "poll()" should work correctly). * * XXX - what about AIX? */ p->selectable_fd = p->fd; /* assume select() works until we know otherwise */ if (uname(&osinfo) == 0) { /* * We can check what OS this is. */ if (strcmp(osinfo.sysname, "FreeBSD") == 0) { if (strncmp(osinfo.release, "4.3-", 4) == 0 || strncmp(osinfo.release, "4.4-", 4) == 0) p->selectable_fd = -1; } } p->read_op = pcap_read_bpf; p->inject_op = pcap_inject_bpf; p->setfilter_op = pcap_setfilter_bpf; p->setdirection_op = pcap_setdirection_bpf; p->set_datalink_op = pcap_set_datalink_bpf; p->getnonblock_op = pcap_getnonblock_fd; p->setnonblock_op = pcap_setnonblock_fd; p->stats_op = pcap_stats_bpf; p->close_op = pcap_close_common; return (p); bad: (void)close(fd); if (p->dlt_list != NULL) free(p->dlt_list); free(p); return (NULL);}intpcap_platform_finddevs(pcap_if_t **alldevsp, char *errbuf){#ifdef HAVE_DAG_API if (dag_platform_finddevs(alldevsp, errbuf) < 0) return (-1);#endif /* HAVE_DAG_API */ return (0);}static intpcap_setfilter_bpf(pcap_t *p, struct bpf_program *fp){ /* * It looks that BPF code generated by gen_protochain() is not * compatible with some of kernel BPF code (for example BSD/OS 3.1). * Take a safer side for now. */ if (no_optimize) { /* * XXX - what if we already have a filter in the kernel? */ if (install_bpf_program(p, fp) < 0) return (-1); p->md.use_bpf = 0; /* filtering in userland */ return (0); } /* * Free any user-mode filter we might happen to have installed. */ pcap_freecode(&p->fcode); /* * Try to install the kernel filter. */ if (ioctl(p->fd, BIOCSETF, (caddr_t)fp) < 0) { snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "BIOCSETF: %s", pcap_strerror(errno)); return (-1); } p->md.use_bpf = 1; /* filtering in the kernel */ /* * Discard any previously-received packets, as they might have * passed whatever filter was formerly in effect, but might * not pass this filter (BIOCSETF discards packets buffered * in the kernel, so you can lose packets in any case). */ p->cc = 0; return (0);}/* * Set direction flag: Which packets do we accept on a forwarding * single device? IN, OUT or both? */static intpcap_setdirection_bpf(pcap_t *p, pcap_direction_t d){#ifdef BIOCSSEESENT u_int seesent;#endif /* * We don't support PCAP_D_OUT. */ if (d == PCAP_D_OUT) { snprintf(p->errbuf, sizeof(p->errbuf), "Setting direction to PCAP_D_OUT is not supported on BPF"); return -1; }#ifdef BIOCSSEESENT seesent = (d == PCAP_D_INOUT); if (ioctl(p->fd, BIOCSSEESENT, &seesent) == -1) { (void) snprintf(p->errbuf, sizeof(p->errbuf), "Cannot set direction to %s: %s", (d == PCAP_D_INOUT) ? "PCAP_D_INOUT" : "PCAP_D_IN", strerror(errno)); return (-1); } return (0);#else (void) snprintf(p->errbuf, sizeof(p->errbuf), "This system doesn't support BIOCSSEESENT, so the direction can't be set"); return (-1);#endif}static intpcap_set_datalink_bpf(pcap_t *p, int dlt){#ifdef BIOCSDLT if (ioctl(p->fd, BIOCSDLT, &dlt) == -1) { (void) snprintf(p->errbuf, sizeof(p->errbuf), "Cannot set DLT %d: %s", dlt, strerror(errno)); return (-1); }#endif return (0);}
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?