summaryrefslogtreecommitdiffstats
path: root/openwrt/target/linux/linux-2.6/patches/generic
diff options
context:
space:
mode:
authornbd <nbd@3c298f89-4303-0410-b956-a3cf2f4a3e73>2005-10-22 22:03:56 +0000
committernbd <nbd@3c298f89-4303-0410-b956-a3cf2f4a3e73>2005-10-22 22:03:56 +0000
commit49417b68d05e44f253f2a97e1d0a9422b5330a55 (patch)
tree2ed2dcf30ae904637c5d18430bbe87673a52d22f /openwrt/target/linux/linux-2.6/patches/generic
parentce409cfdea924fd84ab05cbfb61f22c678264d57 (diff)
add pf_ring patches for kernel and libpcap
git-svn-id: svn://svn.openwrt.org/openwrt/trunk@2266 3c298f89-4303-0410-b956-a3cf2f4a3e73
Diffstat (limited to 'openwrt/target/linux/linux-2.6/patches/generic')
-rw-r--r--openwrt/target/linux/linux-2.6/patches/generic/104-pf_ring.patch5299
1 files changed, 5299 insertions, 0 deletions
diff --git a/openwrt/target/linux/linux-2.6/patches/generic/104-pf_ring.patch b/openwrt/target/linux/linux-2.6/patches/generic/104-pf_ring.patch
new file mode 100644
index 000000000..759fb2cc9
--- /dev/null
+++ b/openwrt/target/linux/linux-2.6/patches/generic/104-pf_ring.patch
@@ -0,0 +1,5299 @@
+diff --unified --recursive --new-file linux-2.6.12.5/include/linux/ring.h linux-2.6.12.5-1-686-smp-ring3/include/linux/ring.h
+--- linux-2.6.12.5/include/linux/ring.h 1970-01-01 01:00:00.000000000 +0100
++++ linux-2.6.12.5-1-686-smp-ring3/include/linux/ring.h 2005-10-22 23:50:44.951445250 +0200
+@@ -0,0 +1,108 @@
++/*
++ * 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 5
++#define RING_VERSION "3.0"
++
++#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, tot_mem;
++
++ u_int64_t tot_pkts, tot_lost;
++ u_int64_t tot_insert, tot_read;
++ u_int16_t insert_idx;
++ u_int16_t 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.12.5/net/Kconfig linux-2.6.12.5-1-686-smp-ring3/net/Kconfig
+--- linux-2.6.12.5/net/Kconfig 2005-08-15 02:20:18.000000000 +0200
++++ linux-2.6.12.5-1-686-smp-ring3/net/Kconfig 2005-10-22 23:50:45.535481750 +0200
+@@ -72,6 +72,7 @@
+
+ Say Y unless you know what you are doing.
+
++source "net/ring/Kconfig"
+ config INET
+ bool "TCP/IP networking"
+ ---help---
+diff --unified --recursive --new-file linux-2.6.12.5/net/Makefile linux-2.6.12.5-1-686-smp-ring3/net/Makefile
+--- linux-2.6.12.5/net/Makefile 2005-08-15 02:20:18.000000000 +0200
++++ linux-2.6.12.5-1-686-smp-ring3/net/Makefile 2005-10-22 23:50:45.491479000 +0200
+@@ -41,6 +41,7 @@
+ obj-$(CONFIG_DECNET) += decnet/
+ obj-$(CONFIG_ECONET) += econet/
+ obj-$(CONFIG_VLAN_8021Q) += 8021q/
++obj-$(CONFIG_RING) += ring/
+ obj-$(CONFIG_IP_SCTP) += sctp/
+
+ ifeq ($(CONFIG_NET),y)
+diff --unified --recursive --new-file linux-2.6.12.5/net/Makefile.ORG linux-2.6.12.5-1-686-smp-ring3/net/Makefile.ORG
+--- linux-2.6.12.5/net/Makefile.ORG 1970-01-01 01:00:00.000000000 +0100
++++ linux-2.6.12.5-1-686-smp-ring3/net/Makefile.ORG 2005-10-22 23:50:45.483478500 +0200
+@@ -0,0 +1,48 @@
++#
++# Makefile for the linux networking.
++#
++# 2 Sep 2000, Christoph Hellwig <hch@infradead.org>
++# Rewritten to use lists instead of if-statements.
++#
++
++obj-y := nonet.o
++
++obj-$(CONFIG_NET) := socket.o core/
++
++tmp-$(CONFIG_COMPAT) := compat.o
++obj-$(CONFIG_NET) += $(tmp-y)
++
++# LLC has to be linked before the files in net/802/
++obj-$(CONFIG_LLC) += llc/
++obj-$(CONFIG_NET) += ethernet/ 802/ sched/ netlink/
++obj-$(CONFIG_INET) += ipv4/
++obj-$(CONFIG_XFRM) += xfrm/
++obj-$(CONFIG_UNIX) += unix/
++ifneq ($(CONFIG_IPV6),)
++obj-y += ipv6/
++endif
++obj-$(CONFIG_PACKET) += packet/
++obj-$(CONFIG_NET_KEY) += key/
++obj-$(CONFIG_NET_SCHED) += sched/
++obj-$(CONFIG_BRIDGE) += bridge/
++obj-$(CONFIG_IPX) += ipx/
++obj-$(CONFIG_ATALK) += appletalk/
++obj-$(CONFIG_WAN_ROUTER) += wanrouter/
++obj-$(CONFIG_X25) += x25/
++obj-$(CONFIG_LAPB) += lapb/
++obj-$(CONFIG_NETROM) += netrom/
++obj-$(CONFIG_ROSE) += rose/
++obj-$(CONFIG_AX25) += ax25/
++obj-$(CONFIG_IRDA) += irda/
++obj-$(CONFIG_BT) += bluetooth/
++obj-$(CONFIG_SUNRPC) += sunrpc/
++obj-$(CONFIG_RXRPC) += rxrpc/
++obj-$(CONFIG_ATM) += atm/
++obj-$(CONFIG_DECNET) += decnet/
++obj-$(CONFIG_ECONET) += econet/
++obj-$(CONFIG_VLAN_8021Q) += 8021q/
++obj-$(CONFIG_IP_SCTP) += sctp/
++
++ifeq ($(CONFIG_NET),y)
++obj-$(CONFIG_SYSCTL) += sysctl_net.o
++endif
+diff --unified --recursive --new-file linux-2.6.12.5/net/core/dev.c linux-2.6.12.5-1-686-smp-ring3/net/core/dev.c
+--- linux-2.6.12.5/net/core/dev.c 2005-08-15 02:20:18.000000000 +0200
++++ linux-2.6.12.5-1-686-smp-ring3/net/core/dev.c 2005-10-22 23:50:45.479478250 +0200
+@@ -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
+ /* This define, if set, will randomly drop a packet when congestion
+ * is more than moderate. It helps fairness in the multi-interface
+ * case when one of them is a hog, but it kills performance for the
+@@ -1293,6 +1343,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);
+
+@@ -1509,6 +1563,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();
+@@ -1655,6 +1716,13 @@
+ 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))
+ return NET_RX_DROP;
+diff --unified --recursive --new-file linux-2.6.12.5/net/core/dev.c.ORG linux-2.6.12.5-1-686-smp-ring3/net/core/dev.c.ORG
+--- linux-2.6.12.5/net/core/dev.c.ORG 1970-01-01 01:00:00.000000000 +0100
++++ linux-2.6.12.5-1-686-smp-ring3/net/core/dev.c.ORG 2005-10-22 23:50:45.203461000 +0200
+@@ -0,0 +1,3385 @@
++/*
++ * NET3 Protocol independent device support routines.
++ *
++ * 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
++ * 2 of the License, or (at your option) any later version.
++ *
++ * Derived from the non IP parts of dev.c 1.0.19
++ * Authors: Ross Biro
++ * Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
++ * Mark Evans, <evansmp@uhura.aston.ac.uk>
++ *
++ * Additional Authors:
++ * Florian la Roche <rzsfl@rz.uni-sb.de>
++ * Alan Cox <gw4pts@gw4pts.ampr.org>
++ * David Hinds <dahinds@users.sourceforge.net>
++ * Alexey Kuznetsov <kuznet@ms2.inr.ac.ru>
++ * Adam Sulmicki <adam@cfar.umd.edu>
++ * Pekka Riikonen <priikone@poesidon.pspt.fi>
++ *
++ * Changes:
++ * D.J. Barrow : Fixed bug where dev->refcnt gets set
++ * to 2 if register_netdev gets called
++ * before net_dev_init & also removed a
++ * few lines of code in the process.
++ * Alan Cox : device private ioctl copies fields back.
++ * Alan Cox : Transmit queue code does relevant
++ * stunts to keep the queue safe.
++ * Alan Cox : Fixed double lock.
++ * Alan Cox : Fixed promisc NULL pointer trap
++ * ???????? : Support the full private ioctl range
++ * Alan Cox : Moved ioctl permission check into
++ * drivers
++ * Tim Kordas : SIOCADDMULTI/SIOCDELMULTI
++ * Alan Cox : 100 backlog just doesn't cut it when
++ * you start doing multicast video 8)
++ * Alan Cox : Rewrote net_bh and list manager.
++ * Alan Cox : Fix ETH_P_ALL echoback lengths.
++ * Alan Cox : Took out transmit every packet pass
++ * Saved a few bytes in the ioctl handler
++ * Alan Cox : Network driver sets packet type before
++ * calling netif_rx. Saves a function
++ * call a packet.
++ * Alan Cox : Hashed net_bh()
++ * Richard Kooijman: Timestamp fixes.
++ * Alan Cox : Wrong field in SIOCGIFDSTADDR
++ * Alan Cox : Device lock protection.
++ * Alan Cox : Fixed nasty side effect of device close
++ * changes.
++ * Rudi Cilibrasi : Pass the right thing to
++ * set_mac_address()
++ * Dave Miller : 32bit quantity for the device lock to
++ * make it work out on a Sparc.
++ * Bjorn Ekwall : Added KERNELD hack.
++ * Alan Cox : Cleaned up the backlog initialise.
++ * Craig Metz : SIOCGIFCONF fix if space for under
++ * 1 device.
++ * Thomas Bogendoerfer : Return ENODEV for dev_open, if there
++ * is no device open function.
++ * Andi Kleen : Fix error reporting for SIOCGIFCONF
++ * Michael Chastain : Fix signed/unsigned for SIOCGIFCONF
++ * Cyrus Durgin : Cleaned for KMOD
++ * Adam Sulmicki : Bug Fix : Network Device Unload
++ * A network device unload needs to purge
++ * the backlog queue.
++ * Paul Rusty Russell : SIOCSIFNAME
++ * Pekka Riikonen : Netdev boot-time settings code
++ * Andrew Morton : Make unregister_netdevice wait
++ * indefinitely on dev->refcnt
++ * J Hadi Salim : - Backlog queue sampling
++ * - netif_rx() feedback
++ */
++
++#include <asm/uaccess.h>
++#include <asm/system.h>
++#include <linux/bitops.h>
++#include <linux/config.h>
++#include <linux/cpu.h>
++#include <linux/types.h>
++#include <linux/kernel.h>
++#include <linux/sched.h>
++#include <linux/string.h>
++#include <linux/mm.h>
++#include <linux/socket.h>
++#include <linux/sockios.h>
++#include <linux/errno.h>
++#include <linux/interrupt.h>
++#include <linux/if_ether.h>
++#include <linux/netdevice.h>
++#include <linux/etherdevice.h>
++#include <linux/notifier.h>
++#include <linux/skbuff.h>
++#include <net/sock.h>
++#include <linux/rtnetlink.h>
++#include <linux/proc_fs.h>
++#include <linux/seq_file.h>
++#include <linux/stat.h>
++#include <linux/if_bridge.h>
++#include <linux/divert.h>
++#include <net/dst.h>
++#include <net/pkt_sched.h>
++#include <net/checksum.h>
++#include <linux/highmem.h>
++#include <linux/init.h>
++#include <linux/kmod.h>
++#include <linux/module.h>
++#include <linux/kallsyms.h>
++#include <linux/netpoll.h>
++#include <linux/rcupdate.h>
++#include <linux/delay.h>
++#ifdef CONFIG_NET_RADIO
++#include <linux/wireless.h> /* Note : will define WIRELESS_EXT */
++#include <net/iw_handler.h>
++#endif /* CONFIG_NET_RADIO */
++#include <asm/current.h>
++
++/* This define, if set, will randomly drop a packet when congestion
++ * is more than moderate. It helps fairness in the multi-interface
++ * case when one of them is a hog, but it kills performance for the
++ * single interface case so it is off now by default.
++ */
++#undef RAND_LIE
++
++/* Setting this will sample the queue lengths and thus congestion
++ * via a timer instead of as each packet is received.
++ */
++#undef OFFLINE_SAMPLE
++
++/*
++ * The list of packet types we will receive (as opposed to discard)
++ * and the routines to invoke.
++ *
++ * Why 16. Because with 16 the only overlap we get on a hash of the
++ * low nibble of the protocol value is RARP/SNAP/X.25.
++ *
++ * NOTE: That is no longer true with the addition of VLAN tags. Not
++ * sure which should go first, but I bet it won't make much
++ * difference if we are running VLANs. The good news is that
++ * this protocol won't be in the list unless compiled in, so
++ * the average user (w/out VLANs) will not be adversly affected.
++ * --BLG
++ *
++ * 0800 IP
++ * 8100 802.1Q VLAN
++ * 0001 802.3
++ * 0002 AX.25
++ * 0004 802.2
++ * 8035 RARP
++ * 0005 SNAP
++ * 0805 X.25
++ * 0806 ARP
++ * 8137 IPX
++ * 0009 Localtalk
++ * 86DD IPv6
++ */
++
++static DEFINE_SPINLOCK(ptype_lock);
++static struct list_head ptype_base[16]; /* 16 way hashed list */
++static struct list_head ptype_all; /* Taps */
++
++#ifdef OFFLINE_SAMPLE
++static void sample_queue(unsigned long dummy);
++static struct timer_list samp_timer = TIMER_INITIALIZER(sample_queue, 0, 0);
++#endif
++
++/*
++ * The @dev_base list is protected by @dev_base_lock and the rtln
++ * semaphore.
++ *
++ * Pure readers hold dev_base_lock for reading.
++ *
++ * Writers must hold the rtnl semaphore while they loop through the
++ * dev_base list, and hold dev_base_lock for writing when they do the
++ * actual updates. This allows pure readers to access the list even
++ * while a writer is preparing to update it.
++ *
++ * To put it another way, dev_base_lock is held for writing only to
++ * protect against pure readers; the rtnl semaphore provides the
++ * protection against other writers.
++ *
++ * See, for example usages, register_netdevice() and
++ * unregister_netdevice(), which must be called with the rtnl
++ * semaphore held.
++ */
++struct net_device *dev_base;
++static struct net_device **dev_tail = &dev_base;
++DEFINE_RWLOCK(dev_base_lock);
++
++EXPORT_SYMBOL(dev_base);
++EXPORT_SYMBOL(dev_base_lock);
++
++#define NETDEV_HASHBITS 8
++static struct hlist_head dev_name_head[1<<NETDEV_HASHBITS];
++static struct hlist_head dev_index_head[1<<NETDEV_HASHBITS];
++
++static inline struct hlist_head *dev_name_hash(const char *name)
++{
++ unsigned hash = full_name_hash(name, strnlen(name, IFNAMSIZ));
++ return &dev_name_head[hash & ((1<<NETDEV_HASHBITS)-1)];
++}
++
++static inline struct hlist_head *dev_index_hash(int ifindex)
++{
++ return &dev_index_head[ifindex & ((1<<NETDEV_HASHBITS)-1)];
++}
++
++/*
++ * Our notifier list
++ */
++
++static struct notifier_block *netdev_chain;
++
++/*
++ * Device drivers call our routines to queue packets here. We empty the
++ * queue in the local softnet handler.
++ */
++DEFINE_PER_CPU(struct softnet_data, softnet_data) = { 0, };
++
++#ifdef CONFIG_SYSFS
++extern int netdev_sysfs_init(void);
++extern int netdev_register_sysfs(struct net_device *);
++extern void netdev_unregister_sysfs(struct net_device *);
++#else
++#define netdev_sysfs_init() (0)
++#define netdev_register_sysfs(dev) (0)
++#define netdev_unregister_sysfs(dev) do { } while(0)
++#endif
++
++
++/*******************************************************************************
++
++ Protocol management and registration routines
++
++*******************************************************************************/
++
++/*
++ * For efficiency
++ */
++
++int netdev_nit;
++
++/*
++ * Add a protocol ID to the list. Now that the input handler is
++ * smarter we can dispense with all the messy stuff that used to be
++ * here.
++ *
++ * BEWARE!!! Protocol handlers, mangling input packets,
++ * MUST BE last in hash buckets and checking protocol handlers
++ * MUST start from promiscuous ptype_all chain in net_bh.
++ * It is true now, do not change it.
++ * Explanation follows: if protocol handler, mangling packet, will
++ * be the first on list, it is not able to sense, that packet
++ * is cloned and should be copied-on-write, so that it will
++ * change it and subsequent readers will get broken packet.
++ * --ANK (980803)
++ */
++
++/**
++ * dev_add_pack - add packet handler
++ * @pt: packet type declaration
++ *
++ * Add a protocol handler to the networking stack. The passed &packet_type
++ * is linked into kernel lists and may not be freed until it has been
++ * removed from the kernel lists.
++ *
++ * This call does not sleep therefore it can not
++ * guarantee all CPU's that are in middle of receiving packets
++ * will see the new packet type (until the next received packet).
++ */
++
++void dev_add_pack(struct packet_type *pt)
++{
++ int hash;
++
++ spin_lock_bh(&ptype_lock);
++ if (pt->type == htons(ETH_P_ALL)) {
++ netdev_nit++;
++ list_add_rcu(&pt->list, &ptype_all);
++ } else {
++ hash = ntohs(pt->type) & 15;
++ list_add_rcu(&pt->list, &ptype_base[hash]);
++ }
++ spin_unlock_bh(&ptype_lock);
++}
++
++extern void linkwatch_run_queue(void);
++
++
++
++/**
++ * __dev_remove_pack - remove packet handler
++ * @pt: packet type declaration
++ *
++ * Remove a protocol handler that was previously added to the kernel
++ * protocol handlers by dev_add_pack(). The passed &packet_type is removed
++ * from the kernel lists and can be freed or reused once this function
++ * returns.
++ *
++ * The packet type might still be in use by receivers
++ * and must not be freed until after all the CPU's have gone
++ * through a quiescent state.
++ */
++void __dev_remove_pack(struct packet_type *pt)
++{
++ struct list_head *head;
++ struct packet_type *pt1;
++
++ spin_lock_bh(&ptype_lock);
++
++ if (pt->type == htons(ETH_P_ALL)) {
++ netdev_nit--;
++ head = &ptype_all;
++ } else
++ head = &ptype_base[ntohs(pt->type) & 15];
++
++ list_for_each_entry(pt1, head, list) {
++ if (pt == pt1) {
++ list_del_rcu(&pt->list);
++ goto out;
++ }
++ }
++
++ printk(KERN_WARNING "dev_remove_pack: %p not found.\n", pt);
++out:
++ spin_unlock_bh(&ptype_lock);
++}
++/**
++ * dev_remove_pack - remove packet handler
++ * @pt: packet type declaration
++ *
++ * Remove a protocol handler that was previously added to the kernel
++ * protocol handlers by dev_add_pack(). The passed &packet_type is removed
++ * from the kernel lists and can be freed or reused once this function
++ * returns.
++ *
++ * This call sleeps to guarantee that no CPU is looking at the packet
++ * type after return.
++ */
++void dev_remove_pack(struct packet_type *pt)
++{
++ __dev_remove_pack(pt);
++
++ synchronize_net();
++}
++
++/******************************************************************************
++
++ Device Boot-time Settings Routines
++
++*******************************************************************************/
++
++/* Boot time configuration table */
++static struct netdev_boot_setup dev_boot_setup[NETDEV_BOOT_SETUP_MAX];
++
++/**
++ * netdev_boot_setup_add - add new setup entry
++ * @name: name of the device
++ * @map: configured settings for the device
++ *
++ * Adds new setup entry to the dev_boot_setup list. The function
++ * returns 0 on error and 1 on success. This is a generic routine to
++ * all netdevices.
++ */
++static int netdev_boot_setup_add(char *name, struct ifmap *map)
++{
++ struct netdev_boot_setup *s;
++ int i;
++
++ s = dev_boot_setup;
++ for (i = 0; i < NETDEV_BOOT_SETUP_MAX; i++) {
++ if (s[i].name[0] == '\0' || s[i].name[0] == ' ') {
++ memset(s[i].name, 0, sizeof(s[i].name));
++ strcpy(s[i].name, name);
++ memcpy(&s[i].map, map, sizeof(s[i].map));
++ break;
++ }
++ }
++
++ return i >= NETDEV_BOOT_SETUP_MAX ? 0 : 1;
++}
++
++/**
++ * netdev_boot_setup_check - check boot time settings
++ * @dev: the netdevice
++ *
++ * Check boot time settings for the device.
++ * The found settings are set for the device to be used
++ * later in the device probing.
++ * Returns 0 if no settings found, 1 if they are.
++ */
++int netdev_boot_setup_check(struct net_device *dev)
++{
++ struct netdev_boot_setup *s = dev_boot_setup;
++ int i;
++
++ for (i = 0; i < NETDEV_BOOT_SETUP_MAX; i++) {
++ if (s[i].name[0] != '\0' && s[i].name[0] != ' ' &&
++ !strncmp(dev->name, s[i].name, strlen(s[i].name))) {
++ dev->irq = s[i].map.irq;
++ dev->base_addr = s[i].map.base_addr;
++ dev->mem_start = s[i].map.mem_start;
++ dev->mem_end = s[i].map.mem_end;
++ return 1;
++ }
++ }
++ return 0;
++}
++
++
++/**
++ * netdev_boot_base - get address from boot time settings
++ * @prefix: prefix for network device
++ * @unit: id for network device
++ *
++ * Check boot time settings for the base address of device.
++ * The found settings are set for the device to be used
++ * later in the device probing.
++ * Returns 0 if no settings found.
++ */
++unsigned long netdev_boot_base(const char *prefix, int unit)
++{
++ const struct netdev_boot_setup *s = dev_boot_setup;
++ char name[IFNAMSIZ];
++ int i;
++
++ sprintf(name, "%s%d", prefix, unit);
++
++ /*
++ * If device already registered then return base of 1
++ * to indicate not to probe for this interface
++ */
++ if (__dev_get_by_name(name))
++ return 1;
++
++ for (i = 0; i < NETDEV_BOOT_SETUP_MAX; i++)
++ if (!strcmp(name, s[i].name))
++ return s[i].map.base_addr;
++ return 0;
++}
++
++/*
++ * Saves at boot time configured settings for any netdevice.
++ */
++int __init netdev_boot_setup(char *str)
++{
++ int ints[5];
++ struct ifmap map;
++
++ str = get_options(str, ARRAY_SIZE(ints), ints);
++ if (!str || !*str)
++ return 0;
++
++ /* Save settings */
++ memset(&map, 0, sizeof(map));
++ if (ints[0] > 0)
++ map.irq = ints[1];
++ if (ints[0] > 1)
++ map.base_addr = ints[2];
++ if (ints[0] > 2)
++ map.mem_start = ints[3];
++ if (ints[0] > 3)
++ map.mem_end = ints[4];
++
++ /* Add new entry to the list */
++ return netdev_boot_setup_add(str, &map);
++}
++
++__setup("netdev=", netdev_boot_setup);
++
++/*******************************************************************************
++
++ Device Interface Subroutines
++
++*******************************************************************************/
++
++/**
++ * __dev_get_by_name - find a device by its name
++ * @name: name to find
++ *
++ * Find an interface by name. Must be called under RTNL semaphore
++ * or @dev_base_lock. If the name is found a pointer to the device
++ * is returned. If the name is not found then %NULL is returned. The
++ * reference counters are not incremented so the caller must be
++ * careful with locks.
++ */
++
++struct net_device *__dev_get_by_name(const char *name)
++{
++ struct hlist_node *p;
++
++ hlist_for_each(p, dev_name_hash(name)) {
++ struct net_device *dev
++ = hlist_entry(p, struct net_device, name_hlist);
++ if (!strncmp(dev->name, name, IFNAMSIZ))
++ return dev;
++ }
++ return NULL;
++}
++
++/**
++ * dev_get_by_name - find a device by its name
++ * @name: name to find
++ *
++ * Find an interface by name. This can be called from any
++ * context and does its own locking. The returned handle has
++ * the usage count incremented and the caller must use dev_put() to
++ * release it when it is no longer needed. %NULL is returned if no
++ * matching device is found.
++ */
++
++struct net_device *dev_get_by_name(const char *name)
++{
++ struct net_device *dev;
++
++ read_lock(&dev_base_lock);
++ dev = __dev_get_by_name(name);
++ if (dev)
++ dev_hold(dev);
++ read_unlock(&dev_base_lock);
++ return dev;
++}
++
++/**
++ * __dev_get_by_index - find a device by its ifindex
++ * @ifindex: index of device
++ *
++ * Search for an interface by index. Returns %NULL if the device
++ * is not found or a pointer to the device. The device has not
++ * had its reference counter increased so the caller must be careful
++ * about locking. The caller must hold either the RTNL semaphore
++ * or @dev_base_lock.
++ */
++
++struct net_device *__dev_get_by_index(int ifindex)
++{
++ struct hlist_node *p;
++
++ hlist_for_each(p, dev_index_hash(ifindex)) {
++ struct net_device *dev
++ = hlist_entry(p, struct net_device, index_hlist);
++ if (dev->ifindex == ifindex)
++ return dev;
++ }
++ return NULL;
++}
++
++
++/**
++ * dev_get_by_index - find a device by its ifindex
++ * @ifindex: index of device
++ *
++ * Search for an interface by index. Returns NULL if the device
++ * is not found or a pointer to the device. The device returned has
++ * had a reference added and the pointer is safe until the user calls
++ * dev_put to indicate they have finished with it.
++ */
++
++struct net_device *dev_get_by_index(int ifindex)
++{
++ struct net_device *dev;
++
++ read_lock(&dev_base_lock);
++ dev = __dev_get_by_index(ifindex);
++ if (dev)
++ dev_hold(dev);
++ read_unlock(&dev_base_lock);
++ return dev;
++}
++
++/**
++ * dev_getbyhwaddr - find a device by its hardware address
++ * @type: media type of device
++ * @ha: hardware address
++ *
++ * Search for an interface by MAC address. Returns NULL if the device
++ * is not found or a pointer to the device. The caller must hold the
++ * rtnl semaphore. The returned device has not had its ref count increased
++ * and the caller must therefore be careful about locking
++ *
++ * BUGS:
++ * If the API was consistent this would be __dev_get_by_hwaddr
++ */
++
++struct net_device *dev_getbyhwaddr(unsigned short type, char *ha)
++{
++ struct net_device *dev;
++
++ ASSERT_RTNL();
++
++ for (dev = dev_base; dev; dev = dev->next)
++ if (dev->type == type &&
++ !memcmp(dev->dev_addr, ha, dev->addr_len))
++ break;
++ return dev;
++}
++
++struct net_device *dev_getfirstbyhwtype(unsigned short type)
++{
++ struct net_device *dev;
++
++ rtnl_lock();
++ for (dev = dev_base; dev; dev = dev->next) {
++ if (dev->type == type) {
++ dev_hold(dev);
++ break;
++ }
++ }
++ rtnl_unlock();
++ return dev;
++}
++
++EXPORT_SYMBOL(dev_getfirstbyhwtype);
++
++/**
++ * dev_get_by_flags - find any device with given flags
++ * @if_flags: IFF_* values
++ * @mask: bitmask of bits in if_flags to check
++ *
++ * Search for any interface with the given flags. Returns NULL if a device
++ * is not found or a pointer to the device. The device returned has
++ * had a reference added and the pointer is safe until the user calls
++ * dev_put to indicate they have finished with it.
++ */
++
++struct net_device * dev_get_by_flags(unsigned short if_flags, unsigned short mask)
++{
++ struct net_device *dev;
++
++ read_lock(&dev_base_lock);
++ for (dev = dev_base; dev != NULL; dev = dev->next) {
++ if (((dev->flags ^ if_flags) & mask) == 0) {
++ dev_hold(dev);
++ break;
++ }
++ }
++ read_unlock(&dev_base_lock);
++ return dev;
++}
++
++/**
++ * dev_valid_name - check if name is okay for network device
++ * @name: name string
++ *
++ * Network device names need to be valid file names to
++ * to allow sysfs to work
++ */
++static int dev_valid_name(const char *name)
++{
++ return !(*name == '\0'
++ || !strcmp(name, ".")
++ || !strcmp(name, "..")
++ || strchr(name, '/'));
++}
++
++/**
++ * dev_alloc_name - allocate a name for a device
++ * @dev: device
++ * @name: name format string
++ *
++ * Passed a format string - eg "lt%d" it will try and find a suitable
++ * id. Not efficient for many devices, not called a lot. The caller
++ * must hold the dev_base or rtnl lock while allocating the name and
++ * adding the device in order to avoid duplicates. Returns the number
++ * of the unit assigned or a negative errno code.
++ */
++
++int dev_alloc_name(struct net_device *dev, const char *name)
++{
++ int i = 0;
++ char buf[IFNAMSIZ];
++ const char *p;
++ const int max_netdevices = 8*PAGE_SIZE;
++ long *inuse;
++ struct net_device *d;
++
++ p = strnchr(name, IFNAMSIZ-1, '%');
++ if (p) {
++ /*
++ * Verify the string as this thing may have come from
++ * the user. There must be either one "%d" and no other "%"
++ * characters.
++ */
++ if (p[1] != 'd' || strchr(p + 2, '%'))
++ return -EINVAL;
++
++ /* Use one page as a bit array of possible slots */
++ inuse = (long *) get_zeroed_page(GFP_ATOMIC);
++ if (!inuse)
++ return -ENOMEM;
++
++ for (d = dev_base; d; d = d->next) {
++ if (!sscanf(d->name, name, &i))
++ continue;
++ if (i < 0 || i >= max_netdevices)
++ continue;
++
++ /* avoid cases where sscanf is not exact inverse of printf */
++ snprintf(buf, sizeof(buf), name, i);
++ if (!strncmp(buf, d->name, IFNAMSIZ))
++ set_bit(i, inuse);
++ }
++
++ i = find_first_zero_bit(inuse, max_netdevices);
++ free_page((unsigned long) inuse);
++ }
++
++ snprintf(buf, sizeof(buf), name, i);
++ if (!__dev_get_by_name(buf)) {
++ strlcpy(dev->name, buf, IFNAMSIZ);
++ return i;
++ }
++
++ /* It is possible to run out of possible slots
++ * when the name is long and there isn't enough space left
++ * for the digits, or if all bits are used.
++ */
++ return -ENFILE;
++}
++
++
++/**
++ * dev_change_name - change name of a device
++ * @dev: device
++ * @newname: name (or format string) must be at least IFNAMSIZ
++ *
++ * Change name of a device, can pass format strings "eth%d".
++ * for wildcarding.
++ */
++int dev_change_name(struct net_device *dev, char *newname)
++{
++ int err = 0;
++
++ ASSERT_RTNL();
++
++ if (dev->flags & IFF_UP)
++ return -EBUSY;
++
++ if (!dev_valid_name(newname))
++ return -EINVAL;
++
++ if (strchr(newname, '%')) {
++ err = dev_alloc_name(dev, newname);
++ if (err < 0)
++ return err;
++ strcpy(newname, dev->name);
++ }
++ else if (__dev_get_by_name(newname))
++ return -EEXIST;
++ else
++ strlcpy(dev->name, newname, IFNAMSIZ);
++
++ err = class_device_rename(&dev->class_dev, dev->name);
++ if (!err) {
++ hlist_del(&dev->name_hlist);
++ hlist_add_head(&dev->name_hlist, dev_name_hash(dev->name));
++ notifier_call_chain(&netdev_chain, NETDEV_CHANGENAME, dev);
++ }
++
++ return err;
++}
++
++/**
++ * netdev_features_change - device changes fatures
++ * @dev: device to cause notification
++ *
++ * Called to indicate a device has changed features.
++ */
++void netdev_features_change(struct net_device *dev)
++{
++ notifier_call_chain(&netdev_chain, NETDEV_FEAT_CHANGE, dev);
++}
++EXPORT_SYMBOL(netdev_features_change);
++
++/**
++ * netdev_state_change - device changes state
++ * @dev: device to cause notification
++ *
++ * Called to indicate a device has changed state. This function calls
++ * the notifier chains for netdev_chain and sends a NEWLINK message
++ * to the routing socket.
++ */
++void netdev_state_change(struct net_device *dev)
++{
++ if (dev->flags & IFF_UP) {
++ notifier_call_chain(&netdev_chain, NETDEV_CHANGE, dev);
++ rtmsg_ifinfo(RTM_NEWLINK, dev, 0);
++ }
++}
++
++/**
++ * dev_load - load a network module
++ * @name: name of interface
++ *
++ * If a network interface is not present and the process has suitable
++ * privileges this function loads the module. If module loading is not
++ * available in this kernel then it becomes a nop.
++ */
++
++void dev_load(const char *name)
++{
++ struct net_device *dev;
++
++ read_lock(&dev_base_lock);
++ dev = __dev_get_by_name(name);
++ read_unlock(&dev_base_lock);
++
++ if (!dev && capable(CAP_SYS_MODULE))
++ request_module("%s", name);
++}
++
++static int default_rebuild_header(struct sk_buff *skb)
++{
++ printk(KERN_DEBUG "%s: default_rebuild_header called -- BUG!\n",
++ skb->dev ? skb->dev->name : "NULL!!!");
++ kfree_skb(skb);
++ return 1;
++}
++
++
++/**
++ * dev_open - prepare an interface for use.
++ * @dev: device to open
++ *
++ * Takes a device from down to up state. The device's private open
++ * function is invoked and then the multicast lists are loaded. Finally
++ * the device is moved into the up state and a %NETDEV_UP message is
++ * sent to the netdev notifier chain.
++ *
++ * Calling this function on an active interface is a nop. On a failure
++ * a negative errno code is returned.
++ */
++int dev_open(struct net_device *dev)
++{
++ int ret = 0;
++
++ /*
++ * Is it already up?
++ */
++
++ if (dev->flags & IFF_UP)
++ return 0;
++
++ /*
++ * Is it even present?
++ */
++ if (!netif_device_present(dev))
++ return -ENODEV;
++
++ /*
++ * Call device private open method
++ */
++ set_bit(__LINK_STATE_START, &dev->state);
++ if (dev->open) {
++ ret = dev->open(dev);
++ if (ret)
++ clear_bit(__LINK_STATE_START, &dev->state);
++ }
++
++ /*
++ * If it went open OK then:
++ */
++
++ if (!ret) {
++ /*
++ * Set the flags.
++ */
++ dev->flags |= IFF_UP;
++
++ /*
++ * Initialize multicasting status
++ */
++ dev_mc_upload(dev);
++
++ /*
++ * Wakeup transmit queue engine
++ */
++ dev_activate(dev);
++
++ /*
++ * ... and announce new interface.
++ */
++ notifier_call_chain(&netdev_chain, NETDEV_UP, dev);
++ }
++ return ret;
++}
++
++/**
++ * dev_close - shutdown an interface.
++ * @dev: device to shutdown
++ *
++ * This function moves an active device into down state. A
++ * %NETDEV_GOING_DOWN is sent to the netdev notifier chain. The device
++ * is then deactivated and finally a %NETDEV_DOWN is sent to the notifier
++ * chain.
++ */
++int dev_close(struct net_device *dev)
++{
++ if (!(dev->flags & IFF_UP))
++ return 0;
++
++ /*
++ * Tell people we are going down, so that they can
++ * prepare to death, when device is still operating.
++ */
++ notifier_call_chain(&netdev_chain, NETDEV_GOING_DOWN, dev);
++
++ dev_deactivate(dev);
++
++ clear_bit(__LINK_STATE_START, &dev->state);
++
++ /* Synchronize to scheduled poll. We cannot touch poll list,
++ * it can be even on different cpu. So just clear netif_running(),
++ * and wait when poll really will happen. Actually, the best place
++ * for this is inside dev->stop() after device stopped its irq
++ * engine, but this requires more changes in devices. */
++
++ smp_mb__after_clear_bit(); /* Commit netif_running(). */
++ while (test_bit(__LINK_STATE_RX_SCHED, &dev->state)) {
++ /* No hurry. */
++ current->state = TASK_INTERRUPTIBLE;
++ schedule_timeout(1);
++ }
++
++ /*
++ * Call the device specific close. This cannot fail.
++ * Only if device is UP
++ *
++ * We allow it to be called even after a DETACH hot-plug
++ * event.
++ */
++ if (dev->stop)
++ dev->stop(dev);
++
++ /*
++ * Device is now down.
++ */
++
++ dev->flags &= ~IFF_UP;
++
++ /*
++ * Tell people we are down
++ */
++ notifier_call_chain(&netdev_chain, NETDEV_DOWN, dev);
++
++ return 0;
++}
++
++
++/*
++ * Device change register/unregister. These are not inline or static
++ * as we export them to the world.
++ */
++
++/**
++ * register_netdevice_notifier - register a network notifier block
++ * @nb: notifier
++ *
++ * Register a notifier to be called when network device events occur.
++ * The notifier passed is linked into the kernel structures and must
++ * not be reused until it has been unregistered. A negative errno code
++ * is returned on a failure.
++ *
++ * When registered all registration and up events are replayed
++ * to the new notifier to allow device to have a race free
++ * view of the network device list.
++ */
++
++int register_netdevice_notifier(struct notifier_block *nb)
++{
++ struct net_device *dev;
++ int err;
++
++ rtnl_lock();
++ err = notifier_chain_register(&netdev_chain, nb);
++ if (!err) {
++ for (dev = dev_base; dev; dev = dev->next) {
++ nb->notifier_call(nb, NETDEV_REGISTER, dev);
++
++ if (dev->flags & IFF_UP)
++ nb->notifier_call(nb, NETDEV_UP, dev);
++ }
++ }
++ rtnl_unlock();
++ return err;
++}
++
++/**
++ * unregister_netdevice_notifier - unregister a network notifier block
++ * @nb: notifier
++ *
++ * Unregister a notifier previously registered by
++ * register_netdevice_notifier(). The notifier is unlinked into the
++ * kernel structures and may then be reused. A negative errno code
++ * is returned on a failure.
++ */
++
++int unregister_netdevice_notifier(struct notifier_block *nb)
++{
++ return notifier_chain_unregister(&netdev_chain, nb);
++}
++
++/**
++ * call_netdevice_notifiers - call all network notifier blocks
++ * @val: value passed unmodified to notifier function
++ * @v: pointer passed unmodified to notifier function
++ *
++ * Call all network notifier blocks. Parameters and return value
++ * are as for notifier_call_chain().
++ */
++
++int call_netdevice_notifiers(unsigned long val, void *v)
++{
++ return notifier_call_chain(&netdev_chain, val, v);
++}
++
++/* When > 0 there are consumers of rx skb time stamps */
++static atomic_t netstamp_needed = ATOMIC_INIT(0);
++
++void net_enable_timestamp(void)
++{
++ atomic_inc(&netstamp_needed);
++}
++
++void net_disable_timestamp(void)
++{
++ atomic_dec(&netstamp_needed);
++}
++
++static inline void net_timestamp(struct timeval *stamp)
++{
++ if (atomic_read(&netstamp_needed))
++ do_gettimeofday(stamp);
++ else {
++ stamp->tv_sec = 0;
++ stamp->tv_usec = 0;
++ }
++}
++
++/*
++ * Support routine. Sends outgoing frames to any network
++ * taps currently in use.
++ */
++
++void dev_queue_xmit_nit(struct sk_buff *skb, struct net_device *dev)
++{
++ struct packet_type *ptype;
++ net_timestamp(&skb->stamp);
++
++ rcu_read_lock();
++ list_for_each_entry_rcu(ptype, &ptype_all, list) {
++ /* Never send packets back to the socket
++ * they originated from - MvS (miquels@drinkel.ow.org)
++ */
++ if ((ptype->dev == dev || !ptype->dev) &&
++ (ptype->af_packet_priv == NULL ||
++ (struct sock *)ptype->af_packet_priv != skb->sk)) {
++ struct sk_buff *skb2= skb_clone(skb, GFP_ATOMIC);
++ if (!skb2)
++ break;
++
++ /* skb->nh should be correctly
++ set by sender, so that the second statement is
++ just protection against buggy protocols.
++ */
++ skb2->mac.raw = skb2->data;
++
++ if (skb2->nh.raw < skb2->data ||
++ skb2->nh.raw > skb2->tail) {
++ if (net_ratelimit())
++ printk(KERN_CRIT "protocol %04x is "
++ "buggy, dev %s\n",
++ skb2->protocol, dev->name);
++ skb2->nh.raw = skb2->data;
++ }
++
++ skb2->h.raw = skb2->nh.raw;
++ skb2->pkt_type = PACKET_OUTGOING;
++ ptype->func(skb2, skb->dev, ptype);
++ }
++ }
++ rcu_read_unlock();
++}
++
++/*
++ * Invalidate hardware checksum when packet is to be mangled, and
++ * complete checksum manually on outgoing path.
++ */
++int skb_checksum_help(struct sk_buff *skb, int inward)
++{
++ unsigned int csum;
++ int ret = 0, offset = skb->h.raw - skb->data;
++
++ if (inward) {
++ skb->ip_summed = CHECKSUM_NONE;
++ goto out;
++ }
++
++ if (skb_cloned(skb)) {
++ ret = pskb_expand_head(skb, 0, 0, GFP_ATOMIC);
++ if (ret)
++ goto out;
++ }
++
++ if (offset > (int)skb->len)
++ BUG();
++ csum = skb_checksum(skb, offset, skb->len-offset, 0);
++
++ offset = skb->tail - skb->h.raw;
++ if (offset <= 0)
++ BUG();
++ if (skb->csum + 2 > offset)
++ BUG();
++
++ *(u16*)(skb->h.raw + skb->csum) = csum_fold(csum);
++ skb->ip_summed = CHECKSUM_NONE;
++out:
++ return ret;
++}
++
++#ifdef CONFIG_HIGHMEM
++/* Actually, we should eliminate this check as soon as we know, that:
++ * 1. IOMMU is present and allows to map all the memory.
++ * 2. No high memory really exists on this machine.
++ */
++
++static inline int illegal_highdma(struct net_device *dev, struct sk_buff *skb)
++{
++ int i;
++
++ if (dev->features & NETIF_F_HIGHDMA)
++ return 0;
++
++ for (i = 0; i < skb_shinfo(skb)->nr_frags; i++)
++ if (PageHighMem(skb_shinfo(skb)->frags[i].page))
++ return 1;
++
++ return 0;
++}
++#else
++#define illegal_highdma(dev, skb) (0)
++#endif
++
++extern void skb_release_data(struct sk_buff *);
++
++/* Keep head the same: replace data */
++int __skb_linearize(struct sk_buff *skb, int gfp_mask)
++{
++ unsigned int size;
++ u8 *data;
++ long offset;
++ struct skb_shared_info *ninfo;
++ int headerlen = skb->data - skb->head;
++ int expand = (skb->tail + skb->data_len) - skb->end;
++
++ if (skb_shared(skb))
++ BUG();
++
++ if (expand <= 0)
++ expand = 0;
++
++ size = skb->end - skb->head + expand;
++ size = SKB_DATA_ALIGN(size);
++ data = kmalloc(size + sizeof(struct skb_shared_info), gfp_mask);
++ if (!data)
++ return -ENOMEM;
++
++ /* Copy entire thing */
++ if (skb_copy_bits(skb, -headerlen, data, headerlen + skb->len))
++ BUG();
++
++ /* Set up shinfo */
++ ninfo = (struct skb_shared_info*)(data + size);
++ atomic_set(&ninfo->dataref, 1);
++ ninfo->tso_size = skb_shinfo(skb)->tso_size;
++ ninfo->tso_segs = skb_shinfo(skb)->tso_segs;
++ ninfo->nr_frags = 0;
++ ninfo->frag_list = NULL;
++
++ /* Offset between the two in bytes */
++ offset = data - skb->head;
++
++ /* Free old data. */
++ skb_release_data(skb);
++
++ skb->head = data;
++ skb->end = data + size;
++
++ /* Set up new pointers */
++ skb->h.raw += offset;
++ skb->nh.raw += offset;
++ skb->mac.raw += offset;
++ skb->tail += offset;
++ skb->data += offset;
++
++ /* We are no longer a clone, even if we were. */
++ skb->cloned = 0;
++
++ skb->tail += skb->data_len;
++ skb->data_len = 0;
++ return 0;
++}
++
++#define HARD_TX_LOCK(dev, cpu) { \
++ if ((dev->features & NETIF_F_LLTX) == 0) { \
++ spin_lock(&dev->xmit_lock); \
++ dev->xmit_lock_owner = cpu; \
++ } \
++}
++
++#define HARD_TX_UNLOCK(dev) { \
++ if ((dev->features & NETIF_F_LLTX) == 0) { \
++ dev->xmit_lock_owner = -1; \
++ spin_unlock(&dev->xmit_lock); \
++ } \
++}
++
++/**
++ * dev_queue_xmit - transmit a buffer
++ * @skb: buffer to transmit
++ *
++ * Queue a buffer for transmission to a network device. The caller must
++ * have set the device and priority and built the buffer before calling
++ * this function. The function can be called from an interrupt.
++ *
++ * A negative errno code is returned on a failure. A success does not
++ * guarantee the frame will be transmitted as it may be dropped due
++ * to congestion or traffic shaping.
++ *
++ * -----------------------------------------------------------------------------------
++ * I notice this method can also return errors from the queue disciplines,
++ * including NET_XMIT_DROP, which is a positive value. So, errors can also
++ * be positive.
++ *
++ * Regardless of the return value, the skb is consumed, so it is currently
++ * difficult to retry a send to this method. (You can bump the ref count
++ * before sending to hold a reference for retry if you are careful.)
++ *
++ * When calling this method, interrupts MUST be enabled. This is because
++ * the BH enable code must have IRQs enabled so that it will not deadlock.
++ * --BLG
++ */
++
++int dev_queue_xmit(struct sk_buff *skb)
++{
++ struct net_device *dev = skb->dev;
++ struct Qdisc *q;
++ int rc = -ENOMEM;
++
++ if (skb_shinfo(skb)->frag_list &&
++ !(dev->features & NETIF_F_FRAGLIST) &&
++ __skb_linearize(skb, GFP_ATOMIC))
++ goto out_kfree_skb;
++
++ /* Fragmented skb is linearized if device does not support SG,
++ * or if at least one of fragments is in highmem and device
++ * does not support DMA from it.
++ */
++ if (skb_shinfo(skb)->nr_frags &&
++ (!(dev->features & NETIF_F_SG) || illegal_highdma(dev, skb)) &&
++ __skb_linearize(skb, GFP_ATOMIC))
++ goto out_kfree_skb;
++
++ /* If packet is not checksummed and device does not support
++ * checksumming for this protocol, complete checksumming here.
++ */
++ if (skb->ip_summed == CHECKSUM_HW &&
++ (!(dev->features & (NETIF_F_HW_CSUM | NETIF_F_NO_CSUM)) &&
++ (!(dev->features & NETIF_F_IP_CSUM) ||
++ skb->protocol != htons(ETH_P_IP))))
++ if (skb_checksum_help(skb, 0))
++ goto out_kfree_skb;
++
++ /* Disable soft irqs for various locks below. Also
++ * stops preemption for RCU.
++ */
++ local_bh_disable();
++
++ /* Updates of qdisc are serialized by queue_lock.
++ * The struct Qdisc which is pointed to by qdisc is now a
++ * rcu structure - it may be accessed without acquiring
++ * a lock (but the structure may be stale.) The freeing of the
++ * qdisc will be deferred until it's known that there are no
++ * more references to it.
++ *
++ * If the qdisc has an enqueue function, we still need to
++ * hold the queue_lock before calling it, since queue_lock
++ * also serializes access to the device queue.
++ */
++
++ q = rcu_dereference(dev->qdisc);
++#ifdef CONFIG_NET_CLS_ACT
++ skb->tc_verd = SET_TC_AT(skb->tc_verd,AT_EGRESS);
++#endif
++ if (q->enqueue) {
++ /* Grab device queue */
++ spin_lock(&dev->queue_lock);
++
++ rc = q->enqueue(skb, q);
++
++ qdisc_run(dev);
++
++ spin_unlock(&dev->queue_lock);
++ rc = rc == NET_XMIT_BYPASS ? NET_XMIT_SUCCESS : rc;
++ goto out;
++ }
++
++ /* The device has no queue. Common case for software devices:
++ loopback, all the sorts of tunnels...
++
++ Really, it is unlikely that xmit_lock protection is necessary here.
++ (f.e. loopback and IP tunnels are clean ignoring statistics
++ counters.)
++ However, it is possible, that they rely on protection
++ made by us here.
++
++ Check this and shot the lock. It is not prone from deadlocks.
++ Either shot noqueue qdisc, it is even simpler 8)
++ */
++ if (dev->flags & IFF_UP) {
++ int cpu = smp_processor_id(); /* ok because BHs are off */
++
++ if (dev->xmit_lock_owner != cpu) {
++
++ HARD_TX_LOCK(dev, cpu);
++
++ if (!netif_queue_stopped(dev)) {
++ if (netdev_nit)
++ dev_queue_xmit_nit(skb, dev);
++
++ rc = 0;
++ if (!dev->hard_start_xmit(skb, dev)) {
++ HARD_TX_UNLOCK(dev);
++ goto out;
++ }
++ }
++ HARD_TX_UNLOCK(dev);
++ if (net_ratelimit())
++ printk(KERN_CRIT "Virtual device %s asks to "
++ "queue packet!\n", dev->name);
++ } else {
++ /* Recursion is detected! It is possible,
++ * unfortunately */
++ if (net_ratelimit())
++ printk(KERN_CRIT "Dead loop on virtual device "
++ "%s, fix it urgently!\n", dev->name);
++ }
++ }
++
++ rc = -ENETDOWN;
++ local_bh_enable();
++
++out_kfree_skb:
++ kfree_skb(skb);
++ return rc;
++out:
++ local_bh_enable();
++ return rc;
++}
++
++
++/*=======================================================================
++ Receiver routines
++ =======================================================================*/
++
++int netdev_max_backlog = 300;
++int weight_p = 64; /* old backlog weight */
++/* These numbers are selected based on intuition and some
++ * experimentatiom, if you have more scientific way of doing this
++ * please go ahead and fix things.
++ */
++int no_cong_thresh = 10;
++int no_cong = 20;
++int lo_cong = 100;
++int mod_cong = 290;
++
++DEFINE_PER_CPU(struct netif_rx_stats, netdev_rx_stat) = { 0, };
++
++
++static void get_sample_stats(int cpu)
++{
++#ifdef RAND_LIE
++ unsigned long rd;
++ int rq;
++#endif
++ struct softnet_data *sd = &per_cpu(softnet_data, cpu);
++ int blog = sd->input_pkt_queue.qlen;
++ int avg_blog = sd->avg_blog;
++
++ avg_blog = (avg_blog >> 1) + (blog >> 1);
++
++ if (avg_blog > mod_cong) {
++ /* Above moderate congestion levels. */
++ sd->cng_level = NET_RX_CN_HIGH;
++#ifdef RAND_LIE
++ rd = net_random();
++ rq = rd % netdev_max_backlog;
++ if (rq < avg_blog) /* unlucky bastard */
++ sd->cng_level = NET_RX_DROP;
++#endif
++ } else if (avg_blog > lo_cong) {
++ sd->cng_level = NET_RX_CN_MOD;
++#ifdef RAND_LIE
++ rd = net_random();
++ rq = rd % netdev_max_backlog;
++ if (rq < avg_blog) /* unlucky bastard */
++ sd->cng_level = NET_RX_CN_HIGH;
++#endif
++ } else if (avg_blog > no_cong)
++ sd->cng_level = NET_RX_CN_LOW;
++ else /* no congestion */
++ sd->cng_level = NET_RX_SUCCESS;
++
++ sd->avg_blog = avg_blog;
++}
++
++#ifdef OFFLINE_SAMPLE
++static void sample_queue(unsigned long dummy)
++{
++/* 10 ms 0r 1ms -- i don't care -- JHS */
++ int next_tick = 1;
++ int cpu = smp_processor_id();
++
++ get_sample_stats(cpu);
++ next_tick += jiffies;
++ mod_timer(&samp_timer, next_tick);
++}
++#endif
++
++
++/**
++ * netif_rx - post buffer to the network code
++ * @skb: buffer to post
++ *
++ * This function receives a packet from a device driver and queues it for
++ * the upper (protocol) levels to process. It always succeeds. The buffer
++ * may be dropped during processing for congestion control or by the
++ * protocol layers.
++ *
++ * return values:
++ * NET_RX_SUCCESS (no congestion)
++ * NET_RX_CN_LOW (low congestion)
++ * NET_RX_CN_MOD (moderate congestion)
++ * NET_RX_CN_HIGH (high congestion)
++ * NET_RX_DROP (packet was dropped)
++ *
++ */
++
++int netif_rx(struct sk_buff *skb)
++{
++ int this_cpu;
++ struct softnet_data *queue;
++ unsigned long flags;
++
++ /* if netpoll wants it, pretend we never saw it */
++ if (netpoll_rx(skb))
++ return NET_RX_DROP;
++
++ if (!skb->stamp.tv_sec)
++ net_timestamp(&skb->stamp);
++
++ /*
++ * The code is rearranged so that the path is the most
++ * short when CPU is congested, but is still operating.
++ */
++ local_irq_save(flags);
++ this_cpu = smp_processor_id();
++ queue = &__get_cpu_var(softnet_data);
++
++ __get_cpu_var(netdev_rx_stat).total++;
++ if (queue->input_pkt_queue.qlen <= netdev_max_backlog) {
++ if (queue->input_pkt_queue.qlen) {
++ if (queue->throttle)
++ goto drop;
++
++enqueue:
++ dev_hold(skb->dev);
++ __skb_queue_tail(&queue->input_pkt_queue, skb);
++#ifndef OFFLINE_SAMPLE
++ get_sample_stats(this_cpu);
++#endif
++ local_irq_restore(flags);
++ return queue->cng_level;
++ }
++
++ if (queue->throttle)
++ queue->throttle = 0;
++
++ netif_rx_schedule(&queue->backlog_dev);
++ goto enqueue;
++ }
++
++ if (!queue->throttle) {
++ queue->throttle = 1;
++ __get_cpu_var(netdev_rx_stat).throttled++;
++ }
++
++drop:
++ __get_cpu_var(netdev_rx_stat).dropped++;
++ local_irq_restore(flags);
++
++ kfree_skb(skb);
++ return NET_RX_DROP;
++}
++
++int netif_rx_ni(struct sk_buff *skb)
++{
++ int err;
++
++ preempt_disable();
++ err = netif_rx(skb);
++ if (local_softirq_pending())
++ do_softirq();
++ preempt_enable();
++
++ return err;
++}
++
++EXPORT_SYMBOL(netif_rx_ni);
++
++static __inline__ void skb_bond(struct sk_buff *skb)
++{
++ struct net_device *dev = skb->dev;
++
++ if (dev->master) {
++ skb->real_dev = skb->dev;
++ skb->dev = dev->master;
++ }
++}
++
++static void net_tx_action(struct softirq_action *h)
++{
++ struct softnet_data *sd = &__get_cpu_var(softnet_data);
++
++ if (sd->completion_queue) {
++ struct sk_buff *clist;
++
++ local_irq_disable();
++ clist = sd->completion_queue;
++ sd->completion_queue = NULL;
++ local_irq_enable();
++
++ while (clist) {
++ struct sk_buff *skb = clist;
++ clist = clist->next;
++
++ BUG_TRAP(!atomic_read(&skb->users));
++ __kfree_skb(skb);
++ }
++ }
++
++ if (sd->output_queue) {
++ struct net_device *head;
++
++ local_irq_disable();
++ head = sd->output_queue;
++ sd->output_queue = NULL;
++ local_irq_enable();
++
++ while (head) {
++ struct net_device *dev = head;
++ head = head->next_sched;
++
++ smp_mb__before_clear_bit();
++ clear_bit(__LINK_STATE_SCHED, &dev->state);
++
++ if (spin_trylock(&dev->queue_lock)) {
++ qdisc_run(dev);
++ spin_unlock(&dev->queue_lock);
++ } else {
++ netif_schedule(dev);
++ }
++ }
++ }
++}
++
++static __inline__ int deliver_skb(struct sk_buff *skb,
++ struct packet_type *pt_prev)
++{
++ atomic_inc(&skb->users);
++ return pt_prev->func(skb, skb->dev, pt_prev);
++}
++
++#if defined(CONFIG_BRIDGE) || defined (CONFIG_BRIDGE_MODULE)
++int (*br_handle_frame_hook)(struct net_bridge_port *p, struct sk_buff **pskb);
++struct net_bridge;
++struct net_bridge_fdb_entry *(*br_fdb_get_hook)(struct net_bridge *br,
++ unsigned char *addr);
++void (*br_fdb_put_hook)(struct net_bridge_fdb_entry *ent);
++
++static __inline__ int handle_bridge(struct sk_buff **pskb,
++ struct packet_type **pt_prev, int *ret)
++{
++ struct net_bridge_port *port;
++
++ if ((*pskb)->pkt_type == PACKET_LOOPBACK ||
++ (port = rcu_dereference((*pskb)->dev->br_port)) == NULL)
++ return 0;
++
++ if (*pt_prev) {
++ *ret = deliver_skb(*pskb, *pt_prev);
++ *pt_prev = NULL;
++ }
++
++ return br_handle_frame_hook(port, pskb);
++}
++#else
++#define handle_bridge(skb, pt_prev, ret) (0)
++#endif
++
++#ifdef CONFIG_NET_CLS_ACT
++/* TODO: Maybe we should just force sch_ingress to be compiled in
++ * when CONFIG_NET_CLS_ACT is? otherwise some useless instructions
++ * a compare and 2 stores extra right now if we dont have it on
++ * but have CONFIG_NET_CLS_ACT
++ * NOTE: This doesnt stop any functionality; if you dont have
++ * the ingress scheduler, you just cant add policies on ingress.
++ *
++ */
++static int ing_filter(struct sk_buff *skb)
++{
++ struct Qdisc *q;
++ struct net_device *dev = skb->dev;
++ int result = TC_ACT_OK;
++
++ if (dev->qdisc_ingress) {
++ __u32 ttl = (__u32) G_TC_RTTL(skb->tc_verd);
++ if (MAX_RED_LOOP < ttl++) {
++ printk("Redir loop detected Dropping packet (%s->%s)\n",
++ skb->input_dev?skb->input_dev->name:"??",skb->dev->name);
++ return TC_ACT_SHOT;
++ }
++
++ skb->tc_verd = SET_TC_RTTL(skb->tc_verd,ttl);
++
++ skb->tc_verd = SET_TC_AT(skb->tc_verd,AT_INGRESS);
++ if (NULL == skb->input_dev) {
++ skb->input_dev = skb->dev;
++ printk("ing_filter: fixed %s out %s\n",skb->input_dev->name,skb->dev->name);
++ }
++ spin_lock(&dev->ingress_lock);
++ if ((q = dev->qdisc_ingress) != NULL)
++ result = q->enqueue(skb, q);
++ spin_unlock(&dev->ingress_lock);
++
++ }
++
++ return result;
++}
++#endif
++
++int netif_receive_skb(struct sk_buff *skb)
++{
++ struct packet_type *ptype, *pt_prev;
++ int ret = NET_RX_DROP;
++ unsigned short type;
++
++ /* if we've gotten here through NAPI, check netpoll */
++ if (skb->dev->poll && netpoll_rx(skb))
++ return NET_RX_DROP;
++
++ if (!skb->stamp.tv_sec)
++ net_timestamp(&skb->stamp);
++
++ skb_bond(skb);
++
++ __get_cpu_var(netdev_rx_stat).total++;
++
++ skb->h.raw = skb->nh.raw = skb->data;
++ skb->mac_len = skb->nh.raw - skb->mac.raw;
++
++ pt_prev = NULL;
++
++ rcu_read_lock();
++
++#ifdef CONFIG_NET_CLS_ACT
++ if (skb->tc_verd & TC_NCLS) {
++ skb->tc_verd = CLR_TC_NCLS(skb->tc_verd);
++ goto ncls;
++ }
++#endif
++
++ list_for_each_entry_rcu(ptype, &ptype_all, list) {
++ if (!ptype->dev || ptype->dev == skb->dev) {
++ if (pt_prev)
++ ret = deliver_skb(skb, pt_prev);
++ pt_prev = ptype;
++ }
++ }
++
++#ifdef CONFIG_NET_CLS_ACT
++ if (pt_prev) {
++ ret = deliver_skb(skb, pt_prev);
++ pt_prev = NULL; /* noone else should process this after*/
++ } else {
++ skb->tc_verd = SET_TC_OK2MUNGE(skb->tc_verd);
++ }
++
++ ret = ing_filter(skb);
++
++ if (ret == TC_ACT_SHOT || (ret == TC_ACT_STOLEN)) {
++ kfree_skb(skb);
++ goto out;
++ }
++
++ skb->tc_verd = 0;
++ncls:
++#endif
++
++ handle_diverter(skb);
++
++ if (handle_bridge(&skb, &pt_prev, &ret))
++ goto out;
++
++ type = skb->protocol;
++ list_for_each_entry_rcu(ptype, &ptype_base[ntohs(type)&15], list) {
++ if (ptype->type == type &&
++ (!ptype->dev || ptype->dev == skb->dev)) {
++ if (pt_prev)
++ ret = deliver_skb(skb, pt_prev);
++ pt_prev = ptype;
++ }
++ }
++
++ if (pt_prev) {
++ ret = pt_prev->func(skb, skb->dev, pt_prev);
++ } else {
++ kfree_skb(skb);
++ /* Jamal, now you will not able to escape explaining
++ * me how you were going to use this. :-)
++ */
++ ret = NET_RX_DROP;
++ }
++
++out:
++ rcu_read_unlock();
++ return ret;
++}
++
++static int process_backlog(struct net_device *backlog_dev, int *budget)
++{
++ int work = 0;
++ int quota = min(backlog_dev->quota, *budget);
++ struct softnet_data *queue = &__get_cpu_var(softnet_data);
++ unsigned long start_time = jiffies;
++
++ backlog_dev->weight = weight_p;
++ for (;;) {
++ struct sk_buff *skb;
++ struct net_device *dev;
++
++ local_irq_disable();
++ skb = __skb_dequeue(&queue->input_pkt_queue);
++ if (!skb)
++ goto job_done;
++ local_irq_enable();
++
++ dev = skb->dev;
++
++ netif_receive_skb(skb);
++
++ dev_put(dev);
++
++ work++;
++
++ if (work >= quota || jiffies - start_time > 1)
++ break;
++
++ }
++
++ backlog_dev->quota -= work;
++ *budget -= work;
++ return -1;
++
++job_done:
++ backlog_dev->quota -= work;
++ *budget -= work;
++
++ list_del(&backlog_dev->poll_list);
++ smp_mb__before_clear_bit();
++ netif_poll_enable(backlog_dev);
++
++ if (queue->throttle)
++ queue->throttle = 0;
++ local_irq_enable();
++ return 0;
++}
++
++static void net_rx_action(struct softirq_action *h)
++{
++ struct softnet_data *queue = &__get_cpu_var(softnet_data);
++ unsigned long start_time = jiffies;
++ int budget = netdev_max_backlog;
++
++
++ local_irq_disable();
++
++ while (!list_empty(&queue->poll_list)) {
++ struct net_device *dev;
++
++ if (budget <= 0 || jiffies - start_time > 1)
++ goto softnet_break;
++
++ local_irq_enable();
++
++ dev = list_entry(queue->poll_list.next,
++ struct net_device, poll_list);
++ netpoll_poll_lock(dev);
++
++ if (dev->quota <= 0 || dev->poll(dev, &budget)) {
++ netpoll_poll_unlock(dev);
++ local_irq_disable();
++ list_del(&dev->poll_list);
++ list_add_tail(&dev->poll_list, &queue->poll_list);
++ if (dev->quota < 0)
++ dev->quota += dev->weight;
++ else
++ dev->quota = dev->weight;
++ } else {
++ netpoll_poll_unlock(dev);
++ dev_put(dev);
++ local_irq_disable();
++ }
++ }
++out:
++ local_irq_enable();
++ return;
++
++softnet_break:
++ __get_cpu_var(netdev_rx_stat).time_squeeze++;
++ __raise_softirq_irqoff(NET_RX_SOFTIRQ);
++ goto out;
++}
++
++static gifconf_func_t * gifconf_list [NPROTO];
++
++/**
++ * register_gifconf - register a SIOCGIF handler
++ * @family: Address family
++ * @gifconf: Function handler
++ *
++ * Register protocol dependent address dumping routines. The handler
++ * that is passed must not be freed or reused until it has been replaced
++ * by another handler.
++ */
++int register_gifconf(unsigned int family, gifconf_func_t * gifconf)
++{
++ if (family >= NPROTO)
++ return -EINVAL;
++ gifconf_list[family] = gifconf;
++ return 0;
++}
++
++
++/*
++ * Map an interface index to its name (SIOCGIFNAME)
++ */
++
++/*
++ * We need this ioctl for efficient implementation of the
++ * if_indextoname() function required by the IPv6 API. Without
++ * it, we would have to search all the interfaces to find a
++ * match. --pb
++ */
++
++static int dev_ifname(struct ifreq __user *arg)
++{
++ struct net_device *dev;
++ struct ifreq ifr;
++
++ /*
++ * Fetch the caller's info block.
++ */
++
++ if (copy_from_user(&ifr, arg, sizeof(struct ifreq)))
++ return -EFAULT;
++
++ read_lock(&dev_base_lock);
++ dev = __dev_get_by_index(ifr.ifr_ifindex);
++ if (!dev) {
++ read_unlock(&dev_base_lock);
++ return -ENODEV;
++ }
++
++ strcpy(ifr.ifr_name, dev->name);
++ read_unlock(&dev_base_lock);
++
++ if (copy_to_user(arg, &ifr, sizeof(struct ifreq)))
++ return -EFAULT;
++ return 0;
++}
++
++/*
++ * Perform a SIOCGIFCONF call. This structure will change
++ * size eventually, and there is nothing I can do about it.
++ * Thus we will need a 'compatibility mode'.
++ */
++
++static int dev_ifconf(char __user *arg)
++{
++ struct ifconf ifc;
++ struct net_device *dev;
++ char __user *pos;
++ int len;
++ int total;
++ int i;
++
++ /*
++ * Fetch the caller's info block.
++ */
++
++ if (copy_from_user(&ifc, arg, sizeof(struct ifconf)))
++ return -EFAULT;
++
++ pos = ifc.ifc_buf;
++ len = ifc.ifc_len;
++
++ /*
++ * Loop over the interfaces, and write an info block for each.
++ */
++
++ total = 0;
++ for (dev = dev_base; dev; dev = dev->next) {
++ for (i = 0; i < NPROTO; i++) {
++ if (gifconf_list[i]) {
++ int done;
++ if (!pos)
++ done = gifconf_list[i](dev, NULL, 0);
++ else
++ done = gifconf_list[i](dev, pos + total,
++ len - total);
++ if (done < 0)
++ return -EFAULT;
++ total += done;
++ }
++ }
++ }
++
++ /*
++ * All done. Write the updated control block back to the caller.
++ */
++ ifc.ifc_len = total;
++
++ /*
++ * Both BSD and Solaris return 0 here, so we do too.
++ */
++ return copy_to_user(arg, &ifc, sizeof(struct ifconf)) ? -EFAULT : 0;
++}
++
++#ifdef CONFIG_PROC_FS
++/*
++ * This is invoked by the /proc filesystem handler to display a device
++ * in detail.
++ */
++static __inline__ struct net_device *dev_get_idx(loff_t pos)
++{
++ struct net_device *dev;
++ loff_t i;
++
++ for (i = 0, dev = dev_base; dev && i < pos; ++i, dev = dev->next);
++
++ return i == pos ? dev : NULL;
++}
++
++void *dev_seq_start(struct seq_file *seq, loff_t *pos)
++{
++ read_lock(&dev_base_lock);
++ return *pos ? dev_get_idx(*pos - 1) : SEQ_START_TOKEN;
++}
++
++void *dev_seq_next(struct seq_file *seq, void *v, loff_t *pos)
++{
++ ++*pos;
++ return v == SEQ_START_TOKEN ? dev_base : ((struct net_device *)v)->next;
++}
++
++void dev_seq_stop(struct seq_file *seq, void *v)
++{
++ read_unlock(&dev_base_lock);
++}
++
++static void dev_seq_printf_stats(struct seq_file *seq, struct net_device *dev)
++{
++ if (dev->get_stats) {
++ struct net_device_stats *stats = dev->get_stats(dev);
++
++ seq_printf(seq, "%6s:%8lu %7lu %4lu %4lu %4lu %5lu %10lu %9lu "
++ "%8lu %7lu %4lu %4lu %4lu %5lu %7lu %10lu\n",
++ dev->name, stats->rx_bytes, stats->rx_packets,
++ stats->rx_errors,
++ stats->rx_dropped + stats->rx_missed_errors,
++ stats->rx_fifo_errors,
++ stats->rx_length_errors + stats->rx_over_errors +
++ stats->rx_crc_errors + stats->rx_frame_errors,
++ stats->rx_compressed, stats->multicast,
++ stats->tx_bytes, stats->tx_packets,
++ stats->tx_errors, stats->tx_dropped,
++ stats->tx_fifo_errors, stats->collisions,
++ stats->tx_carrier_errors +
++ stats->tx_aborted_errors +
++ stats->tx_window_errors +
++ stats->tx_heartbeat_errors,
++ stats->tx_compressed);
++ } else
++ seq_printf(seq, "%6s: No statistics available.\n", dev->name);
++}
++
++/*
++ * Called from the PROCfs module. This now uses the new arbitrary sized
++ * /proc/net interface to create /proc/net/dev
++ */
++static int dev_seq_show(struct seq_file *seq, void *v)
++{
++ if (v == SEQ_START_TOKEN)
++ seq_puts(seq, "Inter-| Receive "
++ " | Transmit\n"
++ " face |bytes packets errs drop fifo frame "
++ "compressed multicast|bytes packets errs "
++ "drop fifo colls carrier compressed\n");
++ else
++ dev_seq_printf_stats(seq, v);
++ return 0;
++}
++
++static struct netif_rx_stats *softnet_get_online(loff_t *pos)
++{
++ struct netif_rx_stats *rc = NULL;
++
++ while (*pos < NR_CPUS)
++ if (cpu_online(*pos)) {
++ rc = &per_cpu(netdev_rx_stat, *pos);
++ break;
++ } else
++ ++*pos;
++ return rc;
++}
++
++static void *softnet_seq_start(struct seq_file *seq, loff_t *pos)
++{
++ return softnet_get_online(pos);
++}
++
++static void *softnet_seq_next(struct seq_file *seq, void *v, loff_t *pos)
++{
++ ++*pos;
++ return softnet_get_online(pos);
++}
++
++static void softnet_seq_stop(struct seq_file *seq, void *v)
++{
++}
++
++static int softnet_seq_show(struct seq_file *seq, void *v)
++{
++ struct netif_rx_stats *s = v;
++
++ seq_printf(seq, "%08x %08x %08x %08x %08x %08x %08x %08x %08x\n",
++ s->total, s->dropped, s->time_squeeze, s->throttled,
++ s->fastroute_hit, s->fastroute_success, s->fastroute_defer,
++ s->fastroute_deferred_out,
++#if 0
++ s->fastroute_latency_reduction
++#else
++ s->cpu_collision
++#endif
++ );
++ return 0;
++}
++
++static struct seq_operations dev_seq_ops = {
++ .start = dev_seq_start,
++ .next = dev_seq_next,
++ .stop = dev_seq_stop,
++ .show = dev_seq_show,
++};
++
++static int dev_seq_open(struct inode *inode, struct file *file)
++{
++ return seq_open(file, &dev_seq_ops);
++}
++
++static struct file_operations dev_seq_fops = {
++ .owner = THIS_MODULE,
++ .open = dev_seq_open,
++ .read = seq_read,
++ .llseek = seq_lseek,
++ .release = seq_release,
++};
++
++static struct seq_operations softnet_seq_ops = {
++ .start = softnet_seq_start,
++ .next = softnet_seq_next,
++ .stop = softnet_seq_stop,
++ .show = softnet_seq_show,
++};
++
++static int softnet_seq_open(struct inode *inode, struct file *file)
++{
++ return seq_open(file, &softnet_seq_ops);
++}
++
++static struct file_operations softnet_seq_fops = {
++ .owner = THIS_MODULE,
++ .open = softnet_seq_open,
++ .read = seq_read,
++ .llseek = seq_lseek,
++ .release = seq_release,
++};
++
++#ifdef WIRELESS_EXT
++extern int wireless_proc_init(void);
++#else
++#define wireless_proc_init() 0
++#endif
++
++static int __init dev_proc_init(void)
++{
++ int rc = -ENOMEM;
++
++ if (!proc_net_fops_create("dev", S_IRUGO, &dev_seq_fops))
++ goto out;
++ if (!proc_net_fops_create("softnet_stat", S_IRUGO, &softnet_seq_fops))
++ goto out_dev;
++ if (wireless_proc_init())
++ goto out_softnet;
++ rc = 0;
++out:
++ return rc;
++out_softnet:
++ proc_net_remove("softnet_stat");
++out_dev:
++ proc_net_remove("dev");
++ goto out;
++}
++#else
++#define dev_proc_init() 0
++#endif /* CONFIG_PROC_FS */
++
++
++/**
++ * netdev_set_master - set up master/slave pair
++ * @slave: slave device
++ * @master: new master device
++ *
++ * Changes the master device of the slave. Pass %NULL to break the
++ * bonding. The caller must hold the RTNL semaphore. On a failure
++ * a negative errno code is returned. On success the reference counts
++ * are adjusted, %RTM_NEWLINK is sent to the routing socket and the
++ * function returns zero.
++ */
++int netdev_set_master(struct net_device *slave, struct net_device *master)
++{
++ struct net_device *old = slave->master;
++
++ ASSERT_RTNL();
++
++ if (master) {
++ if (old)
++ return -EBUSY;
++ dev_hold(master);
++ }
++
++ slave->master = master;
++
++ synchronize_net();
++
++ if (old)
++ dev_put(old);
++
++ if (master)
++ slave->flags |= IFF_SLAVE;
++ else
++ slave->flags &= ~IFF_SLAVE;
++
++ rtmsg_ifinfo(RTM_NEWLINK, slave, IFF_SLAVE);
++ return 0;
++}
++
++/**
++ * dev_set_promiscuity - update promiscuity count on a device
++ * @dev: device
++ * @inc: modifier
++ *
++ * Add or remove promsicuity from a device. While the count in the device
++ * remains above zero the interface remains promiscuous. Once it hits zero
++ * the device reverts back to normal filtering operation. A negative inc
++ * value is used to drop promiscuity on the device.
++ */
++void dev_set_promiscuity(struct net_device *dev, int inc)
++{
++ unsigned short old_flags = dev->flags;
++
++ dev->flags |= IFF_PROMISC;
++ if ((dev->promiscuity += inc) == 0)
++ dev->flags &= ~IFF_PROMISC;
++ if (dev->flags ^ old_flags) {
++ dev_mc_upload(dev);
++ printk(KERN_INFO "device %s %s promiscuous mode\n",
++ dev->name, (dev->flags & IFF_PROMISC) ? "entered" :
++ "left");
++ }
++}
++
++/**
++ * dev_set_allmulti - update allmulti count on a device
++ * @dev: device
++ * @inc: modifier
++ *
++ * Add or remove reception of all multicast frames to a device. While the
++ * count in the device remains above zero the interface remains listening
++ * to all interfaces. Once it hits zero the device reverts back to normal
++ * filtering operation. A negative @inc value is used to drop the counter
++ * when releasing a resource needing all multicasts.
++ */
++
++void dev_set_allmulti(struct net_device *dev, int inc)
++{
++ unsigned short old_flags = dev->flags;
++
++ dev->flags |= IFF_ALLMULTI;
++ if ((dev->allmulti += inc) == 0)
++ dev->flags &= ~IFF_ALLMULTI;
++ if (dev->flags ^ old_flags)
++ dev_mc_upload(dev);
++}
++
++unsigned dev_get_flags(const struct net_device *dev)
++{
++ unsigned flags;
++
++ flags = (dev->flags & ~(IFF_PROMISC |
++ IFF_ALLMULTI |
++ IFF_RUNNING)) |
++ (dev->gflags & (IFF_PROMISC |
++ IFF_ALLMULTI));
++
++ if (netif_running(dev) && netif_carrier_ok(dev))
++ flags |= IFF_RUNNING;
++
++ return flags;
++}
++
++int dev_change_flags(struct net_device *dev, unsigned flags)
++{
++ int ret;
++ int old_flags = dev->flags;
++
++ /*
++ * Set the flags on our device.
++ */
++
++ dev->flags = (flags & (IFF_DEBUG | IFF_NOTRAILERS | IFF_NOARP |
++ IFF_DYNAMIC | IFF_MULTICAST | IFF_PORTSEL |
++ IFF_AUTOMEDIA)) |
++ (dev->flags & (IFF_UP | IFF_VOLATILE | IFF_PROMISC |
++ IFF_ALLMULTI));
++
++ /*
++ * Load in the correct multicast list now the flags have changed.
++ */
++
++ dev_mc_upload(dev);
++
++ /*
++ * Have we downed the interface. We handle IFF_UP ourselves
++ * according to user attempts to set it, rather than blindly
++ * setting it.
++ */
++
++ ret = 0;
++ if ((old_flags ^ flags) & IFF_UP) { /* Bit is different ? */
++ ret = ((old_flags & IFF_UP) ? dev_close : dev_open)(dev);
++
++ if (!ret)
++ dev_mc_upload(dev);
++ }
++
++ if (dev->flags & IFF_UP &&
++ ((old_flags ^ dev->flags) &~ (IFF_UP | IFF_PROMISC | IFF_ALLMULTI |
++ IFF_VOLATILE)))
++ notifier_call_chain(&netdev_chain, NETDEV_CHANGE, dev);
++
++ if ((flags ^ dev->gflags) & IFF_PROMISC) {
++ int inc = (flags & IFF_PROMISC) ? +1 : -1;
++ dev->gflags ^= IFF_PROMISC;
++ dev_set_promiscuity(dev, inc);
++ }
++
++ /* NOTE: order of synchronization of IFF_PROMISC and IFF_ALLMULTI
++ is important. Some (broken) drivers set IFF_PROMISC, when
++ IFF_ALLMULTI is requested not asking us and not reporting.
++ */
++ if ((flags ^ dev->gflags) & IFF_ALLMULTI) {
++ int inc = (flags & IFF_ALLMULTI) ? +1 : -1;
++ dev->gflags ^= IFF_ALLMULTI;
++ dev_set_allmulti(dev, inc);
++ }
++
++ if (old_flags ^ dev->flags)
++ rtmsg_ifinfo(RTM_NEWLINK, dev, old_flags ^ dev->flags);
++
++ return ret;
++}
++
++int dev_set_mtu(struct net_device *dev, int new_mtu)
++{
++ int err;
++
++ if (new_mtu == dev->mtu)
++ return 0;
++
++ /* MTU must be positive. */
++ if (new_mtu < 0)
++ return -EINVAL;
++
++ if (!netif_device_present(dev))
++ return -ENODEV;
++
++ err = 0;
++ if (dev->change_mtu)
++ err = dev->change_mtu(dev, new_mtu);
++ else
++ dev->mtu = new_mtu;
++ if (!err && dev->flags & IFF_UP)
++ notifier_call_chain(&netdev_chain,
++ NETDEV_CHANGEMTU, dev);
++ return err;
++}
++
++int dev_set_mac_address(struct net_device *dev, struct sockaddr *sa)
++{
++ int err;
++
++ if (!dev->set_mac_address)
++ return -EOPNOTSUPP;
++ if (sa->sa_family != dev->type)
++ return -EINVAL;
++ if (!netif_device_present(dev))
++ return -ENODEV;
++ err = dev->set_mac_address(dev, sa);
++ if (!err)
++ notifier_call_chain(&netdev_chain, NETDEV_CHANGEADDR, dev);
++ return err;
++}
++
++/*
++ * Perform the SIOCxIFxxx calls.
++ */
++static int dev_ifsioc(struct ifreq *ifr, unsigned int cmd)
++{
++ int err;
++ struct net_device *dev = __dev_get_by_name(ifr->ifr_name);
++
++ if (!dev)
++ return -ENODEV;
++
++ switch (cmd) {
++ case SIOCGIFFLAGS: /* Get interface flags */
++ ifr->ifr_flags = dev_get_flags(dev);
++ return 0;
++
++ case SIOCSIFFLAGS: /* Set interface flags */
++ return dev_change_flags(dev, ifr->ifr_flags);
++
++ case SIOCGIFMETRIC: /* Get the metric on the interface
++ (currently unused) */
++ ifr->ifr_metric = 0;
++ return 0;
++
++ case SIOCSIFMETRIC: /* Set the metric on the interface
++ (currently unused) */
++ return -EOPNOTSUPP;
++
++ case SIOCGIFMTU: /* Get the MTU of a device */
++ ifr->ifr_mtu = dev->mtu;
++ return 0;
++
++ case SIOCSIFMTU: /* Set the MTU of a device */
++ return dev_set_mtu(dev, ifr->ifr_mtu);
++
++ case SIOCGIFHWADDR:
++ if (!dev->addr_len)
++ memset(ifr->ifr_hwaddr.sa_data, 0, sizeof ifr->ifr_hwaddr.sa_data);
++ else
++ memcpy(ifr->ifr_hwaddr.sa_data, dev->dev_addr,
++ min(sizeof ifr->ifr_hwaddr.sa_data, (size_t) dev->addr_len));
++ ifr->ifr_hwaddr.sa_family = dev->type;
++ return 0;
++
++ case SIOCSIFHWADDR:
++ return dev_set_mac_address(dev, &ifr->ifr_hwaddr);
++
++ case SIOCSIFHWBROADCAST:
++ if (ifr->ifr_hwaddr.sa_family != dev->type)
++ return -EINVAL;
++ memcpy(dev->broadcast, ifr->ifr_hwaddr.sa_data,
++ min(sizeof ifr->ifr_hwaddr.sa_data, (size_t) dev->addr_len));
++ notifier_call_chain(&netdev_chain,
++ NETDEV_CHANGEADDR, dev);
++ return 0;
++
++ case SIOCGIFMAP:
++ ifr->ifr_map.mem_start = dev->mem_start;
++ ifr->ifr_map.mem_end = dev->mem_end;
++ ifr->ifr_map.base_addr = dev->base_addr;
++ ifr->ifr_map.irq = dev->irq;
++ ifr->ifr_map.dma = dev->dma;
++ ifr->ifr_map.port = dev->if_port;
++ return 0;
++
++ case SIOCSIFMAP:
++ if (dev->set_config) {
++ if (!netif_device_present(dev))
++ return -ENODEV;
++ return dev->set_config(dev, &ifr->ifr_map);
++ }
++ return -EOPNOTSUPP;
++
++ case SIOCADDMULTI:
++ if (!dev->set_multicast_list ||
++ ifr->ifr_hwaddr.sa_family != AF_UNSPEC)
++ return -EINVAL;
++ if (!netif_device_present(dev))
++ return -ENODEV;
++ return dev_mc_add(dev, ifr->ifr_hwaddr.sa_data,
++ dev->addr_len, 1);
++
++ case SIOCDELMULTI:
++ if (!dev->set_multicast_list ||
++ ifr->ifr_hwaddr.sa_family != AF_UNSPEC)
++ return -EINVAL;
++ if (!netif_device_present(dev))
++ return -ENODEV;
++ return dev_mc_delete(dev, ifr->ifr_hwaddr.sa_data,
++ dev->addr_len, 1);
++
++ case SIOCGIFINDEX:
++ ifr->ifr_ifindex = dev->ifindex;
++ return 0;
++
++ case SIOCGIFTXQLEN:
++ ifr->ifr_qlen = dev->tx_queue_len;
++ return 0;
++
++ case SIOCSIFTXQLEN:
++ if (ifr->ifr_qlen < 0)
++ return -EINVAL;
++ dev->tx_queue_len = ifr->ifr_qlen;
++ return 0;
++
++ case SIOCSIFNAME:
++ ifr->ifr_newname[IFNAMSIZ-1] = '\0';
++ return dev_change_name(dev, ifr->ifr_newname);
++
++ /*
++ * Unknown or private ioctl
++ */
++
++ default:
++ if ((cmd >= SIOCDEVPRIVATE &&
++ cmd <= SIOCDEVPRIVATE + 15) ||
++ cmd == SIOCBONDENSLAVE ||
++ cmd == SIOCBONDRELEASE ||
++ cmd == SIOCBONDSETHWADDR ||
++ cmd == SIOCBONDSLAVEINFOQUERY ||
++ cmd == SIOCBONDINFOQUERY ||
++ cmd == SIOCBONDCHANGEACTIVE ||
++ cmd == SIOCGMIIPHY ||
++ cmd == SIOCGMIIREG ||
++ cmd == SIOCSMIIREG ||
++ cmd == SIOCBRADDIF ||
++ cmd == SIOCBRDELIF ||
++ cmd == SIOCWANDEV) {
++ err = -EOPNOTSUPP;
++ if (dev->do_ioctl) {
++ if (netif_device_present(dev))
++ err = dev->do_ioctl(dev, ifr,
++ cmd);
++ else
++ err = -ENODEV;
++ }
++ } else
++ err = -EINVAL;
++
++ }
++ return err;
++}
++
++/*
++ * This function handles all "interface"-type I/O control requests. The actual
++ * 'doing' part of this is dev_ifsioc above.
++ */
++
++/**
++ * dev_ioctl - network device ioctl
++ * @cmd: command to issue
++ * @arg: pointer to a struct ifreq in user space
++ *
++ * Issue ioctl functions to devices. This is normally called by the
++ * user space syscall interfaces but can sometimes be useful for
++ * other purposes. The return value is the return from the syscall if
++ * positive or a negative errno code on error.
++ */
++
++int dev_ioctl(unsigned int cmd, void __user *arg)
++{
++ struct ifreq ifr;
++ int ret;
++ char *colon;
++
++ /* One special case: SIOCGIFCONF takes ifconf argument
++ and requires shared lock, because it sleeps writing
++ to user space.
++ */
++
++ if (cmd == SIOCGIFCONF) {
++ rtnl_shlock();
++ ret = dev_ifconf((char __user *) arg);
++ rtnl_shunlock();
++ return ret;
++ }
++ if (cmd == SIOCGIFNAME)
++ return dev_ifname((struct ifreq __user *)arg);
++
++ if (copy_from_user(&ifr, arg, sizeof(struct ifreq)))
++ return -EFAULT;
++
++ ifr.ifr_name[IFNAMSIZ-1] = 0;
++
++ colon = strchr(ifr.ifr_name, ':');
++ if (colon)
++ *colon = 0;
++
++ /*
++ * See which interface the caller is talking about.
++ */
++
++ switch (cmd) {
++ /*
++ * These ioctl calls:
++ * - can be done by all.
++ * - atomic and do not require locking.
++ * - return a value
++ */
++ case SIOCGIFFLAGS:
++ case SIOCGIFMETRIC:
++ case SIOCGIFMTU:
++ case SIOCGIFHWADDR:
++ case SIOCGIFSLAVE:
++ case SIOCGIFMAP:
++ case SIOCGIFINDEX:
++ case SIOCGIFTXQLEN:
++ dev_load(ifr.ifr_name);
++ read_lock(&dev_base_lock);
++ ret = dev_ifsioc(&ifr, cmd);
++ read_unlock(&dev_base_lock);
++ if (!ret) {
++ if (colon)
++ *colon = ':';
++ if (copy_to_user(arg, &ifr,
++ sizeof(struct ifreq)))
++ ret = -EFAULT;
++ }
++ return ret;
++
++ case SIOCETHTOOL:
++ dev_load(ifr.ifr_name);
++ rtnl_lock();
++ ret = dev_ethtool(&ifr);
++ rtnl_unlock();
++ if (!ret) {
++ if (colon)
++ *colon = ':';
++ if (copy_to_user(arg, &ifr,
++ sizeof(struct ifreq)))
++ ret = -EFAULT;
++ }
++ return ret;
++
++ /*
++ * These ioctl calls:
++ * - require superuser power.
++ * - require strict serialization.
++ * - return a value
++ */
++ case SIOCGMIIPHY:
++ case SIOCGMIIREG:
++ case SIOCSIFNAME:
++ if (!capable(CAP_NET_ADMIN))
++ return -EPERM;
++ dev_load(ifr.ifr_name);
++ rtnl_lock();
++ ret = dev_ifsioc(&ifr, cmd);
++ rtnl_unlock();
++ if (!ret) {
++ if (colon)
++ *colon = ':';
++ if (copy_to_user(arg, &ifr,
++ sizeof(struct ifreq)))
++ ret = -EFAULT;
++ }
++ return ret;
++
++ /*
++ * These ioctl calls:
++ * - require superuser power.
++ * - require strict serialization.
++ * - do not return a value
++ */
++ case SIOCSIFFLAGS:
++ case SIOCSIFMETRIC:
++ case SIOCSIFMTU:
++ case SIOCSIFMAP:
++ case SIOCSIFHWADDR:
++ case SIOCSIFSLAVE:
++ case SIOCADDMULTI:
++ case SIOCDELMULTI:
++ case SIOCSIFHWBROADCAST:
++ case SIOCSIFTXQLEN:
++ case SIOCSMIIREG:
++ case SIOCBONDENSLAVE:
++ case SIOCBONDRELEASE:
++ case SIOCBONDSETHWADDR:
++ case SIOCBONDSLAVEINFOQUERY:
++ case SIOCBONDINFOQUERY:
++ case SIOCBONDCHANGEACTIVE:
++ case SIOCBRADDIF:
++ case SIOCBRDELIF:
++ if (!capable(CAP_NET_ADMIN))
++ return -EPERM;
++ dev_load(ifr.ifr_name);
++ rtnl_lock();
++ ret = dev_ifsioc(&ifr, cmd);
++ rtnl_unlock();
++ return ret;
++
++ case SIOCGIFMEM:
++ /* Get the per device memory space. We can add this but
++ * currently do not support it */
++ case SIOCSIFMEM:
++ /* Set the per device memory buffer space.
++ * Not applicable in our case */
++ case SIOCSIFLINK:
++ return -EINVAL;
++
++ /*
++ * Unknown or private ioctl.
++ */
++ default:
++ if (cmd == SIOCWANDEV ||
++ (cmd >= SIOCDEVPRIVATE &&
++ cmd <= SIOCDEVPRIVATE + 15)) {
++ dev_load(ifr.ifr_name);
++ rtnl_lock();
++ ret = dev_ifsioc(&ifr, cmd);
++ rtnl_unlock();
++ if (!ret && copy_to_user(arg, &ifr,
++ sizeof(struct ifreq)))
++ ret = -EFAULT;
++ return ret;
++ }
++#ifdef WIRELESS_EXT
++ /* Take care of Wireless Extensions */
++ if (cmd >= SIOCIWFIRST && cmd <= SIOCIWLAST) {
++ /* If command is `set a parameter', or
++ * `get the encoding parameters', check if
++ * the user has the right to do it */
++ if (IW_IS_SET(cmd) || cmd == SIOCGIWENCODE) {
++ if (!capable(CAP_NET_ADMIN))
++ return -EPERM;
++ }
++ dev_load(ifr.ifr_name);
++ rtnl_lock();
++ /* Follow me in net/core/wireless.c */
++ ret = wireless_process_ioctl(&ifr, cmd);
++ rtnl_unlock();
++ if (IW_IS_GET(cmd) &&
++ copy_to_user(arg, &ifr,
++ sizeof(struct ifreq)))
++ ret = -EFAULT;
++ return ret;
++ }
++#endif /* WIRELESS_EXT */
++ return -EINVAL;
++ }
++}
++
++
++/**
++ * dev_new_index - allocate an ifindex
++ *
++ * Returns a suitable unique value for a new device interface
++ * number. The caller must hold the rtnl semaphore or the
++ * dev_base_lock to be sure it remains unique.
++ */
++static int dev_new_index(void)
++{
++ static int ifindex;
++ for (;;) {
++ if (++ifindex <= 0)
++ ifindex = 1;
++ if (!__dev_get_by_index(ifindex))
++ return ifindex;
++ }
++}
++
++static int dev_boot_phase = 1;
++
++/* Delayed registration/unregisteration */
++static DEFINE_SPINLOCK(net_todo_list_lock);
++static struct list_head net_todo_list = LIST_HEAD_INIT(net_todo_list);
++
++static inline void net_set_todo(struct net_device *dev)
++{
++ spin_lock(&net_todo_list_lock);
++ list_add_tail(&dev->todo_list, &net_todo_list);
++ spin_unlock(&net_todo_list_lock);
++}
++
++/**
++ * register_netdevice - register a network device
++ * @dev: device to register
++ *
++ * Take a completed network device structure and add it to the kernel
++ * interfaces. A %NETDEV_REGISTER message is sent to the netdev notifier
++ * chain. 0 is returned on success. A negative errno code is returned
++ * on a failure to set up the device, or if the name is a duplicate.
++ *
++ * Callers must hold the rtnl semaphore. You may want
++ * register_netdev() instead of this.
++ *
++ * BUGS:
++ * The locking appears insufficient to guarantee two parallel registers
++ * will not get the same name.
++ */
++
++int register_netdevice(struct net_device *dev)
++{
++ struct hlist_head *head;
++ struct hlist_node *p;
++ int ret;
++
++ BUG_ON(dev_boot_phase);
++ ASSERT_RTNL();
++
++ /* When net_device's are persistent, this will be fatal. */
++ BUG_ON(dev->reg_state != NETREG_UNINITIALIZED);
++
++ spin_lock_init(&dev->queue_lock);
++ spin_lock_init(&dev->xmit_lock);
++ dev->xmit_lock_owner = -1;
++#ifdef CONFIG_NET_CLS_ACT
++ spin_lock_init(&dev->ingress_lock);
++#endif
++
++ ret = alloc_divert_blk(dev);
++ if (ret)
++ goto out;
++
++ dev->iflink = -1;
++
++ /* Init, if this function is available */
++ if (dev->init) {
++ ret = dev->init(dev);
++ if (ret) {
++ if (ret > 0)
++ ret = -EIO;
++ goto out_err;
++ }
++ }
++
++ if (!dev_valid_name(dev->name)) {
++ ret = -EINVAL;
++ goto out_err;
++ }
++
++ dev->ifindex = dev_new_index();
++ if (dev->iflink == -1)
++ dev->iflink = dev->ifindex;
++
++ /* Check for existence of name */
++ head = dev_name_hash(dev->name);
++ hlist_for_each(p, head) {
++ struct net_device *d
++ = hlist_entry(p, struct net_device, name_hlist);
++ if (!strncmp(d->name, dev->name, IFNAMSIZ)) {
++ ret = -EEXIST;
++ goto out_err;
++ }
++ }
++
++ /* Fix illegal SG+CSUM combinations. */
++ if ((dev->features & NETIF_F_SG) &&
++ !(dev->features & (NETIF_F_IP_CSUM |
++ NETIF_F_NO_CSUM |
++ NETIF_F_HW_CSUM))) {
++ printk("%s: Dropping NETIF_F_SG since no checksum feature.\n",
++ dev->name);
++ dev->features &= ~NETIF_F_SG;
++ }
++
++ /* TSO requires that SG is present as well. */
++ if ((dev->features & NETIF_F_TSO) &&
++ !(dev->features & NETIF_F_SG)) {
++ printk("%s: Dropping NETIF_F_TSO since no SG feature.\n",
++ dev->name);
++ dev->features &= ~NETIF_F_TSO;
++ }
++
++ /*
++ * nil rebuild_header routine,
++ * that should be never called and used as just bug trap.
++ */
++
++ if (!dev->rebuild_header)
++ dev->rebuild_header = default_rebuild_header;
++
++ /*
++ * Default initial state at registry is that the
++ * device is present.
++ */
++
++ set_bit(__LINK_STATE_PRESENT, &dev->state);
++
++ dev->next = NULL;
++ dev_init_scheduler(dev);
++ write_lock_bh(&dev_base_lock);
++ *dev_tail = dev;
++ dev_tail = &dev->next;
++ hlist_add_head(&dev->name_hlist, head);
++ hlist_add_head(&dev->index_hlist, dev_index_hash(dev->ifindex));
++ dev_hold(dev);
++ dev->reg_state = NETREG_REGISTERING;
++ write_unlock_bh(&dev_base_lock);
++
++ /* Notify protocols, that a new device appeared. */
++ notifier_call_chain(&netdev_chain, NETDEV_REGISTER, dev);
++
++ /* Finish registration after unlock */
++ net_set_todo(dev);
++ ret = 0;
++
++out:
++ return ret;
++out_err:
++ free_divert_blk(dev);
++ goto out;
++}
++
++/**
++ * register_netdev - register a network device
++ * @dev: device to register
++ *
++ * Take a completed network device structure and add it to the kernel
++ * interfaces. A %NETDEV_REGISTER message is sent to the netdev notifier
++ * chain. 0 is returned on success. A negative errno code is returned
++ * on a failure to set up the device, or if the name is a duplicate.
++ *
++ * This is a wrapper around register_netdev that takes the rtnl semaphore
++ * and expands the device name if you passed a format string to
++ * alloc_netdev.
++ */
++int register_netdev(struct net_device *dev)
++{
++ int err;
++
++ rtnl_lock();
++
++ /*
++ * If the name is a format string the caller wants us to do a
++ * name allocation.
++ */
++ if (strchr(dev->name, '%')) {
++ err = dev_alloc_name(dev, dev->name);
++ if (err < 0)
++ goto out;
++ }
++
++ /*
++ * Back compatibility hook. Kill this one in 2.5
++ */
++ if (dev->name[0] == 0 || dev->name[0] == ' ') {
++ err = dev_alloc_name(dev, "eth%d");
++ if (err < 0)
++ goto out;
++ }
++
++ err = register_netdevice(dev);
++out:
++ rtnl_unlock();
++ return err;
++}
++EXPORT_SYMBOL(register_netdev);
++
++/*
++ * netdev_wait_allrefs - wait until all references are gone.
++ *
++ * This is called when unregistering network devices.
++ *
++ * Any protocol or device that holds a reference should register
++ * for netdevice notification, and cleanup and put back the
++ * reference if they receive an UNREGISTER event.
++ * We can get stuck here if buggy protocols don't correctly
++ * call dev_put.
++ */
++static void netdev_wait_allrefs(struct net_device *dev)
++{
++ unsigned long rebroadcast_time, warning_time;
++
++ rebroadcast_time = warning_time = jiffies;
++ while (atomic_read(&dev->refcnt) != 0) {
++ if (time_after(jiffies, rebroadcast_time + 1 * HZ)) {
++ rtnl_shlock();
++
++ /* Rebroadcast unregister notification */
++ notifier_call_chain(&netdev_chain,
++ NETDEV_UNREGISTER, dev);
++
++ if (test_bit(__LINK_STATE_LINKWATCH_PENDING,
++ &dev->state)) {
++ /* We must not have linkwatch events
++ * pending on unregister. If this
++ * happens, we simply run the queue
++ * unscheduled, resulting in a noop
++ * for this device.
++ */
++ linkwatch_run_queue();
++ }
++
++ rtnl_shunlock();
++
++ rebroadcast_time = jiffies;
++ }
++
++ msleep(250);
++
++ if (time_after(jiffies, warning_time + 10 * HZ)) {
++ printk(KERN_EMERG "unregister_netdevice: "
++ "waiting for %s to become free. Usage "
++ "count = %d\n",
++ dev->name, atomic_read(&dev->refcnt));
++ warning_time = jiffies;
++ }
++ }
++}
++
++/* The sequence is:
++ *
++ * rtnl_lock();
++ * ...
++ * register_netdevice(x1);
++ * register_netdevice(x2);
++ * ...
++ * unregister_netdevice(y1);
++ * unregister_netdevice(y2);
++ * ...
++ * rtnl_unlock();
++ * free_netdev(y1);
++ * free_netdev(y2);
++ *
++ * We are invoked by rtnl_unlock() after it drops the semaphore.
++ * This allows us to deal with problems:
++ * 1) We can create/delete sysfs objects which invoke hotplug
++ * without deadlocking with linkwatch via keventd.
++ * 2) Since we run with the RTNL semaphore not held, we can sleep
++ * safely in order to wait for the netdev refcnt to drop to zero.
++ */
++static DECLARE_MUTEX(net_todo_run_mutex);
++void netdev_run_todo(void)
++{
++ struct list_head list = LIST_HEAD_INIT(list);
++ int err;
++
++
++ /* Need to guard against multiple cpu's getting out of order. */
++ down(&net_todo_run_mutex);
++
++ /* Not safe to do outside the semaphore. We must not return
++ * until all unregister events invoked by the local processor
++ * have been completed (either by this todo run, or one on
++ * another cpu).
++ */
++ if (list_empty(&net_todo_list))
++ goto out;
++
++ /* Snapshot list, allow later requests */
++ spin_lock(&net_todo_list_lock);
++ list_splice_init(&net_todo_list, &list);
++ spin_unlock(&net_todo_list_lock);
++
++ while (!list_empty(&list)) {
++ struct net_device *dev
++ = list_entry(list.next, struct net_device, todo_list);
++ list_del(&dev->todo_list);
++
++ switch(dev->reg_state) {
++ case NETREG_REGISTERING:
++ err = netdev_register_sysfs(dev);
++ if (err)
++ printk(KERN_ERR "%s: failed sysfs registration (%d)\n",
++ dev->name, err);
++ dev->reg_state = NETREG_REGISTERED;
++ break;
++
++ case NETREG_UNREGISTERING:
++ netdev_unregister_sysfs(dev);
++ dev->reg_state = NETREG_UNREGISTERED;
++
++ netdev_wait_allrefs(dev);
++
++ /* paranoia */
++ BUG_ON(atomic_read(&dev->refcnt));
++ BUG_TRAP(!dev->ip_ptr);
++ BUG_TRAP(!dev->ip6_ptr);
++ BUG_TRAP(!dev->dn_ptr);
++
++
++ /* It must be the very last action,
++ * after this 'dev' may point to freed up memory.
++ */
++ if (dev->destructor)
++ dev->destructor(dev);
++ break;
++
++ default:
++ printk(KERN_ERR "network todo '%s' but state %d\n",
++ dev->name, dev->reg_state);
++ break;
++ }
++ }
++
++out:
++ up(&net_todo_run_mutex);
++}
++
++/**
++ * alloc_netdev - allocate network device
++ * @sizeof_priv: size of private data to allocate space for
++ * @name: device name format string
++ * @setup: callback to initialize device
++ *
++ * Allocates a struct net_device with private data area for driver use
++ * and performs basic initialization.
++ */
++struct net_device *alloc_netdev(int sizeof_priv, const char *name,
++ void (*setup)(struct net_device *))
++{
++ void *p;
++ struct net_device *dev;
++ int alloc_size;
++
++ /* ensure 32-byte alignment of both the device and private area */
++ alloc_size = (sizeof(*dev) + NETDEV_ALIGN_CONST) & ~NETDEV_ALIGN_CONST;
++ alloc_size += sizeof_priv + NETDEV_ALIGN_CONST;
++
++ p = kmalloc(alloc_size, GFP_KERNEL);
++ if (!p) {
++ printk(KERN_ERR "alloc_dev: Unable to allocate device.\n");
++ return NULL;
++ }
++ memset(p, 0, alloc_size);
++
++ dev = (struct net_device *)
++ (((long)p + NETDEV_ALIGN_CONST) & ~NETDEV_ALIGN_CONST);
++ dev->padded = (char *)dev - (char *)p;
++
++ if (sizeof_priv)
++ dev->priv = netdev_priv(dev);
++
++ setup(dev);
++ strcpy(dev->name, name);
++ return dev;
++}
++EXPORT_SYMBOL(alloc_netdev);
++
++/**
++ * free_netdev - free network device
++ * @dev: device
++ *
++ * This function does the last stage of destroying an allocated device
++ * interface. The reference to the device object is released.
++ * If this is the last reference then it will be freed.
++ */
++void free_netdev(struct net_device *dev)
++{
++#ifdef CONFIG_SYSFS
++ /* Compatiablity with error handling in drivers */
++ if (dev->reg_state == NETREG_UNINITIALIZED) {
++ kfree((char *)dev - dev->padded);
++ return;
++ }
++
++ BUG_ON(dev->reg_state != NETREG_UNREGISTERED);
++ dev->reg_state = NETREG_RELEASED;
++
++ /* will free via class release */
++ class_device_put(&dev->class_dev);
++#else
++ kfree((char *)dev - dev->padded);
++#endif
++}
++
++/* Synchronize with packet receive processing. */
++void synchronize_net(void)
++{
++ might_sleep();
++ synchronize_rcu();
++}
++
++/**
++ * unregister_netdevice - remove device from the kernel
++ * @dev: device
++ *
++ * This function shuts down a device interface and removes it
++ * from the kernel tables. On success 0 is returned, on a failure
++ * a negative errno code is returned.
++ *
++ * Callers must hold the rtnl semaphore. You may want
++ * unregister_netdev() instead of this.
++ */
++
++int unregister_netdevice(struct net_device *dev)
++{
++ struct net_device *d, **dp;
++
++ BUG_ON(dev_boot_phase);
++ ASSERT_RTNL();
++
++ /* Some devices call without registering for initialization unwind. */
++ if (dev->reg_state == NETREG_UNINITIALIZED) {
++ printk(KERN_DEBUG "unregister_netdevice: device %s/%p never "
++ "was registered\n", dev->name, dev);
++ return -ENODEV;
++ }
++
++ BUG_ON(dev->reg_state != NETREG_REGISTERED);
++
++ /* If device is running, close it first. */
++ if (dev->flags & IFF_UP)
++ dev_close(dev);
++
++ /* And unlink it from device chain. */
++ for (dp = &dev_base; (d = *dp) != NULL; dp = &d->next) {
++ if (d == dev) {
++ write_lock_bh(&dev_base_lock);
++ hlist_del(&dev->name_hlist);
++ hlist_del(&dev->index_hlist);
++ if (dev_tail == &dev->next)
++ dev_tail = dp;
++ *dp = d->next;
++ write_unlock_bh(&dev_base_lock);
++ break;
++ }
++ }
++ if (!d) {
++ printk(KERN_ERR "unregister net_device: '%s' not found\n",
++ dev->name);
++ return -ENODEV;
++ }
++
++ dev->reg_state = NETREG_UNREGISTERING;
++
++ synchronize_net();
++
++ /* Shutdown queueing discipline. */
++ dev_shutdown(dev);
++
++
++ /* Notify protocols, that we are about to destroy
++ this device. They should clean all the things.
++ */
++ notifier_call_chain(&netdev_chain, NETDEV_UNREGISTER, dev);
++
++ /*
++ * Flush the multicast chain
++ */
++ dev_mc_discard(dev);
++
++ if (dev->uninit)
++ dev->uninit(dev);
++
++ /* Notifier chain MUST detach us from master device. */
++ BUG_TRAP(!dev->master);
++
++ free_divert_blk(dev);
++
++ /* Finish processing unregister after unlock */
++ net_set_todo(dev);
++
++ synchronize_net();
++
++ dev_put(dev);
++ return 0;
++}
++
++/**
++ * unregister_netdev - remove device from the kernel
++ * @dev: device
++ *
++ * This function shuts down a device interface and removes it
++ * from the kernel tables. On success 0 is returned, on a failure
++ * a negative errno code is returned.
++ *
++ * This is just a wrapper for unregister_netdevice that takes
++ * the rtnl semaphore. In general you want to use this and not
++ * unregister_netdevice.
++ */
++void unregister_netdev(struct net_device *dev)
++{
++ rtnl_lock();
++ unregister_netdevice(dev);
++ rtnl_unlock();
++}
++
++EXPORT_SYMBOL(unregister_netdev);
++
++#ifdef CONFIG_HOTPLUG_CPU
++static int dev_cpu_callback(struct notifier_block *nfb,
++ unsigned long action,
++ void *ocpu)
++{
++ struct sk_buff **list_skb;
++ struct net_device **list_net;
++ struct sk_buff *skb;
++ unsigned int cpu, oldcpu = (unsigned long)ocpu;
++ struct softnet_data *sd, *oldsd;
++
++ if (action != CPU_DEAD)
++ return NOTIFY_OK;
++
++ local_irq_disable();
++ cpu = smp_processor_id();
++ sd = &per_cpu(softnet_data, cpu);
++ oldsd = &per_cpu(softnet_data, oldcpu);
++
++ /* Find end of our completion_queue. */
++ list_skb = &sd->completion_queue;
++ while (*list_skb)
++ list_skb = &(*list_skb)->next;
++ /* Append completion queue from offline CPU. */
++ *list_skb = oldsd->completion_queue;
++ oldsd->completion_queue = NULL;
++
++ /* Find end of our output_queue. */
++ list_net = &sd->output_queue;
++ while (*list_net)
++ list_net = &(*list_net)->next_sched;
++ /* Append output queue from offline CPU. */
++ *list_net = oldsd->output_queue;
++ oldsd->output_queue = NULL;
++
++ raise_softirq_irqoff(NET_TX_SOFTIRQ);
++ local_irq_enable();
++
++ /* Process offline CPU's input_pkt_queue */
++ while ((skb = __skb_dequeue(&oldsd->input_pkt_queue)))
++ netif_rx(skb);
++
++ return NOTIFY_OK;
++}
++#endif /* CONFIG_HOTPLUG_CPU */
++
++
++/*
++ * Initialize the DEV module. At boot time this walks the device list and
++ * unhooks any devices that fail to initialise (normally hardware not
++ * present) and leaves us with a valid list of present and active devices.
++ *
++ */
++
++/*
++ * This is called single threaded during boot, so no need
++ * to take the rtnl semaphore.
++ */
++static int __init net_dev_init(void)
++{
++ int i, rc = -ENOMEM;
++
++ BUG_ON(!dev_boot_phase);
++
++ net_random_init();
++
++ if (dev_proc_init())
++ goto out;
++
++ if (netdev_sysfs_init())
++ goto out;
++
++ INIT_LIST_HEAD(&ptype_all);
++ for (i = 0; i < 16; i++)
++ INIT_LIST_HEAD(&ptype_base[i]);
++
++ for (i = 0; i < ARRAY_SIZE(dev_name_head); i++)
++ INIT_HLIST_HEAD(&dev_name_head[i]);
++
++ for (i = 0; i < ARRAY_SIZE(dev_index_head); i++)
++ INIT_HLIST_HEAD(&dev_index_head[i]);
++
++ /*
++ * Initialise the packet receive queues.
++ */
++
++ for (i = 0; i < NR_CPUS; i++) {
++ struct softnet_data *queue;
++
++ queue = &per_cpu(softnet_data, i);
++ skb_queue_head_init(&queue->input_pkt_queue);
++ queue->throttle = 0;
++ queue->cng_level = 0;
++ queue->avg_blog = 10; /* arbitrary non-zero */
++ queue->completion_queue = NULL;
++ INIT_LIST_HEAD(&queue->poll_list);
++ set_bit(__LINK_STATE_START, &queue->backlog_dev.state);
++ queue->backlog_dev.weight = weight_p;
++ queue->backlog_dev.poll = process_backlog;
++ atomic_set(&queue->backlog_dev.refcnt, 1);
++ }
++
++#ifdef OFFLINE_SAMPLE
++ samp_timer.expires = jiffies + (10 * HZ);
++ add_timer(&samp_timer);
++#endif
++
++ dev_boot_phase = 0;
++
++ open_softirq(NET_TX_SOFTIRQ, net_tx_action, NULL);
++ open_softirq(NET_RX_SOFTIRQ, net_rx_action, NULL);
++
++ hotcpu_notifier(dev_cpu_callback, 0);
++ dst_init();
++ dev_mcast_init();
++ rc = 0;
++out:
++ return rc;
++}
++
++subsys_initcall(net_dev_init);
++
++EXPORT_SYMBOL(__dev_get_by_index);
++EXPORT_SYMBOL(__dev_get_by_name);
++EXPORT_SYMBOL(__dev_remove_pack);
++EXPORT_SYMBOL(__skb_linearize);
++EXPORT_SYMBOL(dev_add_pack);
++EXPORT_SYMBOL(dev_alloc_name);
++EXPORT_SYMBOL(dev_close);
++EXPORT_SYMBOL(dev_get_by_flags);
++EXPORT_SYMBOL(dev_get_by_index);
++EXPORT_SYMBOL(dev_get_by_name);
++EXPORT_SYMBOL(dev_ioctl);
++EXPORT_SYMBOL(dev_open);
++EXPORT_SYMBOL(dev_queue_xmit);
++EXPORT_SYMBOL(dev_remove_pack);
++EXPORT_SYMBOL(dev_set_allmulti);
++EXPORT_SYMBOL(dev_set_promiscuity);
++EXPORT_SYMBOL(dev_change_flags);
++EXPORT_SYMBOL(dev_set_mtu);
++EXPORT_SYMBOL(dev_set_mac_address);
++EXPORT_SYMBOL(free_netdev);
++EXPORT_SYMBOL(netdev_boot_setup_check);
++EXPORT_SYMBOL(netdev_set_master);
++EXPORT_SYMBOL(netdev_state_change);
++EXPORT_SYMBOL(netif_receive_skb);
++EXPORT_SYMBOL(netif_rx);
++EXPORT_SYMBOL(register_gifconf);
++EXPORT_SYMBOL(register_netdevice);
++EXPORT_SYMBOL(register_netdevice_notifier);
++EXPORT_SYMBOL(skb_checksum_help);
++EXPORT_SYMBOL(synchronize_net);
++EXPORT_SYMBOL(unregister_netdevice);
++EXPORT_SYMBOL(unregister_netdevice_notifier);
++EXPORT_SYMBOL(net_enable_timestamp);
++EXPORT_SYMBOL(net_disable_timestamp);
++EXPORT_SYMBOL(dev_get_flags);
++
++#if defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE)
++EXPORT_SYMBOL(br_handle_frame_hook);
++EXPORT_SYMBOL(br_fdb_get_hook);
++EXPORT_SYMBOL(br_fdb_put_hook);
++#endif
++
++#ifdef CONFIG_KMOD
++EXPORT_SYMBOL(dev_load);
++#endif
++
++EXPORT_PER_CPU_SYMBOL(softnet_data);
+diff --unified --recursive --new-file linux-2.6.12.5/net/ring/Kconfig linux-2.6.12.5-1-686-smp-ring3/net/ring/Kconfig
+--- linux-2.6.12.5/net/ring/Kconfig 1970-01-01 01:00:00.000000000 +0100
++++ linux-2.6.12.5-1-686-smp-ring3/net/ring/Kconfig 2005-10-22 23:50:45.539482000 +0200
+@@ -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.12.5/net/ring/Makefile linux-2.6.12.5-1-686-smp-ring3/net/ring/Makefile
+--- linux-2.6.12.5/net/ring/Makefile 1970-01-01 01:00:00.000000000 +0100
++++ linux-2.6.12.5-1-686-smp-ring3/net/ring/Makefile 2005-10-22 23:50:45.051451500 +0200
+@@ -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.12.5/net/ring/ring_packet.c linux-2.6.12.5-1-686-smp-ring3/net/ring/ring_packet.c
+--- linux-2.6.12.5/net/ring/ring_packet.c 1970-01-01 01:00:00.000000000 +0100
++++ linux-2.6.12.5-1-686-smp-ring3/net/ring/ring_packet.c 2005-10-22 23:50:45.159458250 +0200
+@@ -0,0 +1,1592 @@
++/*
++ *
++ * (C) 2004-05 - Luca Deri <deri@ntop.org>
++ *
++ * This code includes patches courtesy of
++ * - Jeff Randall <jrandall@nexvu.com>
++ * - Helmut Manck <helmut.manck@secunet.com>
++ * - Brad Doctor <bdoctor@ps-ax.com>
++ *
++ */
++
++/* FIX: 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 = 0, 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
++
++/*
++ 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;
++ unsigned int bucketSpace;
++ 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);
++ }
++
++ bucketSpace = pfr->slots_info->slot_len
++#ifdef RING_MAGIC
++ - sizeof(u_char)
++#endif
++ - sizeof(u_char) /* flowSlot.slot_state */
++ - sizeof(struct pcap_pkthdr)
++ - 1 /* 10 */ /* safe boundary */;
++
++ bucket = &theSlot->bucket;
++ hdr = (struct pcap_pkthdr*)bucket;
++
++ 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;
++ hdr->caplen = skb->len+displ;
++
++ if(hdr->caplen > bucketSpace)
++ hdr->caplen = bucketSpace;
++
++ 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): [bucketSpace=%d]"
++ "[hdr.caplen=%d][skb->len=%d]"
++ "[pcap_pkthdr=%d][removeIdx=%d]"
++ "[loss=%lu][page=%u][slot=%u]\n",
++ idx-1, bucketSpace, 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;
++
++ 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
++
++ /* [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;
++ }
++
++ if(transparent_mode) rc = 0;
++
++ if((rc != 0) && real_skb)
++ dev_kfree_skb(skb); /* Free the skb */
++
++ 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
++
++ skb.dev = dev, skb.len = len, skb.data = data,
++ skb.data_len = len, skb.stamp.tv_sec = 0; /* Calculate the time */
++
++ 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);
++#endif
++#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))
++ sk = sk_alloc(PF_RING, GFP_ATOMIC, &ring_proto, 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 */
++ + sizeof(u_short) /* flowSlot.slot_len */
++ + 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) {
++#if defined(RING_DEBUG)
++ printk("ERROR: not enough memory\n");
++#endif
++ return(-1);
++ } else {
++#if defined(RING_DEBUG)
++ printk("RING: succesfully allocated %lu KB [tot_mem=%d][order=%ld]\n",
++ PAGE_SIZE >> (10 - pfr->order), tot_mem, pfr->order);
++#endif
++ }
++
++ 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->tot_slots = (tot_mem-sizeof(FlowSlotInfo))/the_slot_len;
++ pfr->slots_info->tot_mem = tot_mem;
++ pfr->slots_info->sample_rate = sample_rate;
++
++#if defined(RING_DEBUG)
++ 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);
++#endif
++
++#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));
++}
++
++/* ************************************* */
++
++#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0))
++
++volatile void* virt_to_kseg(volatile void* address) {
++ pte_t *pte;
++ pud_t *pud;
++ unsigned long addr = (unsigned long)address;
++
++ pud = pud_offset(pgd_offset_k((unsigned long) address),
++ (unsigned long) address);
++
++ /*
++ High-memory support courtesy of
++ Brad Doctor <bdoctor@ps-ax.com>
++ */
++#if defined(CONFIG_X86_PAE) && (!defined(CONFIG_NOHIGHMEM))
++ pte = pte_offset_map(pmd_offset(pud, addr), addr);
++#else
++ pte = pmd_offset_map(pud, addr);
++#endif
++
++ return((volatile void*)pte_page(*pte));
++}
++
++#else /* 2.4 */
++
++/* http://www.scs.ch/~frey/linux/memorymap.html */
++volatile void *virt_to_kseg(volatile void *address)
++{
++ pgd_t *pgd; pmd_t *pmd; pte_t *ptep, pte;
++ unsigned long va, ret = 0UL;
++
++ va=VMALLOC_VMADDR((unsigned long)address);
++
++ /* get the page directory. Use the kernel memory map. */
++ pgd = pgd_offset_k(va);
++
++ /* check whether we found an entry */
++ if (!pgd_none(*pgd))
++ {
++ /* get the page middle directory */
++ pmd = pmd_offset(pgd, va);
++ /* check whether we found an entry */
++ if (!pmd_none(*pmd))
++ {
++ /* get a pointer to the page table entry */
++ ptep = pte_offset(pmd, va);
++ pte = *ptep;
++ /* check for a valid page */
++ if (pte_present(pte))
++ {
++ /* get the address the page is refering to */
++ ret = (unsigned long)page_address(pte_page(pte));
++ /* add the offset within the page to the page address */
++ ret |= (va & (PAGE_SIZE -1));
++ }
++ }
++ }
++ return((volatile void *)ret);
++}
++#endif
++
++/* ************************************* */
++
++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 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