📄 pcap-linux.c
字号:
int arptype; struct ifreq ifr; do { /* Open the socket */ handle->fd = socket(PF_INET, SOCK_PACKET, htons(ETH_P_ALL)); if (handle->fd == -1) { snprintf(ebuf, PCAP_ERRBUF_SIZE, "socket: %s", pcap_strerror(errno)); break; } /* It worked - we are using the old interface */ handle->md.sock_packet = 1; /* ...which means we get the link-layer header. */ handle->md.cooked = 0; /* Bind to the given device */ if (!device) { strncpy(ebuf, "pcap_open_live: The \"any\" device isn't supported on 2.0[.x]-kernel systems", PCAP_ERRBUF_SIZE); break; } if (iface_bind_old(handle->fd, device, ebuf) == -1) break; /* * Try to get the link-layer type. */ arptype = iface_get_arptype(handle->fd, device, ebuf); if (arptype == -1) break; /* * Try to find the DLT_ type corresponding to that * link-layer type. */ map_arphrd_to_dlt(handle, arptype, 0); if (handle->linktype == -1) { snprintf(ebuf, PCAP_ERRBUF_SIZE, "unknown arptype %d", arptype); break; } /* Go to promisc mode if requested */ if (promisc) { memset(&ifr, 0, sizeof(ifr)); strncpy(ifr.ifr_name, device, sizeof(ifr.ifr_name)); if (ioctl(handle->fd, SIOCGIFFLAGS, &ifr) == -1) { snprintf(ebuf, PCAP_ERRBUF_SIZE, "SIOCGIFFLAGS: %s", pcap_strerror(errno)); break; } if ((ifr.ifr_flags & IFF_PROMISC) == 0) { /* * Promiscuous mode isn't currently on, * so turn it on, and remember that * we should turn it off when the * pcap_t is closed. */ /* * If we haven't already done so, arrange * to have "pcap_close_all()" called when * we exit. */ if (!did_atexit) { if (atexit(pcap_close_all) == -1) { /* * "atexit()" failed; don't * put the interface in * promiscuous mode, just * give up. */ strncpy(ebuf, "atexit failed", PCAP_ERRBUF_SIZE); break; } did_atexit = 1; } ifr.ifr_flags |= IFF_PROMISC; if (ioctl(handle->fd, SIOCSIFFLAGS, &ifr) == -1) { snprintf(ebuf, PCAP_ERRBUF_SIZE, "SIOCSIFFLAGS: %s", pcap_strerror(errno)); break; } handle->md.clear_promisc = 1; /* * Add this to the list of pcaps * to close when we exit. */ handle->md.next = pcaps_to_close; pcaps_to_close = handle; } } /* * Default value for offset to align link-layer payload * on a 4-byte boundary. */ handle->offset = 0; return 1; } while (0); pcap_close_linux(handle); return 0;}/* * Bind the socket associated with FD to the given device using the * interface of the old kernels. */static intiface_bind_old(int fd, const char *device, char *ebuf){ struct sockaddr saddr; int err; socklen_t errlen = sizeof(err); memset(&saddr, 0, sizeof(saddr)); strncpy(saddr.sa_data, device, sizeof(saddr.sa_data)); if (bind(fd, &saddr, sizeof(saddr)) == -1) { snprintf(ebuf, PCAP_ERRBUF_SIZE, "bind: %s", pcap_strerror(errno)); return -1; } /* Any pending errors, e.g., network is down? */ if (getsockopt(fd, SOL_SOCKET, SO_ERROR, &err, &errlen) == -1) { snprintf(ebuf, PCAP_ERRBUF_SIZE, "getsockopt: %s", pcap_strerror(errno)); return -1; } if (err > 0) { snprintf(ebuf, PCAP_ERRBUF_SIZE, "bind: %s", pcap_strerror(err)); return -1; } return 0;}/* ===== System calls available on all supported kernels ============== *//* * Query the kernel for the MTU of the given interface. */static intiface_get_mtu(int fd, const char *device, char *ebuf){ struct ifreq ifr; if (!device) return BIGGER_THAN_ALL_MTUS; memset(&ifr, 0, sizeof(ifr)); strncpy(ifr.ifr_name, device, sizeof(ifr.ifr_name)); if (ioctl(fd, SIOCGIFMTU, &ifr) == -1) { snprintf(ebuf, PCAP_ERRBUF_SIZE, "SIOCGIFMTU: %s", pcap_strerror(errno)); return -1; } return ifr.ifr_mtu;}/* * Get the hardware type of the given interface as ARPHRD_xxx constant. */static intiface_get_arptype(int fd, const char *device, char *ebuf){ struct ifreq ifr; memset(&ifr, 0, sizeof(ifr)); strncpy(ifr.ifr_name, device, sizeof(ifr.ifr_name)); if (ioctl(fd, SIOCGIFHWADDR, &ifr) == -1) { snprintf(ebuf, PCAP_ERRBUF_SIZE, "SIOCGIFHWADDR: %s", pcap_strerror(errno)); return -1; } return ifr.ifr_hwaddr.sa_family;}#ifdef SO_ATTACH_FILTERstatic intfix_program(pcap_t *handle, struct sock_fprog *fcode){ size_t prog_size; register int i; register struct bpf_insn *p; struct bpf_insn *f; int len; /* * Make a copy of the filter, and modify that copy if * necessary. */ prog_size = sizeof(*handle->fcode.bf_insns) * handle->fcode.bf_len; len = handle->fcode.bf_len; f = (struct bpf_insn *)malloc(prog_size); if (f == NULL) { snprintf(handle->errbuf, sizeof(handle->errbuf), "malloc: %s", pcap_strerror(errno)); return -1; } memcpy(f, handle->fcode.bf_insns, prog_size); fcode->len = len; fcode->filter = (struct sock_filter *) f; for (i = 0; i < len; ++i) { p = &f[i]; /* * What type of instruction is this? */ switch (BPF_CLASS(p->code)) { case BPF_RET: /* * It's a return instruction; is the snapshot * length a constant, rather than the contents * of the accumulator? */ if (BPF_MODE(p->code) == BPF_K) { /* * Yes - if the value to be returned, * i.e. the snapshot length, is anything * other than 0, make it 65535, so that * the packet is truncated by "recvfrom()", * not by the filter. * * XXX - there's nothing we can easily do * if it's getting the value from the * accumulator; we'd have to insert * code to force non-zero values to be * 65535. */ if (p->k != 0) p->k = 65535; } break; case BPF_LD: case BPF_LDX: /* * It's a load instruction; is it loading * from the packet? */ switch (BPF_MODE(p->code)) { case BPF_ABS: case BPF_IND: case BPF_MSH: /* * Yes; are we in cooked mode? */ if (handle->md.cooked) { /* * Yes, so we need to fix this * instruction. */ if (fix_offset(p) < 0) { /* * We failed to do so. * Return 0, so our caller * knows to punt to userland. */ return 0; } } break; } break; } } return 1; /* we succeeded */}static intfix_offset(struct bpf_insn *p){ /* * What's the offset? */ if (p->k >= SLL_HDR_LEN) { /* * It's within the link-layer payload; that starts at an * offset of 0, as far as the kernel packet filter is * concerned, so subtract the length of the link-layer * header. */ p->k -= SLL_HDR_LEN; } else if (p->k == 14) { /* * It's the protocol field; map it to the special magic * kernel offset for that field. */ p->k = SKF_AD_OFF + SKF_AD_PROTOCOL; } else { /* * It's within the header, but it's not one of those * fields; we can't do that in the kernel, so punt * to userland. */ return -1; } return 0;}static intset_kernel_filter(pcap_t *handle, struct sock_fprog *fcode){ int total_filter_on = 0; int save_mode; int ret; int save_errno; /* * The socket filter code doesn't discard all packets queued * up on the socket when the filter is changed; this means * that packets that don't match the new filter may show up * after the new filter is put onto the socket, if those * packets haven't yet been read. * * This means, for example, that if you do a tcpdump capture * with a filter, the first few packets in the capture might * be packets that wouldn't have passed the filter. * * We therefore discard all packets queued up on the socket * when setting a kernel filter. (This isn't an issue for * userland filters, as the userland filtering is done after * packets are queued up.) * * To flush those packets, we put the socket in read-only mode, * and read packets from the socket until there are no more to * read. * * In order to keep that from being an infinite loop - i.e., * to keep more packets from arriving while we're draining * the queue - we put the "total filter", which is a filter * that rejects all packets, onto the socket before draining * the queue. * * This code deliberately ignores any errors, so that you may * get bogus packets if an error occurs, rather than having * the filtering done in userland even if it could have been * done in the kernel. */ if (setsockopt(handle->fd, SOL_SOCKET, SO_ATTACH_FILTER, &total_fcode, sizeof(total_fcode)) == 0) { char drain[1]; /* * Note that we've put the total filter onto the socket. */ total_filter_on = 1; /* * Save the socket's current mode, and put it in * non-blocking mode; we drain it by reading packets * until we get an error (which is normally a * "nothing more to be read" error). */ save_mode = fcntl(handle->fd, F_GETFL, 0); if (save_mode != -1 && fcntl(handle->fd, F_SETFL, save_mode | O_NONBLOCK) >= 0) { while (recv(handle->fd, &drain, sizeof drain, MSG_TRUNC) >= 0) ; save_errno = errno; fcntl(handle->fd, F_SETFL, save_mode); if (save_errno != EAGAIN) { /* Fatal error */ reset_kernel_filter(handle); snprintf(handle->errbuf, sizeof(handle->errbuf), "recv: %s", pcap_strerror(save_errno)); return -2; } } } /* * Now attach the new filter. */ ret = setsockopt(handle->fd, SOL_SOCKET, SO_ATTACH_FILTER, fcode, sizeof(*fcode)); if (ret == -1 && total_filter_on) { /* * Well, we couldn't set that filter on the socket, * but we could set the total filter on the socket. * * This could, for example, mean that the filter was * too big to put into the kernel, so we'll have to * filter in userland; in any case, we'll be doing * filtering in userland, so we need to remove the * total filter so we see packets. */ save_errno = errno; /* * XXX - if this fails, we're really screwed; * we have the total filter on the socket, * and it won't come off. What do we do then? */ reset_kernel_filter(handle); errno = save_errno; } return ret;}static intreset_kernel_filter(pcap_t *handle){ /* * setsockopt() barfs unless it get a dummy parameter. * valgrind whines unless the value is initialized, * as it has no idea that setsockopt() ignores its * parameter. */ int dummy = 0; return setsockopt(handle->fd, SOL_SOCKET, SO_DETACH_FILTER, &dummy, sizeof(dummy));}#endif
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -