diff -Naru linux-2.4.30/include/linux/netdevice.h linux-2.4.30.w18/include/linux/netdevice.h --- linux-2.4.30/include/linux/netdevice.h 2005-05-14 10:52:07.000000000 +0300 +++ linux-2.4.30.w18/include/linux/netdevice.h 2005-05-14 11:35:41.967550648 +0300 @@ -295,7 +295,9 @@ /* List of functions to handle Wireless Extensions (instead of ioctl). * See for details. Jean II */ - struct iw_handler_def * wireless_handlers; + const struct iw_handler_def * wireless_handlers; + /* Instance data managed by the core of Wireless Extensions. */ + struct iw_public_data * wireless_data; #ifndef CONFIG_BCM4710 struct ethtool_ops *ethtool_ops; diff -Naru linux-2.4.30/include/linux/netdevice.h.orig linux-2.4.30.w18/include/linux/netdevice.h.orig --- linux-2.4.30/include/linux/netdevice.h.orig 1970-01-01 02:00:00.000000000 +0200 +++ linux-2.4.30.w18/include/linux/netdevice.h.orig 2005-05-14 10:52:07.000000000 +0300 @@ -0,0 +1,910 @@ +/* + * INET An implementation of the TCP/IP protocol suite for the LINUX + * operating system. INET is implemented using the BSD Socket + * interface as the means of communication with the user level. + * + * Definitions for the Interfaces handler. + * + * Version: @(#)dev.h 1.0.10 08/12/93 + * + * Authors: Ross Biro, + * Fred N. van Kempen, + * Corey Minyard + * Donald J. Becker, + * Alan Cox, + * Bjorn Ekwall. + * Pekka Riikonen + * + * 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. + * + * Moved to /usr/include/linux for NET3 + */ +#ifndef _LINUX_NETDEVICE_H +#define _LINUX_NETDEVICE_H + +#include +#include +#include + +#include +#include +#include + +#ifdef __KERNEL__ +#include +#ifdef CONFIG_NET_PROFILE +#include +#endif + +struct divert_blk; +struct vlan_group; +struct ethtool_ops; + + /* source back-compat hooks */ +#define SET_ETHTOOL_OPS(netdev,ops) \ + ( (netdev)->ethtool_ops = (ops) ) + +#define HAVE_ALLOC_NETDEV /* feature macro: alloc_xxxdev + functions are available. */ +#define HAVE_FREE_NETDEV /* free_netdev() */ +#define HAVE_NETDEV_PRIV /* netdev_priv() */ + +#define NET_XMIT_SUCCESS 0 +#define NET_XMIT_DROP 1 /* skb dropped */ +#define NET_XMIT_CN 2 /* congestion notification */ +#define NET_XMIT_POLICED 3 /* skb is shot by police */ +#define NET_XMIT_BYPASS 4 /* packet does not leave via dequeue; + (TC use only - dev_queue_xmit + returns this as NET_XMIT_SUCCESS) */ + +/* Backlog congestion levels */ +#define NET_RX_SUCCESS 0 /* keep 'em coming, baby */ +#define NET_RX_DROP 1 /* packet dropped */ +#define NET_RX_CN_LOW 2 /* storm alert, just in case */ +#define NET_RX_CN_MOD 3 /* Storm on its way! */ +#define NET_RX_CN_HIGH 4 /* The storm is here */ +#define NET_RX_BAD 5 /* packet dropped due to kernel error */ + +#define net_xmit_errno(e) ((e) != NET_XMIT_CN ? -ENOBUFS : 0) + +#endif + +#define MAX_ADDR_LEN 8 /* Largest hardware address length */ + +/* + * Compute the worst case header length according to the protocols + * used. + */ + +#if !defined(CONFIG_AX25) && !defined(CONFIG_AX25_MODULE) && !defined(CONFIG_TR) +#define LL_MAX_HEADER 32 +#else +#if defined(CONFIG_AX25) || defined(CONFIG_AX25_MODULE) +#define LL_MAX_HEADER 96 +#else +#define LL_MAX_HEADER 48 +#endif +#endif + +#if !defined(CONFIG_NET_IPIP) && \ + !defined(CONFIG_IPV6) && !defined(CONFIG_IPV6_MODULE) +#define MAX_HEADER LL_MAX_HEADER +#else +#define MAX_HEADER (LL_MAX_HEADER + 48) +#endif + +/* + * Network device statistics. Akin to the 2.0 ether stats but + * with byte counters. + */ + +struct net_device_stats +{ + unsigned long rx_packets; /* total packets received */ + unsigned long tx_packets; /* total packets transmitted */ + unsigned long rx_bytes; /* total bytes received */ + unsigned long tx_bytes; /* total bytes transmitted */ + unsigned long rx_errors; /* bad packets received */ + unsigned long tx_errors; /* packet transmit problems */ + unsigned long rx_dropped; /* no space in linux buffers */ + unsigned long tx_dropped; /* no space available in linux */ + unsigned long multicast; /* multicast packets received */ + unsigned long collisions; + + /* detailed rx_errors: */ + unsigned long rx_length_errors; + unsigned long rx_over_errors; /* receiver ring buff overflow */ + unsigned long rx_crc_errors; /* recved pkt with crc error */ + unsigned long rx_frame_errors; /* recv'd frame alignment error */ + unsigned long rx_fifo_errors; /* recv'r fifo overrun */ + unsigned long rx_missed_errors; /* receiver missed packet */ + + /* detailed tx_errors */ + unsigned long tx_aborted_errors; + unsigned long tx_carrier_errors; + unsigned long tx_fifo_errors; + unsigned long tx_heartbeat_errors; + unsigned long tx_window_errors; + + /* for cslip etc */ + unsigned long rx_compressed; + unsigned long tx_compressed; +}; + + +/* Media selection options. */ +enum { + IF_PORT_UNKNOWN = 0, + IF_PORT_10BASE2, + IF_PORT_10BASET, + IF_PORT_AUI, + IF_PORT_100BASET, + IF_PORT_100BASETX, + IF_PORT_100BASEFX +}; + +#ifdef __KERNEL__ + +extern const char *if_port_text[]; + +#include +#include + +struct neighbour; +struct neigh_parms; +struct sk_buff; + +struct netif_rx_stats +{ + unsigned total; + unsigned dropped; + unsigned time_squeeze; + unsigned throttled; + unsigned fastroute_hit; + unsigned fastroute_success; + unsigned fastroute_defer; + unsigned fastroute_deferred_out; + unsigned fastroute_latency_reduction; + unsigned cpu_collision; +} ____cacheline_aligned; + +extern struct netif_rx_stats netdev_rx_stat[]; + + +/* + * We tag multicasts with these structures. + */ + +struct dev_mc_list +{ + struct dev_mc_list *next; + __u8 dmi_addr[MAX_ADDR_LEN]; + unsigned char dmi_addrlen; + int dmi_users; + int dmi_gusers; +}; + +struct hh_cache +{ + struct hh_cache *hh_next; /* Next entry */ + atomic_t hh_refcnt; /* number of users */ + unsigned short hh_type; /* protocol identifier, f.e ETH_P_IP + * NOTE: For VLANs, this will be the + * encapuslated type. --BLG + */ + int hh_len; /* length of header */ + int (*hh_output)(struct sk_buff *skb); + rwlock_t hh_lock; + + /* cached hardware header; allow for machine alignment needs. */ +#define HH_DATA_MOD 16 +#define HH_DATA_OFF(__len) \ + (HH_DATA_MOD - ((__len) & (HH_DATA_MOD - 1))) +#define HH_DATA_ALIGN(__len) \ + (((__len)+(HH_DATA_MOD-1))&~(HH_DATA_MOD - 1)) + unsigned long hh_data[HH_DATA_ALIGN(LL_MAX_HEADER) / sizeof(long)]; +}; + +/* These flag bits are private to the generic network queueing + * layer, they may not be explicitly referenced by any other + * code. + */ + +enum netdev_state_t +{ + __LINK_STATE_XOFF=0, + __LINK_STATE_START, + __LINK_STATE_PRESENT, + __LINK_STATE_SCHED, + __LINK_STATE_NOCARRIER, + __LINK_STATE_RX_SCHED +}; + + +/* + * This structure holds at boot time configured netdevice settings. They + * are then used in the device probing. + */ +struct netdev_boot_setup { + char name[IFNAMSIZ]; + struct ifmap map; +}; +#define NETDEV_BOOT_SETUP_MAX 8 + + +/* + * The DEVICE structure. + * Actually, this whole structure is a big mistake. It mixes I/O + * data with strictly "high-level" data, and it has to know about + * almost every data structure used in the INET module. + * + * FIXME: cleanup struct net_device such that network protocol info + * moves out. + */ + +struct net_device +{ + + /* + * This is the first field of the "visible" part of this structure + * (i.e. as seen by users in the "Space.c" file). It is the name + * the interface. + */ + char name[IFNAMSIZ]; + + /* + * I/O specific fields + * FIXME: Merge these and struct ifmap into one + */ + unsigned long rmem_end; /* shmem "recv" end */ + unsigned long rmem_start; /* shmem "recv" start */ + unsigned long mem_end; /* shared mem end */ + unsigned long mem_start; /* shared mem start */ + unsigned long base_addr; /* device I/O address */ + unsigned int irq; /* device IRQ number */ + + /* + * Some hardware also needs these fields, but they are not + * part of the usual set specified in Space.c. + */ + + unsigned char if_port; /* Selectable AUI, TP,..*/ + unsigned char dma; /* DMA channel */ + + unsigned long state; + + struct net_device *next; + + /* The device initialization function. Called only once. */ + int (*init)(struct net_device *dev); + + /* ------- Fields preinitialized in Space.c finish here ------- */ + + struct net_device *next_sched; + + /* Interface index. Unique device identifier */ + int ifindex; + int iflink; + + + struct net_device_stats* (*get_stats)(struct net_device *dev); + struct iw_statistics* (*get_wireless_stats)(struct net_device *dev); + + /* List of functions to handle Wireless Extensions (instead of ioctl). + * See for details. Jean II */ + struct iw_handler_def * wireless_handlers; + +#ifndef CONFIG_BCM4710 + struct ethtool_ops *ethtool_ops; +#endif + + + /* + * This marks the end of the "visible" part of the structure. All + * fields hereafter are internal to the system, and may change at + * will (read: may be cleaned up at will). + */ + + /* These may be needed for future network-power-down code. */ + unsigned long trans_start; /* Time (in jiffies) of last Tx */ + unsigned long last_rx; /* Time of last Rx */ + + unsigned short flags; /* interface flags (a la BSD) */ + unsigned short gflags; + unsigned short priv_flags; /* Like 'flags' but invisible to userspace. */ + unsigned short unused_alignment_fixer; /* Because we need priv_flags, + * and we want to be 32-bit aligned. + */ + + unsigned mtu; /* interface MTU value */ + unsigned short type; /* interface hardware type */ + unsigned short hard_header_len; /* hardware hdr length */ + void *priv; /* pointer to private data */ + + struct net_device *master; /* Pointer to master device of a group, + * which this device is member of. + */ + + /* Interface address info. */ + unsigned char broadcast[MAX_ADDR_LEN]; /* hw bcast add */ + unsigned char dev_addr[MAX_ADDR_LEN]; /* hw address */ + unsigned char addr_len; /* hardware address length */ + + struct dev_mc_list *mc_list; /* Multicast mac addresses */ + int mc_count; /* Number of installed mcasts */ + int promiscuity; + int allmulti; + + int watchdog_timeo; + struct timer_list watchdog_timer; + + /* Protocol specific pointers */ + + void *atalk_ptr; /* AppleTalk link */ + void *ip_ptr; /* IPv4 specific data */ + void *dn_ptr; /* DECnet specific data */ + void *ip6_ptr; /* IPv6 specific data */ + void *ec_ptr; /* Econet specific data */ + + struct list_head poll_list; /* Link to poll list */ + int quota; + int weight; + + struct Qdisc *qdisc; + struct Qdisc *qdisc_sleeping; + struct Qdisc *qdisc_ingress; + /* + * this is needed for the wlan driver binary blob from linksys + */ +#ifdef CONFIG_BCM4710 + struct Qdisc *qdisc_list; +#else + struct list_head qdisc_list; +#endif + unsigned long tx_queue_len; /* Max frames per queue allowed */ + + /* hard_start_xmit synchronizer */ + spinlock_t xmit_lock; + /* cpu id of processor entered to hard_start_xmit or -1, + if nobody entered there. + */ + int xmit_lock_owner; + /* device queue lock */ + spinlock_t queue_lock; + /* Number of references to this device */ + atomic_t refcnt; + /* The flag marking that device is unregistered, but held by an user */ + int deadbeaf; + + /* Net device features */ + int features; +#define NETIF_F_SG 1 /* Scatter/gather IO. */ +#define NETIF_F_IP_CSUM 2 /* Can checksum only TCP/UDP over IPv4. */ +#define NETIF_F_NO_CSUM 4 /* Does not require checksum. F.e. loopack. */ +#define NETIF_F_HW_CSUM 8 /* Can checksum all the packets. */ +#define NETIF_F_DYNALLOC 16 /* Self-dectructable device. */ +#define NETIF_F_HIGHDMA 32 /* Can DMA to high memory. */ +#define NETIF_F_FRAGLIST 64 /* Scatter/gather IO. */ +#define NETIF_F_HW_VLAN_TX 128 /* Transmit VLAN hw acceleration */ +#define NETIF_F_HW_VLAN_RX 256 /* Receive VLAN hw acceleration */ +#define NETIF_F_HW_VLAN_FILTER 512 /* Receive filtering on VLAN */ +#define NETIF_F_VLAN_CHALLENGED 1024 /* Device cannot handle VLAN packets */ + + /* Called after device is detached from network. */ + void (*uninit)(struct net_device *dev); + /* Called after last user reference disappears. */ + void (*destructor)(struct net_device *dev); + + /* Pointers to interface service routines. */ + int (*open)(struct net_device *dev); + int (*stop)(struct net_device *dev); + int (*hard_start_xmit) (struct sk_buff *skb, + struct net_device *dev); +#define HAVE_NETDEV_POLL + int (*poll) (struct net_device *dev, int *quota); + int (*hard_header) (struct sk_buff *skb, + struct net_device *dev, + unsigned short type, + void *daddr, + void *saddr, + unsigned len); + int (*rebuild_header)(struct sk_buff *skb); +#define HAVE_MULTICAST + void (*set_multicast_list)(struct net_device *dev); +#define HAVE_SET_MAC_ADDR + int (*set_mac_address)(struct net_device *dev, + void *addr); +#define HAVE_PRIVATE_IOCTL + int (*do_ioctl)(struct net_device *dev, + struct ifreq *ifr, int cmd); +#define HAVE_SET_CONFIG + int (*set_config)(struct net_device *dev, + struct ifmap *map); +#define HAVE_HEADER_CACHE + int (*hard_header_cache)(struct neighbour *neigh, + struct hh_cache *hh); + void (*header_cache_update)(struct hh_cache *hh, + struct net_device *dev, + unsigned char * haddr); +#define HAVE_CHANGE_MTU + int (*change_mtu)(struct net_device *dev, int new_mtu); + +#define HAVE_TX_TIMEOUT + void (*tx_timeout) (struct net_device *dev); + + void (*vlan_rx_register)(struct net_device *dev, + struct vlan_group *grp); + void (*vlan_rx_add_vid)(struct net_device *dev, + unsigned short vid); + void (*vlan_rx_kill_vid)(struct net_device *dev, + unsigned short vid); + + int (*hard_header_parse)(struct sk_buff *skb, + unsigned char *haddr); + int (*neigh_setup)(struct net_device *dev, struct neigh_parms *); + int (*accept_fastpath)(struct net_device *, struct dst_entry*); + + /* open/release and usage marking */ + struct module *owner; + + /* bridge stuff */ + struct net_bridge_port *br_port; + +#ifdef CONFIG_NET_FASTROUTE +#define NETDEV_FASTROUTE_HMASK 0xF + /* Semi-private data. Keep it at the end of device struct. */ + rwlock_t fastpath_lock; + struct dst_entry *fastpath[NETDEV_FASTROUTE_HMASK+1]; +#endif +#ifdef CONFIG_NET_DIVERT + /* this will get initialized at each interface type init routine */ + struct divert_blk *divert; +#endif /* CONFIG_NET_DIVERT */ +}; + +/* 2.6 compatibility */ +#define SET_NETDEV_DEV(net, pdev) do { } while (0) + +struct packet_type +{ + unsigned short type; /* This is really htons(ether_type). */ + struct net_device *dev; /* NULL is wildcarded here */ + int (*func) (struct sk_buff *, struct net_device *, + struct packet_type *); + void *data; /* Private to the packet type */ + struct packet_type *next; +}; + +static inline void *netdev_priv(struct net_device *dev) +{ + return dev->priv; +} + +#include +#include + +extern struct net_device loopback_dev; /* The loopback */ +extern struct net_device *dev_base; /* All devices */ +extern rwlock_t dev_base_lock; /* Device list lock */ + +extern int netdev_boot_setup_add(char *name, struct ifmap *map); +extern int netdev_boot_setup_check(struct net_device *dev); +extern struct net_device *dev_getbyhwaddr(unsigned short type, char *hwaddr); +extern void dev_add_pack(struct packet_type *pt); +extern void dev_remove_pack(struct packet_type *pt); +extern int dev_get(const char *name); +extern struct net_device *dev_get_by_flags(unsigned short flags, + unsigned short mask); +extern struct net_device *__dev_get_by_flags(unsigned short flags, + unsigned short mask); +extern struct net_device *dev_get_by_name(const char *name); +extern struct net_device *__dev_get_by_name(const char *name); +extern struct net_device *dev_alloc(const char *name, int *err); +extern int dev_alloc_name(struct net_device *dev, const char *name); +extern int dev_open(struct net_device *dev); +extern int dev_close(struct net_device *dev); +extern int dev_queue_xmit(struct sk_buff *skb); +extern int register_netdevice(struct net_device *dev); +extern int unregister_netdevice(struct net_device *dev); +extern int register_netdevice_notifier(struct notifier_block *nb); +extern int unregister_netdevice_notifier(struct notifier_block *nb); +extern int dev_new_index(void); +extern struct net_device *dev_get_by_index(int ifindex); +extern struct net_device *__dev_get_by_index(int ifindex); +extern int dev_restart(struct net_device *dev); + +typedef int gifconf_func_t(struct net_device * dev, char * bufptr, int len); +extern int register_gifconf(unsigned int family, gifconf_func_t * gifconf); +static inline int unregister_gifconf(unsigned int family) +{ + return register_gifconf(family, 0); +} + +/* + * Incoming packets are placed on per-cpu queues so that + * no locking is needed. + */ + +struct softnet_data +{ + int throttle; + int cng_level; + int avg_blog; + struct sk_buff_head input_pkt_queue; + struct list_head poll_list; + struct net_device *output_queue; + struct sk_buff *completion_queue; + + struct net_device blog_dev; /* Sorry. 8) */ +} ____cacheline_aligned; + + +extern struct softnet_data softnet_data[NR_CPUS]; + +#define HAVE_NETIF_QUEUE + +static inline void __netif_schedule(struct net_device *dev) +{ + if (!test_and_set_bit(__LINK_STATE_SCHED, &dev->state)) { + unsigned long flags; + int cpu = smp_processor_id(); + + local_irq_save(flags); + dev->next_sched = softnet_data[cpu].output_queue; + softnet_data[cpu].output_queue = dev; + cpu_raise_softirq(cpu, NET_TX_SOFTIRQ); + local_irq_restore(flags); + } +} + +static inline void netif_schedule(struct net_device *dev) +{ + if (!test_bit(__LINK_STATE_XOFF, &dev->state)) + __netif_schedule(dev); +} + +static inline void netif_start_queue(struct net_device *dev) +{ + clear_bit(__LINK_STATE_XOFF, &dev->state); +} + +static inline void netif_wake_queue(struct net_device *dev) +{ + if (test_and_clear_bit(__LINK_STATE_XOFF, &dev->state)) + __netif_schedule(dev); +} + +static inline void netif_stop_queue(struct net_device *dev) +{ + set_bit(__LINK_STATE_XOFF, &dev->state); +} + +static inline int netif_queue_stopped(struct net_device *dev) +{ + return test_bit(__LINK_STATE_XOFF, &dev->state); +} + +static inline int netif_running(struct net_device *dev) +{ + return test_bit(__LINK_STATE_START, &dev->state); +} + + +/* Use this variant when it is known for sure that it + * is executing from interrupt context. + */ +static inline void dev_kfree_skb_irq(struct sk_buff *skb) +{ + if (atomic_dec_and_test(&skb->users)) { + int cpu =smp_processor_id(); + unsigned long flags; + + local_irq_save(flags); + skb->next = softnet_data[cpu].completion_queue; + softnet_data[cpu].completion_queue = skb; + cpu_raise_softirq(cpu, NET_TX_SOFTIRQ); + local_irq_restore(flags); + } +} + +/* Use this variant in places where it could be invoked + * either from interrupt or non-interrupt context. + */ +static inline void dev_kfree_skb_any(struct sk_buff *skb) +{ + if (in_irq()) + dev_kfree_skb_irq(skb); + else + dev_kfree_skb(skb); +} + +#define HAVE_NETIF_RX 1 +extern int netif_rx(struct sk_buff *skb); +#define HAVE_NETIF_RECEIVE_SKB 1 +extern int netif_receive_skb(struct sk_buff *skb); +extern int dev_ioctl(unsigned int cmd, void *); +extern int dev_ethtool(struct ifreq *); +extern int dev_change_flags(struct net_device *, unsigned); +extern void dev_queue_xmit_nit(struct sk_buff *skb, struct net_device *dev); + +extern void dev_init(void); + +extern int netdev_nit; + +/* Post buffer to the network code from _non interrupt_ context. + * see net/core/dev.c for netif_rx description. + */ +static inline int netif_rx_ni(struct sk_buff *skb) +{ + int err = netif_rx(skb); + if (softirq_pending(smp_processor_id())) + do_softirq(); + return err; +} + +static inline void dev_init_buffers(struct net_device *dev) +{ + /* WILL BE REMOVED IN 2.5.0 */ +} + +extern int netdev_finish_unregister(struct net_device *dev); + +static inline void dev_put(struct net_device *dev) +{ + if (atomic_dec_and_test(&dev->refcnt)) + netdev_finish_unregister(dev); +} + +#define __dev_put(dev) atomic_dec(&(dev)->refcnt) +#define dev_hold(dev) atomic_inc(&(dev)->refcnt) + +/* Carrier loss detection, dial on demand. The functions netif_carrier_on + * and _off may be called from IRQ context, but it is caller + * who is responsible for serialization of these calls. + */ + +static inline int netif_carrier_ok(struct net_device *dev) +{ + return !test_bit(__LINK_STATE_NOCARRIER, &dev->state); +} + +extern void __netdev_watchdog_up(struct net_device *dev); + +static inline void netif_carrier_on(struct net_device *dev) +{ + clear_bit(__LINK_STATE_NOCARRIER, &dev->state); + if (netif_running(dev)) + __netdev_watchdog_up(dev); +} + +static inline void netif_carrier_off(struct net_device *dev) +{ + set_bit(__LINK_STATE_NOCARRIER, &dev->state); +} + +/* Hot-plugging. */ +static inline int netif_device_present(struct net_device *dev) +{ + return test_bit(__LINK_STATE_PRESENT, &dev->state); +} + +static inline 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); + } +} + +static inline 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); + } +} + +/* + * Network interface message level settings + */ +#define HAVE_NETIF_MSG 1 + +enum { + NETIF_MSG_DRV = 0x0001, + NETIF_MSG_PROBE = 0x0002, + NETIF_MSG_LINK = 0x0004, + NETIF_MSG_TIMER = 0x0008, + NETIF_MSG_IFDOWN = 0x0010, + NETIF_MSG_IFUP = 0x0020, + NETIF_MSG_RX_ERR = 0x0040, + NETIF_MSG_TX_ERR = 0x0080, + NETIF_MSG_TX_QUEUED = 0x0100, + NETIF_MSG_INTR = 0x0200, + NETIF_MSG_TX_DONE = 0x0400, + NETIF_MSG_RX_STATUS = 0x0800, + NETIF_MSG_PKTDATA = 0x1000, + NETIF_MSG_HW = 0x2000, + NETIF_MSG_WOL = 0x4000, +}; + +#define netif_msg_drv(p) ((p)->msg_enable & NETIF_MSG_DRV) +#define netif_msg_probe(p) ((p)->msg_enable & NETIF_MSG_PROBE) +#define netif_msg_link(p) ((p)->msg_enable & NETIF_MSG_LINK) +#define netif_msg_timer(p) ((p)->msg_enable & NETIF_MSG_TIMER) +#define netif_msg_ifdown(p) ((p)->msg_enable & NETIF_MSG_IFDOWN) +#define netif_msg_ifup(p) ((p)->msg_enable & NETIF_MSG_IFUP) +#define netif_msg_rx_err(p) ((p)->msg_enable & NETIF_MSG_RX_ERR) +#define netif_msg_tx_err(p) ((p)->msg_enable & NETIF_MSG_TX_ERR) +#define netif_msg_tx_queued(p) ((p)->msg_enable & NETIF_MSG_TX_QUEUED) +#define netif_msg_intr(p) ((p)->msg_enable & NETIF_MSG_INTR) +#define netif_msg_tx_done(p) ((p)->msg_enable & NETIF_MSG_TX_DONE) +#define netif_msg_rx_status(p) ((p)->msg_enable & NETIF_MSG_RX_STATUS) +#define netif_msg_pktdata(p) ((p)->msg_enable & NETIF_MSG_PKTDATA) +#define netif_msg_hw(p) ((p)->msg_enable & NETIF_MSG_HW) +#define netif_msg_wol(p) ((p)->msg_enable & NETIF_MSG_WOL) + +static inline u32 netif_msg_init(int debug_value, int default_msg_enable_bits) +{ + /* use default */ + if (debug_value < 0 || debug_value >= (sizeof(u32) * 8)) + return default_msg_enable_bits; + if (debug_value == 0) /* no output */ + return 0; + /* set low N bits */ + return (1 << debug_value) - 1; +} + +/* Schedule rx intr now? */ + +static inline int netif_rx_schedule_prep(struct net_device *dev) +{ + return netif_running(dev) && + !test_and_set_bit(__LINK_STATE_RX_SCHED, &dev->state); +} + +/* Add interface to tail of rx poll list. This assumes that _prep has + * already been called and returned 1. + */ + +static inline void __netif_rx_schedule(struct net_device *dev) +{ + unsigned long flags; + int cpu = smp_processor_id(); + + local_irq_save(flags); + dev_hold(dev); + list_add_tail(&dev->poll_list, &softnet_data[cpu].poll_list); + if (dev->quota < 0) + dev->quota += dev->weight; + else + dev->quota = dev->weight; + __cpu_raise_softirq(cpu, NET_RX_SOFTIRQ); + local_irq_restore(flags); +} + +/* Try to reschedule poll. Called by irq handler. */ + +static inline void netif_rx_schedule(struct net_device *dev) +{ + if (netif_rx_schedule_prep(dev)) + __netif_rx_schedule(dev); +} + +/* Try to reschedule poll. Called by dev->poll() after netif_rx_complete(). + * Do not inline this? + */ +static inline int netif_rx_reschedule(struct net_device *dev, int undo) +{ + if (netif_rx_schedule_prep(dev)) { + unsigned long flags; + int cpu = smp_processor_id(); + + dev->quota += undo; + + local_irq_save(flags); + list_add_tail(&dev->poll_list, &softnet_data[cpu].poll_list); + __cpu_raise_softirq(cpu, NET_RX_SOFTIRQ); + local_irq_restore(flags); + return 1; + } + return 0; +} + +/* Remove interface from poll list: it must be in the poll list + * on current cpu. This primitive is called by dev->poll(), when + * it completes the work. The device cannot be out of poll list at this + * moment, it is BUG(). + */ +static inline void netif_rx_complete(struct net_device *dev) +{ + unsigned long flags; + + local_irq_save(flags); + if (!test_bit(__LINK_STATE_RX_SCHED, &dev->state)) BUG(); + list_del(&dev->poll_list); + smp_mb__before_clear_bit(); + clear_bit(__LINK_STATE_RX_SCHED, &dev->state); + local_irq_restore(flags); +} + +static inline void netif_poll_disable(struct net_device *dev) +{ + while (test_and_set_bit(__LINK_STATE_RX_SCHED, &dev->state)) { + /* No hurry. */ + current->state = TASK_INTERRUPTIBLE; + schedule_timeout(1); + } +} + +static inline void netif_poll_enable(struct net_device *dev) +{ + clear_bit(__LINK_STATE_RX_SCHED, &dev->state); +} + +/* same as netif_rx_complete, except that local_irq_save(flags) + * has already been issued + */ +static inline void __netif_rx_complete(struct net_device *dev) +{ + if (!test_bit(__LINK_STATE_RX_SCHED, &dev->state)) BUG(); + list_del(&dev->poll_list); + smp_mb__before_clear_bit(); + clear_bit(__LINK_STATE_RX_SCHED, &dev->state); +} + +static inline void netif_tx_disable(struct net_device *dev) +{ + spin_lock_bh(&dev->xmit_lock); + netif_stop_queue(dev); + spin_unlock_bh(&dev->xmit_lock); +} + +/* These functions live elsewhere (drivers/net/net_init.c, but related) */ + +extern void ether_setup(struct net_device *dev); +extern void fddi_setup(struct net_device *dev); +extern void tr_setup(struct net_device *dev); +extern void fc_setup(struct net_device *dev); +extern void fc_freedev(struct net_device *dev); +/* Support for loadable net-drivers */ +extern struct net_device *alloc_netdev(int sizeof_priv, const char *name, + void (*setup)(struct net_device *)); +extern int register_netdev(struct net_device *dev); +extern void unregister_netdev(struct net_device *dev); +/* Functions used for multicast support */ +extern void dev_mc_upload(struct net_device *dev); +extern int dev_mc_delete(struct net_device *dev, void *addr, int alen, int all); +extern int dev_mc_add(struct net_device *dev, void *addr, int alen, int newonly); +extern void dev_mc_discard(struct net_device *dev); +extern void dev_set_promiscuity(struct net_device *dev, int inc); +extern void dev_set_allmulti(struct net_device *dev, int inc); +extern void netdev_state_change(struct net_device *dev); +/* Load a device via the kmod */ +extern void dev_load(const char *name); +extern void dev_mcast_init(void); +extern int netdev_register_fc(struct net_device *dev, void (*stimul)(struct net_device *dev)); +extern void netdev_unregister_fc(int bit); +extern int netdev_max_backlog; +extern int weight_p; +extern unsigned long netdev_fc_xoff; +extern atomic_t netdev_dropping; +extern int netdev_set_master(struct net_device *dev, struct net_device *master); +extern struct sk_buff * skb_checksum_help(struct sk_buff *skb); +#ifdef CONFIG_NET_FASTROUTE +extern int netdev_fastroute; +extern int netdev_fastroute_obstacles; +extern void dev_clear_fastroute(struct net_device *dev); +#endif + +static inline void free_netdev(struct net_device *dev) +{ + kfree(dev); +} + +#endif /* __KERNEL__ */ + +#endif /* _LINUX_DEV_H */ diff -Naru linux-2.4.30/include/linux/wireless.h linux-2.4.30.w18/include/linux/wireless.h --- linux-2.4.30/include/linux/wireless.h 2005-05-14 11:18:17.474337000 +0300 +++ linux-2.4.30.w18/include/linux/wireless.h 2005-05-14 11:36:13.041826640 +0300 @@ -1,10 +1,10 @@ /* * This file define a set of standard wireless extensions * - * Version : 16 2.4.03 + * Version : 18 12.3.05 * * Authors : Jean Tourrilhes - HPL - - * Copyright (c) 1997-2002 Jean Tourrilhes, All Rights Reserved. + * Copyright (c) 1997-2005 Jean Tourrilhes, All Rights Reserved. */ #ifndef _LINUX_WIRELESS_H @@ -47,12 +47,12 @@ * # include/net/iw_handler.h * * Note as well that /proc/net/wireless implementation has now moved in : - * # include/linux/wireless.c + * # net/core/wireless.c * * Wireless Events (2002 -> onward) : * -------------------------------- * Events are defined at the end of this file, and implemented in : - * # include/linux/wireless.c + * # net/core/wireless.c * * Other comments : * -------------- @@ -82,7 +82,7 @@ * (there is some stuff that will be added in the future...) * I just plan to increment with each new version. */ -#define WIRELESS_EXT 16 +#define WIRELESS_EXT 18 /* * Changes : @@ -175,6 +175,28 @@ * - Remove IW_MAX_GET_SPY because conflict with enhanced spy support * - Add SIOCSIWTHRSPY/SIOCGIWTHRSPY and "struct iw_thrspy" * - Add IW_ENCODE_TEMP and iw_range->encoding_login_index + * + * V16 to V17 + * ---------- + * - Add flags to frequency -> auto/fixed + * - Document (struct iw_quality *)->updated, add new flags (INVALID) + * - Wireless Event capability in struct iw_range + * - Add support for relative TxPower (yick !) + * + * V17 to V18 (From Jouni Malinen ) + * ---------- + * - Add support for WPA/WPA2 + * - Add extended encoding configuration (SIOCSIWENCODEEXT and + * SIOCGIWENCODEEXT) + * - Add SIOCSIWGENIE/SIOCGIWGENIE + * - Add SIOCSIWMLME + * - Add SIOCSIWPMKSA + * - Add struct iw_range bit field for supported encoding capabilities + * - Add optional scan request parameters for SIOCSIWSCAN + * - Add SIOCSIWAUTH/SIOCGIWAUTH for setting authentication and WPA + * related parameters (extensible up to 4096 parameter values) + * - Add wireless events: IWEVGENIE, IWEVMICHAELMICFAILURE, + * IWEVASSOCREQIE, IWEVASSOCRESPIE, IWEVPMKIDCAND */ /**************************** CONSTANTS ****************************/ @@ -249,9 +271,33 @@ #define SIOCSIWPOWER 0x8B2C /* set Power Management settings */ #define SIOCGIWPOWER 0x8B2D /* get Power Management settings */ +/* WPA : Generic IEEE 802.11 informatiom element (e.g., for WPA/RSN/WMM). + * This ioctl uses struct iw_point and data buffer that includes IE id and len + * fields. More than one IE may be included in the request. Setting the generic + * IE to empty buffer (len=0) removes the generic IE from the driver. Drivers + * are allowed to generate their own WPA/RSN IEs, but in these cases, drivers + * are required to report the used IE as a wireless event, e.g., when + * associating with an AP. */ +#define SIOCSIWGENIE 0x8B30 /* set generic IE */ +#define SIOCGIWGENIE 0x8B31 /* get generic IE */ + +/* WPA : IEEE 802.11 MLME requests */ +#define SIOCSIWMLME 0x8B16 /* request MLME operation; uses + * struct iw_mlme */ +/* WPA : Authentication mode parameters */ +#define SIOCSIWAUTH 0x8B32 /* set authentication mode params */ +#define SIOCGIWAUTH 0x8B33 /* get authentication mode params */ + +/* WPA : Extended version of encoding configuration */ +#define SIOCSIWENCODEEXT 0x8B34 /* set encoding token & mode */ +#define SIOCGIWENCODEEXT 0x8B35 /* get encoding token & mode */ + +/* WPA2 : PMKSA cache management */ +#define SIOCSIWPMKSA 0x8B36 /* PMKSA cache operation */ + /* -------------------- DEV PRIVATE IOCTL LIST -------------------- */ -/* These 16 ioctl are wireless device private. +/* These 32 ioctl are wireless device private, for 16 commands. * Each driver is free to use them for whatever purpose it chooses, * however the driver *must* export the description of those ioctls * with SIOCGIWPRIV and *must* use arguments as defined below. @@ -266,8 +312,8 @@ * We now have 32 commands, so a bit more space ;-). * Also, all 'odd' commands are only usable by root and don't return the * content of ifr/iwr to user (but you are not obliged to use the set/get - * convention, just use every other two command). - * And I repeat : you are not obliged to use them with iwspy, but you + * convention, just use every other two command). More details in iwpriv.c. + * And I repeat : you are not forced to use them with iwpriv, but you * must be compliant with it. */ @@ -290,6 +336,34 @@ #define IWEVCUSTOM 0x8C02 /* Driver specific ascii string */ #define IWEVREGISTERED 0x8C03 /* Discovered a new node (AP mode) */ #define IWEVEXPIRED 0x8C04 /* Expired a node (AP mode) */ +#define IWEVGENIE 0x8C05 /* Generic IE (WPA, RSN, WMM, ..) + * (scan results); This includes id and + * length fields. One IWEVGENIE may + * contain more than one IE. Scan + * results may contain one or more + * IWEVGENIE events. */ +#define IWEVMICHAELMICFAILURE 0x8C06 /* Michael MIC failure + * (struct iw_michaelmicfailure) + */ +#define IWEVASSOCREQIE 0x8C07 /* IEs used in (Re)Association Request. + * The data includes id and length + * fields and may contain more than one + * IE. This event is required in + * Managed mode if the driver + * generates its own WPA/RSN IE. This + * should be sent just before + * IWEVREGISTERED event for the + * association. */ +#define IWEVASSOCRESPIE 0x8C08 /* IEs used in (Re)Association + * Response. The data includes id and + * length fields and may contain more + * than one IE. This may be sent + * between IWEVASSOCREQIE and + * IWEVREGISTERED events for the + * association. */ +#define IWEVPMKIDCAND 0x8C09 /* PMKID candidate for RSN + * pre-authentication + * (struct iw_pmkid_cand) */ #define IWEVFIRST 0x8C00 @@ -352,6 +426,18 @@ #define IW_MODE_SECOND 5 /* Secondary master/repeater (backup) */ #define IW_MODE_MONITOR 6 /* Passive monitor (listen only) */ +/* Statistics flags (bitmask in updated) */ +#define IW_QUAL_QUAL_UPDATED 0x1 /* Value was updated since last read */ +#define IW_QUAL_LEVEL_UPDATED 0x2 +#define IW_QUAL_NOISE_UPDATED 0x4 +#define IW_QUAL_QUAL_INVALID 0x10 /* Driver doesn't provide value */ +#define IW_QUAL_LEVEL_INVALID 0x20 +#define IW_QUAL_NOISE_INVALID 0x40 + +/* Frequency flags */ +#define IW_FREQ_AUTO 0x00 /* Let the driver decides */ +#define IW_FREQ_FIXED 0x01 /* Force a specific value */ + /* Maximum number of size of encoding token available * they are listed in the range structure */ #define IW_MAX_ENCODING_SIZES 8 @@ -390,6 +476,7 @@ #define IW_TXPOW_TYPE 0x00FF /* Type of value */ #define IW_TXPOW_DBM 0x0000 /* Value is in dBm */ #define IW_TXPOW_MWATT 0x0001 /* Value is in mW */ +#define IW_TXPOW_RELATIVE 0x0002 /* Value is in arbitrary units */ #define IW_TXPOW_RANGE 0x1000 /* Range of value between min/max */ /* Retry limits and lifetime flags available */ @@ -412,12 +499,113 @@ #define IW_SCAN_THIS_MODE 0x0020 /* Scan only this Mode */ #define IW_SCAN_ALL_RATE 0x0040 /* Scan all Bit-Rates */ #define IW_SCAN_THIS_RATE 0x0080 /* Scan only this Bit-Rate */ +/* struct iw_scan_req scan_type */ +#define IW_SCAN_TYPE_ACTIVE 0 +#define IW_SCAN_TYPE_PASSIVE 1 /* Maximum size of returned data */ #define IW_SCAN_MAX_DATA 4096 /* In bytes */ /* Max number of char in custom event - use multiple of them if needed */ #define IW_CUSTOM_MAX 256 /* In bytes */ +/* Generic information element */ +#define IW_GENERIC_IE_MAX 1024 + +/* MLME requests (SIOCSIWMLME / struct iw_mlme) */ +#define IW_MLME_DEAUTH 0 +#define IW_MLME_DISASSOC 1 + +/* SIOCSIWAUTH/SIOCGIWAUTH struct iw_param flags */ +#define IW_AUTH_INDEX 0x0FFF +#define IW_AUTH_FLAGS 0xF000 +/* SIOCSIWAUTH/SIOCGIWAUTH parameters (0 .. 4095) + * (IW_AUTH_INDEX mask in struct iw_param flags; this is the index of the + * parameter that is being set/get to; value will be read/written to + * struct iw_param value field) */ +#define IW_AUTH_WPA_VERSION 0 +#define IW_AUTH_CIPHER_PAIRWISE 1 +#define IW_AUTH_CIPHER_GROUP 2 +#define IW_AUTH_KEY_MGMT 3 +#define IW_AUTH_TKIP_COUNTERMEASURES 4 +#define IW_AUTH_DROP_UNENCRYPTED 5 +#define IW_AUTH_80211_AUTH_ALG 6 +#define IW_AUTH_WPA_ENABLED 7 +#define IW_AUTH_RX_UNENCRYPTED_EAPOL 8 +#define IW_AUTH_ROAMING_CONTROL 9 +#define IW_AUTH_PRIVACY_INVOKED 10 + +/* IW_AUTH_WPA_VERSION values (bit field) */ +#define IW_AUTH_WPA_VERSION_DISABLED 0x00000001 +#define IW_AUTH_WPA_VERSION_WPA 0x00000002 +#define IW_AUTH_WPA_VERSION_WPA2 0x00000004 + +/* IW_AUTH_PAIRWISE_CIPHER and IW_AUTH_GROUP_CIPHER values (bit field) */ +#define IW_AUTH_CIPHER_NONE 0x00000001 +#define IW_AUTH_CIPHER_WEP40 0x00000002 +#define IW_AUTH_CIPHER_TKIP 0x00000004 +#define IW_AUTH_CIPHER_CCMP 0x00000008 +#define IW_AUTH_CIPHER_WEP104 0x00000010 + +/* IW_AUTH_KEY_MGMT values (bit field) */ +#define IW_AUTH_KEY_MGMT_802_1X 1 +#define IW_AUTH_KEY_MGMT_PSK 2 + +/* IW_AUTH_80211_AUTH_ALG values (bit field) */ +#define IW_AUTH_ALG_OPEN_SYSTEM 0x00000001 +#define IW_AUTH_ALG_SHARED_KEY 0x00000002 +#define IW_AUTH_ALG_LEAP 0x00000004 + +/* IW_AUTH_ROAMING_CONTROL values */ +#define IW_AUTH_ROAMING_ENABLE 0 /* driver/firmware based roaming */ +#define IW_AUTH_ROAMING_DISABLE 1 /* user space program used for roaming + * control */ + +/* SIOCSIWENCODEEXT definitions */ +#define IW_ENCODE_SEQ_MAX_SIZE 8 +/* struct iw_encode_ext ->alg */ +#define IW_ENCODE_ALG_NONE 0 +#define IW_ENCODE_ALG_WEP 1 +#define IW_ENCODE_ALG_TKIP 2 +#define IW_ENCODE_ALG_CCMP 3 +/* struct iw_encode_ext ->ext_flags */ +#define IW_ENCODE_EXT_TX_SEQ_VALID 0x00000001 +#define IW_ENCODE_EXT_RX_SEQ_VALID 0x00000002 +#define IW_ENCODE_EXT_GROUP_KEY 0x00000004 +#define IW_ENCODE_EXT_SET_TX_KEY 0x00000008 + +/* IWEVMICHAELMICFAILURE : struct iw_michaelmicfailure ->flags */ +#define IW_MICFAILURE_KEY_ID 0x00000003 /* Key ID 0..3 */ +#define IW_MICFAILURE_GROUP 0x00000004 +#define IW_MICFAILURE_PAIRWISE 0x00000008 +#define IW_MICFAILURE_STAKEY 0x00000010 +#define IW_MICFAILURE_COUNT 0x00000060 /* 1 or 2 (0 = count not supported) + */ + +/* Bit field values for enc_capa in struct iw_range */ +#define IW_ENC_CAPA_WPA 0x00000001 +#define IW_ENC_CAPA_WPA2 0x00000002 +#define IW_ENC_CAPA_CIPHER_TKIP 0x00000004 +#define IW_ENC_CAPA_CIPHER_CCMP 0x00000008 + +/* Event capability macros - in (struct iw_range *)->event_capa + * Because we have more than 32 possible events, we use an array of + * 32 bit bitmasks. Note : 32 bits = 0x20 = 2^5. */ +#define IW_EVENT_CAPA_BASE(cmd) ((cmd >= SIOCIWFIRSTPRIV) ? \ + (cmd - SIOCIWFIRSTPRIV + 0x60) : \ + (cmd - SIOCSIWCOMMIT)) +#define IW_EVENT_CAPA_INDEX(cmd) (IW_EVENT_CAPA_BASE(cmd) >> 5) +#define IW_EVENT_CAPA_MASK(cmd) (1 << (IW_EVENT_CAPA_BASE(cmd) & 0x1F)) +/* Event capability constants - event autogenerated by the kernel + * This list is valid for most 802.11 devices, customise as needed... */ +#define IW_EVENT_CAPA_K_0 (IW_EVENT_CAPA_MASK(0x8B04) | \ + IW_EVENT_CAPA_MASK(0x8B06) | \ + IW_EVENT_CAPA_MASK(0x8B1A)) +#define IW_EVENT_CAPA_K_1 (IW_EVENT_CAPA_MASK(0x8B2A)) +/* "Easy" macro to set events in iw_range (less efficient) */ +#define IW_EVENT_CAPA_SET(event_capa, cmd) (event_capa[IW_EVENT_CAPA_INDEX(cmd)] |= IW_EVENT_CAPA_MASK(cmd)) +#define IW_EVENT_CAPA_SET_KERNEL(event_capa) {event_capa[0] |= IW_EVENT_CAPA_K_0; event_capa[1] |= IW_EVENT_CAPA_K_1; } + + /****************************** TYPES ******************************/ /* --------------------------- SUBTYPES --------------------------- */ @@ -456,7 +644,7 @@ __s32 m; /* Mantissa */ __s16 e; /* Exponent */ __u8 i; /* List index (when in range struct) */ - __u8 pad; /* Unused - just for alignement */ + __u8 flags; /* Flags (fixed/auto) */ }; /* @@ -507,6 +695,132 @@ struct iw_quality high; /* High threshold */ }; +/* + * Optional data for scan request + * + * Note: these optional parameters are controlling parameters for the + * scanning behavior, these do not apply to getting scan results + * (SIOCGIWSCAN). Drivers are expected to keep a local BSS table and + * provide a merged results with all BSSes even if the previous scan + * request limited scanning to a subset, e.g., by specifying an SSID. + * Especially, scan results are required to include an entry for the + * current BSS if the driver is in Managed mode and associated with an AP. + */ +struct iw_scan_req +{ + __u8 scan_type; /* IW_SCAN_TYPE_{ACTIVE,PASSIVE} */ + __u8 essid_len; + __u8 num_channels; /* num entries in channel_list; + * 0 = scan all allowed channels */ + __u8 flags; /* reserved as padding; use zero, this may + * be used in the future for adding flags + * to request different scan behavior */ + struct sockaddr bssid; /* ff:ff:ff:ff:ff:ff for broadcast BSSID or + * individual address of a specific BSS */ + + /* + * Use this ESSID if IW_SCAN_THIS_ESSID flag is used instead of using + * the current ESSID. This allows scan requests for specific ESSID + * without having to change the current ESSID and potentially breaking + * the current association. + */ + __u8 essid[IW_ESSID_MAX_SIZE]; + + /* + * Optional parameters for changing the default scanning behavior. + * These are based on the MLME-SCAN.request from IEEE Std 802.11. + * TU is 1.024 ms. If these are set to 0, driver is expected to use + * reasonable default values. min_channel_time defines the time that + * will be used to wait for the first reply on each channel. If no + * replies are received, next channel will be scanned after this. If + * replies are received, total time waited on the channel is defined by + * max_channel_time. + */ + __u32 min_channel_time; /* in TU */ + __u32 max_channel_time; /* in TU */ + + struct iw_freq channel_list[IW_MAX_FREQUENCIES]; +}; + +/* ------------------------- WPA SUPPORT ------------------------- */ + +/* + * Extended data structure for get/set encoding (this is used with + * SIOCSIWENCODEEXT/SIOCGIWENCODEEXT. struct iw_point and IW_ENCODE_* + * flags are used in the same way as with SIOCSIWENCODE/SIOCGIWENCODE and + * only the data contents changes (key data -> this structure, including + * key data). + * + * If the new key is the first group key, it will be set as the default + * TX key. Otherwise, default TX key index is only changed if + * IW_ENCODE_EXT_SET_TX_KEY flag is set. + * + * Key will be changed with SIOCSIWENCODEEXT in all cases except for + * special "change TX key index" operation which is indicated by setting + * key_len = 0 and ext_flags |= IW_ENCODE_EXT_SET_TX_KEY. + * + * tx_seq/rx_seq are only used when respective + * IW_ENCODE_EXT_{TX,RX}_SEQ_VALID flag is set in ext_flags. Normal + * TKIP/CCMP operation is to set RX seq with SIOCSIWENCODEEXT and start + * TX seq from zero whenever key is changed. SIOCGIWENCODEEXT is normally + * used only by an Authenticator (AP or an IBSS station) to get the + * current TX sequence number. Using TX_SEQ_VALID for SIOCSIWENCODEEXT and + * RX_SEQ_VALID for SIOCGIWENCODEEXT are optional, but can be useful for + * debugging/testing. + */ +struct iw_encode_ext +{ + __u32 ext_flags; /* IW_ENCODE_EXT_* */ + __u8 tx_seq[IW_ENCODE_SEQ_MAX_SIZE]; /* LSB first */ + __u8 rx_seq[IW_ENCODE_SEQ_MAX_SIZE]; /* LSB first */ + struct sockaddr addr; /* ff:ff:ff:ff:ff:ff for broadcast/multicast + * (group) keys or unicast address for + * individual keys */ + __u16 alg; /* IW_ENCODE_ALG_* */ + __u16 key_len; + __u8 key[0]; +}; + +/* SIOCSIWMLME data */ +struct iw_mlme +{ + __u16 cmd; /* IW_MLME_* */ + __u16 reason_code; + struct sockaddr addr; +}; + +/* SIOCSIWPMKSA data */ +#define IW_PMKSA_ADD 1 +#define IW_PMKSA_REMOVE 2 +#define IW_PMKSA_FLUSH 3 + +#define IW_PMKID_LEN 16 + +struct iw_pmksa +{ + __u32 cmd; /* IW_PMKSA_* */ + struct sockaddr bssid; + __u8 pmkid[IW_PMKID_LEN]; +}; + +/* IWEVMICHAELMICFAILURE data */ +struct iw_michaelmicfailure +{ + __u32 flags; + struct sockaddr src_addr; + __u8 tsc[IW_ENCODE_SEQ_MAX_SIZE]; /* LSB first */ +}; + +/* IWEVPMKIDCAND data */ +#define IW_PMKID_CAND_PREAUTH 0x00000001 /* RNS pre-authentication enabled */ +struct iw_pmkid_cand +{ + __u32 flags; /* IW_PMKID_CAND_* */ + __u32 index; /* the smaller the index, the higher the + * priority */ + struct sockaddr bssid; +}; + /* ------------------------ WIRELESS STATS ------------------------ */ /* * Wireless statistics (used for /proc/net/wireless) @@ -610,11 +924,12 @@ /* Old Frequency (backward compat - moved lower ) */ __u16 old_num_channels; __u8 old_num_frequency; - /* Filler to keep "version" at the same offset */ - __s32 old_freq[6]; + + /* Wireless event capability bitmasks */ + __u32 event_capa[6]; /* signal level threshold range */ - __s32 sensitivity; + __s32 sensitivity; /* Quality of link & SNR stuff */ /* Quality range (link, level, noise) @@ -685,6 +1000,8 @@ struct iw_freq freq[IW_MAX_FREQUENCIES]; /* list */ /* Note : this frequency list doesn't need to fit channel numbers, * because each entry contain its channel index */ + + __u32 enc_capa; /* IW_ENC_CAPA_* bit field */ }; /* diff -Naru linux-2.4.30/include/net/iw_handler.h linux-2.4.30.w18/include/net/iw_handler.h --- linux-2.4.30/include/net/iw_handler.h 2005-05-14 10:52:39.000000000 +0300 +++ linux-2.4.30.w18/include/net/iw_handler.h 2005-05-14 11:35:41.968550496 +0300 @@ -1,10 +1,10 @@ /* * This file define the new driver API for Wireless Extensions * - * Version : 5 4.12.02 + * Version : 6 21.6.04 * * Authors : Jean Tourrilhes - HPL - - * Copyright (c) 2001-2002 Jean Tourrilhes, All Rights Reserved. + * Copyright (c) 2001-2004 Jean Tourrilhes, All Rights Reserved. */ #ifndef _IW_HANDLER_H @@ -206,7 +206,7 @@ * will be needed... * I just plan to increment with each new version. */ -#define IW_HANDLER_VERSION 5 +#define IW_HANDLER_VERSION 6 /* * Changes : @@ -224,11 +224,18 @@ * V4 to V5 * -------- * - Add new spy support : struct iw_spy_data & prototypes + * + * V5 to V6 + * -------- + * - Change the way we get to spy_data method for added safety + * - Remove spy #ifdef, they are always on -> cleaner code + * - Add IW_DESCR_FLAG_NOMAX flag for very large requests + * - Start migrating get_wireless_stats to struct iw_handler_def */ /**************************** CONSTANTS ****************************/ -/* Enable enhanced spy support. Disable to reduce footprint */ +/* Enhanced spy support available */ #define IW_WIRELESS_SPY #define IW_WIRELESS_THRSPY @@ -258,6 +265,7 @@ #define IW_DESCR_FLAG_EVENT 0x0002 /* Generate an event on SET */ #define IW_DESCR_FLAG_RESTRICT 0x0004 /* GET : request is ROOT only */ /* SET : Omit payload from generated iwevent */ +#define IW_DESCR_FLAG_NOMAX 0x0008 /* GET : no limit on request size */ /* Driver level flags */ #define IW_DESCR_FLAG_WAIT 0x0100 /* Wait for driver event */ @@ -311,23 +319,25 @@ /* Array of handlers for standard ioctls * We will call dev->wireless_handlers->standard[ioctl - SIOCSIWNAME] */ - iw_handler * standard; + const iw_handler * standard; /* Array of handlers for private ioctls * Will call dev->wireless_handlers->private[ioctl - SIOCIWFIRSTPRIV] */ - iw_handler * private; + const iw_handler * private; /* Arguments of private handler. This one is just a list, so you * can put it in any order you want and should not leave holes... * We will automatically export that to user space... */ - struct iw_priv_args * private_args; + const struct iw_priv_args * private_args; - /* Driver enhanced spy support */ - long spy_offset; /* Spy data offset */ + /* This field will be *removed* in the next version of WE */ + long spy_offset; /* DO NOT USE */ - /* In the long term, get_wireless_stats will move from - * 'struct net_device' to here, to minimise bloat. */ + /* New location of get_wireless_stats, to de-bloat struct net_device. + * The old pointer in struct net_device will be gradually phased + * out, and drivers are encouraged to use this one... */ + struct iw_statistics* (*get_wireless_stats)(struct net_device *dev); }; /* ---------------------- IOCTL DESCRIPTION ---------------------- */ @@ -374,18 +384,29 @@ */ struct iw_spy_data { -#ifdef IW_WIRELESS_SPY /* --- Standard spy support --- */ int spy_number; u_char spy_address[IW_MAX_SPY][ETH_ALEN]; struct iw_quality spy_stat[IW_MAX_SPY]; -#ifdef IW_WIRELESS_THRSPY /* --- Enhanced spy support (event) */ struct iw_quality spy_thr_low; /* Low threshold */ struct iw_quality spy_thr_high; /* High threshold */ u_char spy_thr_under[IW_MAX_SPY]; -#endif /* IW_WIRELESS_THRSPY */ -#endif /* IW_WIRELESS_SPY */ +}; + +/* --------------------- DEVICE WIRELESS DATA --------------------- */ +/* + * This is all the wireless data specific to a device instance that + * is managed by the core of Wireless Extensions. + * We only keep pointer to those structures, so that a driver is free + * to share them between instances. + * This structure should be initialised before registering the device. + * Access to this data follow the same rules as any other struct net_device + * data (i.e. valid as long as struct net_device exist, same locking rules). + */ +struct iw_public_data { + /* Driver enhanced spy support */ + struct iw_spy_data * spy_data; }; /**************************** PROTOTYPES ****************************/ diff -Naru linux-2.4.30/net/core/dev.c linux-2.4.30.w18/net/core/dev.c --- linux-2.4.30/net/core/dev.c 2005-05-14 10:51:40.000000000 +0300 +++ linux-2.4.30.w18/net/core/dev.c 2005-05-14 11:35:41.969550344 +0300 @@ -2435,7 +2435,7 @@ /* Follow me in net/core/wireless.c */ ret = wireless_process_ioctl(&ifr, cmd); rtnl_unlock(); - if (!ret && IW_IS_GET(cmd) && + if (IW_IS_GET(cmd) && copy_to_user(arg, &ifr, sizeof(struct ifreq))) return -EFAULT; return ret; diff -Naru linux-2.4.30/net/core/dev.c.orig linux-2.4.30.w18/net/core/dev.c.orig --- linux-2.4.30/net/core/dev.c.orig 1970-01-01 02:00:00.000000000 +0200 +++ linux-2.4.30.w18/net/core/dev.c.orig 2005-05-14 10:51:40.000000000 +0300 @@ -0,0 +1,2935 @@ +/* + * 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 +#if defined(CONFIG_NET_RADIO) || defined(CONFIG_NET_PCMCIA_RADIO) +#include /* Note : will define WIRELESS_EXT */ +#include +#endif /* CONFIG_NET_RADIO || CONFIG_NET_PCMCIA_RADIO */ +#ifdef CONFIG_PLIP +extern int plip_init(void); +#endif + + +/* This define, if set, will randomly drop a packet when congestion + * is more than moderate. It helps fairness in the multi-interface + * case when one of them is a hog, but it kills performance for the + * single interface case so it is off now by default. + */ +#undef RAND_LIE + +/* Setting this will sample the queue lengths and thus congestion + * via a timer instead of as each packet is received. + */ +#undef OFFLINE_SAMPLE + +NET_PROFILE_DEFINE(dev_queue_xmit) +NET_PROFILE_DEFINE(softnet_process) + +const char *if_port_text[] = { + "unknown", + "BNC", + "10baseT", + "AUI", + "100baseT", + "100baseTX", + "100baseFX" +}; + +/* + * The list of packet types we will receive (as opposed to discard) + * and the routines to invoke. + * + * Why 16. Because with 16 the only overlap we get on a hash of the + * low nibble of the protocol value is RARP/SNAP/X.25. + * + * NOTE: That is no longer true with the addition of VLAN tags. Not + * sure which should go first, but I bet it won't make much + * difference if we are running VLANs. The good news is that + * this protocol won't be in the list unless compiled in, so + * the average user (w/out VLANs) will not be adversly affected. + * --BLG + * + * 0800 IP + * 8100 802.1Q VLAN + * 0001 802.3 + * 0002 AX.25 + * 0004 802.2 + * 8035 RARP + * 0005 SNAP + * 0805 X.25 + * 0806 ARP + * 8137 IPX + * 0009 Localtalk + * 86DD IPv6 + */ + +static struct packet_type *ptype_base[16]; /* 16 way hashed list */ +static struct packet_type *ptype_all = NULL; /* Taps */ + +#ifdef OFFLINE_SAMPLE +static void sample_queue(unsigned long dummy); +static struct timer_list samp_timer = { function: sample_queue }; +#endif + +#ifdef CONFIG_HOTPLUG +static int net_run_sbin_hotplug(struct net_device *dev, char *action); +#else +#define net_run_sbin_hotplug(dev, action) ({ 0; }) +#endif + +/* + * Our notifier list + */ + +static struct notifier_block *netdev_chain=NULL; + +/* + * Device drivers call our routines to queue packets here. We empty the + * queue in the local softnet handler. + */ +struct softnet_data softnet_data[NR_CPUS] __cacheline_aligned; + +#ifdef CONFIG_NET_FASTROUTE +int netdev_fastroute; +int netdev_fastroute_obstacles; +#endif + + +/****************************************************************************************** + + Protocol management and registration routines + +*******************************************************************************************/ + +/* + * For efficiency + */ + +int netdev_nit=0; + +/* + * Add a protocol ID to the list. Now that the input handler is + * smarter we can dispense with all the messy stuff that used to be + * here. + * + * BEWARE!!! Protocol handlers, mangling input packets, + * MUST BE last in hash buckets and checking protocol handlers + * MUST start from promiscous ptype_all chain in net_bh. + * It is true now, do not change it. + * Explantion follows: if protocol handler, mangling packet, will + * be the first on list, it is not able to sense, that packet + * is cloned and should be copied-on-write, so that it will + * change it and subsequent readers will get broken packet. + * --ANK (980803) + */ + +/** + * dev_add_pack - add packet handler + * @pt: packet type declaration + * + * Add a protocol handler to the networking stack. The passed &packet_type + * is linked into kernel lists and may not be freed until it has been + * removed from the kernel lists. + */ + +void dev_add_pack(struct packet_type *pt) +{ + int hash; + + br_write_lock_bh(BR_NETPROTO_LOCK); + +#ifdef CONFIG_NET_FASTROUTE + /* Hack to detect packet socket */ + if ((pt->data) && ((int)(pt->data)!=1)) { + netdev_fastroute_obstacles++; + dev_clear_fastroute(pt->dev); + } +#endif + if (pt->type == htons(ETH_P_ALL)) { + netdev_nit++; + pt->next=ptype_all; + ptype_all=pt; + } else { + hash=ntohs(pt->type)&15; + pt->next = ptype_base[hash]; + ptype_base[hash] = pt; + } + br_write_unlock_bh(BR_NETPROTO_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. + */ + +void dev_remove_pack(struct packet_type *pt) +{ + struct packet_type **pt1; + + br_write_lock_bh(BR_NETPROTO_LOCK); + + if (pt->type == htons(ETH_P_ALL)) { + netdev_nit--; + pt1=&ptype_all; + } else { + pt1=&ptype_base[ntohs(pt->type)&15]; + } + + for (; (*pt1) != NULL; pt1 = &((*pt1)->next)) { + if (pt == (*pt1)) { + *pt1 = pt->next; +#ifdef CONFIG_NET_FASTROUTE + if (pt->data) + netdev_fastroute_obstacles--; +#endif + br_write_unlock_bh(BR_NETPROTO_LOCK); + return; + } + } + br_write_unlock_bh(BR_NETPROTO_LOCK); + printk(KERN_WARNING "dev_remove_pack: %p not found.\n", pt); +} + +/****************************************************************************** + + 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. + */ +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; + } + } + + if (i >= NETDEV_BOOT_SETUP_MAX) + return 0; + + return 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; + 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] != ' ' && + !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; +} + +/* + * 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 net_device *dev; + + for (dev = dev_base; dev != NULL; dev = dev->next) { + if (strncmp(dev->name, name, IFNAMSIZ) == 0) + 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; +} + +/* + Return value is changed to int to prevent illegal usage in future. + It is still legal to use to check for device existence. + + User should understand, that the result returned by this function + is meaningless, if it was not issued under rtnl semaphore. + */ + +/** + * dev_get - test if a device exists + * @name: name to test for + * + * Test if a name exists. Returns true if the name is found. In order + * to be sure the name is not allocated or removed during the test the + * caller must hold the rtnl semaphore. + * + * This function primarily exists for back compatibility with older + * drivers. + */ + +int dev_get(const char *name) +{ + struct net_device *dev; + + read_lock(&dev_base_lock); + dev = __dev_get_by_name(name); + read_unlock(&dev_base_lock); + return dev != 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 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 net_device *dev; + + for (dev = dev_base; dev != NULL; dev = dev->next) { + 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 != NULL; dev = dev->next) { + if (dev->type == type && + memcmp(dev->dev_addr, ha, dev->addr_len) == 0) + return dev; + } + return NULL; +} + +/** + * 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); + dev = __dev_get_by_flags(if_flags, mask); + if (dev) + dev_hold(dev); + read_unlock(&dev_base_lock); + return dev; +} + +/** + * __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 caller must hold either + * the RTNL semaphore or @dev_base_lock. + */ + +struct net_device *__dev_get_by_flags(unsigned short if_flags, unsigned short mask) +{ + struct net_device *dev; + + for (dev = dev_base; dev != NULL; dev = dev->next) { + if (((dev->flags ^ if_flags) & mask) == 0) + return dev; + } + return NULL; +} + +/** + * dev_alloc_name - allocate a name for a device + * @dev: device + * @name: name format string + * + * Passed a format string - eg "lt%d" it will try and find a suitable + * id. Not efficient for many devices, not called a lot. The caller + * must hold the dev_base or rtnl lock while allocating the name and + * adding the device in order to avoid duplicates. Returns the number + * of the unit assigned or a negative errno code. + */ + +int dev_alloc_name(struct net_device *dev, const char *name) +{ + int i; + char buf[32]; + char *p; + + /* + * Verify the string as this thing may have come from + * the user. There must be either one "%d" and no other "%" + * characters, or no "%" characters at all. + */ + p = strchr(name, '%'); + if (p && (p[1] != 'd' || strchr(p+2, '%'))) + return -EINVAL; + + /* + * If you need over 100 please also fix the algorithm... + */ + for (i = 0; i < 100; i++) { + snprintf(buf,sizeof(buf),name,i); + if (__dev_get_by_name(buf) == NULL) { + strcpy(dev->name, buf); + return i; + } + } + return -ENFILE; /* Over 100 of the things .. bail out! */ +} + +/** + * dev_alloc - allocate a network device and name + * @name: name format string + * @err: error return pointer + * + * Passed a format string, eg. "lt%d", it will allocate a network device + * and space for the name. %NULL is returned if no memory is available. + * If the allocation succeeds then the name is assigned and the + * device pointer returned. %NULL is returned if the name allocation + * failed. The cause of an error is returned as a negative errno code + * in the variable @err points to. + * + * The caller must hold the @dev_base or RTNL locks when doing this in + * order to avoid duplicate name allocations. + */ + +struct net_device *dev_alloc(const char *name, int *err) +{ + struct net_device *dev=kmalloc(sizeof(struct net_device), GFP_KERNEL); + if (dev == NULL) { + *err = -ENOBUFS; + return NULL; + } + memset(dev, 0, sizeof(struct net_device)); + *err = dev_alloc_name(dev, name); + if (*err < 0) { + kfree(dev); + return NULL; + } + return dev; +} + +/** + * netdev_state_change - device changes state + * @dev: device to cause notification + * + * Called to indicate a device has changed state. This function calls + * the notifier chains for netdev_chain and sends a NEWLINK message + * to the routing socket. + */ + +void netdev_state_change(struct net_device *dev) +{ + if (dev->flags&IFF_UP) { + notifier_call_chain(&netdev_chain, NETDEV_CHANGE, dev); + rtmsg_ifinfo(RTM_NEWLINK, dev, 0); + } +} + + +#ifdef CONFIG_KMOD + +/** + * 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) +{ + if (!dev_get(name) && capable(CAP_SYS_MODULE)) + request_module(name); +} + +#else + +extern inline void dev_load(const char *unused){;} + +#endif + +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 + */ + if (try_inc_mod_count(dev->owner)) { + set_bit(__LINK_STATE_START, &dev->state); + if (dev->open) { + ret = dev->open(dev); + if (ret != 0) { + clear_bit(__LINK_STATE_START, &dev->state); + if (dev->owner) + __MOD_DEC_USE_COUNT(dev->owner); + } + } + } else { + ret = -ENODEV; + } + + /* + * If it went open OK then: + */ + + if (ret == 0) + { + /* + * Set the flags. + */ + dev->flags |= IFF_UP; + + /* + * Initialize multicasting status + */ + dev_mc_upload(dev); + + /* + * Wakeup transmit queue engine + */ + dev_activate(dev); + + /* + * ... and announce new interface. + */ + notifier_call_chain(&netdev_chain, NETDEV_UP, dev); + } + return(ret); +} + +#ifdef CONFIG_NET_FASTROUTE + +static void dev_do_clear_fastroute(struct net_device *dev) +{ + if (dev->accept_fastpath) { + int i; + + for (i=0; i<=NETDEV_FASTROUTE_HMASK; i++) { + struct dst_entry *dst; + + write_lock_irq(&dev->fastpath_lock); + dst = dev->fastpath[i]; + dev->fastpath[i] = NULL; + write_unlock_irq(&dev->fastpath_lock); + + dst_release(dst); + } + } +} + +void dev_clear_fastroute(struct net_device *dev) +{ + if (dev) { + dev_do_clear_fastroute(dev); + } else { + read_lock(&dev_base_lock); + for (dev = dev_base; dev; dev = dev->next) + dev_do_clear_fastroute(dev); + read_unlock(&dev_base_lock); + } +} +#endif + +/** + * dev_close - shutdown an interface. + * @dev: device to shutdown + * + * This function moves an active device into down state. A + * %NETDEV_GOING_DOWN is sent to the netdev notifier chain. The device + * is then deactivated and finally a %NETDEV_DOWN is sent to the notifier + * chain. + */ + +int dev_close(struct net_device *dev) +{ + if (!(dev->flags&IFF_UP)) + return 0; + + /* + * Tell people we are going down, so that they can + * prepare to death, when device is still operating. + */ + notifier_call_chain(&netdev_chain, NETDEV_GOING_DOWN, dev); + + dev_deactivate(dev); + + clear_bit(__LINK_STATE_START, &dev->state); + + /* Synchronize to scheduled poll. We cannot touch poll list, + * it can be even on different cpu. So just clear netif_running(), + * and wait when poll really will happen. Actually, the best place + * for this is inside dev->stop() after device stopped its irq + * engine, but this requires more changes in devices. */ + + smp_mb__after_clear_bit(); /* Commit netif_running(). */ + while (test_bit(__LINK_STATE_RX_SCHED, &dev->state)) { + /* No hurry. */ + current->state = TASK_INTERRUPTIBLE; + schedule_timeout(1); + } + + /* + * Call the device specific close. This cannot fail. + * Only if device is UP + * + * We allow it to be called even after a DETACH hot-plug + * event. + */ + + if (dev->stop) + dev->stop(dev); + + /* + * Device is now down. + */ + + dev->flags &= ~IFF_UP; +#ifdef CONFIG_NET_FASTROUTE + dev_clear_fastroute(dev); +#endif + + /* + * Tell people we are down + */ + notifier_call_chain(&netdev_chain, NETDEV_DOWN, dev); + + /* + * Drop the module refcount + */ + if (dev->owner) + __MOD_DEC_USE_COUNT(dev->owner); + + 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. + */ + +int register_netdevice_notifier(struct notifier_block *nb) +{ + return notifier_chain_register(&netdev_chain, nb); +} + +/** + * unregister_netdevice_notifier - unregister a network notifier block + * @nb: notifier + * + * Unregister a notifier previously registered by + * register_netdevice_notifier(). The notifier is unlinked into the + * kernel structures and may then be reused. A negative errno code + * is returned on a failure. + */ + +int unregister_netdevice_notifier(struct notifier_block *nb) +{ + return notifier_chain_unregister(&netdev_chain,nb); +} + +/* + * Support routine. Sends outgoing frames to any network + * taps currently in use. + */ + +void dev_queue_xmit_nit(struct sk_buff *skb, struct net_device *dev) +{ + struct packet_type *ptype; + do_gettimeofday(&skb->stamp); + + br_read_lock(BR_NETPROTO_LOCK); + for (ptype = ptype_all; ptype!=NULL; ptype = ptype->next) + { + /* Never send packets back to the socket + * they originated from - MvS (miquels@drinkel.ow.org) + */ + if ((ptype->dev == dev || !ptype->dev) && + ((struct sock *)ptype->data != skb->sk)) + { + struct sk_buff *skb2; + if ((skb2 = skb_clone(skb, GFP_ATOMIC)) == NULL) + 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); + } + } + br_read_unlock(BR_NETPROTO_LOCK); +} + +/* Calculate csum in the case, when packet is misrouted. + * If it failed by some reason, ignore and send skb with wrong + * checksum. + */ +struct sk_buff * skb_checksum_help(struct sk_buff *skb) +{ + int offset; + unsigned int csum; + + offset = skb->h.raw - skb->data; + if (offset > (int)skb->len) + BUG(); + csum = skb_checksum(skb, offset, skb->len-offset, 0); + + offset = skb->tail - skb->h.raw; + if (offset <= 0) + BUG(); + if (skb->csum+2 > offset) + BUG(); + + *(u16*)(skb->h.raw + skb->csum) = csum_fold(csum); + skb->ip_summed = CHECKSUM_NONE; + return skb; +} + +#ifdef CONFIG_HIGHMEM +/* Actually, we should eliminate this check as soon as we know, that: + * 1. IOMMU is present and allows to map all the memory. + * 2. No high memory really exists on this machine. + */ + +static inline int +illegal_highdma(struct net_device *dev, struct sk_buff *skb) +{ + int i; + + if (dev->features&NETIF_F_HIGHDMA) + return 0; + + for (i=0; inr_frags; i++) + if (skb_shinfo(skb)->frags[i].page >= highmem_start_page) + return 1; + + return 0; +} +#else +#define illegal_highdma(dev, skb) (0) +#endif + +/** + * 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. + */ + +int dev_queue_xmit(struct sk_buff *skb) +{ + struct net_device *dev = skb->dev; + struct Qdisc *q; + + if (skb_shinfo(skb)->frag_list && + !(dev->features&NETIF_F_FRAGLIST) && + skb_linearize(skb, GFP_ATOMIC) != 0) { + kfree_skb(skb); + return -ENOMEM; + } + + /* Fragmented skb is linearized if device does not support SG, + * or if at least one of fragments is in highmem and device + * does not support DMA from it. + */ + if (skb_shinfo(skb)->nr_frags && + (!(dev->features&NETIF_F_SG) || illegal_highdma(dev, skb)) && + skb_linearize(skb, GFP_ATOMIC) != 0) { + kfree_skb(skb); + return -ENOMEM; + } + + /* If packet is not checksummed and device does not support + * checksumming for this protocol, complete checksumming here. + */ + if (skb->ip_summed == CHECKSUM_HW && + (!(dev->features&(NETIF_F_HW_CSUM|NETIF_F_NO_CSUM)) && + (!(dev->features&NETIF_F_IP_CSUM) || + skb->protocol != htons(ETH_P_IP)))) { + if ((skb = skb_checksum_help(skb)) == NULL) + return -ENOMEM; + } + + /* Grab device queue */ + spin_lock_bh(&dev->queue_lock); + q = dev->qdisc; + if (q->enqueue) { + int ret = q->enqueue(skb, q); + + qdisc_run(dev); + + spin_unlock_bh(&dev->queue_lock); + return ret == NET_XMIT_BYPASS ? NET_XMIT_SUCCESS : ret; + } + + /* The device has no queue. Common case for software devices: + loopback, all the sorts of tunnels... + + Really, it is unlikely that xmit_lock protection is necessary here. + (f.e. loopback and IP tunnels are clean ignoring statistics counters.) + However, it is possible, that they rely on protection + made by us here. + + Check this and shot the lock. It is not prone from deadlocks. + Either shot noqueue qdisc, it is even simpler 8) + */ + if (dev->flags&IFF_UP) { + int cpu = smp_processor_id(); + + if (dev->xmit_lock_owner != cpu) { + spin_unlock(&dev->queue_lock); + spin_lock(&dev->xmit_lock); + dev->xmit_lock_owner = cpu; + + if (!netif_queue_stopped(dev)) { + if (netdev_nit) + dev_queue_xmit_nit(skb,dev); + + if (dev->hard_start_xmit(skb, dev) == 0) { + dev->xmit_lock_owner = -1; + spin_unlock_bh(&dev->xmit_lock); + return 0; + } + } + dev->xmit_lock_owner = -1; + spin_unlock_bh(&dev->xmit_lock); + if (net_ratelimit()) + printk(KERN_CRIT "Virtual device %s asks to queue packet!\n", dev->name); + kfree_skb(skb); + return -ENETDOWN; + } 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); + } + } + spin_unlock_bh(&dev->queue_lock); + + kfree_skb(skb); + return -ENETDOWN; +} + + +/*======================================================================= + Receiver routines + =======================================================================*/ + +int netdev_max_backlog = 300; +int weight_p = 64; /* old backlog weight */ +/* These numbers are selected based on intuition and some + * experimentatiom, if you have more scientific way of doing this + * please go ahead and fix things. + */ +int no_cong_thresh = 10; +int no_cong = 20; +int lo_cong = 100; +int mod_cong = 290; + +struct netif_rx_stats netdev_rx_stat[NR_CPUS]; + + +#ifdef CONFIG_NET_HW_FLOWCONTROL +atomic_t netdev_dropping = ATOMIC_INIT(0); +static unsigned long netdev_fc_mask = 1; +unsigned long netdev_fc_xoff = 0; +spinlock_t netdev_fc_lock = SPIN_LOCK_UNLOCKED; + +static struct +{ + void (*stimul)(struct net_device *); + struct net_device *dev; +} netdev_fc_slots[BITS_PER_LONG]; + +int netdev_register_fc(struct net_device *dev, void (*stimul)(struct net_device *dev)) +{ + int bit = 0; + unsigned long flags; + + spin_lock_irqsave(&netdev_fc_lock, flags); + if (netdev_fc_mask != ~0UL) { + bit = ffz(netdev_fc_mask); + netdev_fc_slots[bit].stimul = stimul; + netdev_fc_slots[bit].dev = dev; + set_bit(bit, &netdev_fc_mask); + clear_bit(bit, &netdev_fc_xoff); + } + spin_unlock_irqrestore(&netdev_fc_lock, flags); + return bit; +} + +void netdev_unregister_fc(int bit) +{ + unsigned long flags; + + spin_lock_irqsave(&netdev_fc_lock, flags); + if (bit > 0) { + netdev_fc_slots[bit].stimul = NULL; + netdev_fc_slots[bit].dev = NULL; + clear_bit(bit, &netdev_fc_mask); + clear_bit(bit, &netdev_fc_xoff); + } + spin_unlock_irqrestore(&netdev_fc_lock, flags); +} + +static void netdev_wakeup(void) +{ + unsigned long xoff; + + spin_lock(&netdev_fc_lock); + xoff = netdev_fc_xoff; + netdev_fc_xoff = 0; + while (xoff) { + int i = ffz(~xoff); + xoff &= ~(1<> 1)+ (blog >> 1); + + if (avg_blog > mod_cong) { + /* Above moderate congestion levels. */ + softnet_data[cpu].cng_level = NET_RX_CN_HIGH; +#ifdef RAND_LIE + rd = net_random(); + rq = rd % netdev_max_backlog; + if (rq < avg_blog) /* unlucky bastard */ + softnet_data[cpu].cng_level = NET_RX_DROP; +#endif + } else if (avg_blog > lo_cong) { + softnet_data[cpu].cng_level = NET_RX_CN_MOD; +#ifdef RAND_LIE + rd = net_random(); + rq = rd % netdev_max_backlog; + if (rq < avg_blog) /* unlucky bastard */ + softnet_data[cpu].cng_level = NET_RX_CN_HIGH; +#endif + } else if (avg_blog > no_cong) + softnet_data[cpu].cng_level = NET_RX_CN_LOW; + else /* no congestion */ + softnet_data[cpu].cng_level = NET_RX_SUCCESS; + + softnet_data[cpu].avg_blog = avg_blog; +} + +#ifdef OFFLINE_SAMPLE +static void sample_queue(unsigned long dummy) +{ +/* 10 ms 0r 1ms -- i dont care -- JHS */ + int next_tick = 1; + int cpu = smp_processor_id(); + + get_sample_stats(cpu); + next_tick += jiffies; + mod_timer(&samp_timer, next_tick); +} +#endif + + +/** + * netif_rx - post buffer to the network code + * @skb: buffer to post + * + * This function receives a packet from a device driver and queues it for + * the upper (protocol) levels to process. It always succeeds. The buffer + * may be dropped during processing for congestion control or by the + * protocol layers. + * + * return values: + * NET_RX_SUCCESS (no congestion) + * NET_RX_CN_LOW (low congestion) + * NET_RX_CN_MOD (moderate congestion) + * NET_RX_CN_HIGH (high congestion) + * NET_RX_DROP (packet was dropped) + * + * + */ + +int netif_rx(struct sk_buff *skb) +{ + int this_cpu = smp_processor_id(); + struct softnet_data *queue; + unsigned long flags; + + if (skb->stamp.tv_sec == 0) + do_gettimeofday(&skb->stamp); + + /* The code is rearranged so that the path is the most + short when CPU is congested, but is still operating. + */ + queue = &softnet_data[this_cpu]; + + local_irq_save(flags); + + netdev_rx_stat[this_cpu].total++; + if (queue->input_pkt_queue.qlen <= netdev_max_backlog) { + if (queue->input_pkt_queue.qlen) { + if (queue->throttle) + goto drop; + +enqueue: + dev_hold(skb->dev); + __skb_queue_tail(&queue->input_pkt_queue,skb); + local_irq_restore(flags); +#ifndef OFFLINE_SAMPLE + get_sample_stats(this_cpu); +#endif + return queue->cng_level; + } + + if (queue->throttle) { + queue->throttle = 0; +#ifdef CONFIG_NET_HW_FLOWCONTROL + if (atomic_dec_and_test(&netdev_dropping)) + netdev_wakeup(); +#endif + } + + netif_rx_schedule(&queue->blog_dev); + goto enqueue; + } + + if (queue->throttle == 0) { + queue->throttle = 1; + netdev_rx_stat[this_cpu].throttled++; +#ifdef CONFIG_NET_HW_FLOWCONTROL + atomic_inc(&netdev_dropping); +#endif + } + +drop: + netdev_rx_stat[this_cpu].dropped++; + local_irq_restore(flags); + + kfree_skb(skb); + return NET_RX_DROP; +} + +/* Deliver skb to an old protocol, which is not threaded well + or which do not understand shared skbs. + */ +static int deliver_to_old_ones(struct packet_type *pt, struct sk_buff *skb, int last) +{ + static spinlock_t net_bh_lock = SPIN_LOCK_UNLOCKED; + int ret = NET_RX_DROP; + + + if (!last) { + skb = skb_clone(skb, GFP_ATOMIC); + if (skb == NULL) + return ret; + } + if (skb_is_nonlinear(skb) && skb_linearize(skb, GFP_ATOMIC) != 0) { + kfree_skb(skb); + return ret; + } + + /* The assumption (correct one) is that old protocols + did not depened on BHs different of NET_BH and TIMER_BH. + */ + + /* Emulate NET_BH with special spinlock */ + spin_lock(&net_bh_lock); + + /* Disable timers and wait for all timers completion */ + tasklet_disable(bh_task_vec+TIMER_BH); + + ret = pt->func(skb, skb->dev, pt); + + tasklet_hi_enable(bh_task_vec+TIMER_BH); + spin_unlock(&net_bh_lock); + return ret; +} + +static __inline__ void skb_bond(struct sk_buff *skb) +{ + struct net_device *dev = skb->dev; + + if (dev->master) { + skb->real_dev = skb->dev; + skb->dev = dev->master; + } +} + +static void net_tx_action(struct softirq_action *h) +{ + int cpu = smp_processor_id(); + + if (softnet_data[cpu].completion_queue) { + struct sk_buff *clist; + + local_irq_disable(); + clist = softnet_data[cpu].completion_queue; + softnet_data[cpu].completion_queue = NULL; + local_irq_enable(); + + while (clist != NULL) { + struct sk_buff *skb = clist; + clist = clist->next; + + BUG_TRAP(atomic_read(&skb->users) == 0); + __kfree_skb(skb); + } + } + + if (softnet_data[cpu].output_queue) { + struct net_device *head; + + local_irq_disable(); + head = softnet_data[cpu].output_queue; + softnet_data[cpu].output_queue = NULL; + local_irq_enable(); + + while (head != NULL) { + 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); + } + } + } +} + + +#if defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE) +int (*br_handle_frame_hook)(struct sk_buff *skb) = NULL; +#endif + +static __inline__ int handle_bridge(struct sk_buff *skb, + struct packet_type *pt_prev) +{ + int ret = NET_RX_DROP; + + if (pt_prev) { + if (!pt_prev->data) + ret = deliver_to_old_ones(pt_prev, skb, 0); + else { + atomic_inc(&skb->users); + ret = pt_prev->func(skb, skb->dev, pt_prev); + } + } + + return ret; +} + + +#ifdef CONFIG_NET_DIVERT +static inline int handle_diverter(struct sk_buff *skb) +{ + /* if diversion is supported on device, then divert */ + if (skb->dev->divert && skb->dev->divert->divert) + divert_frame(skb); + return 0; +} +#endif /* CONFIG_NET_DIVERT */ + +int netif_receive_skb(struct sk_buff *skb) +{ + struct packet_type *ptype, *pt_prev; + int ret = NET_RX_DROP; + unsigned short type; + + if (skb->stamp.tv_sec == 0) + do_gettimeofday(&skb->stamp); + + skb_bond(skb); + + netdev_rx_stat[smp_processor_id()].total++; + +#ifdef CONFIG_NET_FASTROUTE + if (skb->pkt_type == PACKET_FASTROUTE) { + netdev_rx_stat[smp_processor_id()].fastroute_deferred_out++; + return dev_queue_xmit(skb); + } +#endif + + skb->h.raw = skb->nh.raw = skb->data; + + pt_prev = NULL; + for (ptype = ptype_all; ptype; ptype = ptype->next) { + if (!ptype->dev || ptype->dev == skb->dev) { + if (pt_prev) { + if (!pt_prev->data) { + ret = deliver_to_old_ones(pt_prev, skb, 0); + } else { + atomic_inc(&skb->users); + ret = pt_prev->func(skb, skb->dev, pt_prev); + } + } + pt_prev = ptype; + } + } + +#ifdef CONFIG_NET_DIVERT + if (skb->dev->divert && skb->dev->divert->divert) + ret = handle_diverter(skb); +#endif /* CONFIG_NET_DIVERT */ + +#if defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE) + if (skb->dev->br_port != NULL && br_handle_frame_hook != NULL && + skb->pkt_type != PACKET_LOOPBACK) { + int ret; + + ret = handle_bridge(skb, pt_prev); + if (br_handle_frame_hook(skb) == 0) + return ret; + pt_prev = NULL; + } +#endif + + type = skb->protocol; + for (ptype=ptype_base[ntohs(type)&15];ptype;ptype=ptype->next) { + if (ptype->type == type && + (!ptype->dev || ptype->dev == skb->dev)) { + if (pt_prev) { + if (!pt_prev->data) { + ret = deliver_to_old_ones(pt_prev, skb, 0); + } else { + atomic_inc(&skb->users); + ret = pt_prev->func(skb, skb->dev, pt_prev); + } + } + pt_prev = ptype; + } + } + + if (pt_prev) { + if (!pt_prev->data) { + ret = deliver_to_old_ones(pt_prev, skb, 1); + } else { + ret = pt_prev->func(skb, skb->dev, pt_prev); + } + } else { + kfree_skb(skb); + /* Jamal, now you will not able to escape explaining + * me how you were going to use this. :-) + */ + ret = NET_RX_DROP; + } + + return ret; +} + +static int process_backlog(struct net_device *backlog_dev, int *budget) +{ + int work = 0; + int quota = min(backlog_dev->quota, *budget); + int this_cpu = smp_processor_id(); + struct softnet_data *queue = &softnet_data[this_cpu]; + unsigned long start_time = jiffies; + + for (;;) { + struct sk_buff *skb; + struct net_device *dev; + + local_irq_disable(); + skb = __skb_dequeue(&queue->input_pkt_queue); + if (skb == NULL) + 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; + +#ifdef CONFIG_NET_HW_FLOWCONTROL + if (queue->throttle && queue->input_pkt_queue.qlen < no_cong_thresh ) { + queue->throttle = 0; + if (atomic_dec_and_test(&netdev_dropping)) { + netdev_wakeup(); + break; + } + } +#endif + } + + backlog_dev->quota -= work; + *budget -= work; + return -1; + +job_done: + backlog_dev->quota -= work; + *budget -= work; + + list_del(&backlog_dev->poll_list); + smp_mb__before_clear_bit(); + netif_poll_enable(backlog_dev); + + if (queue->throttle) { + queue->throttle = 0; +#ifdef CONFIG_NET_HW_FLOWCONTROL + if (atomic_dec_and_test(&netdev_dropping)) + netdev_wakeup(); +#endif + } + local_irq_enable(); + return 0; +} + +static void net_rx_action(struct softirq_action *h) +{ + int this_cpu = smp_processor_id(); + struct softnet_data *queue = &softnet_data[this_cpu]; + unsigned long start_time = jiffies; + int budget = netdev_max_backlog; + + br_read_lock(BR_NETPROTO_LOCK); + 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); + + if (dev->quota <= 0 || dev->poll(dev, &budget)) { + local_irq_disable(); + list_del(&dev->poll_list); + list_add_tail(&dev->poll_list, &queue->poll_list); + if (dev->quota < 0) + dev->quota += dev->weight; + else + dev->quota = dev->weight; + } else { + dev_put(dev); + local_irq_disable(); + } + } + + local_irq_enable(); + br_read_unlock(BR_NETPROTO_LOCK); + return; + +softnet_break: + netdev_rx_stat[this_cpu].time_squeeze++; + __cpu_raise_softirq(this_cpu, NET_RX_SOFTIRQ); + + local_irq_enable(); + br_read_unlock(BR_NETPROTO_LOCK); +} + +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 *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 *arg) +{ + struct ifconf ifc; + struct net_device *dev; + char *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 != NULL; dev = dev->next) { + for (i=0; iget_stats ? dev->get_stats(dev): NULL); + int size; + + if (stats) + size = sprintf(buffer, "%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 + size = sprintf(buffer, "%6s: No statistics available.\n", dev->name); + + return size; +} + +/* + * Called from the PROCfs module. This now uses the new arbitrary sized /proc/net interface + * to create /proc/net/dev + */ + +static int dev_get_info(char *buffer, char **start, off_t offset, int length) +{ + int len = 0; + off_t begin = 0; + off_t pos = 0; + int size; + struct net_device *dev; + + + size = sprintf(buffer, + "Inter-| Receive | Transmit\n" + " face |bytes packets errs drop fifo frame compressed multicast|bytes packets errs drop fifo colls carrier compressed\n"); + + pos += size; + len += size; + + + read_lock(&dev_base_lock); + for (dev = dev_base; dev != NULL; dev = dev->next) { + size = sprintf_stats(buffer+len, dev); + len += size; + pos = begin + len; + + if (pos < offset) { + len = 0; + begin = pos; + } + if (pos > offset + length) + break; + } + read_unlock(&dev_base_lock); + + *start = buffer + (offset - begin); /* Start of wanted data */ + len -= (offset - begin); /* Start slop */ + if (len > length) + len = length; /* Ending slop */ + if (len < 0) + len = 0; + return len; +} + +static int dev_proc_stats(char *buffer, char **start, off_t offset, + int length, int *eof, void *data) +{ + int i, lcpu; + int len=0; + + for (lcpu=0; lcpu length) + len = length; + if (len < 0) + len = 0; + + *start = buffer + offset; + *eof = 1; + + return len; +} + +#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); + } + + br_write_lock_bh(BR_NETPROTO_LOCK); + slave->master = master; + br_write_unlock_bh(BR_NETPROTO_LOCK); + + if (old) + dev_put(old); + + if (master) + slave->flags |= IFF_SLAVE; + else + slave->flags &= ~IFF_SLAVE; + + rtmsg_ifinfo(RTM_NEWLINK, slave, IFF_SLAVE); + return 0; +} + +/** + * dev_set_promiscuity - update promiscuity count on a device + * @dev: device + * @inc: modifier + * + * Add or remove promsicuity from a device. While the count in the device + * remains above zero the interface remains promiscuous. Once it hits zero + * the device reverts back to normal filtering operation. A negative inc + * value is used to drop promiscuity on the device. + */ + +void dev_set_promiscuity(struct net_device *dev, int inc) +{ + unsigned short old_flags = dev->flags; + + dev->flags |= IFF_PROMISC; + if ((dev->promiscuity += inc) == 0) + dev->flags &= ~IFF_PROMISC; + if (dev->flags^old_flags) { +#ifdef CONFIG_NET_FASTROUTE + if (dev->flags&IFF_PROMISC) { + netdev_fastroute_obstacles++; + dev_clear_fastroute(dev); + } else + netdev_fastroute_obstacles--; +#endif + dev_mc_upload(dev); + printk(KERN_INFO "device %s %s promiscuous mode\n", + dev->name, (dev->flags&IFF_PROMISC) ? "entered" : "left"); + } +} + +/** + * dev_set_allmulti - update allmulti count on a device + * @dev: device + * @inc: modifier + * + * Add or remove reception of all multicast frames to a device. While the + * count in the device remains above zero the interface remains listening + * to all interfaces. Once it hits zero the device reverts back to normal + * filtering operation. A negative @inc value is used to drop the counter + * when releasing a resource needing all multicasts. + */ + +void dev_set_allmulti(struct net_device *dev, int inc) +{ + unsigned short old_flags = dev->flags; + + dev->flags |= IFF_ALLMULTI; + if ((dev->allmulti += inc) == 0) + dev->flags &= ~IFF_ALLMULTI; + if (dev->flags^old_flags) + dev_mc_upload(dev); +} + +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 == 0) + dev_mc_upload(dev); + } + + if (dev->flags&IFF_UP && + ((old_flags^dev->flags)&~(IFF_UP|IFF_PROMISC|IFF_ALLMULTI|IFF_VOLATILE))) + notifier_call_chain(&netdev_chain, NETDEV_CHANGE, dev); + + if ((flags^dev->gflags)&IFF_PROMISC) { + int inc = (flags&IFF_PROMISC) ? +1 : -1; + dev->gflags ^= IFF_PROMISC; + dev_set_promiscuity(dev, inc); + } + + /* NOTE: order of synchronization of IFF_PROMISC and IFF_ALLMULTI + is important. Some (broken) drivers set IFF_PROMISC, when + IFF_ALLMULTI is requested not asking us and not reporting. + */ + if ((flags^dev->gflags)&IFF_ALLMULTI) { + int inc = (flags&IFF_ALLMULTI) ? +1 : -1; + dev->gflags ^= IFF_ALLMULTI; + dev_set_allmulti(dev, inc); + } + + if (old_flags^dev->flags) + rtmsg_ifinfo(RTM_NEWLINK, dev, old_flags^dev->flags); + + return ret; +} + +/* + * Perform the SIOCxIFxxx calls. + */ + +static int dev_ifsioc(struct ifreq *ifr, unsigned int cmd) +{ + struct net_device *dev; + int err; + + if ((dev = __dev_get_by_name(ifr->ifr_name)) == NULL) + return -ENODEV; + + switch(cmd) + { + case SIOCGIFFLAGS: /* Get interface flags */ + ifr->ifr_flags = (dev->flags&~(IFF_PROMISC|IFF_ALLMULTI|IFF_RUNNING)) + |(dev->gflags&(IFF_PROMISC|IFF_ALLMULTI)); + if (netif_running(dev) && netif_carrier_ok(dev)) + ifr->ifr_flags |= IFF_RUNNING; + 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 */ + if (ifr->ifr_mtu == dev->mtu) + return 0; + + /* + * MTU must be positive. + */ + + if (ifr->ifr_mtu<0) + return -EINVAL; + + if (!netif_device_present(dev)) + return -ENODEV; + + if (dev->change_mtu) + err = dev->change_mtu(dev, ifr->ifr_mtu); + else { + dev->mtu = ifr->ifr_mtu; + err = 0; + } + if (!err && dev->flags&IFF_UP) + notifier_call_chain(&netdev_chain, NETDEV_CHANGEMTU, dev); + return err; + + case SIOCGIFHWADDR: + memcpy(ifr->ifr_hwaddr.sa_data,dev->dev_addr, MAX_ADDR_LEN); + ifr->ifr_hwaddr.sa_family=dev->type; + return 0; + + case SIOCSIFHWADDR: + if (dev->set_mac_address == NULL) + return -EOPNOTSUPP; + if (ifr->ifr_hwaddr.sa_family!=dev->type) + return -EINVAL; + if (!netif_device_present(dev)) + return -ENODEV; + err = dev->set_mac_address(dev, &ifr->ifr_hwaddr); + if (!err) + notifier_call_chain(&netdev_chain, NETDEV_CHANGEADDR, dev); + return err; + + case SIOCSIFHWBROADCAST: + if (ifr->ifr_hwaddr.sa_family!=dev->type) + return -EINVAL; + memcpy(dev->broadcast, ifr->ifr_hwaddr.sa_data, MAX_ADDR_LEN); + notifier_call_chain(&netdev_chain, NETDEV_CHANGEADDR, dev); + return 0; + + case SIOCGIFMAP: + ifr->ifr_map.mem_start=dev->mem_start; + ifr->ifr_map.mem_end=dev->mem_end; + ifr->ifr_map.base_addr=dev->base_addr; + ifr->ifr_map.irq=dev->irq; + ifr->ifr_map.dma=dev->dma; + ifr->ifr_map.port=dev->if_port; + return 0; + + case SIOCSIFMAP: + if (dev->set_config) { + if (!netif_device_present(dev)) + return -ENODEV; + return dev->set_config(dev,&ifr->ifr_map); + } + return -EOPNOTSUPP; + + case SIOCADDMULTI: + if (dev->set_multicast_list == NULL || + ifr->ifr_hwaddr.sa_family != AF_UNSPEC) + return -EINVAL; + if (!netif_device_present(dev)) + return -ENODEV; + dev_mc_add(dev,ifr->ifr_hwaddr.sa_data, dev->addr_len, 1); + return 0; + + case SIOCDELMULTI: + if (dev->set_multicast_list == NULL || + ifr->ifr_hwaddr.sa_family!=AF_UNSPEC) + return -EINVAL; + if (!netif_device_present(dev)) + return -ENODEV; + dev_mc_delete(dev,ifr->ifr_hwaddr.sa_data,dev->addr_len, 1); + return 0; + + 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: + if (dev->flags&IFF_UP) + return -EBUSY; + /* Check if name contains a wildcard */ + if (strchr(ifr->ifr_newname, '%')) { + char format[IFNAMSIZ + 1]; + int ret; + memcpy(format, ifr->ifr_newname, IFNAMSIZ); + format[IFNAMSIZ-1] = 0; + /* Find a free name based on format. + * dev_alloc_name() replaces "%d" with at max + * 2 digits, so no name overflow. - Jean II */ + ret = dev_alloc_name(dev, format); + if (ret < 0) + return ret; + /* Copy the new name back to caller. */ + strncpy(ifr->ifr_newname, dev->name, IFNAMSIZ); + } else { + if (__dev_get_by_name(ifr->ifr_newname)) + return -EEXIST; + memcpy(dev->name, ifr->ifr_newname, IFNAMSIZ); + dev->name[IFNAMSIZ-1] = 0; + } + notifier_call_chain(&netdev_chain, NETDEV_CHANGENAME, dev); + return 0; + + /* + * Unknown or private ioctl + */ + + default: + if ((cmd >= SIOCDEVPRIVATE && + cmd <= SIOCDEVPRIVATE + 15) || + cmd == SIOCBONDENSLAVE || + cmd == SIOCBONDRELEASE || + cmd == SIOCBONDSETHWADDR || + cmd == SIOCBONDSLAVEINFOQUERY || + cmd == SIOCBONDINFOQUERY || + cmd == SIOCBONDCHANGEACTIVE || +#ifdef CONFIG_BCM4710 + cmd == SIOCETHTOOL || +#endif + cmd == SIOCGMIIPHY || + cmd == SIOCGMIIREG || + cmd == SIOCSMIIREG || + cmd == SIOCWANDEV) { + if (dev->do_ioctl) { + if (!netif_device_present(dev)) + return -ENODEV; + return dev->do_ioctl(dev, ifr, cmd); + } + return -EOPNOTSUPP; + } + + } + return -EINVAL; +} + +/* + * 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 *arg) +{ + struct ifreq ifr; + int ret; + char *colon; + + /* One special case: SIOCGIFCONF takes ifconf argument + and requires shared lock, because it sleeps writing + to user space. + */ + + if (cmd == SIOCGIFCONF) { + rtnl_shlock(); + ret = dev_ifconf((char *) arg); + rtnl_shunlock(); + return ret; + } + if (cmd == SIOCGIFNAME) { + return dev_ifname((struct ifreq *)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))) + return -EFAULT; + } + return ret; + + case SIOCETHTOOL: +#ifndef CONFIG_BCM4710 + 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; +#endif + + /* + * These ioctl calls: + * - require superuser power. + * - require strict serialization. + * - return a value + */ + + case SIOCSIFNAME: + case SIOCGMIIPHY: + case SIOCGMIIREG: + if (!capable(CAP_NET_ADMIN)) + return -EPERM; + dev_load(ifr.ifr_name); + dev_probe_lock(); + rtnl_lock(); + ret = dev_ifsioc(&ifr, cmd); + rtnl_unlock(); + dev_probe_unlock(); + if (!ret) { + if (colon) + *colon = ':'; + if (copy_to_user(arg, &ifr, sizeof(struct ifreq))) + return -EFAULT; + } + return ret; + + /* + * These ioctl calls: + * - require superuser power. + * - require strict serialization. + * - do not return a value + */ + + case SIOCSIFFLAGS: + case SIOCSIFMETRIC: + case SIOCSIFMTU: + case SIOCSIFMAP: + case SIOCSIFHWADDR: + case SIOCSIFSLAVE: + case SIOCADDMULTI: + case SIOCDELMULTI: + case SIOCSIFHWBROADCAST: + case SIOCSIFTXQLEN: + case SIOCSMIIREG: + case SIOCBONDENSLAVE: + case SIOCBONDRELEASE: + case SIOCBONDSETHWADDR: + case SIOCBONDSLAVEINFOQUERY: + case SIOCBONDINFOQUERY: + case SIOCBONDCHANGEACTIVE: + if (!capable(CAP_NET_ADMIN)) + return -EPERM; + dev_load(ifr.ifr_name); + dev_probe_lock(); + rtnl_lock(); + ret = dev_ifsioc(&ifr, cmd); + rtnl_unlock(); + dev_probe_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); + dev_probe_lock(); + rtnl_lock(); + ret = dev_ifsioc(&ifr, cmd); + rtnl_unlock(); + dev_probe_unlock(); + if (!ret && copy_to_user(arg, &ifr, sizeof(struct ifreq))) + return -EFAULT; + return ret; + } +#ifdef WIRELESS_EXT + /* Take care of Wireless Extensions */ + if (cmd >= SIOCIWFIRST && cmd <= SIOCIWLAST) { + /* If command is `set a parameter', or + * `get the encoding parameters', check if + * the user has the right to do it */ + if (IW_IS_SET(cmd) || (cmd == SIOCGIWENCODE)) { + if(!capable(CAP_NET_ADMIN)) + return -EPERM; + } + dev_load(ifr.ifr_name); + rtnl_lock(); + /* Follow me in net/core/wireless.c */ + ret = wireless_process_ioctl(&ifr, cmd); + rtnl_unlock(); + if (!ret && IW_IS_GET(cmd) && + copy_to_user(arg, &ifr, sizeof(struct ifreq))) + return -EFAULT; + return ret; + } +#endif /* WIRELESS_EXT */ + return -EINVAL; + } +} + + +/** + * dev_new_index - allocate an ifindex + * + * Returns a suitable unique value for a new device interface + * number. The caller must hold the rtnl semaphore or the + * dev_base_lock to be sure it remains unique. + */ + +int dev_new_index(void) +{ + static int ifindex; + for (;;) { + if (++ifindex <= 0) + ifindex=1; + if (__dev_get_by_index(ifindex) == NULL) + return ifindex; + } +} + +static int dev_boot_phase = 1; + +/** + * 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. See the comment at the + * end of Space.c for details about the locking. 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 net_dev_init(void); + +int register_netdevice(struct net_device *dev) +{ + struct net_device *d, **dp; +#ifdef CONFIG_NET_DIVERT + int ret; +#endif + + spin_lock_init(&dev->queue_lock); + spin_lock_init(&dev->xmit_lock); + dev->xmit_lock_owner = -1; +#ifdef CONFIG_NET_FASTROUTE + dev->fastpath_lock=RW_LOCK_UNLOCKED; +#endif + + if (dev_boot_phase) + net_dev_init(); + +#ifdef CONFIG_NET_DIVERT + ret = alloc_divert_blk(dev); + if (ret) + return ret; +#endif /* CONFIG_NET_DIVERT */ + + dev->iflink = -1; + + /* Init, if this function is available */ + if (dev->init && dev->init(dev) != 0) { +#ifdef CONFIG_NET_DIVERT + free_divert_blk(dev); +#endif + return -EIO; + } + + dev->ifindex = dev_new_index(); + if (dev->iflink == -1) + dev->iflink = dev->ifindex; + + /* Check for existence, and append to tail of chain */ + for (dp=&dev_base; (d=*dp) != NULL; dp=&d->next) { + if (d == dev || strcmp(d->name, dev->name) == 0) { +#ifdef CONFIG_NET_DIVERT + free_divert_blk(dev); +#endif + return -EEXIST; + } + } + + /* Fix illegal SG+CSUM combinations. */ + if ((dev->features & NETIF_F_SG) && + !(dev->features & (NETIF_F_IP_CSUM | + NETIF_F_NO_CSUM | + NETIF_F_HW_CSUM))) { + printk("%s: Dropping NETIF_F_SG since no checksum feature.\n", + dev->name); + dev->features &= ~NETIF_F_SG; + } + + /* + * nil rebuild_header routine, + * that should be never called and used as just bug trap. + */ + + if (dev->rebuild_header == NULL) + dev->rebuild_header = default_rebuild_header; + + /* + * Default initial state at registry is that the + * device is present. + */ + + set_bit(__LINK_STATE_PRESENT, &dev->state); + + dev->next = NULL; + dev_init_scheduler(dev); + write_lock_bh(&dev_base_lock); + *dp = dev; + dev_hold(dev); + dev->deadbeaf = 0; + write_unlock_bh(&dev_base_lock); + + /* Notify protocols, that a new device appeared. */ + notifier_call_chain(&netdev_chain, NETDEV_REGISTER, dev); + + net_run_sbin_hotplug(dev, "register"); + + return 0; +} + +/** + * netdev_finish_unregister - complete unregistration + * @dev: device + * + * Destroy and free a dead device. A value of zero is returned on + * success. + */ + +int netdev_finish_unregister(struct net_device *dev) +{ + BUG_TRAP(dev->ip_ptr==NULL); + BUG_TRAP(dev->ip6_ptr==NULL); + BUG_TRAP(dev->dn_ptr==NULL); + + if (!dev->deadbeaf) { + printk(KERN_ERR "Freeing alive device %p, %s\n", dev, dev->name); + return 0; + } +#ifdef NET_REFCNT_DEBUG + printk(KERN_DEBUG "netdev_finish_unregister: %s%s.\n", dev->name, + (dev->features & NETIF_F_DYNALLOC)?"":", old style"); +#endif + if (dev->destructor) + dev->destructor(dev); + if (dev->features & NETIF_F_DYNALLOC) + kfree(dev); + return 0; +} + +/** + * 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. See the comment at the + * end of Space.c for details about the locking. You may want + * unregister_netdev() instead of this. + */ + +int unregister_netdevice(struct net_device *dev) +{ + unsigned long now, warning_time; + struct net_device *d, **dp; + + /* If device is running, close it first. */ + if (dev->flags & IFF_UP) + dev_close(dev); + + BUG_TRAP(dev->deadbeaf==0); + dev->deadbeaf = 1; + + /* 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); + *dp = d->next; + write_unlock_bh(&dev_base_lock); + break; + } + } + if (d == NULL) { + printk(KERN_DEBUG "unregister_netdevice: device %s/%p never was registered\n", dev->name, dev); + return -ENODEV; + } + + /* Synchronize to net_rx_action. */ + br_write_lock_bh(BR_NETPROTO_LOCK); + br_write_unlock_bh(BR_NETPROTO_LOCK); + + if (dev_boot_phase == 0) { +#ifdef CONFIG_NET_FASTROUTE + dev_clear_fastroute(dev); +#endif + + /* Shutdown queueing discipline. */ + dev_shutdown(dev); + + net_run_sbin_hotplug(dev, "unregister"); + + /* Notify protocols, that we are about to destroy + this device. They should clean all the things. + */ + notifier_call_chain(&netdev_chain, NETDEV_UNREGISTER, dev); + + /* + * Flush the multicast chain + */ + dev_mc_discard(dev); + } + + if (dev->uninit) + dev->uninit(dev); + + /* Notifier chain MUST detach us from master device. */ + BUG_TRAP(dev->master==NULL); + +#ifdef CONFIG_NET_DIVERT + free_divert_blk(dev); +#endif + + if (dev->features & NETIF_F_DYNALLOC) { +#ifdef NET_REFCNT_DEBUG + if (atomic_read(&dev->refcnt) != 1) + printk(KERN_DEBUG "unregister_netdevice: holding %s refcnt=%d\n", dev->name, atomic_read(&dev->refcnt)-1); +#endif + dev_put(dev); + return 0; + } + + /* Last reference is our one */ + if (atomic_read(&dev->refcnt) == 1) { + dev_put(dev); + return 0; + } + +#ifdef NET_REFCNT_DEBUG + printk("unregister_netdevice: waiting %s refcnt=%d\n", dev->name, atomic_read(&dev->refcnt)); +#endif + + /* EXPLANATION. If dev->refcnt is not now 1 (our own reference) + it means that someone in the kernel still has a reference + to this device and we cannot release it. + + "New style" devices have destructors, hence we can return from this + function and destructor will do all the work later. As of kernel 2.4.0 + there are very few "New Style" devices. + + "Old style" devices expect that the device is free of any references + upon exit from this function. + We cannot return from this function until all such references have + fallen away. This is because the caller of this function will probably + immediately kfree(*dev) and then be unloaded via sys_delete_module. + + So, we linger until all references fall away. The duration of the + linger is basically unbounded! It is driven by, for example, the + current setting of sysctl_ipfrag_time. + + After 1 second, we start to rebroadcast unregister notifications + in hope that careless clients will release the device. + + */ + + now = warning_time = jiffies; + while (atomic_read(&dev->refcnt) != 1) { + if ((jiffies - now) > 1*HZ) { + /* Rebroadcast unregister notification */ + notifier_call_chain(&netdev_chain, NETDEV_UNREGISTER, dev); + } + current->state = TASK_INTERRUPTIBLE; + schedule_timeout(HZ/4); + current->state = TASK_RUNNING; + if ((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; + } + } + dev_put(dev); + return 0; +} + + +/* + * 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. + * + */ + +extern void net_device_init(void); +extern void ip_auto_config(void); +struct proc_dir_entry *proc_net_drivers; +#ifdef CONFIG_NET_DIVERT +extern void dv_init(void); +#endif /* CONFIG_NET_DIVERT */ + + +/* + * Callers must hold the rtnl semaphore. See the comment at the + * end of Space.c for details about the locking. + */ +int __init net_dev_init(void) +{ + struct net_device *dev, **dp; + int i; + + if (!dev_boot_phase) + return 0; + + +#ifdef CONFIG_NET_DIVERT + dv_init(); +#endif /* CONFIG_NET_DIVERT */ + + /* + * Initialise the packet receive queues. + */ + + for (i = 0; i < NR_CPUS; i++) { + struct softnet_data *queue; + + queue = &softnet_data[i]; + skb_queue_head_init(&queue->input_pkt_queue); + queue->throttle = 0; + queue->cng_level = 0; + queue->avg_blog = 10; /* arbitrary non-zero */ + queue->completion_queue = NULL; + INIT_LIST_HEAD(&queue->poll_list); + set_bit(__LINK_STATE_START, &queue->blog_dev.state); + queue->blog_dev.weight = weight_p; + queue->blog_dev.poll = process_backlog; + atomic_set(&queue->blog_dev.refcnt, 1); + } + +#ifdef CONFIG_NET_PROFILE + net_profile_init(); + NET_PROFILE_REGISTER(dev_queue_xmit); + NET_PROFILE_REGISTER(softnet_process); +#endif + +#ifdef OFFLINE_SAMPLE + samp_timer.expires = jiffies + (10 * HZ); + add_timer(&samp_timer); +#endif + + /* + * Add the devices. + * If the call to dev->init fails, the dev is removed + * from the chain disconnecting the device until the + * next reboot. + * + * NB At boot phase networking is dead. No locking is required. + * But we still preserve dev_base_lock for sanity. + */ + + dp = &dev_base; + while ((dev = *dp) != NULL) { + spin_lock_init(&dev->queue_lock); + spin_lock_init(&dev->xmit_lock); +#ifdef CONFIG_NET_FASTROUTE + dev->fastpath_lock = RW_LOCK_UNLOCKED; +#endif + dev->xmit_lock_owner = -1; + dev->iflink = -1; + dev_hold(dev); + + /* + * Allocate name. If the init() fails + * the name will be reissued correctly. + */ + if (strchr(dev->name, '%')) + dev_alloc_name(dev, dev->name); + + /* + * Check boot time settings for the device. + */ + netdev_boot_setup_check(dev); + + if (dev->init && dev->init(dev)) { + /* + * It failed to come up. It will be unhooked later. + * dev_alloc_name can now advance to next suitable + * name that is checked next. + */ + dev->deadbeaf = 1; + dp = &dev->next; + } else { + dp = &dev->next; + dev->ifindex = dev_new_index(); + if (dev->iflink == -1) + dev->iflink = dev->ifindex; + if (dev->rebuild_header == NULL) + dev->rebuild_header = default_rebuild_header; + dev_init_scheduler(dev); + set_bit(__LINK_STATE_PRESENT, &dev->state); + } + } + + /* + * Unhook devices that failed to come up + */ + dp = &dev_base; + while ((dev = *dp) != NULL) { + if (dev->deadbeaf) { + write_lock_bh(&dev_base_lock); + *dp = dev->next; + write_unlock_bh(&dev_base_lock); + dev_put(dev); + } else { + dp = &dev->next; + } + } + +#ifdef CONFIG_PROC_FS + proc_net_create("dev", 0, dev_get_info); + create_proc_read_entry("net/softnet_stat", 0, 0, dev_proc_stats, NULL); + proc_net_drivers = proc_mkdir("net/drivers", 0); +#ifdef WIRELESS_EXT + /* Available in net/core/wireless.c */ + proc_net_create("wireless", 0, dev_get_wireless_info); +#endif /* WIRELESS_EXT */ +#endif /* CONFIG_PROC_FS */ + + dev_boot_phase = 0; + + open_softirq(NET_TX_SOFTIRQ, net_tx_action, NULL); + open_softirq(NET_RX_SOFTIRQ, net_rx_action, NULL); + + dst_init(); + dev_mcast_init(); + +#ifdef CONFIG_NET_SCHED + pktsched_init(); +#endif + /* + * Initialise network devices + */ + + net_device_init(); + + return 0; +} + +#ifdef CONFIG_HOTPLUG + +/* Notify userspace when a netdevice event occurs, + * by running '/sbin/hotplug net' with certain + * environment variables set. + */ + +static int net_run_sbin_hotplug(struct net_device *dev, char *action) +{ + char *argv[3], *envp[5], ifname[12 + IFNAMSIZ], action_str[32]; + int i; + + sprintf(ifname, "INTERFACE=%s", dev->name); + sprintf(action_str, "ACTION=%s", action); + + i = 0; + argv[i++] = hotplug_path; + argv[i++] = "net"; + argv[i] = 0; + + i = 0; + /* minimal command environment */ + envp [i++] = "HOME=/"; + envp [i++] = "PATH=/sbin:/bin:/usr/sbin:/usr/bin"; + envp [i++] = ifname; + envp [i++] = action_str; + envp [i] = 0; + + return call_usermodehelper(argv [0], argv, envp); +} +#endif diff -Naru linux-2.4.30/net/core/wireless.c linux-2.4.30.w18/net/core/wireless.c --- linux-2.4.30/net/core/wireless.c 2005-05-14 11:19:04.084251000 +0300 +++ linux-2.4.30.w18/net/core/wireless.c 2005-05-14 11:49:17.362591760 +0300 @@ -2,7 +2,7 @@ * This file implement the Wireless Extensions APIs. * * Authors : Jean Tourrilhes - HPL - - * Copyright (c) 1997-2003 Jean Tourrilhes, All Rights Reserved. + * Copyright (c) 1997-2005 Jean Tourrilhes, All Rights Reserved. * * (As all part of the Linux kernel, this file is GPL) */ @@ -48,6 +48,16 @@ * o Add common spy support : iw_handler_set_spy(), wireless_spy_update() * o Add enhanced spy support : iw_handler_set_thrspy() and event. * o Add WIRELESS_EXT version display in /proc/net/wireless + * + * v6 - 18.06.04 - Jean II + * o Change get_spydata() method for added safety + * o Remove spy #ifdef, they are always on -> cleaner code + * o Allow any size GET request if user specifies length > max + * and if request has IW_DESCR_FLAG_NOMAX flag or is SIOCGIWPRIV + * o Start migrating get_wireless_stats to struct iw_handler_def + * o Add wmb() in iw_handler_set_spy() for non-coherent archs/cpus + * Based on patch from Pavel Roskin : + * o Fix kernel data leak to user space in private handler handling */ /***************************** INCLUDES *****************************/ @@ -64,11 +74,7 @@ /**************************** CONSTANTS ****************************/ -/* Enough lenience, let's make sure things are proper... */ -#define WE_STRICT_WRITE /* Check write buffer size */ -/* I'll probably drop both the define and kernel message in the next version */ - -/* Debuging stuff */ +/* Debugging stuff */ #undef WE_IOCTL_DEBUG /* Debug IOCTL API */ #undef WE_EVENT_DEBUG /* Debug Event dispatcher */ #undef WE_SPY_DEBUG /* Debug enhanced spy support */ @@ -134,11 +140,13 @@ /* -- hole -- */ { IW_HEADER_TYPE_NULL, 0, 0, 0, 0, 0}, /* SIOCGIWAPLIST */ - { IW_HEADER_TYPE_POINT, 0, (sizeof(struct sockaddr) + sizeof(struct iw_quality)), 0, IW_MAX_AP, 0}, + { IW_HEADER_TYPE_POINT, 0, (sizeof(struct sockaddr) + sizeof(struct iw_quality)), 0, IW_MAX_AP, IW_DESCR_FLAG_NOMAX}, + /* SIOCSIWMLME */ + { IW_HEADER_TYPE_POINT, 0, 1, sizeof(struct iw_mlme), sizeof(struct iw_mlme), 0}, /* SIOCSIWSCAN */ - { IW_HEADER_TYPE_PARAM, 0, 0, 0, 0, 0}, + { IW_HEADER_TYPE_PARAM, 0, 1, 0, sizeof(struct iw_scan_req), 0}, /* SIOCGIWSCAN */ - { IW_HEADER_TYPE_POINT, 0, 1, 0, IW_SCAN_MAX_DATA, 0}, + { IW_HEADER_TYPE_POINT, 0, 1, 0, IW_SCAN_MAX_DATA, IW_DESCR_FLAG_NOMAX}, /* SIOCSIWESSID */ { IW_HEADER_TYPE_POINT, 0, 1, 0, IW_ESSID_MAX_SIZE + 1, IW_DESCR_FLAG_EVENT}, /* SIOCGIWESSID */ @@ -179,6 +187,20 @@ { IW_HEADER_TYPE_PARAM, 0, 0, 0, 0, 0}, /* SIOCGIWPOWER */ { IW_HEADER_TYPE_PARAM, 0, 0, 0, 0, 0}, + /* SIOCSIWGENIE */ + { IW_HEADER_TYPE_POINT, 0, 1, 0, IW_GENERIC_IE_MAX, 0}, + /* SIOCGIWGENIE */ + { IW_HEADER_TYPE_POINT, 0, 1, 0, IW_HEADER_TYPE_POINT, 0}, + /* SIOCSIWAUTH */ + { IW_HEADER_TYPE_PARAM, 0, 0, 0, 0, 0}, + /* SIOCGIWAUTH */ + { IW_HEADER_TYPE_PARAM, 0, 0, 0, 0, 0}, + /* SIOCSIWENCODEEXT */ + { IW_HEADER_TYPE_POINT, 0, 1, sizeof(struct iw_encode_ext), sizeof(struct iw_encode_ext) + IW_ENCODING_TOKEN_MAX, 0}, + /* SIOCGIWENCODEEXT */ + { IW_HEADER_TYPE_POINT, 0, 1, sizeof(struct iw_encode_ext), sizeof(struct iw_encode_ext) + IW_ENCODING_TOKEN_MAX, 0}, + /* SIOCSIWPMKSA */ + { IW_HEADER_TYPE_POINT, 0, 1, sizeof(struct iw_pmksa), sizeof(struct iw_pmksa), 0}, }; static const int standard_ioctl_num = (sizeof(standard_ioctl) / sizeof(struct iw_ioctl_description)); @@ -198,12 +220,22 @@ { IW_HEADER_TYPE_ADDR, 0, 0, 0, 0, 0}, /* IWEVEXPIRED */ { IW_HEADER_TYPE_ADDR, 0, 0, 0, 0, 0}, + /* IWEVGENIE */ + { IW_HEADER_TYPE_POINT, 0, 1, 0, IW_GENERIC_IE_MAX, 0}, + /* IWEVMICHAELMICFAILURE */ + { IW_HEADER_TYPE_POINT, 0, 1, 0, sizeof(struct iw_michaelmicfailure), 0}, + /* IWEVASSOCREQIE */ + { IW_HEADER_TYPE_POINT, 0, 1, 0, IW_GENERIC_IE_MAX, 0}, + /* IWEVASSOCRESPIE */ + { IW_HEADER_TYPE_POINT, 0, 1, 0, IW_GENERIC_IE_MAX, 0}, + /* IWEVPMKIDCAND */ + { IW_HEADER_TYPE_POINT, 0, 1, 0, sizeof(struct iw_pmkid_cand), 0}, }; static const int standard_event_num = (sizeof(standard_event) / sizeof(struct iw_ioctl_description)); /* Size (in bytes) of the various private data types */ -static const char priv_type_size[] = { +static const char iw_priv_type_size[] = { 0, /* IW_PRIV_TYPE_NONE */ 1, /* IW_PRIV_TYPE_BYTE */ 1, /* IW_PRIV_TYPE_CHAR */ @@ -270,12 +302,15 @@ */ static inline struct iw_statistics *get_wireless_stats(struct net_device *dev) { + /* New location */ + if((dev->wireless_handlers != NULL) && + (dev->wireless_handlers->get_wireless_stats != NULL)) + return dev->wireless_handlers->get_wireless_stats(dev); + + /* Old location, will be phased out in next WE */ return (dev->get_wireless_stats ? dev->get_wireless_stats(dev) : (struct iw_statistics *) NULL); - /* In the future, get_wireless_stats may move from 'struct net_device' - * to 'struct iw_handler_def', to de-bloat struct net_device. - * Definitely worse a thought... */ } /* ---------------------------------------------------------------- */ @@ -310,14 +345,32 @@ /* ---------------------------------------------------------------- */ /* - * Number of private arguments + * Calculate size of private arguments */ static inline int get_priv_size(__u16 args) { int num = args & IW_PRIV_SIZE_MASK; int type = (args & IW_PRIV_TYPE_MASK) >> 12; - return num * priv_type_size[type]; + return num * iw_priv_type_size[type]; +} + +/* ---------------------------------------------------------------- */ +/* + * Re-calculate the size of private arguments + */ +static inline int adjust_priv_size(__u16 args, + union iwreq_data * wrqu) +{ + int num = wrqu->data.length; + int max = args & IW_PRIV_SIZE_MASK; + int type = (args & IW_PRIV_TYPE_MASK) >> 12; + + /* Make sure the driver doesn't goof up */ + if (max < num) + num = max; + + return num * iw_priv_type_size[type]; } @@ -350,11 +403,14 @@ dev->name, stats->status, stats->qual.qual, - stats->qual.updated & 1 ? '.' : ' ', + stats->qual.updated & IW_QUAL_QUAL_UPDATED + ? '.' : ' ', ((__u8) stats->qual.level), - stats->qual.updated & 2 ? '.' : ' ', + stats->qual.updated & IW_QUAL_LEVEL_UPDATED + ? '.' : ' ', ((__u8) stats->qual.noise), - stats->qual.updated & 4 ? '.' : ' ', + stats->qual.updated & IW_QUAL_NOISE_UPDATED + ? '.' : ' ', stats->discard.nwid, stats->discard.code, stats->discard.fragment, @@ -470,13 +526,15 @@ /* Check NULL pointer */ if(iwr->u.data.pointer == NULL) return -EFAULT; -#ifdef WE_STRICT_WRITE + /* Check if there is enough buffer up there */ if(iwr->u.data.length < dev->wireless_handlers->num_private_args) { - printk(KERN_ERR "%s (WE) : Buffer for request SIOCGIWPRIV too small (%d<%d)\n", dev->name, iwr->u.data.length, dev->wireless_handlers->num_private_args); + /* User space can't know in advance how large the buffer + * needs to be. Give it a hint, so that we can support + * any size buffer we want somewhat efficiently... */ + iwr->u.data.length = dev->wireless_handlers->num_private_args; return -E2BIG; } -#endif /* WE_STRICT_WRITE */ /* Set the number of available ioctls. */ iwr->u.data.length = dev->wireless_handlers->num_private_args; @@ -505,7 +563,6 @@ const struct iw_ioctl_description * descr; struct iw_request_info info; int ret = -EINVAL; - int user_size = 0; /* Get the description of the IOCTL */ if((cmd - SIOCIWFIRST) >= standard_ioctl_num) @@ -536,8 +593,14 @@ #endif /* WE_SET_EVENT */ } else { char * extra; + int extra_size; + int user_length = 0; int err; + /* Calculate space needed by arguments. Always allocate + * for max space. Easier, and won't last long... */ + extra_size = descr->max_tokens * descr->token_size; + /* Check what user space is giving us */ if(IW_IS_SET(cmd)) { /* Check NULL pointer */ @@ -554,18 +617,33 @@ if(iwr->u.data.pointer == NULL) return -EFAULT; /* Save user space buffer size for checking */ - user_size = iwr->u.data.length; + user_length = iwr->u.data.length; + + /* Don't check if user_length > max to allow forward + * compatibility. The test user_length < min is + * implied by the test at the end. */ + + /* Support for very large requests */ + if((descr->flags & IW_DESCR_FLAG_NOMAX) && + (user_length > descr->max_tokens)) { + /* Allow userspace to GET more than max so + * we can support any size GET requests. + * There is still a limit : -ENOMEM. */ + extra_size = user_length * descr->token_size; + /* Note : user_length is originally a __u16, + * and token_size is controlled by us, + * so extra_size won't get negative and + * won't overflow... */ + } } #ifdef WE_IOCTL_DEBUG printk(KERN_DEBUG "%s (WE) : Malloc %d bytes\n", - dev->name, descr->max_tokens * descr->token_size); + dev->name, extra_size); #endif /* WE_IOCTL_DEBUG */ - /* Always allocate for max space. Easier, and won't last - * long... */ - extra = kmalloc(descr->max_tokens * descr->token_size, - GFP_KERNEL); + /* Create the kernel buffer */ + extra = kmalloc(extra_size, GFP_KERNEL); if (extra == NULL) { return -ENOMEM; } @@ -591,14 +669,11 @@ /* If we have something to return to the user */ if (!ret && IW_IS_GET(cmd)) { -#ifdef WE_STRICT_WRITE /* Check if there is enough buffer up there */ - if(user_size < iwr->u.data.length) { - printk(KERN_ERR "%s (WE) : Buffer for request %04X too small (%d<%d)\n", dev->name, cmd, user_size, iwr->u.data.length); + if(user_length < iwr->u.data.length) { kfree(extra); return -E2BIG; } -#endif /* WE_STRICT_WRITE */ err = copy_to_user(iwr->u.data.pointer, extra, iwr->u.data.length * @@ -661,7 +736,7 @@ iw_handler handler) { struct iwreq * iwr = (struct iwreq *) ifr; - struct iw_priv_args * descr = NULL; + const struct iw_priv_args * descr = NULL; struct iw_request_info info; int extra_size = 0; int i; @@ -701,7 +776,7 @@ ((extra_size + offset) <= IFNAMSIZ)) extra_size = 0; } else { - /* Size of set arguments */ + /* Size of get arguments */ extra_size = get_priv_size(descr->get_args); /* Does it fits in iwr ? */ @@ -771,6 +846,14 @@ /* If we have something to return to the user */ if (!ret && IW_IS_GET(cmd)) { + + /* Adjust for the actual length if it's variable, + * avoid leaking kernel bits outside. */ + if (!(descr->get_args & IW_PRIV_SIZE_FIXED)) { + extra_size = adjust_priv_size(descr->get_args, + &(iwr->u)); + } + err = copy_to_user(iwr->u.data.pointer, extra, extra_size); if (err) @@ -1042,9 +1125,25 @@ * One of the main advantage of centralising spy support here is that * it becomes much easier to improve and extend it without having to touch * the drivers. One example is the addition of the Spy-Threshold events. - * Note : IW_WIRELESS_SPY is defined in iw_handler.h */ +/* ---------------------------------------------------------------- */ +/* + * Return the pointer to the spy data in the driver. + * Because this is called on the Rx path via wireless_spy_update(), + * we want it to be efficient... + */ +static inline struct iw_spy_data * get_spydata(struct net_device *dev) +{ + /* This is the new way */ + if(dev->wireless_data) + return(dev->wireless_data->spy_data); + + /* This is the old way. Doesn't work for multi-headed drivers. + * It will be removed in the next version of WE. */ + return (dev->priv + dev->wireless_handlers->spy_offset); +} + /*------------------------------------------------------------------*/ /* * Standard Wireless Handler : set Spy List @@ -1054,16 +1153,26 @@ union iwreq_data * wrqu, char * extra) { -#ifdef IW_WIRELESS_SPY - struct iw_spy_data * spydata = (dev->priv + - dev->wireless_handlers->spy_offset); + struct iw_spy_data * spydata = get_spydata(dev); struct sockaddr * address = (struct sockaddr *) extra; + /* Make sure driver is not buggy or using the old API */ + if(!spydata) + return -EOPNOTSUPP; + /* Disable spy collection while we copy the addresses. - * As we don't disable interrupts, we need to do this to avoid races. - * As we are the only writer, this is good enough. */ + * While we copy addresses, any call to wireless_spy_update() + * will NOP. This is OK, as anyway the addresses are changing. */ spydata->spy_number = 0; + /* We want to operate without locking, because wireless_spy_update() + * most likely will happen in the interrupt handler, and therefore + * have its own locking constraints and needs performance. + * The rtnl_lock() make sure we don't race with the other iw_handlers. + * This make sure wireless_spy_update() "see" that the spy list + * is temporarily disabled. */ + wmb(); + /* Are there are addresses to copy? */ if(wrqu->data.length > 0) { int i; @@ -1089,13 +1198,14 @@ spydata->spy_address[i][5]); #endif /* WE_SPY_DEBUG */ } + + /* Make sure above is updated before re-enabling */ + wmb(); + /* Enable addresses */ spydata->spy_number = wrqu->data.length; return 0; -#else /* IW_WIRELESS_SPY */ - return -EOPNOTSUPP; -#endif /* IW_WIRELESS_SPY */ } /*------------------------------------------------------------------*/ @@ -1107,12 +1217,14 @@ union iwreq_data * wrqu, char * extra) { -#ifdef IW_WIRELESS_SPY - struct iw_spy_data * spydata = (dev->priv + - dev->wireless_handlers->spy_offset); + struct iw_spy_data * spydata = get_spydata(dev); struct sockaddr * address = (struct sockaddr *) extra; int i; + /* Make sure driver is not buggy or using the old API */ + if(!spydata) + return -EOPNOTSUPP; + wrqu->data.length = spydata->spy_number; /* Copy addresses. */ @@ -1129,9 +1241,6 @@ for(i = 0; i < spydata->spy_number; i++) spydata->spy_stat[i].updated = 0; return 0; -#else /* IW_WIRELESS_SPY */ - return -EOPNOTSUPP; -#endif /* IW_WIRELESS_SPY */ } /*------------------------------------------------------------------*/ @@ -1143,11 +1252,13 @@ union iwreq_data * wrqu, char * extra) { -#ifdef IW_WIRELESS_THRSPY - struct iw_spy_data * spydata = (dev->priv + - dev->wireless_handlers->spy_offset); + struct iw_spy_data * spydata = get_spydata(dev); struct iw_thrspy * threshold = (struct iw_thrspy *) extra; + /* Make sure driver is not buggy or using the old API */ + if(!spydata) + return -EOPNOTSUPP; + /* Just do it */ memcpy(&(spydata->spy_thr_low), &(threshold->low), 2 * sizeof(struct iw_quality)); @@ -1160,9 +1271,6 @@ #endif /* WE_SPY_DEBUG */ return 0; -#else /* IW_WIRELESS_THRSPY */ - return -EOPNOTSUPP; -#endif /* IW_WIRELESS_THRSPY */ } /*------------------------------------------------------------------*/ @@ -1174,22 +1282,20 @@ union iwreq_data * wrqu, char * extra) { -#ifdef IW_WIRELESS_THRSPY - struct iw_spy_data * spydata = (dev->priv + - dev->wireless_handlers->spy_offset); + struct iw_spy_data * spydata = get_spydata(dev); struct iw_thrspy * threshold = (struct iw_thrspy *) extra; + /* Make sure driver is not buggy or using the old API */ + if(!spydata) + return -EOPNOTSUPP; + /* Just do it */ memcpy(&(threshold->low), &(spydata->spy_thr_low), 2 * sizeof(struct iw_quality)); return 0; -#else /* IW_WIRELESS_THRSPY */ - return -EOPNOTSUPP; -#endif /* IW_WIRELESS_THRSPY */ } -#ifdef IW_WIRELESS_THRSPY /*------------------------------------------------------------------*/ /* * Prepare and send a Spy Threshold event @@ -1227,7 +1333,6 @@ /* Send event to user space */ wireless_send_event(dev, SIOCGIWTHRSPY, &wrqu, (char *) &threshold); } -#endif /* IW_WIRELESS_THRSPY */ /* ---------------------------------------------------------------- */ /* @@ -1240,12 +1345,14 @@ unsigned char * address, struct iw_quality * wstats) { -#ifdef IW_WIRELESS_SPY - struct iw_spy_data * spydata = (dev->priv + - dev->wireless_handlers->spy_offset); + struct iw_spy_data * spydata = get_spydata(dev); int i; int match = -1; + /* Make sure driver is not buggy or using the old API */ + if(!spydata) + return; + #ifdef WE_SPY_DEBUG printk(KERN_DEBUG "wireless_spy_update() : offset %ld, spydata %p, address %02X:%02X:%02X:%02X:%02X:%02X\n", dev->wireless_handlers->spy_offset, spydata, address[0], address[1], address[2], address[3], address[4], address[5]); #endif /* WE_SPY_DEBUG */ @@ -1257,7 +1364,7 @@ sizeof(struct iw_quality)); match = i; } -#ifdef IW_WIRELESS_THRSPY + /* Generate an event if we cross the spy threshold. * To avoid event storms, we have a simple hysteresis : we generate * event only when we go under the low threshold or above the @@ -1277,6 +1384,4 @@ } } } -#endif /* IW_WIRELESS_THRSPY */ -#endif /* IW_WIRELESS_SPY */ }