Current-Users archive

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]

Re: /dev/tap and tcpdump don't go together very well?



On Wed 04 Nov 2009 at 20:37:14 -0500, Greg Troxel wrote:
> 
> Rhialto <rhialto%falu.nl@localhost> writes:
> 
> > On Sun 08 Apr 2007 at 16:21:48 +0200, Rhialto wrote:
> >> BPF sees packets that come in from the wire and optionally (default
> >> on; BIOCSSEESENT; apparently undocumented) the packets that the host
> >> sends to the wire.
> >
> > This seems to be documented now in bpf(4).
> 
> This flag applies to packets injected via BPF, or is it also about bpf
> seeing normal outgoing packets?  The man page (5-stable) is not really
> clear.

I looked back at the original context, and at the kernel source. It
seems it is *only* about normal outgoing packets, not those injected via
BPF. I have included some hopefully clearer wording in the patch below.

> >> Packets injected into BPF are sent to the wire only, not to the host.
> >
> > FreeBSD now apparently has an ioctl for this:
> >
> >      BIOCFEEDBACK    (u_int) Set packet feedback mode.  This allows injected
> >                      packets to be fed back as input to the interface when
> >                      output via the interface is successful.  When 
> > BPF_D_INOUT
> >                      direction is set, injected outgoing packet is not
> >                      returned by BPF to avoid duplication. This flag is ini-
> >                      tialized to zero by default.
> 
> Is this separate from the bpf-ought-to-work-on-tap notion, and about how
> writes to bpf can be made to interoperate with the local host?  If so,
> do we need to port BIOCFEEDBACK to NetBSD's bpf implementation?

It is separate, and about the latter. I just came across the old thread
again and somehow looked at FreeBSD :-) I thought it is a good idea to
copy BIOCFEEDBACK into NetBSD, and so I include a patch. 

(Since I'm also trying out git and crossbuilding from FreeBSD at the
same time, it is not even compile-tested, but the spirit should be
there).

diff --git a/sys/net/bpf.c b/sys/net/bpf.c
index 9e1b9c8..f68af76 100644
--- a/sys/net/bpf.c
+++ b/sys/net/bpf.c
@@ -403,6 +403,7 @@ bpfopen(dev_t dev, int flag, int mode, struct lwp *l)
        d = malloc(sizeof(*d), M_DEVBUF, M_WAITOK|M_ZERO);
        d->bd_bufsize = bpf_bufsize;
        d->bd_seesent = 1;
+       d->bd_feedback = 0;
        d->bd_pid = l->l_proc->p_pid;
        getnanotime(&d->bd_btime);
        d->bd_atime = d->bd_mtime = d->bd_btime;
