bpf.c
来自「基于组件方式开发操作系统的OSKIT源代码」· C语言 代码 · 共 1,379 行 · 第 1/2 页
C
1,379 行
*/ case BIOCFLUSH: s = splimp(); reset_d(d); splx(s); break; /* * Put interface into promiscuous mode. */ case BIOCPROMISC: if (d->bd_bif == 0) { /* * No interface attached yet. */ error = EINVAL; break; } s = splimp(); if (d->bd_promisc == 0) { error = ifpromisc(d->bd_bif->bif_ifp, 1); if (error == 0) d->bd_promisc = 1; } splx(s); break; /* * Get device parameters. */ case BIOCGDLT: if (d->bd_bif == 0) error = EINVAL; else *(u_int *)addr = d->bd_bif->bif_dlt; break; /* * Set interface name. */ case BIOCGETIF: if (d->bd_bif == 0) error = EINVAL; else bpf_ifname(d->bd_bif->bif_ifp, (struct ifreq *)addr); break; /* * Set interface. */ case BIOCSETIF: error = bpf_setif(d, (struct ifreq *)addr); break; /* * Set read timeout. */ case BIOCSRTIMEOUT: { struct timeval *tv = (struct timeval *)addr; u_long msec; /* Compute number of milliseconds. */ msec = tv->tv_sec * 1000 + tv->tv_usec / 1000; /* Scale milliseconds to ticks. Assume hard clock has millisecond or greater resolution (i.e. tick >= 1000). For 10ms hardclock, tick/1000 = 10, so rtout<-msec/10. */ d->bd_rtout = msec / (tick / 1000); break; } /* * Get read timeout. */ case BIOCGRTIMEOUT: { struct timeval *tv = (struct timeval *)addr; u_long msec = d->bd_rtout; msec *= tick / 1000; tv->tv_sec = msec / 1000; tv->tv_usec = msec % 1000; break; } /* * Get packet stats. */ case BIOCGSTATS: { struct bpf_stat *bs = (struct bpf_stat *)addr; bs->bs_recv = d->bd_rcount; bs->bs_drop = d->bd_dcount; break; } /* * Set immediate mode. */ case BIOCIMMEDIATE: d->bd_immediate = *(u_int *)addr; break; case BIOCVERSION: { struct bpf_version *bv = (struct bpf_version *)addr; bv->bv_major = BPF_MAJOR_VERSION; bv->bv_minor = BPF_MINOR_VERSION; break; } case FIONBIO: /* Non-blocking I/O */ if (*(int *)addr) d->bd_rtout = -1; else d->bd_rtout = 0; break; case FIOASYNC: /* Send signal on receive packets */ d->bd_async = *(int *)addr; break;/* N.B. ioctl (FIOSETOWN) and fcntl (F_SETOWN) both end up doing the equivalent of a TIOCSPGRP and hence end up here. *However* TIOCSPGRP's arg is a process group if it's positive and a process id if it's negative. This is exactly the opposite of what the other two functions want! Therefore there is code in ioctl and fcntl to negate the arg before calling here. */ case TIOCSPGRP: /* Process or group to send signals to */ d->bd_pgid = *(int *)addr; break; case TIOCGPGRP: *(int *)addr = d->bd_pgid; break; case BIOCSRSIG: /* Set receive signal */ { u_int sig; sig = *(u_int *)addr; if (sig >= NSIG) error = EINVAL; else d->bd_sig = sig; break; } case BIOCGRSIG: *(u_int *)addr = d->bd_sig; break; } return (error);}/* * Set d's packet filter program to fp. If this file already has a filter, * free it and replace it. Returns EINVAL for bogus requests. */intbpf_setf(d, fp) struct bpf_d *d; struct bpf_program *fp;{ struct bpf_insn *fcode, *old; u_int flen, size; int s; old = d->bd_filter; if (fp->bf_insns == 0) { if (fp->bf_len != 0) return (EINVAL); s = splimp(); d->bd_filter = 0; reset_d(d); splx(s); if (old != 0) free((caddr_t)old, M_DEVBUF); return (0); } flen = fp->bf_len; if (flen > BPF_MAXINSNS) return (EINVAL); size = flen * sizeof(*fp->bf_insns); fcode = (struct bpf_insn *)malloc(size, M_DEVBUF, M_WAITOK); if (copyin((caddr_t)fp->bf_insns, (caddr_t)fcode, size) == 0 && bpf_validate(fcode, (int)flen)) { s = splimp(); d->bd_filter = fcode; reset_d(d); splx(s); if (old != 0) free((caddr_t)old, M_DEVBUF); return (0); } free((caddr_t)fcode, M_DEVBUF); return (EINVAL);}/* * Detach a file from its current interface (if attached at all) and attach * to the interface indicated by the name stored in ifr. * Return an errno or 0. */static intbpf_setif(d, ifr) struct bpf_d *d; struct ifreq *ifr;{ struct bpf_if *bp; char *cp; int unit, s, error; /* * Separate string into name part and unit number. Put a null * byte at the end of the name part, and compute the number. * If the a unit number is unspecified, the default is 0, * as initialized above. XXX This should be common code. */ unit = 0; cp = ifr->ifr_name; cp[sizeof(ifr->ifr_name) - 1] = '\0'; while (*cp++) { if (*cp >= '0' && *cp <= '9') { unit = *cp - '0'; *cp++ = '\0'; while (*cp) unit = 10 * unit + *cp++ - '0'; break; } } /* * Look through attached interfaces for the named one. */ for (bp = bpf_iflist; bp != 0; bp = bp->bif_next) { struct ifnet *ifp = bp->bif_ifp; if (ifp == 0 || unit != ifp->if_unit || strcmp(ifp->if_name, ifr->ifr_name) != 0) continue; /* * We found the requested interface. * If it's not up, return an error. * Allocate the packet buffers if we need to. * If we're already attached to requested interface, * just flush the buffer. */ if ((ifp->if_flags & IFF_UP) == 0) return (ENETDOWN); if (d->bd_sbuf == 0) { error = bpf_allocbufs(d); if (error != 0) return (error); } s = splimp(); if (bp != d->bd_bif) { if (d->bd_bif) /* * Detach if attached to something else. */ bpf_detachd(d); bpf_attachd(d, bp); } reset_d(d); splx(s); return (0); } /* Not found. */ return (ENXIO);}/* * Convert an interface name plus unit number of an ifp to a single * name which is returned in the ifr. */static voidbpf_ifname(ifp, ifr) struct ifnet *ifp; struct ifreq *ifr;{ char *s = ifp->if_name; char *d = ifr->ifr_name; while (*d++ = *s++) continue; d--; /* back to the null */ /* XXX Assume that unit number is less than 10. */ *d++ = ifp->if_unit + '0'; *d = '\0';}/* * The new select interface passes down the proc pointer; the old select * stubs had to grab it out of the user struct. This glue allows either case. */#if BSD >= 199103#define bpf_select bpfselect#elseintbpfselect(dev, rw) register dev_t dev; int rw;{ return (bpf_select(dev, rw, u.u_procp));}#endif/* * Support for select() system call * * Return true iff the specific operation will not block indefinitely. * Otherwise, return false but make a note that a selwakeup() must be done. */intbpf_select(dev, rw, p) register dev_t dev; int rw; struct proc *p;{ register struct bpf_d *d; register int s; if (rw != FREAD) return (0); /* * An imitation of the FIONREAD ioctl code. */ d = &bpf_dtab[minor(dev)]; s = splimp(); if (d->bd_hlen != 0 || (d->bd_immediate && d->bd_slen != 0)) { /* * There is data waiting. */ splx(s); return (1); }#if BSD >= 199103 selrecord(p, &d->bd_sel);#else /* * No data ready. If there's already a select() waiting on this * minor device then this is a collision. This shouldn't happen * because minors really should not be shared, but if a process * forks while one of these is open, it is possible that both * processes could select on the same descriptor. */ if (d->bd_selproc && d->bd_selproc->p_wchan == (caddr_t)&selwait) d->bd_selcoll = 1; else d->bd_selproc = p;#endif splx(s); return (0);}/* * Incoming linkage from device drivers. Process the packet pkt, of length * pktlen, which is stored in a contiguous buffer. The packet is parsed * by each process' filter, and if accepted, stashed into the corresponding * buffer. */voidbpf_tap(arg, pkt, pktlen) caddr_t arg; register u_char *pkt; register u_int pktlen;{ struct bpf_if *bp; register struct bpf_d *d; register u_int slen; /* * Note that the ipl does not have to be raised at this point. * The only problem that could arise here is that if two different * interfaces shared any data. This is not the case. */ bp = (struct bpf_if *)arg; for (d = bp->bif_dlist; d != 0; d = d->bd_next) { ++d->bd_rcount; slen = bpf_filter(d->bd_filter, pkt, pktlen, pktlen); if (slen != 0) catchpacket(d, pkt, pktlen, slen, bcopy); }}/* * Copy data from an mbuf chain into a buffer. This code is derived * from m_copydata in sys/uipc_mbuf.c. */static voidbpf_mcopy(src_arg, dst_arg, len) const void *src_arg; void *dst_arg; register u_int len;{ register const struct mbuf *m; register u_int count; u_char *dst; m = src_arg; dst = dst_arg; while (len > 0) { if (m == 0) panic("bpf_mcopy"); count = min(m->m_len, len); (void)memcpy((caddr_t)dst, mtod(m, caddr_t), count); m = m->m_next; dst += count; len -= count; }}/* * Incoming linkage from device drivers, when packet is in an mbuf chain. */voidbpf_mtap(arg, m) caddr_t arg; struct mbuf *m;{ struct bpf_if *bp = (struct bpf_if *)arg; struct bpf_d *d; u_int pktlen, slen; struct mbuf *m0; pktlen = 0; for (m0 = m; m0 != 0; m0 = m0->m_next) pktlen += m0->m_len; for (d = bp->bif_dlist; d != 0; d = d->bd_next) { ++d->bd_rcount; slen = bpf_filter(d->bd_filter, (u_char *)m, pktlen, 0); if (slen != 0) catchpacket(d, (u_char *)m, pktlen, slen, bpf_mcopy); }}/* * Move the packet data from interface memory (pkt) into the * store buffer. Return 1 if it's time to wakeup a listener (buffer full), * otherwise 0. "copy" is the routine called to do the actual data * transfer. bcopy is passed in to copy contiguous chunks, while * bpf_mcopy is passed in to copy mbuf chains. In the latter case, * pkt is really an mbuf. */static voidcatchpacket(d, pkt, pktlen, snaplen, cpfn) register struct bpf_d *d; register u_char *pkt; register u_int pktlen, snaplen; register void (*cpfn)(const void *, void *, u_int);{ register struct bpf_hdr *hp; register int totlen, curlen; register int hdrlen = d->bd_bif->bif_hdrlen; /* * Figure out how many bytes to move. If the packet is * greater or equal to the snapshot length, transfer that * much. Otherwise, transfer the whole packet (unless * we hit the buffer size limit). */ totlen = hdrlen + min(snaplen, pktlen); if (totlen > d->bd_bufsize) totlen = d->bd_bufsize; /* * Round up the end of the previous packet to the next longword. */ curlen = BPF_WORDALIGN(d->bd_slen); if (curlen + totlen > d->bd_bufsize) { /* * This packet will overflow the storage buffer. * Rotate the buffers if we can, then wakeup any * pending reads. */ if (d->bd_fbuf == 0) { /* * We haven't completed the previous read yet, * so drop the packet. */ ++d->bd_dcount; return; } ROTATE_BUFFERS(d); bpf_wakeup(d); curlen = 0; } else if (d->bd_immediate) /* * Immediate mode is set. A packet arrived so any * reads should be woken up. */ bpf_wakeup(d); /* * Append the bpf header. */ hp = (struct bpf_hdr *)(d->bd_sbuf + curlen);#if BSD >= 199103 microtime(&hp->bh_tstamp);#elif defined(sun) uniqtime(&hp->bh_tstamp);#else hp->bh_tstamp = time;#endif hp->bh_datalen = pktlen; hp->bh_hdrlen = hdrlen; /* * Copy the packet data into the store buffer and update its length. */ (*cpfn)(pkt, (u_char *)hp + hdrlen, (hp->bh_caplen = totlen - hdrlen)); d->bd_slen = curlen + totlen;}/* * Initialize all nonzero fields of a descriptor. */static intbpf_allocbufs(d) register struct bpf_d *d;{ d->bd_fbuf = (caddr_t)malloc(d->bd_bufsize, M_DEVBUF, M_WAITOK); if (d->bd_fbuf == 0) return (ENOBUFS); d->bd_sbuf = (caddr_t)malloc(d->bd_bufsize, M_DEVBUF, M_WAITOK); if (d->bd_sbuf == 0) { free(d->bd_fbuf, M_DEVBUF); return (ENOBUFS); } d->bd_slen = 0; d->bd_hlen = 0; return (0);}/* * Free buffers currently in use by a descriptor. * Called on close. */static voidbpf_freed(d) register struct bpf_d *d;{ /* * We don't need to lock out interrupts since this descriptor has * been detached from its interface and it yet hasn't been marked * free. */ if (d->bd_sbuf != 0) { free(d->bd_sbuf, M_DEVBUF); if (d->bd_hbuf != 0) free(d->bd_hbuf, M_DEVBUF); if (d->bd_fbuf != 0) free(d->bd_fbuf, M_DEVBUF); } if (d->bd_filter) free((caddr_t)d->bd_filter, M_DEVBUF); D_MARKFREE(d);}/* * Attach an interface to bpf. driverp is a pointer to a (struct bpf_if *) * in the driver's softc; dlt is the link layer type; hdrlen is the fixed * size of the link header (variable length headers not yet supported). */voidbpfattach(driverp, ifp, dlt, hdrlen) caddr_t *driverp; struct ifnet *ifp; u_int dlt, hdrlen;{ struct bpf_if *bp; int i;#if BSD < 199103 static struct bpf_if bpf_ifs[NBPFILTER]; static int bpfifno; bp = (bpfifno < NBPFILTER) ? &bpf_ifs[bpfifno++] : 0;#else bp = (struct bpf_if *)malloc(sizeof(*bp), M_DEVBUF, M_DONTWAIT);#endif if (bp == 0) panic("bpfattach"); bp->bif_dlist = 0; bp->bif_driverp = (struct bpf_if **)driverp; bp->bif_ifp = ifp; bp->bif_dlt = dlt; bp->bif_next = bpf_iflist; bpf_iflist = bp; *bp->bif_driverp = 0; /* * Compute the length of the bpf header. This is not necessarily * equal to SIZEOF_BPF_HDR because we want to insert spacing such * that the network layer header begins on a longword boundary (for * performance reasons and to alleviate alignment restrictions). */ bp->bif_hdrlen = BPF_WORDALIGN(hdrlen + SIZEOF_BPF_HDR) - hdrlen; /* * Mark all the descriptors free if this hasn't been done. */ if (!D_ISFREE(&bpf_dtab[0])) for (i = 0; i < NBPFILTER; ++i) D_MARKFREE(&bpf_dtab[i]); if (bootverbose) printf("bpf: %s%d attached\n", ifp->if_name, ifp->if_unit);}#if BSD >= 199103/* XXX This routine belongs in net/if.c. *//* * Set/clear promiscuous mode on interface ifp based on the truth value * of pswitch. The calls are reference counted so that only the first * "on" request actually has an effect, as does the final "off" request. * Results are undefined if the "off" and "on" requests are not matched. */intifpromisc(ifp, pswitch) struct ifnet *ifp; int pswitch;{ struct ifreq ifr; if (pswitch) { /* * If the device is not configured up, we cannot put it in * promiscuous mode. */ if ((ifp->if_flags & IFF_UP) == 0) return (ENETDOWN); if (ifp->if_pcount++ != 0) return (0); ifp->if_flags |= IFF_PROMISC; } else { if (--ifp->if_pcount > 0) return (0); ifp->if_flags &= ~IFF_PROMISC; } ifr.ifr_flags = ifp->if_flags; return ((*ifp->if_ioctl)(ifp, SIOCSIFFLAGS, (caddr_t)&ifr));}#endif#if BSD < 199103/* * Allocate some memory for bpf. This is temporary SunOS support, and * is admittedly a hack. * If resources unavaiable, return 0. */static caddr_tbpf_alloc(size, canwait) register int size; register int canwait;{ register struct mbuf *m; if ((unsigned)size > (MCLBYTES-8)) return 0; MGET(m, canwait, MT_DATA); if (m == 0) return 0; if ((unsigned)size > (MLEN-8)) { MCLGET(m); if (m->m_len != MCLBYTES) { m_freem(m); return 0; } } *mtod(m, struct mbuf **) = m; return mtod(m, caddr_t) + 8;}#endif#endif
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?