1
0
mirror of git://projects.qi-hardware.com/openwrt-xburst.git synced 2024-11-28 15:05:20 +02:00
openwrt-xburst/target/linux/generic-2.6/patches/104-pf_ring.patch

1834 lines
48 KiB
Diff
Raw Normal View History

diff --unified --recursive --new-file linux-2.6.15-rc6/include/linux/ring.h linux-2.6.15-rc6-1-686-smp-ring3/include/linux/ring.h
--- linux-2.6.15-rc6/include/linux/ring.h 1970-01-01 01:00:00.000000000 +0100
+++ linux-2.6.15-rc6-1-686-smp-ring3/include/linux/ring.h 2005-12-24 00:24:01.000000000 +0100
@@ -0,0 +1,107 @@
+/*
+ * Definitions for packet ring
+ *
+ * 2004 - Luca Deri <deri@ntop.org>
+ */
+#ifndef __RING_H
+#define __RING_H
+
+
+#define INCLUDE_MAC_INFO
+
+#ifdef INCLUDE_MAC_INFO
+#define SKB_DISPLACEMENT 14 /* Include MAC address information */
+#else
+#define SKB_DISPLACEMENT 0 /* Do NOT include MAC address information */
+#endif
+
+#define RING_MAGIC
+#define RING_MAGIC_VALUE 0x88
+#define RING_FLOWSLOT_VERSION 6
+#define RING_VERSION "3.1"
+
+#define SO_ADD_TO_CLUSTER 99
+#define SO_REMOVE_FROM_CLUSTER 100
+#define SO_SET_REFLECTOR 101
+
+/* *********************************** */
+
+#ifndef HAVE_PCAP
+struct pcap_pkthdr {
+ struct timeval ts; /* time stamp */
+ u_int32_t caplen; /* length of portion present */
+ u_int32_t len; /* length this packet (off wire) */
+};
+#endif
+
+/* *********************************** */
+
+enum cluster_type {
+ cluster_per_flow = 0,
+ cluster_round_robin
+};
+
+/* *********************************** */
+
+#define RING_MIN_SLOT_SIZE (60+sizeof(struct pcap_pkthdr))
+#define RING_MAX_SLOT_SIZE (1514+sizeof(struct pcap_pkthdr))
+
+/* *********************************** */
+
+typedef struct flowSlotInfo {
+ u_int16_t version, sample_rate;
+ u_int32_t tot_slots, slot_len, data_len, tot_mem;
+
+ u_int64_t tot_pkts, tot_lost;
+ u_int64_t tot_insert, tot_read;
+ u_int32_t insert_idx, remove_idx;
+} FlowSlotInfo;
+
+/* *********************************** */
+
+typedef struct flowSlot {
+#ifdef RING_MAGIC
+ u_char magic; /* It must alwasy be zero */
+#endif
+ u_char slot_state; /* 0=empty, 1=full */
+ u_char bucket; /* bucket[bucketLen] */
+} FlowSlot;
+
+/* *********************************** */
+
+#ifdef __KERNEL__
+
+FlowSlotInfo* getRingPtr(void);
+int allocateRing(char *deviceName, u_int numSlots,
+ u_int bucketLen, u_int sampleRate);
+unsigned int pollRing(struct file *fp, struct poll_table_struct * wait);
+void deallocateRing(void);
+
+/* ************************* */
+
+typedef int (*handle_ring_skb)(struct sk_buff *skb,
+ u_char recv_packet, u_char real_skb);
+extern handle_ring_skb get_skb_ring_handler(void);
+extern void set_skb_ring_handler(handle_ring_skb the_handler);
+extern void do_skb_ring_handler(struct sk_buff *skb,
+ u_char recv_packet, u_char real_skb);
+
+typedef int (*handle_ring_buffer)(struct net_device *dev,
+ char *data, int len);
+extern handle_ring_buffer get_buffer_ring_handler(void);
+extern void set_buffer_ring_handler(handle_ring_buffer the_handler);
+extern int do_buffer_ring_handler(struct net_device *dev,
+ char *data, int len);
+#endif /* __KERNEL__ */
+
+/* *********************************** */
+
+#define PF_RING 27 /* Packet Ring */
+#define SOCK_RING PF_RING
+
+/* ioctl() */
+#define SIORINGPOLL 0x8888
+
+/* *********************************** */
+
+#endif /* __RING_H */
diff --unified --recursive --new-file linux-2.6.15-rc6/net/core/dev.c linux-2.6.15-rc6-1-686-smp-ring3/net/core/dev.c
--- linux-2.6.15-rc6/net/core/dev.c 2005-12-19 01:36:54.000000000 +0100
+++ linux-2.6.15-rc6-1-686-smp-ring3/net/core/dev.c 2005-12-24 00:24:02.000000000 +0100
@@ -115,6 +115,56 @@
#endif /* CONFIG_NET_RADIO */
#include <asm/current.h>
+#if defined (CONFIG_RING) || defined(CONFIG_RING_MODULE)
+
+/* #define RING_DEBUG */
+
+#include <linux/ring.h>
+#include <linux/version.h>
+
+static handle_ring_skb ring_handler = NULL;
+
+handle_ring_skb get_skb_ring_handler() { return(ring_handler); }
+
+void set_skb_ring_handler(handle_ring_skb the_handler) {
+ ring_handler = the_handler;
+}
+
+void do_skb_ring_handler(struct sk_buff *skb,
+ u_char recv_packet, u_char real_skb) {
+ if(ring_handler)
+ ring_handler(skb, recv_packet, real_skb);
+}
+
+/* ******************* */
+
+static handle_ring_buffer buffer_ring_handler = NULL;
+
+handle_ring_buffer get_buffer_ring_handler() { return(buffer_ring_handler); }
+
+void set_buffer_ring_handler(handle_ring_buffer the_handler) {
+ buffer_ring_handler = the_handler;
+}
+
+int do_buffer_ring_handler(struct net_device *dev, char *data, int len) {
+ if(buffer_ring_handler) {
+ buffer_ring_handler(dev, data, len);
+ return(1);
+ } else
+ return(0);
+}
+
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0))
+EXPORT_SYMBOL(get_skb_ring_handler);
+EXPORT_SYMBOL(set_skb_ring_handler);
+EXPORT_SYMBOL(do_skb_ring_handler);
+
+EXPORT_SYMBOL(get_buffer_ring_handler);
+EXPORT_SYMBOL(set_buffer_ring_handler);
+EXPORT_SYMBOL(do_buffer_ring_handler);
+#endif
+
+#endif
/*
* The list of packet types we will receive (as opposed to discard)
* and the routines to invoke.
@@ -1296,6 +1346,10 @@
skb->tc_verd = SET_TC_AT(skb->tc_verd,AT_EGRESS);
#endif
if (q->enqueue) {
+#if defined (CONFIG_RING) || defined(CONFIG_RING_MODULE)
+ if(ring_handler) ring_handler(skb, 0, 1);
+#endif /* CONFIG_RING */
+
/* Grab device queue */
spin_lock(&dev->queue_lock);
@@ -1437,6 +1491,13 @@
preempt_disable();
err = netif_rx(skb);
+#if defined (CONFIG_RING) || defined(CONFIG_RING_MODULE)
+ if(ring_handler && ring_handler(skb, 1, 1)) {
+ /* The packet has been copied into a ring */
+ return(NET_RX_SUCCESS);
+ }
+#endif /* CONFIG_RING */
+
if (local_softirq_pending())
do_softirq();
preempt_enable();
@@ -1582,6 +1643,13 @@
struct net_device *orig_dev;
int ret = NET_RX_DROP;
unsigned short type;
+#if defined (CONFIG_RING) || defined(CONFIG_RING_MODULE)
+ if(ring_handler && ring_handler(skb, 1, 1)) {
+ /* The packet has been copied into a ring */
+ return(NET_RX_SUCCESS);
+ }
+#endif /* CONFIG_RING */
+
/* if we've gotten here through NAPI, check netpoll */
if (skb->dev->poll && netpoll_rx(skb))
diff --unified --recursive --new-file linux-2.6.15-rc6/net/Kconfig linux-2.6.15-rc6-1-686-smp-ring3/net/Kconfig
--- linux-2.6.15-rc6/net/Kconfig 2005-12-19 01:36:54.000000000 +0100
+++ linux-2.6.15-rc6-1-686-smp-ring3/net/Kconfig 2005-12-24 00:24:02.000000000 +0100
@@ -31,6 +31,7 @@
source "net/unix/Kconfig"
source "net/xfrm/Kconfig"
+source "net/ring/Kconfig"
config INET
bool "TCP/IP networking"
---help---
diff --unified --recursive --new-file linux-2.6.15-rc6/net/Makefile linux-2.6.15-rc6-1-686-smp-ring3/net/Makefile
--- linux-2.6.15-rc6/net/Makefile 2005-12-19 01:36:54.000000000 +0100
+++ linux-2.6.15-rc6-1-686-smp-ring3/net/Makefile 2005-12-24 00:24:02.000000000 +0100
@@ -42,6 +42,7 @@
obj-$(CONFIG_DECNET) += decnet/
obj-$(CONFIG_ECONET) += econet/
obj-$(CONFIG_VLAN_8021Q) += 8021q/
+obj-$(CONFIG_RING) += ring/
obj-$(CONFIG_IP_DCCP) += dccp/
obj-$(CONFIG_IP_SCTP) += sctp/
obj-$(CONFIG_IEEE80211) += ieee80211/
diff --unified --recursive --new-file linux-2.6.15-rc6/net/ring/Kconfig linux-2.6.15-rc6-1-686-smp-ring3/net/ring/Kconfig
--- linux-2.6.15-rc6/net/ring/Kconfig 1970-01-01 01:00:00.000000000 +0100
+++ linux-2.6.15-rc6-1-686-smp-ring3/net/ring/Kconfig 2005-12-24 00:24:02.000000000 +0100
@@ -0,0 +1,14 @@
+config RING
+ tristate "PF_RING sockets (EXPERIMENTAL)"
+ depends on EXPERIMENTAL
+ ---help---
+ PF_RING socket family, optimized for packet capture.
+ If a PF_RING socket is bound to an adapter (via the bind() system
+ call), such adapter will be used in read-only mode until the socket
+ is destroyed. Whenever an incoming packet is received from the adapter
+ it will not passed to upper layers, but instead it is copied to a ring
+ buffer, which in turn is exported to user space applications via mmap.
+ Please refer to http://luca.ntop.org/Ring.pdf for more.
+
+ Say N unless you know what you are doing.
+
diff --unified --recursive --new-file linux-2.6.15-rc6/net/ring/Makefile linux-2.6.15-rc6-1-686-smp-ring3/net/ring/Makefile
--- linux-2.6.15-rc6/net/ring/Makefile 1970-01-01 01:00:00.000000000 +0100
+++ linux-2.6.15-rc6-1-686-smp-ring3/net/ring/Makefile 2005-12-24 00:24:02.000000000 +0100
@@ -0,0 +1,7 @@
+#
+# Makefile for the ring driver.
+#
+
+obj-m += ring.o
+
+ring-objs := ring_packet.o
diff --unified --recursive --new-file linux-2.6.15-rc6/net/ring/ring_packet.c linux-2.6.15-rc6-1-686-smp-ring3/net/ring/ring_packet.c
--- linux-2.6.15-rc6/net/ring/ring_packet.c 1970-01-01 01:00:00.000000000 +0100
+++ linux-2.6.15-rc6-1-686-smp-ring3/net/ring/ring_packet.c 2005-12-24 00:24:02.000000000 +0100
@@ -0,0 +1,1568 @@
+/*
+ *
+ * (C) 2004-05 - Luca Deri <deri@ntop.org>
+ *
+ * This code includes contributions courtesy of
+ * - Jeff Randall <jrandall@nexvu.com>
+ * - Helmut Manck <helmut.manck@secunet.com>
+ * - Brad Doctor <brad@stillsecure.com>
+ * - Amit D. Chaudhary <amit_ml@rajgad.com>
+ *
+ */
+
+/*
+ TO DO:
+ add an entry inside the /proc filesystem
+*/
+
+#include <linux/version.h>
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/socket.h>
+#include <linux/skbuff.h>
+#include <linux/rtnetlink.h>
+#include <linux/in.h>
+#include <linux/in6.h>
+#include <linux/init.h>
+#include <linux/filter.h>
+#include <linux/ring.h>
+#include <linux/ip.h>
+#include <linux/tcp.h>
+#include <linux/udp.h>
+#include <linux/list.h>
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0))
+#include <net/xfrm.h>
+#else
+#include <linux/poll.h>
+#endif
+#include <net/sock.h>
+#include <asm/io.h> /* needed for virt_to_phys() */
+
+/* #define RING_DEBUG */
+
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,11))
+static inline int remap_page_range(struct vm_area_struct *vma,
+ unsigned long uvaddr,
+ unsigned long paddr,
+ unsigned long size,
+ pgprot_t prot) {
+ return(remap_pfn_range(vma, uvaddr, paddr >> PAGE_SHIFT,
+ size, prot));
+}
+#endif
+
+/* ************************************************* */
+
+#define CLUSTER_LEN 8
+
+struct ring_cluster {
+ u_short cluster_id; /* 0 = no cluster */
+ u_short num_cluster_elements;
+ enum cluster_type hashing_mode;
+ u_short hashing_id;
+ struct sock *sk[CLUSTER_LEN];
+ struct ring_cluster *next; /* NULL = last element of the cluster */
+};
+
+/* ************************************************* */
+
+struct ring_element {
+ struct list_head list;
+ struct sock *sk;
+};
+
+/* ************************************************* */
+
+struct ring_opt {
+ struct net_device *ring_netdev;
+
+ /* Cluster */
+ u_short cluster_id; /* 0 = no cluster */
+
+ /* Reflector */
+ struct net_device *reflector_dev;
+
+ /* Packet buffers */
+ unsigned long order;
+
+ /* Ring Slots */
+ unsigned long ring_memory;
+ FlowSlotInfo *slots_info; /* Basically it points to ring_memory */
+ char *ring_slots; /* Basically it points to ring_memory
+ +sizeof(FlowSlotInfo) */
+
+ /* Packet Sampling */
+ u_int pktToSample, sample_rate;
+
+ /* BPF Filter */
+ struct sk_filter *bpfFilter;
+
+ /* Locks */
+ atomic_t num_ring_slots_waiters;
+ wait_queue_head_t ring_slots_waitqueue;
+ rwlock_t ring_index_lock;
+
+ /* Indexes (Internal) */
+ u_int insert_page_id, insert_slot_id;
+};
+
+/* ************************************************* */
+
+/* List of all ring sockets. */
+static struct list_head ring_table;
+
+/* List of all clusters */
+static struct ring_cluster *ring_cluster_list;
+
+static rwlock_t ring_mgmt_lock = RW_LOCK_UNLOCKED;
+
+/* ********************************** */
+
+/* Forward */
+static struct proto_ops ring_ops;
+
+#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,11))
+static struct proto ring_proto;
+#endif
+
+static int skb_ring_handler(struct sk_buff *skb, u_char recv_packet,
+ u_char real_skb);
+static int buffer_ring_handler(struct net_device *dev, char *data, int len);
+static int remove_from_cluster(struct sock *sock, struct ring_opt *pfr);
+
+/* Extern */
+
+/* ********************************** */
+
+/* Defaults */
+static u_int bucket_len = 128, num_slots = 4096, sample_rate = 1,
+ transparent_mode = 1, enable_tx_capture = 0;
+
+MODULE_PARM(bucket_len, "i");
+MODULE_PARM_DESC(bucket_len, "Number of ring buckets");
+MODULE_PARM(num_slots, "i");
+MODULE_PARM_DESC(num_slots, "Number of ring slots");
+MODULE_PARM(sample_rate, "i");
+MODULE_PARM_DESC(sample_rate, "Ring packet sample rate");
+MODULE_PARM(transparent_mode, "i");
+MODULE_PARM_DESC(transparent_mode,
+ "Set to 1 to set transparent mode "
+ "(slower but backwards compatible)");
+MODULE_PARM(enable_tx_capture, "i");
+MODULE_PARM_DESC(enable_tx_capture, "Set to 1 to capture outgoing packets");
+
+/* ********************************** */
+
+#define MIN_QUEUED_PKTS 64
+#define MAX_QUEUE_LOOPS 64
+
+
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0))
+#define ring_sk_datatype(__sk) ((struct ring_opt *)__sk)
+#define ring_sk(__sk) ((__sk)->sk_protinfo)
+#else
+#define ring_sk_datatype(a) (a)
+#define ring_sk(__sk) ((__sk)->protinfo.pf_ring)
+#endif
+
+#define _rdtsc() ({ uint64_t x; asm volatile("rdtsc" : "=A" (x)); x; })
+
+/*
+ int dev_queue_xmit(struct sk_buff *skb)
+ skb->dev;
+ struct net_device *dev_get_by_name(const char *name)
+*/
+
+/* ********************************** */
+
+static void ring_sock_destruct(struct sock *sk) {
+
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0))
+ skb_queue_purge(&sk->sk_receive_queue);
+
+ if (!sock_flag(sk, SOCK_DEAD)) {
+#if defined(RING_DEBUG)
+ printk("Attempt to release alive ring socket: %p\n", sk);
+#endif
+ return;
+ }
+
+ BUG_TRAP(!atomic_read(&sk->sk_rmem_alloc));
+ BUG_TRAP(!atomic_read(&sk->sk_wmem_alloc));
+#else
+
+ BUG_TRAP(atomic_read(&sk->rmem_alloc)==0);
+ BUG_TRAP(atomic_read(&sk->wmem_alloc)==0);
+
+ if (!sk->dead) {
+#if defined(RING_DEBUG)
+ printk("Attempt to release alive ring socket: %p\n", sk);
+#endif
+ return;
+ }
+#endif
+
+ kfree(ring_sk(sk));
+
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0))
+ MOD_DEC_USE_COUNT;
+#endif
+}
+
+/* ********************************** */
+/*
+ * ring_insert()
+ *
+ * store the sk in a new element and add it
+ * to the head of the list.
+ */
+static inline void ring_insert(struct sock *sk) {
+ struct ring_element *next;
+
+#if defined(RING_DEBUG)
+ printk("RING: ring_insert()\n");
+#endif
+
+ next = kmalloc(sizeof(struct ring_element), GFP_ATOMIC);
+ if(next != NULL) {
+ next->sk = sk;
+ write_lock_irq(&ring_mgmt_lock);
+ list_add(&next->list, &ring_table);
+ write_unlock_irq(&ring_mgmt_lock);
+ } else {
+ if (net_ratelimit())
+ printk("RING: could not kmalloc slot!!\n");
+ }
+}
+
+/* ********************************** */
+/*
+ * ring_remove()
+ *
+ * For each of the elements in the list:
+ * - check if this is the element we want to delete
+ * - if it is, remove it from the list, and free it.
+ *
+ * stop when we find the one we're looking for (break),
+ * or when we reach the end of the list.
+ */
+static inline void ring_remove(struct sock *sk) {
+ struct list_head *ptr;
+ struct ring_element *entry;
+
+
+ for(ptr = ring_table.next; ptr != &ring_table; ptr = ptr->next) {
+ entry = list_entry(ptr, struct ring_element, list);
+
+ if(entry->sk == sk) {
+ write_lock_irq(&ring_mgmt_lock);
+ list_del(ptr);
+ kfree(ptr);
+ write_unlock_irq(&ring_mgmt_lock);
+ break;
+ }
+ }
+
+}
+
+/* ********************************** */
+
+static u_int32_t num_queued_pkts(struct ring_opt *pfr) {
+
+ if(pfr->ring_slots != NULL) {
+
+ u_int32_t tot_insert = pfr->slots_info->insert_idx,
+#if defined(RING_DEBUG)
+ tot_read = pfr->slots_info->tot_read, tot_pkts;
+#else
+ tot_read = pfr->slots_info->tot_read;
+#endif
+
+ if(tot_insert >= tot_read) {
+#if defined(RING_DEBUG)
+ tot_pkts = tot_insert-tot_read;
+#endif
+ return(tot_insert-tot_read);
+ } else {
+#if defined(RING_DEBUG)
+ tot_pkts = ((u_int32_t)-1)+tot_insert-tot_read;
+#endif
+ return(((u_int32_t)-1)+tot_insert-tot_read);
+ }
+
+#if defined(RING_DEBUG)
+ printk("-> num_queued_pkts=%d [tot_insert=%d][tot_read=%d]\n",
+ tot_pkts, tot_insert, tot_read);
+#endif
+
+ } else
+ return(0);
+}
+
+/* ********************************** */
+
+static inline FlowSlot* get_insert_slot(struct ring_opt *pfr) {
+#if defined(RING_DEBUG)
+ printk("get_insert_slot(%d)\n", pfr->slots_info->insert_idx);
+#endif
+
+ if(pfr->ring_slots != NULL) {
+ FlowSlot *slot = (FlowSlot*)&(pfr->ring_slots[pfr->slots_info->insert_idx
+ *pfr->slots_info->slot_len]);
+ return(slot);
+ } else
+ return(NULL);
+}
+
+/* ********************************** */
+
+static inline FlowSlot* get_remove_slot(struct ring_opt *pfr) {
+#if defined(RING_DEBUG)
+ printk("get_remove_slot(%d)\n", pfr->slots_info->remove_idx);
+#endif
+
+ if(pfr->ring_slots != NULL)
+ return((FlowSlot*)&(pfr->ring_slots[pfr->slots_info->remove_idx*
+ pfr->slots_info->slot_len]));
+ else
+ return(NULL);
+}
+
+/* ********************************** */
+
+static void add_skb_to_ring(struct sk_buff *skb,
+ struct ring_opt *pfr,
+ u_char recv_packet,
+ u_char real_skb /* 1=skb 0=faked skb */) {
+ FlowSlot *theSlot;
+ int idx, displ;
+
+ if(recv_packet) {
+ /* Hack for identifying a packet received by the e1000 */
+ if(real_skb) {
+ displ = SKB_DISPLACEMENT;
+ } else
+ displ = 0; /* Received by the e1000 wrapper */
+ } else
+ displ = 0;
+
+ write_lock(&pfr->ring_index_lock);
+ pfr->slots_info->tot_pkts++;
+ write_unlock(&pfr->ring_index_lock);
+
+ /* BPF Filtering (from af_packet.c) */
+ if(pfr->bpfFilter != NULL) {
+ unsigned res = 1, len;
+
+ len = skb->len-skb->data_len;
+
+ write_lock(&pfr->ring_index_lock);
+ skb->data -= displ;
+ res = sk_run_filter(skb, pfr->bpfFilter->insns, pfr->bpfFilter->len);
+ skb->data += displ;
+ write_unlock(&pfr->ring_index_lock);
+
+ if(res == 0) {
+ /* Filter failed */
+
+#if defined(RING_DEBUG)
+ printk("add_skb_to_ring(skb): Filter failed [len=%d][tot=%llu]"
+ "[insertIdx=%d][pkt_type=%d][cloned=%d]\n",
+ (int)skb->len, pfr->slots_info->tot_pkts,
+ pfr->slots_info->insert_idx,
+ skb->pkt_type, skb->cloned);
+#endif
+
+ return;
+ }
+ }
+
+ /* ************************** */
+
+ if(pfr->sample_rate > 1) {
+ if(pfr->pktToSample == 0) {
+ write_lock(&pfr->ring_index_lock);
+ pfr->pktToSample = pfr->sample_rate;
+ write_unlock(&pfr->ring_index_lock);
+ } else {
+ write_lock(&pfr->ring_index_lock);
+ pfr->pktToSample--;
+ write_unlock(&pfr->ring_index_lock);
+
+#if defined(RING_DEBUG)
+ printk("add_skb_to_ring(skb): sampled packet [len=%d]"
+ "[tot=%llu][insertIdx=%d][pkt_type=%d][cloned=%d]\n",
+ (int)skb->len, pfr->slots_info->tot_pkts,
+ pfr->slots_info->insert_idx,
+ skb->pkt_type, skb->cloned);
+#endif
+ return;
+ }
+ }
+
+ /* ************************************* */
+
+ if((pfr->reflector_dev != NULL)
+ && (!netif_queue_stopped(pfr->reflector_dev))) {
+ int cpu = smp_processor_id();
+
+ /* increase reference counter so that this skb is not freed */
+ atomic_inc(&skb->users);
+
+ skb->data -= displ;
+
+ /* send it */
+ if (pfr->reflector_dev->xmit_lock_owner != cpu) {
+ spin_lock_bh(&pfr->reflector_dev->xmit_lock);
+ pfr->reflector_dev->xmit_lock_owner = cpu;
+ spin_unlock_bh(&pfr->reflector_dev->xmit_lock);
+
+ if (pfr->reflector_dev->hard_start_xmit(skb,
+ pfr->reflector_dev) == 0) {
+ spin_lock_bh(&pfr->reflector_dev->xmit_lock);
+ pfr->reflector_dev->xmit_lock_owner = -1;
+ skb->data += displ;
+ spin_unlock_bh(&pfr->reflector_dev->xmit_lock);
+#if defined(RING_DEBUG)
+ printk("++ hard_start_xmit succeeded\n");
+#endif
+ return; /* OK */
+ }
+
+ spin_lock_bh(&pfr->reflector_dev->xmit_lock);
+ pfr->reflector_dev->xmit_lock_owner = -1;
+ spin_unlock_bh(&pfr->reflector_dev->xmit_lock);
+ }
+
+#if defined(RING_DEBUG)
+ printk("++ hard_start_xmit failed\n");
+#endif
+ skb->data += displ;
+ return; /* -ENETDOWN */
+ }
+
+ /* ************************************* */
+
+#if defined(RING_DEBUG)
+ printk("add_skb_to_ring(skb) [len=%d][tot=%llu][insertIdx=%d]"
+ "[pkt_type=%d][cloned=%d]\n",
+ (int)skb->len, pfr->slots_info->tot_pkts,
+ pfr->slots_info->insert_idx,
+ skb->pkt_type, skb->cloned);
+#endif
+
+ idx = pfr->slots_info->insert_idx;
+ theSlot = get_insert_slot(pfr);
+
+ if((theSlot != NULL) && (theSlot->slot_state == 0)) {
+ struct pcap_pkthdr *hdr;
+ char *bucket;
+
+ /* Update Index */
+ idx++;
+
+ if(idx == pfr->slots_info->tot_slots) {
+ write_lock(&pfr->ring_index_lock);
+ pfr->slots_info->insert_idx = 0;
+ write_unlock(&pfr->ring_index_lock);
+ } else {
+ write_lock(&pfr->ring_index_lock);
+ pfr->slots_info->insert_idx = idx;
+ write_unlock(&pfr->ring_index_lock);
+ }
+
+ bucket = &theSlot->bucket;
+ hdr = (struct pcap_pkthdr*)bucket;
+
+ /* BD - API changed for time keeping */
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,14))
+ if(skb->stamp.tv_sec == 0) do_gettimeofday(&skb->stamp);
+
+ hdr->ts.tv_sec = skb->stamp.tv_sec, hdr->ts.tv_usec = skb->stamp.tv_usec;
+#else
+ if(skb->tstamp.off_sec == 0) __net_timestamp(skb);
+
+ hdr->ts.tv_sec = skb->tstamp.off_sec, hdr->ts.tv_usec = skb->tstamp.off_usec;
+#endif
+
+ hdr->caplen = skb->len+displ;
+
+ if(hdr->caplen > pfr->slots_info->data_len)
+ hdr->caplen = pfr->slots_info->data_len;
+
+ hdr->len = skb->len+displ;
+ memcpy(&bucket[sizeof(struct pcap_pkthdr)],
+ skb->data-displ, hdr->caplen);
+
+#if defined(RING_DEBUG)
+ {
+ static unsigned int lastLoss = 0;
+
+ if(pfr->slots_info->tot_lost
+ && (lastLoss != pfr->slots_info->tot_lost)) {
+ printk("add_skb_to_ring(%d): [data_len=%d]"
+ "[hdr.caplen=%d][skb->len=%d]"
+ "[pcap_pkthdr=%d][removeIdx=%d]"
+ "[loss=%lu][page=%u][slot=%u]\n",
+ idx-1, pfr->slots_info->data_len, hdr->caplen, skb->len,
+ sizeof(struct pcap_pkthdr),
+ pfr->slots_info->remove_idx,
+ (long unsigned int)pfr->slots_info->tot_lost,
+ pfr->insert_page_id, pfr->insert_slot_id);
+
+ lastLoss = pfr->slots_info->tot_lost;
+ }
+ }
+#endif
+
+ write_lock(&pfr->ring_index_lock);
+ pfr->slots_info->tot_insert++;
+ theSlot->slot_state = 1;
+ write_unlock(&pfr->ring_index_lock);
+ } else {
+ write_lock(&pfr->ring_index_lock);
+ pfr->slots_info->tot_lost++;
+ write_unlock(&pfr->ring_index_lock);
+
+#if defined(RING_DEBUG)
+ printk("add_skb_to_ring(skb): packet lost [loss=%lu]"
+ "[removeIdx=%u][insertIdx=%u]\n",
+ (long unsigned int)pfr->slots_info->tot_lost,
+ pfr->slots_info->remove_idx, pfr->slots_info->insert_idx);
+#endif
+ }
+
+ /* wakeup in case of poll() */
+ if(waitqueue_active(&pfr->ring_slots_waitqueue))
+ wake_up_interruptible(&pfr->ring_slots_waitqueue);
+}
+
+/* ********************************** */
+
+static u_int hash_skb(struct ring_cluster *cluster_ptr,
+ struct sk_buff *skb, u_char recv_packet) {
+ u_int idx;
+ int displ;
+ struct iphdr *ip;
+
+ if(cluster_ptr->hashing_mode == cluster_round_robin) {
+ idx = cluster_ptr->hashing_id++;
+ } else {
+ /* Per-flow clustering */
+ if(skb->len > sizeof(struct iphdr)+sizeof(struct tcphdr)) {
+ if(recv_packet)
+ displ = 0;
+ else
+ displ = SKB_DISPLACEMENT;
+
+ /*
+ skb->data+displ
+
+ Always points to to the IP part of the packet
+ */
+
+ ip = (struct iphdr*)(skb->data+displ);
+
+ idx = ip->saddr+ip->daddr+ip->protocol;
+
+ if(ip->protocol == IPPROTO_TCP) {
+ struct tcphdr *tcp = (struct tcphdr*)(skb->data+displ
+ +sizeof(struct iphdr));
+ idx += tcp->source+tcp->dest;
+ } else if(ip->protocol == IPPROTO_UDP) {
+ struct udphdr *udp = (struct udphdr*)(skb->data+displ
+ +sizeof(struct iphdr));
+ idx += udp->source+udp->dest;
+ }
+ } else
+ idx = skb->len;
+ }
+
+ return(idx % cluster_ptr->num_cluster_elements);
+}
+
+/* ********************************** */
+
+static int skb_ring_handler(struct sk_buff *skb,
+ u_char recv_packet,
+ u_char real_skb /* 1=skb 0=faked skb */) {
+ struct sock *skElement;
+ int rc = 0;
+ struct list_head *ptr;
+ struct ring_cluster *cluster_ptr;
+
+#ifdef PROFILING
+ uint64_t rdt = _rdtsc(), rdt1, rdt2;
+#endif
+
+ if((!skb) /* Invalid skb */
+ || ((!enable_tx_capture) && (!recv_packet))) {
+ /*
+ An outgoing packet is about to be sent out
+ but we decided not to handle transmitted
+ packets.
+ */
+ return(0);
+ }
+
+#if defined(RING_DEBUG)
+ if(0) {
+ printk("skb_ring_handler() [len=%d][dev=%s]\n", skb->len,
+ skb->dev->name == NULL ? "<NULL>" : skb->dev->name);
+ }
+#endif
+
+#ifdef PROFILING
+ rdt1 = _rdtsc();
+#endif
+
+ /* [1] Check unclustered sockets */
+ for (ptr = ring_table.next; ptr != &ring_table; ptr = ptr->next) {
+ struct ring_opt *pfr;
+ struct ring_element *entry;
+
+ entry = list_entry(ptr, struct ring_element, list);
+
+ read_lock(&ring_mgmt_lock);
+ skElement = entry->sk;
+ pfr = ring_sk(skElement);
+ read_unlock(&ring_mgmt_lock);
+
+ if((pfr != NULL)
+ && (pfr->cluster_id == 0 /* No cluster */)
+ && (pfr->ring_slots != NULL)
+ && (pfr->ring_netdev == skb->dev)) {
+ /* We've found the ring where the packet can be stored */
+ read_lock(&ring_mgmt_lock);
+ add_skb_to_ring(skb, pfr, recv_packet, real_skb);
+ read_unlock(&ring_mgmt_lock);
+
+ rc = 1; /* Ring found: we've done our job */
+ }
+ }
+
+ /* [2] Check socket clusters */
+ cluster_ptr = ring_cluster_list;
+
+ while(cluster_ptr != NULL) {
+ struct ring_opt *pfr;
+
+ if(cluster_ptr->num_cluster_elements > 0) {
+ u_int skb_hash = hash_skb(cluster_ptr, skb, recv_packet);
+
+ read_lock(&ring_mgmt_lock);
+ skElement = cluster_ptr->sk[skb_hash];
+ read_unlock(&ring_mgmt_lock);
+
+ if(skElement != NULL) {
+ pfr = ring_sk(skElement);
+
+ if((pfr != NULL)
+ && (pfr->ring_slots != NULL)
+ && (pfr->ring_netdev == skb->dev)) {
+ /* We've found the ring where the packet can be stored */
+ read_lock(&ring_mgmt_lock);
+ add_skb_to_ring(skb, pfr, recv_packet, real_skb);
+ read_unlock(&ring_mgmt_lock);
+
+ rc = 1; /* Ring found: we've done our job */
+ }
+ }
+ }
+
+ cluster_ptr = cluster_ptr->next;
+ }
+
+#ifdef PROFILING
+ rdt1 = _rdtsc()-rdt1;
+#endif
+
+#ifdef PROFILING
+ rdt2 = _rdtsc();
+#endif
+
+ if(transparent_mode) rc = 0;
+
+ if((rc != 0) && real_skb)
+ dev_kfree_skb(skb); /* Free the skb */
+
+#ifdef PROFILING
+ rdt2 = _rdtsc()-rdt2;
+ rdt = _rdtsc()-rdt;
+
+#if defined(RING_DEBUG)
+ printk("# cycles: %d [lock costed %d %d%%][free costed %d %d%%]\n",
+ (int)rdt, rdt-rdt1,
+ (int)((float)((rdt-rdt1)*100)/(float)rdt),
+ rdt2,
+ (int)((float)(rdt2*100)/(float)rdt));
+#endif
+#endif
+
+ return(rc); /* 0 = packet not handled */
+}
+
+/* ********************************** */
+
+struct sk_buff skb;
+
+static int buffer_ring_handler(struct net_device *dev,
+ char *data, int len) {
+
+#if defined(RING_DEBUG)
+ printk("buffer_ring_handler: [dev=%s][len=%d]\n",
+ dev->name == NULL ? "<NULL>" : dev->name, len);
+#endif
+
+ /* BD - API changed for time keeping */
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,14))
+ skb.dev = dev, skb.len = len, skb.data = data,
+ skb.data_len = len, skb.stamp.tv_sec = 0; /* Calculate the time */
+#else
+ skb.dev = dev, skb.len = len, skb.data = data,
+ skb.data_len = len, skb.tstamp.off_sec = 0; /* Calculate the time */
+#endif
+
+ skb_ring_handler(&skb, 1, 0 /* fake skb */);
+
+ return(0);
+}
+
+/* ********************************** */
+
+static int ring_create(struct socket *sock, int protocol) {
+ struct sock *sk;
+ struct ring_opt *pfr;
+ int err;
+
+#if defined(RING_DEBUG)
+ printk("RING: ring_create()\n");
+#endif
+
+ /* Are you root, superuser or so ? */
+ if(!capable(CAP_NET_ADMIN))
+ return -EPERM;
+
+ if(sock->type != SOCK_RAW)
+ return -ESOCKTNOSUPPORT;
+
+ if(protocol != htons(ETH_P_ALL))
+ return -EPROTONOSUPPORT;
+
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0))
+ MOD_INC_USE_COUNT;
+#endif
+
+ err = -ENOMEM;
+
+ // BD: -- broke this out to keep it more simple and clear as to what the
+ // options are.
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0))
+#if (LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,11))
+ sk = sk_alloc(PF_RING, GFP_KERNEL, 1, NULL);
+#else
+ // BD: API changed in 2.6.12, ref:
+ // http://svn.clkao.org/svnweb/linux/revision/?rev=28201
+ sk = sk_alloc(PF_RING, GFP_ATOMIC, &ring_proto, 1);
+#endif
+#else
+ /* Kernel 2.4 */
+ sk = sk_alloc(PF_RING, GFP_KERNEL, 1);
+#endif
+
+ if (sk == NULL)
+ goto out;
+
+ sock->ops = &ring_ops;
+ sock_init_data(sock, sk);
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0))
+#if (LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,11))
+ sk_set_owner(sk, THIS_MODULE);
+#endif
+#endif
+
+ err = -ENOMEM;
+ ring_sk(sk) = ring_sk_datatype(kmalloc(sizeof(*pfr), GFP_KERNEL));
+
+ if (!(pfr = ring_sk(sk))) {
+ sk_free(sk);
+ goto out;
+ }
+ memset(pfr, 0, sizeof(*pfr));
+ init_waitqueue_head(&pfr->ring_slots_waitqueue);
+ pfr->ring_index_lock = RW_LOCK_UNLOCKED;
+ atomic_set(&pfr->num_ring_slots_waiters, 0);
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0))
+ sk->sk_family = PF_RING;
+ sk->sk_destruct = ring_sock_destruct;
+#else
+ sk->family = PF_RING;
+ sk->destruct = ring_sock_destruct;
+ sk->num = protocol;
+#endif
+
+ ring_insert(sk);
+
+#if defined(RING_DEBUG)
+ printk("RING: ring_create() - created\n");
+#endif
+
+ return(0);
+ out:
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0))
+ MOD_DEC_USE_COUNT;
+#endif
+ return err;
+}
+
+/* *********************************************** */
+
+static int ring_release(struct socket *sock)
+{
+ struct sock *sk = sock->sk;
+ struct ring_opt *pfr = ring_sk(sk);
+
+ if(!sk)
+ return 0;
+
+#if defined(RING_DEBUG)
+ printk("RING: called ring_release\n");
+#endif
+
+#if defined(RING_DEBUG)
+ printk("RING: ring_release entered\n");
+#endif
+
+ ring_remove(sk);
+
+ sock_orphan(sk);
+ sock->sk = NULL;
+
+ /* Free the ring buffer */
+ if(pfr->ring_memory) {
+ struct page *page, *page_end;
+
+ page_end = virt_to_page(pfr->ring_memory + (PAGE_SIZE << pfr->order) - 1);
+ for(page = virt_to_page(pfr->ring_memory); page <= page_end; page++)
+ ClearPageReserved(page);
+
+ free_pages(pfr->ring_memory, pfr->order);
+ }
+
+ kfree(pfr);
+ ring_sk(sk) = NULL;
+
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0))
+ skb_queue_purge(&sk->sk_write_queue);
+#endif
+ sock_put(sk);
+
+#if defined(RING_DEBUG)
+ printk("RING: ring_release leaving\n");
+#endif
+
+ return 0;
+}
+
+/* ********************************** */
+/*
+ * We create a ring for this socket and bind it to the specified device
+ */
+static int packet_ring_bind(struct sock *sk, struct net_device *dev)
+{
+ u_int the_slot_len;
+ u_int32_t tot_mem;
+ struct ring_opt *pfr = ring_sk(sk);
+ struct page *page, *page_end;
+
+ if(!dev) return(-1);
+
+#if defined(RING_DEBUG)
+ printk("RING: packet_ring_bind(%s) called\n", dev->name);
+#endif
+
+ /* **********************************************
+
+ *************************************
+ * *
+ * FlowSlotInfo *
+ * *
+ ************************************* <-+
+ * FlowSlot * |
+ ************************************* |
+ * FlowSlot * |
+ ************************************* +- num_slots
+ * FlowSlot * |
+ ************************************* |
+ * FlowSlot * |
+ ************************************* <-+
+
+ ********************************************** */
+
+ the_slot_len = sizeof(u_char) /* flowSlot.slot_state */
+#ifdef RING_MAGIC
+ + sizeof(u_char)
+#endif
+ + sizeof(struct pcap_pkthdr)
+ + bucket_len /* flowSlot.bucket */;
+
+ tot_mem = sizeof(FlowSlotInfo) + num_slots*the_slot_len;
+
+ /*
+ Calculate the value of the order parameter used later.
+ See http://www.linuxjournal.com/article.php?sid=1133
+ */
+ for(pfr->order = 0;(PAGE_SIZE << pfr->order) < tot_mem; pfr->order++) ;
+
+ /*
+ We now try to allocate the memory as required. If we fail
+ we try to allocate a smaller amount or memory (hence a
+ smaller ring).
+ */
+ while((pfr->ring_memory = __get_free_pages(GFP_ATOMIC, pfr->order)) == 0)
+ if(pfr->order-- == 0)
+ break;
+
+ if(pfr->order == 0) {
+ printk("RING: ERROR not enough memory for ring\n");
+ return(-1);
+ } else {
+ printk("RING: succesfully allocated %lu KB [tot_mem=%d][order=%ld]\n",
+ PAGE_SIZE >> (10 - pfr->order), tot_mem, pfr->order);
+ }
+
+ tot_mem = PAGE_SIZE << pfr->order;
+ memset((char*)pfr->ring_memory, 0, tot_mem);
+
+ /* Now we need to reserve the pages */
+ page_end = virt_to_page(pfr->ring_memory + (PAGE_SIZE << pfr->order) - 1);
+ for(page = virt_to_page(pfr->ring_memory); page <= page_end; page++)
+ SetPageReserved(page);
+
+ pfr->slots_info = (FlowSlotInfo*)pfr->ring_memory;
+ pfr->ring_slots = (char*)(pfr->ring_memory+sizeof(FlowSlotInfo));
+
+ pfr->slots_info->version = RING_FLOWSLOT_VERSION;
+ pfr->slots_info->slot_len = the_slot_len;
+ pfr->slots_info->data_len = bucket_len;
+ pfr->slots_info->tot_slots = (tot_mem-sizeof(FlowSlotInfo))/the_slot_len;
+ pfr->slots_info->tot_mem = tot_mem;
+ pfr->slots_info->sample_rate = sample_rate;
+
+ printk("RING: allocated %d slots [slot_len=%d][tot_mem=%u]\n",
+ pfr->slots_info->tot_slots, pfr->slots_info->slot_len,
+ pfr->slots_info->tot_mem);
+
+#ifdef RING_MAGIC
+ {
+ int i;
+
+ for(i=0; i<pfr->slots_info->tot_slots; i++) {
+ unsigned long idx = i*pfr->slots_info->slot_len;
+ FlowSlot *slot = (FlowSlot*)&pfr->ring_slots[idx];
+ slot->magic = RING_MAGIC_VALUE; slot->slot_state = 0;
+ }
+ }
+#endif
+
+ pfr->insert_page_id = 1, pfr->insert_slot_id = 0;
+
+ /*
+ IMPORTANT
+ Leave this statement here as last one. In fact when
+ the ring_netdev != NULL the socket is ready to be used.
+ */
+ pfr->ring_netdev = dev;
+
+ return(0);
+}
+
+/* ************************************* */
+
+/* Bind to a device */
+static int ring_bind(struct socket *sock,
+ struct sockaddr *sa, int addr_len)
+{
+ struct sock *sk=sock->sk;
+ struct net_device *dev = NULL;
+
+#if defined(RING_DEBUG)
+ printk("RING: ring_bind() called\n");
+#endif
+
+ /*
+ * Check legality
+ */
+ if (addr_len != sizeof(struct sockaddr))
+ return -EINVAL;
+ if (sa->sa_family != PF_RING)
+ return -EINVAL;
+
+ /* Safety check: add trailing zero if missing */
+ sa->sa_data[sizeof(sa->sa_data)-1] = '\0';
+
+#if defined(RING_DEBUG)
+ printk("RING: searching device %s\n", sa->sa_data);
+#endif
+
+ if((dev = __dev_get_by_name(sa->sa_data)) == NULL) {
+#if defined(RING_DEBUG)
+ printk("RING: search failed\n");
+#endif
+ return(-EINVAL);
+ } else
+ return(packet_ring_bind(sk, dev));
+}
+
+/* ************************************* */
+
+static int ring_mmap(struct file *file,
+ struct socket *sock,
+ struct vm_area_struct *vma)
+{
+ struct sock *sk = sock->sk;
+ struct ring_opt *pfr = ring_sk(sk);
+ unsigned long size, start;
+ u_int pagesToMap;
+ char *ptr;
+
+#if defined(RING_DEBUG)
+ printk("RING: ring_mmap() called\n");
+#endif
+
+ if(pfr->ring_memory == 0) {
+#if defined(RING_DEBUG)
+ printk("RING: ring_mmap() failed: mapping area to an unbound socket\n");
+#endif
+ return -EINVAL;
+ }
+
+ size = (unsigned long)(vma->vm_end-vma->vm_start);
+
+ if(size % PAGE_SIZE) {
+#if defined(RING_DEBUG)
+ printk("RING: ring_mmap() failed: len is not multiple of PAGE_SIZE\n");
+#endif
+ return(-EINVAL);
+ }
+
+ /* if userspace tries to mmap beyond end of our buffer, fail */
+ if(size > pfr->slots_info->tot_mem) {
+#if defined(RING_DEBUG)
+ printk("proc_mmap() failed: area too large [%ld > %d]\n", size, pfr->slots_info->tot_mem);
+#endif
+ return(-EINVAL);
+ }
+
+ pagesToMap = size/PAGE_SIZE;
+
+#if defined(RING_DEBUG)
+ printk("RING: ring_mmap() called. %d pages to map\n", pagesToMap);
+#endif
+
+#if defined(RING_DEBUG)
+ printk("RING: mmap [slot_len=%d][tot_slots=%d] for ring on device %s\n",
+ pfr->slots_info->slot_len, pfr->slots_info->tot_slots,
+ pfr->ring_netdev->name);
+#endif
+
+ /* we do not want to have this area swapped out, lock it */
+ vma->vm_flags |= VM_LOCKED;
+ start = vma->vm_start;
+
+ /* Ring slots start from page 1 (page 0 is reserved for FlowSlotInfo) */
+ ptr = (char*)(start+PAGE_SIZE);
+
+ if(remap_page_range(
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0))
+ vma,
+#endif
+ start,
+ __pa(pfr->ring_memory),
+ PAGE_SIZE*pagesToMap, vma->vm_page_prot)) {
+#if defined(RING_DEBUG)
+ printk("remap_page_range() failed\n");
+#endif
+ return(-EAGAIN);
+ }
+
+#if defined(RING_DEBUG)
+ printk("proc_mmap(pagesToMap=%d): success.\n", pagesToMap);
+#endif
+
+ return 0;
+}
+
+/* ************************************* */
+
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0))
+static int ring_recvmsg(struct kiocb *iocb, struct socket *sock,
+ struct msghdr *msg, size_t len, int flags)
+#else
+ static int ring_recvmsg(struct socket *sock, struct msghdr *msg, int len,
+ int flags, struct scm_cookie *scm)
+#endif
+{
+ FlowSlot* slot;
+ struct ring_opt *pfr = ring_sk(sock->sk);
+ u_int32_t queued_pkts, num_loops = 0;
+
+#if defined(RING_DEBUG)
+ printk("ring_recvmsg called\n");
+#endif
+
+ slot = get_remove_slot(pfr);
+
+ while((queued_pkts = num_queued_pkts(pfr)) < MIN_QUEUED_PKTS) {
+ wait_event_interruptible(pfr->ring_slots_waitqueue, 1);
+
+#if defined(RING_DEBUG)
+ printk("-> ring_recvmsg returning %d [queued_pkts=%d][num_loops=%d]\n",
+ slot->slot_state, queued_pkts, num_loops);
+#endif
+
+ if(queued_pkts > 0) {
+ if(num_loops++ > MAX_QUEUE_LOOPS)
+ break;
+ }
+ }
+
+#if defined(RING_DEBUG)
+ if(slot != NULL)
+ printk("ring_recvmsg is returning [queued_pkts=%d][num_loops=%d]\n",
+ queued_pkts, num_loops);
+#endif
+
+ return(queued_pkts);
+}
+
+/* ************************************* */
+
+unsigned int ring_poll(struct file * file,
+ struct socket *sock, poll_table *wait)
+{
+ FlowSlot* slot;
+ struct ring_opt *pfr = ring_sk(sock->sk);
+
+#if defined(RING_DEBUG)
+ printk("poll called\n");
+#endif
+
+ slot = get_remove_slot(pfr);
+
+ if((slot != NULL) && (slot->slot_state == 0))
+ poll_wait(file, &pfr->ring_slots_waitqueue, wait);
+
+#if defined(RING_DEBUG)
+ printk("poll returning %d\n", slot->slot_state);
+#endif
+
+ if((slot != NULL) && (slot->slot_state == 1))
+ return(POLLIN | POLLRDNORM);
+ else
+ return(0);
+}
+
+/* ************************************* */
+
+int add_to_cluster_list(struct ring_cluster *el,
+ struct sock *sock) {
+
+ if(el->num_cluster_elements == CLUSTER_LEN)
+ return(-1); /* Cluster full */
+
+ ring_sk_datatype(ring_sk(sock))->cluster_id = el->cluster_id;
+ el->sk[el->num_cluster_elements] = sock;
+ el->num_cluster_elements++;
+ return(0);
+}
+
+/* ************************************* */
+
+int remove_from_cluster_list(struct ring_cluster *el,
+ struct sock *sock) {
+ int i, j;
+
+ for(i=0; i<CLUSTER_LEN; i++)
+ if(el->sk[i] == sock) {
+ el->num_cluster_elements--;
+
+ if(el->num_cluster_elements > 0) {
+ /* The cluster contains other elements */
+ for(j=i; j<CLUSTER_LEN-1; j++)
+ el->sk[j] = el->sk[j+1];
+
+ el->sk[CLUSTER_LEN-1] = NULL;
+ } else {
+ /* Empty cluster */
+ memset(el->sk, 0, sizeof(el->sk));
+ }
+
+ return(0);
+ }
+
+ return(-1); /* Not found */
+}
+
+/* ************************************* */
+
+static int remove_from_cluster(struct sock *sock,
+ struct ring_opt *pfr)
+{
+ struct ring_cluster *el;
+
+#if defined(RING_DEBUG)
+ printk("--> remove_from_cluster(%d)\n", pfr->cluster_id);
+#endif
+
+ if(pfr->cluster_id == 0 /* 0 = No Cluster */)
+ return(0); /* Noting to do */
+
+ el = ring_cluster_list;
+
+ while(el != NULL) {
+ if(el->cluster_id == pfr->cluster_id) {
+ return(remove_from_cluster_list(el, sock));
+ } else
+ el = el->next;
+ }
+
+ return(-EINVAL); /* Not found */
+}
+
+/* ************************************* */
+
+static int add_to_cluster(struct sock *sock,
+ struct ring_opt *pfr,
+ u_short cluster_id)
+{
+ struct ring_cluster *el;
+
+#ifndef RING_DEBUG
+ printk("--> add_to_cluster(%d)\n", cluster_id);
+#endif
+
+ if(cluster_id == 0 /* 0 = No Cluster */) return(-EINVAL);
+
+ if(pfr->cluster_id != 0)
+ remove_from_cluster(sock, pfr);
+
+ el = ring_cluster_list;
+
+ while(el != NULL) {
+ if(el->cluster_id == cluster_id) {
+ return(add_to_cluster_list(el, sock));
+ } else
+ el = el->next;
+ }
+
+ /* There's no existing cluster. We need to create one */
+ if((el = kmalloc(sizeof(struct ring_cluster), GFP_KERNEL)) == NULL)
+ return(-ENOMEM);
+
+ el->cluster_id = cluster_id;
+ el->num_cluster_elements = 1;
+ el->hashing_mode = cluster_per_flow; /* Default */
+ el->hashing_id = 0;
+
+ memset(el->sk, 0, sizeof(el->sk));
+ el->sk[0] = sock;
+ el->next = ring_cluster_list;
+ ring_cluster_list = el;
+ pfr->cluster_id = cluster_id;
+
+ return(0); /* 0 = OK */
+}
+
+/* ************************************* */
+
+/* Code taken/inspired from core/sock.c */
+static int ring_setsockopt(struct socket *sock,
+ int level, int optname,
+ char *optval, int optlen)
+{
+ struct ring_opt *pfr = ring_sk(sock->sk);
+ int val, found, ret = 0;
+ u_int cluster_id;
+ char devName[8];
+
+ if((optlen<sizeof(int)) || (pfr == NULL))
+ return(-EINVAL);
+
+ if (get_user(val, (int *)optval))
+ return -EFAULT;
+
+ found = 1;
+
+ switch(optname)
+ {
+ case SO_ATTACH_FILTER:
+ ret = -EINVAL;
+ if (optlen == sizeof(struct sock_fprog)) {
+ unsigned int fsize;
+ struct sock_fprog fprog;
+ struct sk_filter *filter;
+
+ ret = -EFAULT;
+
+ /*
+ NOTE
+
+ Do not call copy_from_user within a held
+ splinlock (e.g. ring_mgmt_lock) as this caused
+ problems when certain debugging was enabled under
+ 2.6.5 -- including hard lockups of the machine.
+ */
+ if(copy_from_user(&fprog, optval, sizeof(fprog)))
+ break;
+
+ fsize = sizeof(struct sock_filter) * fprog.len;
+ filter = kmalloc(fsize, GFP_KERNEL);
+
+ if(filter == NULL) {
+ ret = -ENOMEM;
+ break;
+ }
+
+ if(copy_from_user(filter->insns, fprog.filter, fsize))
+ break;
+
+ filter->len = fprog.len;
+
+ if(sk_chk_filter(filter->insns, filter->len) != 0) {
+ /* Bad filter specified */
+ kfree(filter);
+ pfr->bpfFilter = NULL;
+ break;
+ }
+
+ /* get the lock, set the filter, release the lock */
+ write_lock(&ring_mgmt_lock);
+ pfr->bpfFilter = filter;
+ write_unlock(&ring_mgmt_lock);
+ }
+ ret = 0;
+ break;
+
+ case SO_DETACH_FILTER:
+ write_lock(&ring_mgmt_lock);
+ found = 1;
+ if(pfr->bpfFilter != NULL) {
+ kfree(pfr->bpfFilter);
+ pfr->bpfFilter = NULL;
+ write_unlock(&ring_mgmt_lock);
+ break;
+ }
+ ret = -ENONET;
+ break;
+
+ case SO_ADD_TO_CLUSTER:
+ if (optlen!=sizeof(val))
+ return -EINVAL;
+
+ if (copy_from_user(&cluster_id, optval, sizeof(cluster_id)))
+ return -EFAULT;
+
+ write_lock(&ring_mgmt_lock);
+ ret = add_to_cluster(sock->sk, pfr, cluster_id);
+ write_unlock(&ring_mgmt_lock);
+ break;
+
+ case SO_REMOVE_FROM_CLUSTER:
+ write_lock(&ring_mgmt_lock);
+ ret = remove_from_cluster(sock->sk, pfr);
+ write_unlock(&ring_mgmt_lock);
+ break;
+
+ case SO_SET_REFLECTOR:
+ if(optlen >= (sizeof(devName)-1))
+ return -EINVAL;
+
+ if(optlen > 0) {
+ if(copy_from_user(devName, optval, optlen))
+ return -EFAULT;
+ }
+
+ devName[optlen] = '\0';
+
+#if defined(RING_DEBUG)
+ printk("+++ SO_SET_REFLECTOR(%s)\n", devName);
+#endif
+
+ write_lock(&ring_mgmt_lock);
+ pfr->reflector_dev = dev_get_by_name(devName);
+ write_unlock(&ring_mgmt_lock);
+
+#if defined(RING_DEBUG)
+ if(pfr->reflector_dev != NULL)
+ printk("SO_SET_REFLECTOR(%s): succeded\n", devName);
+ else
+ printk("SO_SET_REFLECTOR(%s): device unknown\n", devName);
+#endif
+ break;
+
+ default:
+ found = 0;
+ break;
+ }
+
+ if(found)
+ return(ret);
+ else
+ return(sock_setsockopt(sock, level, optname, optval, optlen));
+}
+
+/* ************************************* */
+
+static int ring_ioctl(struct socket *sock,
+ unsigned int cmd, unsigned long arg)
+{
+ switch(cmd)
+ {
+ case SIOCGIFFLAGS:
+ case SIOCSIFFLAGS:
+ case SIOCGIFCONF:
+ case SIOCGIFMETRIC:
+ case SIOCSIFMETRIC:
+ case SIOCGIFMEM:
+ case SIOCSIFMEM:
+ case SIOCGIFMTU:
+ case SIOCSIFMTU:
+ case SIOCSIFLINK:
+ case SIOCGIFHWADDR:
+ case SIOCSIFHWADDR:
+ case SIOCSIFMAP:
+ case SIOCGIFMAP:
+ case SIOCSIFSLAVE:
+ case SIOCGIFSLAVE:
+ case SIOCGIFINDEX:
+ case SIOCGIFNAME:
+ case SIOCGIFCOUNT:
+ case SIOCSIFHWBROADCAST:
+ return(dev_ioctl(cmd,(void *) arg));
+
+ default:
+ return -EOPNOTSUPP;
+ }
+
+ return 0;
+}
+
+/* ************************************* */
+
+static struct proto_ops ring_ops = {
+ .family = PF_RING,
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0))
+ .owner = THIS_MODULE,
+#endif
+
+ /* Operations that make no sense on ring sockets. */
+ .connect = sock_no_connect,
+ .socketpair = sock_no_socketpair,
+ .accept = sock_no_accept,
+ .getname = sock_no_getname,
+ .listen = sock_no_listen,
+ .shutdown = sock_no_shutdown,
+ .sendpage = sock_no_sendpage,
+ .sendmsg = sock_no_sendmsg,
+ .getsockopt = sock_no_getsockopt,
+
+ /* Now the operations that really occur. */
+ .release = ring_release,
+ .bind = ring_bind,
+ .mmap = ring_mmap,
+ .poll = ring_poll,
+ .setsockopt = ring_setsockopt,
+ .ioctl = ring_ioctl,
+ .recvmsg = ring_recvmsg,
+};
+
+/* ************************************ */
+
+static struct net_proto_family ring_family_ops = {
+ .family = PF_RING,
+ .create = ring_create,
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0))
+ .owner = THIS_MODULE,
+#endif
+};
+
+// BD: API changed in 2.6.12, ref:
+// http://svn.clkao.org/svnweb/linux/revision/?rev=28201
+#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,11))
+static struct proto ring_proto = {
+ .name = "PF_RING",
+ .owner = THIS_MODULE,
+ .obj_size = sizeof(struct sock),
+};
+#endif
+
+/* ************************************ */
+
+static void __exit ring_exit(void)
+{
+ struct list_head *ptr;
+ struct ring_element *entry;
+
+ for(ptr = ring_table.next; ptr != &ring_table; ptr = ptr->next) {
+ entry = list_entry(ptr, struct ring_element, list);
+ kfree(entry);
+ }
+
+ while(ring_cluster_list != NULL) {
+ struct ring_cluster *next = ring_cluster_list->next;
+ kfree(ring_cluster_list);
+ ring_cluster_list = next;
+ }
+
+ set_skb_ring_handler(NULL);
+ set_buffer_ring_handler(NULL);
+ sock_unregister(PF_RING);
+
+ printk("PF_RING shut down.\n");
+}
+
+/* ************************************ */
+
+static int __init ring_init(void)
+{
+ printk("Welcome to PF_RING %s\n(C) 2004-05 L.Deri <deri@ntop.org>\n",
+ RING_VERSION);
+
+ INIT_LIST_HEAD(&ring_table);
+ ring_cluster_list = NULL;
+
+ sock_register(&ring_family_ops);
+
+ set_skb_ring_handler(skb_ring_handler);
+ set_buffer_ring_handler(buffer_ring_handler);
+
+ if(get_buffer_ring_handler() != buffer_ring_handler) {
+ printk("PF_RING: set_buffer_ring_handler FAILED\n");
+
+ set_skb_ring_handler(NULL);
+ set_buffer_ring_handler(NULL);
+ sock_unregister(PF_RING);
+ return -1;
+ } else {
+ printk("PF_RING: bucket length %d bytes\n", bucket_len);
+ printk("PF_RING: ring slots %d\n", num_slots);
+ printk("PF_RING: sample rate %d [1=no sampling]\n", sample_rate);
+ printk("PF_RING: capture TX %s\n",
+ enable_tx_capture ? "Yes [RX+TX]" : "No [RX only]");
+ printk("PF_RING: transparent mode %s\n",
+ transparent_mode ? "Yes" : "No");
+
+ printk("PF_RING initialized correctly.\n");
+ return 0;
+ }
+}
+
+module_init(ring_init);
+module_exit(ring_exit);
+MODULE_LICENSE("GPL");
+
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0))
+MODULE_ALIAS_NETPROTO(PF_RING);
+#endif