@@ -618,7 +619,7 @@ bpf_write(struct file *fp, off_t *offp, struct uio *uio,
 {
        struct bpf_d *d = fp->f_data;
        struct ifnet *ifp;
-       struct mbuf *m;
+       struct mbuf *m, *mc;
        int error, s;
        static struct sockaddr_storage dst;
 
@@ -655,8 +656,25 @@ bpf_write(struct file *fp, off_t *offp, struct uio *uio,
        if (d->bd_hdrcmplt)
                dst.ss_family = pseudo_AF_HDRCMPLT;
 
+        if (d->bd_feedback) {
+               mc = m_dup(m, 0, M_COPYALL, M_NOWAIT);
+                if (mc != NULL)
+                        mc->m_pkthdr.rcvif = ifp;
+                /* Set M_PROMISC for outgoing packets to be discarded. */
+                if (1 /*d->bd_direction == BPF_D_INOUT*/)
+                        m->m_flags |= M_PROMISC;
+        } else  
+                mc = NULL;
+
        s = splsoftnet();
        error = (*ifp->if_output)(ifp, m, (struct sockaddr *) &dst, NULL);
+
+        if (mc != NULL) {
+                if (error == 0)
+                        (*ifp->if_input)(ifp, mc);
+                else
+                        m_freem(mc);
+        }
        splx(s);
        KERNEL_UNLOCK_ONE(NULL);
        /*
@@ -700,6 +718,10 @@ reset_d(struct bpf_d *d)
  *  BIOCVERSION                Get filter language version.
  *  BIOCGHDRCMPLT      Get "header already complete" flag.
  *  BIOCSHDRCMPLT      Set "header already complete" flag.
+ *  BIOCFEEDBACK       Set packet feedback mode.
+ *  BIOCGFEEDBACK      Get packet feedback mode.
+ *  BIOCGSEESENT       Get "see sent packets" mode.
+ *  BIOCSSEESENT       Set "see sent packets" mode.
  */
 /* ARGSUSED */
 static int
@@ -971,6 +993,20 @@ bpf_ioctl(struct file *fp, u_long cmd, void *addr)
                d->bd_seesent = *(u_int *)addr;
                break;
 
+       /*
+        * Set "feed packets from bpf back to input" mode
+        */
+       case BIOCSFEEDBACK:
+               d->bd_feedback = *(u_int *)addr;
+               break;
+
+       /*
+        * Get "feed packets from bpf back to input" mode
+        */
+       case BIOCGFEEDBACK:
+               *(u_int *)addr = d->bd_feedback;
+               break;
+
        case FIONBIO:           /* Non-blocking I/O */
                /*
                 * No need to do anything special as we use IO_NDELAY in
@@ -1355,9 +1391,15 @@ bpf_mtap2(void *arg, void *data, u_int dlen, struct mbuf 
*m)
        u_int pktlen;
        struct mbuf mb;
 
+       /* Skip outgoing duplicate packets. */
+       if ((m->m_flags & M_PROMISC) != 0 && m->m_pkthdr.rcvif == NULL) {
+           m->m_flags &= ~M_PROMISC;
+           return;
+       }
+
        pktlen = m_length(m) + dlen;
 
-       /*
+        /*
         * Craft on-stack mbuf suitable for passing to bpf_filter.
         * Note that we cut corners here; we only setup what's
         * absolutely needed--this mbuf should never go anywhere else.
@@ -1381,6 +1423,12 @@ bpf_mtap(void *arg, struct mbuf *m)
        u_int pktlen, buflen;
        void *marg;
 
+       /* Skip outgoing duplicate packets. */
+       if ((m->m_flags & M_PROMISC) != 0 && m->m_pkthdr.rcvif == NULL) {
+           m->m_flags &= ~M_PROMISC;
+           return;
+       }
+
        pktlen = m_length(m);
 
        if (pktlen == m->m_len) {
diff --git a/sys/net/bpf.h b/sys/net/bpf.h
index cb0d585..a280b67 100644
--- a/sys/net/bpf.h
+++ b/sys/net/bpf.h
@@ -139,6 +139,9 @@ struct bpf_version {
 #define BIOCSSEESENT    _IOW('B',121, u_int)
 #define BIOCSRTIMEOUT   _IOW('B',122, struct timeval)
 #define BIOCGRTIMEOUT   _IOR('B',123, struct timeval)
+#define BIOCGFEEDBACK   _IOR('B',124, u_int)
+#define BIOCSFEEDBACK   _IOW('B',125, u_int)
+#define BIOCFEEDBACK     BIOCSFEEDBACK         /* FreeBSD name */
 
 /*
  * Structure prepended to each packet. This is "wire" format, so we
diff --git a/sys/net/bpfdesc.h b/sys/net/bpfdesc.h
index 7cc01ca..97729e5 100644
--- a/sys/net/bpfdesc.h
+++ b/sys/net/bpfdesc.h
@@ -79,6 +79,7 @@ struct bpf_d {
        u_char          bd_immediate;   /* true to return on packet arrival */
        int             bd_hdrcmplt;    /* false to fill in src lladdr */
        int             bd_seesent;     /* true if bpf should see sent packets 
*/
+       int             bd_feedback;    /* true to feed back sent packets */
        int             bd_async;       /* non-zero if packet reception should 
generate signal */
        pid_t           bd_pgid;        /* process or group id for signal */
 #if BSD < 199103
diff --git a/share/man/man4/bpf.4 b/share/man/man4/bpf.4
index 9507cf9..3f5ac41 100644
--- a/share/man/man4/bpf.4
+++ b/share/man/man4/bpf.4
@@ -297,8 +297,23 @@ By default, the flag is disabled (value is 0).
 Enable/disable or get the
 .Dq see sent
 flag status.
-If enabled, packets sent will be passed to the filter.
+If enabled, packets sent by the host (not from
+.Nm )
+will be passed to the filter.
 By default, the flag is enabled (value is 1).
+.It Dv BIOCFEEDBACK BIOCSFEEDBACK BIOCGFEEDBACK (u_int)
+Set (or get)
+.Dq packet feedback mode .
+This allows injected packets to be fed back as input to the interface when
+output via the interface is successful.
+The first name is meant for FreeBSD compatibility, the two others follow
+the Get/Set convention.
+.\"When
+.\".Dv BPF_D_INOUT
+.\"direction is set, injected
+Injected
+outgoing packets are not returned by BPF to avoid
+duplication. This flag is initialized to zero by default.
 .El
 .Sh STANDARD IOCTLS
 .Nm

-Olaf.
-- 
___ Olaf 'Rhialto' Seibert    -- You author it, and I'll reader it.
\X/ rhialto/at/xs4all.nl      -- Cetero censeo "authored" delendum esse.


Home | Main Index | Thread Index | Old Index