pcap-bpf.c

来自「Ubuntu packages of security software。 相」· C语言 代码 · 共 1,135 行 · 第 1/2 页

C
1,135
字号
/* * Copyright (c) 1993, 1994, 1995, 1996, 1998 *	The Regents of the University of California.  All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that: (1) source code distributions * retain the above copyright notice and this paragraph in its entirety, (2) * distributions including binary code include the above copyright notice and * this paragraph in its entirety in the documentation or other materials * provided with the distribution, and (3) all advertising materials mentioning * features or use of this software display the following acknowledgement: * ``This product includes software developed by the University of California, * Lawrence Berkeley Laboratory and its contributors.'' Neither the name of * the University nor the names of its contributors may be used to endorse * or promote products derived from this software without specific prior * written permission. * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. */#ifndef lintstatic const char rcsid[] _U_ =    "@(#) $Header: /tcpdump/master/libpcap/pcap-bpf.c,v 1.86.2.8 2005/07/10 10:55:31 guy Exp $ (LBL)";#endif#ifdef HAVE_CONFIG_H#include "config.h"#endif#include <sys/param.h>			/* optionally get BSD define */#include <sys/time.h>#include <sys/timeb.h>#include <sys/socket.h>#include <sys/file.h>#include <sys/ioctl.h>#include <sys/utsname.h>#include <net/if.h>#ifdef _AIX/* * Make "pcap.h" not include "pcap-bpf.h"; we are going to include the * native OS version, as we need "struct bpf_config" from it. */#define PCAP_DONT_INCLUDE_PCAP_BPF_H#include <sys/types.h>/* * Prevent bpf.h from redefining the DLT_ values to their * IFT_ values, as we're going to return the standard libpcap * values, not IBM's non-standard IFT_ values. */#undef _AIX#include <net/bpf.h>#define _AIX#include <net/if_types.h>		/* for IFT_ values */#include <sys/sysconfig.h>#include <sys/device.h>#include <sys/cfgodm.h>#include <cf.h>#ifdef __64BIT__#define domakedev makedev64#define getmajor major64#define bpf_hdr bpf_hdr32#else /* __64BIT__ */#define domakedev makedev#define getmajor major#endif /* __64BIT__ */#define BPF_NAME "bpf"#define BPF_MINORS 4#define DRIVER_PATH "/usr/lib/drivers"#define BPF_NODE "/dev/bpf"static int bpfloadedflag = 0;static int odmlockid = 0;#else /* _AIX */#include <net/bpf.h>#endif /* _AIX */#include <ctype.h>#include <errno.h>#include <netdb.h>#include <stdio.h>#include <stdlib.h>#include <string.h>#include <unistd.h>#include "pcap-int.h"#ifdef HAVE_DAG_API#include "pcap-dag.h"#endif /* HAVE_DAG_API */#ifdef HAVE_OS_PROTO_H#include "os-proto.h"#endif#include "gencode.h"	/* for "no_optimize" */static int pcap_setfilter_bpf(pcap_t *p, struct bpf_program *fp);static int pcap_setdirection_bpf(pcap_t *, pcap_direction_t);static int pcap_set_datalink_bpf(pcap_t *p, int dlt);static intpcap_stats_bpf(pcap_t *p, struct pcap_stat *ps){	struct bpf_stat s;	/*	 * "ps_recv" counts packets handed to the filter, not packets	 * that passed the filter.  This includes packets later dropped	 * because we ran out of buffer space.	 *	 * "ps_drop" counts packets dropped inside the BPF device	 * because we ran out of buffer space.  It doesn't count	 * packets dropped by the interface driver.  It counts	 * only packets that passed the filter.	 *	 * Both statistics include packets not yet read from the kernel	 * by libpcap, and thus not yet seen by the application.	 */	if (ioctl(p->fd, BIOCGSTATS, (caddr_t)&s) < 0) {		snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "BIOCGSTATS: %s",		    pcap_strerror(errno));		return (-1);	}	ps->ps_recv = s.bs_recv;	ps->ps_drop = s.bs_drop;	return (0);}static intpcap_read_bpf(pcap_t *p, int cnt, pcap_handler callback, u_char *user){	int cc;	int n = 0;	register u_char *bp, *ep;	u_char *datap;	struct bpf_insn *fcode;#ifdef PCAP_FDDIPAD	register int pad;#endif	fcode = p->md.use_bpf ? NULL : p->fcode.bf_insns; again:	/*	 * Has "pcap_breakloop()" been called?	 */	if (p->break_loop) {		/*		 * Yes - clear the flag that indicates that it		 * has, and return -2 to indicate that we were		 * told to break out of the loop.		 */		p->break_loop = 0;		return (-2);	}	cc = p->cc;	if (p->cc == 0) {		cc = read(p->fd, (char *)p->buffer, p->bufsize);		if (cc < 0) {			/* Don't choke when we get ptraced */			switch (errno) {			case EINTR:				goto again;#ifdef _AIX			case EFAULT:				/*				 * Sigh.  More AIX wonderfulness.				 *				 * For some unknown reason the uiomove()				 * operation in the bpf kernel extension				 * used to copy the buffer into user 				 * space sometimes returns EFAULT. I have				 * no idea why this is the case given that				 * a kernel debugger shows the user buffer 				 * is correct. This problem appears to 				 * be mostly mitigated by the memset of 				 * the buffer before it is first used. 				 * Very strange.... Shaun Clowes				 *				 * In any case this means that we shouldn't 				 * treat EFAULT as a fatal error; as we				 * don't have an API for returning				 * a "some packets were dropped since				 * the last packet you saw" indication,				 * we just ignore EFAULT and keep reading.				 */				goto again;#endif   			case EWOULDBLOCK:				return (0);#if defined(sun) && !defined(BSD)			/*			 * Due to a SunOS bug, after 2^31 bytes, the kernel			 * file offset overflows and read fails with EINVAL.			 * The lseek() to 0 will fix things.			 */			case EINVAL:				if (lseek(p->fd, 0L, SEEK_CUR) +				    p->bufsize < 0) {					(void)lseek(p->fd, 0L, SEEK_SET);					goto again;				}				/* fall through */#endif			}			snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "read: %s",			    pcap_strerror(errno));			return (-1);		}		bp = p->buffer;	} else		bp = p->bp;	/*	 * Loop through each packet.	 */#define bhp ((struct bpf_hdr *)bp)	ep = bp + cc;#ifdef PCAP_FDDIPAD	pad = p->fddipad;#endif	while (bp < ep) {		register int caplen, hdrlen;		/*		 * 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->bp = bp;				p->cc = ep - bp;				return (n);			}		}		caplen = bhp->bh_caplen;		hdrlen = bhp->bh_hdrlen;		datap = bp + hdrlen;		/*		 * Short-circuit evaluation: if using BPF filter		 * in kernel, no need to do it now.		 *#ifdef PCAP_FDDIPAD		 * Note: the filter code was generated assuming		 * that p->fddipad was the amount of padding		 * before the header, as that's what's required		 * in the kernel, so we run the filter before		 * skipping that padding.#endif		 */		if (fcode == NULL ||		    bpf_filter(fcode, datap, bhp->bh_datalen, caplen)) {			struct pcap_pkthdr pkthdr;			pkthdr.ts.tv_sec = bhp->bh_tstamp.tv_sec;#ifdef _AIX			/*			 * AIX's BPF returns seconds/nanoseconds time			 * stamps, not seconds/microseconds time stamps.			 */			pkthdr.ts.tv_usec = bhp->bh_tstamp.tv_usec/1000;#else			pkthdr.ts.tv_usec = bhp->bh_tstamp.tv_usec;#endif#ifdef PCAP_FDDIPAD			if (caplen > pad)				pkthdr.caplen = caplen - pad;			else				pkthdr.caplen = 0;			if (bhp->bh_datalen > pad)				pkthdr.len = bhp->bh_datalen - pad;			else				pkthdr.len = 0;			datap += pad;#else			pkthdr.caplen = caplen;			pkthdr.len = bhp->bh_datalen;#endif			(*callback)(user, &pkthdr, datap);			bp += BPF_WORDALIGN(caplen + hdrlen);			if (++n >= cnt && cnt > 0) {				p->bp = bp;				p->cc = ep - bp;				return (n);			}		} else {			/*			 * Skip this packet.			 */			bp += BPF_WORDALIGN(caplen + hdrlen);		}	}#undef bhp	p->cc = 0;	return (n);}static intpcap_inject_bpf(pcap_t *p, const void *buf, size_t size){	int ret;	ret = write(p->fd, buf, size);#ifdef __APPLE__	if (ret == -1 && errno == EAFNOSUPPORT) {		/*		 * In Mac OS X, there's a bug wherein setting the		 * BIOCSHDRCMPLT flag causes writes to fail; see,		 * for example:		 *		 *	http://cerberus.sourcefire.com/~jeff/archives/patches/macosx/BIOCSHDRCMPLT-10.3.3.patch		 *		 * So, if, on OS X, we get EAFNOSUPPORT from the write, we		 * assume it's due to that bug, and turn off that flag		 * and try again.  If we succeed, it either means that		 * somebody applied the fix from that URL, or other patches		 * for that bug from		 *		 *	http://cerberus.sourcefire.com/~jeff/archives/patches/macosx/		 *		 * and are running a Darwin kernel with those fixes, or		 * that Apple fixed the problem in some OS X release.		 */		u_int spoof_eth_src = 0;		if (ioctl(p->fd, BIOCSHDRCMPLT, &spoof_eth_src) == -1) {			(void)snprintf(p->errbuf, PCAP_ERRBUF_SIZE,			    "send: can't turn off BIOCSHDRCMPLT: %s",			    pcap_strerror(errno));			return (-1);		}		/*		 * Now try the write again.		 */		ret = write(p->fd, buf, size);	}#endif /* __APPLE__ */	if (ret == -1) {		snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "send: %s",		    pcap_strerror(errno));		return (-1);	}	return (ret);}#ifdef _AIXstatic int bpf_odminit(char *errbuf){	char *errstr;	if (odm_initialize() == -1) {		if (odm_err_msg(odmerrno, &errstr) == -1)			errstr = "Unknown error";		snprintf(errbuf, PCAP_ERRBUF_SIZE,		    "bpf_load: odm_initialize failed: %s",		    errstr);		return (-1);	}	if ((odmlockid = odm_lock("/etc/objrepos/config_lock", ODM_WAIT)) == -1) {		if (odm_err_msg(odmerrno, &errstr) == -1)			errstr = "Unknown error";		snprintf(errbuf, PCAP_ERRBUF_SIZE,		    "bpf_load: odm_lock of /etc/objrepos/config_lock failed: %s",		    errstr);		return (-1);	}	return (0);}static int bpf_odmcleanup(char *errbuf){	char *errstr;	if (odm_unlock(odmlockid) == -1) {		if (odm_err_msg(odmerrno, &errstr) == -1)			errstr = "Unknown error";		snprintf(errbuf, PCAP_ERRBUF_SIZE,		    "bpf_load: odm_unlock failed: %s",		    errstr);		return (-1);	}	if (odm_terminate() == -1) {		if (odm_err_msg(odmerrno, &errstr) == -1)			errstr = "Unknown error";		snprintf(errbuf, PCAP_ERRBUF_SIZE,		    "bpf_load: odm_terminate failed: %s",		    errstr);		return (-1);	}	return (0);}static intbpf_load(char *errbuf){	long major;	int *minors;	int numminors, i, rc;	char buf[1024];	struct stat sbuf;	struct bpf_config cfg_bpf;	struct cfg_load cfg_ld;	struct cfg_kmod cfg_km;	/*	 * This is very very close to what happens in the real implementation	 * but I've fixed some (unlikely) bug situations.	 */	if (bpfloadedflag)		return (0);	if (bpf_odminit(errbuf) != 0)		return (-1);	major = genmajor(BPF_NAME);	if (major == -1) {		snprintf(errbuf, PCAP_ERRBUF_SIZE,		    "bpf_load: genmajor failed: %s", pcap_strerror(errno));		return (-1);	}	minors = getminor(major, &numminors, BPF_NAME);	if (!minors) {		minors = genminor("bpf", major, 0, BPF_MINORS, 1, 1);		if (!minors) {			snprintf(errbuf, PCAP_ERRBUF_SIZE,			    "bpf_load: genminor failed: %s",			    pcap_strerror(errno));			return (-1);		}	}	if (bpf_odmcleanup(errbuf))		return (-1);	rc = stat(BPF_NODE "0", &sbuf);	if (rc == -1 && errno != ENOENT) {		snprintf(errbuf, PCAP_ERRBUF_SIZE,		    "bpf_load: can't stat %s: %s",		    BPF_NODE "0", pcap_strerror(errno));		return (-1);	}	if (rc == -1 || getmajor(sbuf.st_rdev) != major) {		for (i = 0; i < BPF_MINORS; i++) {			sprintf(buf, "%s%d", BPF_NODE, i);			unlink(buf);			if (mknod(buf, S_IRUSR | S_IFCHR, domakedev(major, i)) == -1) {				snprintf(errbuf, PCAP_ERRBUF_SIZE,				    "bpf_load: can't mknod %s: %s",				    buf, pcap_strerror(errno));				return (-1);			}		}	}	/* Check if the driver is loaded */	memset(&cfg_ld, 0x0, sizeof(cfg_ld));	cfg_ld.path = buf;	sprintf(cfg_ld.path, "%s/%s", DRIVER_PATH, BPF_NAME);	if ((sysconfig(SYS_QUERYLOAD, (void *)&cfg_ld, sizeof(cfg_ld)) == -1) ||	    (cfg_ld.kmid == 0)) {		/* Driver isn't loaded, load it now */		if (sysconfig(SYS_SINGLELOAD, (void *)&cfg_ld, sizeof(cfg_ld)) == -1) {			snprintf(errbuf, PCAP_ERRBUF_SIZE,			    "bpf_load: could not load driver: %s",			    strerror(errno));			return (-1);		}	}	/* Configure the driver */	cfg_km.cmd = CFG_INIT;	cfg_km.kmid = cfg_ld.kmid;	cfg_km.mdilen = sizeof(cfg_bpf);	cfg_km.mdiptr = (void *)&cfg_bpf; 	for (i = 0; i < BPF_MINORS; i++) {		cfg_bpf.devno = domakedev(major, i);		if (sysconfig(SYS_CFGKMOD, (void *)&cfg_km, sizeof(cfg_km)) == -1) {			snprintf(errbuf, PCAP_ERRBUF_SIZE,			    "bpf_load: could not configure driver: %s",			    strerror(errno));			return (-1);		}	}		bpfloadedflag = 1;	return (0);}#endifstatic inline intbpf_open(pcap_t *p, char *errbuf){	int fd;	int n = 0;	char device[sizeof "/dev/bpf0000000000"];#ifdef _AIX	/*	 * Load the bpf driver, if it isn't already loaded,	 * and create the BPF device entries, if they don't	 * already exist.	 */	if (bpf_load(errbuf) == -1)		return (-1);#endif	/*	 * Go through all the minors and find one that isn't in use.	 */	do {		(void)snprintf(device, sizeof(device), "/dev/bpf%d", n++);		/*		 * Initially try a read/write open (to allow the inject		 * method to work).  If that fails due to permission		 * issues, fall back to read-only.  This allows a		 * non-root user to be granted specific access to pcap		 * capabilities via file permissions.		 *		 * XXX - we should have an API that has a flag that		 * controls whether to open read-only or read-write,		 * so that denial of permission to send (or inability		 * to send, if sending packets isn't supported on		 * the device in question) can be indicated at open		 * time.		 */		fd = open(device, O_RDWR);		if (fd == -1 && errno == EACCES)			fd = open(device, O_RDONLY);	} while (fd < 0 && errno == EBUSY);	/*	 * XXX better message for all minors used	 */	if (fd < 0)		snprintf(errbuf, PCAP_ERRBUF_SIZE, "(no devices found) %s: %s",		    device, pcap_strerror(errno));

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?