Imported Upstream version 3.2.2
[debian/gnuradio] / usrp2 / host / lib / pktfilter.cc
diff --git a/usrp2/host/lib/pktfilter.cc b/usrp2/host/lib/pktfilter.cc
new file mode 100644 (file)
index 0000000..1ccf904
--- /dev/null
@@ -0,0 +1,187 @@
+/* -*- c++ -*- */
+/*
+ * Copyright 2005,2007,2008 Free Software Foundation, Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "pktfilter.h"
+#include <iostream>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+#include <net/if.h>
+#include <features.h>
+#include <netpacket/packet.h>
+#include <net/ethernet.h>
+#include <netinet/in.h>
+#include <assert.h>
+#include <linux/types.h>
+#include <linux/filter.h>      // packet filter
+
+namespace usrp2 {
+  
+  /*
+   * This is all based on the Berkeley Packet Filter (BPF) as implemented on Linux.
+   *
+   * The BPF allows you to run an interpreted program (a filter) in the
+   * kernel that sorts through the packets looking for ones you are
+   * interested in.  This eliminates the overhead of passing all of the
+   * networking packets up into user space for filtering there.
+   *
+   * For documentation on this see
+   * /usr/src/linux/Documentation/networking/filter.txt, The BSD
+   * Berkeley Packet Filter manual page, and "The BSD Packet Filter: A
+   * New Architecture for User-level Packet Capture", by Steven McCanne
+   * and Van Jacobson.
+   */
+  
+  pktfilter::pktfilter ()
+    : d_len (0), d_inst (0)
+  {
+    // NOP
+  }
+  
+  pktfilter::~pktfilter ()
+  {
+    delete [] d_inst;
+  }
+  
+  inline static sock_filter
+  make_stmt (__u16 code, __u32 k)
+  {
+    sock_filter        f;
+    f.code = code;
+    f.jt = 0;
+    f.jf = 0;
+    f.k = k;
+    return f;
+  }
+  
+  inline static sock_filter
+  make_jump (__u16 code, __u32 k, __u8 jt, __u8 jf)
+  {
+    sock_filter        f;
+    f.code = code;
+    f.jt = jt;
+    f.jf = jf;
+    f.k = k;
+    return f;
+  }
+  
+  /*
+   * Return a filter that harvests packets with the specified ethertype.
+   */
+  pktfilter *
+  pktfilter::make_ethertype (unsigned short ethertype)
+  {
+    static const int MAX_LEN = 20;
+    sock_filter        *inst = new sock_filter [MAX_LEN];
+    pktfilter  *pf = new pktfilter ();
+    
+    // nothing quite like coding in assembly without the benefit of an assembler ;-)
+    
+    // ignore packets that don't have the right ethertype
+    
+    int i = 0;
+    inst[i++] = make_stmt (BPF_LD|BPF_H|BPF_ABS, 12);  // load ethertype
+    inst[i++] = make_jump (BPF_JMP|BPF_JEQ|BPF_K, ethertype, 1, 0);
+    inst[i++] = make_stmt (BPF_RET|BPF_K, 0);          // return 0 (ignore packet)
+    inst[i++] = make_stmt (BPF_RET|BPF_K, (unsigned) -1);      // return whole packet
+    
+    assert (i <= MAX_LEN);
+    
+    pf->d_inst = inst;
+    pf->d_len = i;
+    
+    return pf;
+  }
+  
+  /*
+   * Return a filter that harvests inbound packets with the specified ethertype.
+   * \param ethertype  the ethertype we're looking for
+   * \param our_mac    our ethernet MAC address so we can avoid pkts we sent
+   */
+  pktfilter *
+  pktfilter::make_ethertype_inbound (unsigned short ethertype, const unsigned char *our_mac)
+  {
+    static const int MAX_LEN = 20;
+    sock_filter        *inst = new sock_filter [MAX_LEN];
+    pktfilter  *pf = new pktfilter ();
+    
+    __u16 smac_hi = (our_mac[0] << 8) | our_mac[1];
+    __u32 smac_lo = (our_mac[2] << 24) | (our_mac[3] << 16) | (our_mac[4] << 8) | our_mac[5];
+    
+    // nothing quite like coding in assembly without the benefit of an assembler ;-)
+    
+    // ignore packets that have a different ethertype
+    // and packets that have a source mac address == our_mac (packets we sent)
+    
+    int i = 0;
+    inst[i++] = make_stmt (BPF_LD|BPF_H|BPF_ABS, 12);  // load ethertype
+    inst[i++] = make_jump (BPF_JMP|BPF_JEQ|BPF_K, ethertype, 0, 5);
+    inst[i++] = make_stmt (BPF_LD|BPF_W|BPF_ABS, 8);   // load low 32-bit of src mac
+    inst[i++] = make_jump (BPF_JMP|BPF_JEQ|BPF_K, smac_lo, 0, 2);
+    inst[i++] = make_stmt (BPF_LD|BPF_H|BPF_ABS, 6);   // load high 16-bits of src mac
+    inst[i++] = make_jump (BPF_JMP|BPF_JEQ|BPF_K, smac_hi, 1, 0);
+    inst[i++] = make_stmt (BPF_RET|BPF_K, (unsigned) -1);      // return whole packet
+    inst[i++] = make_stmt (BPF_RET|BPF_K, 0);          // return 0 (ignore packet)
+    
+    assert (i <= MAX_LEN);
+    
+    pf->d_inst = inst;
+    pf->d_len = i;
+    
+    return pf;
+  }
+  /*
+   * Return a filter that harvests inbound packets with the specified ethertype and target USRP2 MAC address.
+   * \param ethertype  the ethertype we're looking for
+   * \param usrp_mac    our target USRP2 MAC address
+   */
+  pktfilter *
+  pktfilter::make_ethertype_inbound_target (unsigned short ethertype, const unsigned char *usrp_mac)
+  {
+    static const int MAX_LEN = 20;
+    sock_filter        *inst = new sock_filter [MAX_LEN];
+    pktfilter  *pf = new pktfilter ();
+
+    __u16 tmac_hi = (usrp_mac[0] << 8) | usrp_mac[1];
+    __u32 tmac_lo = (usrp_mac[2] << 24) | (usrp_mac[3] << 16) | (usrp_mac[4] << 8) | usrp_mac[5];
+    
+    // ignore packets that have a different ethertype
+    // and only return packets that have a source mac address == usrp_mac
+    
+    int i = 0;
+    inst[i++] = make_stmt (BPF_LD|BPF_H|BPF_ABS, 12);  // load ethertype
+    inst[i++] = make_jump (BPF_JMP|BPF_JEQ|BPF_K, ethertype, 0, 5);
+    inst[i++] = make_stmt (BPF_LD|BPF_W|BPF_ABS, 8);   // load low 32-bit of src mac
+    inst[i++] = make_jump (BPF_JMP|BPF_JEQ|BPF_K, tmac_lo, 0, 3);
+    inst[i++] = make_stmt (BPF_LD|BPF_H|BPF_ABS, 6);   // load high 16-bits of src mac
+    inst[i++] = make_jump (BPF_JMP|BPF_JEQ|BPF_K, tmac_hi, 0, 1);
+    inst[i++] = make_stmt (BPF_RET|BPF_K, (unsigned) -1);      // return whole packet
+    inst[i++] = make_stmt (BPF_RET|BPF_K, 0);          // return 0 (ignore packet)
+    
+    assert (i <= MAX_LEN);
+    
+    pf->d_inst = inst;
+    pf->d_len = i;
+    
+    return pf;
+  }
+  
+} // namespace usrp2