]> git.saurik.com Git - apple/xnu.git/blobdiff - bsd/net/bpf.c
xnu-3789.70.16.tar.gz
[apple/xnu.git] / bsd / net / bpf.c
index f98100d2b739f8cbee0d93c998f3b97a49c9aa5c..66e0c3b549cc5b03d0a3fcded261f6a69ef0c719 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2000-2014 Apple Inc. All rights reserved.
+ * Copyright (c) 2000-2017 Apple Inc. All rights reserved.
  *
  * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
  * 
@@ -1514,7 +1514,14 @@ bpfioctl(dev_t dev, u_long cmd, caddr_t addr, __unused int flags,
 
                        bcopy(addr, &size, sizeof (size));
 
-                       if (size > bpf_maxbufsize)
+                       /*
+                        * Allow larger buffer in head drop mode with the
+                        * assumption the capture is in standby mode to
+                        * keep a cache of recent traffic
+                        */
+                       if (d->bd_headdrop != 0 && size > 2 * bpf_maxbufsize)
+                               size = 2 * bpf_maxbufsize;
+                       else if (size > bpf_maxbufsize)
                                size = bpf_maxbufsize;
                        else if (size < BPF_MINBUFSIZE)
                                size = BPF_MINBUFSIZE;
@@ -1970,20 +1977,16 @@ bpf_setif(struct bpf_d *d, ifnet_t theywant, u_int32_t dlt)
                        continue;
                /*
                 * We found the requested interface.
-                * Allocate the packet buffers if we need to.
-                * If we're already attached to requested interface,
-                * just flush the buffer.
+                * Allocate the packet buffers.
+                */
+               error = bpf_allocbufs(d);
+               if (error != 0)
+                       return (error);
+               /*
+                * Detach if attached to something else.
                 */
-               if (d->bd_sbuf == 0) {
-                       error = bpf_allocbufs(d);
-                       if (error != 0)
-                               return (error);
-               }
                if (bp != d->bd_bif) {
-                               /*
-                                * Detach if attached to something else.
-                                */
-                       if (d->bd_bif) {
+                       if (d->bd_bif != NULL) {
                                if (bpf_detachd(d, 0) != 0)
                                        return (ENXIO);
                        }
@@ -2196,73 +2199,22 @@ bpfselect(dev_t dev, int which, void * wql, struct proc *p)
 int bpfkqfilter(dev_t dev, struct knote *kn);
 static void filt_bpfdetach(struct knote *);
 static int filt_bpfread(struct knote *, long);
+static int filt_bpftouch(struct knote *kn, struct kevent_internal_s *kev);
+static int filt_bpfprocess(struct knote *kn, struct filt_process_s *data, struct kevent_internal_s *kev);
 
-static struct filterops bpfread_filtops = {
+struct filterops bpfread_filtops = {
        .f_isfd = 1, 
        .f_detach = filt_bpfdetach,
        .f_event = filt_bpfread,
+       .f_touch = filt_bpftouch,
+       .f_process = filt_bpfprocess,
 };
 
-int
-bpfkqfilter(dev_t dev, struct knote *kn)
-{
-       struct bpf_d *d;
-
-       /*
-        * Is this device a bpf?
-        */
-       if (major(dev) != CDEV_MAJOR) {
-               return (EINVAL);
-       }
-
-       if (kn->kn_filter != EVFILT_READ) {
-               return (EINVAL);
-       }
-
-       lck_mtx_lock(bpf_mlock);
-
-       d = bpf_dtab[minor(dev)];
-       if (d == 0 || d == (void *)1 || (d->bd_flags & BPF_CLOSING) != 0) {
-               lck_mtx_unlock(bpf_mlock);
-               return (ENXIO);
-       }
-
-       if (d->bd_bif == NULL) {
-               lck_mtx_unlock(bpf_mlock);
-               return (ENXIO);
-       }
-
-       kn->kn_hook = d;
-       kn->kn_fop = &bpfread_filtops;
-       KNOTE_ATTACH(&d->bd_sel.si_note, kn);
-       d->bd_flags |= BPF_KNOTE;
-
-       lck_mtx_unlock(bpf_mlock);
-       return (0);
-}
-
-static void
-filt_bpfdetach(struct knote *kn)
-{
-       struct bpf_d *d = (struct bpf_d *)kn->kn_hook;
-
-       lck_mtx_lock(bpf_mlock);
-       if (d->bd_flags & BPF_KNOTE) {
-               KNOTE_DETACH(&d->bd_sel.si_note, kn);
-               d->bd_flags &= ~BPF_KNOTE;
-       }
-       lck_mtx_unlock(bpf_mlock);
-}
-
 static int
-filt_bpfread(struct knote *kn, long hint)
+filt_bpfread_common(struct knote *kn, struct bpf_d *d)
 {
-       struct bpf_d *d = (struct bpf_d *)kn->kn_hook;
        int ready = 0;
 
-       if (hint == 0)
-               lck_mtx_lock(bpf_mlock);
-       
        if (d->bd_immediate) {
                /*
                 * If there's data in the hold buffer, it's the 
@@ -2312,11 +2264,113 @@ filt_bpfread(struct knote *kn, long hint)
        if (!ready)
                bpf_start_timer(d);
 
-       if (hint == 0)
-               lck_mtx_unlock(bpf_mlock);
        return (ready);
 }
 
+int
+bpfkqfilter(dev_t dev, struct knote *kn)
+{
+       struct bpf_d *d;
+       int res;
+
+       /*
+        * Is this device a bpf?
+        */
+       if (major(dev) != CDEV_MAJOR ||
+           kn->kn_filter != EVFILT_READ) {
+               kn->kn_flags = EV_ERROR;
+               kn->kn_data = EINVAL;
+               return 0;
+       }
+
+       lck_mtx_lock(bpf_mlock);
+
+       d = bpf_dtab[minor(dev)];
+
+       if (d == 0 ||
+           d == (void *)1 ||
+           d->bd_bif == NULL ||
+           (d->bd_flags & BPF_CLOSING) != 0) {
+               lck_mtx_unlock(bpf_mlock);
+               kn->kn_flags = EV_ERROR;
+               kn->kn_data = ENXIO;
+               return 0;
+       }
+
+       kn->kn_hook = d;
+       kn->kn_filtid = EVFILTID_BPFREAD;
+       KNOTE_ATTACH(&d->bd_sel.si_note, kn);
+       d->bd_flags |= BPF_KNOTE;
+
+       /* capture the current state */
+       res = filt_bpfread_common(kn, d);
+
+       lck_mtx_unlock(bpf_mlock);
+
+       return (res);
+}
+
+static void
+filt_bpfdetach(struct knote *kn)
+{
+       struct bpf_d *d = (struct bpf_d *)kn->kn_hook;
+
+       lck_mtx_lock(bpf_mlock);
+       if (d->bd_flags & BPF_KNOTE) {
+               KNOTE_DETACH(&d->bd_sel.si_note, kn);
+               d->bd_flags &= ~BPF_KNOTE;
+       }
+       lck_mtx_unlock(bpf_mlock);
+}
+
+static int
+filt_bpfread(struct knote *kn, long hint)
+{
+#pragma unused(hint)
+       struct bpf_d *d = (struct bpf_d *)kn->kn_hook;
+
+       return filt_bpfread_common(kn, d);
+}
+
+static int
+filt_bpftouch(struct knote *kn, struct kevent_internal_s *kev)
+{
+       struct bpf_d *d = (struct bpf_d *)kn->kn_hook;
+       int res;
+
+       lck_mtx_lock(bpf_mlock);
+
+       /* save off the lowat threshold and flag */
+       kn->kn_sdata = kev->data;
+       kn->kn_sfflags = kev->fflags;
+       if ((kn->kn_status & KN_UDATA_SPECIFIC) == 0)
+               kn->kn_udata = kev->udata;
+
+       /* output data will be re-generated here */
+       res = filt_bpfread_common(kn, d);
+
+       lck_mtx_unlock(bpf_mlock);
+
+       return res;
+}
+
+static int
+filt_bpfprocess(struct knote *kn, struct filt_process_s *data, struct kevent_internal_s *kev)
+{
+#pragma unused(data)
+       struct bpf_d *d = (struct bpf_d *)kn->kn_hook;
+       int res;
+
+       lck_mtx_lock(bpf_mlock);
+       res = filt_bpfread_common(kn, d);
+       if (res) {
+               *kev = kn->kn_kevent;
+       }
+       lck_mtx_unlock(bpf_mlock);
+
+       return res;
+}
+
 /*
  * Copy data from an mbuf chain into a buffer.  This code is derived
  * from m_copydata in sys/uipc_mbuf.c.
@@ -2499,7 +2553,15 @@ catchpacket(struct bpf_d *d, u_char *pkt, struct mbuf *m, u_int pktlen,
                 * This packet will overflow the storage buffer.
                 * Rotate the buffers if we can, then wakeup any
                 * pending reads.
+                *
+                * We cannot rotate buffers if a read is in progress
+                * so drop the packet
                 */
+               if (d->bd_hbuf_read) {
+                       ++d->bd_dcount;
+                       return;
+               }
+               
                if (d->bd_fbuf == NULL) {
                        if (d->bd_headdrop == 0) {
                                /*
@@ -2564,6 +2626,18 @@ catchpacket(struct bpf_d *d, u_char *pkt, struct mbuf *m, u_int pktlen,
                        }
                        ehp->bh_svc = so_svc2tc(m->m_pkthdr.pkt_svc);
                        ehp->bh_flags |= BPF_HDR_EXT_FLAGS_DIR_OUT;
+                       if (m->m_pkthdr.pkt_flags & PKTF_TCP_REXMT)
+                               ehp->bh_pktflags |= BPF_PKTFLAGS_TCP_REXMT;
+                       if (m->m_pkthdr.pkt_flags & PKTF_START_SEQ)
+                               ehp->bh_pktflags |= BPF_PKTFLAGS_START_SEQ;
+                       if (m->m_pkthdr.pkt_flags & PKTF_LAST_PKT)
+                               ehp->bh_pktflags |= BPF_PKTFLAGS_LAST_PKT;
+                       if (m->m_pkthdr.pkt_flags & PKTF_VALID_UNSENT_DATA) {
+                               ehp->bh_unsent_bytes =
+                                   m->m_pkthdr.bufstatus_if;
+                               ehp->bh_unsent_snd =
+                                   m->m_pkthdr.bufstatus_sndbuf;
+                       }
                } else
                        ehp->bh_flags |= BPF_HDR_EXT_FLAGS_DIR_IN;
                payload = (u_char *)ehp + hdrlen;
@@ -2595,13 +2669,27 @@ catchpacket(struct bpf_d *d, u_char *pkt, struct mbuf *m, u_int pktlen,
 static int
 bpf_allocbufs(struct bpf_d *d)
 {
+       if (d->bd_sbuf != NULL) {
+               FREE(d->bd_sbuf, M_DEVBUF);
+               d->bd_sbuf = NULL;
+       }
+       if (d->bd_hbuf != NULL) {
+               FREE(d->bd_hbuf, M_DEVBUF);
+               d->bd_hbuf = NULL;
+       }
+       if (d->bd_fbuf != NULL) {
+               FREE(d->bd_fbuf, M_DEVBUF);
+               d->bd_fbuf = NULL;
+       }
+
        d->bd_fbuf = (caddr_t) _MALLOC(d->bd_bufsize, M_DEVBUF, M_WAIT);
-       if (d->bd_fbuf == 0)
+       if (d->bd_fbuf == NULL)
                return (ENOBUFS);
 
        d->bd_sbuf = (caddr_t) _MALLOC(d->bd_bufsize, M_DEVBUF, M_WAIT);
-       if (d->bd_sbuf == 0) {
+       if (d->bd_sbuf == NULL) {
                FREE(d->bd_fbuf, M_DEVBUF);
+               d->bd_fbuf = NULL;
                return (ENOBUFS);
        }
        d->bd_slen = 0;