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 + -
显示快捷键?