bpf.c

来自「<B>Digital的Unix操作系统VAX 4.2源码</B>」· C语言 代码 · 共 1,218 行 · 第 1/2 页

C
1,218
字号
/* * Copyright (c) 1990 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. * * This code is derived from the Stanford/CMU enet packet filter, * (net/enet.c) distributed in 4.3BSD Unix. * * SCCSID: @(#)bpf.c	4.1	ULTRIX	1/25/91 * Based on: * rcsid[] = "$Header: bpf.c,v 1.21 91/01/14 21:52:48 mccanne Exp $" */#include "bpfilter.h"#if (NBPFILTER > 0)#ifndef __GNUC__#define inline#endif#include <sys/param.h>#include <sys/systm.h>#include <sys/mbuf.h>#include <sys/buf.h>#include <sys/dir.h>#include <sys/user.h>#include <sys/ioctl.h>#include <sys/map.h>#include <sys/proc.h>#include <sys/file.h>#ifdef sparc#include <sys/stream.h>#endif#include <sys/tty.h>#include <sys/uio.h>#include <sys/protosw.h>#include <sys/socket.h>#include <net/if.h>#include <net/bpf.h>#include <net/bpfdesc.h>#include <sys/errno.h>#include <netinet/in.h>#include <netinet/if_ether.h>#include <sys/kernel.h>#define PRINET  26			/* interruptible *//* *  'bpf_iftab' is the driver state table per logical unit number *  'bpf_dtab' holds the descriptors, indexed by minor device # *  'bpf_units' is the number of attached units * * We really don't need NBPFILTER bpf_if entries, but this eliminates * the need to account for all possible drivers here. * This problem will go away when these structures are allocated dynamically. */static struct bpf_if 	bpf_iftab[NBPFILTER];static struct bpf_d	bpf_dtab[NBPFILTER];static u_int		bpf_units = 0;static void	bpf_timeout();static void	bpf_ifname();static void	catchpacket();static int	bpf_setif();static int	bpf_initd();/* * The default filter accepts the maximum number of bytes from each packet. */struct bpf_insn bpf_default_filter[] = {	BPF_STMT(RetOp, MCLBYTES),};/* * This routine was inspired by/stolen from ../sys/uipc_socket.c * Move data from 'm' to user's read buffer.   * We assume 'm' is not chained. * Returns error code (or 0 if success). */static inline intbpf_moveout(m, uio)	register struct mbuf *m;	register struct uio *uio;{	register int len;		len = m->m_len;	if (uio->uio_resid < len)		len = uio->uio_resid;	if (len > 0)		return uiomove(mtod(m, caddr_t), len, UIO_READ, uio);	return 0;}static intbpf_movein(uio, linktype, mp, sockp)	register struct uio *uio;	int linktype;	register struct mbuf **mp;	register struct sockaddr *sockp;{	struct mbuf *m;	int error;	int len;	int hlen;	/*	 * Build a sockaddr based on the data link layer type.	 * We do this at this level because the ethernet header	 * is copied directly into the data field of the sockaddr.	 * In the case of SLIP, there is no header and the packet	 * is forwarded as is.	 * Also, we are careful to leave room at the front of the mbuf	 * for the link level header.	 */	switch (linktype) {	case DLT_SLIP:		sockp->sa_family = AF_INET;		hlen = 0;		break;	case DLT_EN10MB:		sockp->sa_family = AF_UNSPEC;		/* XXX Would MAXLINKHDR be better? */		hlen = sizeof(struct ether_header);		break;	default:		return EIO;	}	len = uio->uio_resid;	if ((unsigned)len > MCLBYTES)		return EIO;	MGET(m, M_WAIT, MT_DATA);	if (m == 0)		return ENOBUFS;	if (len > MLEN) {		MCLGET(m);		if (m->m_len != MCLBYTES) {			error = ENOBUFS;			goto bad;		}	}	m->m_len = len;	*mp = m;	/*	 * Make room for link header.	 */	if (hlen) {		m->m_len -= hlen;		m->m_off += hlen;		error = uiomove((caddr_t)sockp->sa_data, hlen, UIO_WRITE, uio);		if (error)			goto bad;	}	error = uiomove(mtod(m, caddr_t), len - hlen, UIO_WRITE, uio);	if (!error) 		return 0; bad:	m_freem(m);	return error;}/* * Attach 'd' to the bpf interface 'bp', i.e. make 'd' listen on 'bp'. * Must be called at splimp. */static voidbpf_attachd(d, bp)	struct bpf_d *d;	struct bpf_if *bp;{	/* Point 'd' at 'bp'. */	d->bd_bif = bp;	/* Add 'd' to 'bp's list of listeners. */	d->bd_next = bp->bif_dlist;	bp->bif_dlist = d;	/*	 * Let the driver know we're here (if it doesn't already).	 */	*bp->bif_driverp = bp;}static voidbpf_detachd(d)	struct bpf_d *d;{	struct bpf_d **p;	struct bpf_if *bp;	bp = d->bd_bif;	/*	 * Check if this descriptor had requested promiscuous mode.	 * If so, turn it off.	 */	if (d->bd_promisc) {		d->bd_promisc = 0;		if (ifpromisc(bp->bif_ifp, 0))			/*			 * Something is really wrong if we were able to put			 * the driver into promiscuous mode, but can't			 * take it out.			 */			panic("bpf_detachd: exit promisc unsucessful");	}	/* Remove 'd' from the interface's descriptor list. */	p = &bp->bif_dlist;	while (*p != d) {		p = &(*p)->bd_next;		if (*p == 0)			panic("bpf_detachd: descriptor not in list");	}	*p = (*p)->bd_next;	if (bp->bif_dlist == 0)		/*		 * Let the driver know that there are no more listeners.		 */		*d->bd_bif->bif_driverp = 0;	d->bd_bif = 0;}/* * Mark a descriptor free by making it point to itself.  * This is probably cheaper than marking with a constant since * the address should be in a register anyway. */#define D_ISFREE(d) ((d) == (d)->bd_next)#define D_MARKFREE(d) ((d)->bd_next = (d))#define D_MARKUSED(d) ((d)->bd_next = 0)/* *  bpfopen - open ethernet device * *  Errors:	ENXIO	- illegal minor device number *		EBUSY	- too many files open *//* ARGSUSED */intbpfopen(dev, flag)	dev_t dev;	int flag;{	int error, s;	register struct bpf_d *d;		if (minor(dev) >= NBPFILTER)		return ENXIO;	/*	 * Each minor can be opened by only one process.  If the requested	 * minor is in use, return EBUSY.	 */	s = splimp();	d = &bpf_dtab[minor(dev)];	if (!D_ISFREE(d)) {		splx(s);		return EBUSY;	} else		/* Mark "free" and do most initialization. */		bzero((char *)d, sizeof(*d));	d->bd_filter = bpf_default_filter;	splx(s);	error = bpf_initd(d);	if (error) {		D_MARKFREE(d);		return error;	}	return 0;}/* * Close the descriptor by detaching it from its interface, * deallocating its buffers, and marking it free. *//* ARGSUSED */voidbpfclose(dev, flag)	dev_t dev;	int flag;{	register struct bpf_d *d = &bpf_dtab[minor(dev)];	int s;	s = splimp();	if (d->bd_bif)		bpf_detachd(d);	splx(s);	/* Let the buffers go. */	m_freem(d->bd_hbuf);	m_freem(d->bd_sbuf);	m_freem(d->bd_fbuf);	m_freem(d->bd_filterm);		D_MARKFREE(d);}#define RS_IDLE 0#define RS_WAIT 1#define RS_TIMEDOUT 2/* *  bpfread - read next chunk of packets from buffers */intbpfread(dev, uio)	dev_t dev;	register struct uio *uio;{	register struct bpf_d *d = &bpf_dtab[minor(dev)];	struct mbuf *m;	int error;	int s;	/*	 * Restrict application to use a buffer the same size as 	 * as kernel buffers.	 */	if (uio->uio_resid != MCLBYTES)		return EIO;	s = splimp();	/*	 * If the hold buffer is empty, then set a timer and sleep	 * until either the timeout has occurred or enough packets have	 * arrived to fill the store buffer.	 */	while (d->bd_hbuf == 0) {		if (d->bd_immediate && d->bd_sbuf->m_len) {			/*			 * A packet(s) either arrived since the previous			 * read or arrived while we were asleep.			 * Rotate the buffers and return what's here.			 */			d->bd_hbuf = d->bd_sbuf;			d->bd_sbuf = d->bd_fbuf;			d->bd_sbuf->m_len = 0;			d->bd_fbuf = 0;			break;		}		if (d->bd_rtout) {			/*			 * If there was a previous timeout pending for this 			 * file, cancel it before setting another.  This is			 * necessary since a cancel after the sleep might 			 * never happen if the read is interrupted by a signal.			 */			if (d->bd_state == RS_WAIT)				untimeout(bpf_timeout, (caddr_t)d);			timeout(bpf_timeout, (caddr_t)d, (int)d->bd_rtout);			d->bd_state = RS_WAIT;		}		else			d->bd_state = RS_IDLE;				sleep((caddr_t)d, PRINET);				if (d->bd_state == RS_WAIT) {			untimeout(bpf_timeout, (caddr_t)d);			d->bd_state = RS_IDLE;		}		else if (d->bd_state == RS_TIMEDOUT) {			/*			 * On a timeout, return what's in the buffer,			 * which may be nothing.  We do this by moving			 * the store buffer into the hold slot.			 */			if (d->bd_hbuf)				/*				 * We filled up the buffer in between 				 * getting the timeout and arriving				 * here, so we don't need to rotate.				 */				break;			if (d->bd_sbuf->m_len == 0) {				splx(s);				return(0);			}			d->bd_hbuf = d->bd_sbuf;			d->bd_sbuf = d->bd_fbuf;			d->bd_sbuf->m_len = 0;			d->bd_fbuf = 0;			break;		}	}	/*	 * At this point, we know we have something in the hold slot.	 */	m = d->bd_hbuf;	splx(s);		/*  	 * Move data from hold buffer into user space.	 * We know the entire buffer is transferred since	 * we checked above that the read buffer is MCLBYTES.	 */	error = bpf_moveout(m, uio);	s = splimp();	if (d->bd_fbuf != 0)		panic("bpfread: free mbuf slot occupied");	d->bd_fbuf = m;	d->bd_hbuf = (struct mbuf *)0;	splx(s);		return error;}/* * If there are processes select sleeping on this descriptor, * wake them up.   */static inline voidbpf_wakeup(d)	register struct bpf_d *d;{	if (d->bd_SelProc) {		selwakeup(d->bd_SelProc, d->bd_SelColl);		d->bd_SelColl = 0;		d->bd_SelProc = 0;	}}/* *  bpf_timeout - process ethernet read timeout */static voidbpf_timeout(d)	register struct bpf_d * d;{	register int s = splimp();	d->bd_state = RS_TIMEDOUT;	wakeup((caddr_t)d);	bpf_wakeup(d);	splx(s);}intbpfwrite(dev, uio)	dev_t dev;	struct uio *uio;{	register struct bpf_d *d = &bpf_dtab[minor(dev)];	struct ifnet *ifp;	struct mbuf *m;	int error, s;	static struct sockaddr dst;	if (d->bd_bif == 0)		return ENXIO;	ifp = d->bd_bif->bif_ifp;	if (uio->uio_resid == 0)		return 0;	if (uio->uio_resid > ifp->if_mtu)		return EMSGSIZE;	error = bpf_movein(uio, d->bd_bif->bif_devp.bdev_type, &m, &dst);	if (error)		return error;	s = splnet();	error = (*ifp->if_output)(ifp, m, &dst);	splx(s);	/*	 * The driver frees the mbuf. 	 */	return error;}/* * Reset a descriptor by flushing its packet before * and clearing the receive and drop counts.  Should * be called at splimp. */static voidreset_d(d)	struct bpf_d *d;{	if (d->bd_hbuf) {		/* Free the hold buffer. */		d->bd_fbuf = d->bd_hbuf;		d->bd_hbuf = 0;	}	d->bd_sbuf->m_len = 0;	d->bd_rcount = 0;	d->bd_dcount = 0;}/* *  bpfioctl - packet filter control * *  FIONREAD		Check for read packet available. *  SIOCGIFADDR		Get interface address - convenient hook to driver. *  BIOCGFLEN		Get max filter len. *  BIOCGBLEN		Get buffer len [for read()]. *  BIOCSETF		Set ethernet read filter. *  BIOCFLUSH		Flush read packet buffer. *  BIOCPROMISC		Put interface into promiscuous mode. *  BIOCDEVP		Get device parameters. *  BIOCGETIF		Get interface name. *  BIOCSETIF		Set interface. *  BIOCSRTIMEOUT	Set read timeout. *  BIOCGRTIMEOUT	Get read timeout. *  BIOCGSTATS		Get packet stats. *  BIOCIMMEDIATE	Set immediate mode. *//* ARGSUSED */intbpfioctl(dev, cmd, addr, flag)	dev_t dev;	int cmd;	caddr_t addr;	int flag;{	register struct bpf_d *d = &bpf_dtab[minor(dev)];	int s, error = 0;	switch (cmd) {	default:		error = EINVAL;		break;	/*	 * Check for read packet available.	 */	case FIONREAD:		{			int n;					s = splimp();			n = d->bd_sbuf->m_len;			if (d->bd_hbuf) 				n += d->bd_hbuf->m_len;			splx(s);			*(int *)addr = n;			break;		}	case SIOCGIFADDR:		{			struct ifnet *ifp;			if (d->bd_bif == 0)				error = EINVAL;			else {				ifp = d->bd_bif->bif_ifp;				error =  (*ifp->if_ioctl)(ifp, cmd, addr);			}			break;		}	/*	 * Get max filter len.	 */	case BIOCGFLEN:		*(u_int *)addr = MCLBYTES / sizeof(struct bpf_insn);		break;	/*	 * Get buffer len [for read()].	 */	case BIOCGBLEN:		*(u_int *)addr = MCLBYTES;		break;	/*	 * Set ethernet read filter.	 */        case BIOCSETF:		error = bpf_setf(d, (struct bpf_program *)addr);		break;

⌨️ 快捷键说明

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