diff --unified --recursive --new-file linux-2.6.21.4/include/linux/ring.h linux-2.6.21.4-1-686-smp-ring3/include/linux/ring.h --- linux-2.6.21.4/include/linux/ring.h 1970-01-01 00:00:00.000000000 +0000 +++ linux-2.6.21.4-1-686-smp-ring3/include/linux/ring.h 2007-06-10 16:43:04.346421348 +0000 @@ -0,0 +1,240 @@ +/* + * Definitions for packet ring + * + * 2004-07 Luca Deri + */ +#ifndef __RING_H +#define __RING_H + +#define INCLUDE_MAC_INFO + +#ifdef INCLUDE_MAC_INFO +#define SKB_DISPLACEMENT 14 /* Include MAC address information */ +#else +#define SKB_DISPLACEMENT 0 /* Do NOT include MAC address information */ +#endif + +#define RING_MAGIC +#define RING_MAGIC_VALUE 0x88 +#define RING_FLOWSLOT_VERSION 6 +#define RING_VERSION "3.4.1" + +#define SO_ADD_TO_CLUSTER 99 +#define SO_REMOVE_FROM_CLUSTER 100 +#define SO_SET_REFLECTOR 101 +#define SO_SET_BLOOM 102 +#define SO_SET_STRING 103 +#define SO_TOGGLE_BLOOM_STATE 104 +#define SO_RESET_BLOOM_FILTERS 105 + +#define BITMASK_SET(n, p) (((char*)p->bits_memory)[n/8] |= (1<<(n % 8))) +#define BITMASK_CLR(n, p) (((char*)p->bits_memory)[n/8] &= ~(1<<(n % 8))) +#define BITMASK_ISSET(n, p) (((char*)p->bits_memory)[n/8] & (1<<(n % 8))) + +/* *********************************** */ + +/* + Aho-Corasick code taken from Snort + under GPL license +*/ +/* + * DEFINES and Typedef's + */ +#define MAX_ALPHABET_SIZE 256 + +/* + FAIL STATE for 1,2,or 4 bytes for state transitions + + Uncomment this define to use 32 bit state values + #define AC32 +*/ + +typedef unsigned short acstate_t; +#define ACSM_FAIL_STATE2 0xffff + +/* + * + */ +typedef +struct _acsm_pattern2 +{ + struct _acsm_pattern2 *next; + + unsigned char *patrn; + unsigned char *casepatrn; + int n; + int nocase; + int offset; + int depth; + void * id; + int iid; + +} ACSM_PATTERN2; + +/* + * transition nodes - either 8 or 12 bytes + */ +typedef +struct trans_node_s { + + acstate_t key; /* The character that got us here - sized to keep structure aligned on 4 bytes */ + /* to better the caching opportunities. A value that crosses the cache line */ + /* forces an expensive reconstruction, typing this as acstate_t stops that. */ + acstate_t next_state; /* */ + struct trans_node_s * next; /* next transition for this state */ + +} trans_node_t; + + +/* + * User specified final storage type for the state transitions + */ +enum { + ACF_FULL, + ACF_SPARSE, + ACF_BANDED, + ACF_SPARSEBANDS, +}; + +/* + * User specified machine types + * + * TRIE : Keyword trie + * NFA : + * DFA : + */ +enum { + FSA_TRIE, + FSA_NFA, + FSA_DFA, +}; + +/* + * Aho-Corasick State Machine Struct - one per group of pattterns + */ +typedef struct { + int acsmMaxStates; + int acsmNumStates; + + ACSM_PATTERN2 * acsmPatterns; + acstate_t * acsmFailState; + ACSM_PATTERN2 ** acsmMatchList; + + /* list of transitions in each state, this is used to build the nfa & dfa */ + /* after construction we convert to sparse or full format matrix and free */ + /* the transition lists */ + trans_node_t ** acsmTransTable; + + acstate_t ** acsmNextState; + int acsmFormat; + int acsmSparseMaxRowNodes; + int acsmSparseMaxZcnt; + + int acsmNumTrans; + int acsmAlphabetSize; + int acsmFSA; + +} ACSM_STRUCT2; + +/* *********************************** */ + +#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) */ + /* packet parsing info */ + u_int16_t eth_type; /* Ethernet type */ + u_int16_t vlan_id; /* VLAN Id or -1 for no vlan */ + u_int8_t l3_proto; /* Layer 3 protocol */ + u_int16_t l3_offset, l4_offset, payload_offset; /* Offsets of L3/L4/payload elements */ + u_int32_t ipv4_src, ipv4_dst; /* IPv4 src/dst IP addresses */ + u_int16_t l4_src_port, l4_dst_port; /* Layer 4 src/dst ports */ +}; +#endif + +/* *********************************** */ + +typedef struct _counter_list { + u_int32_t bit_id; + u_int32_t bit_counter; + struct _counter_list *next; +} bitmask_counter_list; + +typedef struct { + u_int32_t num_bits, order, num_pages; + unsigned long bits_memory; + bitmask_counter_list *clashes; +} bitmask_selector; + +/* *********************************** */ + +enum cluster_type { + cluster_per_flow = 0, + cluster_round_robin +}; + +/* *********************************** */ + +#define RING_MIN_SLOT_SIZE (60+sizeof(struct pcap_pkthdr)) +#define RING_MAX_SLOT_SIZE (1514+sizeof(struct pcap_pkthdr)) + +/* *********************************** */ + +typedef struct flowSlotInfo { + u_int16_t version, sample_rate; + u_int32_t tot_slots, slot_len, data_len, tot_mem; + + u_int64_t tot_pkts, tot_lost; + u_int64_t tot_insert, tot_read; + u_int32_t insert_idx, remove_idx; +} FlowSlotInfo; + +/* *********************************** */ + +typedef struct flowSlot { +#ifdef RING_MAGIC + u_char magic; /* It must alwasy be zero */ +#endif + u_char slot_state; /* 0=empty, 1=full */ + u_char bucket; /* bucket[bucketLen] */ +} FlowSlot; + +/* *********************************** */ + +#ifdef __KERNEL__ + +FlowSlotInfo* getRingPtr(void); +int allocateRing(char *deviceName, u_int numSlots, + u_int bucketLen, u_int sampleRate); +unsigned int pollRing(struct file *fp, struct poll_table_struct * wait); +void deallocateRing(void); + +/* ************************* */ + +typedef int (*handle_ring_skb)(struct sk_buff *skb, + u_char recv_packet, u_char real_skb); +extern handle_ring_skb get_skb_ring_handler(void); +extern void set_skb_ring_handler(handle_ring_skb the_handler); +extern void do_skb_ring_handler(struct sk_buff *skb, + u_char recv_packet, u_char real_skb); + +typedef int (*handle_ring_buffer)(struct net_device *dev, + char *data, int len); +extern handle_ring_buffer get_buffer_ring_handler(void); +extern void set_buffer_ring_handler(handle_ring_buffer the_handler); +extern int do_buffer_ring_handler(struct net_device *dev, + char *data, int len); +#endif /* __KERNEL__ */ + +/* *********************************** */ + +#define PF_RING 27 /* Packet Ring */ +#define SOCK_RING PF_RING + +/* ioctl() */ +#define SIORINGPOLL 0x8888 + +/* *********************************** */ + +#endif /* __RING_H */ diff --unified --recursive --new-file linux-2.6.21.4/net/Kconfig linux-2.6.21.4-1-686-smp-ring3/net/Kconfig --- linux-2.6.21.4/net/Kconfig 2007-06-07 21:27:31.000000000 +0000 +++ linux-2.6.21.4-1-686-smp-ring3/net/Kconfig 2007-06-10 16:43:04.402423771 +0000 @@ -39,6 +39,7 @@ source "net/xfrm/Kconfig" source "net/iucv/Kconfig" +source "net/ring/Kconfig" config INET bool "TCP/IP networking" ---help--- diff --unified --recursive --new-file linux-2.6.21.4/net/Makefile linux-2.6.21.4-1-686-smp-ring3/net/Makefile --- linux-2.6.21.4/net/Makefile 2007-06-07 21:27:31.000000000 +0000 +++ linux-2.6.21.4-1-686-smp-ring3/net/Makefile 2007-06-10 16:43:04.394423425 +0000 @@ -42,6 +42,7 @@ obj-$(CONFIG_DECNET) += decnet/ obj-$(CONFIG_ECONET) += econet/ obj-$(CONFIG_VLAN_8021Q) += 8021q/ +obj-$(CONFIG_RING) += ring/ obj-$(CONFIG_IP_DCCP) += dccp/ obj-$(CONFIG_IP_SCTP) += sctp/ obj-$(CONFIG_IEEE80211) += ieee80211/ diff --unified --recursive --new-file linux-2.6.21.4/net/Makefile.ORG linux-2.6.21.4-1-686-smp-ring3/net/Makefile.ORG --- linux-2.6.21.4/net/Makefile.ORG 1970-01-01 00:00:00.000000000 +0000 +++ linux-2.6.21.4-1-686-smp-ring3/net/Makefile.ORG 2007-06-10 16:43:04.386423079 +0000 @@ -0,0 +1,54 @@ +# +# Makefile for the linux networking. +# +# 2 Sep 2000, Christoph Hellwig +# 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_NETFILTER) += netfilter/ +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_DCCP) += dccp/ +obj-$(CONFIG_IP_SCTP) += sctp/ +obj-$(CONFIG_IEEE80211) += ieee80211/ +obj-$(CONFIG_TIPC) += tipc/ +obj-$(CONFIG_NETLABEL) += netlabel/ +obj-$(CONFIG_IUCV) += iucv/ + +ifeq ($(CONFIG_NET),y) +obj-$(CONFIG_SYSCTL) += sysctl_net.o +endif diff --unified --recursive --new-file linux-2.6.21.4/net/core/dev.c linux-2.6.21.4-1-686-smp-ring3/net/core/dev.c --- linux-2.6.21.4/net/core/dev.c 2007-06-07 21:27:31.000000000 +0000 +++ linux-2.6.21.4-1-686-smp-ring3/net/core/dev.c 2007-06-10 16:43:04.382422906 +0000 @@ -117,6 +117,56 @@ #include #include +#if defined (CONFIG_RING) || defined(CONFIG_RING_MODULE) + +/* #define RING_DEBUG */ + +#include +#include + +static handle_ring_skb ring_handler = NULL; + +handle_ring_skb get_skb_ring_handler() { return(ring_handler); } + +void set_skb_ring_handler(handle_ring_skb the_handler) { + ring_handler = the_handler; +} + +void do_skb_ring_handler(struct sk_buff *skb, + u_char recv_packet, u_char real_skb) { + if(ring_handler) + ring_handler(skb, recv_packet, real_skb); +} + +/* ******************* */ + +static handle_ring_buffer buffer_ring_handler = NULL; + +handle_ring_buffer get_buffer_ring_handler() { return(buffer_ring_handler); } + +void set_buffer_ring_handler(handle_ring_buffer the_handler) { + buffer_ring_handler = the_handler; +} + +int do_buffer_ring_handler(struct net_device *dev, char *data, int len) { + if(buffer_ring_handler) { + buffer_ring_handler(dev, data, len); + return(1); + } else + return(0); +} + +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0)) +EXPORT_SYMBOL(get_skb_ring_handler); +EXPORT_SYMBOL(set_skb_ring_handler); +EXPORT_SYMBOL(do_skb_ring_handler); + +EXPORT_SYMBOL(get_buffer_ring_handler); +EXPORT_SYMBOL(set_buffer_ring_handler); +EXPORT_SYMBOL(do_buffer_ring_handler); +#endif + +#endif /* * The list of packet types we will receive (as opposed to discard) * and the routines to invoke. @@ -1474,6 +1524,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); q = dev->qdisc; @@ -1574,6 +1628,13 @@ unsigned long flags; /* if netpoll wants it, pretend we never saw it */ +#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 (netpoll_rx(skb)) return NET_RX_DROP; @@ -1764,6 +1825,13 @@ struct net_device *orig_dev; int ret = NET_RX_DROP; __be16 type; +#if defined (CONFIG_RING) || defined(CONFIG_RING_MODULE) + if(ring_handler && ring_handler(skb, 1, 1)) { + /* The packet has been copied into a ring */ + return(NET_RX_SUCCESS); + } +#endif /* CONFIG_RING */ + /* if we've gotten here through NAPI, check netpoll */ if (skb->dev->poll && netpoll_rx(skb)) diff --unified --recursive --new-file linux-2.6.21.4/net/core/dev.c.ORG linux-2.6.21.4-1-686-smp-ring3/net/core/dev.c.ORG --- linux-2.6.21.4/net/core/dev.c.ORG 1970-01-01 00:00:00.000000000 +0000 +++ linux-2.6.21.4-1-686-smp-ring3/net/core/dev.c.ORG 2007-06-10 16:43:04.354421694 +0000 @@ -0,0 +1,3571 @@ +/* + * 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, + * Mark Evans, + * + * Additional Authors: + * Florian la Roche + * Alan Cox + * David Hinds + * Alexey Kuznetsov + * Adam Sulmicki + * Pekka Riikonen + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * 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 adversely 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 CONFIG_NET_DMA +static struct dma_client *net_dma_client; +static unsigned int net_dma_count; +static spinlock_t net_dma_event_lock; +#endif + +/* + * The @dev_base list is protected by @dev_base_lock and the rtnl + * 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<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); +} + +/** + * __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; +} + +EXPORT_SYMBOL(dev_getbyhwaddr); + +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. We also disallow any kind of + * whitespace. + */ +int dev_valid_name(const char *name) +{ + if (*name == '\0') + return 0; + if (strlen(name) >= IFNAMSIZ) + return 0; + if (!strcmp(name, ".") || !strcmp(name, "..")) + return 0; + + while (*name) { + if (*name == '/' || isspace(*name)) + return 0; + name++; + } + return 1; +} + +/** + * 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. It scans list of devices to build up a free map, then chooses + * the first empty slot. The caller must hold the dev_base or rtnl lock + * while allocating the name and adding the device in order to avoid + * duplicates. + * Limited to bits_per_byte * page size devices (ie 32K on most platforms). + * 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); + + device_rename(&dev->dev, dev->name); + hlist_del(&dev->name_hlist); + hlist_add_head(&dev->name_hlist, dev_name_hash(dev->name)); + raw_notifier_call_chain(&netdev_chain, NETDEV_CHANGENAME, dev); + + return err; +} + +/** + * netdev_features_change - device changes features + * @dev: device to cause notification + * + * Called to indicate a device has changed features. + */ +void netdev_features_change(struct net_device *dev) +{ + raw_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) { + raw_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. + */ + raw_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. + */ + raw_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. */ + msleep(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 + */ + raw_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 = raw_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) +{ + int err; + + rtnl_lock(); + err = raw_notifier_chain_unregister(&netdev_chain, nb); + rtnl_unlock(); + return err; +} + +/** + * 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 raw_notifier_call_chain(). + */ + +int call_netdevice_notifiers(unsigned long val, void *v) +{ + return raw_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); +} + +void __net_timestamp(struct sk_buff *skb) +{ + struct timeval tv; + + do_gettimeofday(&tv); + skb_set_timestamp(skb, &tv); +} +EXPORT_SYMBOL(__net_timestamp); + +static inline void net_timestamp(struct sk_buff *skb) +{ + if (atomic_read(&netstamp_needed)) + __net_timestamp(skb); + else { + skb->tstamp.off_sec = 0; + skb->tstamp.off_usec = 0; + } +} + +/* + * Support routine. Sends outgoing frames to any network + * taps currently in use. + */ + +static void dev_queue_xmit_nit(struct sk_buff *skb, struct net_device *dev) +{ + struct packet_type *ptype; + + net_timestamp(skb); + + 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, skb->dev); + } + } + rcu_read_unlock(); +} + + +void __netif_schedule(struct net_device *dev) +{ + if (!test_and_set_bit(__LINK_STATE_SCHED, &dev->state)) { + unsigned long flags; + struct softnet_data *sd; + + local_irq_save(flags); + sd = &__get_cpu_var(softnet_data); + dev->next_sched = sd->output_queue; + sd->output_queue = dev; + raise_softirq_irqoff(NET_TX_SOFTIRQ); + local_irq_restore(flags); + } +} +EXPORT_SYMBOL(__netif_schedule); + +void __netif_rx_schedule(struct net_device *dev) +{ + unsigned long flags; + + local_irq_save(flags); + dev_hold(dev); + list_add_tail(&dev->poll_list, &__get_cpu_var(softnet_data).poll_list); + if (dev->quota < 0) + dev->quota += dev->weight; + else + dev->quota = dev->weight; + __raise_softirq_irqoff(NET_RX_SOFTIRQ); + local_irq_restore(flags); +} +EXPORT_SYMBOL(__netif_rx_schedule); + +void dev_kfree_skb_any(struct sk_buff *skb) +{ + if (in_irq() || irqs_disabled()) + dev_kfree_skb_irq(skb); + else + dev_kfree_skb(skb); +} +EXPORT_SYMBOL(dev_kfree_skb_any); + + +/* Hot-plugging. */ +void netif_device_detach(struct net_device *dev) +{ + if (test_and_clear_bit(__LINK_STATE_PRESENT, &dev->state) && + netif_running(dev)) { + netif_stop_queue(dev); + } +} +EXPORT_SYMBOL(netif_device_detach); + +void netif_device_attach(struct net_device *dev) +{ + if (!test_and_set_bit(__LINK_STATE_PRESENT, &dev->state) && + netif_running(dev)) { + netif_wake_queue(dev); + __netdev_watchdog_up(dev); + } +} +EXPORT_SYMBOL(netif_device_attach); + + +/* + * Invalidate hardware checksum when packet is to be mangled, and + * complete checksum manually on outgoing path. + */ +int skb_checksum_help(struct sk_buff *skb) +{ + __wsum csum; + int ret = 0, offset = skb->h.raw - skb->data; + + if (skb->ip_summed == CHECKSUM_COMPLETE) + goto out_set_summed; + + if (unlikely(skb_shinfo(skb)->gso_size)) { + /* Let GSO fix up the checksum. */ + goto out_set_summed; + } + + if (skb_cloned(skb)) { + ret = pskb_expand_head(skb, 0, 0, GFP_ATOMIC); + if (ret) + goto out; + } + + BUG_ON(offset > (int)skb->len); + csum = skb_checksum(skb, offset, skb->len-offset, 0); + + offset = skb->tail - skb->h.raw; + BUG_ON(offset <= 0); + BUG_ON(skb->csum_offset + 2 > offset); + + *(__sum16*)(skb->h.raw + skb->csum_offset) = csum_fold(csum); + +out_set_summed: + skb->ip_summed = CHECKSUM_NONE; +out: + return ret; +} + +/** + * skb_gso_segment - Perform segmentation on skb. + * @skb: buffer to segment + * @features: features for the output path (see dev->features) + * + * This function segments the given skb and returns a list of segments. + * + * It may return NULL if the skb requires no segmentation. This is + * only possible when GSO is used for verifying header integrity. + */ +struct sk_buff *skb_gso_segment(struct sk_buff *skb, int features) +{ + struct sk_buff *segs = ERR_PTR(-EPROTONOSUPPORT); + struct packet_type *ptype; + __be16 type = skb->protocol; + int err; + + BUG_ON(skb_shinfo(skb)->frag_list); + + skb->mac.raw = skb->data; + skb->mac_len = skb->nh.raw - skb->data; + __skb_pull(skb, skb->mac_len); + + if (unlikely(skb->ip_summed != CHECKSUM_PARTIAL)) { + if (skb_header_cloned(skb) && + (err = pskb_expand_head(skb, 0, 0, GFP_ATOMIC))) + return ERR_PTR(err); + } + + rcu_read_lock(); + list_for_each_entry_rcu(ptype, &ptype_base[ntohs(type) & 15], list) { + if (ptype->type == type && !ptype->dev && ptype->gso_segment) { + if (unlikely(skb->ip_summed != CHECKSUM_PARTIAL)) { + err = ptype->gso_send_check(skb); + segs = ERR_PTR(err); + if (err || skb_gso_ok(skb, features)) + break; + __skb_push(skb, skb->data - skb->nh.raw); + } + segs = ptype->gso_segment(skb, features); + break; + } + } + rcu_read_unlock(); + + __skb_push(skb, skb->data - skb->mac.raw); + + return segs; +} + +EXPORT_SYMBOL(skb_gso_segment); + +/* Take action when hardware reception checksum errors are detected. */ +#ifdef CONFIG_BUG +void netdev_rx_csum_fault(struct net_device *dev) +{ + if (net_ratelimit()) { + printk(KERN_ERR "%s: hw csum failure.\n", + dev ? dev->name : ""); + dump_stack(); + } +} +EXPORT_SYMBOL(netdev_rx_csum_fault); +#endif + +/* 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) +{ +#ifdef CONFIG_HIGHMEM + 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; + +#endif + return 0; +} + +struct dev_gso_cb { + void (*destructor)(struct sk_buff *skb); +}; + +#define DEV_GSO_CB(skb) ((struct dev_gso_cb *)(skb)->cb) + +static void dev_gso_skb_destructor(struct sk_buff *skb) +{ + struct dev_gso_cb *cb; + + do { + struct sk_buff *nskb = skb->next; + + skb->next = nskb->next; + nskb->next = NULL; + kfree_skb(nskb); + } while (skb->next); + + cb = DEV_GSO_CB(skb); + if (cb->destructor) + cb->destructor(skb); +} + +/** + * dev_gso_segment - Perform emulated hardware segmentation on skb. + * @skb: buffer to segment + * + * This function segments the given skb and stores the list of segments + * in skb->next. + */ +static int dev_gso_segment(struct sk_buff *skb) +{ + struct net_device *dev = skb->dev; + struct sk_buff *segs; + int features = dev->features & ~(illegal_highdma(dev, skb) ? + NETIF_F_SG : 0); + + segs = skb_gso_segment(skb, features); + + /* Verifying header integrity only. */ + if (!segs) + return 0; + + if (unlikely(IS_ERR(segs))) + return PTR_ERR(segs); + + skb->next = segs; + DEV_GSO_CB(skb)->destructor = skb->destructor; + skb->destructor = dev_gso_skb_destructor; + + return 0; +} + +int dev_hard_start_xmit(struct sk_buff *skb, struct net_device *dev) +{ + if (likely(!skb->next)) { + if (netdev_nit) + dev_queue_xmit_nit(skb, dev); + + if (netif_needs_gso(dev, skb)) { + if (unlikely(dev_gso_segment(skb))) + goto out_kfree_skb; + if (skb->next) + goto gso; + } + + return dev->hard_start_xmit(skb, dev); + } + +gso: + do { + struct sk_buff *nskb = skb->next; + int rc; + + skb->next = nskb->next; + nskb->next = NULL; + rc = dev->hard_start_xmit(nskb, dev); + if (unlikely(rc)) { + nskb->next = skb->next; + skb->next = nskb; + return rc; + } + if (unlikely(netif_queue_stopped(dev) && skb->next)) + return NETDEV_TX_BUSY; + } while (skb->next); + + skb->destructor = DEV_GSO_CB(skb)->destructor; + +out_kfree_skb: + kfree_skb(skb); + return 0; +} + +#define HARD_TX_LOCK(dev, cpu) { \ + if ((dev->features & NETIF_F_LLTX) == 0) { \ + netif_tx_lock(dev); \ + } \ +} + +#define HARD_TX_UNLOCK(dev) { \ + if ((dev->features & NETIF_F_LLTX) == 0) { \ + netif_tx_unlock(dev); \ + } \ +} + +/** + * 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; + + /* GSO will handle the following emulations directly. */ + if (netif_needs_gso(dev, skb)) + goto gso; + + if (skb_shinfo(skb)->frag_list && + !(dev->features & NETIF_F_FRAGLIST) && + __skb_linearize(skb)) + 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)) + 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_PARTIAL && + (!(dev->features & NETIF_F_GEN_CSUM) && + (!(dev->features & NETIF_F_IP_CSUM) || + skb->protocol != htons(ETH_P_IP)))) + if (skb_checksum_help(skb)) + goto out_kfree_skb; + +gso: + spin_lock_prefetch(&dev->queue_lock); + + /* Disable soft irqs for various locks below. Also + * stops preemption for RCU. + */ + rcu_read_lock_bh(); + + /* 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); + q = dev->qdisc; + if (q->enqueue) { + rc = q->enqueue(skb, q); + qdisc_run(dev); + spin_unlock(&dev->queue_lock); + + rc = rc == NET_XMIT_BYPASS ? NET_XMIT_SUCCESS : rc; + goto out; + } + spin_unlock(&dev->queue_lock); + } + + /* The device has no queue. Common case for software devices: + loopback, all the sorts of tunnels... + + Really, it is unlikely that netif_tx_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)) { + 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; + rcu_read_unlock_bh(); + +out_kfree_skb: + kfree_skb(skb); + return rc; +out: + rcu_read_unlock_bh(); + return rc; +} + + +/*======================================================================= + Receiver routines + =======================================================================*/ + +int netdev_max_backlog = 1000; +int netdev_budget = 300; +int weight_p = 64; /* old backlog weight */ + +DEFINE_PER_CPU(struct netif_rx_stats, netdev_rx_stat) = { 0, }; + + +/** + * 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) +{ + 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->tstamp.off_sec) + net_timestamp(skb); + + /* + * The code is rearranged so that the path is the most + * short when CPU is congested, but is still operating. + */ + local_irq_save(flags); + 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) { +enqueue: + dev_hold(skb->dev); + __skb_queue_tail(&queue->input_pkt_queue, skb); + local_irq_restore(flags); + return NET_RX_SUCCESS; + } + + netif_rx_schedule(&queue->backlog_dev); + goto enqueue; + } + + __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 struct net_device *skb_bond(struct sk_buff *skb) +{ + struct net_device *dev = skb->dev; + + if (dev->master) { + if (skb_bond_should_drop(skb)) { + kfree_skb(skb); + return NULL; + } + skb->dev = dev->master; + } + + return dev; +} + +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, + struct net_device *orig_dev) +{ + atomic_inc(&skb->users); + return pt_prev->func(skb, skb->dev, pt_prev, orig_dev); +} + +#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_device *orig_dev) +{ + 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, orig_dev); + *pt_prev = NULL; + } + + return br_handle_frame_hook(port, pskb); +} +#else +#define handle_bridge(skb, pt_prev, ret, orig_dev) (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(KERN_WARNING "Redir loop detected Dropping packet (%d->%d)\n", + skb->iif, skb->dev->ifindex); + 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); + + spin_lock(&dev->queue_lock); + if ((q = dev->qdisc_ingress) != NULL) + result = q->enqueue(skb, q); + spin_unlock(&dev->queue_lock); + + } + + return result; +} +#endif + +int netif_receive_skb(struct sk_buff *skb) +{ + struct packet_type *ptype, *pt_prev; + struct net_device *orig_dev; + int ret = NET_RX_DROP; + __be16 type; + + /* if we've gotten here through NAPI, check netpoll */ + if (skb->dev->poll && netpoll_rx(skb)) + return NET_RX_DROP; + + if (!skb->tstamp.off_sec) + net_timestamp(skb); + + if (!skb->iif) + skb->iif = skb->dev->ifindex; + + orig_dev = skb_bond(skb); + + if (!orig_dev) + return NET_RX_DROP; + + __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, orig_dev); + pt_prev = ptype; + } + } + +#ifdef CONFIG_NET_CLS_ACT + if (pt_prev) { + ret = deliver_skb(skb, pt_prev, orig_dev); + 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 + + if (handle_bridge(&skb, &pt_prev, &ret, orig_dev)) + 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, orig_dev); + pt_prev = ptype; + } + } + + if (pt_prev) { + ret = pt_prev->func(skb, skb->dev, pt_prev, orig_dev); + } 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); + + 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_budget; + void *have; + + 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); + have = netpoll_poll_lock(dev); + + if (dev->quota <= 0 || dev->poll(dev, &budget)) { + netpoll_poll_unlock(have); + local_irq_disable(); + list_move_tail(&dev->poll_list, &queue->poll_list); + if (dev->quota < 0) + dev->quota += dev->weight; + else + dev->quota = dev->weight; + } else { + netpoll_poll_unlock(have); + dev_put(dev); + local_irq_disable(); + } + } +out: +#ifdef CONFIG_NET_DMA + /* + * There may not be any more sk_buffs coming right now, so push + * any pending DMA copies to hardware + */ + if (net_dma_client) { + struct dma_chan *chan; + rcu_read_lock(); + list_for_each_entry_rcu(chan, &net_dma_client->channels, client_node) + dma_async_memcpy_issue_pending(chan); + rcu_read_unlock(); + } +#endif + 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, 0, + 0, 0, 0, 0, /* was fastroute */ + s->cpu_collision ); + 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 const 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 const struct file_operations softnet_seq_fops = { + .owner = THIS_MODULE, + .open = softnet_seq_open, + .read = seq_read, + .llseek = seq_lseek, + .release = seq_release, +}; + +#ifdef CONFIG_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 promiscuity 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; + + if ((dev->promiscuity += inc) == 0) + dev->flags &= ~IFF_PROMISC; + else + 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"); + audit_log(current->audit_context, GFP_ATOMIC, + AUDIT_ANOM_PROMISCUOUS, + "dev=%s prom=%d old_prom=%d auid=%u", + dev->name, (dev->flags & IFF_PROMISC), + (old_flags & IFF_PROMISC), + audit_get_loginuid(current->audit_context)); + } +} + +/** + * 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 | + IFF_LOWER_UP | + IFF_DORMANT)) | + (dev->gflags & (IFF_PROMISC | + IFF_ALLMULTI)); + + if (netif_running(dev)) { + if (netif_oper_up(dev)) + flags |= IFF_RUNNING; + if (netif_carrier_ok(dev)) + flags |= IFF_LOWER_UP; + if (netif_dormant(dev)) + flags |= IFF_DORMANT; + } + + 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))) + raw_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) + raw_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) + raw_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)); + raw_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_lock(); + ret = dev_ifconf((char __user *) arg); + rtnl_unlock(); + 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 SIOCBONDCHANGEACTIVE: + case SIOCBRADDIF: + case SIOCBRDELIF: + if (!capable(CAP_NET_ADMIN)) + return -EPERM; + /* fall through */ + case SIOCBONDSLAVEINFOQUERY: + case SIOCBONDINFOQUERY: + 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 CONFIG_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 + || cmd == SIOCGIWENCODEEXT) { + 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 /* CONFIG_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(); + + might_sleep(); + + /* 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 + + 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; + } + } + + if (!dev_valid_name(dev->name)) { + ret = -EINVAL; + goto out; + } + + 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; + } + } + + /* Fix illegal SG+CSUM combinations. */ + if ((dev->features & NETIF_F_SG) && + !(dev->features & NETIF_F_ALL_CSUM)) { + printk(KERN_NOTICE "%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(KERN_NOTICE "%s: Dropping NETIF_F_TSO since no SG feature.\n", + dev->name); + dev->features &= ~NETIF_F_TSO; + } + if (dev->features & NETIF_F_UFO) { + if (!(dev->features & NETIF_F_HW_CSUM)) { + printk(KERN_ERR "%s: Dropping NETIF_F_UFO since no " + "NETIF_F_HW_CSUM feature.\n", + dev->name); + dev->features &= ~NETIF_F_UFO; + } + if (!(dev->features & NETIF_F_SG)) { + printk(KERN_ERR "%s: Dropping NETIF_F_UFO since no " + "NETIF_F_SG feature.\n", + dev->name); + dev->features &= ~NETIF_F_UFO; + } + } + + /* + * 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; + + ret = netdev_register_sysfs(dev); + if (ret) + goto out; + dev->reg_state = NETREG_REGISTERED; + + /* + * 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); + write_unlock_bh(&dev_base_lock); + + /* Notify protocols, that a new device appeared. */ + raw_notifier_call_chain(&netdev_chain, NETDEV_REGISTER, dev); + + ret = 0; + +out: + return ret; +} + +/** + * 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; + } + + 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_lock(); + + /* Rebroadcast unregister notification */ + raw_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_unlock(); + + 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 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 DEFINE_MUTEX(net_todo_run_mutex); +void netdev_run_todo(void) +{ + struct list_head list; + + /* Need to guard against multiple cpu's getting out of order. */ + mutex_lock(&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_replace_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); + + if (unlikely(dev->reg_state != NETREG_UNREGISTERING)) { + printk(KERN_ERR "network todo '%s' but state %d\n", + dev->name, dev->reg_state); + dump_stack(); + continue; + } + + 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); + } + +out: + mutex_unlock(&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; + + BUG_ON(strlen(name) >= sizeof(dev->name)); + + /* 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 = kzalloc(alloc_size, GFP_KERNEL); + if (!p) { + printk(KERN_ERR "alloc_netdev: Unable to allocate device.\n"); + return NULL; + } + + 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 + /* Compatibility 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 device release */ + put_device(&dev->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. + */ + +void 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); + + WARN_ON(1); + return; + } + + 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; + } + } + BUG_ON(!d); + + 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. + */ + raw_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); + + /* Finish processing unregister after unlock */ + net_set_todo(dev); + + synchronize_net(); + + dev_put(dev); +} + +/** + * 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); + +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; +} + +#ifdef CONFIG_NET_DMA +/** + * net_dma_rebalance - + * This is called when the number of channels allocated to the net_dma_client + * changes. The net_dma_client tries to have one DMA channel per CPU. + */ +static void net_dma_rebalance(void) +{ + unsigned int cpu, i, n; + struct dma_chan *chan; + + if (net_dma_count == 0) { + for_each_online_cpu(cpu) + rcu_assign_pointer(per_cpu(softnet_data, cpu).net_dma, NULL); + return; + } + + i = 0; + cpu = first_cpu(cpu_online_map); + + rcu_read_lock(); + list_for_each_entry(chan, &net_dma_client->channels, client_node) { + n = ((num_online_cpus() / net_dma_count) + + (i < (num_online_cpus() % net_dma_count) ? 1 : 0)); + + while(n) { + per_cpu(softnet_data, cpu).net_dma = chan; + cpu = next_cpu(cpu, cpu_online_map); + n--; + } + i++; + } + rcu_read_unlock(); +} + +/** + * netdev_dma_event - event callback for the net_dma_client + * @client: should always be net_dma_client + * @chan: DMA channel for the event + * @event: event type + */ +static void netdev_dma_event(struct dma_client *client, struct dma_chan *chan, + enum dma_event event) +{ + spin_lock(&net_dma_event_lock); + switch (event) { + case DMA_RESOURCE_ADDED: + net_dma_count++; + net_dma_rebalance(); + break; + case DMA_RESOURCE_REMOVED: + net_dma_count--; + net_dma_rebalance(); + break; + default: + break; + } + spin_unlock(&net_dma_event_lock); +} + +/** + * netdev_dma_regiser - register the networking subsystem as a DMA client + */ +static int __init netdev_dma_register(void) +{ + spin_lock_init(&net_dma_event_lock); + net_dma_client = dma_async_client_register(netdev_dma_event); + if (net_dma_client == NULL) + return -ENOMEM; + + dma_async_client_chan_request(net_dma_client, num_online_cpus()); + return 0; +} + +#else +static int __init netdev_dma_register(void) { return -ENODEV; } +#endif /* CONFIG_NET_DMA */ + +/* + * 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); + + 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_each_possible_cpu(i) { + struct softnet_data *queue; + + queue = &per_cpu(softnet_data, i); + skb_queue_head_init(&queue->input_pkt_queue); + 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); + } + + netdev_dma_register(); + + 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(dev_valid_name); +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_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.21.4/net/ring/Kconfig linux-2.6.21.4-1-686-smp-ring3/net/ring/Kconfig --- linux-2.6.21.4/net/ring/Kconfig 1970-01-01 00:00:00.000000000 +0000 +++ linux-2.6.21.4-1-686-smp-ring3/net/ring/Kconfig 2007-06-10 16:43:04.406423944 +0000 @@ -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.21.4/net/ring/Makefile linux-2.6.21.4-1-686-smp-ring3/net/ring/Makefile --- linux-2.6.21.4/net/ring/Makefile 1970-01-01 00:00:00.000000000 +0000 +++ linux-2.6.21.4-1-686-smp-ring3/net/ring/Makefile 2007-06-10 16:43:04.350421521 +0000 @@ -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.21.4/net/ring/ring_packet.c linux-2.6.21.4-1-686-smp-ring3/net/ring/ring_packet.c --- linux-2.6.21.4/net/ring/ring_packet.c 1970-01-01 00:00:00.000000000 +0000 +++ linux-2.6.21.4-1-686-smp-ring3/net/ring/ring_packet.c 2007-06-10 16:43:04.354421694 +0000 @@ -0,0 +1,4257 @@ +/* *************************************************************** + * + * (C) 2004-07 - Luca Deri + * + * This code includes contributions courtesy of + * - Jeff Randall + * - Helmut Manck + * - Brad Doctor + * - Amit D. Chaudhary + * - Francesco Fusco + * - Michael Stiller + * + * + * 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. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + */ + +#include +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,19)) +#include +#else +#include +#endif +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0)) +#include +#else +#include +#endif +#include +#include /* needed for virt_to_phys() */ +#ifdef CONFIG_INET +#include +#endif + +/* #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; + + u_short ring_pid; + + /* 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; + + /* Aho-Corasick */ + ACSM_STRUCT2 * acsm; + + /* Locks */ + atomic_t num_ring_slots_waiters; + wait_queue_head_t ring_slots_waitqueue; + rwlock_t ring_index_lock; + + /* Bloom Filters */ + u_char bitmask_enabled; + bitmask_selector mac_bitmask, vlan_bitmask, ip_bitmask, twin_ip_bitmask, + port_bitmask, twin_port_bitmask, proto_bitmask; + u_int32_t num_mac_bitmask_add, num_mac_bitmask_remove; + u_int32_t num_vlan_bitmask_add, num_vlan_bitmask_remove; + u_int32_t num_ip_bitmask_add, num_ip_bitmask_remove; + u_int32_t num_port_bitmask_add, num_port_bitmask_remove; + u_int32_t num_proto_bitmask_add, num_proto_bitmask_remove; + + /* Indexes (Internal) */ + u_int insert_page_id, insert_slot_id; +}; + +/* ************************************************* */ + +/* List of all ring sockets. */ +static struct list_head ring_table; +static u_int ring_table_size; + +/* List of all clusters */ +static struct ring_cluster *ring_cluster_list; + +static rwlock_t ring_mgmt_lock = RW_LOCK_UNLOCKED; + +/* ********************************** */ + +/* /proc entry for ring module */ +struct proc_dir_entry *ring_proc_dir = NULL; +struct proc_dir_entry *ring_proc = NULL; + +static int ring_proc_get_info(char *, char **, off_t, int, int *, void *); +static void ring_proc_add(struct ring_opt *pfr); +static void ring_proc_remove(struct ring_opt *pfr); +static void ring_proc_init(void); +static void ring_proc_term(void); + +/* ********************************** */ + +/* 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 unsigned int bucket_len = 128, num_slots = 4096, sample_rate = 1, + transparent_mode = 1, enable_tx_capture = 1; + +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,16)) +module_param(bucket_len, uint, 0644); +module_param(num_slots, uint, 0644); +module_param(sample_rate, uint, 0644); +module_param(transparent_mode, uint, 0644); +module_param(enable_tx_capture, uint, 0644); +#else +MODULE_PARM(bucket_len, "i"); +MODULE_PARM(num_slots, "i"); +MODULE_PARM(sample_rate, "i"); +MODULE_PARM(transparent_mode, "i"); +MODULE_PARM(enable_tx_capture, "i"); +#endif + +MODULE_PARM_DESC(bucket_len, "Number of ring buckets"); +MODULE_PARM_DESC(num_slots, "Number of ring slots"); +MODULE_PARM_DESC(sample_rate, "Ring packet sample rate"); +MODULE_PARM_DESC(transparent_mode, + "Set to 1 to set transparent mode " + "(slower but backwards compatible)"); + +MODULE_PARM_DESC(enable_tx_capture, "Set to 1 to capture outgoing packets"); + +/* ********************************** */ + +#define MIN_QUEUED_PKTS 64 +#define MAX_QUEUE_LOOPS 64 + + +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0)) +#define ring_sk_datatype(__sk) ((struct ring_opt *)__sk) +#define ring_sk(__sk) ((__sk)->sk_protinfo) +#else +#define ring_sk_datatype(a) (a) +#define ring_sk(__sk) ((__sk)->protinfo.pf_ring) +#endif + +#define _rdtsc() ({ uint64_t x; asm volatile("rdtsc" : "=A" (x)); x; }) + +/* + int dev_queue_xmit(struct sk_buff *skb) + skb->dev; + struct net_device *dev_get_by_name(const char *name) +*/ + +/* ********************************** */ + +/* +** $Id$ +** +** acsmx2.c +** +** Multi-Pattern Search Engine +** +** Aho-Corasick State Machine - version 2.0 +** +** Supports both Non-Deterministic and Deterministic Finite Automata +** +** +** Reference - Efficient String matching: An Aid to Bibliographic Search +** Alfred V Aho and Margaret J Corasick +** Bell Labratories +** Copyright(C) 1975 Association for Computing Machinery,Inc +** +** +++ +** +++ Version 1.0 notes - Marc Norton: +** +++ +** +** Original implementation based on the 4 algorithms in the paper by Aho & Corasick, +** some implementation ideas from 'Practical Algorithms in C', and some +** of my own. +** +** 1) Finds all occurrences of all patterns within a text. +** +** +++ +** +++ Version 2.0 Notes - Marc Norton/Dan Roelker: +** +++ +** +** New implementation modifies the state table storage and access model to use +** compacted sparse vector storage. Dan Roelker and I hammered this strategy out +** amongst many others in order to reduce memory usage and improve caching performance. +** The memory usage is greatly reduced, we only use 1/4 of what we use to. The caching +** performance is better in pure benchmarking tests, but does not show overall improvement +** in Snort. Unfortunately, once a pattern match test has been performed Snort moves on to doing +** many other things before we get back to a patteren match test, so the cache is voided. +** +** This versions has better caching performance characteristics, reduced memory, +** more state table storage options, and requires no a priori case conversions. +** It does maintain the same public interface. (Snort only used banded storage). +** +** 1) Supports NFA and DFA state machines, and basic keyword state machines +** 2) Initial transition table uses Linked Lists +** 3) Improved state table memory options. NFA and DFA state +** transition tables are converted to one of 4 formats during compilation. +** a) Full matrix +** b) Sparse matrix +** c) Banded matrix (Default-this is the only one used in snort) +** d) Sparse-Banded matrix +** 4) Added support for acstate_t in .h file so we can compile states as +** 16, or 32 bit state values for another reduction in memory consumption, +** smaller states allows more of the state table to be cached, and improves +** performance on x86-P4. Your mileage may vary, especially on risc systems. +** 5) Added a bool to each state transition list to indicate if there is a matching +** pattern in the state. This prevents us from accessing another data array +** and can improve caching/performance. +** 6) The search functions are very sensitive, don't change them without extensive testing, +** or you'll just spoil the caching and prefetching opportunities. +** +** Extras for fellow pattern matchers: +** The table below explains the storage format used at each step. +** You can use an NFA or DFA to match with, the NFA is slower but tiny - set the structure directly. +** You can use any of the 4 storage modes above -full,sparse,banded,sparse-bands, set the structure directly. +** For applications where you have lots of data and a pattern set to search, this version was up to 3x faster +** than the previous verion, due to caching performance. This cannot be fully realized in Snort yet, +** but other applications may have better caching opportunities. +** Snort only needs to use the banded or full storage. +** +** Transition table format at each processing stage. +** ------------------------------------------------- +** Patterns -> Keyword State Table (List) +** Keyword State Table -> NFA (List) +** NFA -> DFA (List) +** DFA (List)-> Sparse Rows O(m-avg # transitions per state) +** -> Banded Rows O(1) +** -> Sparse-Banded Rows O(nb-# bands) +** -> Full Matrix O(1) +** +** Copyright(C) 2002,2003,2004 Marc Norton +** Copyright(C) 2003,2004 Daniel Roelker +** Copyright(C) 2002,2003,2004 Sourcefire,Inc. +** +** This program is free software; you can redistribute it and/or modify +** it under the terms of the GNU General Public License as published by +** the Free Software Foundation; either version 2 of the License, or +** (at your option) any later version. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program; if not, write to the Free Software +** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +* +*/ + +/* + * + */ +#define MEMASSERT(p,s) if(!p){printk("ACSM-No Memory: %s!\n",s);} + +/* + * + */ +static int max_memory = 0; + +/* + * + */ +typedef struct acsm_summary_s +{ + unsigned num_states; + unsigned num_transitions; + ACSM_STRUCT2 acsm; + +}acsm_summary_t; + +/* + * + */ +static acsm_summary_t summary={0,0}; + +/* +** Case Translation Table +*/ +static unsigned char xlatcase[256]; +/* + * + */ + +inline int toupper(int ch) { + if ( (unsigned int)(ch - 'a') < 26u ) + ch += 'A' - 'a'; + return ch; +} + +static void init_xlatcase(void) +{ + int i; + for (i = 0; i < 256; i++) + { + xlatcase[i] = toupper(i); + } +} + +/* + * Case Conversion + */ +static +inline +void +ConvertCaseEx (unsigned char *d, unsigned char *s, int m) +{ + int i; +#ifdef XXXX + int n; + n = m & 3; + m >>= 2; + + for (i = 0; i < m; i++ ) + { + d[0] = xlatcase[ s[0] ]; + d[2] = xlatcase[ s[2] ]; + d[1] = xlatcase[ s[1] ]; + d[3] = xlatcase[ s[3] ]; + d+=4; + s+=4; + } + + for (i=0; i < n; i++) + { + d[i] = xlatcase[ s[i] ]; + } +#else + for (i=0; i < m; i++) + { + d[i] = xlatcase[ s[i] ]; + } + +#endif +} + + +/* + * + */ +static void * +AC_MALLOC (int n) +{ + void *p; + p = kmalloc (n, GFP_KERNEL); + if (p) + max_memory += n; + return p; +} + + +/* + * + */ +static void +AC_FREE (void *p) +{ + if (p) + kfree (p); +} + + +/* + * Simple QUEUE NODE + */ +typedef struct _qnode +{ + int state; + struct _qnode *next; +} + QNODE; + +/* + * Simple QUEUE Structure + */ +typedef struct _queue +{ + QNODE * head, *tail; + int count; +} + QUEUE; + +/* + * Initialize the queue + */ +static void +queue_init (QUEUE * s) +{ + s->head = s->tail = 0; + s->count= 0; +} + +/* + * Find a State in the queue + */ +static int +queue_find (QUEUE * s, int state) +{ + QNODE * q; + q = s->head; + while( q ) + { + if( q->state == state ) return 1; + q = q->next; + } + return 0; +} + +/* + * Add Tail Item to queue (FiFo/LiLo) + */ +static void +queue_add (QUEUE * s, int state) +{ + QNODE * q; + + if( queue_find( s, state ) ) return; + + if (!s->head) + { + q = s->tail = s->head = (QNODE *) AC_MALLOC (sizeof (QNODE)); + MEMASSERT (q, "queue_add"); + q->state = state; + q->next = 0; + } + else + { + q = (QNODE *) AC_MALLOC (sizeof (QNODE)); + q->state = state; + q->next = 0; + s->tail->next = q; + s->tail = q; + } + s->count++; +} + + +/* + * Remove Head Item from queue + */ +static int +queue_remove (QUEUE * s) +{ + int state = 0; + QNODE * q; + if (s->head) + { + q = s->head; + state = q->state; + s->head = s->head->next; + s->count--; + + if( !s->head ) + { + s->tail = 0; + s->count = 0; + } + AC_FREE (q); + } + return state; +} + + +/* + * Return items in the queue + */ +static int +queue_count (QUEUE * s) +{ + return s->count; +} + + +/* + * Free the queue + */ +static void +queue_free (QUEUE * s) +{ + while (queue_count (s)) + { + queue_remove (s); + } +} + +/* + * Get Next State-NFA + */ +static +int List_GetNextState( ACSM_STRUCT2 * acsm, int state, int input ) +{ + trans_node_t * t = acsm->acsmTransTable[state]; + + while( t ) + { + if( t->key == input ) + { + return t->next_state; + } + t=t->next; + } + + if( state == 0 ) return 0; + + return ACSM_FAIL_STATE2; /* Fail state ??? */ +} + +/* + * Get Next State-DFA + */ +static +int List_GetNextState2( ACSM_STRUCT2 * acsm, int state, int input ) +{ + trans_node_t * t = acsm->acsmTransTable[state]; + + while( t ) + { + if( t->key == input ) + { + return t->next_state; + } + t = t->next; + } + + return 0; /* default state */ +} +/* + * Put Next State - Head insertion, and transition updates + */ +static +int List_PutNextState( ACSM_STRUCT2 * acsm, int state, int input, int next_state ) +{ + trans_node_t * p; + trans_node_t * tnew; + + // printk(" List_PutNextState: state=%d, input='%c', next_state=%d\n",state,input,next_state); + + + /* Check if the transition already exists, if so just update the next_state */ + p = acsm->acsmTransTable[state]; + while( p ) + { + if( p->key == input ) /* transition already exists- reset the next state */ + { + p->next_state = next_state; + return 0; + } + p=p->next; + } + + /* Definitely not an existing transition - add it */ + tnew = (trans_node_t*)AC_MALLOC(sizeof(trans_node_t)); + if( !tnew ) return -1; + + tnew->key = input; + tnew->next_state = next_state; + tnew->next = 0; + + tnew->next = acsm->acsmTransTable[state]; + acsm->acsmTransTable[state] = tnew; + + acsm->acsmNumTrans++; + + return 0; +} +/* + * Free the entire transition table + */ +static +int List_FreeTransTable( ACSM_STRUCT2 * acsm ) +{ + int i; + trans_node_t * t, *p; + + if( !acsm->acsmTransTable ) return 0; + + for(i=0;i< acsm->acsmMaxStates;i++) + { + t = acsm->acsmTransTable[i]; + + while( t ) + { + p = t->next; + kfree(t); + t = p; + max_memory -= sizeof(trans_node_t); + } + } + + kfree(acsm->acsmTransTable); + + max_memory -= sizeof(void*) * acsm->acsmMaxStates; + + acsm->acsmTransTable = 0; + + return 0; +} + +/* + * + */ +/* + static + int List_FreeList( trans_node_t * t ) + { + int tcnt=0; + + trans_node_t *p; + + while( t ) + { + p = t->next; + kfree(t); + t = p; + max_memory -= sizeof(trans_node_t); + tcnt++; + } + + return tcnt; + } +*/ + +/* + * Converts row of states from list to a full vector format + */ +static +int List_ConvToFull(ACSM_STRUCT2 * acsm, acstate_t state, acstate_t * full ) +{ + int tcnt = 0; + trans_node_t * t = acsm->acsmTransTable[ state ]; + + memset(full,0,sizeof(acstate_t)*acsm->acsmAlphabetSize); + + if( !t ) return 0; + + while(t) + { + full[ t->key ] = t->next_state; + tcnt++; + t = t->next; + } + return tcnt; +} + +/* + * Copy a Match List Entry - don't dup the pattern data + */ +static ACSM_PATTERN2* +CopyMatchListEntry (ACSM_PATTERN2 * px) +{ + ACSM_PATTERN2 * p; + + p = (ACSM_PATTERN2 *) AC_MALLOC (sizeof (ACSM_PATTERN2)); + MEMASSERT (p, "CopyMatchListEntry"); + + memcpy (p, px, sizeof (ACSM_PATTERN2)); + + p->next = 0; + + return p; +} + +/* + * Check if a pattern is in the list already, + * validate it using the 'id' field. This must be unique + * for every pattern. + */ +/* + static + int FindMatchListEntry (ACSM_STRUCT2 * acsm, int state, ACSM_PATTERN2 * px) + { + ACSM_PATTERN2 * p; + + p = acsm->acsmMatchList[state]; + while( p ) + { + if( p->id == px->id ) return 1; + p = p->next; + } + + return 0; + } +*/ + + +/* + * Add a pattern to the list of patterns terminated at this state. + * Insert at front of list. + */ +static void +AddMatchListEntry (ACSM_STRUCT2 * acsm, int state, ACSM_PATTERN2 * px) +{ + ACSM_PATTERN2 * p; + + p = (ACSM_PATTERN2 *) AC_MALLOC (sizeof (ACSM_PATTERN2)); + + MEMASSERT (p, "AddMatchListEntry"); + + memcpy (p, px, sizeof (ACSM_PATTERN2)); + + p->next = acsm->acsmMatchList[state]; + + acsm->acsmMatchList[state] = p; +} + + +static void +AddPatternStates (ACSM_STRUCT2 * acsm, ACSM_PATTERN2 * p) +{ + int state, next, n; + unsigned char *pattern; + + n = p->n; + pattern = p->patrn; + state = 0; + + /* + * Match up pattern with existing states + */ + for (; n > 0; pattern++, n--) + { + next = List_GetNextState(acsm,state,*pattern); + if (next == ACSM_FAIL_STATE2 || next == 0) + { + break; + } + state = next; + } + + /* + * Add new states for the rest of the pattern bytes, 1 state per byte + */ + for (; n > 0; pattern++, n--) + { + acsm->acsmNumStates++; + List_PutNextState(acsm,state,*pattern,acsm->acsmNumStates); + state = acsm->acsmNumStates; + } + + AddMatchListEntry (acsm, state, p ); +} + +/* + * Build A Non-Deterministic Finite Automata + * The keyword state table must already be built, via AddPatternStates(). + */ +static void +Build_NFA (ACSM_STRUCT2 * acsm) +{ + int r, s, i; + QUEUE q, *queue = &q; + acstate_t * FailState = acsm->acsmFailState; + ACSM_PATTERN2 ** MatchList = acsm->acsmMatchList; + ACSM_PATTERN2 * mlist,* px; + + /* Init a Queue */ + queue_init (queue); + + + /* Add the state 0 transitions 1st, the states at depth 1, fail to state 0 */ + for (i = 0; i < acsm->acsmAlphabetSize; i++) + { + s = List_GetNextState2(acsm,0,i); + if( s ) + { + queue_add (queue, s); + FailState[s] = 0; + } + } + + /* Build the fail state successive layer of transitions */ + while (queue_count (queue) > 0) + { + r = queue_remove (queue); + + /* Find Final States for any Failure */ + for (i = 0; i < acsm->acsmAlphabetSize; i++) + { + int fs, next; + + s = List_GetNextState(acsm,r,i); + + if( s != ACSM_FAIL_STATE2 ) + { + queue_add (queue, s); + + fs = FailState[r]; + + /* + * Locate the next valid state for 'i' starting at fs + */ + while( (next=List_GetNextState(acsm,fs,i)) == ACSM_FAIL_STATE2 ) + { + fs = FailState[fs]; + } + + /* + * Update 's' state failure state to point to the next valid state + */ + FailState[s] = next; + + /* + * Copy 'next'states MatchList to 's' states MatchList, + * we copy them so each list can be AC_FREE'd later, + * else we could just manipulate pointers to fake the copy. + */ + for( mlist = MatchList[next]; + mlist; + mlist = mlist->next) + { + px = CopyMatchListEntry (mlist); + + /* Insert at front of MatchList */ + px->next = MatchList[s]; + MatchList[s] = px; + } + } + } + } + + /* Clean up the queue */ + queue_free (queue); +} + +/* + * Build Deterministic Finite Automata from the NFA + */ +static void +Convert_NFA_To_DFA (ACSM_STRUCT2 * acsm) +{ + int i, r, s, cFailState; + QUEUE q, *queue = &q; + acstate_t * FailState = acsm->acsmFailState; + + /* Init a Queue */ + queue_init (queue); + + /* Add the state 0 transitions 1st */ + for(i=0; iacsmAlphabetSize; i++) + { + s = List_GetNextState(acsm,0,i); + if ( s != 0 ) + { + queue_add (queue, s); + } + } + + /* Start building the next layer of transitions */ + while( queue_count(queue) > 0 ) + { + r = queue_remove(queue); + + /* Process this states layer */ + for (i = 0; i < acsm->acsmAlphabetSize; i++) + { + s = List_GetNextState(acsm,r,i); + + if( s != ACSM_FAIL_STATE2 && s!= 0) + { + queue_add (queue, s); + } + else + { + cFailState = List_GetNextState(acsm,FailState[r],i); + + if( cFailState != 0 && cFailState != ACSM_FAIL_STATE2 ) + { + List_PutNextState(acsm,r,i,cFailState); + } + } + } + } + + /* Clean up the queue */ + queue_free (queue); +} + +/* + * + * Convert a row lists for the state table to a full vector format + * + */ +static int +Conv_List_To_Full(ACSM_STRUCT2 * acsm) +{ + int tcnt, k; + acstate_t * p; + acstate_t ** NextState = acsm->acsmNextState; + + for(k=0;kacsmMaxStates;k++) + { + p = AC_MALLOC( sizeof(acstate_t) * (acsm->acsmAlphabetSize+2) ); + if(!p) return -1; + + tcnt = List_ConvToFull( acsm, (acstate_t)k, p+2 ); + + p[0] = ACF_FULL; + p[1] = 0; /* no matches yet */ + + NextState[k] = p; /* now we have a full format row vector */ + } + + return 0; +} + +/* + * Convert DFA memory usage from list based storage to a sparse-row storage. + * + * The Sparse format allows each row to be either full or sparse formatted. If the sparse row has + * too many transitions, performance or space may dictate that we use the standard full formatting + * for the row. More than 5 or 10 transitions per state ought to really whack performance. So the + * user can specify the max state transitions per state allowed in the sparse format. + * + * Standard Full Matrix Format + * --------------------------- + * acstate_t ** NextState ( 1st index is row/state, 2nd index is column=event/input) + * + * example: + * + * events -> a b c d e f g h i j k l m n o p + * states + * N 1 7 0 0 0 3 0 0 0 0 0 0 0 0 0 0 + * + * Sparse Format, each row : Words Value + * 1-1 fmt(0-full,1-sparse,2-banded,3-sparsebands) + * 2-2 bool match flag (indicates this state has pattern matches) + * 3-3 sparse state count ( # of input/next-state pairs ) + * 4-3+2*cnt 'input,next-state' pairs... each sizof(acstate_t) + * + * above example case yields: + * Full Format: 0, 1 7 0 0 0 3 0 0 0 0 0 0 0 0 0 0 ... + * Sparse format: 1, 3, 'a',1,'b',7,'f',3 - uses 2+2*ntransitions (non-default transitions) + */ +static int +Conv_Full_DFA_To_Sparse(ACSM_STRUCT2 * acsm) +{ + int cnt, m, k, i; + acstate_t * p, state, maxstates=0; + acstate_t ** NextState = acsm->acsmNextState; + acstate_t full[MAX_ALPHABET_SIZE]; + + for(k=0;kacsmMaxStates;k++) + { + cnt=0; + + List_ConvToFull(acsm, (acstate_t)k, full ); + + for (i = 0; i < acsm->acsmAlphabetSize; i++) + { + state = full[i]; + if( state != 0 && state != ACSM_FAIL_STATE2 ) cnt++; + } + + if( cnt > 0 ) maxstates++; + + if( k== 0 || cnt > acsm->acsmSparseMaxRowNodes ) + { + p = AC_MALLOC(sizeof(acstate_t)*(acsm->acsmAlphabetSize+2) ); + if(!p) return -1; + + p[0] = ACF_FULL; + p[1] = 0; + memcpy(&p[2],full,acsm->acsmAlphabetSize*sizeof(acstate_t)); + } + else + { + p = AC_MALLOC(sizeof(acstate_t)*(3+2*cnt)); + if(!p) return -1; + + m = 0; + p[m++] = ACF_SPARSE; + p[m++] = 0; /* no matches */ + p[m++] = cnt; + + for(i = 0; i < acsm->acsmAlphabetSize ; i++) + { + state = full[i]; + if( state != 0 && state != ACSM_FAIL_STATE2 ) + { + p[m++] = i; + p[m++] = state; + } + } + } + + NextState[k] = p; /* now we are a sparse formatted state transition array */ + } + + return 0; +} +/* + Convert Full matrix to Banded row format. + + Word values + 1 2 -> banded + 2 n number of values + 3 i index of 1st value (0-256) + 4 - 3+n next-state values at each index + +*/ +static int +Conv_Full_DFA_To_Banded(ACSM_STRUCT2 * acsm) +{ + int first = -1, last; + acstate_t * p, state, full[MAX_ALPHABET_SIZE]; + acstate_t ** NextState = acsm->acsmNextState; + int cnt,m,k,i; + + for(k=0;kacsmMaxStates;k++) + { + cnt=0; + + List_ConvToFull(acsm, (acstate_t)k, full ); + + first=-1; + last =-2; + + for (i = 0; i < acsm->acsmAlphabetSize; i++) + { + state = full[i]; + + if( state !=0 && state != ACSM_FAIL_STATE2 ) + { + if( first < 0 ) first = i; + last = i; + } + } + + /* calc band width */ + cnt= last - first + 1; + + p = AC_MALLOC(sizeof(acstate_t)*(4+cnt)); + + if(!p) return -1; + + m = 0; + p[m++] = ACF_BANDED; + p[m++] = 0; /* no matches */ + p[m++] = cnt; + p[m++] = first; + + for(i = first; i <= last; i++) + { + p[m++] = full[i]; + } + + NextState[k] = p; /* now we are a banded formatted state transition array */ + } + + return 0; +} + +/* + * Convert full matrix to Sparse Band row format. + * + * next - Full formatted row of next states + * asize - size of alphabet + * zcnt - max number of zeros in a run of zeros in any given band. + * + * Word Values + * 1 ACF_SPARSEBANDS + * 2 number of bands + * repeat 3 - 5+ ....once for each band in this row. + * 3 number of items in this band* 4 start index of this band + * 5- next-state values in this band... + */ +static +int calcSparseBands( acstate_t * next, int * begin, int * end, int asize, int zmax ) +{ + int i, nbands,zcnt,last=0; + acstate_t state; + + nbands=0; + for( i=0; i zmax ) break; + } + else + { + zcnt=0; + last = i; + } + } + + end[nbands++] = last; + + } + } + + return nbands; +} + + +/* + * Sparse Bands + * + * Row Format: + * Word + * 1 SPARSEBANDS format indicator + * 2 bool indicates a pattern match in this state + * 3 number of sparse bands + * 4 number of elements in this band + * 5 start index of this band + * 6- list of next states + * + * m number of elements in this band + * m+1 start index of this band + * m+2- list of next states + */ +static int +Conv_Full_DFA_To_SparseBands(ACSM_STRUCT2 * acsm) +{ + acstate_t * p; + acstate_t ** NextState = acsm->acsmNextState; + int cnt,m,k,i,zcnt=acsm->acsmSparseMaxZcnt; + + int band_begin[MAX_ALPHABET_SIZE]; + int band_end[MAX_ALPHABET_SIZE]; + int nbands,j; + acstate_t full[MAX_ALPHABET_SIZE]; + + for(k=0;kacsmMaxStates;k++) + { + cnt=0; + + List_ConvToFull(acsm, (acstate_t)k, full ); + + nbands = calcSparseBands( full, band_begin, band_end, acsm->acsmAlphabetSize, zcnt ); + + /* calc band width space*/ + cnt = 3; + for(i=0;iacsmNextState; + + p = NextState[k]; + + if( !p ) return 0; + + fmt = *p++; + + bmatch = *p++; + + if( fmt ==ACF_SPARSE ) + { + n = *p++; + for( ; n>0; n--, p+=2 ) + { + full[ p[0] ] = p[1]; + } + } + else if( fmt ==ACF_BANDED ) + { + + n = *p++; + index = *p++; + + for( ; n>0; n--, p++ ) + { + full[ index++ ] = p[0]; + } + } + else if( fmt ==ACF_SPARSEBANDS ) + { + nb = *p++; + for(i=0;i0; n--, p++ ) + { + full[ index++ ] = p[0]; + } + } + } + else if( fmt == ACF_FULL ) + { + memcpy(full,p,acsm->acsmAlphabetSize*sizeof(acstate_t)); + } + + return full; + } +*/ + +/* + * Select the desired storage mode + */ +int acsmSelectFormat2( ACSM_STRUCT2 * acsm, int m ) +{ + switch( m ) + { + case ACF_FULL: + case ACF_SPARSE: + case ACF_BANDED: + case ACF_SPARSEBANDS: + acsm->acsmFormat = m; + break; + default: + return -1; + } + + return 0; +} +/* + * + */ +void acsmSetMaxSparseBandZeros2( ACSM_STRUCT2 * acsm, int n ) +{ + acsm->acsmSparseMaxZcnt = n; +} +/* + * + */ +void acsmSetMaxSparseElements2( ACSM_STRUCT2 * acsm, int n ) +{ + acsm->acsmSparseMaxRowNodes = n; +} +/* + * + */ +int acsmSelectFSA2( ACSM_STRUCT2 * acsm, int m ) +{ + switch( m ) + { + case FSA_TRIE: + case FSA_NFA: + case FSA_DFA: + acsm->acsmFSA = m; + default: + return -1; + } +} +/* + * + */ +int acsmSetAlphabetSize2( ACSM_STRUCT2 * acsm, int n ) +{ + if( n <= MAX_ALPHABET_SIZE ) + { + acsm->acsmAlphabetSize = n; + } + else + { + return -1; + } + return 0; +} +/* + * Create a new AC state machine + */ +static ACSM_STRUCT2 * acsmNew2 (void) +{ + ACSM_STRUCT2 * p; + + init_xlatcase (); + + p = (ACSM_STRUCT2 *) AC_MALLOC(sizeof (ACSM_STRUCT2)); + MEMASSERT (p, "acsmNew"); + + if (p) + { + memset (p, 0, sizeof (ACSM_STRUCT2)); + + /* Some defaults */ + p->acsmFSA = FSA_DFA; + p->acsmFormat = ACF_BANDED; + p->acsmAlphabetSize = 256; + p->acsmSparseMaxRowNodes = 256; + p->acsmSparseMaxZcnt = 10; + } + + return p; +} +/* + * Add a pattern to the list of patterns for this state machine + * + */ +int +acsmAddPattern2 (ACSM_STRUCT2 * p, unsigned char *pat, int n, int nocase, + int offset, int depth, void * id, int iid) +{ + ACSM_PATTERN2 * plist; + + plist = (ACSM_PATTERN2 *) AC_MALLOC (sizeof (ACSM_PATTERN2)); + MEMASSERT (plist, "acsmAddPattern"); + + plist->patrn = (unsigned char *) AC_MALLOC ( n ); + MEMASSERT (plist->patrn, "acsmAddPattern"); + + ConvertCaseEx(plist->patrn, pat, n); + + plist->casepatrn = (unsigned char *) AC_MALLOC ( n ); + MEMASSERT (plist->casepatrn, "acsmAddPattern"); + + memcpy (plist->casepatrn, pat, n); + + plist->n = n; + plist->nocase = nocase; + plist->offset = offset; + plist->depth = depth; + plist->id = id; + plist->iid = iid; + + plist->next = p->acsmPatterns; + p->acsmPatterns = plist; + + return 0; +} +/* + * Add a Key to the list of key+data pairs + */ +int acsmAddKey2(ACSM_STRUCT2 * p, unsigned char *key, int klen, int nocase, void * data) +{ + ACSM_PATTERN2 * plist; + + plist = (ACSM_PATTERN2 *) AC_MALLOC (sizeof (ACSM_PATTERN2)); + MEMASSERT (plist, "acsmAddPattern"); + + plist->patrn = (unsigned char *) AC_MALLOC (klen); + memcpy (plist->patrn, key, klen); + + plist->casepatrn = (unsigned char *) AC_MALLOC (klen); + memcpy (plist->casepatrn, key, klen); + + plist->n = klen; + plist->nocase = nocase; + plist->offset = 0; + plist->depth = 0; + plist->id = 0; + plist->iid = 0; + + plist->next = p->acsmPatterns; + p->acsmPatterns = plist; + + return 0; +} + +/* + * Copy a boolean match flag int NextState table, for caching purposes. + */ +static +void acsmUpdateMatchStates( ACSM_STRUCT2 * acsm ) +{ + acstate_t state; + acstate_t ** NextState = acsm->acsmNextState; + ACSM_PATTERN2 ** MatchList = acsm->acsmMatchList; + + for( state=0; stateacsmNumStates; state++ ) + { + if( MatchList[state] ) + { + NextState[state][1] = 1; + } + else + { + NextState[state][1] = 0; + } + } +} + +/* + * Compile State Machine - NFA or DFA and Full or Banded or Sparse or SparseBands + */ +int +acsmCompile2 (ACSM_STRUCT2 * acsm) +{ + int k; + ACSM_PATTERN2 * plist; + + /* Count number of states */ + for (plist = acsm->acsmPatterns; plist != NULL; plist = plist->next) + { + acsm->acsmMaxStates += plist->n; + /* acsm->acsmMaxStates += plist->n*2; if we handle case in the table */ + } + acsm->acsmMaxStates++; /* one extra */ + + /* Alloc a List based State Transition table */ + acsm->acsmTransTable =(trans_node_t**) AC_MALLOC(sizeof(trans_node_t*) * acsm->acsmMaxStates ); + MEMASSERT (acsm->acsmTransTable, "acsmCompile"); + + memset (acsm->acsmTransTable, 0, sizeof(trans_node_t*) * acsm->acsmMaxStates); + + /* Alloc a failure table - this has a failure state, and a match list for each state */ + acsm->acsmFailState =(acstate_t*) AC_MALLOC(sizeof(acstate_t) * acsm->acsmMaxStates ); + MEMASSERT (acsm->acsmFailState, "acsmCompile"); + + memset (acsm->acsmFailState, 0, sizeof(acstate_t) * acsm->acsmMaxStates ); + + /* Alloc a MatchList table - this has a lis tof pattern matches for each state, if any */ + acsm->acsmMatchList=(ACSM_PATTERN2**) AC_MALLOC(sizeof(ACSM_PATTERN2*) * acsm->acsmMaxStates ); + MEMASSERT (acsm->acsmMatchList, "acsmCompile"); + + memset (acsm->acsmMatchList, 0, sizeof(ACSM_PATTERN2*) * acsm->acsmMaxStates ); + + /* Alloc a separate state transition table == in state 's' due to event 'k', transition to 'next' state */ + acsm->acsmNextState=(acstate_t**)AC_MALLOC( acsm->acsmMaxStates * sizeof(acstate_t*) ); + MEMASSERT(acsm->acsmNextState, "acsmCompile-NextState"); + + for (k = 0; k < acsm->acsmMaxStates; k++) + { + acsm->acsmNextState[k]=(acstate_t*)0; + } + + /* Initialize state zero as a branch */ + acsm->acsmNumStates = 0; + + /* Add the 0'th state, */ + //acsm->acsmNumStates++; + + /* Add each Pattern to the State Table - This forms a keywords state table */ + for (plist = acsm->acsmPatterns; plist != NULL; plist = plist->next) + { + AddPatternStates (acsm, plist); + } + + acsm->acsmNumStates++; + + if( acsm->acsmFSA == FSA_DFA || acsm->acsmFSA == FSA_NFA ) + { + /* Build the NFA */ + Build_NFA (acsm); + } + + if( acsm->acsmFSA == FSA_DFA ) + { + /* Convert the NFA to a DFA */ + Convert_NFA_To_DFA (acsm); + } + + /* + * + * Select Final Transition Table Storage Mode + * + */ + if( acsm->acsmFormat == ACF_SPARSE ) + { + /* Convert DFA Full matrix to a Sparse matrix */ + if( Conv_Full_DFA_To_Sparse(acsm) ) + return -1; + } + + else if( acsm->acsmFormat == ACF_BANDED ) + { + /* Convert DFA Full matrix to a Sparse matrix */ + if( Conv_Full_DFA_To_Banded(acsm) ) + return -1; + } + + else if( acsm->acsmFormat == ACF_SPARSEBANDS ) + { + /* Convert DFA Full matrix to a Sparse matrix */ + if( Conv_Full_DFA_To_SparseBands(acsm) ) + return -1; + } + else if( acsm->acsmFormat == ACF_FULL ) + { + if( Conv_List_To_Full( acsm ) ) + return -1; + } + + acsmUpdateMatchStates( acsm ); /* load boolean match flags into state table */ + + /* Free up the Table Of Transition Lists */ + List_FreeTransTable( acsm ); + + /* For now -- show this info */ + /* + * acsmPrintInfo( acsm ); + */ + + + /* Accrue Summary State Stats */ + summary.num_states += acsm->acsmNumStates; + summary.num_transitions += acsm->acsmNumTrans; + + memcpy( &summary.acsm, acsm, sizeof(ACSM_STRUCT2)); + + return 0; +} + +/* + * Get the NextState from the NFA, all NFA storage formats use this + */ +inline +acstate_t SparseGetNextStateNFA(acstate_t * ps, acstate_t state, unsigned input) +{ + acstate_t fmt; + acstate_t n; + int index; + int nb; + + fmt = *ps++; + + ps++; /* skip bMatchState */ + + switch( fmt ) + { + case ACF_BANDED: + { + n = ps[0]; + index = ps[1]; + + if( input < index ) + { + if(state==0) + { + return 0; + } + else + { + return (acstate_t)ACSM_FAIL_STATE2; + } + } + if( input >= index + n ) + { + if(state==0) + { + return 0; + } + else + { + return (acstate_t)ACSM_FAIL_STATE2; + } + } + if( ps[input-index] == 0 ) + { + if( state != 0 ) + { + return ACSM_FAIL_STATE2; + } + } + + return (acstate_t) ps[input-index]; + } + + case ACF_SPARSE: + { + n = *ps++; /* number of sparse index-value entries */ + + for( ; n>0 ; n-- ) + { + if( ps[0] > input ) /* cannot match the input, already a higher value than the input */ + { + return (acstate_t)ACSM_FAIL_STATE2; /* default state */ + } + else if( ps[0] == input ) + { + return ps[1]; /* next state */ + } + ps+=2; + } + if( state == 0 ) + { + return 0; + } + return ACSM_FAIL_STATE2; + } + + case ACF_SPARSEBANDS: + { + nb = *ps++; /* number of bands */ + + while( nb > 0 ) /* for each band */ + { + n = *ps++; /* number of elements */ + index = *ps++; /* 1st element value */ + + if( input < index ) + { + if( state != 0 ) + { + return (acstate_t)ACSM_FAIL_STATE2; + } + return (acstate_t)0; + } + if( (input >= index) && (input < (index + n)) ) + { + if( ps[input-index] == 0 ) + { + if( state != 0 ) + { + return ACSM_FAIL_STATE2; + } + } + return (acstate_t) ps[input-index]; + } + nb--; + ps += n; + } + if( state != 0 ) + { + return (acstate_t)ACSM_FAIL_STATE2; + } + return (acstate_t)0; + } + + case ACF_FULL: + { + if( ps[input] == 0 ) + { + if( state != 0 ) + { + return ACSM_FAIL_STATE2; + } + } + return ps[input]; + } + } + + return 0; +} + + + +/* + * Get the NextState from the DFA Next State Transition table + * Full and banded are supported separately, this is for + * sparse and sparse-bands + */ +inline +acstate_t SparseGetNextStateDFA(acstate_t * ps, acstate_t state, unsigned input) +{ + acstate_t n, nb; + int index; + + switch( ps[0] ) + { + /* BANDED */ + case ACF_BANDED: + { + /* n=ps[2] : number of entries in the band */ + /* index=ps[3] : index of the 1st entry, sequential thereafter */ + + if( input < ps[3] ) return 0; + if( input >= (ps[3]+ps[2]) ) return 0; + + return ps[4+input-ps[3]]; + } + + /* FULL */ + case ACF_FULL: + { + return ps[2+input]; + } + + /* SPARSE */ + case ACF_SPARSE: + { + n = ps[2]; /* number of entries/ key+next pairs */ + + ps += 3; + + for( ; n>0 ; n-- ) + { + if( input < ps[0] ) /* cannot match the input, already a higher value than the input */ + { + return (acstate_t)0; /* default state */ + } + else if( ps[0] == input ) + { + return ps[1]; /* next state */ + } + ps += 2; + } + return (acstate_t)0; + } + + + /* SPARSEBANDS */ + case ACF_SPARSEBANDS: + { + nb = ps[2]; /* number of bands */ + + ps += 3; + + while( nb > 0 ) /* for each band */ + { + n = ps[0]; /* number of elements in this band */ + index = ps[1]; /* start index/char of this band */ + if( input < index ) + { + return (acstate_t)0; + } + if( (input < (index + n)) ) + { + return (acstate_t) ps[2+input-index]; + } + nb--; + ps += n; + } + return (acstate_t)0; + } + } + + return 0; +} +/* + * Search Text or Binary Data for Pattern matches + * + * Sparse & Sparse-Banded Matrix search + */ +static +inline +int +acsmSearchSparseDFA(ACSM_STRUCT2 * acsm, unsigned char *Tx, int n, + int (*Match) (void * id, int index, void *data), + void *data) +{ + acstate_t state; + ACSM_PATTERN2 * mlist; + unsigned char * Tend; + int nfound = 0; + unsigned char * T, * Tc; + int index; + acstate_t ** NextState = acsm->acsmNextState; + ACSM_PATTERN2 ** MatchList = acsm->acsmMatchList; + + Tc = Tx; + T = Tx; + Tend = T + n; + + for( state = 0; T < Tend; T++ ) + { + state = SparseGetNextStateDFA ( NextState[state], state, xlatcase[*T] ); + + /* test if this state has any matching patterns */ + if( NextState[state][1] ) + { + for( mlist = MatchList[state]; + mlist!= NULL; + mlist = mlist->next ) + { + index = T - mlist->n - Tc; + if( mlist->nocase ) + { + nfound++; + if (Match (mlist->id, index, data)) + return nfound; + } + else + { + if( memcmp (mlist->casepatrn, Tx + index, mlist->n) == 0 ) + { + nfound++; + if (Match (mlist->id, index, data)) + return nfound; + } + } + } + } + } + return nfound; +} +/* + * Full format DFA search + * Do not change anything here without testing, caching and prefetching + * performance is very sensitive to any changes. + * + * Perf-Notes: + * 1) replaced ConvertCaseEx with inline xlatcase - this improves performance 5-10% + * 2) using 'nocase' improves performance again by 10-15%, since memcmp is not needed + * 3) + */ +static +inline +int +acsmSearchSparseDFA_Full(ACSM_STRUCT2 * acsm, unsigned char *Tx, int n, + int (*Match) (void * id, int index, void *data), + void *data) +{ + ACSM_PATTERN2 * mlist; + unsigned char * Tend; + unsigned char * T; + int index; + acstate_t state; + acstate_t * ps; + acstate_t sindex; + acstate_t ** NextState = acsm->acsmNextState; + ACSM_PATTERN2 ** MatchList = acsm->acsmMatchList; + int nfound = 0; + + T = Tx; + Tend = Tx + n; + + for( state = 0; T < Tend; T++ ) + { + ps = NextState[ state ]; + + sindex = xlatcase[ T[0] ]; + + /* check the current state for a pattern match */ + if( ps[1] ) + { + for( mlist = MatchList[state]; + mlist!= NULL; + mlist = mlist->next ) + { + index = T - mlist->n - Tx; + + + if( mlist->nocase ) + { + nfound++; + if (Match (mlist->id, index, data)) + return nfound; + } + else + { + if( memcmp (mlist->casepatrn, Tx + index, mlist->n ) == 0 ) + { + nfound++; + if (Match (mlist->id, index, data)) + return nfound; + } + } + + } + } + + state = ps[ 2u + sindex ]; + } + + /* Check the last state for a pattern match */ + for( mlist = MatchList[state]; + mlist!= NULL; + mlist = mlist->next ) + { + index = T - mlist->n - Tx; + + if( mlist->nocase ) + { + nfound++; + if (Match (mlist->id, index, data)) + return nfound; + } + else + { + if( memcmp (mlist->casepatrn, Tx + index, mlist->n) == 0 ) + { + nfound++; + if (Match (mlist->id, index, data)) + return nfound; + } + } + } + + return nfound; +} +/* + * Banded-Row format DFA search + * Do not change anything here, caching and prefetching + * performance is very sensitive to any changes. + * + * ps[0] = storage fmt + * ps[1] = bool match flag + * ps[2] = # elements in band + * ps[3] = index of 1st element + */ +static +inline +int +acsmSearchSparseDFA_Banded(ACSM_STRUCT2 * acsm, unsigned char *Tx, int n, + int (*Match) (void * id, int index, void *data), + void *data) +{ + acstate_t state; + unsigned char * Tend; + unsigned char * T; + int sindex; + int index; + acstate_t ** NextState = acsm->acsmNextState; + ACSM_PATTERN2 ** MatchList = acsm->acsmMatchList; + ACSM_PATTERN2 * mlist; + acstate_t * ps; + int nfound = 0; + + T = Tx; + Tend = T + n; + + for( state = 0; T < Tend; T++ ) + { + ps = NextState[state]; + + sindex = xlatcase[ T[0] ]; + + /* test if this state has any matching patterns */ + if( ps[1] ) + { + for( mlist = MatchList[state]; + mlist!= NULL; + mlist = mlist->next ) + { + index = T - mlist->n - Tx; + + if( mlist->nocase ) + { + nfound++; + if (Match (mlist->id, index, data)) + return nfound; + } + else + { + if( memcmp (mlist->casepatrn, Tx + index, mlist->n) == 0 ) + { + nfound++; + if (Match (mlist->id, index, data)) + return nfound; + } + } + } + } + + if( sindex < ps[3] ) state = 0; + else if( sindex >= (ps[3] + ps[2]) ) state = 0; + else state = ps[ 4u + sindex - ps[3] ]; + } + + /* Check the last state for a pattern match */ + for( mlist = MatchList[state]; + mlist!= NULL; + mlist = mlist->next ) + { + index = T - mlist->n - Tx; + + if( mlist->nocase ) + { + nfound++; + if (Match (mlist->id, index, data)) + return nfound; + } + else + { + if( memcmp (mlist->casepatrn, Tx + index, mlist->n) == 0 ) + { + nfound++; + if (Match (mlist->id, index, data)) + return nfound; + } + } + } + + return nfound; +} + + + +/* + * Search Text or Binary Data for Pattern matches + * + * Sparse Storage Version + */ +static +inline +int +acsmSearchSparseNFA(ACSM_STRUCT2 * acsm, unsigned char *Tx, int n, + int (*Match) (void * id, int index, void *data), + void *data) +{ + acstate_t state; + ACSM_PATTERN2 * mlist; + unsigned char * Tend; + int nfound = 0; + unsigned char * T, *Tc; + int index; + acstate_t ** NextState= acsm->acsmNextState; + acstate_t * FailState= acsm->acsmFailState; + ACSM_PATTERN2 ** MatchList = acsm->acsmMatchList; + unsigned char Tchar; + + Tc = Tx; + T = Tx; + Tend = T + n; + + for( state = 0; T < Tend; T++ ) + { + acstate_t nstate; + + Tchar = xlatcase[ *T ]; + + while( (nstate=SparseGetNextStateNFA(NextState[state],state,Tchar))==ACSM_FAIL_STATE2 ) + state = FailState[state]; + + state = nstate; + + for( mlist = MatchList[state]; + mlist!= NULL; + mlist = mlist->next ) + { + index = T - mlist->n - Tx; + if( mlist->nocase ) + { + nfound++; + if (Match (mlist->id, index, data)) + return nfound; + } + else + { + if( memcmp (mlist->casepatrn, Tx + index, mlist->n) == 0 ) + { + nfound++; + if (Match (mlist->id, index, data)) + return nfound; + } + } + } + } + + return nfound; +} + +/* + * Search Function + */ +int +acsmSearch2(ACSM_STRUCT2 * acsm, unsigned char *Tx, int n, + int (*Match) (void * id, int index, void *data), + void *data) +{ + + switch( acsm->acsmFSA ) + { + case FSA_DFA: + + if( acsm->acsmFormat == ACF_FULL ) + { + return acsmSearchSparseDFA_Full( acsm, Tx, n, Match,data ); + } + else if( acsm->acsmFormat == ACF_BANDED ) + { + return acsmSearchSparseDFA_Banded( acsm, Tx, n, Match,data ); + } + else + { + return acsmSearchSparseDFA( acsm, Tx, n, Match,data ); + } + + case FSA_NFA: + + return acsmSearchSparseNFA( acsm, Tx, n, Match,data ); + + case FSA_TRIE: + + return 0; + } + return 0; +} + + +/* + * Free all memory + */ +void +acsmFree2 (ACSM_STRUCT2 * acsm) +{ + int i; + ACSM_PATTERN2 * mlist, *ilist; + for (i = 0; i < acsm->acsmMaxStates; i++) + { + mlist = acsm->acsmMatchList[i]; + + while (mlist) + { + ilist = mlist; + mlist = mlist->next; + AC_FREE (ilist); + } + AC_FREE(acsm->acsmNextState[i]); + } + AC_FREE(acsm->acsmFailState); + AC_FREE(acsm->acsmMatchList); +} + +/* ********************************** */ + +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 +} + +/* ********************************** */ + +static void ring_proc_add(struct ring_opt *pfr) { + if(ring_proc_dir != NULL) { + char name[16]; + + pfr->ring_pid = current->pid; + + snprintf(name, sizeof(name), "%d", pfr->ring_pid); + create_proc_read_entry(name, 0, ring_proc_dir, + ring_proc_get_info, pfr); + /* printk("PF_RING: added /proc/net/pf_ring/%s\n", name); */ + } +} + +/* ********************************** */ + +static void ring_proc_remove(struct ring_opt *pfr) { + if(ring_proc_dir != NULL) { + char name[16]; + + snprintf(name, sizeof(name), "%d", pfr->ring_pid); + remove_proc_entry(name, ring_proc_dir); + /* printk("PF_RING: removed /proc/net/pf_ring/%s\n", name); */ + } +} + +/* ********************************** */ + +static int ring_proc_get_info(char *buf, char **start, off_t offset, + int len, int *unused, void *data) +{ + int rlen = 0; + struct ring_opt *pfr; + FlowSlotInfo *fsi; + + if(data == NULL) { + /* /proc/net/pf_ring/info */ + rlen = sprintf(buf,"Version : %s\n", RING_VERSION); + rlen += sprintf(buf + rlen,"Bucket length : %d bytes\n", bucket_len); + rlen += sprintf(buf + rlen,"Ring slots : %d\n", num_slots); + rlen += sprintf(buf + rlen,"Sample rate : %d [1=no sampling]\n", sample_rate); + + rlen += sprintf(buf + rlen,"Capture TX : %s\n", + enable_tx_capture ? "Yes [RX+TX]" : "No [RX only]"); + rlen += sprintf(buf + rlen,"Transparent mode : %s\n", + transparent_mode ? "Yes" : "No"); + rlen += sprintf(buf + rlen,"Total rings : %d\n", ring_table_size); + } else { + /* detailed statistics about a PF_RING */ + pfr = (struct ring_opt*)data; + + if(data) { + fsi = pfr->slots_info; + + if(fsi) { + rlen = sprintf(buf, "Bound Device : %s\n", + pfr->ring_netdev->name == NULL ? "" : pfr->ring_netdev->name); + rlen += sprintf(buf + rlen,"Version : %d\n", fsi->version); + rlen += sprintf(buf + rlen,"Sampling Rate : %d\n", pfr->sample_rate); + rlen += sprintf(buf + rlen,"BPF Filtering : %s\n", pfr->bpfFilter ? "Enabled" : "Disabled"); + rlen += sprintf(buf + rlen,"Bloom Filters : %s\n", pfr->bitmask_enabled ? "Enabled" : "Disabled"); + rlen += sprintf(buf + rlen,"Pattern Search: %s\n", pfr->acsm ? "Enabled" : "Disabled"); + rlen += sprintf(buf + rlen,"Cluster Id : %d\n", pfr->cluster_id); + rlen += sprintf(buf + rlen,"Tot Slots : %d\n", fsi->tot_slots); + rlen += sprintf(buf + rlen,"Slot Len : %d\n", fsi->slot_len); + rlen += sprintf(buf + rlen,"Data Len : %d\n", fsi->data_len); + rlen += sprintf(buf + rlen,"Tot Memory : %d\n", fsi->tot_mem); + rlen += sprintf(buf + rlen,"Tot Packets : %lu\n", (unsigned long)fsi->tot_pkts); + rlen += sprintf(buf + rlen,"Tot Pkt Lost : %lu\n", (unsigned long)fsi->tot_lost); + rlen += sprintf(buf + rlen,"Tot Insert : %lu\n", (unsigned long)fsi->tot_insert); + rlen += sprintf(buf + rlen,"Tot Read : %lu\n", (unsigned long)fsi->tot_read); + + } else + rlen = sprintf(buf, "WARNING fsi == NULL\n"); + } else + rlen = sprintf(buf, "WARNING data == NULL\n"); + } + + return rlen; +} + +/* ********************************** */ + +static void ring_proc_init(void) { + ring_proc_dir = proc_mkdir("pf_ring", proc_net); + + if(ring_proc_dir) { + ring_proc_dir->owner = THIS_MODULE; + ring_proc = create_proc_read_entry("info", 0, ring_proc_dir, + ring_proc_get_info, NULL); + if(!ring_proc) + printk("PF_RING: unable to register proc file\n"); + else { + ring_proc->owner = THIS_MODULE; + printk("PF_RING: registered /proc/net/pf_ring/\n"); + } + } else + printk("PF_RING: unable to create /proc/net/pf_ring\n"); +} + +/* ********************************** */ + +static void ring_proc_term(void) { + if(ring_proc != NULL) { + remove_proc_entry("info", ring_proc_dir); + if(ring_proc_dir != NULL) remove_proc_entry("pf_ring", proc_net); + + printk("PF_RING: deregistered /proc/net/pf_ring\n"); + } +} + +/* ********************************** */ + +/* + * 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_table_size++; + ring_proc_add(ring_sk(sk)); +} + +/* ********************************** */ + +/* + * 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) { + list_del(ptr); + kfree(ptr); + ring_table_size--; + 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 int parse_pkt(struct sk_buff *skb, u_int16_t skb_displ, + u_int8_t *l3_proto, u_int16_t *eth_type, + u_int16_t *l3_offset, u_int16_t *l4_offset, + u_int16_t *vlan_id, u_int32_t *ipv4_src, + u_int32_t *ipv4_dst, + u_int16_t *l4_src_port, u_int16_t *l4_dst_port, + u_int16_t *payload_offset) { + struct iphdr *ip; + struct ethhdr *eh = (struct ethhdr*)(skb->data-skb_displ); + u_int16_t displ; + + *l3_offset = *l4_offset = *l3_proto = *payload_offset = 0; + *eth_type = ntohs(eh->h_proto); + + if(*eth_type == 0x8100 /* 802.1q (VLAN) */) { + (*vlan_id) = (skb->data[14] & 15)*256 + skb->data[15]; + *eth_type = (skb->data[16])*256 + skb->data[17]; + displ = 4; + } else { + displ = 0; + (*vlan_id) = (u_int16_t)-1; + } + + if(*eth_type == 0x0800 /* IP */) { + *l3_offset = displ+sizeof(struct ethhdr); + ip = (struct iphdr*)(skb->data-skb_displ+(*l3_offset)); + + *ipv4_src = ntohl(ip->saddr), *ipv4_dst = ntohl(ip->daddr), *l3_proto = ip->protocol; + + if((ip->protocol == IPPROTO_TCP) || (ip->protocol == IPPROTO_UDP)) { + *l4_offset = (*l3_offset)+(ip->ihl*4); + + if(ip->protocol == IPPROTO_TCP) { + struct tcphdr *tcp = (struct tcphdr*)(skb->data-skb_displ+(*l4_offset)); + *l4_src_port = ntohs(tcp->source), *l4_dst_port = ntohs(tcp->dest); + *payload_offset = (*l4_offset)+(tcp->doff * 4); + } else if(ip->protocol == IPPROTO_UDP) { + struct udphdr *udp = (struct udphdr*)(skb->data-skb_displ+(*l4_offset)); + *l4_src_port = ntohs(udp->source), *l4_dst_port = ntohs(udp->dest); + *payload_offset = (*l4_offset)+sizeof(struct udphdr); + } else + *payload_offset = (*l4_offset); + } else + *l4_src_port = *l4_dst_port = 0; + + return(1); /* IP */ + } /* TODO: handle IPv6 */ + + return(0); /* No IP */ +} + +/* **************************************************************** */ + +static void reset_bitmask(bitmask_selector *selector) +{ + memset((char*)selector->bits_memory, 0, selector->num_bits/8); + + while(selector->clashes != NULL) { + bitmask_counter_list *next = selector->clashes->next; + kfree(selector->clashes); + selector->clashes = next; + } +} + +/* **************************************************************** */ + +static void alloc_bitmask(u_int32_t tot_bits, bitmask_selector *selector) +{ + u_int tot_mem = tot_bits/8; + + if(tot_mem <= PAGE_SIZE) + selector->order = 1; + else { + for(selector->order = 0; (PAGE_SIZE << selector->order) < tot_mem; selector->order++) + ; + } + + printk("BITMASK: [order=%d][tot_mem=%d]\n", selector->order, tot_mem); + + while((selector->bits_memory = __get_free_pages(GFP_ATOMIC, selector->order)) == 0) + if(selector->order-- == 0) + break; + + if(selector->order == 0) { + printk("BITMASK: ERROR not enough memory for bitmask\n"); + selector->num_bits = 0; + return; + } + + tot_mem = PAGE_SIZE << selector->order; + printk("BITMASK: succesfully allocated [tot_mem=%d][order=%d]\n", + tot_mem, selector->order); + + selector->num_bits = tot_mem*8; + selector->clashes = NULL; + reset_bitmask(selector); +} + +/* ********************************** */ + +static void free_bitmask(bitmask_selector *selector) +{ + if(selector->bits_memory > 0) + free_pages(selector->bits_memory, selector->order); +} + +/* ********************************** */ + +static void set_bit_bitmask(bitmask_selector *selector, u_int32_t the_bit) { + u_int32_t idx = the_bit % selector->num_bits; + + if(BITMASK_ISSET(idx, selector)) { + bitmask_counter_list *head = selector->clashes; + + printk("BITMASK: bit %u was already set\n", the_bit); + + while(head != NULL) { + if(head->bit_id == the_bit) { + head->bit_counter++; + printk("BITMASK: bit %u is now set to %d\n", the_bit, head->bit_counter); + return; + } + + head = head->next; + } + + head = kmalloc(sizeof(bitmask_counter_list), GFP_KERNEL); + if(head) { + head->bit_id = the_bit; + head->bit_counter = 1 /* previous value */ + 1 /* the requested set */; + head->next = selector->clashes; + selector->clashes = head; + } else { + printk("BITMASK: not enough memory\n"); + return; + } + } else { + BITMASK_SET(idx, selector); + printk("BITMASK: bit %u is now set\n", the_bit); + } +} + +/* ********************************** */ + +static u_char is_set_bit_bitmask(bitmask_selector *selector, u_int32_t the_bit) { + u_int32_t idx = the_bit % selector->num_bits; + return(BITMASK_ISSET(idx, selector)); +} + +/* ********************************** */ + +static void clear_bit_bitmask(bitmask_selector *selector, u_int32_t the_bit) { + u_int32_t idx = the_bit % selector->num_bits; + + if(!BITMASK_ISSET(idx, selector)) + printk("BITMASK: bit %u was not set\n", the_bit); + else { + bitmask_counter_list *head = selector->clashes, *prev = NULL; + + while(head != NULL) { + if(head->bit_id == the_bit) { + head->bit_counter--; + + printk("BITMASK: bit %u is now set to %d\n", + the_bit, head->bit_counter); + + if(head->bit_counter == 1) { + /* We can now delete this entry as '1' can be + accommodated into the bitmask */ + + if(prev == NULL) + selector->clashes = head->next; + else + prev->next = head->next; + + kfree(head); + } + return; + } + + prev = head; head = head->next; + } + + BITMASK_CLR(idx, selector); + printk("BITMASK: bit %u is now reset\n", the_bit); + } +} + +/* ********************************** */ + +/* Hash function */ +static u_int32_t sdb_hash(u_int32_t value) { + u_int32_t hash = 0, i; + u_int8_t str[sizeof(value)]; + + memcpy(str, &value, sizeof(value)); + + for(i = 0; i < sizeof(value); i++) { + hash = str[i] + (hash << 6) + (hash << 16) - hash; + } + + return(hash); +} + +/* ********************************** */ + +static void handle_bloom_filter_rule(struct ring_opt *pfr, char *buf) { + u_int count; + + if(buf == NULL) + return; + else + count = strlen(buf); + + printk("PF_RING: -> handle_bloom_filter_rule(%s)\n", buf); + + if((buf[count-1] == '\n') || (buf[count-1] == '\r')) buf[count-1] = '\0'; + + if(count > 1) { + u_int32_t the_bit; + + if(!strncmp(&buf[1], "vlan=", 5)) { + sscanf(&buf[6], "%d", &the_bit); + + if(buf[0] == '+') + set_bit_bitmask(&pfr->vlan_bitmask, the_bit), pfr->num_vlan_bitmask_add++; + else + clear_bit_bitmask(&pfr->vlan_bitmask, the_bit), pfr->num_vlan_bitmask_remove++; + } else if(!strncmp(&buf[1], "mac=", 4)) { + int a, b, c, d, e, f; + + if(sscanf(&buf[5], "%02x:%02x:%02x:%02x:%02x:%02x:", + &a, &b, &c, &d, &e, &f) == 6) { + u_int32_t mac_addr = (a & 0xff) + (b & 0xff) + ((c & 0xff) << 24) + ((d & 0xff) << 16) + ((e & 0xff) << 8) + (f & 0xff); + + /* printk("PF_RING: -> [%u][%u][%u][%u][%u][%u] -> [%u]\n", a, b, c, d, e, f, mac_addr); */ + + if(buf[0] == '+') + set_bit_bitmask(&pfr->mac_bitmask, mac_addr), pfr->num_mac_bitmask_add++; + else + clear_bit_bitmask(&pfr->mac_bitmask, mac_addr), pfr->num_mac_bitmask_remove++; + } else + printk("PF_RING: -> Invalid MAC address '%s'\n", &buf[5]); + } else if(!strncmp(&buf[1], "ip=", 3)) { + int a, b, c, d; + + if(sscanf(&buf[4], "%d.%d.%d.%d", &a, &b, &c, &d) == 4) { + u_int32_t ip_addr = ((a & 0xff) << 24) + ((b & 0xff) << 16) + ((c & 0xff) << 8) + (d & 0xff); + + if(buf[0] == '+') + set_bit_bitmask(&pfr->ip_bitmask, ip_addr), set_bit_bitmask(&pfr->ip_bitmask, sdb_hash(ip_addr)), pfr->num_ip_bitmask_add++; + else + clear_bit_bitmask(&pfr->ip_bitmask, ip_addr), clear_bit_bitmask(&pfr->twin_ip_bitmask, sdb_hash(ip_addr)), pfr->num_ip_bitmask_remove++; + } else + printk("PF_RING: -> Invalid IP address '%s'\n", &buf[4]); + } else if(!strncmp(&buf[1], "port=", 5)) { + sscanf(&buf[6], "%d", &the_bit); + + if(buf[0] == '+') + set_bit_bitmask(&pfr->port_bitmask, the_bit), set_bit_bitmask(&pfr->port_bitmask, sdb_hash(the_bit)), pfr->num_port_bitmask_add++; + else + clear_bit_bitmask(&pfr->port_bitmask, the_bit), clear_bit_bitmask(&pfr->twin_port_bitmask, sdb_hash(the_bit)), pfr->num_port_bitmask_remove++; + } else if(!strncmp(&buf[1], "proto=", 6)) { + if(!strncmp(&buf[7], "tcp", 3)) the_bit = 6; + else if(!strncmp(&buf[7], "udp", 3)) the_bit = 17; + else if(!strncmp(&buf[7], "icmp", 4)) the_bit = 1; + else sscanf(&buf[7], "%d", &the_bit); + + if(buf[0] == '+') + set_bit_bitmask(&pfr->proto_bitmask, the_bit); + else + clear_bit_bitmask(&pfr->proto_bitmask, the_bit); + } else + printk("PF_RING: -> Unknown rule type '%s'\n", buf); + } +} + +/* ********************************** */ + +static void reset_bloom_filters(struct ring_opt *pfr) { + reset_bitmask(&pfr->mac_bitmask); + reset_bitmask(&pfr->vlan_bitmask); + reset_bitmask(&pfr->ip_bitmask); reset_bitmask(&pfr->twin_ip_bitmask); + reset_bitmask(&pfr->port_bitmask); reset_bitmask(&pfr->twin_port_bitmask); + reset_bitmask(&pfr->proto_bitmask); + + pfr->num_mac_bitmask_add = pfr->num_mac_bitmask_remove = 0; + pfr->num_vlan_bitmask_add = pfr->num_vlan_bitmask_remove = 0; + pfr->num_ip_bitmask_add = pfr->num_ip_bitmask_remove = 0; + pfr->num_port_bitmask_add = pfr->num_port_bitmask_remove = 0; + pfr->num_proto_bitmask_add = pfr->num_proto_bitmask_remove = 0; + + printk("PF_RING: rules have been reset\n"); +} + +/* ********************************** */ + +static void init_blooms(struct ring_opt *pfr) { + alloc_bitmask(4096, &pfr->mac_bitmask); + alloc_bitmask(4096, &pfr->vlan_bitmask); + alloc_bitmask(32768, &pfr->ip_bitmask); alloc_bitmask(32768, &pfr->twin_ip_bitmask); + alloc_bitmask(4096, &pfr->port_bitmask); alloc_bitmask(4096, &pfr->twin_port_bitmask); + alloc_bitmask(4096, &pfr->proto_bitmask); + + pfr->num_mac_bitmask_add = pfr->num_mac_bitmask_remove = 0; + pfr->num_vlan_bitmask_add = pfr->num_vlan_bitmask_remove = 0; + pfr->num_ip_bitmask_add = pfr->num_ip_bitmask_remove = 0; + pfr->num_port_bitmask_add = pfr->num_port_bitmask_remove = 0; + pfr->num_proto_bitmask_add = pfr->num_proto_bitmask_remove = 0; + + reset_bloom_filters(pfr); +} + +/* ********************************** */ + +inline int MatchFound (void* id, int index, void *data) { return(0); } + +/* ********************************** */ + +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, fwd_pkt = 0; + + 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) { + /* Patch below courtesy of Matthew J. Roth */ +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,18)) + spin_lock_bh(&pfr->reflector_dev->xmit_lock); + pfr->reflector_dev->xmit_lock_owner = cpu; + spin_unlock_bh(&pfr->reflector_dev->xmit_lock); +#else + netif_tx_lock_bh(pfr->reflector_dev); +#endif + if (pfr->reflector_dev->hard_start_xmit(skb, pfr->reflector_dev) == 0) { +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,18)) + spin_lock_bh(&pfr->reflector_dev->xmit_lock); + pfr->reflector_dev->xmit_lock_owner = -1; + spin_unlock_bh(&pfr->reflector_dev->xmit_lock); +#else + netif_tx_unlock_bh(pfr->reflector_dev); +#endif + skb->data += displ; +#if defined(RING_DEBUG) + printk("++ hard_start_xmit succeeded\n"); +#endif + return; /* OK */ + } + +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,18)) + spin_lock_bh(&pfr->reflector_dev->xmit_lock); + pfr->reflector_dev->xmit_lock_owner = -1; + spin_unlock_bh(&pfr->reflector_dev->xmit_lock); +#else + netif_tx_unlock_bh(pfr->reflector_dev); +#endif + } + +#if defined(RING_DEBUG) + printk("++ hard_start_xmit failed\n"); +#endif + skb->data += displ; + return; /* -ENETDOWN */ + } + + /* ************************************* */ + +#if defined(RING_DEBUG) + printk("add_skb_to_ring(skb) [len=%d][tot=%llu][insertIdx=%d]" + "[pkt_type=%d][cloned=%d]\n", + (int)skb->len, pfr->slots_info->tot_pkts, + pfr->slots_info->insert_idx, + skb->pkt_type, skb->cloned); +#endif + + idx = pfr->slots_info->insert_idx; + theSlot = get_insert_slot(pfr); + + if((theSlot != NULL) && (theSlot->slot_state == 0)) { + struct pcap_pkthdr *hdr; + char *bucket; + int is_ip_pkt, debug = 0; + + /* Update Index */ + idx++; + + bucket = &theSlot->bucket; + hdr = (struct pcap_pkthdr*)bucket; + + /* BD - API changed for time keeping */ +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,14)) + if(skb->stamp.tv_sec == 0) do_gettimeofday(&skb->stamp); + + hdr->ts.tv_sec = skb->stamp.tv_sec, hdr->ts.tv_usec = skb->stamp.tv_usec; +#else + if(skb->tstamp.off_sec == 0) __net_timestamp(skb); + + hdr->ts.tv_sec = skb->tstamp.off_sec, hdr->ts.tv_usec = skb->tstamp.off_usec; +#endif + hdr->caplen = skb->len+displ; + + if(hdr->caplen > pfr->slots_info->data_len) + hdr->caplen = pfr->slots_info->data_len; + + hdr->len = skb->len+displ; + + /* Extensions */ + is_ip_pkt = parse_pkt(skb, displ, + &hdr->l3_proto, + &hdr->eth_type, + &hdr->l3_offset, + &hdr->l4_offset, + &hdr->vlan_id, + &hdr->ipv4_src, + &hdr->ipv4_dst, + &hdr->l4_src_port, + &hdr->l4_dst_port, + &hdr->payload_offset); + + if(is_ip_pkt && pfr->bitmask_enabled) { + int vlan_match = 0; + + fwd_pkt = 0; + + if(debug) { + if(is_ip_pkt) + printk(KERN_INFO "PF_RING: [proto=%d][vlan=%d][sport=%d][dport=%d][src=%u][dst=%u]\n", + hdr->l3_proto, hdr->vlan_id, hdr->l4_src_port, hdr->l4_dst_port, hdr->ipv4_src, hdr->ipv4_dst); + else + printk(KERN_INFO "PF_RING: [proto=%d][vlan=%d]\n", hdr->l3_proto, hdr->vlan_id); + } + + if(hdr->vlan_id != (u_int16_t)-1) { + vlan_match = is_set_bit_bitmask(&pfr->vlan_bitmask, hdr->vlan_id); + } else + vlan_match = 1; + + if(vlan_match) { + struct ethhdr *eh = (struct ethhdr*)(skb->data); + u_int32_t src_mac = (eh->h_source[0] & 0xff) + (eh->h_source[1] & 0xff) + ((eh->h_source[2] & 0xff) << 24) + + ((eh->h_source[3] & 0xff) << 16) + ((eh->h_source[4] & 0xff) << 8) + (eh->h_source[5] & 0xff); + + if(debug) printk(KERN_INFO "PF_RING: [src_mac=%u]\n", src_mac); + + fwd_pkt |= is_set_bit_bitmask(&pfr->mac_bitmask, src_mac); + + if(!fwd_pkt) { + u_int32_t dst_mac = (eh->h_dest[0] & 0xff) + (eh->h_dest[1] & 0xff) + ((eh->h_dest[2] & 0xff) << 24) + + ((eh->h_dest[3] & 0xff) << 16) + ((eh->h_dest[4] & 0xff) << 8) + (eh->h_dest[5] & 0xff); + + if(debug) printk(KERN_INFO "PF_RING: [dst_mac=%u]\n", dst_mac); + + fwd_pkt |= is_set_bit_bitmask(&pfr->mac_bitmask, dst_mac); + + if(is_ip_pkt && (!fwd_pkt)) { + fwd_pkt |= is_set_bit_bitmask(&pfr->ip_bitmask, hdr->ipv4_src); + + if(!fwd_pkt) { + fwd_pkt |= is_set_bit_bitmask(&pfr->ip_bitmask, hdr->ipv4_dst); + + if((!fwd_pkt) && ((hdr->l3_proto == IPPROTO_TCP) + || (hdr->l3_proto == IPPROTO_UDP))) { + fwd_pkt |= is_set_bit_bitmask(&pfr->port_bitmask, hdr->l4_src_port); + if(!fwd_pkt) fwd_pkt |= is_set_bit_bitmask(&pfr->port_bitmask, hdr->l4_dst_port); + } + + if(!fwd_pkt) fwd_pkt |= is_set_bit_bitmask(&pfr->proto_bitmask, hdr->l3_proto); + } + } + } + } + } else + fwd_pkt = 1; + + if(fwd_pkt && (pfr->acsm != NULL)) { + if((hdr->payload_offset > 0) && ((skb->len+skb->mac_len) > hdr->payload_offset)) { + char *payload = (skb->data-displ+hdr->payload_offset); + int payload_len = skb->len /* + skb->mac_len */ - hdr->payload_offset; + + if((payload_len > 0) + && ((hdr->l4_src_port == 80) || (hdr->l4_dst_port == 80))) { + int rc; + + if(0) { + char buf[1500]; + + memcpy(buf, payload, payload_len); + buf[payload_len] = '\0'; + printk("[%s]\n", payload); + } + + /* printk("Tring to match pattern [len=%d][%s]\n", payload_len, payload); */ + rc = acsmSearch2(pfr->acsm, payload, payload_len, MatchFound, (void *)0) ? 1 : 0; + + // printk("Match result: %d\n", fwd_pkt); + if(rc) { + printk("Pattern matched!\n"); + } else { + fwd_pkt = 0; + } + } else + fwd_pkt = 0; + } else + fwd_pkt = 0; + } + + if(fwd_pkt) { + memcpy(&bucket[sizeof(struct pcap_pkthdr)], skb->data-displ, hdr->caplen); + +#if defined(RING_DEBUG) + { + static unsigned int lastLoss = 0; + + if(pfr->slots_info->tot_lost + && (lastLoss != pfr->slots_info->tot_lost)) { + printk("add_skb_to_ring(%d): [data_len=%d]" + "[hdr.caplen=%d][skb->len=%d]" + "[pcap_pkthdr=%d][removeIdx=%d]" + "[loss=%lu][page=%u][slot=%u]\n", + idx-1, pfr->slots_info->data_len, hdr->caplen, skb->len, + sizeof(struct pcap_pkthdr), + pfr->slots_info->remove_idx, + (long unsigned int)pfr->slots_info->tot_lost, + pfr->insert_page_id, pfr->insert_slot_id); + + lastLoss = pfr->slots_info->tot_lost; + } + } +#endif + + write_lock(&pfr->ring_index_lock); + if(idx == pfr->slots_info->tot_slots) + pfr->slots_info->insert_idx = 0; + else + pfr->slots_info->insert_idx = idx; + + 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 + } + + if(fwd_pkt) { + + /* wakeup in case of poll() */ + if(waitqueue_active(&pfr->ring_slots_waitqueue)) + wake_up_interruptible(&pfr->ring_slots_waitqueue); + } +} + +/* ********************************** */ + +static u_int hash_skb(struct ring_cluster *cluster_ptr, + struct sk_buff *skb, u_char recv_packet) { + u_int idx; + int displ; + struct iphdr *ip; + + if(cluster_ptr->hashing_mode == cluster_round_robin) { + idx = cluster_ptr->hashing_id++; + } else { + /* Per-flow clustering */ + if(skb->len > sizeof(struct iphdr)+sizeof(struct tcphdr)) { + if(recv_packet) + displ = 0; + else + displ = SKB_DISPLACEMENT; + + /* + skb->data+displ + + Always points to to the IP part of the packet + */ + + ip = (struct iphdr*)(skb->data+displ); + + idx = ip->saddr+ip->daddr+ip->protocol; + + if(ip->protocol == IPPROTO_TCP) { + struct tcphdr *tcp = (struct tcphdr*)(skb->data+displ + +sizeof(struct iphdr)); + idx += tcp->source+tcp->dest; + } else if(ip->protocol == IPPROTO_UDP) { + struct udphdr *udp = (struct udphdr*)(skb->data+displ + +sizeof(struct iphdr)); + idx += udp->source+udp->dest; + } + } else + idx = skb->len; + } + + return(idx % cluster_ptr->num_cluster_elements); +} + +/* ********************************** */ + +static int skb_ring_handler(struct sk_buff *skb, + u_char recv_packet, + u_char real_skb /* 1=skb 0=faked skb */) { + struct sock *skElement; + int rc = 0; + struct list_head *ptr; + struct ring_cluster *cluster_ptr; + +#ifdef PROFILING + uint64_t rdt = _rdtsc(), rdt1, rdt2; +#endif + + if((!skb) /* Invalid skb */ + || ((!enable_tx_capture) && (!recv_packet))) { + /* + An outgoing packet is about to be sent out + but we decided not to handle transmitted + packets. + */ + return(0); + } + +#if defined(RING_DEBUG) + if(0) { + printk("skb_ring_handler() [len=%d][dev=%s]\n", skb->len, + skb->dev->name == NULL ? "" : skb->dev->name); + } +#endif + +#ifdef PROFILING + rdt1 = _rdtsc(); +#endif + + /* [1] Check unclustered sockets */ + for (ptr = ring_table.next; ptr != &ring_table; ptr = ptr->next) { + struct ring_opt *pfr; + struct ring_element *entry; + + entry = list_entry(ptr, struct ring_element, list); + + read_lock(&ring_mgmt_lock); + skElement = entry->sk; + pfr = ring_sk(skElement); + read_unlock(&ring_mgmt_lock); + + if((pfr != NULL) + && (pfr->cluster_id == 0 /* No cluster */) + && (pfr->ring_slots != NULL) + && ((pfr->ring_netdev == skb->dev) || ((skb->dev->flags & IFF_SLAVE) && pfr->ring_netdev == skb->dev->master))) { + /* 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) || ((skb->dev->flags & IFF_SLAVE) && pfr->ring_netdev == skb->dev->master))) { + /* We've found the ring where the packet can be stored */ + read_lock(&ring_mgmt_lock); + add_skb_to_ring(skb, pfr, recv_packet, real_skb); + read_unlock(&ring_mgmt_lock); + + rc = 1; /* Ring found: we've done our job */ + } + } + } + + cluster_ptr = cluster_ptr->next; + } + +#ifdef PROFILING + rdt1 = _rdtsc()-rdt1; +#endif + +#ifdef PROFILING + rdt2 = _rdtsc(); +#endif + + if(transparent_mode) rc = 0; + + if((rc != 0) && real_skb) + dev_kfree_skb(skb); /* Free the skb */ + +#ifdef PROFILING + rdt2 = _rdtsc()-rdt2; + rdt = _rdtsc()-rdt; + +#if defined(RING_DEBUG) + printk("# cycles: %d [lock costed %d %d%%][free costed %d %d%%]\n", + (int)rdt, rdt-rdt1, + (int)((float)((rdt-rdt1)*100)/(float)rdt), + rdt2, + (int)((float)(rdt2*100)/(float)rdt)); +#endif +#endif + + return(rc); /* 0 = packet not handled */ +} + +/* ********************************** */ + +struct sk_buff skb; + +static int buffer_ring_handler(struct net_device *dev, + char *data, int len) { + +#if defined(RING_DEBUG) + printk("buffer_ring_handler: [dev=%s][len=%d]\n", + dev->name == NULL ? "" : dev->name, len); +#endif + + /* BD - API changed for time keeping */ +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,14)) + skb.dev = dev, skb.len = len, skb.data = data, + skb.data_len = len, skb.stamp.tv_sec = 0; /* Calculate the time */ +#else + skb.dev = dev, skb.len = len, skb.data = data, + skb.data_len = len, skb.tstamp.off_sec = 0; /* Calculate the time */ +#endif + + skb_ring_handler(&skb, 1, 0 /* fake skb */); + + return(0); +} + +/* ********************************** */ + +static int ring_create(struct socket *sock, int protocol) { + struct sock *sk; + struct ring_opt *pfr; + int err; + +#if defined(RING_DEBUG) + printk("RING: ring_create()\n"); +#endif + + /* Are you root, superuser or so ? */ + if(!capable(CAP_NET_ADMIN)) + return -EPERM; + + if(sock->type != SOCK_RAW) + return -ESOCKTNOSUPPORT; + + if(protocol != htons(ETH_P_ALL)) + return -EPROTONOSUPPORT; + +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0)) + MOD_INC_USE_COUNT; +#endif + + err = -ENOMEM; + + // BD: -- broke this out to keep it more simple and clear as to what the + // options are. +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0)) +#if (LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,11)) + sk = sk_alloc(PF_RING, GFP_KERNEL, 1, NULL); +#else + // BD: API changed in 2.6.12, ref: + // http://svn.clkao.org/svnweb/linux/revision/?rev=28201 + sk = sk_alloc(PF_RING, GFP_ATOMIC, &ring_proto, 1); +#endif +#else + /* Kernel 2.4 */ + sk = sk_alloc(PF_RING, GFP_KERNEL, 1); +#endif + + if (sk == NULL) + goto out; + + sock->ops = &ring_ops; + sock_init_data(sock, sk); +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0)) +#if (LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,11)) + sk_set_owner(sk, THIS_MODULE); +#endif +#endif + + err = -ENOMEM; + ring_sk(sk) = ring_sk_datatype(kmalloc(sizeof(*pfr), GFP_KERNEL)); + + if (!(pfr = ring_sk(sk))) { + sk_free(sk); + goto out; + } + memset(pfr, 0, sizeof(*pfr)); + init_waitqueue_head(&pfr->ring_slots_waitqueue); + pfr->ring_index_lock = RW_LOCK_UNLOCKED; + atomic_set(&pfr->num_ring_slots_waiters, 0); + init_blooms(pfr); + pfr->acsm = NULL; + +#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 + + /* + The calls below must be placed outside the + write_lock_irq...write_unlock_irq block. + */ + sock_orphan(sk); + ring_proc_remove(ring_sk(sk)); + + write_lock_irq(&ring_mgmt_lock); + ring_remove(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); + } + + free_bitmask(&pfr->mac_bitmask); + free_bitmask(&pfr->vlan_bitmask); + free_bitmask(&pfr->ip_bitmask); free_bitmask(&pfr->twin_ip_bitmask); + free_bitmask(&pfr->port_bitmask); free_bitmask(&pfr->twin_port_bitmask); + free_bitmask(&pfr->proto_bitmask); + + if(pfr->acsm != NULL) acsmFree2(pfr->acsm); + + 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); + write_unlock_irq(&ring_mgmt_lock); + +#if defined(RING_DEBUG) + printk("RING: ring_release leaving\n"); +#endif + + return 0; +} + +/* ********************************** */ +/* + * We create a ring for this socket and bind it to the specified device + */ +static int packet_ring_bind(struct sock *sk, struct net_device *dev) +{ + u_int the_slot_len; + u_int32_t tot_mem; + struct ring_opt *pfr = ring_sk(sk); + struct page *page, *page_end; + + if(!dev) return(-1); + +#if defined(RING_DEBUG) + printk("RING: packet_ring_bind(%s) called\n", dev->name); +#endif + + /* ********************************************** + + ************************************* + * * + * FlowSlotInfo * + * * + ************************************* <-+ + * FlowSlot * | + ************************************* | + * FlowSlot * | + ************************************* +- num_slots + * FlowSlot * | + ************************************* | + * FlowSlot * | + ************************************* <-+ + + ********************************************** */ + + the_slot_len = sizeof(u_char) /* flowSlot.slot_state */ +#ifdef RING_MAGIC + + sizeof(u_char) +#endif + + sizeof(struct pcap_pkthdr) + + bucket_len /* flowSlot.bucket */; + + tot_mem = sizeof(FlowSlotInfo) + num_slots*the_slot_len; + + /* + Calculate the value of the order parameter used later. + See http://www.linuxjournal.com/article.php?sid=1133 + */ + for(pfr->order = 0;(PAGE_SIZE << pfr->order) < tot_mem; pfr->order++) ; + + /* + We now try to allocate the memory as required. If we fail + we try to allocate a smaller amount or memory (hence a + smaller ring). + */ + while((pfr->ring_memory = __get_free_pages(GFP_ATOMIC, pfr->order)) == 0) + if(pfr->order-- == 0) + break; + + if(pfr->order == 0) { + printk("RING: ERROR not enough memory for ring\n"); + return(-1); + } else { + printk("RING: succesfully allocated %lu KB [tot_mem=%d][order=%ld]\n", + PAGE_SIZE >> (10 - pfr->order), tot_mem, pfr->order); + } + + tot_mem = PAGE_SIZE << pfr->order; + memset((char*)pfr->ring_memory, 0, tot_mem); + + /* Now we need to reserve the pages */ + page_end = virt_to_page(pfr->ring_memory + (PAGE_SIZE << pfr->order) - 1); + for(page = virt_to_page(pfr->ring_memory); page <= page_end; page++) + SetPageReserved(page); + + pfr->slots_info = (FlowSlotInfo*)pfr->ring_memory; + pfr->ring_slots = (char*)(pfr->ring_memory+sizeof(FlowSlotInfo)); + + pfr->slots_info->version = RING_FLOWSLOT_VERSION; + pfr->slots_info->slot_len = the_slot_len; + pfr->slots_info->data_len = bucket_len; + pfr->slots_info->tot_slots = (tot_mem-sizeof(FlowSlotInfo))/the_slot_len; + pfr->slots_info->tot_mem = tot_mem; + pfr->slots_info->sample_rate = sample_rate; + + printk("RING: allocated %d slots [slot_len=%d][tot_mem=%u]\n", + pfr->slots_info->tot_slots, pfr->slots_info->slot_len, + pfr->slots_info->tot_mem); + +#ifdef RING_MAGIC + { + int i; + + for(i=0; islots_info->tot_slots; i++) { + unsigned long idx = i*pfr->slots_info->slot_len; + FlowSlot *slot = (FlowSlot*)&pfr->ring_slots[idx]; + slot->magic = RING_MAGIC_VALUE; slot->slot_state = 0; + } + } +#endif + + pfr->insert_page_id = 1, pfr->insert_slot_id = 0; + + /* + IMPORTANT + Leave this statement here as last one. In fact when + the ring_netdev != NULL the socket is ready to be used. + */ + pfr->ring_netdev = dev; + + return(0); +} + +/* ************************************* */ + +/* Bind to a device */ +static int ring_bind(struct socket *sock, + struct sockaddr *sa, int addr_len) +{ + struct sock *sk=sock->sk; + struct net_device *dev = NULL; + +#if defined(RING_DEBUG) + printk("RING: ring_bind() called\n"); +#endif + + /* + * Check legality + */ + if (addr_len != sizeof(struct sockaddr)) + return -EINVAL; + if (sa->sa_family != PF_RING) + return -EINVAL; + + /* Safety check: add trailing zero if missing */ + sa->sa_data[sizeof(sa->sa_data)-1] = '\0'; + +#if defined(RING_DEBUG) + printk("RING: searching device %s\n", sa->sa_data); +#endif + + if((dev = __dev_get_by_name(sa->sa_data)) == NULL) { +#if defined(RING_DEBUG) + printk("RING: search failed\n"); +#endif + return(-EINVAL); + } else + return(packet_ring_bind(sk, dev)); +} + +/* ************************************* */ + +static int ring_mmap(struct file *file, + struct socket *sock, + struct vm_area_struct *vma) +{ + struct sock *sk = sock->sk; + struct ring_opt *pfr = ring_sk(sk); + unsigned long size, start; + u_int pagesToMap; + char *ptr; + +#if defined(RING_DEBUG) + printk("RING: ring_mmap() called\n"); +#endif + + if(pfr->ring_memory == 0) { +#if defined(RING_DEBUG) + printk("RING: ring_mmap() failed: mapping area to an unbound socket\n"); +#endif + return -EINVAL; + } + + size = (unsigned long)(vma->vm_end-vma->vm_start); + + if(size % PAGE_SIZE) { +#if defined(RING_DEBUG) + printk("RING: ring_mmap() failed: len is not multiple of PAGE_SIZE\n"); +#endif + return(-EINVAL); + } + + /* if userspace tries to mmap beyond end of our buffer, fail */ + if(size > pfr->slots_info->tot_mem) { +#if defined(RING_DEBUG) + printk("proc_mmap() failed: area too large [%ld > %d]\n", size, pfr->slots_info->tot_mem); +#endif + return(-EINVAL); + } + + pagesToMap = size/PAGE_SIZE; + +#if defined(RING_DEBUG) + printk("RING: ring_mmap() called. %d pages to map\n", pagesToMap); +#endif + +#if defined(RING_DEBUG) + printk("RING: mmap [slot_len=%d][tot_slots=%d] for ring on device %s\n", + pfr->slots_info->slot_len, pfr->slots_info->tot_slots, + pfr->ring_netdev->name); +#endif + + /* we do not want to have this area swapped out, lock it */ + vma->vm_flags |= VM_LOCKED; + start = vma->vm_start; + + /* Ring slots start from page 1 (page 0 is reserved for FlowSlotInfo) */ + ptr = (char*)(start+PAGE_SIZE); + + if(remap_page_range( +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0)) + vma, +#endif + start, + __pa(pfr->ring_memory), + PAGE_SIZE*pagesToMap, vma->vm_page_prot)) { +#if defined(RING_DEBUG) + printk("remap_page_range() failed\n"); +#endif + return(-EAGAIN); + } + +#if defined(RING_DEBUG) + printk("proc_mmap(pagesToMap=%d): success.\n", pagesToMap); +#endif + + return 0; +} + +/* ************************************* */ + +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0)) +static int ring_recvmsg(struct kiocb *iocb, struct socket *sock, + struct msghdr *msg, size_t len, int flags) +#else + static int ring_recvmsg(struct socket *sock, struct msghdr *msg, int len, + int flags, struct scm_cookie *scm) +#endif +{ + FlowSlot* slot; + struct ring_opt *pfr = ring_sk(sock->sk); + u_int32_t queued_pkts, num_loops = 0; + +#if defined(RING_DEBUG) + printk("ring_recvmsg called\n"); +#endif + + slot = get_remove_slot(pfr); + + while((queued_pkts = num_queued_pkts(pfr)) < MIN_QUEUED_PKTS) { + wait_event_interruptible(pfr->ring_slots_waitqueue, 1); + +#if defined(RING_DEBUG) + printk("-> ring_recvmsg returning %d [queued_pkts=%d][num_loops=%d]\n", + slot->slot_state, queued_pkts, num_loops); +#endif + + if(queued_pkts > 0) { + if(num_loops++ > MAX_QUEUE_LOOPS) + break; + } + } + +#if defined(RING_DEBUG) + if(slot != NULL) + printk("ring_recvmsg is returning [queued_pkts=%d][num_loops=%d]\n", + queued_pkts, num_loops); +#endif + + return(queued_pkts); +} + +/* ************************************* */ + +unsigned int ring_poll(struct file * file, + struct socket *sock, poll_table *wait) +{ + FlowSlot* slot; + struct ring_opt *pfr = ring_sk(sock->sk); + +#if defined(RING_DEBUG) + printk("poll called\n"); +#endif + + slot = get_remove_slot(pfr); + + if((slot != NULL) && (slot->slot_state == 0)) + poll_wait(file, &pfr->ring_slots_waitqueue, wait); + +#if defined(RING_DEBUG) + printk("poll returning %d\n", slot->slot_state); +#endif + + if((slot != NULL) && (slot->slot_state == 1)) + return(POLLIN | POLLRDNORM); + else + return(0); +} + +/* ************************************* */ + +int add_to_cluster_list(struct ring_cluster *el, + struct sock *sock) { + + if(el->num_cluster_elements == CLUSTER_LEN) + return(-1); /* Cluster full */ + + ring_sk_datatype(ring_sk(sock))->cluster_id = el->cluster_id; + el->sk[el->num_cluster_elements] = sock; + el->num_cluster_elements++; + return(0); +} + +/* ************************************* */ + +int remove_from_cluster_list(struct ring_cluster *el, + struct sock *sock) { + int i, j; + + for(i=0; isk[i] == sock) { + el->num_cluster_elements--; + + if(el->num_cluster_elements > 0) { + /* The cluster contains other elements */ + for(j=i; jsk[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, do_enable; + char devName[8], bloom_filter[256], aho_pattern[256]; + + if(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; + + case SO_SET_BLOOM: + if(optlen >= (sizeof(bloom_filter)-1)) + return -EINVAL; + + if(optlen > 0) { + if(copy_from_user(bloom_filter, optval, optlen)) + return -EFAULT; + } + + bloom_filter[optlen] = '\0'; + + write_lock(&ring_mgmt_lock); + handle_bloom_filter_rule(pfr, bloom_filter); + write_unlock(&ring_mgmt_lock); + break; + + case SO_SET_STRING: + if(optlen >= (sizeof(aho_pattern)-1)) + return -EINVAL; + + if(optlen > 0) { + if(copy_from_user(aho_pattern, optval, optlen)) + return -EFAULT; + } + + aho_pattern[optlen] = '\0'; + + write_lock(&ring_mgmt_lock); + if(pfr->acsm != NULL) acsmFree2(pfr->acsm); + if(optlen > 0) { +#if 1 + if((pfr->acsm = acsmNew2()) != NULL) { + int nc=1 /* case sensitive */, i = 0; + + pfr->acsm->acsmFormat = ACF_BANDED; + acsmAddPattern2(pfr->acsm, (unsigned char*)aho_pattern, + (int)strlen(aho_pattern), nc, 0, 0,(void*)aho_pattern, i); + acsmCompile2(pfr->acsm); + } +#else + pfr->acsm = kmalloc (10, GFP_KERNEL); /* TEST */ +#endif + } + write_unlock(&ring_mgmt_lock); + break; + + case SO_TOGGLE_BLOOM_STATE: + if(optlen >= (sizeof(bloom_filter)-1)) + return -EINVAL; + + if(optlen > 0) { + if(copy_from_user(&do_enable, optval, optlen)) + return -EFAULT; + } + + write_lock(&ring_mgmt_lock); + if(do_enable) + pfr->bitmask_enabled = 1; + else + pfr->bitmask_enabled = 0; + write_unlock(&ring_mgmt_lock); + printk("SO_TOGGLE_BLOOM_STATE: bloom bitmask %s\n", + pfr->bitmask_enabled ? "enabled" : "disabled"); + break; + + case SO_RESET_BLOOM_FILTERS: + if(optlen >= (sizeof(bloom_filter)-1)) + return -EINVAL; + + if(optlen > 0) { + if(copy_from_user(&do_enable, optval, optlen)) + return -EFAULT; + } + + write_lock(&ring_mgmt_lock); + reset_bloom_filters(pfr); + write_unlock(&ring_mgmt_lock); + 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) + { +#ifdef CONFIG_INET + 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(inet_dgram_ops.ioctl(sock, cmd, arg)); +#endif + + default: + return -ENOIOCTLCMD; + } + + 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); + ring_proc_term(); + printk("PF_RING shut down.\n"); +} + +/* ************************************ */ + +static int __init ring_init(void) +{ + printk("Welcome to PF_RING %s\n(C) 2004-07 L.Deri \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"); + + ring_proc_init(); + 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