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