From 63e690caf24e8f43ba019fe1107669746b072d80 Mon Sep 17 00:00:00 2001 From: Andrea Gelmini Date: Mon, 8 Mar 2010 13:13:07 +0100 Subject: netfilter: include/linux/netfilter/nf_conntrack_tuple_common.h: Checkpatch cleanup include/linux/netfilter/nf_conntrack_tuple_common.h:5: ERROR: open brace '{' following enum go on the same line Signed-off-by: Andrea Gelmini Acked-by: David S. Miller Signed-off-by: Patrick McHardy --- include/linux/netfilter/nf_conntrack_tuple_common.h | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'include/linux') diff --git a/include/linux/netfilter/nf_conntrack_tuple_common.h b/include/linux/netfilter/nf_conntrack_tuple_common.h index 8e145f0d61c..2ea22b018a8 100644 --- a/include/linux/netfilter/nf_conntrack_tuple_common.h +++ b/include/linux/netfilter/nf_conntrack_tuple_common.h @@ -1,8 +1,7 @@ #ifndef _NF_CONNTRACK_TUPLE_COMMON_H #define _NF_CONNTRACK_TUPLE_COMMON_H -enum ip_conntrack_dir -{ +enum ip_conntrack_dir { IP_CT_DIR_ORIGINAL, IP_CT_DIR_REPLY, IP_CT_DIR_MAX -- cgit v1.2.3 From 7b4df05537f4e6c0c3524055ece7f99b5c98cc87 Mon Sep 17 00:00:00 2001 From: YOSHIFUJI Hideaki Date: Mon, 8 Mar 2010 13:17:01 +0100 Subject: netfilter: remove stale declaration for ip6_masked_addrcmp() Commit f2ffd9ee... ("[NETFILTER]: Move ip6_masked_addrcmp to include/net/ipv6.h") replaced ip6_masked_addrcmp() with ipv6_masked_addr_cmp(). Function definition went away. Let's remove its declaration as well in header file. Signed-off-by: YOSHIFUJI Hideaki Signed-off-by: Patrick McHardy --- include/linux/netfilter_ipv6/ip6_tables.h | 4 ---- 1 file changed, 4 deletions(-) (limited to 'include/linux') diff --git a/include/linux/netfilter_ipv6/ip6_tables.h b/include/linux/netfilter_ipv6/ip6_tables.h index e5ba03d783c..18442ff19c0 100644 --- a/include/linux/netfilter_ipv6/ip6_tables.h +++ b/include/linux/netfilter_ipv6/ip6_tables.h @@ -316,10 +316,6 @@ extern int ip6t_ext_hdr(u8 nexthdr); extern int ipv6_find_hdr(const struct sk_buff *skb, unsigned int *offset, int target, unsigned short *fragoff); -extern int ip6_masked_addrcmp(const struct in6_addr *addr1, - const struct in6_addr *mask, - const struct in6_addr *addr2); - #define IP6T_ALIGN(s) XT_ALIGN(s) #ifdef CONFIG_COMPAT -- cgit v1.2.3 From 0a9627f2649a02bea165cfd529d7bcb625c2fcad Mon Sep 17 00:00:00 2001 From: Tom Herbert Date: Tue, 16 Mar 2010 08:03:29 +0000 Subject: rps: Receive Packet Steering This patch implements software receive side packet steering (RPS). RPS distributes the load of received packet processing across multiple CPUs. Problem statement: Protocol processing done in the NAPI context for received packets is serialized per device queue and becomes a bottleneck under high packet load. This substantially limits pps that can be achieved on a single queue NIC and provides no scaling with multiple cores. This solution queues packets early on in the receive path on the backlog queues of other CPUs. This allows protocol processing (e.g. IP and TCP) to be performed on packets in parallel. For each device (or each receive queue in a multi-queue device) a mask of CPUs is set to indicate the CPUs that can process packets. A CPU is selected on a per packet basis by hashing contents of the packet header (e.g. the TCP or UDP 4-tuple) and using the result to index into the CPU mask. The IPI mechanism is used to raise networking receive softirqs between CPUs. This effectively emulates in software what a multi-queue NIC can provide, but is generic requiring no device support. Many devices now provide a hash over the 4-tuple on a per packet basis (e.g. the Toeplitz hash). This patch allow drivers to set the HW reported hash in an skb field, and that value in turn is used to index into the RPS maps. Using the HW generated hash can avoid cache misses on the packet when steering it to a remote CPU. The CPU mask is set on a per device and per queue basis in the sysfs variable /sys/class/net//queues/rx-/rps_cpus. This is a set of canonical bit maps for receive queues in the device (numbered by ). If a device does not support multi-queue, a single variable is used for the device (rx-0). Generally, we have found this technique increases pps capabilities of a single queue device with good CPU utilization. Optimal settings for the CPU mask seem to depend on architectures and cache hierarcy. Below are some results running 500 instances of netperf TCP_RR test with 1 byte req. and resp. Results show cumulative transaction rate and system CPU utilization. e1000e on 8 core Intel Without RPS: 108K tps at 33% CPU With RPS: 311K tps at 64% CPU forcedeth on 16 core AMD Without RPS: 156K tps at 15% CPU With RPS: 404K tps at 49% CPU bnx2x on 16 core AMD Without RPS 567K tps at 61% CPU (4 HW RX queues) Without RPS 738K tps at 96% CPU (8 HW RX queues) With RPS: 854K tps at 76% CPU (4 HW RX queues) Caveats: - The benefits of this patch are dependent on architecture and cache hierarchy. Tuning the masks to get best performance is probably necessary. - This patch adds overhead in the path for processing a single packet. In a lightly loaded server this overhead may eliminate the advantages of increased parallelism, and possibly cause some relative performance degradation. We have found that masks that are cache aware (share same caches with the interrupting CPU) mitigate much of this. - The RPS masks can be changed dynamically, however whenever the mask is changed this introduces the possibility of generating out of order packets. It's probably best not change the masks too frequently. Signed-off-by: Tom Herbert include/linux/netdevice.h | 32 ++++- include/linux/skbuff.h | 3 + net/core/dev.c | 335 +++++++++++++++++++++++++++++++++++++-------- net/core/net-sysfs.c | 225 ++++++++++++++++++++++++++++++- net/core/skbuff.c | 2 + 5 files changed, 538 insertions(+), 59 deletions(-) Signed-off-by: Eric Dumazet Signed-off-by: David S. Miller --- include/linux/netdevice.h | 32 ++++- include/linux/skbuff.h | 3 + net/core/dev.c | 335 ++++++++++++++++++++++++++++++++++++++-------- net/core/net-sysfs.c | 225 ++++++++++++++++++++++++++++++- net/core/skbuff.c | 2 + 5 files changed, 538 insertions(+), 59 deletions(-) (limited to 'include/linux') diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index c79a88be7c3..de1a52bcb9e 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -223,6 +223,7 @@ struct netif_rx_stats { unsigned dropped; unsigned time_squeeze; unsigned cpu_collision; + unsigned received_rps; }; DECLARE_PER_CPU(struct netif_rx_stats, netdev_rx_stat); @@ -530,6 +531,24 @@ struct netdev_queue { unsigned long tx_dropped; } ____cacheline_aligned_in_smp; +/* + * This structure holds an RPS map which can be of variable length. The + * map is an array of CPUs. + */ +struct rps_map { + unsigned int len; + struct rcu_head rcu; + u16 cpus[0]; +}; +#define RPS_MAP_SIZE(_num) (sizeof(struct rps_map) + (_num * sizeof(u16))) + +/* This structure contains an instance of an RX queue. */ +struct netdev_rx_queue { + struct rps_map *rps_map; + struct kobject kobj; + struct netdev_rx_queue *first; + atomic_t count; +} ____cacheline_aligned_in_smp; /* * This structure defines the management hooks for network devices. @@ -878,6 +897,13 @@ struct net_device { unsigned char broadcast[MAX_ADDR_LEN]; /* hw bcast add */ + struct kset *queues_kset; + + struct netdev_rx_queue *_rx; + + /* Number of RX queues allocated at alloc_netdev_mq() time */ + unsigned int num_rx_queues; + struct netdev_queue rx_queue; struct netdev_queue *_tx ____cacheline_aligned_in_smp; @@ -1311,14 +1337,16 @@ static inline int unregister_gifconf(unsigned int family) */ struct softnet_data { struct Qdisc *output_queue; - struct sk_buff_head input_pkt_queue; struct list_head poll_list; struct sk_buff *completion_queue; + /* Elements below can be accessed between CPUs for RPS */ + struct call_single_data csd ____cacheline_aligned_in_smp; + struct sk_buff_head input_pkt_queue; struct napi_struct backlog; }; -DECLARE_PER_CPU(struct softnet_data,softnet_data); +DECLARE_PER_CPU_ALIGNED(struct softnet_data, softnet_data); #define HAVE_NETIF_QUEUE diff --git a/include/linux/skbuff.h b/include/linux/skbuff.h index 03f816a9b65..def10b064f2 100644 --- a/include/linux/skbuff.h +++ b/include/linux/skbuff.h @@ -300,6 +300,7 @@ typedef unsigned char *sk_buff_data_t; * @nfct_reasm: netfilter conntrack re-assembly pointer * @nf_bridge: Saved data about a bridged frame - see br_netfilter.c * @skb_iif: ifindex of device we arrived on + * @rxhash: the packet hash computed on receive * @queue_mapping: Queue mapping for multiqueue devices * @tc_index: Traffic control index * @tc_verd: traffic control verdict @@ -375,6 +376,8 @@ struct sk_buff { #endif #endif + __u32 rxhash; + kmemcheck_bitfield_begin(flags2); __u16 queue_mapping:16; #ifdef CONFIG_IPV6_NDISC_NODETYPE diff --git a/net/core/dev.c b/net/core/dev.c index bcc490cc945..17b16867150 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -1931,7 +1931,7 @@ out_kfree_skb: return rc; } -static u32 skb_tx_hashrnd; +static u32 hashrnd __read_mostly; u16 skb_tx_hash(const struct net_device *dev, const struct sk_buff *skb) { @@ -1949,7 +1949,7 @@ u16 skb_tx_hash(const struct net_device *dev, const struct sk_buff *skb) else hash = skb->protocol; - hash = jhash_1word(hash, skb_tx_hashrnd); + hash = jhash_1word(hash, hashrnd); return (u16) (((u64) hash * dev->real_num_tx_queues) >> 32); } @@ -1959,10 +1959,9 @@ static inline u16 dev_cap_txqueue(struct net_device *dev, u16 queue_index) { if (unlikely(queue_index >= dev->real_num_tx_queues)) { if (net_ratelimit()) { - WARN(1, "%s selects TX queue %d, but " + netdev_warn(dev, "selects TX queue %d, but " "real number of TX queues is %d\n", - dev->name, queue_index, - dev->real_num_tx_queues); + queue_index, dev->real_num_tx_queues); } return 0; } @@ -2175,6 +2174,172 @@ int weight_p __read_mostly = 64; /* old backlog weight */ DEFINE_PER_CPU(struct netif_rx_stats, netdev_rx_stat) = { 0, }; +/* + * get_rps_cpu is called from netif_receive_skb and returns the target + * CPU from the RPS map of the receiving queue for a given skb. + */ +static int get_rps_cpu(struct net_device *dev, struct sk_buff *skb) +{ + struct ipv6hdr *ip6; + struct iphdr *ip; + struct netdev_rx_queue *rxqueue; + struct rps_map *map; + int cpu = -1; + u8 ip_proto; + u32 addr1, addr2, ports, ihl; + + rcu_read_lock(); + + if (skb_rx_queue_recorded(skb)) { + u16 index = skb_get_rx_queue(skb); + if (unlikely(index >= dev->num_rx_queues)) { + if (net_ratelimit()) { + netdev_warn(dev, "received packet on queue " + "%u, but number of RX queues is %u\n", + index, dev->num_rx_queues); + } + goto done; + } + rxqueue = dev->_rx + index; + } else + rxqueue = dev->_rx; + + if (!rxqueue->rps_map) + goto done; + + if (skb->rxhash) + goto got_hash; /* Skip hash computation on packet header */ + + switch (skb->protocol) { + case __constant_htons(ETH_P_IP): + if (!pskb_may_pull(skb, sizeof(*ip))) + goto done; + + ip = (struct iphdr *) skb->data; + ip_proto = ip->protocol; + addr1 = ip->saddr; + addr2 = ip->daddr; + ihl = ip->ihl; + break; + case __constant_htons(ETH_P_IPV6): + if (!pskb_may_pull(skb, sizeof(*ip6))) + goto done; + + ip6 = (struct ipv6hdr *) skb->data; + ip_proto = ip6->nexthdr; + addr1 = ip6->saddr.s6_addr32[3]; + addr2 = ip6->daddr.s6_addr32[3]; + ihl = (40 >> 2); + break; + default: + goto done; + } + ports = 0; + switch (ip_proto) { + case IPPROTO_TCP: + case IPPROTO_UDP: + case IPPROTO_DCCP: + case IPPROTO_ESP: + case IPPROTO_AH: + case IPPROTO_SCTP: + case IPPROTO_UDPLITE: + if (pskb_may_pull(skb, (ihl * 4) + 4)) + ports = *((u32 *) (skb->data + (ihl * 4))); + break; + + default: + break; + } + + skb->rxhash = jhash_3words(addr1, addr2, ports, hashrnd); + if (!skb->rxhash) + skb->rxhash = 1; + +got_hash: + map = rcu_dereference(rxqueue->rps_map); + if (map) { + u16 tcpu = map->cpus[((u64) skb->rxhash * map->len) >> 32]; + + if (cpu_online(tcpu)) { + cpu = tcpu; + goto done; + } + } + +done: + rcu_read_unlock(); + return cpu; +} + +/* + * This structure holds the per-CPU mask of CPUs for which IPIs are scheduled + * to be sent to kick remote softirq processing. There are two masks since + * the sending of IPIs must be done with interrupts enabled. The select field + * indicates the current mask that enqueue_backlog uses to schedule IPIs. + * select is flipped before net_rps_action is called while still under lock, + * net_rps_action then uses the non-selected mask to send the IPIs and clears + * it without conflicting with enqueue_backlog operation. + */ +struct rps_remote_softirq_cpus { + cpumask_t mask[2]; + int select; +}; +static DEFINE_PER_CPU(struct rps_remote_softirq_cpus, rps_remote_softirq_cpus); + +/* Called from hardirq (IPI) context */ +static void trigger_softirq(void *data) +{ + struct softnet_data *queue = data; + __napi_schedule(&queue->backlog); + __get_cpu_var(netdev_rx_stat).received_rps++; +} + +/* + * enqueue_to_backlog is called to queue an skb to a per CPU backlog + * queue (may be a remote CPU queue). + */ +static int enqueue_to_backlog(struct sk_buff *skb, int cpu) +{ + struct softnet_data *queue; + unsigned long flags; + + queue = &per_cpu(softnet_data, cpu); + + local_irq_save(flags); + __get_cpu_var(netdev_rx_stat).total++; + + spin_lock(&queue->input_pkt_queue.lock); + if (queue->input_pkt_queue.qlen <= netdev_max_backlog) { + if (queue->input_pkt_queue.qlen) { +enqueue: + __skb_queue_tail(&queue->input_pkt_queue, skb); + spin_unlock_irqrestore(&queue->input_pkt_queue.lock, + flags); + return NET_RX_SUCCESS; + } + + /* Schedule NAPI for backlog device */ + if (napi_schedule_prep(&queue->backlog)) { + if (cpu != smp_processor_id()) { + struct rps_remote_softirq_cpus *rcpus = + &__get_cpu_var(rps_remote_softirq_cpus); + + cpu_set(cpu, rcpus->mask[rcpus->select]); + __raise_softirq_irqoff(NET_RX_SOFTIRQ); + } else + __napi_schedule(&queue->backlog); + } + goto enqueue; + } + + spin_unlock(&queue->input_pkt_queue.lock); + + __get_cpu_var(netdev_rx_stat).dropped++; + local_irq_restore(flags); + + kfree_skb(skb); + return NET_RX_DROP; +} /** * netif_rx - post buffer to the network code @@ -2193,8 +2358,7 @@ DEFINE_PER_CPU(struct netif_rx_stats, netdev_rx_stat) = { 0, }; int netif_rx(struct sk_buff *skb) { - struct softnet_data *queue; - unsigned long flags; + int cpu; /* if netpoll wants it, pretend we never saw it */ if (netpoll_rx(skb)) @@ -2203,31 +2367,11 @@ int netif_rx(struct sk_buff *skb) if (!skb->tstamp.tv64) net_timestamp(skb); - /* - * The code is rearranged so that the path is the most - * short when CPU is congested, but is still operating. - */ - local_irq_save(flags); - queue = &__get_cpu_var(softnet_data); - - __get_cpu_var(netdev_rx_stat).total++; - if (queue->input_pkt_queue.qlen <= netdev_max_backlog) { - if (queue->input_pkt_queue.qlen) { -enqueue: - __skb_queue_tail(&queue->input_pkt_queue, skb); - local_irq_restore(flags); - return NET_RX_SUCCESS; - } - - napi_schedule(&queue->backlog); - goto enqueue; - } - - __get_cpu_var(netdev_rx_stat).dropped++; - local_irq_restore(flags); + cpu = get_rps_cpu(skb->dev, skb); + if (cpu < 0) + cpu = smp_processor_id(); - kfree_skb(skb); - return NET_RX_DROP; + return enqueue_to_backlog(skb, cpu); } EXPORT_SYMBOL(netif_rx); @@ -2464,22 +2608,7 @@ void netif_nit_deliver(struct sk_buff *skb) rcu_read_unlock(); } -/** - * netif_receive_skb - process receive buffer from network - * @skb: buffer to process - * - * netif_receive_skb() is the main receive data processing function. - * It always succeeds. The buffer may be dropped during processing - * for congestion control or by the protocol layers. - * - * This function may only be called from softirq context and interrupts - * should be enabled. - * - * Return values (usually ignored): - * NET_RX_SUCCESS: no congestion - * NET_RX_DROP: packet was dropped - */ -int netif_receive_skb(struct sk_buff *skb) +int __netif_receive_skb(struct sk_buff *skb) { struct packet_type *ptype, *pt_prev; struct net_device *orig_dev; @@ -2588,6 +2717,33 @@ out: rcu_read_unlock(); return ret; } + +/** + * netif_receive_skb - process receive buffer from network + * @skb: buffer to process + * + * netif_receive_skb() is the main receive data processing function. + * It always succeeds. The buffer may be dropped during processing + * for congestion control or by the protocol layers. + * + * This function may only be called from softirq context and interrupts + * should be enabled. + * + * Return values (usually ignored): + * NET_RX_SUCCESS: no congestion + * NET_RX_DROP: packet was dropped + */ +int netif_receive_skb(struct sk_buff *skb) +{ + int cpu; + + cpu = get_rps_cpu(skb->dev, skb); + + if (cpu < 0) + return __netif_receive_skb(skb); + else + return enqueue_to_backlog(skb, cpu); +} EXPORT_SYMBOL(netif_receive_skb); /* Network device is going away, flush any packets still pending */ @@ -2914,16 +3070,16 @@ static int process_backlog(struct napi_struct *napi, int quota) do { struct sk_buff *skb; - local_irq_disable(); + spin_lock_irq(&queue->input_pkt_queue.lock); skb = __skb_dequeue(&queue->input_pkt_queue); if (!skb) { __napi_complete(napi); - local_irq_enable(); + spin_unlock_irq(&queue->input_pkt_queue.lock); break; } - local_irq_enable(); + spin_unlock_irq(&queue->input_pkt_queue.lock); - netif_receive_skb(skb); + __netif_receive_skb(skb); } while (++work < quota && jiffies == start_time); return work; @@ -3012,6 +3168,22 @@ void netif_napi_del(struct napi_struct *napi) } EXPORT_SYMBOL(netif_napi_del); +/* + * net_rps_action sends any pending IPI's for rps. This is only called from + * softirq and interrupts must be enabled. + */ +static void net_rps_action(cpumask_t *mask) +{ + int cpu; + + /* Send pending IPI's to kick RPS processing on remote cpus. */ + for_each_cpu_mask_nr(cpu, *mask) { + struct softnet_data *queue = &per_cpu(softnet_data, cpu); + if (cpu_online(cpu)) + __smp_call_function_single(cpu, &queue->csd, 0); + } + cpus_clear(*mask); +} static void net_rx_action(struct softirq_action *h) { @@ -3019,6 +3191,8 @@ static void net_rx_action(struct softirq_action *h) unsigned long time_limit = jiffies + 2; int budget = netdev_budget; void *have; + int select; + struct rps_remote_softirq_cpus *rcpus; local_irq_disable(); @@ -3081,8 +3255,14 @@ static void net_rx_action(struct softirq_action *h) netpoll_poll_unlock(have); } out: + rcpus = &__get_cpu_var(rps_remote_softirq_cpus); + select = rcpus->select; + rcpus->select ^= 1; + local_irq_enable(); + net_rps_action(&rcpus->mask[select]); + #ifdef CONFIG_NET_DMA /* * There may not be any more sk_buffs coming right now, so push @@ -3327,10 +3507,10 @@ static int softnet_seq_show(struct seq_file *seq, void *v) { struct netif_rx_stats *s = v; - seq_printf(seq, "%08x %08x %08x %08x %08x %08x %08x %08x %08x\n", + seq_printf(seq, "%08x %08x %08x %08x %08x %08x %08x %08x %08x %08x\n", s->total, s->dropped, s->time_squeeze, 0, 0, 0, 0, 0, /* was fastroute */ - s->cpu_collision); + s->cpu_collision, s->received_rps); return 0; } @@ -5067,6 +5247,23 @@ int register_netdevice(struct net_device *dev) dev->iflink = -1; + if (!dev->num_rx_queues) { + /* + * Allocate a single RX queue if driver never called + * alloc_netdev_mq + */ + + dev->_rx = kzalloc(sizeof(struct netdev_rx_queue), GFP_KERNEL); + if (!dev->_rx) { + ret = -ENOMEM; + goto out; + } + + dev->_rx->first = dev->_rx; + atomic_set(&dev->_rx->count, 1); + dev->num_rx_queues = 1; + } + /* Init, if this function is available */ if (dev->netdev_ops->ndo_init) { ret = dev->netdev_ops->ndo_init(dev); @@ -5424,9 +5621,11 @@ struct net_device *alloc_netdev_mq(int sizeof_priv, const char *name, void (*setup)(struct net_device *), unsigned int queue_count) { struct netdev_queue *tx; + struct netdev_rx_queue *rx; struct net_device *dev; size_t alloc_size; struct net_device *p; + int i; BUG_ON(strlen(name) >= sizeof(dev->name)); @@ -5452,11 +5651,27 @@ struct net_device *alloc_netdev_mq(int sizeof_priv, const char *name, goto free_p; } + rx = kcalloc(queue_count, sizeof(struct netdev_rx_queue), GFP_KERNEL); + if (!rx) { + printk(KERN_ERR "alloc_netdev: Unable to allocate " + "rx queues.\n"); + goto free_tx; + } + + atomic_set(&rx->count, queue_count); + + /* + * Set a pointer to first element in the array which holds the + * reference count. + */ + for (i = 0; i < queue_count; i++) + rx[i].first = rx; + dev = PTR_ALIGN(p, NETDEV_ALIGN); dev->padded = (char *)dev - (char *)p; if (dev_addr_init(dev)) - goto free_tx; + goto free_rx; dev_unicast_init(dev); @@ -5466,6 +5681,9 @@ struct net_device *alloc_netdev_mq(int sizeof_priv, const char *name, dev->num_tx_queues = queue_count; dev->real_num_tx_queues = queue_count; + dev->_rx = rx; + dev->num_rx_queues = queue_count; + dev->gso_max_size = GSO_MAX_SIZE; netdev_init_queues(dev); @@ -5480,9 +5698,10 @@ struct net_device *alloc_netdev_mq(int sizeof_priv, const char *name, strcpy(dev->name, name); return dev; +free_rx: + kfree(rx); free_tx: kfree(tx); - free_p: kfree(p); return NULL; @@ -5985,6 +6204,10 @@ static int __init net_dev_init(void) queue->completion_queue = NULL; INIT_LIST_HEAD(&queue->poll_list); + queue->csd.func = trigger_softirq; + queue->csd.info = queue; + queue->csd.flags = 0; + queue->backlog.poll = process_backlog; queue->backlog.weight = weight_p; queue->backlog.gro_list = NULL; @@ -6023,7 +6246,7 @@ subsys_initcall(net_dev_init); static int __init initialize_hashrnd(void) { - get_random_bytes(&skb_tx_hashrnd, sizeof(skb_tx_hashrnd)); + get_random_bytes(&hashrnd, sizeof(hashrnd)); return 0; } diff --git a/net/core/net-sysfs.c b/net/core/net-sysfs.c index 099c753c421..7a46343d5ae 100644 --- a/net/core/net-sysfs.c +++ b/net/core/net-sysfs.c @@ -466,6 +466,216 @@ static struct attribute_group wireless_group = { }; #endif +/* + * RX queue sysfs structures and functions. + */ +struct rx_queue_attribute { + struct attribute attr; + ssize_t (*show)(struct netdev_rx_queue *queue, + struct rx_queue_attribute *attr, char *buf); + ssize_t (*store)(struct netdev_rx_queue *queue, + struct rx_queue_attribute *attr, const char *buf, size_t len); +}; +#define to_rx_queue_attr(_attr) container_of(_attr, \ + struct rx_queue_attribute, attr) + +#define to_rx_queue(obj) container_of(obj, struct netdev_rx_queue, kobj) + +static ssize_t rx_queue_attr_show(struct kobject *kobj, struct attribute *attr, + char *buf) +{ + struct rx_queue_attribute *attribute = to_rx_queue_attr(attr); + struct netdev_rx_queue *queue = to_rx_queue(kobj); + + if (!attribute->show) + return -EIO; + + return attribute->show(queue, attribute, buf); +} + +static ssize_t rx_queue_attr_store(struct kobject *kobj, struct attribute *attr, + const char *buf, size_t count) +{ + struct rx_queue_attribute *attribute = to_rx_queue_attr(attr); + struct netdev_rx_queue *queue = to_rx_queue(kobj); + + if (!attribute->store) + return -EIO; + + return attribute->store(queue, attribute, buf, count); +} + +static struct sysfs_ops rx_queue_sysfs_ops = { + .show = rx_queue_attr_show, + .store = rx_queue_attr_store, +}; + +static ssize_t show_rps_map(struct netdev_rx_queue *queue, + struct rx_queue_attribute *attribute, char *buf) +{ + struct rps_map *map; + cpumask_var_t mask; + size_t len = 0; + int i; + + if (!zalloc_cpumask_var(&mask, GFP_KERNEL)) + return -ENOMEM; + + rcu_read_lock(); + map = rcu_dereference(queue->rps_map); + if (map) + for (i = 0; i < map->len; i++) + cpumask_set_cpu(map->cpus[i], mask); + + len += cpumask_scnprintf(buf + len, PAGE_SIZE, mask); + if (PAGE_SIZE - len < 3) { + rcu_read_unlock(); + free_cpumask_var(mask); + return -EINVAL; + } + rcu_read_unlock(); + + free_cpumask_var(mask); + len += sprintf(buf + len, "\n"); + return len; +} + +static void rps_map_release(struct rcu_head *rcu) +{ + struct rps_map *map = container_of(rcu, struct rps_map, rcu); + + kfree(map); +} + +ssize_t store_rps_map(struct netdev_rx_queue *queue, + struct rx_queue_attribute *attribute, + const char *buf, size_t len) +{ + struct rps_map *old_map, *map; + cpumask_var_t mask; + int err, cpu, i; + static DEFINE_SPINLOCK(rps_map_lock); + + if (!capable(CAP_NET_ADMIN)) + return -EPERM; + + if (!alloc_cpumask_var(&mask, GFP_KERNEL)) + return -ENOMEM; + + err = bitmap_parse(buf, len, cpumask_bits(mask), nr_cpumask_bits); + if (err) { + free_cpumask_var(mask); + return err; + } + + map = kzalloc(max_t(unsigned, + RPS_MAP_SIZE(cpumask_weight(mask)), L1_CACHE_BYTES), + GFP_KERNEL); + if (!map) { + free_cpumask_var(mask); + return -ENOMEM; + } + + i = 0; + for_each_cpu_and(cpu, mask, cpu_online_mask) + map->cpus[i++] = cpu; + + if (i) + map->len = i; + else { + kfree(map); + map = NULL; + } + + spin_lock(&rps_map_lock); + old_map = queue->rps_map; + rcu_assign_pointer(queue->rps_map, map); + spin_unlock(&rps_map_lock); + + if (old_map) + call_rcu(&old_map->rcu, rps_map_release); + + free_cpumask_var(mask); + return len; +} + +static struct rx_queue_attribute rps_cpus_attribute = + __ATTR(rps_cpus, S_IRUGO | S_IWUSR, show_rps_map, store_rps_map); + +static struct attribute *rx_queue_default_attrs[] = { + &rps_cpus_attribute.attr, + NULL +}; + +static void rx_queue_release(struct kobject *kobj) +{ + struct netdev_rx_queue *queue = to_rx_queue(kobj); + struct rps_map *map = queue->rps_map; + struct netdev_rx_queue *first = queue->first; + + if (map) + call_rcu(&map->rcu, rps_map_release); + + if (atomic_dec_and_test(&first->count)) + kfree(first); +} + +static struct kobj_type rx_queue_ktype = { + .sysfs_ops = &rx_queue_sysfs_ops, + .release = rx_queue_release, + .default_attrs = rx_queue_default_attrs, +}; + +static int rx_queue_add_kobject(struct net_device *net, int index) +{ + struct netdev_rx_queue *queue = net->_rx + index; + struct kobject *kobj = &queue->kobj; + int error = 0; + + kobj->kset = net->queues_kset; + error = kobject_init_and_add(kobj, &rx_queue_ktype, NULL, + "rx-%u", index); + if (error) { + kobject_put(kobj); + return error; + } + + kobject_uevent(kobj, KOBJ_ADD); + + return error; +} + +static int rx_queue_register_kobjects(struct net_device *net) +{ + int i; + int error = 0; + + net->queues_kset = kset_create_and_add("queues", + NULL, &net->dev.kobj); + if (!net->queues_kset) + return -ENOMEM; + for (i = 0; i < net->num_rx_queues; i++) { + error = rx_queue_add_kobject(net, i); + if (error) + break; + } + + if (error) + while (--i >= 0) + kobject_put(&net->_rx[i].kobj); + + return error; +} + +static void rx_queue_remove_kobjects(struct net_device *net) +{ + int i; + + for (i = 0; i < net->num_rx_queues; i++) + kobject_put(&net->_rx[i].kobj); + kset_unregister(net->queues_kset); +} + #endif /* CONFIG_SYSFS */ #ifdef CONFIG_HOTPLUG @@ -529,6 +739,8 @@ void netdev_unregister_kobject(struct net_device * net) if (!net_eq(dev_net(net), &init_net)) return; + rx_queue_remove_kobjects(net); + device_del(dev); } @@ -537,6 +749,7 @@ int netdev_register_kobject(struct net_device *net) { struct device *dev = &(net->dev); const struct attribute_group **groups = net->sysfs_groups; + int error = 0; dev->class = &net_class; dev->platform_data = net; @@ -563,7 +776,17 @@ int netdev_register_kobject(struct net_device *net) if (!net_eq(dev_net(net), &init_net)) return 0; - return device_add(dev); + error = device_add(dev); + if (error) + return error; + + error = rx_queue_register_kobjects(net); + if (error) { + device_del(dev); + return error; + } + + return error; } int netdev_class_create_file(struct class_attribute *class_attr) diff --git a/net/core/skbuff.c b/net/core/skbuff.c index 93c4e060c91..bdea0efdf8c 100644 --- a/net/core/skbuff.c +++ b/net/core/skbuff.c @@ -534,6 +534,7 @@ static void __copy_skb_header(struct sk_buff *new, const struct sk_buff *old) new->network_header = old->network_header; new->mac_header = old->mac_header; skb_dst_set(new, dst_clone(skb_dst(old))); + new->rxhash = old->rxhash; #ifdef CONFIG_XFRM new->sp = secpath_get(old->sp); #endif @@ -581,6 +582,7 @@ static struct sk_buff *__skb_clone(struct sk_buff *n, struct sk_buff *skb) C(len); C(data_len); C(mac_len); + C(rxhash); n->hdr_len = skb->nohdr ? skb_headroom(skb) : skb->hdr_len; n->cloned = 1; n->nohdr = 0; -- cgit v1.2.3 From 10708f37ae729baba9b67bd134c3720709d4ae62 Mon Sep 17 00:00:00 2001 From: Jan Engelhardt Date: Thu, 11 Mar 2010 09:57:29 +0000 Subject: net: core: add IFLA_STATS64 support `ip -s link` shows interface counters truncated to 32 bit. This is because interface statistics are transported only in 32-bit quantity to userspace. This commit adds a new IFLA_STATS64 attribute that exports them in full 64 bit. References: http://lkml.indiana.edu/hypermail/linux/kernel/0307.3/0215.html Signed-off-by: Jan Engelhardt Signed-off-by: David S. Miller --- include/linux/if_link.h | 33 +++++++++++++++++++++++++++++++++ net/core/rtnetlink.c | 42 +++++++++++++++++++++++++++++++++++++++++- 2 files changed, 74 insertions(+), 1 deletion(-) (limited to 'include/linux') diff --git a/include/linux/if_link.h b/include/linux/if_link.h index c9bf92cd765..cfd420ba72d 100644 --- a/include/linux/if_link.h +++ b/include/linux/if_link.h @@ -37,6 +37,38 @@ struct rtnl_link_stats { __u32 tx_compressed; }; +struct rtnl_link_stats64 { + __u64 rx_packets; /* total packets received */ + __u64 tx_packets; /* total packets transmitted */ + __u64 rx_bytes; /* total bytes received */ + __u64 tx_bytes; /* total bytes transmitted */ + __u64 rx_errors; /* bad packets received */ + __u64 tx_errors; /* packet transmit problems */ + __u64 rx_dropped; /* no space in linux buffers */ + __u64 tx_dropped; /* no space available in linux */ + __u64 multicast; /* multicast packets received */ + __u64 collisions; + + /* detailed rx_errors: */ + __u64 rx_length_errors; + __u64 rx_over_errors; /* receiver ring buff overflow */ + __u64 rx_crc_errors; /* recved pkt with crc error */ + __u64 rx_frame_errors; /* recv'd frame alignment error */ + __u64 rx_fifo_errors; /* recv'r fifo overrun */ + __u64 rx_missed_errors; /* receiver missed packet */ + + /* detailed tx_errors */ + __u64 tx_aborted_errors; + __u64 tx_carrier_errors; + __u64 tx_fifo_errors; + __u64 tx_heartbeat_errors; + __u64 tx_window_errors; + + /* for cslip etc */ + __u64 rx_compressed; + __u64 tx_compressed; +}; + /* The struct should be in sync with struct ifmap */ struct rtnl_link_ifmap { __u64 mem_start; @@ -83,6 +115,7 @@ enum { IFLA_VF_VLAN, IFLA_VF_TX_RATE, /* TX Bandwidth Allocation */ IFLA_VFINFO, + IFLA_STATS64, __IFLA_MAX }; diff --git a/net/core/rtnetlink.c b/net/core/rtnetlink.c index 4568120d853..e1121f0bca6 100644 --- a/net/core/rtnetlink.c +++ b/net/core/rtnetlink.c @@ -600,7 +600,39 @@ static void copy_rtnl_link_stats(struct rtnl_link_stats *a, a->rx_compressed = b->rx_compressed; a->tx_compressed = b->tx_compressed; -}; +} + +static void copy_rtnl_link_stats64(struct rtnl_link_stats64 *a, + const struct net_device_stats *b) +{ + a->rx_packets = b->rx_packets; + a->tx_packets = b->tx_packets; + a->rx_bytes = b->rx_bytes; + a->tx_bytes = b->tx_bytes; + a->rx_errors = b->rx_errors; + a->tx_errors = b->tx_errors; + a->rx_dropped = b->rx_dropped; + a->tx_dropped = b->tx_dropped; + + a->multicast = b->multicast; + a->collisions = b->collisions; + + a->rx_length_errors = b->rx_length_errors; + a->rx_over_errors = b->rx_over_errors; + a->rx_crc_errors = b->rx_crc_errors; + a->rx_frame_errors = b->rx_frame_errors; + a->rx_fifo_errors = b->rx_fifo_errors; + a->rx_missed_errors = b->rx_missed_errors; + + a->tx_aborted_errors = b->tx_aborted_errors; + a->tx_carrier_errors = b->tx_carrier_errors; + a->tx_fifo_errors = b->tx_fifo_errors; + a->tx_heartbeat_errors = b->tx_heartbeat_errors; + a->tx_window_errors = b->tx_window_errors; + + a->rx_compressed = b->rx_compressed; + a->tx_compressed = b->tx_compressed; +} static inline int rtnl_vfinfo_size(const struct net_device *dev) { @@ -698,6 +730,14 @@ static int rtnl_fill_ifinfo(struct sk_buff *skb, struct net_device *dev, stats = dev_get_stats(dev); copy_rtnl_link_stats(nla_data(attr), stats); + attr = nla_reserve(skb, IFLA_STATS64, + sizeof(struct rtnl_link_stats64)); + if (attr == NULL) + goto nla_put_failure; + + stats = dev_get_stats(dev); + copy_rtnl_link_stats64(nla_data(attr), stats); + if (dev->netdev_ops->ndo_get_vf_config && dev->dev.parent) { int i; struct ifla_vf_info ivi; -- cgit v1.2.3 From abf35df21513c51d7761c41fa6d3b819cdf4103e Mon Sep 17 00:00:00 2001 From: Jason Gunthorpe Date: Tue, 9 Mar 2010 09:17:42 +0000 Subject: NET: Support clause 45 MDIO commands at the MDIO bus level IEEE 802.3ae clause 45 specifies a somewhat modified MDIO protocol for use by 10GIGE phys. The main change is a 21 bit address split into a 5 bit device ID and a 16 bit register offset. The definition is designed so that normal and extended devices can run on the same MDIO bus. Extend mdio-bitbang to do the new protocol. At the MDIO bus level the protocol is requested by or'ing MII_ADDR_C45 into the register offset. Make phy_read/phy_write/etc pass a full 32 bit register offset. This does not attempt to make the phy layer support C45 style PHYs, just to provide the MDIO bus support. Tested against a Broadcom 10GE phy with ID 0x206034, and several Broadcom 10/100/1000 Phys in normal mode. Signed-off-by: Jason Gunthorpe Signed-off-by: David S. Miller --- drivers/net/phy/mdio-bitbang.c | 60 +++++++++++++++++++++++++++++++++++------- drivers/net/phy/mdio_bus.c | 4 +-- include/linux/phy.h | 12 ++++++--- 3 files changed, 61 insertions(+), 15 deletions(-) (limited to 'include/linux') diff --git a/drivers/net/phy/mdio-bitbang.c b/drivers/net/phy/mdio-bitbang.c index 2576055b350..0ff06617a4a 100644 --- a/drivers/net/phy/mdio-bitbang.c +++ b/drivers/net/phy/mdio-bitbang.c @@ -23,8 +23,13 @@ #include #include -#define MDIO_READ 1 -#define MDIO_WRITE 0 +#define MDIO_READ 2 +#define MDIO_WRITE 1 + +#define MDIO_C45 (1<<15) +#define MDIO_C45_ADDR (MDIO_C45 | 0) +#define MDIO_C45_READ (MDIO_C45 | 3) +#define MDIO_C45_WRITE (MDIO_C45 | 1) #define MDIO_SETUP_TIME 10 #define MDIO_HOLD_TIME 10 @@ -90,7 +95,7 @@ static u16 mdiobb_get_num(struct mdiobb_ctrl *ctrl, int bits) /* Utility to send the preamble, address, and * register (common to read and write). */ -static void mdiobb_cmd(struct mdiobb_ctrl *ctrl, int read, u8 phy, u8 reg) +static void mdiobb_cmd(struct mdiobb_ctrl *ctrl, int op, u8 phy, u8 reg) { const struct mdiobb_ops *ops = ctrl->ops; int i; @@ -109,23 +114,56 @@ static void mdiobb_cmd(struct mdiobb_ctrl *ctrl, int read, u8 phy, u8 reg) for (i = 0; i < 32; i++) mdiobb_send_bit(ctrl, 1); - /* send the start bit (01) and the read opcode (10) or write (10) */ + /* send the start bit (01) and the read opcode (10) or write (10). + Clause 45 operation uses 00 for the start and 11, 10 for + read/write */ mdiobb_send_bit(ctrl, 0); - mdiobb_send_bit(ctrl, 1); - mdiobb_send_bit(ctrl, read); - mdiobb_send_bit(ctrl, !read); + if (op & MDIO_C45) + mdiobb_send_bit(ctrl, 0); + else + mdiobb_send_bit(ctrl, 1); + mdiobb_send_bit(ctrl, (op >> 1) & 1); + mdiobb_send_bit(ctrl, (op >> 0) & 1); mdiobb_send_num(ctrl, phy, 5); mdiobb_send_num(ctrl, reg, 5); } +/* In clause 45 mode all commands are prefixed by MDIO_ADDR to specify the + lower 16 bits of the 21 bit address. This transfer is done identically to a + MDIO_WRITE except for a different code. To enable clause 45 mode or + MII_ADDR_C45 into the address. Theoretically clause 45 and normal devices + can exist on the same bus. Normal devices should ignore the MDIO_ADDR + phase. */ +static int mdiobb_cmd_addr(struct mdiobb_ctrl *ctrl, int phy, u32 addr) +{ + unsigned int dev_addr = (addr >> 16) & 0x1F; + unsigned int reg = addr & 0xFFFF; + mdiobb_cmd(ctrl, MDIO_C45_ADDR, phy, dev_addr); + + /* send the turnaround (10) */ + mdiobb_send_bit(ctrl, 1); + mdiobb_send_bit(ctrl, 0); + + mdiobb_send_num(ctrl, reg, 16); + + ctrl->ops->set_mdio_dir(ctrl, 0); + mdiobb_get_bit(ctrl); + + return dev_addr; +} static int mdiobb_read(struct mii_bus *bus, int phy, int reg) { struct mdiobb_ctrl *ctrl = bus->priv; int ret, i; - mdiobb_cmd(ctrl, MDIO_READ, phy, reg); + if (reg & MII_ADDR_C45) { + reg = mdiobb_cmd_addr(ctrl, phy, reg); + mdiobb_cmd(ctrl, MDIO_C45_READ, phy, reg); + } else + mdiobb_cmd(ctrl, MDIO_READ, phy, reg); + ctrl->ops->set_mdio_dir(ctrl, 0); /* check the turnaround bit: the PHY should be driving it to zero */ @@ -148,7 +186,11 @@ static int mdiobb_write(struct mii_bus *bus, int phy, int reg, u16 val) { struct mdiobb_ctrl *ctrl = bus->priv; - mdiobb_cmd(ctrl, MDIO_WRITE, phy, reg); + if (reg & MII_ADDR_C45) { + reg = mdiobb_cmd_addr(ctrl, phy, reg); + mdiobb_cmd(ctrl, MDIO_C45_WRITE, phy, reg); + } else + mdiobb_cmd(ctrl, MDIO_WRITE, phy, reg); /* send the turnaround (10) */ mdiobb_send_bit(ctrl, 1); diff --git a/drivers/net/phy/mdio_bus.c b/drivers/net/phy/mdio_bus.c index e17b70291bb..6a6b8199a0d 100644 --- a/drivers/net/phy/mdio_bus.c +++ b/drivers/net/phy/mdio_bus.c @@ -208,7 +208,7 @@ EXPORT_SYMBOL(mdiobus_scan); * because the bus read/write functions may wait for an interrupt * to conclude the operation. */ -int mdiobus_read(struct mii_bus *bus, int addr, u16 regnum) +int mdiobus_read(struct mii_bus *bus, int addr, u32 regnum) { int retval; @@ -233,7 +233,7 @@ EXPORT_SYMBOL(mdiobus_read); * because the bus read/write functions may wait for an interrupt * to conclude the operation. */ -int mdiobus_write(struct mii_bus *bus, int addr, u16 regnum, u16 val) +int mdiobus_write(struct mii_bus *bus, int addr, u32 regnum, u16 val) { int err; diff --git a/include/linux/phy.h b/include/linux/phy.h index 14d7fdf6a90..d9bce4b526b 100644 --- a/include/linux/phy.h +++ b/include/linux/phy.h @@ -81,6 +81,10 @@ typedef enum { */ #define MII_BUS_ID_SIZE (20 - 3) +/* Or MII_ADDR_C45 into regnum for read/write on mii_bus to enable the 21 bit + IEEE 802.3ae clause 45 addressing mode used by 10GIGE phy chips. */ +#define MII_ADDR_C45 (1<<30) + /* * The Bus class for PHYs. Devices which provide access to * PHYs should register using this structure @@ -127,8 +131,8 @@ int mdiobus_register(struct mii_bus *bus); void mdiobus_unregister(struct mii_bus *bus); void mdiobus_free(struct mii_bus *bus); struct phy_device *mdiobus_scan(struct mii_bus *bus, int addr); -int mdiobus_read(struct mii_bus *bus, int addr, u16 regnum); -int mdiobus_write(struct mii_bus *bus, int addr, u16 regnum, u16 val); +int mdiobus_read(struct mii_bus *bus, int addr, u32 regnum); +int mdiobus_write(struct mii_bus *bus, int addr, u32 regnum, u16 val); #define PHY_INTERRUPT_DISABLED 0x0 @@ -422,7 +426,7 @@ struct phy_fixup { * because the bus read/write functions may wait for an interrupt * to conclude the operation. */ -static inline int phy_read(struct phy_device *phydev, u16 regnum) +static inline int phy_read(struct phy_device *phydev, u32 regnum) { return mdiobus_read(phydev->bus, phydev->addr, regnum); } @@ -437,7 +441,7 @@ static inline int phy_read(struct phy_device *phydev, u16 regnum) * because the bus read/write functions may wait for an interrupt * to conclude the operation. */ -static inline int phy_write(struct phy_device *phydev, u16 regnum, u16 val) +static inline int phy_write(struct phy_device *phydev, u32 regnum, u16 val) { return mdiobus_write(phydev->bus, phydev->addr, regnum, val); } -- cgit v1.2.3 From 28b949885f80efb87d7cebdcf879c99db12c37bd Mon Sep 17 00:00:00 2001 From: Jan Engelhardt Date: Sat, 28 Feb 2009 03:23:57 +0100 Subject: netfilter: xtables: merge xt_MARK into xt_mark Two arguments for combining the two: - xt_mark is pretty useless without xt_MARK - the actual code is so small anyway that the kmod metadata and the module in its loaded state totally outweighs the combined actual code size. i586-before: -rw-r--r-- 1 jengelh users 3821 Feb 10 01:01 xt_MARK.ko -rw-r--r-- 1 jengelh users 2592 Feb 10 00:04 xt_MARK.o -rw-r--r-- 1 jengelh users 3274 Feb 10 01:01 xt_mark.ko -rw-r--r-- 1 jengelh users 2108 Feb 10 00:05 xt_mark.o text data bss dec hex filename 354 264 0 618 26a xt_MARK.o 223 176 0 399 18f xt_mark.o And the runtime size is like 14 KB. i586-after: -rw-r--r-- 1 jengelh users 3264 Feb 18 17:28 xt_mark.o Signed-off-by: Jan Engelhardt --- include/linux/netfilter/xt_MARK.h | 6 +---- include/linux/netfilter/xt_mark.h | 4 +++ net/netfilter/Kconfig | 46 ++++++++++++++++++++------------ net/netfilter/Makefile | 5 ++-- net/netfilter/xt_MARK.c | 56 --------------------------------------- net/netfilter/xt_mark.c | 35 ++++++++++++++++++++++-- 6 files changed, 70 insertions(+), 82 deletions(-) delete mode 100644 net/netfilter/xt_MARK.c (limited to 'include/linux') diff --git a/include/linux/netfilter/xt_MARK.h b/include/linux/netfilter/xt_MARK.h index bc9561bdef7..41c456deba2 100644 --- a/include/linux/netfilter/xt_MARK.h +++ b/include/linux/netfilter/xt_MARK.h @@ -1,10 +1,6 @@ #ifndef _XT_MARK_H_target #define _XT_MARK_H_target -#include - -struct xt_mark_tginfo2 { - __u32 mark, mask; -}; +#include #endif /*_XT_MARK_H_target */ diff --git a/include/linux/netfilter/xt_mark.h b/include/linux/netfilter/xt_mark.h index 6607c8f38ea..ecadc40d5cd 100644 --- a/include/linux/netfilter/xt_mark.h +++ b/include/linux/netfilter/xt_mark.h @@ -3,6 +3,10 @@ #include +struct xt_mark_tginfo2 { + __u32 mark, mask; +}; + struct xt_mark_mtinfo1 { __u32 mark, mask; __u8 invert; diff --git a/net/netfilter/Kconfig b/net/netfilter/Kconfig index abf4ce6d183..236aa20ce5c 100644 --- a/net/netfilter/Kconfig +++ b/net/netfilter/Kconfig @@ -314,6 +314,23 @@ config NETFILTER_XTABLES if NETFILTER_XTABLES +comment "Xtables combined modules" + +config NETFILTER_XT_MARK + tristate 'nfmark target and match support' + default m if NETFILTER_ADVANCED=n + ---help--- + This option adds the "MARK" target and "mark" match. + + Netfilter mark matching allows you to match packets based on the + "nfmark" value in the packet. + The target allows you to create rules in the "mangle" table which alter + the netfilter mark (nfmark) field associated with the packet. + + Prior to routing, the nfmark can influence the routing method (see + "Use netfilter MARK value as routing key") and can also be used by + other subsystems to change their behavior. + # alphabetically ordered list of targets comment "Xtables targets" @@ -425,16 +442,12 @@ config NETFILTER_XT_TARGET_LED config NETFILTER_XT_TARGET_MARK tristate '"MARK" target support' - default m if NETFILTER_ADVANCED=n - help - This option adds a `MARK' target, which allows you to create rules - in the `mangle' table which alter the netfilter mark (nfmark) field - associated with the packet prior to routing. This can change - the routing method (see `Use netfilter MARK value as routing - key') and can also be used by other subsystems to change their - behavior. - - To compile it as a module, choose M here. If unsure, say N. + depends on NETFILTER_ADVANCED + select NETFILTER_XT_MARK + ---help--- + This is a backwards-compat option for the user's convenience + (e.g. when running oldconfig). It selects + CONFIG_NETFILTER_XT_MARK (combined mark/MARK module). config NETFILTER_XT_TARGET_NFLOG tristate '"NFLOG" target support' @@ -739,13 +752,12 @@ config NETFILTER_XT_MATCH_MAC config NETFILTER_XT_MATCH_MARK tristate '"mark" match support' - default m if NETFILTER_ADVANCED=n - help - Netfilter mark matching allows you to match packets based on the - `nfmark' value in the packet. This can be set by the MARK target - (see below). - - To compile it as a module, choose M here. If unsure, say N. + depends on NETFILTER_ADVANCED + select NETFILTER_XT_MARK + ---help--- + This is a backwards-compat option for the user's convenience + (e.g. when running oldconfig). It selects + CONFIG_NETFILTER_XT_MARK (combined mark/MARK module). config NETFILTER_XT_MATCH_MULTIPORT tristate '"multiport" Multiple port match support' diff --git a/net/netfilter/Makefile b/net/netfilter/Makefile index f873644f02f..19775cc30fe 100644 --- a/net/netfilter/Makefile +++ b/net/netfilter/Makefile @@ -40,6 +40,9 @@ obj-$(CONFIG_NETFILTER_TPROXY) += nf_tproxy_core.o # generic X tables obj-$(CONFIG_NETFILTER_XTABLES) += x_tables.o xt_tcpudp.o +# combos +obj-$(CONFIG_NETFILTER_XT_MARK) += xt_mark.o + # targets obj-$(CONFIG_NETFILTER_XT_TARGET_CLASSIFY) += xt_CLASSIFY.o obj-$(CONFIG_NETFILTER_XT_TARGET_CONNMARK) += xt_CONNMARK.o @@ -48,7 +51,6 @@ obj-$(CONFIG_NETFILTER_XT_TARGET_CT) += xt_CT.o obj-$(CONFIG_NETFILTER_XT_TARGET_DSCP) += xt_DSCP.o obj-$(CONFIG_NETFILTER_XT_TARGET_HL) += xt_HL.o obj-$(CONFIG_NETFILTER_XT_TARGET_LED) += xt_LED.o -obj-$(CONFIG_NETFILTER_XT_TARGET_MARK) += xt_MARK.o obj-$(CONFIG_NETFILTER_XT_TARGET_NFLOG) += xt_NFLOG.o obj-$(CONFIG_NETFILTER_XT_TARGET_NFQUEUE) += xt_NFQUEUE.o obj-$(CONFIG_NETFILTER_XT_TARGET_NOTRACK) += xt_NOTRACK.o @@ -76,7 +78,6 @@ obj-$(CONFIG_NETFILTER_XT_MATCH_IPRANGE) += xt_iprange.o obj-$(CONFIG_NETFILTER_XT_MATCH_LENGTH) += xt_length.o obj-$(CONFIG_NETFILTER_XT_MATCH_LIMIT) += xt_limit.o obj-$(CONFIG_NETFILTER_XT_MATCH_MAC) += xt_mac.o -obj-$(CONFIG_NETFILTER_XT_MATCH_MARK) += xt_mark.o obj-$(CONFIG_NETFILTER_XT_MATCH_MULTIPORT) += xt_multiport.o obj-$(CONFIG_NETFILTER_XT_MATCH_OSF) += xt_osf.o obj-$(CONFIG_NETFILTER_XT_MATCH_OWNER) += xt_owner.o diff --git a/net/netfilter/xt_MARK.c b/net/netfilter/xt_MARK.c deleted file mode 100644 index 225f8d11e17..00000000000 --- a/net/netfilter/xt_MARK.c +++ /dev/null @@ -1,56 +0,0 @@ -/* - * xt_MARK - Netfilter module to modify the NFMARK field of an skb - * - * (C) 1999-2001 Marc Boucher - * Copyright © CC Computer Consultants GmbH, 2007 - 2008 - * Jan Engelhardt - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ - -#include -#include -#include -#include - -#include -#include - -MODULE_LICENSE("GPL"); -MODULE_AUTHOR("Marc Boucher "); -MODULE_DESCRIPTION("Xtables: packet mark modification"); -MODULE_ALIAS("ipt_MARK"); -MODULE_ALIAS("ip6t_MARK"); - -static unsigned int -mark_tg(struct sk_buff *skb, const struct xt_target_param *par) -{ - const struct xt_mark_tginfo2 *info = par->targinfo; - - skb->mark = (skb->mark & ~info->mask) ^ info->mark; - return XT_CONTINUE; -} - -static struct xt_target mark_tg_reg __read_mostly = { - .name = "MARK", - .revision = 2, - .family = NFPROTO_UNSPEC, - .target = mark_tg, - .targetsize = sizeof(struct xt_mark_tginfo2), - .me = THIS_MODULE, -}; - -static int __init mark_tg_init(void) -{ - return xt_register_target(&mark_tg_reg); -} - -static void __exit mark_tg_exit(void) -{ - xt_unregister_target(&mark_tg_reg); -} - -module_init(mark_tg_init); -module_exit(mark_tg_exit); diff --git a/net/netfilter/xt_mark.c b/net/netfilter/xt_mark.c index 1db07d8125f..035c468a004 100644 --- a/net/netfilter/xt_mark.c +++ b/net/netfilter/xt_mark.c @@ -18,9 +18,20 @@ MODULE_LICENSE("GPL"); MODULE_AUTHOR("Marc Boucher "); -MODULE_DESCRIPTION("Xtables: packet mark match"); +MODULE_DESCRIPTION("Xtables: packet mark operations"); MODULE_ALIAS("ipt_mark"); MODULE_ALIAS("ip6t_mark"); +MODULE_ALIAS("ipt_MARK"); +MODULE_ALIAS("ip6t_MARK"); + +static unsigned int +mark_tg(struct sk_buff *skb, const struct xt_target_param *par) +{ + const struct xt_mark_tginfo2 *info = par->targinfo; + + skb->mark = (skb->mark & ~info->mask) ^ info->mark; + return XT_CONTINUE; +} static bool mark_mt(const struct sk_buff *skb, const struct xt_match_param *par) @@ -30,6 +41,15 @@ mark_mt(const struct sk_buff *skb, const struct xt_match_param *par) return ((skb->mark & info->mask) == info->mark) ^ info->invert; } +static struct xt_target mark_tg_reg __read_mostly = { + .name = "MARK", + .revision = 2, + .family = NFPROTO_UNSPEC, + .target = mark_tg, + .targetsize = sizeof(struct xt_mark_tginfo2), + .me = THIS_MODULE, +}; + static struct xt_match mark_mt_reg __read_mostly = { .name = "mark", .revision = 1, @@ -41,12 +61,23 @@ static struct xt_match mark_mt_reg __read_mostly = { static int __init mark_mt_init(void) { - return xt_register_match(&mark_mt_reg); + int ret; + + ret = xt_register_target(&mark_tg_reg); + if (ret < 0) + return ret; + ret = xt_register_match(&mark_mt_reg); + if (ret < 0) { + xt_unregister_target(&mark_tg_reg); + return ret; + } + return 0; } static void __exit mark_mt_exit(void) { xt_unregister_match(&mark_mt_reg); + xt_unregister_target(&mark_tg_reg); } module_init(mark_mt_init); -- cgit v1.2.3 From b8f00ba27e4acc4a2224360ce3aa8a22354434c5 Mon Sep 17 00:00:00 2001 From: Jan Engelhardt Date: Fri, 26 Feb 2010 14:20:32 +0100 Subject: netfilter: xtables: merge xt_CONNMARK into xt_connmark Signed-off-by: Jan Engelhardt --- include/linux/netfilter/xt_CONNMARK.h | 22 +------ include/linux/netfilter/xt_connmark.h | 11 ++++ net/netfilter/Kconfig | 39 +++++++----- net/netfilter/Makefile | 3 +- net/netfilter/xt_CONNMARK.c | 113 ---------------------------------- net/netfilter/xt_connmark.c | 84 ++++++++++++++++++++++++- 6 files changed, 116 insertions(+), 156 deletions(-) delete mode 100644 net/netfilter/xt_CONNMARK.c (limited to 'include/linux') diff --git a/include/linux/netfilter/xt_CONNMARK.h b/include/linux/netfilter/xt_CONNMARK.h index 0a854586675..2f2e48ec802 100644 --- a/include/linux/netfilter/xt_CONNMARK.h +++ b/include/linux/netfilter/xt_CONNMARK.h @@ -1,26 +1,6 @@ #ifndef _XT_CONNMARK_H_target #define _XT_CONNMARK_H_target -#include - -/* Copyright (C) 2002,2004 MARA Systems AB - * by Henrik Nordstrom - * - * 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. - */ - -enum { - XT_CONNMARK_SET = 0, - XT_CONNMARK_SAVE, - XT_CONNMARK_RESTORE -}; - -struct xt_connmark_tginfo1 { - __u32 ctmark, ctmask, nfmask; - __u8 mode; -}; +#include #endif /*_XT_CONNMARK_H_target*/ diff --git a/include/linux/netfilter/xt_connmark.h b/include/linux/netfilter/xt_connmark.h index 619e47cde01..efc17a8305f 100644 --- a/include/linux/netfilter/xt_connmark.h +++ b/include/linux/netfilter/xt_connmark.h @@ -12,6 +12,17 @@ * (at your option) any later version. */ +enum { + XT_CONNMARK_SET = 0, + XT_CONNMARK_SAVE, + XT_CONNMARK_RESTORE +}; + +struct xt_connmark_tginfo1 { + __u32 ctmark, ctmask, nfmask; + __u8 mode; +}; + struct xt_connmark_mtinfo1 { __u32 mark, mask; __u8 invert; diff --git a/net/netfilter/Kconfig b/net/netfilter/Kconfig index 236aa20ce5c..8550dfde780 100644 --- a/net/netfilter/Kconfig +++ b/net/netfilter/Kconfig @@ -331,6 +331,18 @@ config NETFILTER_XT_MARK "Use netfilter MARK value as routing key") and can also be used by other subsystems to change their behavior. +config NETFILTER_XT_CONNMARK + tristate 'ctmark target and match support' + depends on NF_CONNTRACK + depends on NETFILTER_ADVANCED + select NF_CONNTRACK_MARK + ---help--- + This option adds the "CONNMARK" target and "connmark" match. + + Netfilter allows you to store a mark value per connection (a.k.a. + ctmark), similarly to the packet mark (nfmark). Using this + target and match, you can set and match on this mark. + # alphabetically ordered list of targets comment "Xtables targets" @@ -351,15 +363,11 @@ config NETFILTER_XT_TARGET_CONNMARK tristate '"CONNMARK" target support' depends on NF_CONNTRACK depends on NETFILTER_ADVANCED - select NF_CONNTRACK_MARK - help - This option adds a `CONNMARK' target, which allows one to manipulate - the connection mark value. Similar to the MARK target, but - affects the connection mark value rather than the packet mark value. - - If you want to compile it as a module, say M here and read - . The module will be called - ipt_CONNMARK. If unsure, say `N'. + select NETFILTER_XT_CONNMARK + ---help--- + This is a backwards-compat option for the user's convenience + (e.g. when running oldconfig). It selects + CONFIG_NETFILTER_XT_CONNMARK (combined connmark/CONNMARK module). config NETFILTER_XT_TARGET_CONNSECMARK tristate '"CONNSECMARK" target support' @@ -621,14 +629,11 @@ config NETFILTER_XT_MATCH_CONNMARK tristate '"connmark" connection mark match support' depends on NF_CONNTRACK depends on NETFILTER_ADVANCED - select NF_CONNTRACK_MARK - help - This option adds a `connmark' match, which allows you to match the - connection mark value previously set for the session by `CONNMARK'. - - If you want to compile it as a module, say M here and read - . The module will be called - ipt_connmark. If unsure, say `N'. + select NETFILTER_XT_CONNMARK + ---help--- + This is a backwards-compat option for the user's convenience + (e.g. when running oldconfig). It selects + CONFIG_NETFILTER_XT_CONNMARK (combined connmark/CONNMARK module). config NETFILTER_XT_MATCH_CONNTRACK tristate '"conntrack" connection tracking match support' diff --git a/net/netfilter/Makefile b/net/netfilter/Makefile index 19775cc30fe..cd31afe0692 100644 --- a/net/netfilter/Makefile +++ b/net/netfilter/Makefile @@ -42,10 +42,10 @@ obj-$(CONFIG_NETFILTER_XTABLES) += x_tables.o xt_tcpudp.o # combos obj-$(CONFIG_NETFILTER_XT_MARK) += xt_mark.o +obj-$(CONFIG_NETFILTER_XT_CONNMARK) += xt_connmark.o # targets obj-$(CONFIG_NETFILTER_XT_TARGET_CLASSIFY) += xt_CLASSIFY.o -obj-$(CONFIG_NETFILTER_XT_TARGET_CONNMARK) += xt_CONNMARK.o obj-$(CONFIG_NETFILTER_XT_TARGET_CONNSECMARK) += xt_CONNSECMARK.o obj-$(CONFIG_NETFILTER_XT_TARGET_CT) += xt_CT.o obj-$(CONFIG_NETFILTER_XT_TARGET_DSCP) += xt_DSCP.o @@ -66,7 +66,6 @@ obj-$(CONFIG_NETFILTER_XT_MATCH_CLUSTER) += xt_cluster.o obj-$(CONFIG_NETFILTER_XT_MATCH_COMMENT) += xt_comment.o obj-$(CONFIG_NETFILTER_XT_MATCH_CONNBYTES) += xt_connbytes.o obj-$(CONFIG_NETFILTER_XT_MATCH_CONNLIMIT) += xt_connlimit.o -obj-$(CONFIG_NETFILTER_XT_MATCH_CONNMARK) += xt_connmark.o obj-$(CONFIG_NETFILTER_XT_MATCH_CONNTRACK) += xt_conntrack.o obj-$(CONFIG_NETFILTER_XT_MATCH_DCCP) += xt_dccp.o obj-$(CONFIG_NETFILTER_XT_MATCH_DSCP) += xt_dscp.o diff --git a/net/netfilter/xt_CONNMARK.c b/net/netfilter/xt_CONNMARK.c deleted file mode 100644 index 593457068ae..00000000000 --- a/net/netfilter/xt_CONNMARK.c +++ /dev/null @@ -1,113 +0,0 @@ -/* - * xt_CONNMARK - Netfilter module to modify the connection mark values - * - * Copyright (C) 2002,2004 MARA Systems AB - * by Henrik Nordstrom - * Copyright © CC Computer Consultants GmbH, 2007 - 2008 - * Jan Engelhardt - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ -#include -#include -#include -#include - -MODULE_AUTHOR("Henrik Nordstrom "); -MODULE_DESCRIPTION("Xtables: connection mark modification"); -MODULE_LICENSE("GPL"); -MODULE_ALIAS("ipt_CONNMARK"); -MODULE_ALIAS("ip6t_CONNMARK"); - -#include -#include -#include - -static unsigned int -connmark_tg(struct sk_buff *skb, const struct xt_target_param *par) -{ - const struct xt_connmark_tginfo1 *info = par->targinfo; - enum ip_conntrack_info ctinfo; - struct nf_conn *ct; - u_int32_t newmark; - - ct = nf_ct_get(skb, &ctinfo); - if (ct == NULL) - return XT_CONTINUE; - - switch (info->mode) { - case XT_CONNMARK_SET: - newmark = (ct->mark & ~info->ctmask) ^ info->ctmark; - if (ct->mark != newmark) { - ct->mark = newmark; - nf_conntrack_event_cache(IPCT_MARK, ct); - } - break; - case XT_CONNMARK_SAVE: - newmark = (ct->mark & ~info->ctmask) ^ - (skb->mark & info->nfmask); - if (ct->mark != newmark) { - ct->mark = newmark; - nf_conntrack_event_cache(IPCT_MARK, ct); - } - break; - case XT_CONNMARK_RESTORE: - newmark = (skb->mark & ~info->nfmask) ^ - (ct->mark & info->ctmask); - skb->mark = newmark; - break; - } - - return XT_CONTINUE; -} - -static bool connmark_tg_check(const struct xt_tgchk_param *par) -{ - if (nf_ct_l3proto_try_module_get(par->family) < 0) { - printk(KERN_WARNING "cannot load conntrack support for " - "proto=%u\n", par->family); - return false; - } - return true; -} - -static void connmark_tg_destroy(const struct xt_tgdtor_param *par) -{ - nf_ct_l3proto_module_put(par->family); -} - -static struct xt_target connmark_tg_reg __read_mostly = { - .name = "CONNMARK", - .revision = 1, - .family = NFPROTO_UNSPEC, - .checkentry = connmark_tg_check, - .target = connmark_tg, - .targetsize = sizeof(struct xt_connmark_tginfo1), - .destroy = connmark_tg_destroy, - .me = THIS_MODULE, -}; - -static int __init connmark_tg_init(void) -{ - return xt_register_target(&connmark_tg_reg); -} - -static void __exit connmark_tg_exit(void) -{ - xt_unregister_target(&connmark_tg_reg); -} - -module_init(connmark_tg_init); -module_exit(connmark_tg_exit); diff --git a/net/netfilter/xt_connmark.c b/net/netfilter/xt_connmark.c index 122aa8b0147..d184515604b 100644 --- a/net/netfilter/xt_connmark.c +++ b/net/netfilter/xt_connmark.c @@ -1,5 +1,5 @@ /* - * xt_connmark - Netfilter module to match connection mark values + * xt_connmark - Netfilter module to operate on connection marks * * Copyright (C) 2002,2004 MARA Systems AB * by Henrik Nordstrom @@ -24,15 +24,71 @@ #include #include #include +#include #include #include MODULE_AUTHOR("Henrik Nordstrom "); -MODULE_DESCRIPTION("Xtables: connection mark match"); +MODULE_DESCRIPTION("Xtables: connection mark operations"); MODULE_LICENSE("GPL"); +MODULE_ALIAS("ipt_CONNMARK"); +MODULE_ALIAS("ip6t_CONNMARK"); MODULE_ALIAS("ipt_connmark"); MODULE_ALIAS("ip6t_connmark"); +static unsigned int +connmark_tg(struct sk_buff *skb, const struct xt_target_param *par) +{ + const struct xt_connmark_tginfo1 *info = par->targinfo; + enum ip_conntrack_info ctinfo; + struct nf_conn *ct; + u_int32_t newmark; + + ct = nf_ct_get(skb, &ctinfo); + if (ct == NULL) + return XT_CONTINUE; + + switch (info->mode) { + case XT_CONNMARK_SET: + newmark = (ct->mark & ~info->ctmask) ^ info->ctmark; + if (ct->mark != newmark) { + ct->mark = newmark; + nf_conntrack_event_cache(IPCT_MARK, ct); + } + break; + case XT_CONNMARK_SAVE: + newmark = (ct->mark & ~info->ctmask) ^ + (skb->mark & info->nfmask); + if (ct->mark != newmark) { + ct->mark = newmark; + nf_conntrack_event_cache(IPCT_MARK, ct); + } + break; + case XT_CONNMARK_RESTORE: + newmark = (skb->mark & ~info->nfmask) ^ + (ct->mark & info->ctmask); + skb->mark = newmark; + break; + } + + return XT_CONTINUE; +} + +static bool connmark_tg_check(const struct xt_tgchk_param *par) +{ + if (nf_ct_l3proto_try_module_get(par->family) < 0) { + printk(KERN_WARNING "cannot load conntrack support for " + "proto=%u\n", par->family); + return false; + } + return true; +} + +static void connmark_tg_destroy(const struct xt_tgdtor_param *par) +{ + nf_ct_l3proto_module_put(par->family); +} + static bool connmark_mt(const struct sk_buff *skb, const struct xt_match_param *par) { @@ -62,6 +118,17 @@ static void connmark_mt_destroy(const struct xt_mtdtor_param *par) nf_ct_l3proto_module_put(par->family); } +static struct xt_target connmark_tg_reg __read_mostly = { + .name = "CONNMARK", + .revision = 1, + .family = NFPROTO_UNSPEC, + .checkentry = connmark_tg_check, + .target = connmark_tg, + .targetsize = sizeof(struct xt_connmark_tginfo1), + .destroy = connmark_tg_destroy, + .me = THIS_MODULE, +}; + static struct xt_match connmark_mt_reg __read_mostly = { .name = "connmark", .revision = 1, @@ -75,12 +142,23 @@ static struct xt_match connmark_mt_reg __read_mostly = { static int __init connmark_mt_init(void) { - return xt_register_match(&connmark_mt_reg); + int ret; + + ret = xt_register_target(&connmark_tg_reg); + if (ret < 0) + return ret; + ret = xt_register_match(&connmark_mt_reg); + if (ret < 0) { + xt_unregister_target(&connmark_tg_reg); + return ret; + } + return 0; } static void __exit connmark_mt_exit(void) { xt_unregister_match(&connmark_mt_reg); + xt_unregister_target(&connmark_tg_reg); } module_init(connmark_mt_init); -- cgit v1.2.3 From 0079c5aee34880bcee7feee9960f0502c73dc5fa Mon Sep 17 00:00:00 2001 From: Tim Gardner Date: Tue, 16 Mar 2010 19:53:13 +0100 Subject: netfilter: xt_recent: add an entry reaper One of the problems with the way xt_recent is implemented is that there is no efficient way to remove expired entries. Of course, one can write a rule '-m recent --remove', but you have to know beforehand which entry to delete. This commit adds reaper logic which checks the head of the LRU list when a rule is invoked that has a '--seconds' value and XT_RECENT_REAP set. If an entry ceases to accumulate time stamps, then it will eventually bubble to the top of the LRU list where it is then reaped. Signed-off-by: Tim Gardner Signed-off-by: Eric Dumazet Signed-off-by: Patrick McHardy --- include/linux/netfilter/xt_recent.h | 4 ++++ net/netfilter/xt_recent.c | 28 +++++++++++++++++++++++++++- 2 files changed, 31 insertions(+), 1 deletion(-) (limited to 'include/linux') diff --git a/include/linux/netfilter/xt_recent.h b/include/linux/netfilter/xt_recent.h index d2c27660992..bba990ecb01 100644 --- a/include/linux/netfilter/xt_recent.h +++ b/include/linux/netfilter/xt_recent.h @@ -9,6 +9,7 @@ enum { XT_RECENT_UPDATE = 1 << 2, XT_RECENT_REMOVE = 1 << 3, XT_RECENT_TTL = 1 << 4, + XT_RECENT_REAP = 1 << 5, XT_RECENT_SOURCE = 0, XT_RECENT_DEST = 1, @@ -16,6 +17,9 @@ enum { XT_RECENT_NAME_LEN = 200, }; +/* Only allowed with --rcheck and --update */ +#define XT_RECENT_MODIFIERS (XT_RECENT_TTL|XT_RECENT_REAP) + struct xt_recent_mtinfo { __u32 seconds; __u32 hit_count; diff --git a/net/netfilter/xt_recent.c b/net/netfilter/xt_recent.c index 2ff8aae84a3..b65eca9e13a 100644 --- a/net/netfilter/xt_recent.c +++ b/net/netfilter/xt_recent.c @@ -143,6 +143,25 @@ static void recent_entry_remove(struct recent_table *t, struct recent_entry *e) t->entries--; } +/* + * Drop entries with timestamps older then 'time'. + */ +static void recent_entry_reap(struct recent_table *t, unsigned long time) +{ + struct recent_entry *e; + + /* + * The head of the LRU list is always the oldest entry. + */ + e = list_entry(t->lru_list.next, struct recent_entry, lru_list); + + /* + * The last time stamp is the most recent. + */ + if (time_after(time, e->stamps[e->index-1])) + recent_entry_remove(t, e); +} + static struct recent_entry * recent_entry_init(struct recent_table *t, const union nf_inet_addr *addr, u_int16_t family, u_int8_t ttl) @@ -269,6 +288,10 @@ recent_mt(const struct sk_buff *skb, const struct xt_match_param *par) break; } } + + /* info->seconds must be non-zero */ + if (info->check_set & XT_RECENT_REAP) + recent_entry_reap(t, time); } if (info->check_set & XT_RECENT_SET || @@ -301,7 +324,10 @@ static bool recent_mt_check(const struct xt_mtchk_param *par) XT_RECENT_CHECK | XT_RECENT_UPDATE)) != 1) return false; if ((info->check_set & (XT_RECENT_SET | XT_RECENT_REMOVE)) && - (info->seconds || info->hit_count)) + (info->seconds || info->hit_count || + (info->check_set & XT_RECENT_MODIFIERS))) + return false; + if ((info->check_set & XT_RECENT_REAP) && !info->seconds) return false; if (info->hit_count > ip_pkt_list_tot) { pr_info(KBUILD_MODNAME ": hitcount (%u) is larger than " -- cgit v1.2.3 From 606a9a02633c02d0e09fc96706f041053dbc57ee Mon Sep 17 00:00:00 2001 From: Tim Gardner Date: Wed, 17 Mar 2010 16:18:56 +0100 Subject: netfilter: xt_recent: check for unsupported user space flags Signed-off-by: Tim Gardner Signed-off-by: Patrick McHardy --- include/linux/netfilter/xt_recent.h | 3 +++ net/netfilter/xt_recent.c | 5 +++++ 2 files changed, 8 insertions(+) (limited to 'include/linux') diff --git a/include/linux/netfilter/xt_recent.h b/include/linux/netfilter/xt_recent.h index bba990ecb01..83318e01425 100644 --- a/include/linux/netfilter/xt_recent.h +++ b/include/linux/netfilter/xt_recent.h @@ -20,6 +20,9 @@ enum { /* Only allowed with --rcheck and --update */ #define XT_RECENT_MODIFIERS (XT_RECENT_TTL|XT_RECENT_REAP) +#define XT_RECENT_VALID_FLAGS (XT_RECENT_CHECK|XT_RECENT_SET|XT_RECENT_UPDATE|\ + XT_RECENT_REMOVE|XT_RECENT_TTL|XT_RECENT_REAP) + struct xt_recent_mtinfo { __u32 seconds; __u32 hit_count; diff --git a/net/netfilter/xt_recent.c b/net/netfilter/xt_recent.c index b65eca9e13a..d2e7c80cd3c 100644 --- a/net/netfilter/xt_recent.c +++ b/net/netfilter/xt_recent.c @@ -319,6 +319,11 @@ static bool recent_mt_check(const struct xt_mtchk_param *par) get_random_bytes(&hash_rnd, sizeof(hash_rnd)); hash_rnd_inited = true; } + if (info->check_set & ~XT_RECENT_VALID_FLAGS) { + pr_info(KBUILD_MODNAME ": Unsupported user space flags " + "(%08x)\n", info->check_set); + return false; + } if (hweight8(info->check_set & (XT_RECENT_SET | XT_RECENT_REMOVE | XT_RECENT_CHECK | XT_RECENT_UPDATE)) != 1) -- cgit v1.2.3 From 16599786ae5e9d5f936706d2202d8c7224cd51ed Mon Sep 17 00:00:00 2001 From: Jan Engelhardt Date: Thu, 18 Mar 2010 10:30:44 +0100 Subject: netfilter: update documentation fields of x_tables.h Signed-off-by: Jan Engelhardt --- include/linux/netfilter/x_tables.h | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) (limited to 'include/linux') diff --git a/include/linux/netfilter/x_tables.h b/include/linux/netfilter/x_tables.h index 84c7c928e9e..c68ff82366b 100644 --- a/include/linux/netfilter/x_tables.h +++ b/include/linux/netfilter/x_tables.h @@ -197,6 +197,7 @@ struct xt_counters_info { * @family: Actual NFPROTO_* through which the function is invoked * (helpful when match->family == NFPROTO_UNSPEC) * @hotdrop: drop packet if we had inspection problems + * Network namespace obtainable using dev_net(in/out) */ struct xt_match_param { const struct net_device *in, *out; @@ -213,12 +214,14 @@ struct xt_match_param { * struct xt_mtchk_param - parameters for match extensions' * checkentry functions * + * @net: network namespace through which the check was invoked * @table: table the rule is tried to be inserted into * @entryinfo: the family-specific rule data - * (struct ipt_ip, ip6t_ip, ebt_entry) + * (struct ipt_ip, ip6t_ip, arpt_arp or (note) ebt_entry) * @match: struct xt_match through which this function was invoked * @matchinfo: per-match data * @hook_mask: via which hooks the new rule is reachable + * Other fields as above. */ struct xt_mtchk_param { struct net *net; @@ -230,7 +233,10 @@ struct xt_mtchk_param { u_int8_t family; }; -/* Match destructor parameters */ +/** + * struct xt_mdtor_param - match destructor parameters + * Fields as above. + */ struct xt_mtdtor_param { struct net *net; const struct xt_match *match; -- cgit v1.2.3 From 4f948db1915ff05e4ce0fd98e6323db6a3ec0fc0 Mon Sep 17 00:00:00 2001 From: Jan Engelhardt Date: Thu, 18 Mar 2010 11:03:51 +0100 Subject: netfilter: xtables: remove almost-unused xt_match_param.data member This member is taking up a "long" per match, yet is only used by one module out of the roughly 90 modules, ip6t_hbh. ip6t_hbh can be restructured a little to accomodate for the lack of the .data member. This variant uses checking the par->match address, which should avoid having to add two extra functions, including calls, i.e. (hbh_mt6: call hbhdst_mt6(skb, par, NEXTHDR_OPT), dst_mt6: call hbhdst_mt6(skb, par, NEXTHDR_DEST)) Signed-off-by: Jan Engelhardt --- include/linux/netfilter/x_tables.h | 3 --- net/ipv6/netfilter/ip6t_hbh.c | 9 ++++++--- 2 files changed, 6 insertions(+), 6 deletions(-) (limited to 'include/linux') diff --git a/include/linux/netfilter/x_tables.h b/include/linux/netfilter/x_tables.h index c68ff82366b..cf91473624e 100644 --- a/include/linux/netfilter/x_tables.h +++ b/include/linux/netfilter/x_tables.h @@ -315,9 +315,6 @@ struct xt_match { /* Set this to THIS_MODULE if you are a module, otherwise NULL */ struct module *me; - /* Free to use by each match */ - unsigned long data; - const char *table; unsigned int matchsize; #ifdef CONFIG_COMPAT diff --git a/net/ipv6/netfilter/ip6t_hbh.c b/net/ipv6/netfilter/ip6t_hbh.c index cbe8dec9744..82593c8bdc3 100644 --- a/net/ipv6/netfilter/ip6t_hbh.c +++ b/net/ipv6/netfilter/ip6t_hbh.c @@ -41,6 +41,8 @@ MODULE_ALIAS("ip6t_dst"); * 5 -> RTALERT 2 x x */ +static struct xt_match hbh_mt6_reg[] __read_mostly; + static bool hbh_mt6(const struct sk_buff *skb, const struct xt_match_param *par) { @@ -58,7 +60,9 @@ hbh_mt6(const struct sk_buff *skb, const struct xt_match_param *par) unsigned int optlen; int err; - err = ipv6_find_hdr(skb, &ptr, par->match->data, NULL); + err = ipv6_find_hdr(skb, &ptr, + (par->match == &hbh_mt6_reg[0]) ? + NEXTHDR_HOP : NEXTHDR_DEST, NULL); if (err < 0) { if (err != -ENOENT) *par->hotdrop = true; @@ -179,13 +183,13 @@ static bool hbh_mt6_check(const struct xt_mtchk_param *par) static struct xt_match hbh_mt6_reg[] __read_mostly = { { + /* Note, hbh_mt6 relies on the order of hbh_mt6_reg */ .name = "hbh", .family = NFPROTO_IPV6, .match = hbh_mt6, .matchsize = sizeof(struct ip6t_opts), .checkentry = hbh_mt6_check, .me = THIS_MODULE, - .data = NEXTHDR_HOP, }, { .name = "dst", @@ -194,7 +198,6 @@ static struct xt_match hbh_mt6_reg[] __read_mostly = { .matchsize = sizeof(struct ip6t_opts), .checkentry = hbh_mt6_check, .me = THIS_MODULE, - .data = NEXTHDR_DEST, }, }; -- cgit v1.2.3 From f5c511c67aaec323c186543856cfddab31bed1d1 Mon Sep 17 00:00:00 2001 From: Jan Engelhardt Date: Thu, 18 Mar 2010 14:02:10 +0100 Subject: netfilter: xtables: reduce holes in struct xt_target This will save one full padding chunk (8 bytes on x86_64) per target. Signed-off-by: Jan Engelhardt --- include/linux/netfilter/x_tables.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include/linux') diff --git a/include/linux/netfilter/x_tables.h b/include/linux/netfilter/x_tables.h index cf91473624e..f8f55515337 100644 --- a/include/linux/netfilter/x_tables.h +++ b/include/linux/netfilter/x_tables.h @@ -331,6 +331,7 @@ struct xt_target { struct list_head list; const char name[XT_FUNCTION_MAXNAMELEN-1]; + u_int8_t revision; /* Returns verdict. Argument order changed since 2.6.9, as this must now handle non-linear skbs, using skb_copy_bits and @@ -363,7 +364,6 @@ struct xt_target { unsigned short proto; unsigned short family; - u_int8_t revision; }; /* Furniture shopping... */ -- cgit v1.2.3 From 1e94d72feab025b8f7c55d07020602f82f3a97dd Mon Sep 17 00:00:00 2001 From: Tom Herbert Date: Thu, 18 Mar 2010 17:45:44 -0700 Subject: rps: Fixed build with CONFIG_SMP not enabled. Signed-off-by: Tom Herbert Signed-off-by: David S. Miller --- include/linux/netdevice.h | 2 ++ net/core/dev.c | 24 ++++++++++++++++++++++++ 2 files changed, 26 insertions(+) (limited to 'include/linux') diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index de1a52bcb9e..726ecd1af53 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -1341,7 +1341,9 @@ struct softnet_data { struct sk_buff *completion_queue; /* Elements below can be accessed between CPUs for RPS */ +#ifdef CONFIG_SMP struct call_single_data csd ____cacheline_aligned_in_smp; +#endif struct sk_buff_head input_pkt_queue; struct napi_struct backlog; }; diff --git a/net/core/dev.c b/net/core/dev.c index 17b16867150..1a7e1d1d5ad 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -2174,6 +2174,7 @@ int weight_p __read_mostly = 64; /* old backlog weight */ DEFINE_PER_CPU(struct netif_rx_stats, netdev_rx_stat) = { 0, }; +#ifdef CONFIG_SMP /* * get_rps_cpu is called from netif_receive_skb and returns the target * CPU from the RPS map of the receiving queue for a given skb. @@ -2293,6 +2294,7 @@ static void trigger_softirq(void *data) __napi_schedule(&queue->backlog); __get_cpu_var(netdev_rx_stat).received_rps++; } +#endif /* CONFIG_SMP */ /* * enqueue_to_backlog is called to queue an skb to a per CPU backlog @@ -2320,6 +2322,7 @@ enqueue: /* Schedule NAPI for backlog device */ if (napi_schedule_prep(&queue->backlog)) { +#ifdef CONFIG_SMP if (cpu != smp_processor_id()) { struct rps_remote_softirq_cpus *rcpus = &__get_cpu_var(rps_remote_softirq_cpus); @@ -2328,6 +2331,9 @@ enqueue: __raise_softirq_irqoff(NET_RX_SOFTIRQ); } else __napi_schedule(&queue->backlog); +#else + __napi_schedule(&queue->backlog); +#endif } goto enqueue; } @@ -2367,9 +2373,13 @@ int netif_rx(struct sk_buff *skb) if (!skb->tstamp.tv64) net_timestamp(skb); +#ifdef CONFIG_SMP cpu = get_rps_cpu(skb->dev, skb); if (cpu < 0) cpu = smp_processor_id(); +#else + cpu = smp_processor_id(); +#endif return enqueue_to_backlog(skb, cpu); } @@ -2735,6 +2745,7 @@ out: */ int netif_receive_skb(struct sk_buff *skb) { +#ifdef CONFIG_SMP int cpu; cpu = get_rps_cpu(skb->dev, skb); @@ -2743,6 +2754,9 @@ int netif_receive_skb(struct sk_buff *skb) return __netif_receive_skb(skb); else return enqueue_to_backlog(skb, cpu); +#else + return __netif_receive_skb(skb); +#endif } EXPORT_SYMBOL(netif_receive_skb); @@ -3168,6 +3182,7 @@ void netif_napi_del(struct napi_struct *napi) } EXPORT_SYMBOL(netif_napi_del); +#ifdef CONFIG_SMP /* * net_rps_action sends any pending IPI's for rps. This is only called from * softirq and interrupts must be enabled. @@ -3184,6 +3199,7 @@ static void net_rps_action(cpumask_t *mask) } cpus_clear(*mask); } +#endif static void net_rx_action(struct softirq_action *h) { @@ -3191,8 +3207,10 @@ static void net_rx_action(struct softirq_action *h) unsigned long time_limit = jiffies + 2; int budget = netdev_budget; void *have; +#ifdef CONFIG_SMP int select; struct rps_remote_softirq_cpus *rcpus; +#endif local_irq_disable(); @@ -3255,6 +3273,7 @@ static void net_rx_action(struct softirq_action *h) netpoll_poll_unlock(have); } out: +#ifdef CONFIG_SMP rcpus = &__get_cpu_var(rps_remote_softirq_cpus); select = rcpus->select; rcpus->select ^= 1; @@ -3262,6 +3281,9 @@ out: local_irq_enable(); net_rps_action(&rcpus->mask[select]); +#else + local_irq_enable(); +#endif #ifdef CONFIG_NET_DMA /* @@ -6204,9 +6226,11 @@ static int __init net_dev_init(void) queue->completion_queue = NULL; INIT_LIST_HEAD(&queue->poll_list); +#ifdef CONFIG_SMP queue->csd.func = trigger_softirq; queue->csd.info = queue; queue->csd.flags = 0; +#endif queue->backlog.poll = process_backlog; queue->backlog.weight = weight_p; -- cgit v1.2.3 From 93d9b7d7a85cfb4e1711d5226eba73586dd4919f Mon Sep 17 00:00:00 2001 From: Jiri Pirko Date: Wed, 10 Mar 2010 10:28:56 +0000 Subject: net: rename notifier defines for netdev type change Since generally there could be more netdevices changing type other than bonding, making this event type name "bonding-unrelated" Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- drivers/net/bonding/bond_main.c | 6 ++++-- include/linux/notifier.h | 4 ++-- net/ipv4/devinet.c | 4 ++-- net/ipv6/addrconf.c | 16 ++++++++-------- 4 files changed, 16 insertions(+), 14 deletions(-) (limited to 'include/linux') diff --git a/drivers/net/bonding/bond_main.c b/drivers/net/bonding/bond_main.c index 430c02267d7..7eeb18751d6 100644 --- a/drivers/net/bonding/bond_main.c +++ b/drivers/net/bonding/bond_main.c @@ -1480,14 +1480,16 @@ int bond_enslave(struct net_device *bond_dev, struct net_device *slave_dev) bond_dev->name, bond_dev->type, slave_dev->type); - netdev_bonding_change(bond_dev, NETDEV_BONDING_OLDTYPE); + netdev_bonding_change(bond_dev, + NETDEV_PRE_TYPE_CHANGE); if (slave_dev->type != ARPHRD_ETHER) bond_setup_by_slave(bond_dev, slave_dev); else ether_setup(bond_dev); - netdev_bonding_change(bond_dev, NETDEV_BONDING_NEWTYPE); + netdev_bonding_change(bond_dev, + NETDEV_POST_TYPE_CHANGE); } } else if (bond_dev->type != slave_dev->type) { pr_err("%s ether type (%d) is different from other slaves (%d), can not enslave it.\n", diff --git a/include/linux/notifier.h b/include/linux/notifier.h index fee6c2f6807..f3635fc6e94 100644 --- a/include/linux/notifier.h +++ b/include/linux/notifier.h @@ -199,8 +199,8 @@ static inline int notifier_to_errno(int ret) #define NETDEV_FEAT_CHANGE 0x000B #define NETDEV_BONDING_FAILOVER 0x000C #define NETDEV_PRE_UP 0x000D -#define NETDEV_BONDING_OLDTYPE 0x000E -#define NETDEV_BONDING_NEWTYPE 0x000F +#define NETDEV_PRE_TYPE_CHANGE 0x000E +#define NETDEV_POST_TYPE_CHANGE 0x000F #define NETDEV_POST_INIT 0x0010 #define NETDEV_UNREGISTER_BATCH 0x0011 diff --git a/net/ipv4/devinet.c b/net/ipv4/devinet.c index 51ca946e339..c75320ef95c 100644 --- a/net/ipv4/devinet.c +++ b/net/ipv4/devinet.c @@ -1095,10 +1095,10 @@ static int inetdev_event(struct notifier_block *this, unsigned long event, case NETDEV_DOWN: ip_mc_down(in_dev); break; - case NETDEV_BONDING_OLDTYPE: + case NETDEV_PRE_TYPE_CHANGE: ip_mc_unmap(in_dev); break; - case NETDEV_BONDING_NEWTYPE: + case NETDEV_POST_TYPE_CHANGE: ip_mc_remap(in_dev); break; case NETDEV_CHANGEMTU: diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c index 3381b4317c2..8d41abc40db 100644 --- a/net/ipv6/addrconf.c +++ b/net/ipv6/addrconf.c @@ -137,8 +137,8 @@ static DEFINE_SPINLOCK(addrconf_verify_lock); static void addrconf_join_anycast(struct inet6_ifaddr *ifp); static void addrconf_leave_anycast(struct inet6_ifaddr *ifp); -static void addrconf_bonding_change(struct net_device *dev, - unsigned long event); +static void addrconf_type_change(struct net_device *dev, + unsigned long event); static int addrconf_ifdown(struct net_device *dev, int how); static void addrconf_dad_start(struct inet6_ifaddr *ifp, u32 flags); @@ -2584,9 +2584,9 @@ static int addrconf_notify(struct notifier_block *this, unsigned long event, return notifier_from_errno(err); } break; - case NETDEV_BONDING_OLDTYPE: - case NETDEV_BONDING_NEWTYPE: - addrconf_bonding_change(dev, event); + case NETDEV_PRE_TYPE_CHANGE: + case NETDEV_POST_TYPE_CHANGE: + addrconf_type_change(dev, event); break; } @@ -2601,16 +2601,16 @@ static struct notifier_block ipv6_dev_notf = { .priority = 0 }; -static void addrconf_bonding_change(struct net_device *dev, unsigned long event) +static void addrconf_type_change(struct net_device *dev, unsigned long event) { struct inet6_dev *idev; ASSERT_RTNL(); idev = __in6_dev_get(dev); - if (event == NETDEV_BONDING_NEWTYPE) + if (event == NETDEV_POST_TYPE_CHANGE) ipv6_mc_remap(idev); - else if (event == NETDEV_BONDING_OLDTYPE) + else if (event == NETDEV_PRE_TYPE_CHANGE) ipv6_mc_unmap(idev); } -- cgit v1.2.3 From 3ca5b4042ecae5e73c59de62e4ac0db31c10e0f8 Mon Sep 17 00:00:00 2001 From: Jiri Pirko Date: Wed, 10 Mar 2010 10:29:35 +0000 Subject: bonding: check return value of nofitier when changing type This patch adds the possibility to refuse the bonding type change for other subsystems (such as for example bridge, vlan, etc.) Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- drivers/net/bonding/bond_main.c | 11 +++++++++-- include/linux/netdevice.h | 2 +- net/core/dev.c | 4 ++-- 3 files changed, 12 insertions(+), 5 deletions(-) (limited to 'include/linux') diff --git a/drivers/net/bonding/bond_main.c b/drivers/net/bonding/bond_main.c index 7eeb18751d6..cbe9e353d46 100644 --- a/drivers/net/bonding/bond_main.c +++ b/drivers/net/bonding/bond_main.c @@ -1480,8 +1480,15 @@ int bond_enslave(struct net_device *bond_dev, struct net_device *slave_dev) bond_dev->name, bond_dev->type, slave_dev->type); - netdev_bonding_change(bond_dev, - NETDEV_PRE_TYPE_CHANGE); + res = netdev_bonding_change(bond_dev, + NETDEV_PRE_TYPE_CHANGE); + res = notifier_to_errno(res); + if (res) { + pr_err("%s: refused to change device type\n", + bond_dev->name); + res = -EBUSY; + goto err_undo_flags; + } if (slave_dev->type != ARPHRD_ETHER) bond_setup_by_slave(bond_dev, slave_dev); diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index 726ecd1af53..813bed723f5 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -2005,7 +2005,7 @@ extern void __dev_addr_unsync(struct dev_addr_list **to, int *to_count, struct extern int dev_set_promiscuity(struct net_device *dev, int inc); extern int dev_set_allmulti(struct net_device *dev, int inc); extern void netdev_state_change(struct net_device *dev); -extern void netdev_bonding_change(struct net_device *dev, +extern int netdev_bonding_change(struct net_device *dev, unsigned long event); extern void netdev_features_change(struct net_device *dev); /* Load a device via the kmod */ diff --git a/net/core/dev.c b/net/core/dev.c index 1a7e1d1d5ad..d1f027c41e7 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -1084,9 +1084,9 @@ void netdev_state_change(struct net_device *dev) } EXPORT_SYMBOL(netdev_state_change); -void netdev_bonding_change(struct net_device *dev, unsigned long event) +int netdev_bonding_change(struct net_device *dev, unsigned long event) { - call_netdevice_notifiers(event, dev); + return call_netdevice_notifiers(event, dev); } EXPORT_SYMBOL(netdev_bonding_change); -- cgit v1.2.3 From 5c578aedcb21d79eeb4e9cf04ca5b276ac82614c Mon Sep 17 00:00:00 2001 From: stephen hemminger Date: Wed, 17 Mar 2010 20:31:11 +0000 Subject: IPv6: convert addrconf hash list to RCU Convert from reader/writer lock to RCU and spinlock for addrconf hash list. Adds an additional helper macro for hlist_for_each_entry_continue_rcu to handle the continue case. Signed-off-by: Stephen Hemminger Signed-off-by: David S. Miller --- include/linux/rculist.h | 13 ++++++++ include/net/if_inet6.h | 1 + net/ipv6/addrconf.c | 85 +++++++++++++++++++++++++------------------------ 3 files changed, 58 insertions(+), 41 deletions(-) (limited to 'include/linux') diff --git a/include/linux/rculist.h b/include/linux/rculist.h index 2c9b46cff3d..004908b104d 100644 --- a/include/linux/rculist.h +++ b/include/linux/rculist.h @@ -428,5 +428,18 @@ static inline void hlist_add_after_rcu(struct hlist_node *prev, ({ tpos = hlist_entry(pos, typeof(*tpos), member); 1; }); \ pos = rcu_dereference_raw(pos->next)) +/** + * hlist_for_each_entry_continue_rcu - iterate over a hlist continuing after current point + * @tpos: the type * to use as a loop cursor. + * @pos: the &struct hlist_node to use as a loop cursor. + * @member: the name of the hlist_node within the struct. + */ +#define hlist_for_each_entry_continue_rcu(tpos, pos, member) \ + for (pos = rcu_dereference((pos)->next); \ + pos && ({ prefetch(pos->next); 1; }) && \ + ({ tpos = hlist_entry(pos, typeof(*tpos), member); 1; }); \ + pos = rcu_dereference(pos->next)) + + #endif /* __KERNEL__ */ #endif diff --git a/include/net/if_inet6.h b/include/net/if_inet6.h index 49840154151..22a00b1d2c3 100644 --- a/include/net/if_inet6.h +++ b/include/net/if_inet6.h @@ -64,6 +64,7 @@ struct inet6_ifaddr { #endif int dead; + struct rcu_head rcu; }; struct ip6_sf_socklist { diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c index 0488b9f8071..7ffd5eeab96 100644 --- a/net/ipv6/addrconf.c +++ b/net/ipv6/addrconf.c @@ -127,7 +127,7 @@ static int ipv6_count_addresses(struct inet6_dev *idev); * Configured unicast address hash table */ static struct hlist_head inet6_addr_lst[IN6_ADDR_HSIZE]; -static DEFINE_RWLOCK(addrconf_hash_lock); +static DEFINE_SPINLOCK(addrconf_hash_lock); static void addrconf_verify(unsigned long); @@ -523,8 +523,13 @@ static int addrconf_fixup_forwarding(struct ctl_table *table, int *p, int old) } #endif -/* Nobody refers to this ifaddr, destroy it */ +static void inet6_ifa_finish_destroy_rcu(struct rcu_head *head) +{ + struct inet6_ifaddr *ifp = container_of(head, struct inet6_ifaddr, rcu); + kfree(ifp); +} +/* Nobody refers to this ifaddr, destroy it */ void inet6_ifa_finish_destroy(struct inet6_ifaddr *ifp) { WARN_ON(ifp->if_next != NULL); @@ -545,7 +550,7 @@ void inet6_ifa_finish_destroy(struct inet6_ifaddr *ifp) } dst_release(&ifp->rt->u.dst); - kfree(ifp); + call_rcu(&ifp->rcu, inet6_ifa_finish_destroy_rcu); } static void @@ -616,7 +621,7 @@ ipv6_add_addr(struct inet6_dev *idev, const struct in6_addr *addr, int pfxlen, goto out2; } - write_lock(&addrconf_hash_lock); + spin_lock(&addrconf_hash_lock); /* Ignore adding duplicate addresses on an interface */ if (ipv6_chk_same_addr(dev_net(idev->dev), addr, idev->dev)) { @@ -670,9 +675,9 @@ ipv6_add_addr(struct inet6_dev *idev, const struct in6_addr *addr, int pfxlen, /* Add to big hash table */ hash = ipv6_addr_hash(addr); - hlist_add_head(&ifa->addr_lst, &inet6_addr_lst[hash]); + hlist_add_head_rcu(&ifa->addr_lst, &inet6_addr_lst[hash]); in6_ifa_hold(ifa); - write_unlock(&addrconf_hash_lock); + spin_unlock(&addrconf_hash_lock); write_lock(&idev->lock); /* Add to inet6_dev unicast addr list. */ @@ -699,7 +704,7 @@ out2: return ifa; out: - write_unlock(&addrconf_hash_lock); + spin_unlock(&addrconf_hash_lock); goto out2; } @@ -717,10 +722,10 @@ static void ipv6_del_addr(struct inet6_ifaddr *ifp) ifp->dead = 1; - write_lock_bh(&addrconf_hash_lock); - hlist_del_init(&ifp->addr_lst); + spin_lock_bh(&addrconf_hash_lock); + hlist_del_init_rcu(&ifp->addr_lst); __in6_ifa_put(ifp); - write_unlock_bh(&addrconf_hash_lock); + spin_unlock_bh(&addrconf_hash_lock); write_lock_bh(&idev->lock); #ifdef CONFIG_IPV6_PRIVACY @@ -1274,8 +1279,8 @@ int ipv6_chk_addr(struct net *net, struct in6_addr *addr, struct hlist_node *node; u8 hash = ipv6_addr_hash(addr); - read_lock_bh(&addrconf_hash_lock); - hlist_for_each_entry(ifp, node, &inet6_addr_lst[hash], addr_lst) { + rcu_read_lock_bh(); + hlist_for_each_entry_rcu(ifp, node, &inet6_addr_lst[hash], addr_lst) { if (!net_eq(dev_net(ifp->idev->dev), net)) continue; if (ipv6_addr_equal(&ifp->addr, addr) && @@ -1285,7 +1290,8 @@ int ipv6_chk_addr(struct net *net, struct in6_addr *addr, break; } } - read_unlock_bh(&addrconf_hash_lock); + rcu_read_unlock_bh(); + return ifp != NULL; } EXPORT_SYMBOL(ipv6_chk_addr); @@ -1341,8 +1347,8 @@ struct inet6_ifaddr *ipv6_get_ifaddr(struct net *net, const struct in6_addr *add struct hlist_node *node; u8 hash = ipv6_addr_hash(addr); - read_lock_bh(&addrconf_hash_lock); - hlist_for_each_entry(ifp, node, &inet6_addr_lst[hash], addr_lst) { + rcu_read_lock_bh(); + hlist_for_each_entry_rcu(ifp, node, &inet6_addr_lst[hash], addr_lst) { if (!net_eq(dev_net(ifp->idev->dev), net)) continue; if (ipv6_addr_equal(&ifp->addr, addr)) { @@ -1353,7 +1359,7 @@ struct inet6_ifaddr *ipv6_get_ifaddr(struct net *net, const struct in6_addr *add } } } - read_unlock_bh(&addrconf_hash_lock); + rcu_read_unlock_bh(); return ifp; } @@ -2698,10 +2704,10 @@ static int addrconf_ifdown(struct net_device *dev, int how) write_unlock_bh(&idev->lock); /* clear hash table */ - write_lock_bh(&addrconf_hash_lock); - hlist_del_init(&ifa->addr_lst); + spin_lock_bh(&addrconf_hash_lock); + hlist_del_init_rcu(&ifa->addr_lst); __in6_ifa_put(ifa); - write_unlock_bh(&addrconf_hash_lock); + spin_unlock_bh(&addrconf_hash_lock); __ipv6_ifa_notify(RTM_DELADDR, ifa); atomic_notifier_call_chain(&inet6addr_chain, NETDEV_DOWN, ifa); @@ -2946,11 +2952,10 @@ static struct inet6_ifaddr *if6_get_first(struct seq_file *seq) for (state->bucket = 0; state->bucket < IN6_ADDR_HSIZE; ++state->bucket) { struct hlist_node *n; - hlist_for_each_entry(ifa, n, - &inet6_addr_lst[state->bucket], addr_lst) { + hlist_for_each_entry_rcu(ifa, n, &inet6_addr_lst[state->bucket], + addr_lst) if (net_eq(dev_net(ifa->idev->dev), net)) return ifa; - } } return NULL; } @@ -2962,10 +2967,9 @@ static struct inet6_ifaddr *if6_get_next(struct seq_file *seq, struct net *net = seq_file_net(seq); struct hlist_node *n = &ifa->addr_lst; - hlist_for_each_entry_continue(ifa, n, addr_lst) { + hlist_for_each_entry_continue_rcu(ifa, n, addr_lst) if (net_eq(dev_net(ifa->idev->dev), net)) return ifa; - } while (++state->bucket < IN6_ADDR_HSIZE) { hlist_for_each_entry(ifa, n, @@ -2989,9 +2993,9 @@ static struct inet6_ifaddr *if6_get_idx(struct seq_file *seq, loff_t pos) } static void *if6_seq_start(struct seq_file *seq, loff_t *pos) - __acquires(addrconf_hash_lock) + __acquires(rcu) { - read_lock_bh(&addrconf_hash_lock); + rcu_read_lock_bh(); return if6_get_idx(seq, *pos); } @@ -3005,9 +3009,9 @@ static void *if6_seq_next(struct seq_file *seq, void *v, loff_t *pos) } static void if6_seq_stop(struct seq_file *seq, void *v) - __releases(addrconf_hash_lock) + __releases(rcu) { - read_unlock_bh(&addrconf_hash_lock); + rcu_read_unlock_bh(); } static int if6_seq_show(struct seq_file *seq, void *v) @@ -3081,8 +3085,8 @@ int ipv6_chk_home_addr(struct net *net, struct in6_addr *addr) struct hlist_node *n; u8 hash = ipv6_addr_hash(addr); - read_lock_bh(&addrconf_hash_lock); - hlist_for_each_entry(ifp, n, &inet6_addr_lst[hash], addr_lst) { + rcu_read_lock_bh(); + hlist_for_each_entry_rcu(ifp, n, &inet6_addr_lst[hash], addr_lst) { if (!net_eq(dev_net(ifp->idev->dev), net)) continue; if (ipv6_addr_equal(&ifp->addr, addr) && @@ -3091,7 +3095,7 @@ int ipv6_chk_home_addr(struct net *net, struct in6_addr *addr) break; } } - read_unlock_bh(&addrconf_hash_lock); + rcu_read_unlock_bh(); return ret; } #endif @@ -3107,7 +3111,8 @@ static void addrconf_verify(unsigned long foo) unsigned long now, next; int i; - spin_lock_bh(&addrconf_verify_lock); + rcu_read_lock_bh(); + spin_lock(&addrconf_verify_lock); now = jiffies; next = now + ADDR_CHECK_FREQUENCY; @@ -3116,8 +3121,8 @@ static void addrconf_verify(unsigned long foo) for (i=0; i < IN6_ADDR_HSIZE; i++) { restart: - read_lock(&addrconf_hash_lock); - hlist_for_each_entry(ifp, node, &inet6_addr_lst[i], addr_lst) { + hlist_for_each_entry_rcu(ifp, node, + &inet6_addr_lst[i], addr_lst) { unsigned long age; #ifdef CONFIG_IPV6_PRIVACY unsigned long regen_advance; @@ -3139,7 +3144,6 @@ restart: age >= ifp->valid_lft) { spin_unlock(&ifp->lock); in6_ifa_hold(ifp); - read_unlock(&addrconf_hash_lock); ipv6_del_addr(ifp); goto restart; } else if (ifp->prefered_lft == INFINITY_LIFE_TIME) { @@ -3161,7 +3165,6 @@ restart: if (deprecate) { in6_ifa_hold(ifp); - read_unlock(&addrconf_hash_lock); ipv6_ifa_notify(0, ifp); in6_ifa_put(ifp); @@ -3179,7 +3182,7 @@ restart: in6_ifa_hold(ifp); in6_ifa_hold(ifpub); spin_unlock(&ifp->lock); - read_unlock(&addrconf_hash_lock); + spin_lock(&ifpub->lock); ifpub->regen_count = 0; spin_unlock(&ifpub->lock); @@ -3199,12 +3202,12 @@ restart: spin_unlock(&ifp->lock); } } - read_unlock(&addrconf_hash_lock); } addr_chk_timer.expires = time_before(next, jiffies + HZ) ? jiffies + HZ : next; add_timer(&addr_chk_timer); - spin_unlock_bh(&addrconf_verify_lock); + spin_unlock(&addrconf_verify_lock); + rcu_read_unlock_bh(); } static struct in6_addr *extract_addr(struct nlattr *addr, struct nlattr *local) @@ -4621,10 +4624,10 @@ void addrconf_cleanup(void) /* * Check hash table. */ - write_lock_bh(&addrconf_hash_lock); + spin_lock_bh(&addrconf_hash_lock); for (i = 0; i < IN6_ADDR_HSIZE; i++) WARN_ON(!hlist_empty(&inet6_addr_lst[i])); - write_unlock_bh(&addrconf_hash_lock); + spin_unlock_bh(&addrconf_hash_lock); del_timer(&addr_chk_timer); rtnl_unlock(); -- cgit v1.2.3 From 755d0e77ac9c8d125388922dc33434ed5b2ebe80 Mon Sep 17 00:00:00 2001 From: Patrick McHardy Date: Fri, 19 Mar 2010 04:42:24 +0000 Subject: net: rtnetlink: ignore NETDEV_PRE_TYPE_CHANGE in rtnetlink_event() Ignore the new NETDEV_PRE_TYPE_CHANGE event in rtnetlink_event() since there have been no changes userspace needs to be notified of. Also add a comment to the netdev notifier event definitions to remind people to update the exclusion list when adding new event types. Signed-off-by: Patrick McHardy Signed-off-by: David S. Miller --- include/linux/notifier.h | 5 ++++- net/core/rtnetlink.c | 1 + 2 files changed, 5 insertions(+), 1 deletion(-) (limited to 'include/linux') diff --git a/include/linux/notifier.h b/include/linux/notifier.h index f3635fc6e94..9c5d3fad01f 100644 --- a/include/linux/notifier.h +++ b/include/linux/notifier.h @@ -182,7 +182,10 @@ static inline int notifier_to_errno(int ret) * VC switch chains (for loadable kernel svgalib VC switch helpers) etc... */ -/* netdevice notifier chain */ +/* netdevice notifier chain. Please remember to update the rtnetlink + * notification exclusion list in rtnetlink_event() when adding new + * types. + */ #define NETDEV_UP 0x0001 /* For now you can't veto a device up/down */ #define NETDEV_DOWN 0x0002 #define NETDEV_REBOOT 0x0003 /* Tell a protocol stack a network interface diff --git a/net/core/rtnetlink.c b/net/core/rtnetlink.c index e1121f0bca6..ffc6cf3495a 100644 --- a/net/core/rtnetlink.c +++ b/net/core/rtnetlink.c @@ -1513,6 +1513,7 @@ static int rtnetlink_event(struct notifier_block *this, unsigned long event, voi case NETDEV_POST_INIT: case NETDEV_REGISTER: case NETDEV_CHANGE: + case NETDEV_PRE_TYPE_CHANGE: case NETDEV_GOING_DOWN: case NETDEV_UNREGISTER: case NETDEV_UNREGISTER_BATCH: -- cgit v1.2.3 From 32a806c194ea112cfab00f558482dd97bee5e44e Mon Sep 17 00:00:00 2001 From: Jiri Pirko Date: Fri, 19 Mar 2010 04:00:23 +0000 Subject: bonding: flush unicast and multicast lists when changing type After the type change, addresses in unicast and multicast lists wouldn't make sense, not to mention possible different lenghts. So flush both lists here. Note "dev_addr_discard" will be very soon replaced by "dev_mc_flush" (once mc_list conversion will be done). Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- drivers/net/bonding/bond_main.c | 4 ++++ include/linux/netdevice.h | 2 ++ net/core/dev.c | 6 ++++-- 3 files changed, 10 insertions(+), 2 deletions(-) (limited to 'include/linux') diff --git a/drivers/net/bonding/bond_main.c b/drivers/net/bonding/bond_main.c index cbe9e353d46..c2aceaab014 100644 --- a/drivers/net/bonding/bond_main.c +++ b/drivers/net/bonding/bond_main.c @@ -1490,6 +1490,10 @@ int bond_enslave(struct net_device *bond_dev, struct net_device *slave_dev) goto err_undo_flags; } + /* Flush unicast and multicast addresses */ + dev_unicast_flush(bond_dev); + dev_addr_discard(bond_dev); + if (slave_dev->type != ARPHRD_ETHER) bond_setup_by_slave(bond_dev, slave_dev); else diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index 9fc6ee8e750..c96c41e08e3 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -1994,10 +1994,12 @@ extern int dev_unicast_delete(struct net_device *dev, void *addr); extern int dev_unicast_add(struct net_device *dev, void *addr); extern int dev_unicast_sync(struct net_device *to, struct net_device *from); extern void dev_unicast_unsync(struct net_device *to, struct net_device *from); +extern void dev_unicast_flush(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 int dev_mc_sync(struct net_device *to, struct net_device *from); extern void dev_mc_unsync(struct net_device *to, struct net_device *from); +extern void dev_addr_discard(struct net_device *dev); extern int __dev_addr_delete(struct dev_addr_list **list, int *count, void *addr, int alen, int all); extern int __dev_addr_add(struct dev_addr_list **list, int *count, void *addr, int alen, int newonly); extern int __dev_addr_sync(struct dev_addr_list **to, int *to_count, struct dev_addr_list **from, int *from_count); diff --git a/net/core/dev.c b/net/core/dev.c index c0e260870c0..fe2a754238a 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -4457,12 +4457,13 @@ void dev_unicast_unsync(struct net_device *to, struct net_device *from) } EXPORT_SYMBOL(dev_unicast_unsync); -static void dev_unicast_flush(struct net_device *dev) +void dev_unicast_flush(struct net_device *dev) { netif_addr_lock_bh(dev); __hw_addr_flush(&dev->uc); netif_addr_unlock_bh(dev); } +EXPORT_SYMBOL(dev_unicast_flush); static void dev_unicast_init(struct net_device *dev) { @@ -4484,7 +4485,7 @@ static void __dev_addr_discard(struct dev_addr_list **list) } } -static void dev_addr_discard(struct net_device *dev) +void dev_addr_discard(struct net_device *dev) { netif_addr_lock_bh(dev); @@ -4493,6 +4494,7 @@ static void dev_addr_discard(struct net_device *dev) netif_addr_unlock_bh(dev); } +EXPORT_SYMBOL(dev_addr_discard); /** * dev_get_flags - get flags reported to userspace -- cgit v1.2.3 From 907cdda5205b012eec7513f66713749b293188c9 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Fri, 19 Mar 2010 05:37:18 +0000 Subject: tcp: Add SNMP counter for DEFER_ACCEPT Its currently hard to diagnose when ACK frames are dropped because an application set TCP_DEFER_ACCEPT on its listening socket. See http://bugzilla.kernel.org/show_bug.cgi?id=15507 This patch adds a SNMP value, named TCPDeferAcceptDrop netstat -s | grep TCPDeferAcceptDrop TCPDeferAcceptDrop: 0 This counter is incremented every time we drop a pure ACK frame received by a socket in SYN_RECV state because its SYNACK retrans count is lower than defer_accept value. Signed-off-by: Eric Dumazet Signed-off-by: David S. Miller --- include/linux/snmp.h | 1 + net/ipv4/proc.c | 1 + net/ipv4/tcp_minisocks.c | 1 + 3 files changed, 3 insertions(+) (limited to 'include/linux') diff --git a/include/linux/snmp.h b/include/linux/snmp.h index 4435d108475..d2a9aa3c6c8 100644 --- a/include/linux/snmp.h +++ b/include/linux/snmp.h @@ -227,6 +227,7 @@ enum LINUX_MIB_SACKSHIFTFALLBACK, LINUX_MIB_TCPBACKLOGDROP, LINUX_MIB_TCPMINTTLDROP, /* RFC 5082 */ + LINUX_MIB_TCPDEFERACCEPTDROP, __LINUX_MIB_MAX }; diff --git a/net/ipv4/proc.c b/net/ipv4/proc.c index 4f1f337f433..3dc9914c1dc 100644 --- a/net/ipv4/proc.c +++ b/net/ipv4/proc.c @@ -251,6 +251,7 @@ static const struct snmp_mib snmp4_net_list[] = { SNMP_MIB_ITEM("TCPSackShiftFallback", LINUX_MIB_SACKSHIFTFALLBACK), SNMP_MIB_ITEM("TCPBacklogDrop", LINUX_MIB_TCPBACKLOGDROP), SNMP_MIB_ITEM("TCPMinTTLDrop", LINUX_MIB_TCPMINTTLDROP), + SNMP_MIB_ITEM("TCPDeferAcceptDrop", LINUX_MIB_TCPDEFERACCEPTDROP), SNMP_MIB_SENTINEL }; diff --git a/net/ipv4/tcp_minisocks.c b/net/ipv4/tcp_minisocks.c index 4199bc6915c..32f96278a24 100644 --- a/net/ipv4/tcp_minisocks.c +++ b/net/ipv4/tcp_minisocks.c @@ -671,6 +671,7 @@ struct sock *tcp_check_req(struct sock *sk, struct sk_buff *skb, if (req->retrans < inet_csk(sk)->icsk_accept_queue.rskq_defer_accept && TCP_SKB_CB(skb)->end_seq == tcp_rsk(req)->rcv_isn + 1) { inet_rsk(req)->acked = 1; + NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_TCPDEFERACCEPTDROP); return NULL; } -- cgit v1.2.3 From f6e0bb56d43d1f3b2ad54d51b65c07ef3bdead16 Mon Sep 17 00:00:00 2001 From: Joe Perches Date: Thu, 18 Mar 2010 18:29:32 -0700 Subject: include/linux/wireless.h: Add IW_HANDLER macro to initialize array entry Copied the idea from orinoco Signed-off-by: Joe Perches Signed-off-by: John W. Linville --- include/linux/wireless.h | 2 ++ 1 file changed, 2 insertions(+) (limited to 'include/linux') diff --git a/include/linux/wireless.h b/include/linux/wireless.h index 5b4c6c772a9..0955b67616d 100644 --- a/include/linux/wireless.h +++ b/include/linux/wireless.h @@ -346,6 +346,8 @@ #define SIOCIWFIRST 0x8B00 #define SIOCIWLAST SIOCIWLASTPRIV /* 0x8BFF */ #define IW_IOCTL_IDX(cmd) ((cmd) - SIOCIWFIRST) +#define IW_HANDLER(id, func) \ + [IW_IOCTL_IDX(id)] = func /* Odd : get (world access), even : set (root access) */ #define IW_IS_SET(cmd) (!((cmd) & 0x1)) -- cgit v1.2.3 From 2ae2332ed11687325096e68e326ec57f0294cff9 Mon Sep 17 00:00:00 2001 From: Joe Perches Date: Thu, 18 Mar 2010 18:29:33 -0700 Subject: wireless.h: Use SIOCIWFIRST not SIOCSIWCOMMIT for range check These two #defines use the same value, but SIOCIWFIRST makes more sense in this use. Signed-off-by: Joe Perches Signed-off-by: John W. Linville --- include/linux/wireless.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include/linux') diff --git a/include/linux/wireless.h b/include/linux/wireless.h index 0955b67616d..e6827eedf18 100644 --- a/include/linux/wireless.h +++ b/include/linux/wireless.h @@ -650,7 +650,7 @@ * 32 bit bitmasks. Note : 32 bits = 0x20 = 2^5. */ #define IW_EVENT_CAPA_BASE(cmd) ((cmd >= SIOCIWFIRSTPRIV) ? \ (cmd - SIOCIWFIRSTPRIV + 0x60) : \ - (cmd - SIOCSIWCOMMIT)) + (cmd - SIOCIWFIRST)) #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 -- cgit v1.2.3 From d6dc1a386358979e12366d1f35eeb68fc181e101 Mon Sep 17 00:00:00 2001 From: Juuso Oikarinen Date: Tue, 23 Mar 2010 09:02:33 +0200 Subject: cfg80211: Add connection quality monitoring support to nl80211 Add support for basic configuration of a connection quality monitoring to the nl80211 interface, and basic support for notifying about triggered monitoring events. Via this interface a user-space connection manager may configure and receive pre-warning events of deteriorating WLAN connection quality, and start preparing for roaming in advance, before the connection is already lost. An example usage of such a trigger is starting scanning for nearby AP's in an attempt to find one with better connection quality, and associate to it before the connection characteristics of the existing connection become too bad or the association is even lost, leading in a prolonged delay in connectivity. The interface currently supports only RSSI, but it could be later extended to include other parameters, such as signal-to-noise ratio, if need for that arises. Signed-off-by: Juuso Oikarinen Reviewed-by: Johannes Berg Signed-off-by: John W. Linville --- include/linux/nl80211.h | 46 +++++++++++++++++ include/net/cfg80211.h | 19 +++++++ net/wireless/mlme.c | 13 +++++ net/wireless/nl80211.c | 131 ++++++++++++++++++++++++++++++++++++++++++++++++ net/wireless/nl80211.h | 6 +++ 5 files changed, 215 insertions(+) (limited to 'include/linux') diff --git a/include/linux/nl80211.h b/include/linux/nl80211.h index 28ba20fda3e..89947597b9c 100644 --- a/include/linux/nl80211.h +++ b/include/linux/nl80211.h @@ -323,6 +323,12 @@ * the TX command and %NL80211_ATTR_FRAME includes the contents of the * frame. %NL80211_ATTR_ACK flag is included if the recipient acknowledged * the frame. + * @NL80211_CMD_SET_CQM: Connection quality monitor configuration. This command + * is used to configure connection quality monitoring notification trigger + * levels. + * @NL80211_CMD_NOTIFY_CQM: Connection quality monitor notification. This + * command is used as an event to indicate the that a trigger level was + * reached. * * @NL80211_CMD_MAX: highest used command number * @__NL80211_CMD_AFTER_LAST: internal use @@ -419,6 +425,9 @@ enum nl80211_commands { NL80211_CMD_SET_POWER_SAVE, NL80211_CMD_GET_POWER_SAVE, + NL80211_CMD_SET_CQM, + NL80211_CMD_NOTIFY_CQM, + /* add new commands above here */ /* used to define NL80211_CMD_MAX below */ @@ -691,6 +700,9 @@ enum nl80211_commands { * @NL80211_ATTR_ACK: Flag attribute indicating that the frame was * acknowledged by the recipient. * + * @NL80211_ATTR_CQM: connection quality monitor configuration in a + * nested attribute with %NL80211_ATTR_CQM_* sub-attributes. + * * @NL80211_ATTR_MAX: highest attribute number currently defined * @__NL80211_ATTR_AFTER_LAST: internal use */ @@ -842,6 +854,8 @@ enum nl80211_attrs { NL80211_ATTR_PS_STATE, + NL80211_ATTR_CQM, + /* add attributes here, update the policy in nl80211.c */ __NL80211_ATTR_AFTER_LAST, @@ -1583,4 +1597,36 @@ enum nl80211_ps_state { NL80211_PS_ENABLED, }; +/** + * enum nl80211_attr_cqm - connection quality monitor attributes + * @__NL80211_ATTR_CQM_INVALID: invalid + * @NL80211_ATTR_CQM_RSSI_THOLD: RSSI threshold in dBm (zero to disable) + * @NL80211_ATTR_CQM_RSSI_HYST: RSSI hysteresis in dBm + * @NL80211_ATTR_CQM_RSSI_THRESHOLD_EVENT: RSSI threshold event + * @__NL80211_ATTR_CQM_AFTER_LAST: internal + * @NL80211_ATTR_CQM_MAX: highest key attribute + */ +enum nl80211_attr_cqm { + __NL80211_ATTR_CQM_INVALID, + NL80211_ATTR_CQM_RSSI_THOLD, + NL80211_ATTR_CQM_RSSI_HYST, + NL80211_ATTR_CQM_RSSI_THRESHOLD_EVENT, + + /* keep last */ + __NL80211_ATTR_CQM_AFTER_LAST, + NL80211_ATTR_CQM_MAX = __NL80211_ATTR_CQM_AFTER_LAST - 1 +}; + +/** + * enum nl80211_cqm_rssi_threshold_event - RSSI threshold event + * @NL80211_CQM_RSSI_THRESHOLD_EVENT_LOW - The RSSI level is lower than the + * configured threshold + * @NL80211_CQM_RSSI_THRESHOLD_EVENT_HIGH - The RSSI is higher than the + * configured threshold + */ +enum nl80211_cqm_rssi_threshold_event { + NL80211_CQM_RSSI_THRESHOLD_EVENT_LOW, + NL80211_CQM_RSSI_THRESHOLD_EVENT_HIGH, +}; + #endif /* __LINUX_NL80211_H */ diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index 3d134a1fb96..868cfd3b972 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -1007,6 +1007,7 @@ struct cfg80211_pmksa { * RSN IE. It allows for faster roaming between WPA2 BSSIDs. * @del_pmksa: Delete a cached PMKID. * @flush_pmksa: Flush all cached PMKIDs. + * @set_cqm_rssi_config: Configure connection quality monitor RSSI threshold. * */ struct cfg80211_ops { @@ -1152,6 +1153,10 @@ struct cfg80211_ops { int (*set_power_mgmt)(struct wiphy *wiphy, struct net_device *dev, bool enabled, int timeout); + + int (*set_cqm_rssi_config)(struct wiphy *wiphy, + struct net_device *dev, + s32 rssi_thold, u32 rssi_hyst); }; /* @@ -2337,4 +2342,18 @@ bool cfg80211_rx_action(struct net_device *dev, int freq, const u8 *buf, void cfg80211_action_tx_status(struct net_device *dev, u64 cookie, const u8 *buf, size_t len, bool ack, gfp_t gfp); + +/** + * cfg80211_cqm_rssi_notify - connection quality monitoring rssi event + * @dev: network device + * @rssi_event: the triggered RSSI event + * @gfp: context flags + * + * This function is called when a configured connection quality monitoring + * rssi threshold reached event occurs. + */ +void cfg80211_cqm_rssi_notify(struct net_device *dev, + enum nl80211_cqm_rssi_threshold_event rssi_event, + gfp_t gfp); + #endif /* __NET_CFG80211_H */ diff --git a/net/wireless/mlme.c b/net/wireless/mlme.c index 62bc8855e12..0855f0d3234 100644 --- a/net/wireless/mlme.c +++ b/net/wireless/mlme.c @@ -894,3 +894,16 @@ void cfg80211_action_tx_status(struct net_device *dev, u64 cookie, nl80211_send_action_tx_status(rdev, dev, cookie, buf, len, ack, gfp); } EXPORT_SYMBOL(cfg80211_action_tx_status); + +void cfg80211_cqm_rssi_notify(struct net_device *dev, + enum nl80211_cqm_rssi_threshold_event rssi_event, + gfp_t gfp) +{ + struct wireless_dev *wdev = dev->ieee80211_ptr; + struct wiphy *wiphy = wdev->wiphy; + struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); + + /* Indicate roaming trigger event to user space */ + nl80211_send_cqm_rssi_notify(rdev, dev, rssi_event, gfp); +} +EXPORT_SYMBOL(cfg80211_cqm_rssi_notify); diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index e447db04cf7..a7fc3d83f5f 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -149,6 +149,7 @@ static const struct nla_policy nl80211_policy[NL80211_ATTR_MAX+1] = { .len = IEEE80211_MAX_DATA_LEN }, [NL80211_ATTR_FRAME_MATCH] = { .type = NLA_BINARY, }, [NL80211_ATTR_PS_STATE] = { .type = NLA_U32 }, + [NL80211_ATTR_CQM] = { .type = NLA_NESTED, }, }; /* policy for the attributes */ @@ -4778,6 +4779,84 @@ unlock_rtnl: return err; } +static struct nla_policy +nl80211_attr_cqm_policy[NL80211_ATTR_CQM_MAX + 1] __read_mostly = { + [NL80211_ATTR_CQM_RSSI_THOLD] = { .type = NLA_U32 }, + [NL80211_ATTR_CQM_RSSI_HYST] = { .type = NLA_U32 }, + [NL80211_ATTR_CQM_RSSI_THRESHOLD_EVENT] = { .type = NLA_U32 }, +}; + +static int nl80211_set_cqm_rssi(struct genl_info *info, + s32 threshold, u32 hysteresis) +{ + struct cfg80211_registered_device *rdev; + struct wireless_dev *wdev; + struct net_device *dev; + int err; + + if (threshold > 0) + return -EINVAL; + + rtnl_lock(); + + err = get_rdev_dev_by_info_ifindex(info, &rdev, &dev); + if (err) + goto unlock_rdev; + + wdev = dev->ieee80211_ptr; + + if (!rdev->ops->set_cqm_rssi_config) { + err = -EOPNOTSUPP; + goto unlock_rdev; + } + + if (wdev->iftype != NL80211_IFTYPE_STATION) { + err = -EOPNOTSUPP; + goto unlock_rdev; + } + + err = rdev->ops->set_cqm_rssi_config(wdev->wiphy, dev, + threshold, hysteresis); + +unlock_rdev: + cfg80211_unlock_rdev(rdev); + dev_put(dev); + rtnl_unlock(); + + return err; +} + +static int nl80211_set_cqm(struct sk_buff *skb, struct genl_info *info) +{ + struct nlattr *attrs[NL80211_ATTR_CQM_MAX + 1]; + struct nlattr *cqm; + int err; + + cqm = info->attrs[NL80211_ATTR_CQM]; + if (!cqm) { + err = -EINVAL; + goto out; + } + + err = nla_parse_nested(attrs, NL80211_ATTR_CQM_MAX, cqm, + nl80211_attr_cqm_policy); + if (err) + goto out; + + if (attrs[NL80211_ATTR_CQM_RSSI_THOLD] && + attrs[NL80211_ATTR_CQM_RSSI_HYST]) { + s32 threshold; + u32 hysteresis; + threshold = nla_get_u32(attrs[NL80211_ATTR_CQM_RSSI_THOLD]); + hysteresis = nla_get_u32(attrs[NL80211_ATTR_CQM_RSSI_HYST]); + err = nl80211_set_cqm_rssi(info, threshold, hysteresis); + } else + err = -EINVAL; + +out: + return err; +} + static struct genl_ops nl80211_ops[] = { { .cmd = NL80211_CMD_GET_WIPHY, @@ -5082,6 +5161,12 @@ static struct genl_ops nl80211_ops[] = { .policy = nl80211_policy, /* can be retrieved by unprivileged users */ }, + { + .cmd = NL80211_CMD_SET_CQM, + .doit = nl80211_set_cqm, + .policy = nl80211_policy, + .flags = GENL_ADMIN_PERM, + }, }; static struct genl_multicast_group nl80211_mlme_mcgrp = { @@ -5832,6 +5917,52 @@ void nl80211_send_action_tx_status(struct cfg80211_registered_device *rdev, nlmsg_free(msg); } +void +nl80211_send_cqm_rssi_notify(struct cfg80211_registered_device *rdev, + struct net_device *netdev, + enum nl80211_cqm_rssi_threshold_event rssi_event, + gfp_t gfp) +{ + struct sk_buff *msg; + struct nlattr *pinfoattr; + void *hdr; + + msg = nlmsg_new(NLMSG_GOODSIZE, gfp); + if (!msg) + return; + + hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_NOTIFY_CQM); + if (!hdr) { + nlmsg_free(msg); + return; + } + + NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx); + NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex); + + pinfoattr = nla_nest_start(msg, NL80211_ATTR_CQM); + if (!pinfoattr) + goto nla_put_failure; + + NLA_PUT_U32(msg, NL80211_ATTR_CQM_RSSI_THRESHOLD_EVENT, + rssi_event); + + nla_nest_end(msg, pinfoattr); + + if (genlmsg_end(msg, hdr) < 0) { + nlmsg_free(msg); + return; + } + + genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0, + nl80211_mlme_mcgrp.id, gfp); + return; + + nla_put_failure: + genlmsg_cancel(msg, hdr); + nlmsg_free(msg); +} + static int nl80211_netlink_notify(struct notifier_block * nb, unsigned long state, void *_notify) diff --git a/net/wireless/nl80211.h b/net/wireless/nl80211.h index 4ca511102c6..2ad7fbc7d9f 100644 --- a/net/wireless/nl80211.h +++ b/net/wireless/nl80211.h @@ -82,4 +82,10 @@ void nl80211_send_action_tx_status(struct cfg80211_registered_device *rdev, const u8 *buf, size_t len, bool ack, gfp_t gfp); +void +nl80211_send_cqm_rssi_notify(struct cfg80211_registered_device *rdev, + struct net_device *netdev, + enum nl80211_cqm_rssi_threshold_event rssi_event, + gfp_t gfp); + #endif /* __NET_WIRELESS_NL80211_H */ -- cgit v1.2.3 From 4ac5541bdaea4d8e93710c58775cfa48ac82e09b Mon Sep 17 00:00:00 2001 From: Juuso Oikarinen Date: Wed, 24 Mar 2010 10:11:14 +0200 Subject: cfg80211: Improve connection quality maintenance docs in nl80211.h In nl80211.h, be a little more elaborate in the docs for the definitions NL80211_ATTR_CQM_RSSI_THOLD and NL80211_ATTR_CQM_RSSI_HYST. Reported-by: Luis R. Rodriguez Signed-off-by: Juuso Oikarinen Signed-off-by: John W. Linville --- include/linux/nl80211.h | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) (limited to 'include/linux') diff --git a/include/linux/nl80211.h b/include/linux/nl80211.h index 89947597b9c..daf6a3432b9 100644 --- a/include/linux/nl80211.h +++ b/include/linux/nl80211.h @@ -1600,8 +1600,12 @@ enum nl80211_ps_state { /** * enum nl80211_attr_cqm - connection quality monitor attributes * @__NL80211_ATTR_CQM_INVALID: invalid - * @NL80211_ATTR_CQM_RSSI_THOLD: RSSI threshold in dBm (zero to disable) - * @NL80211_ATTR_CQM_RSSI_HYST: RSSI hysteresis in dBm + * @NL80211_ATTR_CQM_RSSI_THOLD: RSSI threshold in dBm. This value specifies + * the threshold for the RSSI level at which an event will be sent. Zero + * to disable. + * @NL80211_ATTR_CQM_RSSI_HYST: RSSI hysteresis in dBm. This value specifies + * the minimum amount the RSSI level must change after an event before a + * new event may be issued (to reduce effects of RSSI oscillation). * @NL80211_ATTR_CQM_RSSI_THRESHOLD_EVENT: RSSI threshold event * @__NL80211_ATTR_CQM_AFTER_LAST: internal * @NL80211_ATTR_CQM_MAX: highest key attribute -- cgit v1.2.3 From fd0ec0e6216baea854465bbdb177f2d1b2ccaf22 Mon Sep 17 00:00:00 2001 From: Jan Engelhardt Date: Fri, 10 Jul 2009 19:27:47 +0200 Subject: netfilter: xtables: consolidate code into xt_request_find_match Signed-off-by: Jan Engelhardt --- include/linux/netfilter/x_tables.h | 2 ++ net/bridge/netfilter/ebtables.c | 5 +---- net/ipv4/netfilter/ip_tables.c | 18 ++++++++---------- net/ipv6/netfilter/ip6_tables.c | 18 ++++++++---------- net/netfilter/x_tables.c | 11 +++++++++++ 5 files changed, 30 insertions(+), 24 deletions(-) (limited to 'include/linux') diff --git a/include/linux/netfilter/x_tables.h b/include/linux/netfilter/x_tables.h index f8f55515337..dd9d15a73a9 100644 --- a/include/linux/netfilter/x_tables.h +++ b/include/linux/netfilter/x_tables.h @@ -436,6 +436,8 @@ extern struct xt_table_info *xt_replace_table(struct xt_table *table, extern struct xt_match *xt_find_match(u8 af, const char *name, u8 revision); extern struct xt_target *xt_find_target(u8 af, const char *name, u8 revision); +extern struct xt_match *xt_request_find_match(u8 af, const char *name, + u8 revision); extern struct xt_target *xt_request_find_target(u8 af, const char *name, u8 revision); extern int xt_find_revision(u8 af, const char *name, u8 revision, diff --git a/net/bridge/netfilter/ebtables.c b/net/bridge/netfilter/ebtables.c index 6d3b256d2f6..c41f3fad058 100644 --- a/net/bridge/netfilter/ebtables.c +++ b/net/bridge/netfilter/ebtables.c @@ -361,12 +361,9 @@ ebt_check_match(struct ebt_entry_match *m, struct xt_mtchk_param *par, left - sizeof(struct ebt_entry_match) < m->match_size) return -EINVAL; - match = try_then_request_module(xt_find_match(NFPROTO_BRIDGE, - m->u.name, 0), "ebt_%s", m->u.name); + match = xt_request_find_match(NFPROTO_BRIDGE, m->u.name, 0); if (IS_ERR(match)) return PTR_ERR(match); - if (match == NULL) - return -ENOENT; m->u.match = match; par->match = match; diff --git a/net/ipv4/netfilter/ip_tables.c b/net/ipv4/netfilter/ip_tables.c index e24ec48ee8c..09f6567a85b 100644 --- a/net/ipv4/netfilter/ip_tables.c +++ b/net/ipv4/netfilter/ip_tables.c @@ -629,12 +629,11 @@ find_check_match(struct ipt_entry_match *m, struct xt_mtchk_param *par) struct xt_match *match; int ret; - match = try_then_request_module(xt_find_match(AF_INET, m->u.user.name, - m->u.user.revision), - "ipt_%s", m->u.user.name); - if (IS_ERR(match) || !match) { + match = xt_request_find_match(NFPROTO_IPV4, m->u.user.name, + m->u.user.revision); + if (IS_ERR(match)) { duprintf("find_check_match: `%s' not found\n", m->u.user.name); - return match ? PTR_ERR(match) : -ENOENT; + return PTR_ERR(match); } m->u.kernel.match = match; @@ -1472,13 +1471,12 @@ compat_find_calc_match(struct ipt_entry_match *m, { struct xt_match *match; - match = try_then_request_module(xt_find_match(AF_INET, m->u.user.name, - m->u.user.revision), - "ipt_%s", m->u.user.name); - if (IS_ERR(match) || !match) { + match = xt_request_find_match(NFPROTO_IPV4, m->u.user.name, + m->u.user.revision); + if (IS_ERR(match)) { duprintf("compat_check_calc_match: `%s' not found\n", m->u.user.name); - return match ? PTR_ERR(match) : -ENOENT; + return PTR_ERR(match); } m->u.kernel.match = match; *size += xt_compat_match_offset(match); diff --git a/net/ipv6/netfilter/ip6_tables.c b/net/ipv6/netfilter/ip6_tables.c index 842bef374dc..41e2429c016 100644 --- a/net/ipv6/netfilter/ip6_tables.c +++ b/net/ipv6/netfilter/ip6_tables.c @@ -660,12 +660,11 @@ find_check_match(struct ip6t_entry_match *m, struct xt_mtchk_param *par) struct xt_match *match; int ret; - match = try_then_request_module(xt_find_match(AF_INET6, m->u.user.name, - m->u.user.revision), - "ip6t_%s", m->u.user.name); - if (IS_ERR(match) || !match) { + match = xt_request_find_match(NFPROTO_IPV6, m->u.user.name, + m->u.user.revision); + if (IS_ERR(match)) { duprintf("find_check_match: `%s' not found\n", m->u.user.name); - return match ? PTR_ERR(match) : -ENOENT; + return PTR_ERR(match); } m->u.kernel.match = match; @@ -1506,13 +1505,12 @@ compat_find_calc_match(struct ip6t_entry_match *m, { struct xt_match *match; - match = try_then_request_module(xt_find_match(AF_INET6, m->u.user.name, - m->u.user.revision), - "ip6t_%s", m->u.user.name); - if (IS_ERR(match) || !match) { + match = xt_request_find_match(NFPROTO_IPV6, m->u.user.name, + m->u.user.revision); + if (IS_ERR(match)) { duprintf("compat_check_calc_match: `%s' not found\n", m->u.user.name); - return match ? PTR_ERR(match) : -ENOENT; + return PTR_ERR(match); } m->u.kernel.match = match; *size += xt_compat_match_offset(match); diff --git a/net/netfilter/x_tables.c b/net/netfilter/x_tables.c index bf2806afd92..ee7fe215b3e 100644 --- a/net/netfilter/x_tables.c +++ b/net/netfilter/x_tables.c @@ -214,6 +214,17 @@ struct xt_match *xt_find_match(u8 af, const char *name, u8 revision) } EXPORT_SYMBOL(xt_find_match); +struct xt_match * +xt_request_find_match(uint8_t nfproto, const char *name, uint8_t revision) +{ + struct xt_match *match; + + match = try_then_request_module(xt_find_match(nfproto, name, revision), + "%st_%s", xt_prefix[nfproto], name); + return (match != NULL) ? match : ERR_PTR(-ENOENT); +} +EXPORT_SYMBOL_GPL(xt_request_find_match); + /* Find target, grabs ref. Returns ERR_PTR() on error. */ struct xt_target *xt_find_target(u8 af, const char *name, u8 revision) { -- cgit v1.2.3 From b0f38452ff73da7e9e0ddc68cd5c6b93c897ca0d Mon Sep 17 00:00:00 2001 From: Jan Engelhardt Date: Fri, 19 Mar 2010 17:16:42 +0100 Subject: netfilter: xtables: change xt_match.checkentry return type Restore function signatures from bool to int so that we can report memory allocation failures or similar using -ENOMEM rather than always having to pass -EINVAL back. This semantic patch may not be too precise (checking for functions that use xt_mtchk_param rather than functions referenced by xt_match.checkentry), but reviewed, it produced the intended result. // @@ type bool; identifier check, par; @@ -bool check +int check (struct xt_mtchk_param *par) { ... } // Signed-off-by: Jan Engelhardt --- include/linux/netfilter/x_tables.h | 2 +- net/bridge/netfilter/ebt_802_3.c | 2 +- net/bridge/netfilter/ebt_among.c | 2 +- net/bridge/netfilter/ebt_arp.c | 2 +- net/bridge/netfilter/ebt_ip.c | 2 +- net/bridge/netfilter/ebt_ip6.c | 2 +- net/bridge/netfilter/ebt_limit.c | 2 +- net/bridge/netfilter/ebt_mark_m.c | 2 +- net/bridge/netfilter/ebt_pkttype.c | 2 +- net/bridge/netfilter/ebt_stp.c | 2 +- net/bridge/netfilter/ebt_vlan.c | 2 +- net/ipv4/netfilter/ip_tables.c | 2 +- net/ipv4/netfilter/ipt_addrtype.c | 2 +- net/ipv4/netfilter/ipt_ah.c | 2 +- net/ipv4/netfilter/ipt_ecn.c | 2 +- net/ipv6/netfilter/ip6_tables.c | 2 +- net/ipv6/netfilter/ip6t_ah.c | 2 +- net/ipv6/netfilter/ip6t_frag.c | 2 +- net/ipv6/netfilter/ip6t_hbh.c | 2 +- net/ipv6/netfilter/ip6t_ipv6header.c | 2 +- net/ipv6/netfilter/ip6t_mh.c | 2 +- net/ipv6/netfilter/ip6t_rt.c | 2 +- net/netfilter/xt_cluster.c | 2 +- net/netfilter/xt_connbytes.c | 2 +- net/netfilter/xt_connlimit.c | 2 +- net/netfilter/xt_connmark.c | 2 +- net/netfilter/xt_conntrack.c | 2 +- net/netfilter/xt_dccp.c | 2 +- net/netfilter/xt_dscp.c | 2 +- net/netfilter/xt_esp.c | 2 +- net/netfilter/xt_hashlimit.c | 4 ++-- net/netfilter/xt_helper.c | 2 +- net/netfilter/xt_limit.c | 2 +- net/netfilter/xt_multiport.c | 8 ++++---- net/netfilter/xt_physdev.c | 2 +- net/netfilter/xt_policy.c | 2 +- net/netfilter/xt_quota.c | 2 +- net/netfilter/xt_rateest.c | 2 +- net/netfilter/xt_recent.c | 2 +- net/netfilter/xt_sctp.c | 2 +- net/netfilter/xt_state.c | 2 +- net/netfilter/xt_statistic.c | 2 +- net/netfilter/xt_string.c | 2 +- net/netfilter/xt_tcpudp.c | 4 ++-- net/netfilter/xt_time.c | 2 +- 45 files changed, 50 insertions(+), 50 deletions(-) (limited to 'include/linux') diff --git a/include/linux/netfilter/x_tables.h b/include/linux/netfilter/x_tables.h index dd9d15a73a9..33c1a62a099 100644 --- a/include/linux/netfilter/x_tables.h +++ b/include/linux/netfilter/x_tables.h @@ -303,7 +303,7 @@ struct xt_match { const struct xt_match_param *); /* Called when user tries to insert an entry of this type. */ - bool (*checkentry)(const struct xt_mtchk_param *); + int (*checkentry)(const struct xt_mtchk_param *); /* Called when entry of this type deleted. */ void (*destroy)(const struct xt_mtdtor_param *); diff --git a/net/bridge/netfilter/ebt_802_3.c b/net/bridge/netfilter/ebt_802_3.c index 5d1176758ca..7b6f4c4cccb 100644 --- a/net/bridge/netfilter/ebt_802_3.c +++ b/net/bridge/netfilter/ebt_802_3.c @@ -36,7 +36,7 @@ ebt_802_3_mt(const struct sk_buff *skb, const struct xt_match_param *par) return true; } -static bool ebt_802_3_mt_check(const struct xt_mtchk_param *par) +static int ebt_802_3_mt_check(const struct xt_mtchk_param *par) { const struct ebt_802_3_info *info = par->matchinfo; diff --git a/net/bridge/netfilter/ebt_among.c b/net/bridge/netfilter/ebt_among.c index 60ad6308bc1..8a75d399b51 100644 --- a/net/bridge/netfilter/ebt_among.c +++ b/net/bridge/netfilter/ebt_among.c @@ -172,7 +172,7 @@ ebt_among_mt(const struct sk_buff *skb, const struct xt_match_param *par) return true; } -static bool ebt_among_mt_check(const struct xt_mtchk_param *par) +static int ebt_among_mt_check(const struct xt_mtchk_param *par) { const struct ebt_among_info *info = par->matchinfo; const struct ebt_entry_match *em = diff --git a/net/bridge/netfilter/ebt_arp.c b/net/bridge/netfilter/ebt_arp.c index e727697c584..fc62055adb1 100644 --- a/net/bridge/netfilter/ebt_arp.c +++ b/net/bridge/netfilter/ebt_arp.c @@ -100,7 +100,7 @@ ebt_arp_mt(const struct sk_buff *skb, const struct xt_match_param *par) return true; } -static bool ebt_arp_mt_check(const struct xt_mtchk_param *par) +static int ebt_arp_mt_check(const struct xt_mtchk_param *par) { const struct ebt_arp_info *info = par->matchinfo; const struct ebt_entry *e = par->entryinfo; diff --git a/net/bridge/netfilter/ebt_ip.c b/net/bridge/netfilter/ebt_ip.c index 5de6df6f86b..d1a555dc887 100644 --- a/net/bridge/netfilter/ebt_ip.c +++ b/net/bridge/netfilter/ebt_ip.c @@ -77,7 +77,7 @@ ebt_ip_mt(const struct sk_buff *skb, const struct xt_match_param *par) return true; } -static bool ebt_ip_mt_check(const struct xt_mtchk_param *par) +static int ebt_ip_mt_check(const struct xt_mtchk_param *par) { const struct ebt_ip_info *info = par->matchinfo; const struct ebt_entry *e = par->entryinfo; diff --git a/net/bridge/netfilter/ebt_ip6.c b/net/bridge/netfilter/ebt_ip6.c index 63e3888d20c..fa4ecf50fdc 100644 --- a/net/bridge/netfilter/ebt_ip6.c +++ b/net/bridge/netfilter/ebt_ip6.c @@ -80,7 +80,7 @@ ebt_ip6_mt(const struct sk_buff *skb, const struct xt_match_param *par) return true; } -static bool ebt_ip6_mt_check(const struct xt_mtchk_param *par) +static int ebt_ip6_mt_check(const struct xt_mtchk_param *par) { const struct ebt_entry *e = par->entryinfo; struct ebt_ip6_info *info = par->matchinfo; diff --git a/net/bridge/netfilter/ebt_limit.c b/net/bridge/netfilter/ebt_limit.c index 5b7330b6254..abfb0ecd7c1 100644 --- a/net/bridge/netfilter/ebt_limit.c +++ b/net/bridge/netfilter/ebt_limit.c @@ -65,7 +65,7 @@ user2credits(u_int32_t user) return (user * HZ * CREDITS_PER_JIFFY) / EBT_LIMIT_SCALE; } -static bool ebt_limit_mt_check(const struct xt_mtchk_param *par) +static int ebt_limit_mt_check(const struct xt_mtchk_param *par) { struct ebt_limit_info *info = par->matchinfo; diff --git a/net/bridge/netfilter/ebt_mark_m.c b/net/bridge/netfilter/ebt_mark_m.c index 8de8c396d91..1e5b0b316fb 100644 --- a/net/bridge/netfilter/ebt_mark_m.c +++ b/net/bridge/netfilter/ebt_mark_m.c @@ -22,7 +22,7 @@ ebt_mark_mt(const struct sk_buff *skb, const struct xt_match_param *par) return ((skb->mark & info->mask) == info->mark) ^ info->invert; } -static bool ebt_mark_mt_check(const struct xt_mtchk_param *par) +static int ebt_mark_mt_check(const struct xt_mtchk_param *par) { const struct ebt_mark_m_info *info = par->matchinfo; diff --git a/net/bridge/netfilter/ebt_pkttype.c b/net/bridge/netfilter/ebt_pkttype.c index e2a07e6cbef..9b3c6451660 100644 --- a/net/bridge/netfilter/ebt_pkttype.c +++ b/net/bridge/netfilter/ebt_pkttype.c @@ -20,7 +20,7 @@ ebt_pkttype_mt(const struct sk_buff *skb, const struct xt_match_param *par) return (skb->pkt_type == info->pkt_type) ^ info->invert; } -static bool ebt_pkttype_mt_check(const struct xt_mtchk_param *par) +static int ebt_pkttype_mt_check(const struct xt_mtchk_param *par) { const struct ebt_pkttype_info *info = par->matchinfo; diff --git a/net/bridge/netfilter/ebt_stp.c b/net/bridge/netfilter/ebt_stp.c index 92a93d36376..521186fa699 100644 --- a/net/bridge/netfilter/ebt_stp.c +++ b/net/bridge/netfilter/ebt_stp.c @@ -153,7 +153,7 @@ ebt_stp_mt(const struct sk_buff *skb, const struct xt_match_param *par) return true; } -static bool ebt_stp_mt_check(const struct xt_mtchk_param *par) +static int ebt_stp_mt_check(const struct xt_mtchk_param *par) { const struct ebt_stp_info *info = par->matchinfo; const uint8_t bridge_ula[6] = {0x01, 0x80, 0xc2, 0x00, 0x00, 0x00}; diff --git a/net/bridge/netfilter/ebt_vlan.c b/net/bridge/netfilter/ebt_vlan.c index 5c44f51063c..04a9575389d 100644 --- a/net/bridge/netfilter/ebt_vlan.c +++ b/net/bridge/netfilter/ebt_vlan.c @@ -79,7 +79,7 @@ ebt_vlan_mt(const struct sk_buff *skb, const struct xt_match_param *par) return true; } -static bool ebt_vlan_mt_check(const struct xt_mtchk_param *par) +static int ebt_vlan_mt_check(const struct xt_mtchk_param *par) { struct ebt_vlan_info *info = par->matchinfo; const struct ebt_entry *e = par->entryinfo; diff --git a/net/ipv4/netfilter/ip_tables.c b/net/ipv4/netfilter/ip_tables.c index 09f6567a85b..771ffa7b9af 100644 --- a/net/ipv4/netfilter/ip_tables.c +++ b/net/ipv4/netfilter/ip_tables.c @@ -2176,7 +2176,7 @@ icmp_match(const struct sk_buff *skb, const struct xt_match_param *par) !!(icmpinfo->invflags&IPT_ICMP_INV)); } -static bool icmp_checkentry(const struct xt_mtchk_param *par) +static int icmp_checkentry(const struct xt_mtchk_param *par) { const struct ipt_icmp *icmpinfo = par->matchinfo; diff --git a/net/ipv4/netfilter/ipt_addrtype.c b/net/ipv4/netfilter/ipt_addrtype.c index ea4f58a46c6..81197f456d7 100644 --- a/net/ipv4/netfilter/ipt_addrtype.c +++ b/net/ipv4/netfilter/ipt_addrtype.c @@ -70,7 +70,7 @@ addrtype_mt_v1(const struct sk_buff *skb, const struct xt_match_param *par) return ret; } -static bool addrtype_mt_checkentry_v1(const struct xt_mtchk_param *par) +static int addrtype_mt_checkentry_v1(const struct xt_mtchk_param *par) { struct ipt_addrtype_info_v1 *info = par->matchinfo; diff --git a/net/ipv4/netfilter/ipt_ah.c b/net/ipv4/netfilter/ipt_ah.c index 4f27e170c63..667ded16e12 100644 --- a/net/ipv4/netfilter/ipt_ah.c +++ b/net/ipv4/netfilter/ipt_ah.c @@ -55,7 +55,7 @@ static bool ah_mt(const struct sk_buff *skb, const struct xt_match_param *par) !!(ahinfo->invflags & IPT_AH_INV_SPI)); } -static bool ah_mt_check(const struct xt_mtchk_param *par) +static int ah_mt_check(const struct xt_mtchk_param *par) { const struct ipt_ah *ahinfo = par->matchinfo; diff --git a/net/ipv4/netfilter/ipt_ecn.c b/net/ipv4/netfilter/ipt_ecn.c index e661108c73f..d1e234fe7f1 100644 --- a/net/ipv4/netfilter/ipt_ecn.c +++ b/net/ipv4/netfilter/ipt_ecn.c @@ -85,7 +85,7 @@ static bool ecn_mt(const struct sk_buff *skb, const struct xt_match_param *par) return true; } -static bool ecn_mt_check(const struct xt_mtchk_param *par) +static int ecn_mt_check(const struct xt_mtchk_param *par) { const struct ipt_ecn_info *info = par->matchinfo; const struct ipt_ip *ip = par->entryinfo; diff --git a/net/ipv6/netfilter/ip6_tables.c b/net/ipv6/netfilter/ip6_tables.c index 41e2429c016..595b45d52ff 100644 --- a/net/ipv6/netfilter/ip6_tables.c +++ b/net/ipv6/netfilter/ip6_tables.c @@ -2209,7 +2209,7 @@ icmp6_match(const struct sk_buff *skb, const struct xt_match_param *par) } /* Called when user tries to insert an entry of this type. */ -static bool icmp6_checkentry(const struct xt_mtchk_param *par) +static int icmp6_checkentry(const struct xt_mtchk_param *par) { const struct ip6t_icmp *icmpinfo = par->matchinfo; diff --git a/net/ipv6/netfilter/ip6t_ah.c b/net/ipv6/netfilter/ip6t_ah.c index 4429bfd39e1..3d570446dee 100644 --- a/net/ipv6/netfilter/ip6t_ah.c +++ b/net/ipv6/netfilter/ip6t_ah.c @@ -87,7 +87,7 @@ static bool ah_mt6(const struct sk_buff *skb, const struct xt_match_param *par) !(ahinfo->hdrres && ah->reserved); } -static bool ah_mt6_check(const struct xt_mtchk_param *par) +static int ah_mt6_check(const struct xt_mtchk_param *par) { const struct ip6t_ah *ahinfo = par->matchinfo; diff --git a/net/ipv6/netfilter/ip6t_frag.c b/net/ipv6/netfilter/ip6t_frag.c index 5c0da913b4a..c2dba2701fa 100644 --- a/net/ipv6/netfilter/ip6t_frag.c +++ b/net/ipv6/netfilter/ip6t_frag.c @@ -102,7 +102,7 @@ frag_mt6(const struct sk_buff *skb, const struct xt_match_param *par) (ntohs(fh->frag_off) & IP6_MF)); } -static bool frag_mt6_check(const struct xt_mtchk_param *par) +static int frag_mt6_check(const struct xt_mtchk_param *par) { const struct ip6t_frag *fraginfo = par->matchinfo; diff --git a/net/ipv6/netfilter/ip6t_hbh.c b/net/ipv6/netfilter/ip6t_hbh.c index f4b73889d00..1b294317707 100644 --- a/net/ipv6/netfilter/ip6t_hbh.c +++ b/net/ipv6/netfilter/ip6t_hbh.c @@ -164,7 +164,7 @@ hbh_mt6(const struct sk_buff *skb, const struct xt_match_param *par) return false; } -static bool hbh_mt6_check(const struct xt_mtchk_param *par) +static int hbh_mt6_check(const struct xt_mtchk_param *par) { const struct ip6t_opts *optsinfo = par->matchinfo; diff --git a/net/ipv6/netfilter/ip6t_ipv6header.c b/net/ipv6/netfilter/ip6t_ipv6header.c index 91490ad9302..90e1e04b793 100644 --- a/net/ipv6/netfilter/ip6t_ipv6header.c +++ b/net/ipv6/netfilter/ip6t_ipv6header.c @@ -118,7 +118,7 @@ ipv6header_mt6(const struct sk_buff *skb, const struct xt_match_param *par) } } -static bool ipv6header_mt6_check(const struct xt_mtchk_param *par) +static int ipv6header_mt6_check(const struct xt_mtchk_param *par) { const struct ip6t_ipv6header_info *info = par->matchinfo; diff --git a/net/ipv6/netfilter/ip6t_mh.c b/net/ipv6/netfilter/ip6t_mh.c index 0181eb81d24..d9408045994 100644 --- a/net/ipv6/netfilter/ip6t_mh.c +++ b/net/ipv6/netfilter/ip6t_mh.c @@ -62,7 +62,7 @@ static bool mh_mt6(const struct sk_buff *skb, const struct xt_match_param *par) !!(mhinfo->invflags & IP6T_MH_INV_TYPE)); } -static bool mh_mt6_check(const struct xt_mtchk_param *par) +static int mh_mt6_check(const struct xt_mtchk_param *par) { const struct ip6t_mh *mhinfo = par->matchinfo; diff --git a/net/ipv6/netfilter/ip6t_rt.c b/net/ipv6/netfilter/ip6t_rt.c index c58d6533657..76397f35eaf 100644 --- a/net/ipv6/netfilter/ip6t_rt.c +++ b/net/ipv6/netfilter/ip6t_rt.c @@ -183,7 +183,7 @@ static bool rt_mt6(const struct sk_buff *skb, const struct xt_match_param *par) return false; } -static bool rt_mt6_check(const struct xt_mtchk_param *par) +static int rt_mt6_check(const struct xt_mtchk_param *par) { const struct ip6t_rt *rtinfo = par->matchinfo; diff --git a/net/netfilter/xt_cluster.c b/net/netfilter/xt_cluster.c index 4c273e87130..1f2c35ef142 100644 --- a/net/netfilter/xt_cluster.c +++ b/net/netfilter/xt_cluster.c @@ -132,7 +132,7 @@ xt_cluster_mt(const struct sk_buff *skb, const struct xt_match_param *par) !!(info->flags & XT_CLUSTER_F_INV); } -static bool xt_cluster_mt_checkentry(const struct xt_mtchk_param *par) +static int xt_cluster_mt_checkentry(const struct xt_mtchk_param *par) { struct xt_cluster_match_info *info = par->matchinfo; diff --git a/net/netfilter/xt_connbytes.c b/net/netfilter/xt_connbytes.c index edb7bbd9ae5..136ef4ccdac 100644 --- a/net/netfilter/xt_connbytes.c +++ b/net/netfilter/xt_connbytes.c @@ -93,7 +93,7 @@ connbytes_mt(const struct sk_buff *skb, const struct xt_match_param *par) return what >= sinfo->count.from; } -static bool connbytes_mt_check(const struct xt_mtchk_param *par) +static int connbytes_mt_check(const struct xt_mtchk_param *par) { const struct xt_connbytes_info *sinfo = par->matchinfo; diff --git a/net/netfilter/xt_connlimit.c b/net/netfilter/xt_connlimit.c index d5b26dab9e2..a9fec38ab02 100644 --- a/net/netfilter/xt_connlimit.c +++ b/net/netfilter/xt_connlimit.c @@ -216,7 +216,7 @@ connlimit_mt(const struct sk_buff *skb, const struct xt_match_param *par) return false; } -static bool connlimit_mt_check(const struct xt_mtchk_param *par) +static int connlimit_mt_check(const struct xt_mtchk_param *par) { struct xt_connlimit_info *info = par->matchinfo; unsigned int i; diff --git a/net/netfilter/xt_connmark.c b/net/netfilter/xt_connmark.c index 7a51ba63f54..df7eaff874f 100644 --- a/net/netfilter/xt_connmark.c +++ b/net/netfilter/xt_connmark.c @@ -103,7 +103,7 @@ connmark_mt(const struct sk_buff *skb, const struct xt_match_param *par) return ((ct->mark & info->mask) == info->mark) ^ info->invert; } -static bool connmark_mt_check(const struct xt_mtchk_param *par) +static int connmark_mt_check(const struct xt_mtchk_param *par) { if (nf_ct_l3proto_try_module_get(par->family) < 0) { pr_info("cannot load conntrack support for proto=%u\n", diff --git a/net/netfilter/xt_conntrack.c b/net/netfilter/xt_conntrack.c index 387172b6b0d..500e0338a18 100644 --- a/net/netfilter/xt_conntrack.c +++ b/net/netfilter/xt_conntrack.c @@ -206,7 +206,7 @@ conntrack_mt_v2(const struct sk_buff *skb, const struct xt_match_param *par) return conntrack_mt(skb, par, info->state_mask, info->status_mask); } -static bool conntrack_mt_check(const struct xt_mtchk_param *par) +static int conntrack_mt_check(const struct xt_mtchk_param *par) { if (nf_ct_l3proto_try_module_get(par->family) < 0) { pr_info("cannot load conntrack support for proto=%u\n", diff --git a/net/netfilter/xt_dccp.c b/net/netfilter/xt_dccp.c index 8f6014f7c88..da8c301d24e 100644 --- a/net/netfilter/xt_dccp.c +++ b/net/netfilter/xt_dccp.c @@ -123,7 +123,7 @@ dccp_mt(const struct sk_buff *skb, const struct xt_match_param *par) XT_DCCP_OPTION, info->flags, info->invflags); } -static bool dccp_mt_check(const struct xt_mtchk_param *par) +static int dccp_mt_check(const struct xt_mtchk_param *par) { const struct xt_dccp_info *info = par->matchinfo; diff --git a/net/netfilter/xt_dscp.c b/net/netfilter/xt_dscp.c index 6ecedc13db0..295da4ce822 100644 --- a/net/netfilter/xt_dscp.c +++ b/net/netfilter/xt_dscp.c @@ -42,7 +42,7 @@ dscp_mt6(const struct sk_buff *skb, const struct xt_match_param *par) return (dscp == info->dscp) ^ !!info->invert; } -static bool dscp_mt_check(const struct xt_mtchk_param *par) +static int dscp_mt_check(const struct xt_mtchk_param *par) { const struct xt_dscp_info *info = par->matchinfo; diff --git a/net/netfilter/xt_esp.c b/net/netfilter/xt_esp.c index 1a446d62676..9f5da979567 100644 --- a/net/netfilter/xt_esp.c +++ b/net/netfilter/xt_esp.c @@ -60,7 +60,7 @@ static bool esp_mt(const struct sk_buff *skb, const struct xt_match_param *par) !!(espinfo->invflags & XT_ESP_INV_SPI)); } -static bool esp_mt_check(const struct xt_mtchk_param *par) +static int esp_mt_check(const struct xt_mtchk_param *par) { const struct xt_esp *espinfo = par->matchinfo; diff --git a/net/netfilter/xt_hashlimit.c b/net/netfilter/xt_hashlimit.c index 8f3e0c02ca5..d13800c9593 100644 --- a/net/netfilter/xt_hashlimit.c +++ b/net/netfilter/xt_hashlimit.c @@ -671,7 +671,7 @@ hashlimit_mt(const struct sk_buff *skb, const struct xt_match_param *par) return false; } -static bool hashlimit_mt_check_v0(const struct xt_mtchk_param *par) +static int hashlimit_mt_check_v0(const struct xt_mtchk_param *par) { struct net *net = par->net; struct xt_hashlimit_info *r = par->matchinfo; @@ -707,7 +707,7 @@ static bool hashlimit_mt_check_v0(const struct xt_mtchk_param *par) return true; } -static bool hashlimit_mt_check(const struct xt_mtchk_param *par) +static int hashlimit_mt_check(const struct xt_mtchk_param *par) { struct net *net = par->net; struct xt_hashlimit_mtinfo1 *info = par->matchinfo; diff --git a/net/netfilter/xt_helper.c b/net/netfilter/xt_helper.c index 482aff2ccf7..6e177b279f9 100644 --- a/net/netfilter/xt_helper.c +++ b/net/netfilter/xt_helper.c @@ -54,7 +54,7 @@ helper_mt(const struct sk_buff *skb, const struct xt_match_param *par) return ret; } -static bool helper_mt_check(const struct xt_mtchk_param *par) +static int helper_mt_check(const struct xt_mtchk_param *par) { struct xt_helper_info *info = par->matchinfo; diff --git a/net/netfilter/xt_limit.c b/net/netfilter/xt_limit.c index b3dfca63fa5..138a324df8d 100644 --- a/net/netfilter/xt_limit.c +++ b/net/netfilter/xt_limit.c @@ -97,7 +97,7 @@ user2credits(u_int32_t user) return (user * HZ * CREDITS_PER_JIFFY) / XT_LIMIT_SCALE; } -static bool limit_mt_check(const struct xt_mtchk_param *par) +static int limit_mt_check(const struct xt_mtchk_param *par) { struct xt_rateinfo *r = par->matchinfo; struct xt_limit_priv *priv; diff --git a/net/netfilter/xt_multiport.c b/net/netfilter/xt_multiport.c index 4fa90c86fdb..b446738eab1 100644 --- a/net/netfilter/xt_multiport.c +++ b/net/netfilter/xt_multiport.c @@ -152,7 +152,7 @@ check(u_int16_t proto, && count <= XT_MULTI_PORTS; } -static bool multiport_mt_check_v0(const struct xt_mtchk_param *par) +static int multiport_mt_check_v0(const struct xt_mtchk_param *par) { const struct ipt_ip *ip = par->entryinfo; const struct xt_multiport *multiinfo = par->matchinfo; @@ -161,7 +161,7 @@ static bool multiport_mt_check_v0(const struct xt_mtchk_param *par) multiinfo->count); } -static bool multiport_mt_check(const struct xt_mtchk_param *par) +static int multiport_mt_check(const struct xt_mtchk_param *par) { const struct ipt_ip *ip = par->entryinfo; const struct xt_multiport_v1 *multiinfo = par->matchinfo; @@ -170,7 +170,7 @@ static bool multiport_mt_check(const struct xt_mtchk_param *par) multiinfo->count); } -static bool multiport_mt6_check_v0(const struct xt_mtchk_param *par) +static int multiport_mt6_check_v0(const struct xt_mtchk_param *par) { const struct ip6t_ip6 *ip = par->entryinfo; const struct xt_multiport *multiinfo = par->matchinfo; @@ -179,7 +179,7 @@ static bool multiport_mt6_check_v0(const struct xt_mtchk_param *par) multiinfo->count); } -static bool multiport_mt6_check(const struct xt_mtchk_param *par) +static int multiport_mt6_check(const struct xt_mtchk_param *par) { const struct ip6t_ip6 *ip = par->entryinfo; const struct xt_multiport_v1 *multiinfo = par->matchinfo; diff --git a/net/netfilter/xt_physdev.c b/net/netfilter/xt_physdev.c index 3d42a278408..850e412c83e 100644 --- a/net/netfilter/xt_physdev.c +++ b/net/netfilter/xt_physdev.c @@ -83,7 +83,7 @@ match_outdev: return (!!ret ^ !(info->invert & XT_PHYSDEV_OP_OUT)); } -static bool physdev_mt_check(const struct xt_mtchk_param *par) +static int physdev_mt_check(const struct xt_mtchk_param *par) { const struct xt_physdev_info *info = par->matchinfo; diff --git a/net/netfilter/xt_policy.c b/net/netfilter/xt_policy.c index de3aded6afb..c9965b640b1 100644 --- a/net/netfilter/xt_policy.c +++ b/net/netfilter/xt_policy.c @@ -128,7 +128,7 @@ policy_mt(const struct sk_buff *skb, const struct xt_match_param *par) return ret; } -static bool policy_mt_check(const struct xt_mtchk_param *par) +static int policy_mt_check(const struct xt_mtchk_param *par) { const struct xt_policy_info *info = par->matchinfo; diff --git a/net/netfilter/xt_quota.c b/net/netfilter/xt_quota.c index 390b7d09fe5..2861fac5f2e 100644 --- a/net/netfilter/xt_quota.c +++ b/net/netfilter/xt_quota.c @@ -43,7 +43,7 @@ quota_mt(const struct sk_buff *skb, const struct xt_match_param *par) return ret; } -static bool quota_mt_check(const struct xt_mtchk_param *par) +static int quota_mt_check(const struct xt_mtchk_param *par) { struct xt_quota_info *q = par->matchinfo; diff --git a/net/netfilter/xt_rateest.c b/net/netfilter/xt_rateest.c index 4fc6a917f6d..3b5e3d613b1 100644 --- a/net/netfilter/xt_rateest.c +++ b/net/netfilter/xt_rateest.c @@ -74,7 +74,7 @@ xt_rateest_mt(const struct sk_buff *skb, const struct xt_match_param *par) return ret; } -static bool xt_rateest_mt_checkentry(const struct xt_mtchk_param *par) +static int xt_rateest_mt_checkentry(const struct xt_mtchk_param *par) { struct xt_rateest_match_info *info = par->matchinfo; struct xt_rateest *est1, *est2; diff --git a/net/netfilter/xt_recent.c b/net/netfilter/xt_recent.c index 85309448c5e..52042c8bf7f 100644 --- a/net/netfilter/xt_recent.c +++ b/net/netfilter/xt_recent.c @@ -305,7 +305,7 @@ out: return ret; } -static bool recent_mt_check(const struct xt_mtchk_param *par) +static int recent_mt_check(const struct xt_mtchk_param *par) { struct recent_net *recent_net = recent_pernet(par->net); const struct xt_recent_mtinfo *info = par->matchinfo; diff --git a/net/netfilter/xt_sctp.c b/net/netfilter/xt_sctp.c index 977b182dea5..5037a7a0059 100644 --- a/net/netfilter/xt_sctp.c +++ b/net/netfilter/xt_sctp.c @@ -144,7 +144,7 @@ sctp_mt(const struct sk_buff *skb, const struct xt_match_param *par) XT_SCTP_CHUNK_TYPES, info->flags, info->invflags); } -static bool sctp_mt_check(const struct xt_mtchk_param *par) +static int sctp_mt_check(const struct xt_mtchk_param *par) { const struct xt_sctp_info *info = par->matchinfo; diff --git a/net/netfilter/xt_state.c b/net/netfilter/xt_state.c index 94893be8027..8b15b1317f1 100644 --- a/net/netfilter/xt_state.c +++ b/net/netfilter/xt_state.c @@ -37,7 +37,7 @@ state_mt(const struct sk_buff *skb, const struct xt_match_param *par) return (sinfo->statemask & statebit); } -static bool state_mt_check(const struct xt_mtchk_param *par) +static int state_mt_check(const struct xt_mtchk_param *par) { if (nf_ct_l3proto_try_module_get(par->family) < 0) { pr_info("cannot load conntrack support for proto=%u\n", diff --git a/net/netfilter/xt_statistic.c b/net/netfilter/xt_statistic.c index 51ac1bbb4f5..a577ab008f5 100644 --- a/net/netfilter/xt_statistic.c +++ b/net/netfilter/xt_statistic.c @@ -52,7 +52,7 @@ statistic_mt(const struct sk_buff *skb, const struct xt_match_param *par) return ret; } -static bool statistic_mt_check(const struct xt_mtchk_param *par) +static int statistic_mt_check(const struct xt_mtchk_param *par) { struct xt_statistic_info *info = par->matchinfo; diff --git a/net/netfilter/xt_string.c b/net/netfilter/xt_string.c index b4d77411131..7d1412154e2 100644 --- a/net/netfilter/xt_string.c +++ b/net/netfilter/xt_string.c @@ -40,7 +40,7 @@ string_mt(const struct sk_buff *skb, const struct xt_match_param *par) #define STRING_TEXT_PRIV(m) ((struct xt_string_info *)(m)) -static bool string_mt_check(const struct xt_mtchk_param *par) +static int string_mt_check(const struct xt_mtchk_param *par) { struct xt_string_info *conf = par->matchinfo; struct ts_config *ts_conf; diff --git a/net/netfilter/xt_tcpudp.c b/net/netfilter/xt_tcpudp.c index b53887f83c4..00728410099 100644 --- a/net/netfilter/xt_tcpudp.c +++ b/net/netfilter/xt_tcpudp.c @@ -120,7 +120,7 @@ static bool tcp_mt(const struct sk_buff *skb, const struct xt_match_param *par) return true; } -static bool tcp_mt_check(const struct xt_mtchk_param *par) +static int tcp_mt_check(const struct xt_mtchk_param *par) { const struct xt_tcp *tcpinfo = par->matchinfo; @@ -155,7 +155,7 @@ static bool udp_mt(const struct sk_buff *skb, const struct xt_match_param *par) !!(udpinfo->invflags & XT_UDP_INV_DSTPT)); } -static bool udp_mt_check(const struct xt_mtchk_param *par) +static int udp_mt_check(const struct xt_mtchk_param *par) { const struct xt_udp *udpinfo = par->matchinfo; diff --git a/net/netfilter/xt_time.c b/net/netfilter/xt_time.c index 45ed05b5161..db74f4fd57d 100644 --- a/net/netfilter/xt_time.c +++ b/net/netfilter/xt_time.c @@ -217,7 +217,7 @@ time_mt(const struct sk_buff *skb, const struct xt_match_param *par) return true; } -static bool time_mt_check(const struct xt_mtchk_param *par) +static int time_mt_check(const struct xt_mtchk_param *par) { const struct xt_time_info *info = par->matchinfo; -- cgit v1.2.3 From 135367b8f6a18507af6b9a6910a14b5699415309 Mon Sep 17 00:00:00 2001 From: Jan Engelhardt Date: Fri, 19 Mar 2010 17:16:42 +0100 Subject: netfilter: xtables: change xt_target.checkentry return type Restore function signatures from bool to int so that we can report memory allocation failures or similar using -ENOMEM rather than always having to pass -EINVAL back. // @@ type bool; identifier check, par; @@ -bool check +int check (struct xt_tgchk_param *par) { ... } // Minus the change it does to xt_ct_find_proto. Signed-off-by: Jan Engelhardt --- include/linux/netfilter/x_tables.h | 4 ++-- net/bridge/netfilter/ebt_arpreply.c | 2 +- net/bridge/netfilter/ebt_dnat.c | 2 +- net/bridge/netfilter/ebt_log.c | 2 +- net/bridge/netfilter/ebt_mark.c | 2 +- net/bridge/netfilter/ebt_nflog.c | 2 +- net/bridge/netfilter/ebt_redirect.c | 2 +- net/bridge/netfilter/ebt_snat.c | 2 +- net/bridge/netfilter/ebt_ulog.c | 2 +- net/ipv4/netfilter/arpt_mangle.c | 2 +- net/ipv4/netfilter/ipt_CLUSTERIP.c | 2 +- net/ipv4/netfilter/ipt_ECN.c | 2 +- net/ipv4/netfilter/ipt_LOG.c | 2 +- net/ipv4/netfilter/ipt_MASQUERADE.c | 2 +- net/ipv4/netfilter/ipt_NETMAP.c | 2 +- net/ipv4/netfilter/ipt_REDIRECT.c | 2 +- net/ipv4/netfilter/ipt_REJECT.c | 2 +- net/ipv4/netfilter/ipt_ULOG.c | 2 +- net/ipv4/netfilter/nf_nat_rule.c | 4 ++-- net/ipv6/netfilter/ip6t_LOG.c | 2 +- net/ipv6/netfilter/ip6t_REJECT.c | 2 +- net/netfilter/xt_CONNSECMARK.c | 2 +- net/netfilter/xt_CT.c | 2 +- net/netfilter/xt_DSCP.c | 2 +- net/netfilter/xt_HL.c | 4 ++-- net/netfilter/xt_LED.c | 2 +- net/netfilter/xt_NFLOG.c | 2 +- net/netfilter/xt_NFQUEUE.c | 2 +- net/netfilter/xt_RATEEST.c | 2 +- net/netfilter/xt_SECMARK.c | 2 +- net/netfilter/xt_TCPMSS.c | 4 ++-- net/netfilter/xt_TPROXY.c | 2 +- net/netfilter/xt_connmark.c | 2 +- 33 files changed, 37 insertions(+), 37 deletions(-) (limited to 'include/linux') diff --git a/include/linux/netfilter/x_tables.h b/include/linux/netfilter/x_tables.h index 33c1a62a099..1a65d45ee4f 100644 --- a/include/linux/netfilter/x_tables.h +++ b/include/linux/netfilter/x_tables.h @@ -342,8 +342,8 @@ struct xt_target { /* Called when user tries to insert an entry of this type: hook_mask is a bitmask of hooks from which it can be called. */ - /* Should return true or false. */ - bool (*checkentry)(const struct xt_tgchk_param *); + /* Should return true or false, or an error code (-Exxxx). */ + int (*checkentry)(const struct xt_tgchk_param *); /* Called when entry of this type deleted. */ void (*destroy)(const struct xt_tgdtor_param *); diff --git a/net/bridge/netfilter/ebt_arpreply.c b/net/bridge/netfilter/ebt_arpreply.c index f392e9d93f5..2491564e9e0 100644 --- a/net/bridge/netfilter/ebt_arpreply.c +++ b/net/bridge/netfilter/ebt_arpreply.c @@ -57,7 +57,7 @@ ebt_arpreply_tg(struct sk_buff *skb, const struct xt_target_param *par) return info->target; } -static bool ebt_arpreply_tg_check(const struct xt_tgchk_param *par) +static int ebt_arpreply_tg_check(const struct xt_tgchk_param *par) { const struct ebt_arpreply_info *info = par->targinfo; const struct ebt_entry *e = par->entryinfo; diff --git a/net/bridge/netfilter/ebt_dnat.c b/net/bridge/netfilter/ebt_dnat.c index 2bb40d728a3..5fddebea45c 100644 --- a/net/bridge/netfilter/ebt_dnat.c +++ b/net/bridge/netfilter/ebt_dnat.c @@ -26,7 +26,7 @@ ebt_dnat_tg(struct sk_buff *skb, const struct xt_target_param *par) return info->target; } -static bool ebt_dnat_tg_check(const struct xt_tgchk_param *par) +static int ebt_dnat_tg_check(const struct xt_tgchk_param *par) { const struct ebt_nat_info *info = par->targinfo; unsigned int hook_mask; diff --git a/net/bridge/netfilter/ebt_log.c b/net/bridge/netfilter/ebt_log.c index e873924ddb5..a0aeac6176e 100644 --- a/net/bridge/netfilter/ebt_log.c +++ b/net/bridge/netfilter/ebt_log.c @@ -24,7 +24,7 @@ static DEFINE_SPINLOCK(ebt_log_lock); -static bool ebt_log_tg_check(const struct xt_tgchk_param *par) +static int ebt_log_tg_check(const struct xt_tgchk_param *par) { struct ebt_log_info *info = par->targinfo; diff --git a/net/bridge/netfilter/ebt_mark.c b/net/bridge/netfilter/ebt_mark.c index 2b5ce533d6b..dd94dafa615 100644 --- a/net/bridge/netfilter/ebt_mark.c +++ b/net/bridge/netfilter/ebt_mark.c @@ -36,7 +36,7 @@ ebt_mark_tg(struct sk_buff *skb, const struct xt_target_param *par) return info->target | ~EBT_VERDICT_BITS; } -static bool ebt_mark_tg_check(const struct xt_tgchk_param *par) +static int ebt_mark_tg_check(const struct xt_tgchk_param *par) { const struct ebt_mark_t_info *info = par->targinfo; int tmp; diff --git a/net/bridge/netfilter/ebt_nflog.c b/net/bridge/netfilter/ebt_nflog.c index 40dbd248b9a..1f2b7bbdde7 100644 --- a/net/bridge/netfilter/ebt_nflog.c +++ b/net/bridge/netfilter/ebt_nflog.c @@ -35,7 +35,7 @@ ebt_nflog_tg(struct sk_buff *skb, const struct xt_target_param *par) return EBT_CONTINUE; } -static bool ebt_nflog_tg_check(const struct xt_tgchk_param *par) +static int ebt_nflog_tg_check(const struct xt_tgchk_param *par) { struct ebt_nflog_info *info = par->targinfo; diff --git a/net/bridge/netfilter/ebt_redirect.c b/net/bridge/netfilter/ebt_redirect.c index 9be8fbcd370..73c4d3ac6f2 100644 --- a/net/bridge/netfilter/ebt_redirect.c +++ b/net/bridge/netfilter/ebt_redirect.c @@ -32,7 +32,7 @@ ebt_redirect_tg(struct sk_buff *skb, const struct xt_target_param *par) return info->target; } -static bool ebt_redirect_tg_check(const struct xt_tgchk_param *par) +static int ebt_redirect_tg_check(const struct xt_tgchk_param *par) { const struct ebt_redirect_info *info = par->targinfo; unsigned int hook_mask; diff --git a/net/bridge/netfilter/ebt_snat.c b/net/bridge/netfilter/ebt_snat.c index 9c7b520765a..94bcecd90d7 100644 --- a/net/bridge/netfilter/ebt_snat.c +++ b/net/bridge/netfilter/ebt_snat.c @@ -42,7 +42,7 @@ out: return info->target | ~EBT_VERDICT_BITS; } -static bool ebt_snat_tg_check(const struct xt_tgchk_param *par) +static int ebt_snat_tg_check(const struct xt_tgchk_param *par) { const struct ebt_nat_info *info = par->targinfo; int tmp; diff --git a/net/bridge/netfilter/ebt_ulog.c b/net/bridge/netfilter/ebt_ulog.c index 789ea36f1db..f554bc2515d 100644 --- a/net/bridge/netfilter/ebt_ulog.c +++ b/net/bridge/netfilter/ebt_ulog.c @@ -249,7 +249,7 @@ ebt_ulog_tg(struct sk_buff *skb, const struct xt_target_param *par) return EBT_CONTINUE; } -static bool ebt_ulog_tg_check(const struct xt_tgchk_param *par) +static int ebt_ulog_tg_check(const struct xt_tgchk_param *par) { struct ebt_ulog_info *uloginfo = par->targinfo; diff --git a/net/ipv4/netfilter/arpt_mangle.c b/net/ipv4/netfilter/arpt_mangle.c index b0d5b1d0a76..4b51a027f30 100644 --- a/net/ipv4/netfilter/arpt_mangle.c +++ b/net/ipv4/netfilter/arpt_mangle.c @@ -54,7 +54,7 @@ target(struct sk_buff *skb, const struct xt_target_param *par) return mangle->target; } -static bool checkentry(const struct xt_tgchk_param *par) +static int checkentry(const struct xt_tgchk_param *par) { const struct arpt_mangle *mangle = par->targinfo; diff --git a/net/ipv4/netfilter/ipt_CLUSTERIP.c b/net/ipv4/netfilter/ipt_CLUSTERIP.c index fcaa0dc8e07..290a7b9b393 100644 --- a/net/ipv4/netfilter/ipt_CLUSTERIP.c +++ b/net/ipv4/netfilter/ipt_CLUSTERIP.c @@ -347,7 +347,7 @@ clusterip_tg(struct sk_buff *skb, const struct xt_target_param *par) return XT_CONTINUE; } -static bool clusterip_tg_check(const struct xt_tgchk_param *par) +static int clusterip_tg_check(const struct xt_tgchk_param *par) { struct ipt_clusterip_tgt_info *cipinfo = par->targinfo; const struct ipt_entry *e = par->entryinfo; diff --git a/net/ipv4/netfilter/ipt_ECN.c b/net/ipv4/netfilter/ipt_ECN.c index 01988752547..9d96500a415 100644 --- a/net/ipv4/netfilter/ipt_ECN.c +++ b/net/ipv4/netfilter/ipt_ECN.c @@ -93,7 +93,7 @@ ecn_tg(struct sk_buff *skb, const struct xt_target_param *par) return XT_CONTINUE; } -static bool ecn_tg_check(const struct xt_tgchk_param *par) +static int ecn_tg_check(const struct xt_tgchk_param *par) { const struct ipt_ECN_info *einfo = par->targinfo; const struct ipt_entry *e = par->entryinfo; diff --git a/net/ipv4/netfilter/ipt_LOG.c b/net/ipv4/netfilter/ipt_LOG.c index b3bf623fa22..c9ee5c40d1b 100644 --- a/net/ipv4/netfilter/ipt_LOG.c +++ b/net/ipv4/netfilter/ipt_LOG.c @@ -439,7 +439,7 @@ log_tg(struct sk_buff *skb, const struct xt_target_param *par) return XT_CONTINUE; } -static bool log_tg_check(const struct xt_tgchk_param *par) +static int log_tg_check(const struct xt_tgchk_param *par) { const struct ipt_log_info *loginfo = par->targinfo; diff --git a/net/ipv4/netfilter/ipt_MASQUERADE.c b/net/ipv4/netfilter/ipt_MASQUERADE.c index 5063ddac7c0..5a182f6de5d 100644 --- a/net/ipv4/netfilter/ipt_MASQUERADE.c +++ b/net/ipv4/netfilter/ipt_MASQUERADE.c @@ -28,7 +28,7 @@ MODULE_AUTHOR("Netfilter Core Team "); MODULE_DESCRIPTION("Xtables: automatic-address SNAT"); /* FIXME: Multiple targets. --RR */ -static bool masquerade_tg_check(const struct xt_tgchk_param *par) +static int masquerade_tg_check(const struct xt_tgchk_param *par) { const struct nf_nat_multi_range_compat *mr = par->targinfo; diff --git a/net/ipv4/netfilter/ipt_NETMAP.c b/net/ipv4/netfilter/ipt_NETMAP.c index 51ab01a0a95..cbfe5f7e082 100644 --- a/net/ipv4/netfilter/ipt_NETMAP.c +++ b/net/ipv4/netfilter/ipt_NETMAP.c @@ -22,7 +22,7 @@ MODULE_LICENSE("GPL"); MODULE_AUTHOR("Svenning Soerensen "); MODULE_DESCRIPTION("Xtables: 1:1 NAT mapping of IPv4 subnets"); -static bool netmap_tg_check(const struct xt_tgchk_param *par) +static int netmap_tg_check(const struct xt_tgchk_param *par) { const struct nf_nat_multi_range_compat *mr = par->targinfo; diff --git a/net/ipv4/netfilter/ipt_REDIRECT.c b/net/ipv4/netfilter/ipt_REDIRECT.c index 74f1f55fd61..f8daec20fb0 100644 --- a/net/ipv4/netfilter/ipt_REDIRECT.c +++ b/net/ipv4/netfilter/ipt_REDIRECT.c @@ -26,7 +26,7 @@ MODULE_AUTHOR("Netfilter Core Team "); MODULE_DESCRIPTION("Xtables: Connection redirection to localhost"); /* FIXME: Take multiple ranges --RR */ -static bool redirect_tg_check(const struct xt_tgchk_param *par) +static int redirect_tg_check(const struct xt_tgchk_param *par) { const struct nf_nat_multi_range_compat *mr = par->targinfo; diff --git a/net/ipv4/netfilter/ipt_REJECT.c b/net/ipv4/netfilter/ipt_REJECT.c index ff32252bad5..cf76f1bc3f1 100644 --- a/net/ipv4/netfilter/ipt_REJECT.c +++ b/net/ipv4/netfilter/ipt_REJECT.c @@ -174,7 +174,7 @@ reject_tg(struct sk_buff *skb, const struct xt_target_param *par) return NF_DROP; } -static bool reject_tg_check(const struct xt_tgchk_param *par) +static int reject_tg_check(const struct xt_tgchk_param *par) { const struct ipt_reject_info *rejinfo = par->targinfo; const struct ipt_entry *e = par->entryinfo; diff --git a/net/ipv4/netfilter/ipt_ULOG.c b/net/ipv4/netfilter/ipt_ULOG.c index d926201560d..7f73bbe2193 100644 --- a/net/ipv4/netfilter/ipt_ULOG.c +++ b/net/ipv4/netfilter/ipt_ULOG.c @@ -307,7 +307,7 @@ static void ipt_logfn(u_int8_t pf, ipt_ulog_packet(hooknum, skb, in, out, &loginfo, prefix); } -static bool ulog_tg_check(const struct xt_tgchk_param *par) +static int ulog_tg_check(const struct xt_tgchk_param *par) { const struct ipt_ulog_info *loginfo = par->targinfo; diff --git a/net/ipv4/netfilter/nf_nat_rule.c b/net/ipv4/netfilter/nf_nat_rule.c index 7d6345e416c..11722670873 100644 --- a/net/ipv4/netfilter/nf_nat_rule.c +++ b/net/ipv4/netfilter/nf_nat_rule.c @@ -74,7 +74,7 @@ ipt_dnat_target(struct sk_buff *skb, const struct xt_target_param *par) return nf_nat_setup_info(ct, &mr->range[0], IP_NAT_MANIP_DST); } -static bool ipt_snat_checkentry(const struct xt_tgchk_param *par) +static int ipt_snat_checkentry(const struct xt_tgchk_param *par) { const struct nf_nat_multi_range_compat *mr = par->targinfo; @@ -86,7 +86,7 @@ static bool ipt_snat_checkentry(const struct xt_tgchk_param *par) return true; } -static bool ipt_dnat_checkentry(const struct xt_tgchk_param *par) +static int ipt_dnat_checkentry(const struct xt_tgchk_param *par) { const struct nf_nat_multi_range_compat *mr = par->targinfo; diff --git a/net/ipv6/netfilter/ip6t_LOG.c b/net/ipv6/netfilter/ip6t_LOG.c index 5a79883220e..bcc3fc19374 100644 --- a/net/ipv6/netfilter/ip6t_LOG.c +++ b/net/ipv6/netfilter/ip6t_LOG.c @@ -451,7 +451,7 @@ log_tg6(struct sk_buff *skb, const struct xt_target_param *par) } -static bool log_tg6_check(const struct xt_tgchk_param *par) +static int log_tg6_check(const struct xt_tgchk_param *par) { const struct ip6t_log_info *loginfo = par->targinfo; diff --git a/net/ipv6/netfilter/ip6t_REJECT.c b/net/ipv6/netfilter/ip6t_REJECT.c index 45efb9f38fc..8d5141ece67 100644 --- a/net/ipv6/netfilter/ip6t_REJECT.c +++ b/net/ipv6/netfilter/ip6t_REJECT.c @@ -213,7 +213,7 @@ reject_tg6(struct sk_buff *skb, const struct xt_target_param *par) return NF_DROP; } -static bool reject_tg6_check(const struct xt_tgchk_param *par) +static int reject_tg6_check(const struct xt_tgchk_param *par) { const struct ip6t_reject_info *rejinfo = par->targinfo; const struct ip6t_entry *e = par->entryinfo; diff --git a/net/netfilter/xt_CONNSECMARK.c b/net/netfilter/xt_CONNSECMARK.c index 6812865488d..3f9d0f4f852 100644 --- a/net/netfilter/xt_CONNSECMARK.c +++ b/net/netfilter/xt_CONNSECMARK.c @@ -84,7 +84,7 @@ connsecmark_tg(struct sk_buff *skb, const struct xt_target_param *par) return XT_CONTINUE; } -static bool connsecmark_tg_check(const struct xt_tgchk_param *par) +static int connsecmark_tg_check(const struct xt_tgchk_param *par) { const struct xt_connsecmark_target_info *info = par->targinfo; diff --git a/net/netfilter/xt_CT.c b/net/netfilter/xt_CT.c index 6509e03f1e6..c1553bf06cf 100644 --- a/net/netfilter/xt_CT.c +++ b/net/netfilter/xt_CT.c @@ -53,7 +53,7 @@ static u8 xt_ct_find_proto(const struct xt_tgchk_param *par) return 0; } -static bool xt_ct_tg_check(const struct xt_tgchk_param *par) +static int xt_ct_tg_check(const struct xt_tgchk_param *par) { struct xt_ct_target_info *info = par->targinfo; struct nf_conntrack_tuple t; diff --git a/net/netfilter/xt_DSCP.c b/net/netfilter/xt_DSCP.c index bbf08a91c60..1fa7b67bf22 100644 --- a/net/netfilter/xt_DSCP.c +++ b/net/netfilter/xt_DSCP.c @@ -60,7 +60,7 @@ dscp_tg6(struct sk_buff *skb, const struct xt_target_param *par) return XT_CONTINUE; } -static bool dscp_tg_check(const struct xt_tgchk_param *par) +static int dscp_tg_check(const struct xt_tgchk_param *par) { const struct xt_DSCP_info *info = par->targinfo; diff --git a/net/netfilter/xt_HL.c b/net/netfilter/xt_HL.c index 7004ed2ffa4..15ba1610818 100644 --- a/net/netfilter/xt_HL.c +++ b/net/netfilter/xt_HL.c @@ -101,7 +101,7 @@ hl_tg6(struct sk_buff *skb, const struct xt_target_param *par) return XT_CONTINUE; } -static bool ttl_tg_check(const struct xt_tgchk_param *par) +static int ttl_tg_check(const struct xt_tgchk_param *par) { const struct ipt_TTL_info *info = par->targinfo; @@ -114,7 +114,7 @@ static bool ttl_tg_check(const struct xt_tgchk_param *par) return true; } -static bool hl_tg6_check(const struct xt_tgchk_param *par) +static int hl_tg6_check(const struct xt_tgchk_param *par) { const struct ip6t_HL_info *info = par->targinfo; diff --git a/net/netfilter/xt_LED.c b/net/netfilter/xt_LED.c index f511bea9464..1a3e3dd5a77 100644 --- a/net/netfilter/xt_LED.c +++ b/net/netfilter/xt_LED.c @@ -80,7 +80,7 @@ static void led_timeout_callback(unsigned long data) led_trigger_event(&ledinternal->netfilter_led_trigger, LED_OFF); } -static bool led_tg_check(const struct xt_tgchk_param *par) +static int led_tg_check(const struct xt_tgchk_param *par) { struct xt_led_info *ledinfo = par->targinfo; struct xt_led_info_internal *ledinternal; diff --git a/net/netfilter/xt_NFLOG.c b/net/netfilter/xt_NFLOG.c index a57c5cf018e..13e6c0002c8 100644 --- a/net/netfilter/xt_NFLOG.c +++ b/net/netfilter/xt_NFLOG.c @@ -37,7 +37,7 @@ nflog_tg(struct sk_buff *skb, const struct xt_target_param *par) return XT_CONTINUE; } -static bool nflog_tg_check(const struct xt_tgchk_param *par) +static int nflog_tg_check(const struct xt_tgchk_param *par) { const struct xt_nflog_info *info = par->targinfo; diff --git a/net/netfilter/xt_NFQUEUE.c b/net/netfilter/xt_NFQUEUE.c index 7cc0de63aa0..d435579a64c 100644 --- a/net/netfilter/xt_NFQUEUE.c +++ b/net/netfilter/xt_NFQUEUE.c @@ -81,7 +81,7 @@ nfqueue_tg_v1(struct sk_buff *skb, const struct xt_target_param *par) return NF_QUEUE_NR(queue); } -static bool nfqueue_tg_v1_check(const struct xt_tgchk_param *par) +static int nfqueue_tg_v1_check(const struct xt_tgchk_param *par) { const struct xt_NFQ_info_v1 *info = par->targinfo; u32 maxid; diff --git a/net/netfilter/xt_RATEEST.c b/net/netfilter/xt_RATEEST.c index 87ae97e5516..9743e50be8e 100644 --- a/net/netfilter/xt_RATEEST.c +++ b/net/netfilter/xt_RATEEST.c @@ -85,7 +85,7 @@ xt_rateest_tg(struct sk_buff *skb, const struct xt_target_param *par) return XT_CONTINUE; } -static bool xt_rateest_tg_checkentry(const struct xt_tgchk_param *par) +static int xt_rateest_tg_checkentry(const struct xt_tgchk_param *par) { struct xt_rateest_target_info *info = par->targinfo; struct xt_rateest *est; diff --git a/net/netfilter/xt_SECMARK.c b/net/netfilter/xt_SECMARK.c index 4855fd9d7c6..48f8e4f7ea8 100644 --- a/net/netfilter/xt_SECMARK.c +++ b/net/netfilter/xt_SECMARK.c @@ -80,7 +80,7 @@ static bool checkentry_selinux(struct xt_secmark_target_info *info) return true; } -static bool secmark_tg_check(const struct xt_tgchk_param *par) +static int secmark_tg_check(const struct xt_tgchk_param *par) { struct xt_secmark_target_info *info = par->targinfo; diff --git a/net/netfilter/xt_TCPMSS.c b/net/netfilter/xt_TCPMSS.c index 45161d9a9f2..70288dc3158 100644 --- a/net/netfilter/xt_TCPMSS.c +++ b/net/netfilter/xt_TCPMSS.c @@ -234,7 +234,7 @@ static inline bool find_syn_match(const struct xt_entry_match *m) return false; } -static bool tcpmss_tg4_check(const struct xt_tgchk_param *par) +static int tcpmss_tg4_check(const struct xt_tgchk_param *par) { const struct xt_tcpmss_info *info = par->targinfo; const struct ipt_entry *e = par->entryinfo; @@ -256,7 +256,7 @@ static bool tcpmss_tg4_check(const struct xt_tgchk_param *par) } #if defined(CONFIG_IP6_NF_IPTABLES) || defined(CONFIG_IP6_NF_IPTABLES_MODULE) -static bool tcpmss_tg6_check(const struct xt_tgchk_param *par) +static int tcpmss_tg6_check(const struct xt_tgchk_param *par) { const struct xt_tcpmss_info *info = par->targinfo; const struct ip6t_entry *e = par->entryinfo; diff --git a/net/netfilter/xt_TPROXY.c b/net/netfilter/xt_TPROXY.c index e9244fdc123..189df9af4de 100644 --- a/net/netfilter/xt_TPROXY.c +++ b/net/netfilter/xt_TPROXY.c @@ -59,7 +59,7 @@ tproxy_tg(struct sk_buff *skb, const struct xt_target_param *par) return NF_DROP; } -static bool tproxy_tg_check(const struct xt_tgchk_param *par) +static int tproxy_tg_check(const struct xt_tgchk_param *par) { const struct ipt_ip *i = par->entryinfo; diff --git a/net/netfilter/xt_connmark.c b/net/netfilter/xt_connmark.c index df7eaff874f..0e69427f8cd 100644 --- a/net/netfilter/xt_connmark.c +++ b/net/netfilter/xt_connmark.c @@ -74,7 +74,7 @@ connmark_tg(struct sk_buff *skb, const struct xt_target_param *par) return XT_CONTINUE; } -static bool connmark_tg_check(const struct xt_tgchk_param *par) +static int connmark_tg_check(const struct xt_tgchk_param *par) { if (nf_ct_l3proto_try_module_get(par->family) < 0) { pr_info("cannot load conntrack support for proto=%u\n", -- cgit v1.2.3 From df3345457a7a174dfb5872a070af80d456985038 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Wed, 24 Mar 2010 19:13:54 +0000 Subject: rps: add CONFIG_RPS RPS currently depends on SMP and SYSFS Adding a CONFIG_RPS makes sense in case this requirement changes in the future. This patch saves about 1500 bytes of kernel text in case SMP is on but SYSFS is off. Signed-off-by: Eric Dumazet Signed-off-by: David S. Miller --- include/linux/netdevice.h | 4 ++++ net/Kconfig | 5 +++++ net/core/dev.c | 29 +++++++++++++++++++---------- 3 files changed, 28 insertions(+), 10 deletions(-) (limited to 'include/linux') diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index c96c41e08e3..53c272f2a73 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -531,6 +531,7 @@ struct netdev_queue { unsigned long tx_dropped; } ____cacheline_aligned_in_smp; +#ifdef CONFIG_RPS /* * This structure holds an RPS map which can be of variable length. The * map is an array of CPUs. @@ -549,6 +550,7 @@ struct netdev_rx_queue { struct netdev_rx_queue *first; atomic_t count; } ____cacheline_aligned_in_smp; +#endif /* * This structure defines the management hooks for network devices. @@ -897,12 +899,14 @@ struct net_device { unsigned char broadcast[MAX_ADDR_LEN]; /* hw bcast add */ +#ifdef CONFIG_RPS struct kset *queues_kset; struct netdev_rx_queue *_rx; /* Number of RX queues allocated at alloc_netdev_mq() time */ unsigned int num_rx_queues; +#endif struct netdev_queue rx_queue; diff --git a/net/Kconfig b/net/Kconfig index 041c35edb76..68514644ce9 100644 --- a/net/Kconfig +++ b/net/Kconfig @@ -203,6 +203,11 @@ source "net/ieee802154/Kconfig" source "net/sched/Kconfig" source "net/dcb/Kconfig" +config RPS + boolean + depends on SMP && SYSFS + default y + menu "Network testing" config NET_PKTGEN diff --git a/net/core/dev.c b/net/core/dev.c index 5e3dc28cbf5..bcb3ed26af1 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -2177,7 +2177,7 @@ int weight_p __read_mostly = 64; /* old backlog weight */ DEFINE_PER_CPU(struct netif_rx_stats, netdev_rx_stat) = { 0, }; -#ifdef CONFIG_SMP +#ifdef CONFIG_RPS /* * get_rps_cpu is called from netif_receive_skb and returns the target * CPU from the RPS map of the receiving queue for a given skb. @@ -2325,7 +2325,7 @@ enqueue: /* Schedule NAPI for backlog device */ if (napi_schedule_prep(&queue->backlog)) { -#ifdef CONFIG_SMP +#ifdef CONFIG_RPS if (cpu != smp_processor_id()) { struct rps_remote_softirq_cpus *rcpus = &__get_cpu_var(rps_remote_softirq_cpus); @@ -2376,7 +2376,7 @@ int netif_rx(struct sk_buff *skb) if (!skb->tstamp.tv64) net_timestamp(skb); -#ifdef CONFIG_SMP +#ifdef CONFIG_RPS cpu = get_rps_cpu(skb->dev, skb); if (cpu < 0) cpu = smp_processor_id(); @@ -2750,7 +2750,7 @@ out: */ int netif_receive_skb(struct sk_buff *skb) { -#ifdef CONFIG_SMP +#ifdef CONFIG_RPS int cpu; cpu = get_rps_cpu(skb->dev, skb); @@ -3189,7 +3189,7 @@ void netif_napi_del(struct napi_struct *napi) } EXPORT_SYMBOL(netif_napi_del); -#ifdef CONFIG_SMP +#ifdef CONFIG_RPS /* * net_rps_action sends any pending IPI's for rps. This is only called from * softirq and interrupts must be enabled. @@ -3214,7 +3214,7 @@ static void net_rx_action(struct softirq_action *h) unsigned long time_limit = jiffies + 2; int budget = netdev_budget; void *have; -#ifdef CONFIG_SMP +#ifdef CONFIG_RPS int select; struct rps_remote_softirq_cpus *rcpus; #endif @@ -3280,7 +3280,7 @@ static void net_rx_action(struct softirq_action *h) netpoll_poll_unlock(have); } out: -#ifdef CONFIG_SMP +#ifdef CONFIG_RPS rcpus = &__get_cpu_var(rps_remote_softirq_cpus); select = rcpus->select; rcpus->select ^= 1; @@ -5277,6 +5277,7 @@ int register_netdevice(struct net_device *dev) dev->iflink = -1; +#ifdef CONFIG_RPS if (!dev->num_rx_queues) { /* * Allocate a single RX queue if driver never called @@ -5293,7 +5294,7 @@ int register_netdevice(struct net_device *dev) atomic_set(&dev->_rx->count, 1); dev->num_rx_queues = 1; } - +#endif /* Init, if this function is available */ if (dev->netdev_ops->ndo_init) { ret = dev->netdev_ops->ndo_init(dev); @@ -5653,11 +5654,13 @@ struct net_device *alloc_netdev_mq(int sizeof_priv, const char *name, void (*setup)(struct net_device *), unsigned int queue_count) { struct netdev_queue *tx; - struct netdev_rx_queue *rx; struct net_device *dev; size_t alloc_size; struct net_device *p; +#ifdef CONFIG_RPS + struct netdev_rx_queue *rx; int i; +#endif BUG_ON(strlen(name) >= sizeof(dev->name)); @@ -5683,6 +5686,7 @@ struct net_device *alloc_netdev_mq(int sizeof_priv, const char *name, goto free_p; } +#ifdef CONFIG_RPS rx = kcalloc(queue_count, sizeof(struct netdev_rx_queue), GFP_KERNEL); if (!rx) { printk(KERN_ERR "alloc_netdev: Unable to allocate " @@ -5698,6 +5702,7 @@ struct net_device *alloc_netdev_mq(int sizeof_priv, const char *name, */ for (i = 0; i < queue_count; i++) rx[i].first = rx; +#endif dev = PTR_ALIGN(p, NETDEV_ALIGN); dev->padded = (char *)dev - (char *)p; @@ -5713,8 +5718,10 @@ struct net_device *alloc_netdev_mq(int sizeof_priv, const char *name, dev->num_tx_queues = queue_count; dev->real_num_tx_queues = queue_count; +#ifdef CONFIG_RPS dev->_rx = rx; dev->num_rx_queues = queue_count; +#endif dev->gso_max_size = GSO_MAX_SIZE; @@ -5731,8 +5738,10 @@ struct net_device *alloc_netdev_mq(int sizeof_priv, const char *name, return dev; free_rx: +#ifdef CONFIG_RPS kfree(rx); free_tx: +#endif kfree(tx); free_p: kfree(p); @@ -6236,7 +6245,7 @@ static int __init net_dev_init(void) queue->completion_queue = NULL; INIT_LIST_HEAD(&queue->poll_list); -#ifdef CONFIG_SMP +#ifdef CONFIG_RPS queue->csd.func = trigger_softirq; queue->csd.info = queue; queue->csd.flags = 0; -- cgit v1.2.3 From 70596b612c04694806a31dd389bd796c035085fa Mon Sep 17 00:00:00 2001 From: Sjur Braendeland Date: Tue, 30 Mar 2010 13:56:19 +0000 Subject: net-caif: add CAIF protocol definitions Add CAIF definitions to existing header files. Files: if_arp.h, if_ether.h, socket.h. Types: ARPHRD_CAIF, ETH_P_CAIF, AF_CAIF, PF_CAIF, SOL_CAIF, N_CAIF Signed-off-by: Sjur Braendeland Signed-off-by: David S. Miller --- include/linux/if_arp.h | 1 + include/linux/if_ether.h | 1 + include/linux/socket.h | 5 ++++- 3 files changed, 6 insertions(+), 1 deletion(-) (limited to 'include/linux') diff --git a/include/linux/if_arp.h b/include/linux/if_arp.h index e80b7f88f7c..6d722f41ee7 100644 --- a/include/linux/if_arp.h +++ b/include/linux/if_arp.h @@ -90,6 +90,7 @@ #define ARPHRD_PHONET 820 /* PhoNet media type */ #define ARPHRD_PHONET_PIPE 821 /* PhoNet pipe header */ +#define ARPHRD_CAIF 822 /* CAIF media type */ #define ARPHRD_VOID 0xFFFF /* Void type, nothing is known */ #define ARPHRD_NONE 0xFFFE /* zero header length */ diff --git a/include/linux/if_ether.h b/include/linux/if_ether.h index 299b4121f91..bed7a4682b9 100644 --- a/include/linux/if_ether.h +++ b/include/linux/if_ether.h @@ -109,6 +109,7 @@ #define ETH_P_TRAILER 0x001C /* Trailer switch tagging */ #define ETH_P_PHONET 0x00F5 /* Nokia Phonet frames */ #define ETH_P_IEEE802154 0x00F6 /* IEEE802.15.4 frame */ +#define ETH_P_CAIF 0x00F7 /* ST-Ericsson CAIF protocol */ /* * This is an Ethernet frame header. diff --git a/include/linux/socket.h b/include/linux/socket.h index 7b3aae2052a..960659bd8f7 100644 --- a/include/linux/socket.h +++ b/include/linux/socket.h @@ -189,7 +189,8 @@ struct ucred { #define AF_ISDN 34 /* mISDN sockets */ #define AF_PHONET 35 /* Phonet sockets */ #define AF_IEEE802154 36 /* IEEE802154 sockets */ -#define AF_MAX 37 /* For now.. */ +#define AF_CAIF 37 /* CAIF sockets */ +#define AF_MAX 38 /* For now.. */ /* Protocol families, same as address families. */ #define PF_UNSPEC AF_UNSPEC @@ -229,6 +230,7 @@ struct ucred { #define PF_ISDN AF_ISDN #define PF_PHONET AF_PHONET #define PF_IEEE802154 AF_IEEE802154 +#define PF_CAIF AF_CAIF #define PF_MAX AF_MAX /* Maximum queue length specifiable by listen. */ @@ -300,6 +302,7 @@ struct ucred { #define SOL_PNPIPE 275 #define SOL_RDS 276 #define SOL_IUCV 277 +#define SOL_CAIF 278 /* IPX options */ #define IPX_TYPE 1 -- cgit v1.2.3 From f671c54207d8a47129f35a84569fdfda614d2439 Mon Sep 17 00:00:00 2001 From: Sjur Braendeland Date: Tue, 30 Mar 2010 13:56:20 +0000 Subject: net-caif: add CAIF socket and configuration headers Add CAIF types for Socket Address, Socket Options, and configuration parameters for the GPRS IP network interface. Signed-off-by: Sjur Braendeland Signed-off-by: David S. Miller --- include/linux/caif/caif_socket.h | 164 +++++++++++++++++++++++++++++++++++++++ include/linux/caif/if_caif.h | 34 ++++++++ 2 files changed, 198 insertions(+) create mode 100644 include/linux/caif/caif_socket.h create mode 100644 include/linux/caif/if_caif.h (limited to 'include/linux') diff --git a/include/linux/caif/caif_socket.h b/include/linux/caif/caif_socket.h new file mode 100644 index 00000000000..8e5c8444a3f --- /dev/null +++ b/include/linux/caif/caif_socket.h @@ -0,0 +1,164 @@ +/* linux/caif_socket.h + * CAIF Definitions for CAIF socket and network layer + * Copyright (C) ST-Ericsson AB 2010 + * Author: Sjur Brendeland/ sjur.brandeland@stericsson.com + * License terms: GNU General Public License (GPL) version 2 + */ + +#ifndef _LINUX_CAIF_SOCKET_H +#define _LINUX_CAIF_SOCKET_H + +#include + +#ifdef __KERNEL__ +#include +#else +#include +#endif + + +/** + * enum caif_link_selector - Physical Link Selection. + * @CAIF_LINK_HIGH_BANDW: Physical interface for high-bandwidth + * traffic. + * @CAIF_LINK_LOW_LATENCY: Physical interface for low-latency + * traffic. + * + * CAIF Link Layers can register their link properties. + * This enum is used for choosing between CAIF Link Layers when + * setting up CAIF Channels when multiple CAIF Link Layers exists. + */ +enum caif_link_selector { + CAIF_LINK_HIGH_BANDW, + CAIF_LINK_LOW_LATENCY +}; + +/** + * enum caif_channel_priority - CAIF channel priorities. + * + * @CAIF_PRIO_MIN: Min priority for a channel. + * @CAIF_PRIO_LOW: Low-priority channel. + * @CAIF_PRIO_NORMAL: Normal/default priority level. + * @CAIF_PRIO_HIGH: High priority level + * @CAIF_PRIO_MAX: Max priority for channel + * + * Priority can be set on CAIF Channels in order to + * prioritize between traffic on different CAIF Channels. + * These priority levels are recommended, but the priority value + * is not restricted to the values defined in this enum, any value + * between CAIF_PRIO_MIN and CAIF_PRIO_MAX could be used. + */ +enum caif_channel_priority { + CAIF_PRIO_MIN = 0x01, + CAIF_PRIO_LOW = 0x04, + CAIF_PRIO_NORMAL = 0x0f, + CAIF_PRIO_HIGH = 0x14, + CAIF_PRIO_MAX = 0x1F +}; + +/** + * enum caif_protocol_type - CAIF Channel type. + * @CAIFPROTO_AT: Classic AT channel. + * @CAIFPROTO_DATAGRAM: Datagram channel. + * @CAIFPROTO_DATAGRAM_LOOP: Datagram loopback channel, used for testing. + * @CAIFPROTO_UTIL: Utility (Psock) channel. + * @CAIFPROTO_RFM: Remote File Manager + * + * This enum defines the CAIF Channel type to be used. This defines + * the service to connect to on the modem. + */ +enum caif_protocol_type { + CAIFPROTO_AT, + CAIFPROTO_DATAGRAM, + CAIFPROTO_DATAGRAM_LOOP, + CAIFPROTO_UTIL, + CAIFPROTO_RFM, + _CAIFPROTO_MAX +}; +#define CAIFPROTO_MAX _CAIFPROTO_MAX + +/** + * enum caif_at_type - AT Service Endpoint + * @CAIF_ATTYPE_PLAIN: Connects to a plain vanilla AT channel. + */ +enum caif_at_type { + CAIF_ATTYPE_PLAIN = 2 +}; + +/** + * struct sockaddr_caif - the sockaddr structure for CAIF sockets. + * @u: Union of address data 'switched' by family. + * : + * @u.at: Applies when family = CAIFPROTO_AT. + * + * @u.at.type: Type of AT link to set up (enum caif_at_type). + * + * @u.util: Applies when family = CAIFPROTO_UTIL + * + * @u.util.service: Utility service name. + * + * @u.dgm: Applies when family = CAIFPROTO_DATAGRAM + * + * @u.dgm.connection_id: Datagram connection id. + * + * @u.dgm.nsapi: NSAPI of the PDP-Context. + * + * @u.rfm: Applies when family = CAIFPROTO_RFM + * + * @u.rfm.connection_id: Connection ID for RFM. + * + * @u.rfm.volume: Volume to mount. + * + * Description: + * This structure holds the connect parameters used for setting up a + * CAIF Channel. It defines the service to connect to on the modem. + */ +struct sockaddr_caif { + sa_family_t family; + union { + struct { + __u8 type; /* type: enum caif_at_type */ + } at; /* CAIFPROTO_AT */ + struct { + char service[16]; + } util; /* CAIFPROTO_UTIL */ + union { + __u32 connection_id; + __u8 nsapi; + } dgm; /* CAIFPROTO_DATAGRAM(_LOOP)*/ + struct { + __u32 connection_id; + char volume[16]; + } rfm; /* CAIFPROTO_RFM */ + } u; +}; + +/** + * enum caif_socket_opts - CAIF option values for getsockopt and setsockopt. + * + * @CAIFSO_LINK_SELECT: Selector used if multiple CAIF Link layers are + * available. Either a high bandwidth + * link can be selected (CAIF_LINK_HIGH_BANDW) or + * or a low latency link (CAIF_LINK_LOW_LATENCY). + * This option is of type __u32. + * Alternatively SO_BINDTODEVICE can be used. + * + * @CAIFSO_REQ_PARAM: Used to set the request parameters for a + * utility channel. (maximum 256 bytes). This + * option must be set before connecting. + * + * @CAIFSO_RSP_PARAM: Gets the response parameters for a utility + * channel. (maximum 256 bytes). This option + * is valid after a successful connect. + * + * + * This enum defines the CAIF Socket options to be used on a socket + * + */ +enum caif_socket_opts { + CAIFSO_LINK_SELECT = 127, + CAIFSO_REQ_PARAM = 128, + CAIFSO_RSP_PARAM = 129, +}; + +#endif /* _LINUX_CAIF_SOCKET_H */ diff --git a/include/linux/caif/if_caif.h b/include/linux/caif/if_caif.h new file mode 100644 index 00000000000..5e7eed4edf5 --- /dev/null +++ b/include/linux/caif/if_caif.h @@ -0,0 +1,34 @@ +/* + * Copyright (C) ST-Ericsson AB 2010 + * Author: Sjur Brendeland/ sjur.brandeland@stericsson.com + * License terms: GNU General Public License (GPL) version 2 + */ + +#ifndef IF_CAIF_H_ +#define IF_CAIF_H_ +#include +#include +#include + +/** + * enum ifla_caif - CAIF NetlinkRT parameters. + * @IFLA_CAIF_IPV4_CONNID: Connection ID for IPv4 PDP Context. + * The type of attribute is NLA_U32. + * @IFLA_CAIF_IPV6_CONNID: Connection ID for IPv6 PDP Context. + * The type of attribute is NLA_U32. + * @IFLA_CAIF_LOOPBACK: If different from zero, device is doing loopback + * The type of attribute is NLA_U8. + * + * When using RT Netlink to create, destroy or configure a CAIF IP interface, + * enum ifla_caif is used to specify the configuration attributes. + */ +enum ifla_caif { + __IFLA_CAIF_UNSPEC, + IFLA_CAIF_IPV4_CONNID, + IFLA_CAIF_IPV6_CONNID, + IFLA_CAIF_LOOPBACK, + __IFLA_CAIF_MAX +}; +#define IFLA_CAIF_MAX (__IFLA_CAIF_MAX-1) + +#endif /*IF_CAIF_H_*/ -- cgit v1.2.3 From 9b27105b4a44c54bf91ecd7d0315034ae75684f7 Mon Sep 17 00:00:00 2001 From: Sjur Braendeland Date: Tue, 30 Mar 2010 13:56:30 +0000 Subject: net-caif-driver: add CAIF serial driver (ldisc) Add CAIF Serial driver. This driver is implemented as a line discipline. caif_serial uses the following module parameters: ser_use_stx - specifies if STart of frame eXtension is in use. ser_loop - sets the interface in loopback mode. Signed-off-by: Sjur Braendeland Signed-off-by: David S. Miller --- drivers/net/Kconfig | 2 + drivers/net/Makefile | 1 + drivers/net/caif/Kconfig | 17 ++ drivers/net/caif/Makefile | 12 ++ drivers/net/caif/caif_serial.c | 441 +++++++++++++++++++++++++++++++++++++++++ include/linux/tty.h | 4 +- 6 files changed, 475 insertions(+), 2 deletions(-) create mode 100644 drivers/net/caif/Kconfig create mode 100644 drivers/net/caif/Makefile create mode 100644 drivers/net/caif/caif_serial.c (limited to 'include/linux') diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig index 4f1f9741c71..da6657c1166 100644 --- a/drivers/net/Kconfig +++ b/drivers/net/Kconfig @@ -2837,6 +2837,8 @@ source "drivers/ieee802154/Kconfig" source "drivers/s390/net/Kconfig" +source "drivers/net/caif/Kconfig" + config XEN_NETDEV_FRONTEND tristate "Xen network device frontend driver" depends on XEN diff --git a/drivers/net/Makefile b/drivers/net/Makefile index 478886234c2..ba06c1c0db8 100644 --- a/drivers/net/Makefile +++ b/drivers/net/Makefile @@ -290,5 +290,6 @@ obj-$(CONFIG_VIRTIO_NET) += virtio_net.o obj-$(CONFIG_SFC) += sfc/ obj-$(CONFIG_WIMAX) += wimax/ +obj-$(CONFIG_CAIF) += caif/ obj-$(CONFIG_OCTEON_MGMT_ETHERNET) += octeon/ diff --git a/drivers/net/caif/Kconfig b/drivers/net/caif/Kconfig new file mode 100644 index 00000000000..0b28e010769 --- /dev/null +++ b/drivers/net/caif/Kconfig @@ -0,0 +1,17 @@ +# +# CAIF physical drivers +# + +if CAIF + +comment "CAIF transport drivers" + +config CAIF_TTY + tristate "CAIF TTY transport driver" + default n + ---help--- + The CAIF TTY transport driver is a Line Discipline (ldisc) + identified as N_CAIF. When this ldisc is opened from user space + it will redirect the TTY's traffic into the CAIF stack. + +endif # CAIF diff --git a/drivers/net/caif/Makefile b/drivers/net/caif/Makefile new file mode 100644 index 00000000000..52b6d1f826f --- /dev/null +++ b/drivers/net/caif/Makefile @@ -0,0 +1,12 @@ +ifeq ($(CONFIG_CAIF_DEBUG),1) +CAIF_DBG_FLAGS := -DDEBUG +endif + +KBUILD_EXTRA_SYMBOLS=net/caif/Module.symvers + +ccflags-y := $(CAIF_FLAGS) $(CAIF_DBG_FLAGS) +clean-dirs:= .tmp_versions +clean-files:= Module.symvers modules.order *.cmd *~ \ + +# Serial interface +obj-$(CONFIG_CAIF_TTY) += caif_serial.o diff --git a/drivers/net/caif/caif_serial.c b/drivers/net/caif/caif_serial.c new file mode 100644 index 00000000000..3502f607117 --- /dev/null +++ b/drivers/net/caif/caif_serial.c @@ -0,0 +1,441 @@ +/* + * Copyright (C) ST-Ericsson AB 2010 + * Author: Sjur Brendeland / sjur.brandeland@stericsson.com + * License terms: GNU General Public License (GPL) version 2 + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Sjur Brendeland"); +MODULE_DESCRIPTION("CAIF serial device TTY line discipline"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS_LDISC(N_CAIF); + +#define SEND_QUEUE_LOW 10 +#define SEND_QUEUE_HIGH 100 +#define CAIF_SENDING 1 /* Bit 1 = 0x02*/ +#define CAIF_FLOW_OFF_SENT 4 /* Bit 4 = 0x10 */ +#define MAX_WRITE_CHUNK 4096 +#define ON 1 +#define OFF 0 +#define CAIF_MAX_MTU 4096 + +/*This list is protected by the rtnl lock. */ +static LIST_HEAD(ser_list); + +static int ser_loop; +module_param(ser_loop, bool, S_IRUGO); +MODULE_PARM_DESC(ser_loop, "Run in simulated loopback mode."); + +static int ser_use_stx = 1; +module_param(ser_use_stx, bool, S_IRUGO); +MODULE_PARM_DESC(ser_use_stx, "STX enabled or not."); + +static int ser_use_fcs = 1; + +module_param(ser_use_fcs, bool, S_IRUGO); +MODULE_PARM_DESC(ser_use_fcs, "FCS enabled or not."); + +static int ser_write_chunk = MAX_WRITE_CHUNK; +module_param(ser_write_chunk, int, S_IRUGO); + +MODULE_PARM_DESC(ser_write_chunk, "Maximum size of data written to UART."); + +static struct dentry *debugfsdir; + +static int caif_net_open(struct net_device *dev); +static int caif_net_close(struct net_device *dev); + +struct ser_device { + struct caif_dev_common common; + struct list_head node; + struct net_device *dev; + struct sk_buff_head head; + struct tty_struct *tty; + bool tx_started; + unsigned long state; + char *tty_name; +#ifdef CONFIG_DEBUG_FS + struct dentry *debugfs_tty_dir; + struct debugfs_blob_wrapper tx_blob; + struct debugfs_blob_wrapper rx_blob; + u8 rx_data[128]; + u8 tx_data[128]; + u8 tty_status; + +#endif +}; + +static void caifdev_setup(struct net_device *dev); +static void ldisc_tx_wakeup(struct tty_struct *tty); +#ifdef CONFIG_DEBUG_FS +static inline void update_tty_status(struct ser_device *ser) +{ + ser->tty_status = + ser->tty->stopped << 5 | + ser->tty->hw_stopped << 4 | + ser->tty->flow_stopped << 3 | + ser->tty->packet << 2 | + ser->tty->low_latency << 1 | + ser->tty->warned; +} +static inline void debugfs_init(struct ser_device *ser, struct tty_struct *tty) +{ + ser->debugfs_tty_dir = + debugfs_create_dir(tty->name, debugfsdir); + if (!IS_ERR(ser->debugfs_tty_dir)) { + debugfs_create_blob("last_tx_msg", S_IRUSR, + ser->debugfs_tty_dir, + &ser->tx_blob); + + debugfs_create_blob("last_rx_msg", S_IRUSR, + ser->debugfs_tty_dir, + &ser->rx_blob); + + debugfs_create_x32("ser_state", S_IRUSR, + ser->debugfs_tty_dir, + (u32 *)&ser->state); + + debugfs_create_x8("tty_status", S_IRUSR, + ser->debugfs_tty_dir, + &ser->tty_status); + + } + ser->tx_blob.data = ser->tx_data; + ser->tx_blob.size = 0; + ser->rx_blob.data = ser->rx_data; + ser->rx_blob.size = 0; +} + +static inline void debugfs_deinit(struct ser_device *ser) +{ + debugfs_remove_recursive(ser->debugfs_tty_dir); +} + +static inline void debugfs_rx(struct ser_device *ser, const u8 *data, int size) +{ + if (size > sizeof(ser->rx_data)) + size = sizeof(ser->rx_data); + memcpy(ser->rx_data, data, size); + ser->rx_blob.data = ser->rx_data; + ser->rx_blob.size = size; +} + +static inline void debugfs_tx(struct ser_device *ser, const u8 *data, int size) +{ + if (size > sizeof(ser->tx_data)) + size = sizeof(ser->tx_data); + memcpy(ser->tx_data, data, size); + ser->tx_blob.data = ser->tx_data; + ser->tx_blob.size = size; +} +#else +static inline void debugfs_init(struct ser_device *ser, struct tty_struct *tty) +{ +} + +static inline void debugfs_deinit(struct ser_device *ser) +{ +} + +static inline void update_tty_status(struct ser_device *ser) +{ +} + +static inline void debugfs_rx(struct ser_device *ser, const u8 *data, int size) +{ +} + +static inline void debugfs_tx(struct ser_device *ser, const u8 *data, int size) +{ +} + +#endif + +static void ldisc_receive(struct tty_struct *tty, const u8 *data, + char *flags, int count) +{ + struct sk_buff *skb = NULL; + struct ser_device *ser; + int ret; + u8 *p; + ser = tty->disc_data; + + /* + * NOTE: flags may contain information about break or overrun. + * This is not yet handled. + */ + + + /* + * Workaround for garbage at start of transmission, + * only enable if STX handling is not enabled. + */ + if (!ser->common.use_stx && !ser->tx_started) { + dev_info(&ser->dev->dev, + "Bytes received before initial transmission -" + "bytes discarded.\n"); + return; + } + + BUG_ON(ser->dev == NULL); + + /* Get a suitable caif packet and copy in data. */ + skb = netdev_alloc_skb(ser->dev, count+1); + BUG_ON(skb == NULL); + p = skb_put(skb, count); + memcpy(p, data, count); + + skb->protocol = htons(ETH_P_CAIF); + skb_reset_mac_header(skb); + skb->dev = ser->dev; + debugfs_rx(ser, data, count); + /* Push received packet up the stack. */ + ret = netif_rx_ni(skb); + if (!ret) { + ser->dev->stats.rx_packets++; + ser->dev->stats.rx_bytes += count; + } else + ++ser->dev->stats.rx_dropped; + update_tty_status(ser); +} + +static int handle_tx(struct ser_device *ser) +{ + struct tty_struct *tty; + struct sk_buff *skb; + int tty_wr, len, room; + tty = ser->tty; + ser->tx_started = true; + + /* Enter critical section */ + if (test_and_set_bit(CAIF_SENDING, &ser->state)) + return 0; + + /* skb_peek is safe because handle_tx is called after skb_queue_tail */ + while ((skb = skb_peek(&ser->head)) != NULL) { + + /* Make sure you don't write too much */ + len = skb->len; + room = tty_write_room(tty); + if (!room) + break; + if (room > ser_write_chunk) + room = ser_write_chunk; + if (len > room) + len = room; + + /* Write to tty or loopback */ + if (!ser_loop) { + tty_wr = tty->ops->write(tty, skb->data, len); + update_tty_status(ser); + } else { + tty_wr = len; + ldisc_receive(tty, skb->data, NULL, len); + } + ser->dev->stats.tx_packets++; + ser->dev->stats.tx_bytes += tty_wr; + + /* Error on TTY ?! */ + if (tty_wr < 0) + goto error; + /* Reduce buffer written, and discard if empty */ + skb_pull(skb, tty_wr); + if (skb->len == 0) { + struct sk_buff *tmp = skb_dequeue(&ser->head); + BUG_ON(tmp != skb); + if (in_interrupt()) + dev_kfree_skb_irq(skb); + else + kfree_skb(skb); + } + } + /* Send flow off if queue is empty */ + if (ser->head.qlen <= SEND_QUEUE_LOW && + test_and_clear_bit(CAIF_FLOW_OFF_SENT, &ser->state) && + ser->common.flowctrl != NULL) + ser->common.flowctrl(ser->dev, ON); + clear_bit(CAIF_SENDING, &ser->state); + return 0; +error: + clear_bit(CAIF_SENDING, &ser->state); + return tty_wr; +} + +static int caif_xmit(struct sk_buff *skb, struct net_device *dev) +{ + struct ser_device *ser; + BUG_ON(dev == NULL); + ser = netdev_priv(dev); + + /* Send flow off once, on high water mark */ + if (ser->head.qlen > SEND_QUEUE_HIGH && + !test_and_set_bit(CAIF_FLOW_OFF_SENT, &ser->state) && + ser->common.flowctrl != NULL) + + ser->common.flowctrl(ser->dev, OFF); + + skb_queue_tail(&ser->head, skb); + return handle_tx(ser); +} + + +static void ldisc_tx_wakeup(struct tty_struct *tty) +{ + struct ser_device *ser; + ser = tty->disc_data; + BUG_ON(ser == NULL); + BUG_ON(ser->tty != tty); + handle_tx(ser); +} + + +static int ldisc_open(struct tty_struct *tty) +{ + struct ser_device *ser; + struct net_device *dev; + char name[64]; + int result; + + sprintf(name, "cf%s", tty->name); + dev = alloc_netdev(sizeof(*ser), name, caifdev_setup); + ser = netdev_priv(dev); + ser->tty = tty; + ser->dev = dev; + debugfs_init(ser, tty); + tty->receive_room = N_TTY_BUF_SIZE; + tty->disc_data = ser; + set_bit(TTY_DO_WRITE_WAKEUP, &tty->flags); + rtnl_lock(); + result = register_netdevice(dev); + if (result) { + rtnl_unlock(); + free_netdev(dev); + return -ENODEV; + } + + list_add(&ser->node, &ser_list); + rtnl_unlock(); + netif_stop_queue(dev); + update_tty_status(ser); + return 0; +} + +static void ldisc_close(struct tty_struct *tty) +{ + struct ser_device *ser = tty->disc_data; + /* Remove may be called inside or outside of rtnl_lock */ + int islocked = rtnl_is_locked(); + if (!islocked) + rtnl_lock(); + /* device is freed automagically by net-sysfs */ + dev_close(ser->dev); + unregister_netdevice(ser->dev); + list_del(&ser->node); + debugfs_deinit(ser); + if (!islocked) + rtnl_unlock(); +} + +/* The line discipline structure. */ +static struct tty_ldisc_ops caif_ldisc = { + .owner = THIS_MODULE, + .magic = TTY_LDISC_MAGIC, + .name = "n_caif", + .open = ldisc_open, + .close = ldisc_close, + .receive_buf = ldisc_receive, + .write_wakeup = ldisc_tx_wakeup +}; + +static int register_ldisc(void) +{ + int result; + result = tty_register_ldisc(N_CAIF, &caif_ldisc); + if (result < 0) { + pr_err("cannot register CAIF ldisc=%d err=%d\n", N_CAIF, + result); + return result; + } + return result; +} +static const struct net_device_ops netdev_ops = { + .ndo_open = caif_net_open, + .ndo_stop = caif_net_close, + .ndo_start_xmit = caif_xmit +}; + +static void caifdev_setup(struct net_device *dev) +{ + struct ser_device *serdev = netdev_priv(dev); + dev->features = 0; + dev->netdev_ops = &netdev_ops; + dev->type = ARPHRD_CAIF; + dev->flags = IFF_POINTOPOINT | IFF_NOARP; + dev->mtu = CAIF_MAX_MTU; + dev->hard_header_len = CAIF_NEEDED_HEADROOM; + dev->tx_queue_len = 0; + dev->destructor = free_netdev; + skb_queue_head_init(&serdev->head); + serdev->common.link_select = CAIF_LINK_LOW_LATENCY; + serdev->common.use_frag = true; + serdev->common.use_stx = ser_use_stx; + serdev->common.use_fcs = ser_use_fcs; + serdev->dev = dev; +} + + +static int caif_net_open(struct net_device *dev) +{ + struct ser_device *ser; + ser = netdev_priv(dev); + netif_wake_queue(dev); + return 0; +} + +static int caif_net_close(struct net_device *dev) +{ + netif_stop_queue(dev); + return 0; +} + +static int __init caif_ser_init(void) +{ + int ret; + ret = register_ldisc(); + debugfsdir = debugfs_create_dir("caif_serial", NULL); + return ret; +} + +static void __exit caif_ser_exit(void) +{ + struct ser_device *ser = NULL; + struct list_head *node; + struct list_head *_tmp; + list_for_each_safe(node, _tmp, &ser_list) { + ser = list_entry(node, struct ser_device, node); + dev_close(ser->dev); + unregister_netdevice(ser->dev); + list_del(node); + } + tty_unregister_ldisc(N_CAIF); + debugfs_remove_recursive(debugfsdir); +} + +module_init(caif_ser_init); +module_exit(caif_ser_exit); diff --git a/include/linux/tty.h b/include/linux/tty.h index 568369a8630..71c7e9c96b2 100644 --- a/include/linux/tty.h +++ b/include/linux/tty.h @@ -23,7 +23,7 @@ */ #define NR_UNIX98_PTY_DEFAULT 4096 /* Default maximum for Unix98 ptys */ #define NR_UNIX98_PTY_MAX (1 << MINORBITS) /* Absolute limit */ -#define NR_LDISCS 20 +#define NR_LDISCS 21 /* line disciplines */ #define N_TTY 0 @@ -46,8 +46,8 @@ #define N_GIGASET_M101 16 /* Siemens Gigaset M101 serial DECT adapter */ #define N_SLCAN 17 /* Serial / USB serial CAN Adaptors */ #define N_PPS 18 /* Pulse per Second */ - #define N_V253 19 /* Codec control over voice modem */ +#define N_CAIF 20 /* CAIF protocol for talking to modems */ /* * This character is the same as _POSIX_VDISABLE: it cannot be used as -- cgit v1.2.3 From b00fabb4020d17bda4bea59507e09fadf573088d Mon Sep 17 00:00:00 2001 From: stephen hemminger Date: Mon, 29 Mar 2010 14:47:27 +0000 Subject: netdev: ethtool RXHASH flag This adds ethtool and device feature flag to allow control of receive hashing offload. Signed-off-by: Stephen Hemminger Acked-by: Jeff Garzik Signed-off-by: David S. Miller --- include/linux/ethtool.h | 1 + include/linux/netdevice.h | 1 + net/core/ethtool.c | 7 ++++++- 3 files changed, 8 insertions(+), 1 deletion(-) (limited to 'include/linux') diff --git a/include/linux/ethtool.h b/include/linux/ethtool.h index b33f316bb92..b391969a0dd 100644 --- a/include/linux/ethtool.h +++ b/include/linux/ethtool.h @@ -310,6 +310,7 @@ struct ethtool_perm_addr { enum ethtool_flags { ETH_FLAG_LRO = (1 << 15), /* LRO is enabled */ ETH_FLAG_NTUPLE = (1 << 27), /* N-tuple filters enabled */ + ETH_FLAG_RXHASH = (1 << 28), }; /* The following structures are for supporting RX network flow diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index 53c272f2a73..b5670ab5107 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -785,6 +785,7 @@ struct net_device { #define NETIF_F_SCTP_CSUM (1 << 25) /* SCTP checksum offload */ #define NETIF_F_FCOE_MTU (1 << 26) /* Supports max FCoE MTU, 2158 bytes*/ #define NETIF_F_NTUPLE (1 << 27) /* N-tuple filters supported */ +#define NETIF_F_RXHASH (1 << 28) /* Receive hashing offload */ /* Segmentation offload features */ #define NETIF_F_GSO_SHIFT 16 diff --git a/net/core/ethtool.c b/net/core/ethtool.c index f4cb6b6299d..73c81edde8d 100644 --- a/net/core/ethtool.c +++ b/net/core/ethtool.c @@ -121,7 +121,7 @@ int ethtool_op_set_ufo(struct net_device *dev, u32 data) * NETIF_F_xxx values in include/linux/netdevice.h */ static const u32 flags_dup_features = - (ETH_FLAG_LRO | ETH_FLAG_NTUPLE); + (ETH_FLAG_LRO | ETH_FLAG_NTUPLE | ETH_FLAG_RXHASH); u32 ethtool_op_get_flags(struct net_device *dev) { @@ -152,6 +152,11 @@ int ethtool_op_set_flags(struct net_device *dev, u32 data) features &= ~NETIF_F_NTUPLE; } + if (data & ETH_FLAG_RXHASH) + features |= NETIF_F_RXHASH; + else + features &= ~NETIF_F_RXHASH; + dev->features = features; return 0; } -- cgit v1.2.3 From e446630c960946b5c1762e4eadb618becef599e7 Mon Sep 17 00:00:00 2001 From: Marc Zyngier Date: Mon, 29 Mar 2010 08:57:56 +0000 Subject: Add hotplug support to mcp251x driver Chip model can now be selected directly by matching the modalias name (instead of filling the .model field in platform_data), and allows the module to be auto-loaded. Previous behaviour is of course still supported. Convert the two in-tree users to this feature (icontrol & zeus). Tested on an Zeus platform (mcp2515). Signed-off-by: Marc Zyngier Acked-by: Christian Pellegrin Cc: Edwin Peer Acked-by: Wolfgang Grandegger Signed-off-by: David S. Miller --- arch/arm/mach-pxa/icontrol.c | 9 ++++----- arch/arm/mach-pxa/zeus.c | 4 +--- drivers/net/can/mcp251x.c | 14 ++++++++++++++ include/linux/can/platform/mcp251x.h | 4 ++-- 4 files changed, 21 insertions(+), 10 deletions(-) (limited to 'include/linux') diff --git a/arch/arm/mach-pxa/icontrol.c b/arch/arm/mach-pxa/icontrol.c index 771137fc1a8..5ccb0ceff6c 100644 --- a/arch/arm/mach-pxa/icontrol.c +++ b/arch/arm/mach-pxa/icontrol.c @@ -73,7 +73,6 @@ static struct pxa2xx_spi_chip mcp251x_chip_info4 = { static struct mcp251x_platform_data mcp251x_info = { .oscillator_frequency = 16E6, - .model = CAN_MCP251X_MCP2515, .board_specific_setup = NULL, .power_enable = NULL, .transceiver_enable = NULL @@ -81,7 +80,7 @@ static struct mcp251x_platform_data mcp251x_info = { static struct spi_board_info mcp251x_board_info[] = { { - .modalias = "mcp251x", + .modalias = "mcp2515", .max_speed_hz = 6500000, .bus_num = 3, .chip_select = 0, @@ -90,7 +89,7 @@ static struct spi_board_info mcp251x_board_info[] = { .irq = gpio_to_irq(ICONTROL_MCP251x_nIRQ1) }, { - .modalias = "mcp251x", + .modalias = "mcp2515", .max_speed_hz = 6500000, .bus_num = 3, .chip_select = 1, @@ -99,7 +98,7 @@ static struct spi_board_info mcp251x_board_info[] = { .irq = gpio_to_irq(ICONTROL_MCP251x_nIRQ2) }, { - .modalias = "mcp251x", + .modalias = "mcp2515", .max_speed_hz = 6500000, .bus_num = 4, .chip_select = 0, @@ -108,7 +107,7 @@ static struct spi_board_info mcp251x_board_info[] = { .irq = gpio_to_irq(ICONTROL_MCP251x_nIRQ3) }, { - .modalias = "mcp251x", + .modalias = "mcp2515", .max_speed_hz = 6500000, .bus_num = 4, .chip_select = 1, diff --git a/arch/arm/mach-pxa/zeus.c b/arch/arm/mach-pxa/zeus.c index 39896d88358..dbd25696637 100644 --- a/arch/arm/mach-pxa/zeus.c +++ b/arch/arm/mach-pxa/zeus.c @@ -414,15 +414,13 @@ static int zeus_mcp2515_transceiver_enable(int enable) static struct mcp251x_platform_data zeus_mcp2515_pdata = { .oscillator_frequency = 16*1000*1000, - .model = CAN_MCP251X_MCP2515, .board_specific_setup = zeus_mcp2515_setup, - .transceiver_enable = zeus_mcp2515_transceiver_enable, .power_enable = zeus_mcp2515_transceiver_enable, }; static struct spi_board_info zeus_spi_board_info[] = { [0] = { - .modalias = "mcp251x", + .modalias = "mcp2515", .platform_data = &zeus_mcp2515_pdata, .irq = gpio_to_irq(ZEUS_CAN_GPIO), .max_speed_hz = 1*1000*1000, diff --git a/drivers/net/can/mcp251x.c b/drivers/net/can/mcp251x.c index f8cc168ec76..f521579f5ad 100644 --- a/drivers/net/can/mcp251x.c +++ b/drivers/net/can/mcp251x.c @@ -922,12 +922,16 @@ static int __devinit mcp251x_can_probe(struct spi_device *spi) struct net_device *net; struct mcp251x_priv *priv; struct mcp251x_platform_data *pdata = spi->dev.platform_data; + int model = spi_get_device_id(spi)->driver_data; int ret = -ENODEV; if (!pdata) /* Platform data is required for osc freq */ goto error_out; + if (model) + pdata->model = model; + /* Allocate can/net device */ net = alloc_candev(sizeof(struct mcp251x_priv), TX_ECHO_SKB_MAX); if (!net) { @@ -1117,6 +1121,15 @@ static int mcp251x_can_resume(struct spi_device *spi) #define mcp251x_can_resume NULL #endif +static struct spi_device_id mcp251x_id_table[] = { + { "mcp251x", 0 /* Use pdata.model */ }, + { "mcp2510", CAN_MCP251X_MCP2510 }, + { "mcp2515", CAN_MCP251X_MCP2515 }, + { }, +}; + +MODULE_DEVICE_TABLE(spi, mcp251x_id_table); + static struct spi_driver mcp251x_can_driver = { .driver = { .name = DEVICE_NAME, @@ -1124,6 +1137,7 @@ static struct spi_driver mcp251x_can_driver = { .owner = THIS_MODULE, }, + .id_table = mcp251x_id_table, .probe = mcp251x_can_probe, .remove = __devexit_p(mcp251x_can_remove), .suspend = mcp251x_can_suspend, diff --git a/include/linux/can/platform/mcp251x.h b/include/linux/can/platform/mcp251x.h index 1448177d86d..dba28268e65 100644 --- a/include/linux/can/platform/mcp251x.h +++ b/include/linux/can/platform/mcp251x.h @@ -26,8 +26,8 @@ struct mcp251x_platform_data { unsigned long oscillator_frequency; int model; -#define CAN_MCP251X_MCP2510 0 -#define CAN_MCP251X_MCP2515 1 +#define CAN_MCP251X_MCP2510 0x2510 +#define CAN_MCP251X_MCP2515 0x2515 int (*board_specific_setup)(struct spi_device *spi); int (*transceiver_enable)(int enable); int (*power_enable) (int enable); -- cgit v1.2.3 From 8626d3b4328061f5b82b11ae1d6918a0c3602f42 Mon Sep 17 00:00:00 2001 From: David Woodhouse Date: Fri, 2 Apr 2010 01:05:27 +0000 Subject: phylib: Support phy module autoloading We don't use the normal hotplug mechanism because it doesn't work. It will load the module some time after the device appears, but that's not good enough for us -- we need the driver loaded _immediately_ because otherwise the NIC driver may just abort and then the phy 'device' goes away. [bwh: s/phy/mdio/ in module alias, kerneldoc for struct mdio_device_id] Signed-off-by: David Woodhouse Signed-off-by: Ben Hutchings Acked-by: Andy Fleming Signed-off-by: David S. Miller --- drivers/net/phy/phy_device.c | 12 ++++++++++++ include/linux/mod_devicetable.h | 26 ++++++++++++++++++++++++++ include/linux/phy.h | 1 + scripts/mod/file2alias.c | 26 ++++++++++++++++++++++++++ 4 files changed, 65 insertions(+) (limited to 'include/linux') diff --git a/drivers/net/phy/phy_device.c b/drivers/net/phy/phy_device.c index db1794546c5..1a99bb24410 100644 --- a/drivers/net/phy/phy_device.c +++ b/drivers/net/phy/phy_device.c @@ -149,6 +149,7 @@ EXPORT_SYMBOL(phy_scan_fixups); struct phy_device* phy_device_create(struct mii_bus *bus, int addr, int phy_id) { struct phy_device *dev; + /* We allocate the device, and initialize the * default values */ dev = kzalloc(sizeof(*dev), GFP_KERNEL); @@ -179,6 +180,17 @@ struct phy_device* phy_device_create(struct mii_bus *bus, int addr, int phy_id) mutex_init(&dev->lock); INIT_DELAYED_WORK(&dev->state_queue, phy_state_machine); + /* Request the appropriate module unconditionally; don't + bother trying to do so only if it isn't already loaded, + because that gets complicated. A hotplug event would have + done an unconditional modprobe anyway. + We don't do normal hotplug because it won't work for MDIO + -- because it relies on the device staying around for long + enough for the driver to get loaded. With MDIO, the NIC + driver will get bored and give up as soon as it finds that + there's no driver _already_ loaded. */ + request_module(MDIO_MODULE_PREFIX MDIO_ID_FMT, MDIO_ID_ARGS(phy_id)); + return dev; } EXPORT_SYMBOL(phy_device_create); diff --git a/include/linux/mod_devicetable.h b/include/linux/mod_devicetable.h index f58e9d836f3..55f1f9c9506 100644 --- a/include/linux/mod_devicetable.h +++ b/include/linux/mod_devicetable.h @@ -474,4 +474,30 @@ struct platform_device_id { __attribute__((aligned(sizeof(kernel_ulong_t)))); }; +#define MDIO_MODULE_PREFIX "mdio:" + +#define MDIO_ID_FMT "%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d" +#define MDIO_ID_ARGS(_id) \ + (_id)>>31, ((_id)>>30) & 1, ((_id)>>29) & 1, ((_id)>>28) & 1, \ + ((_id)>>27) & 1, ((_id)>>26) & 1, ((_id)>>25) & 1, ((_id)>>24) & 1, \ + ((_id)>>23) & 1, ((_id)>>22) & 1, ((_id)>>21) & 1, ((_id)>>20) & 1, \ + ((_id)>>19) & 1, ((_id)>>18) & 1, ((_id)>>17) & 1, ((_id)>>16) & 1, \ + ((_id)>>15) & 1, ((_id)>>14) & 1, ((_id)>>13) & 1, ((_id)>>12) & 1, \ + ((_id)>>11) & 1, ((_id)>>10) & 1, ((_id)>>9) & 1, ((_id)>>8) & 1, \ + ((_id)>>7) & 1, ((_id)>>6) & 1, ((_id)>>5) & 1, ((_id)>>4) & 1, \ + ((_id)>>3) & 1, ((_id)>>2) & 1, ((_id)>>1) & 1, (_id) & 1 + +/** + * struct mdio_device_id - identifies PHY devices on an MDIO/MII bus + * @phy_id: The result of + * (mdio_read(&MII_PHYSID1) << 16 | mdio_read(&PHYSID2)) & @phy_id_mask + * for this PHY type + * @phy_id_mask: Defines the significant bits of @phy_id. A value of 0 + * is used to terminate an array of struct mdio_device_id. + */ +struct mdio_device_id { + __u32 phy_id; + __u32 phy_id_mask; +}; + #endif /* LINUX_MOD_DEVICETABLE_H */ diff --git a/include/linux/phy.h b/include/linux/phy.h index d9bce4b526b..987e111f7b1 100644 --- a/include/linux/phy.h +++ b/include/linux/phy.h @@ -24,6 +24,7 @@ #include #include #include +#include #include diff --git a/scripts/mod/file2alias.c b/scripts/mod/file2alias.c index 220213e603d..36a60a85317 100644 --- a/scripts/mod/file2alias.c +++ b/scripts/mod/file2alias.c @@ -796,6 +796,28 @@ static int do_platform_entry(const char *filename, return 1; } +static int do_mdio_entry(const char *filename, + struct mdio_device_id *id, char *alias) +{ + int i; + + alias += sprintf(alias, MDIO_MODULE_PREFIX); + + for (i = 0; i < 32; i++) { + if (!((id->phy_id_mask >> (31-i)) & 1)) + *(alias++) = '?'; + else if ((id->phy_id >> (31-i)) & 1) + *(alias++) = '1'; + else + *(alias++) = '0'; + } + + /* Terminate the string */ + *alias = 0; + + return 1; +} + /* Ignore any prefix, eg. some architectures prepend _ */ static inline int sym_is(const char *symbol, const char *name) { @@ -943,6 +965,10 @@ void handle_moddevtable(struct module *mod, struct elf_info *info, do_table(symval, sym->st_size, sizeof(struct platform_device_id), "platform", do_platform_entry, mod); + else if (sym_is(symname, "__mod_mdio_device_table")) + do_table(symval, sym->st_size, + sizeof(struct mdio_device_id), "mdio", + do_mdio_entry, mod); free(zeros); } -- cgit v1.2.3 From a748ee2426817a95b1f03012d8f339c45c722ae1 Mon Sep 17 00:00:00 2001 From: Jiri Pirko Date: Thu, 1 Apr 2010 21:22:09 +0000 Subject: net: move address list functions to a separate file +little renaming of unicast functions to be smooth with multicast ones Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- drivers/net/bonding/bond_main.c | 2 +- drivers/net/macvlan.c | 10 +- drivers/scsi/fcoe/fcoe.c | 14 +- include/linux/netdevice.h | 15 +- net/8021q/vlan.c | 4 +- net/8021q/vlan_dev.c | 14 +- net/core/Makefile | 3 +- net/core/dev.c | 430 +----------------------------------- net/core/dev_addr_lists.c | 478 ++++++++++++++++++++++++++++++++++++++++ net/dsa/slave.c | 14 +- net/packet/af_packet.c | 4 +- 11 files changed, 524 insertions(+), 464 deletions(-) create mode 100644 net/core/dev_addr_lists.c (limited to 'include/linux') diff --git a/drivers/net/bonding/bond_main.c b/drivers/net/bonding/bond_main.c index f8ab60b914c..d6ae63b2cf0 100644 --- a/drivers/net/bonding/bond_main.c +++ b/drivers/net/bonding/bond_main.c @@ -1491,7 +1491,7 @@ int bond_enslave(struct net_device *bond_dev, struct net_device *slave_dev) } /* Flush unicast and multicast addresses */ - dev_unicast_flush(bond_dev); + dev_uc_flush(bond_dev); dev_addr_discard(bond_dev); if (slave_dev->type != ARPHRD_ETHER) diff --git a/drivers/net/macvlan.c b/drivers/net/macvlan.c index 445e73c343b..9a939d828b4 100644 --- a/drivers/net/macvlan.c +++ b/drivers/net/macvlan.c @@ -282,7 +282,7 @@ static int macvlan_open(struct net_device *dev) if (macvlan_addr_busy(vlan->port, dev->dev_addr)) goto out; - err = dev_unicast_add(lowerdev, dev->dev_addr); + err = dev_uc_add(lowerdev, dev->dev_addr); if (err < 0) goto out; if (dev->flags & IFF_ALLMULTI) { @@ -294,7 +294,7 @@ static int macvlan_open(struct net_device *dev) return 0; del_unicast: - dev_unicast_delete(lowerdev, dev->dev_addr); + dev_uc_del(lowerdev, dev->dev_addr); out: return err; } @@ -308,7 +308,7 @@ static int macvlan_stop(struct net_device *dev) if (dev->flags & IFF_ALLMULTI) dev_set_allmulti(lowerdev, -1); - dev_unicast_delete(lowerdev, dev->dev_addr); + dev_uc_del(lowerdev, dev->dev_addr); macvlan_hash_del(vlan); return 0; @@ -332,11 +332,11 @@ static int macvlan_set_mac_address(struct net_device *dev, void *p) if (macvlan_addr_busy(vlan->port, addr->sa_data)) return -EBUSY; - err = dev_unicast_add(lowerdev, addr->sa_data); + err = dev_uc_add(lowerdev, addr->sa_data); if (err) return err; - dev_unicast_delete(lowerdev, dev->dev_addr); + dev_uc_del(lowerdev, dev->dev_addr); macvlan_hash_change_addr(vlan, addr->sa_data); } diff --git a/drivers/scsi/fcoe/fcoe.c b/drivers/scsi/fcoe/fcoe.c index 2f47ae7cce9..de33e38a405 100644 --- a/drivers/scsi/fcoe/fcoe.c +++ b/drivers/scsi/fcoe/fcoe.c @@ -308,9 +308,9 @@ static int fcoe_interface_setup(struct fcoe_interface *fcoe, * for multiple unicast MACs. */ memcpy(flogi_maddr, (u8[6]) FC_FCOE_FLOGI_MAC, ETH_ALEN); - dev_unicast_add(netdev, flogi_maddr); + dev_uc_add(netdev, flogi_maddr); if (fip->spma) - dev_unicast_add(netdev, fip->ctl_src_addr); + dev_uc_add(netdev, fip->ctl_src_addr); dev_mc_add(netdev, FIP_ALL_ENODE_MACS, ETH_ALEN, 0); /* @@ -394,9 +394,9 @@ void fcoe_interface_cleanup(struct fcoe_interface *fcoe) /* Delete secondary MAC addresses */ memcpy(flogi_maddr, (u8[6]) FC_FCOE_FLOGI_MAC, ETH_ALEN); - dev_unicast_delete(netdev, flogi_maddr); + dev_uc_del(netdev, flogi_maddr); if (fip->spma) - dev_unicast_delete(netdev, fip->ctl_src_addr); + dev_uc_del(netdev, fip->ctl_src_addr); dev_mc_delete(netdev, FIP_ALL_ENODE_MACS, ETH_ALEN, 0); /* Tell the LLD we are done w/ FCoE */ @@ -490,9 +490,9 @@ static void fcoe_update_src_mac(struct fc_lport *lport, u8 *addr) rtnl_lock(); if (!is_zero_ether_addr(port->data_src_addr)) - dev_unicast_delete(fcoe->netdev, port->data_src_addr); + dev_uc_del(fcoe->netdev, port->data_src_addr); if (!is_zero_ether_addr(addr)) - dev_unicast_add(fcoe->netdev, addr); + dev_uc_add(fcoe->netdev, addr); memcpy(port->data_src_addr, addr, ETH_ALEN); rtnl_unlock(); } @@ -819,7 +819,7 @@ static void fcoe_if_destroy(struct fc_lport *lport) rtnl_lock(); if (!is_zero_ether_addr(port->data_src_addr)) - dev_unicast_delete(netdev, port->data_src_addr); + dev_uc_del(netdev, port->data_src_addr); rtnl_unlock(); /* receives may not be stopped until after this */ diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index b5670ab5107..60f0c83192f 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -1991,15 +1991,20 @@ extern int dev_addr_add_multiple(struct net_device *to_dev, extern int dev_addr_del_multiple(struct net_device *to_dev, struct net_device *from_dev, unsigned char addr_type); +extern void dev_addr_flush(struct net_device *dev); +extern int dev_addr_init(struct net_device *dev); + +/* Functions used for unicast addresses handling */ +extern int dev_uc_add(struct net_device *dev, unsigned char *addr); +extern int dev_uc_del(struct net_device *dev, unsigned char *addr); +extern int dev_uc_sync(struct net_device *to, struct net_device *from); +extern void dev_uc_unsync(struct net_device *to, struct net_device *from); +extern void dev_uc_flush(struct net_device *dev); +extern void dev_uc_init(struct net_device *dev); /* Functions used for secondary unicast and multicast support */ extern void dev_set_rx_mode(struct net_device *dev); extern void __dev_set_rx_mode(struct net_device *dev); -extern int dev_unicast_delete(struct net_device *dev, void *addr); -extern int dev_unicast_add(struct net_device *dev, void *addr); -extern int dev_unicast_sync(struct net_device *to, struct net_device *from); -extern void dev_unicast_unsync(struct net_device *to, struct net_device *from); -extern void dev_unicast_flush(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 int dev_mc_sync(struct net_device *to, struct net_device *from); diff --git a/net/8021q/vlan.c b/net/8021q/vlan.c index c39a5f41169..bd33f02013e 100644 --- a/net/8021q/vlan.c +++ b/net/8021q/vlan.c @@ -356,13 +356,13 @@ static void vlan_sync_address(struct net_device *dev, * the new address */ if (compare_ether_addr(vlandev->dev_addr, vlan->real_dev_addr) && !compare_ether_addr(vlandev->dev_addr, dev->dev_addr)) - dev_unicast_delete(dev, vlandev->dev_addr); + dev_uc_del(dev, vlandev->dev_addr); /* vlan address was equal to the old address and is different from * the new address */ if (!compare_ether_addr(vlandev->dev_addr, vlan->real_dev_addr) && compare_ether_addr(vlandev->dev_addr, dev->dev_addr)) - dev_unicast_add(dev, vlandev->dev_addr); + dev_uc_add(dev, vlandev->dev_addr); memcpy(vlan->real_dev_addr, dev->dev_addr, ETH_ALEN); } diff --git a/net/8021q/vlan_dev.c b/net/8021q/vlan_dev.c index 9e83272fc5b..7f4d247237e 100644 --- a/net/8021q/vlan_dev.c +++ b/net/8021q/vlan_dev.c @@ -461,7 +461,7 @@ static int vlan_dev_open(struct net_device *dev) return -ENETDOWN; if (compare_ether_addr(dev->dev_addr, real_dev->dev_addr)) { - err = dev_unicast_add(real_dev, dev->dev_addr); + err = dev_uc_add(real_dev, dev->dev_addr); if (err < 0) goto out; } @@ -490,7 +490,7 @@ clear_allmulti: dev_set_allmulti(real_dev, -1); del_unicast: if (compare_ether_addr(dev->dev_addr, real_dev->dev_addr)) - dev_unicast_delete(real_dev, dev->dev_addr); + dev_uc_del(real_dev, dev->dev_addr); out: netif_carrier_off(dev); return err; @@ -505,14 +505,14 @@ static int vlan_dev_stop(struct net_device *dev) vlan_gvrp_request_leave(dev); dev_mc_unsync(real_dev, dev); - dev_unicast_unsync(real_dev, dev); + dev_uc_unsync(real_dev, dev); if (dev->flags & IFF_ALLMULTI) dev_set_allmulti(real_dev, -1); if (dev->flags & IFF_PROMISC) dev_set_promiscuity(real_dev, -1); if (compare_ether_addr(dev->dev_addr, real_dev->dev_addr)) - dev_unicast_delete(real_dev, dev->dev_addr); + dev_uc_del(real_dev, dev->dev_addr); netif_carrier_off(dev); return 0; @@ -531,13 +531,13 @@ static int vlan_dev_set_mac_address(struct net_device *dev, void *p) goto out; if (compare_ether_addr(addr->sa_data, real_dev->dev_addr)) { - err = dev_unicast_add(real_dev, addr->sa_data); + err = dev_uc_add(real_dev, addr->sa_data); if (err < 0) return err; } if (compare_ether_addr(dev->dev_addr, real_dev->dev_addr)) - dev_unicast_delete(real_dev, dev->dev_addr); + dev_uc_del(real_dev, dev->dev_addr); out: memcpy(dev->dev_addr, addr->sa_data, ETH_ALEN); @@ -654,7 +654,7 @@ static void vlan_dev_change_rx_flags(struct net_device *dev, int change) static void vlan_dev_set_rx_mode(struct net_device *vlan_dev) { dev_mc_sync(vlan_dev_info(vlan_dev)->real_dev, vlan_dev); - dev_unicast_sync(vlan_dev_info(vlan_dev)->real_dev, vlan_dev); + dev_uc_sync(vlan_dev_info(vlan_dev)->real_dev, vlan_dev); } /* diff --git a/net/core/Makefile b/net/core/Makefile index 08791ac3e05..0a899f1aadb 100644 --- a/net/core/Makefile +++ b/net/core/Makefile @@ -8,7 +8,8 @@ obj-y := sock.o request_sock.o skbuff.o iovec.o datagram.o stream.o scm.o \ obj-$(CONFIG_SYSCTL) += sysctl_net_core.o obj-y += dev.o ethtool.o dev_mcast.o dst.o netevent.o \ - neighbour.o rtnetlink.o utils.o link_watch.o filter.o + neighbour.o rtnetlink.o utils.o link_watch.o filter.o \ + dev_addr_lists.o obj-$(CONFIG_XFRM) += flow.o obj-y += net-sysfs.o diff --git a/net/core/dev.c b/net/core/dev.c index c6b52068d5e..949c62dba71 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -3968,314 +3968,6 @@ void dev_set_rx_mode(struct net_device *dev) netif_addr_unlock_bh(dev); } -/* hw addresses list handling functions */ - -static int __hw_addr_add(struct netdev_hw_addr_list *list, unsigned char *addr, - int addr_len, unsigned char addr_type) -{ - struct netdev_hw_addr *ha; - int alloc_size; - - if (addr_len > MAX_ADDR_LEN) - return -EINVAL; - - list_for_each_entry(ha, &list->list, list) { - if (!memcmp(ha->addr, addr, addr_len) && - ha->type == addr_type) { - ha->refcount++; - return 0; - } - } - - - alloc_size = sizeof(*ha); - if (alloc_size < L1_CACHE_BYTES) - alloc_size = L1_CACHE_BYTES; - ha = kmalloc(alloc_size, GFP_ATOMIC); - if (!ha) - return -ENOMEM; - memcpy(ha->addr, addr, addr_len); - ha->type = addr_type; - ha->refcount = 1; - ha->synced = false; - list_add_tail_rcu(&ha->list, &list->list); - list->count++; - return 0; -} - -static void ha_rcu_free(struct rcu_head *head) -{ - struct netdev_hw_addr *ha; - - ha = container_of(head, struct netdev_hw_addr, rcu_head); - kfree(ha); -} - -static int __hw_addr_del(struct netdev_hw_addr_list *list, unsigned char *addr, - int addr_len, unsigned char addr_type) -{ - struct netdev_hw_addr *ha; - - list_for_each_entry(ha, &list->list, list) { - if (!memcmp(ha->addr, addr, addr_len) && - (ha->type == addr_type || !addr_type)) { - if (--ha->refcount) - return 0; - list_del_rcu(&ha->list); - call_rcu(&ha->rcu_head, ha_rcu_free); - list->count--; - return 0; - } - } - return -ENOENT; -} - -static int __hw_addr_add_multiple(struct netdev_hw_addr_list *to_list, - struct netdev_hw_addr_list *from_list, - int addr_len, - unsigned char addr_type) -{ - int err; - struct netdev_hw_addr *ha, *ha2; - unsigned char type; - - list_for_each_entry(ha, &from_list->list, list) { - type = addr_type ? addr_type : ha->type; - err = __hw_addr_add(to_list, ha->addr, addr_len, type); - if (err) - goto unroll; - } - return 0; - -unroll: - list_for_each_entry(ha2, &from_list->list, list) { - if (ha2 == ha) - break; - type = addr_type ? addr_type : ha2->type; - __hw_addr_del(to_list, ha2->addr, addr_len, type); - } - return err; -} - -static void __hw_addr_del_multiple(struct netdev_hw_addr_list *to_list, - struct netdev_hw_addr_list *from_list, - int addr_len, - unsigned char addr_type) -{ - struct netdev_hw_addr *ha; - unsigned char type; - - list_for_each_entry(ha, &from_list->list, list) { - type = addr_type ? addr_type : ha->type; - __hw_addr_del(to_list, ha->addr, addr_len, addr_type); - } -} - -static int __hw_addr_sync(struct netdev_hw_addr_list *to_list, - struct netdev_hw_addr_list *from_list, - int addr_len) -{ - int err = 0; - struct netdev_hw_addr *ha, *tmp; - - list_for_each_entry_safe(ha, tmp, &from_list->list, list) { - if (!ha->synced) { - err = __hw_addr_add(to_list, ha->addr, - addr_len, ha->type); - if (err) - break; - ha->synced = true; - ha->refcount++; - } else if (ha->refcount == 1) { - __hw_addr_del(to_list, ha->addr, addr_len, ha->type); - __hw_addr_del(from_list, ha->addr, addr_len, ha->type); - } - } - return err; -} - -static void __hw_addr_unsync(struct netdev_hw_addr_list *to_list, - struct netdev_hw_addr_list *from_list, - int addr_len) -{ - struct netdev_hw_addr *ha, *tmp; - - list_for_each_entry_safe(ha, tmp, &from_list->list, list) { - if (ha->synced) { - __hw_addr_del(to_list, ha->addr, - addr_len, ha->type); - ha->synced = false; - __hw_addr_del(from_list, ha->addr, - addr_len, ha->type); - } - } -} - -static void __hw_addr_flush(struct netdev_hw_addr_list *list) -{ - struct netdev_hw_addr *ha, *tmp; - - list_for_each_entry_safe(ha, tmp, &list->list, list) { - list_del_rcu(&ha->list); - call_rcu(&ha->rcu_head, ha_rcu_free); - } - list->count = 0; -} - -static void __hw_addr_init(struct netdev_hw_addr_list *list) -{ - INIT_LIST_HEAD(&list->list); - list->count = 0; -} - -/* Device addresses handling functions */ - -static void dev_addr_flush(struct net_device *dev) -{ - /* rtnl_mutex must be held here */ - - __hw_addr_flush(&dev->dev_addrs); - dev->dev_addr = NULL; -} - -static int dev_addr_init(struct net_device *dev) -{ - unsigned char addr[MAX_ADDR_LEN]; - struct netdev_hw_addr *ha; - int err; - - /* rtnl_mutex must be held here */ - - __hw_addr_init(&dev->dev_addrs); - memset(addr, 0, sizeof(addr)); - err = __hw_addr_add(&dev->dev_addrs, addr, sizeof(addr), - NETDEV_HW_ADDR_T_LAN); - if (!err) { - /* - * Get the first (previously created) address from the list - * and set dev_addr pointer to this location. - */ - ha = list_first_entry(&dev->dev_addrs.list, - struct netdev_hw_addr, list); - dev->dev_addr = ha->addr; - } - return err; -} - -/** - * dev_addr_add - Add a device address - * @dev: device - * @addr: address to add - * @addr_type: address type - * - * Add a device address to the device or increase the reference count if - * it already exists. - * - * The caller must hold the rtnl_mutex. - */ -int dev_addr_add(struct net_device *dev, unsigned char *addr, - unsigned char addr_type) -{ - int err; - - ASSERT_RTNL(); - - err = __hw_addr_add(&dev->dev_addrs, addr, dev->addr_len, addr_type); - if (!err) - call_netdevice_notifiers(NETDEV_CHANGEADDR, dev); - return err; -} -EXPORT_SYMBOL(dev_addr_add); - -/** - * dev_addr_del - Release a device address. - * @dev: device - * @addr: address to delete - * @addr_type: address type - * - * Release reference to a device address and remove it from the device - * if the reference count drops to zero. - * - * The caller must hold the rtnl_mutex. - */ -int dev_addr_del(struct net_device *dev, unsigned char *addr, - unsigned char addr_type) -{ - int err; - struct netdev_hw_addr *ha; - - ASSERT_RTNL(); - - /* - * We can not remove the first address from the list because - * dev->dev_addr points to that. - */ - ha = list_first_entry(&dev->dev_addrs.list, - struct netdev_hw_addr, list); - if (ha->addr == dev->dev_addr && ha->refcount == 1) - return -ENOENT; - - err = __hw_addr_del(&dev->dev_addrs, addr, dev->addr_len, - addr_type); - if (!err) - call_netdevice_notifiers(NETDEV_CHANGEADDR, dev); - return err; -} -EXPORT_SYMBOL(dev_addr_del); - -/** - * dev_addr_add_multiple - Add device addresses from another device - * @to_dev: device to which addresses will be added - * @from_dev: device from which addresses will be added - * @addr_type: address type - 0 means type will be used from from_dev - * - * Add device addresses of the one device to another. - ** - * The caller must hold the rtnl_mutex. - */ -int dev_addr_add_multiple(struct net_device *to_dev, - struct net_device *from_dev, - unsigned char addr_type) -{ - int err; - - ASSERT_RTNL(); - - if (from_dev->addr_len != to_dev->addr_len) - return -EINVAL; - err = __hw_addr_add_multiple(&to_dev->dev_addrs, &from_dev->dev_addrs, - to_dev->addr_len, addr_type); - if (!err) - call_netdevice_notifiers(NETDEV_CHANGEADDR, to_dev); - return err; -} -EXPORT_SYMBOL(dev_addr_add_multiple); - -/** - * dev_addr_del_multiple - Delete device addresses by another device - * @to_dev: device where the addresses will be deleted - * @from_dev: device by which addresses the addresses will be deleted - * @addr_type: address type - 0 means type will used from from_dev - * - * Deletes addresses in to device by the list of addresses in from device. - * - * The caller must hold the rtnl_mutex. - */ -int dev_addr_del_multiple(struct net_device *to_dev, - struct net_device *from_dev, - unsigned char addr_type) -{ - ASSERT_RTNL(); - - if (from_dev->addr_len != to_dev->addr_len) - return -EINVAL; - __hw_addr_del_multiple(&to_dev->dev_addrs, &from_dev->dev_addrs, - to_dev->addr_len, addr_type); - call_netdevice_notifiers(NETDEV_CHANGEADDR, to_dev); - return 0; -} -EXPORT_SYMBOL(dev_addr_del_multiple); - /* multicast addresses handling functions */ int __dev_addr_delete(struct dev_addr_list **list, int *count, @@ -4336,57 +4028,6 @@ int __dev_addr_add(struct dev_addr_list **list, int *count, return 0; } -/** - * dev_unicast_delete - Release secondary unicast address. - * @dev: device - * @addr: address to delete - * - * Release reference to a secondary unicast address and remove it - * from the device if the reference count drops to zero. - * - * The caller must hold the rtnl_mutex. - */ -int dev_unicast_delete(struct net_device *dev, void *addr) -{ - int err; - - ASSERT_RTNL(); - - netif_addr_lock_bh(dev); - err = __hw_addr_del(&dev->uc, addr, dev->addr_len, - NETDEV_HW_ADDR_T_UNICAST); - if (!err) - __dev_set_rx_mode(dev); - netif_addr_unlock_bh(dev); - return err; -} -EXPORT_SYMBOL(dev_unicast_delete); - -/** - * dev_unicast_add - add a secondary unicast address - * @dev: device - * @addr: address to add - * - * Add a secondary unicast address to the device or increase - * the reference count if it already exists. - * - * The caller must hold the rtnl_mutex. - */ -int dev_unicast_add(struct net_device *dev, void *addr) -{ - int err; - - ASSERT_RTNL(); - - netif_addr_lock_bh(dev); - err = __hw_addr_add(&dev->uc, addr, dev->addr_len, - NETDEV_HW_ADDR_T_UNICAST); - if (!err) - __dev_set_rx_mode(dev); - netif_addr_unlock_bh(dev); - return err; -} -EXPORT_SYMBOL(dev_unicast_add); int __dev_addr_sync(struct dev_addr_list **to, int *to_count, struct dev_addr_list **from, int *from_count) @@ -4436,71 +4077,6 @@ void __dev_addr_unsync(struct dev_addr_list **to, int *to_count, } EXPORT_SYMBOL_GPL(__dev_addr_unsync); -/** - * dev_unicast_sync - Synchronize device's unicast list to another device - * @to: destination device - * @from: source device - * - * Add newly added addresses to the destination device and release - * addresses that have no users left. The source device must be - * locked by netif_tx_lock_bh. - * - * This function is intended to be called from the dev->set_rx_mode - * function of layered software devices. - */ -int dev_unicast_sync(struct net_device *to, struct net_device *from) -{ - int err = 0; - - if (to->addr_len != from->addr_len) - return -EINVAL; - - netif_addr_lock_bh(to); - err = __hw_addr_sync(&to->uc, &from->uc, to->addr_len); - if (!err) - __dev_set_rx_mode(to); - netif_addr_unlock_bh(to); - return err; -} -EXPORT_SYMBOL(dev_unicast_sync); - -/** - * dev_unicast_unsync - Remove synchronized addresses from the destination device - * @to: destination device - * @from: source device - * - * Remove all addresses that were added to the destination device by - * dev_unicast_sync(). This function is intended to be called from the - * dev->stop function of layered software devices. - */ -void dev_unicast_unsync(struct net_device *to, struct net_device *from) -{ - if (to->addr_len != from->addr_len) - return; - - netif_addr_lock_bh(from); - netif_addr_lock(to); - __hw_addr_unsync(&to->uc, &from->uc, to->addr_len); - __dev_set_rx_mode(to); - netif_addr_unlock(to); - netif_addr_unlock_bh(from); -} -EXPORT_SYMBOL(dev_unicast_unsync); - -void dev_unicast_flush(struct net_device *dev) -{ - netif_addr_lock_bh(dev); - __hw_addr_flush(&dev->uc); - netif_addr_unlock_bh(dev); -} -EXPORT_SYMBOL(dev_unicast_flush); - -static void dev_unicast_init(struct net_device *dev) -{ - __hw_addr_init(&dev->uc); -} - - static void __dev_addr_discard(struct dev_addr_list **list) { struct dev_addr_list *tmp; @@ -5153,7 +4729,7 @@ static void rollback_registered_many(struct list_head *head) /* * Flush the unicast and multicast chains */ - dev_unicast_flush(dev); + dev_uc_flush(dev); dev_addr_discard(dev); if (dev->netdev_ops->ndo_uninit) @@ -5734,7 +5310,7 @@ struct net_device *alloc_netdev_mq(int sizeof_priv, const char *name, if (dev_addr_init(dev)) goto free_rx; - dev_unicast_init(dev); + dev_uc_init(dev); dev_net_set(dev, &init_net); @@ -5968,7 +5544,7 @@ int dev_change_net_namespace(struct net_device *dev, struct net *net, const char /* * Flush the unicast and multicast chains */ - dev_unicast_flush(dev); + dev_uc_flush(dev); dev_addr_discard(dev); netdev_unregister_kobject(dev); diff --git a/net/core/dev_addr_lists.c b/net/core/dev_addr_lists.c new file mode 100644 index 00000000000..7e52b6d18ad --- /dev/null +++ b/net/core/dev_addr_lists.c @@ -0,0 +1,478 @@ +/* + * net/core/dev_addr_lists.c - Functions for handling net device lists + * Copyright (c) 2010 Jiri Pirko + * + * This file contains functions for working with unicast, multicast and device + * addresses lists. + * + * 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. + */ + +#include +#include +#include + +/* + * General list handling functions + */ + +static int __hw_addr_add(struct netdev_hw_addr_list *list, unsigned char *addr, + int addr_len, unsigned char addr_type) +{ + struct netdev_hw_addr *ha; + int alloc_size; + + if (addr_len > MAX_ADDR_LEN) + return -EINVAL; + + list_for_each_entry(ha, &list->list, list) { + if (!memcmp(ha->addr, addr, addr_len) && + ha->type == addr_type) { + ha->refcount++; + return 0; + } + } + + + alloc_size = sizeof(*ha); + if (alloc_size < L1_CACHE_BYTES) + alloc_size = L1_CACHE_BYTES; + ha = kmalloc(alloc_size, GFP_ATOMIC); + if (!ha) + return -ENOMEM; + memcpy(ha->addr, addr, addr_len); + ha->type = addr_type; + ha->refcount = 1; + ha->synced = false; + list_add_tail_rcu(&ha->list, &list->list); + list->count++; + return 0; +} + +static void ha_rcu_free(struct rcu_head *head) +{ + struct netdev_hw_addr *ha; + + ha = container_of(head, struct netdev_hw_addr, rcu_head); + kfree(ha); +} + +static int __hw_addr_del(struct netdev_hw_addr_list *list, unsigned char *addr, + int addr_len, unsigned char addr_type) +{ + struct netdev_hw_addr *ha; + + list_for_each_entry(ha, &list->list, list) { + if (!memcmp(ha->addr, addr, addr_len) && + (ha->type == addr_type || !addr_type)) { + if (--ha->refcount) + return 0; + list_del_rcu(&ha->list); + call_rcu(&ha->rcu_head, ha_rcu_free); + list->count--; + return 0; + } + } + return -ENOENT; +} + +static int __hw_addr_add_multiple(struct netdev_hw_addr_list *to_list, + struct netdev_hw_addr_list *from_list, + int addr_len, + unsigned char addr_type) +{ + int err; + struct netdev_hw_addr *ha, *ha2; + unsigned char type; + + list_for_each_entry(ha, &from_list->list, list) { + type = addr_type ? addr_type : ha->type; + err = __hw_addr_add(to_list, ha->addr, addr_len, type); + if (err) + goto unroll; + } + return 0; + +unroll: + list_for_each_entry(ha2, &from_list->list, list) { + if (ha2 == ha) + break; + type = addr_type ? addr_type : ha2->type; + __hw_addr_del(to_list, ha2->addr, addr_len, type); + } + return err; +} + +static void __hw_addr_del_multiple(struct netdev_hw_addr_list *to_list, + struct netdev_hw_addr_list *from_list, + int addr_len, + unsigned char addr_type) +{ + struct netdev_hw_addr *ha; + unsigned char type; + + list_for_each_entry(ha, &from_list->list, list) { + type = addr_type ? addr_type : ha->type; + __hw_addr_del(to_list, ha->addr, addr_len, addr_type); + } +} + +static int __hw_addr_sync(struct netdev_hw_addr_list *to_list, + struct netdev_hw_addr_list *from_list, + int addr_len) +{ + int err = 0; + struct netdev_hw_addr *ha, *tmp; + + list_for_each_entry_safe(ha, tmp, &from_list->list, list) { + if (!ha->synced) { + err = __hw_addr_add(to_list, ha->addr, + addr_len, ha->type); + if (err) + break; + ha->synced = true; + ha->refcount++; + } else if (ha->refcount == 1) { + __hw_addr_del(to_list, ha->addr, addr_len, ha->type); + __hw_addr_del(from_list, ha->addr, addr_len, ha->type); + } + } + return err; +} + +static void __hw_addr_unsync(struct netdev_hw_addr_list *to_list, + struct netdev_hw_addr_list *from_list, + int addr_len) +{ + struct netdev_hw_addr *ha, *tmp; + + list_for_each_entry_safe(ha, tmp, &from_list->list, list) { + if (ha->synced) { + __hw_addr_del(to_list, ha->addr, + addr_len, ha->type); + ha->synced = false; + __hw_addr_del(from_list, ha->addr, + addr_len, ha->type); + } + } +} + +static void __hw_addr_flush(struct netdev_hw_addr_list *list) +{ + struct netdev_hw_addr *ha, *tmp; + + list_for_each_entry_safe(ha, tmp, &list->list, list) { + list_del_rcu(&ha->list); + call_rcu(&ha->rcu_head, ha_rcu_free); + } + list->count = 0; +} + +static void __hw_addr_init(struct netdev_hw_addr_list *list) +{ + INIT_LIST_HEAD(&list->list); + list->count = 0; +} + +/* + * Device addresses handling functions + */ + +/** + * dev_addr_flush - Flush device address list + * @dev: device + * + * Flush device address list and reset ->dev_addr. + * + * The caller must hold the rtnl_mutex. + */ +void dev_addr_flush(struct net_device *dev) +{ + /* rtnl_mutex must be held here */ + + __hw_addr_flush(&dev->dev_addrs); + dev->dev_addr = NULL; +} +EXPORT_SYMBOL(dev_addr_flush); + +/** + * dev_addr_init - Init device address list + * @dev: device + * + * Init device address list and create the first element, + * used by ->dev_addr. + * + * The caller must hold the rtnl_mutex. + */ +int dev_addr_init(struct net_device *dev) +{ + unsigned char addr[MAX_ADDR_LEN]; + struct netdev_hw_addr *ha; + int err; + + /* rtnl_mutex must be held here */ + + __hw_addr_init(&dev->dev_addrs); + memset(addr, 0, sizeof(addr)); + err = __hw_addr_add(&dev->dev_addrs, addr, sizeof(addr), + NETDEV_HW_ADDR_T_LAN); + if (!err) { + /* + * Get the first (previously created) address from the list + * and set dev_addr pointer to this location. + */ + ha = list_first_entry(&dev->dev_addrs.list, + struct netdev_hw_addr, list); + dev->dev_addr = ha->addr; + } + return err; +} +EXPORT_SYMBOL(dev_addr_init); + +/** + * dev_addr_add - Add a device address + * @dev: device + * @addr: address to add + * @addr_type: address type + * + * Add a device address to the device or increase the reference count if + * it already exists. + * + * The caller must hold the rtnl_mutex. + */ +int dev_addr_add(struct net_device *dev, unsigned char *addr, + unsigned char addr_type) +{ + int err; + + ASSERT_RTNL(); + + err = __hw_addr_add(&dev->dev_addrs, addr, dev->addr_len, addr_type); + if (!err) + call_netdevice_notifiers(NETDEV_CHANGEADDR, dev); + return err; +} +EXPORT_SYMBOL(dev_addr_add); + +/** + * dev_addr_del - Release a device address. + * @dev: device + * @addr: address to delete + * @addr_type: address type + * + * Release reference to a device address and remove it from the device + * if the reference count drops to zero. + * + * The caller must hold the rtnl_mutex. + */ +int dev_addr_del(struct net_device *dev, unsigned char *addr, + unsigned char addr_type) +{ + int err; + struct netdev_hw_addr *ha; + + ASSERT_RTNL(); + + /* + * We can not remove the first address from the list because + * dev->dev_addr points to that. + */ + ha = list_first_entry(&dev->dev_addrs.list, + struct netdev_hw_addr, list); + if (ha->addr == dev->dev_addr && ha->refcount == 1) + return -ENOENT; + + err = __hw_addr_del(&dev->dev_addrs, addr, dev->addr_len, + addr_type); + if (!err) + call_netdevice_notifiers(NETDEV_CHANGEADDR, dev); + return err; +} +EXPORT_SYMBOL(dev_addr_del); + +/** + * dev_addr_add_multiple - Add device addresses from another device + * @to_dev: device to which addresses will be added + * @from_dev: device from which addresses will be added + * @addr_type: address type - 0 means type will be used from from_dev + * + * Add device addresses of the one device to another. + ** + * The caller must hold the rtnl_mutex. + */ +int dev_addr_add_multiple(struct net_device *to_dev, + struct net_device *from_dev, + unsigned char addr_type) +{ + int err; + + ASSERT_RTNL(); + + if (from_dev->addr_len != to_dev->addr_len) + return -EINVAL; + err = __hw_addr_add_multiple(&to_dev->dev_addrs, &from_dev->dev_addrs, + to_dev->addr_len, addr_type); + if (!err) + call_netdevice_notifiers(NETDEV_CHANGEADDR, to_dev); + return err; +} +EXPORT_SYMBOL(dev_addr_add_multiple); + +/** + * dev_addr_del_multiple - Delete device addresses by another device + * @to_dev: device where the addresses will be deleted + * @from_dev: device by which addresses the addresses will be deleted + * @addr_type: address type - 0 means type will used from from_dev + * + * Deletes addresses in to device by the list of addresses in from device. + * + * The caller must hold the rtnl_mutex. + */ +int dev_addr_del_multiple(struct net_device *to_dev, + struct net_device *from_dev, + unsigned char addr_type) +{ + ASSERT_RTNL(); + + if (from_dev->addr_len != to_dev->addr_len) + return -EINVAL; + __hw_addr_del_multiple(&to_dev->dev_addrs, &from_dev->dev_addrs, + to_dev->addr_len, addr_type); + call_netdevice_notifiers(NETDEV_CHANGEADDR, to_dev); + return 0; +} +EXPORT_SYMBOL(dev_addr_del_multiple); + +/* + * Unicast list handling functions + */ + +/** + * dev_uc_add - Add a secondary unicast address + * @dev: device + * @addr: address to add + * + * Add a secondary unicast address to the device or increase + * the reference count if it already exists. + */ +int dev_uc_add(struct net_device *dev, unsigned char *addr) +{ + int err; + + netif_addr_lock_bh(dev); + err = __hw_addr_add(&dev->uc, addr, dev->addr_len, + NETDEV_HW_ADDR_T_UNICAST); + if (!err) + __dev_set_rx_mode(dev); + netif_addr_unlock_bh(dev); + return err; +} +EXPORT_SYMBOL(dev_uc_add); + +/** + * dev_uc_del - Release secondary unicast address. + * @dev: device + * @addr: address to delete + * + * Release reference to a secondary unicast address and remove it + * from the device if the reference count drops to zero. + */ +int dev_uc_del(struct net_device *dev, unsigned char *addr) +{ + int err; + + netif_addr_lock_bh(dev); + err = __hw_addr_del(&dev->uc, addr, dev->addr_len, + NETDEV_HW_ADDR_T_UNICAST); + if (!err) + __dev_set_rx_mode(dev); + netif_addr_unlock_bh(dev); + return err; +} +EXPORT_SYMBOL(dev_uc_del); + +/** + * dev_uc_sync - Synchronize device's unicast list to another device + * @to: destination device + * @from: source device + * + * Add newly added addresses to the destination device and release + * addresses that have no users left. The source device must be + * locked by netif_tx_lock_bh. + * + * This function is intended to be called from the dev->set_rx_mode + * function of layered software devices. + */ +int dev_uc_sync(struct net_device *to, struct net_device *from) +{ + int err = 0; + + if (to->addr_len != from->addr_len) + return -EINVAL; + + netif_addr_lock_bh(to); + err = __hw_addr_sync(&to->uc, &from->uc, to->addr_len); + if (!err) + __dev_set_rx_mode(to); + netif_addr_unlock_bh(to); + return err; +} +EXPORT_SYMBOL(dev_uc_sync); + +/** + * dev_uc_unsync - Remove synchronized addresses from the destination device + * @to: destination device + * @from: source device + * + * Remove all addresses that were added to the destination device by + * dev_uc_sync(). This function is intended to be called from the + * dev->stop function of layered software devices. + */ +void dev_uc_unsync(struct net_device *to, struct net_device *from) +{ + if (to->addr_len != from->addr_len) + return; + + netif_addr_lock_bh(from); + netif_addr_lock(to); + __hw_addr_unsync(&to->uc, &from->uc, to->addr_len); + __dev_set_rx_mode(to); + netif_addr_unlock(to); + netif_addr_unlock_bh(from); +} +EXPORT_SYMBOL(dev_uc_unsync); + +/** + * dev_uc_flush - Flush unicast addresses + * @dev: device + * + * Flush unicast addresses. + */ +void dev_uc_flush(struct net_device *dev) +{ + netif_addr_lock_bh(dev); + __hw_addr_flush(&dev->uc); + netif_addr_unlock_bh(dev); +} +EXPORT_SYMBOL(dev_uc_flush); + +/** + * dev_uc_flush - Init unicast address list + * @dev: device + * + * Init unicast address list. + */ +void dev_uc_init(struct net_device *dev) +{ + __hw_addr_init(&dev->uc); +} +EXPORT_SYMBOL(dev_uc_init); + +/* + * Multicast list handling functions + */ + +/* To be filled here */ diff --git a/net/dsa/slave.c b/net/dsa/slave.c index 2175e6d5cc8..8fdca56bb08 100644 --- a/net/dsa/slave.c +++ b/net/dsa/slave.c @@ -67,7 +67,7 @@ static int dsa_slave_open(struct net_device *dev) return -ENETDOWN; if (compare_ether_addr(dev->dev_addr, master->dev_addr)) { - err = dev_unicast_add(master, dev->dev_addr); + err = dev_uc_add(master, dev->dev_addr); if (err < 0) goto out; } @@ -90,7 +90,7 @@ clear_allmulti: dev_set_allmulti(master, -1); del_unicast: if (compare_ether_addr(dev->dev_addr, master->dev_addr)) - dev_unicast_delete(master, dev->dev_addr); + dev_uc_del(master, dev->dev_addr); out: return err; } @@ -101,14 +101,14 @@ static int dsa_slave_close(struct net_device *dev) struct net_device *master = p->parent->dst->master_netdev; dev_mc_unsync(master, dev); - dev_unicast_unsync(master, dev); + dev_uc_unsync(master, dev); if (dev->flags & IFF_ALLMULTI) dev_set_allmulti(master, -1); if (dev->flags & IFF_PROMISC) dev_set_promiscuity(master, -1); if (compare_ether_addr(dev->dev_addr, master->dev_addr)) - dev_unicast_delete(master, dev->dev_addr); + dev_uc_del(master, dev->dev_addr); return 0; } @@ -130,7 +130,7 @@ static void dsa_slave_set_rx_mode(struct net_device *dev) struct net_device *master = p->parent->dst->master_netdev; dev_mc_sync(master, dev); - dev_unicast_sync(master, dev); + dev_uc_sync(master, dev); } static int dsa_slave_set_mac_address(struct net_device *dev, void *a) @@ -147,13 +147,13 @@ static int dsa_slave_set_mac_address(struct net_device *dev, void *a) goto out; if (compare_ether_addr(addr->sa_data, master->dev_addr)) { - err = dev_unicast_add(master, addr->sa_data); + err = dev_uc_add(master, addr->sa_data); if (err < 0) return err; } if (compare_ether_addr(dev->dev_addr, master->dev_addr)) - dev_unicast_delete(master, dev->dev_addr); + dev_uc_del(master, dev->dev_addr); out: memcpy(dev->dev_addr, addr->sa_data, ETH_ALEN); diff --git a/net/packet/af_packet.c b/net/packet/af_packet.c index 1612d417d10..48c1e0ae565 100644 --- a/net/packet/af_packet.c +++ b/net/packet/af_packet.c @@ -1705,9 +1705,9 @@ static int packet_dev_mc(struct net_device *dev, struct packet_mclist *i, if (i->alen != dev->addr_len) return -EINVAL; if (what > 0) - return dev_unicast_add(dev, i->addr); + return dev_uc_add(dev, i->addr); else - return dev_unicast_delete(dev, i->addr); + return dev_uc_del(dev, i->addr); break; default: break; -- cgit v1.2.3 From 22bedad3ce112d5ca1eaf043d4990fa2ed698c87 Mon Sep 17 00:00:00 2001 From: Jiri Pirko Date: Thu, 1 Apr 2010 21:22:57 +0000 Subject: net: convert multicast list to list_head Converts the list and the core manipulating with it to be the same as uc_list. +uses two functions for adding/removing mc address (normal and "global" variant) instead of a function parameter. +removes dev_mcast.c completely. +exposes netdev_hw_addr_list_* macros along with __hw_addr_* functions for manipulation with lists on a sandbox (used in bonding and 80211 drivers) Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- drivers/infiniband/hw/nes/nes_nic.c | 7 +- drivers/infiniband/ulp/ipoib/ipoib_multicast.c | 9 +- drivers/media/dvb/dvb-core/dvb_net.c | 10 +- drivers/net/3c505.c | 7 +- drivers/net/3c523.c | 7 +- drivers/net/3c527.c | 6 +- drivers/net/7990.c | 6 +- drivers/net/8139cp.c | 6 +- drivers/net/8139too.c | 6 +- drivers/net/82596.c | 6 +- drivers/net/a2065.c | 6 +- drivers/net/amd8111e.c | 6 +- drivers/net/arm/am79c961a.c | 6 +- drivers/net/arm/at91_ether.c | 6 +- drivers/net/arm/ixp4xx_eth.c | 8 +- drivers/net/arm/ks8695net.c | 10 +- drivers/net/at1700.c | 6 +- drivers/net/atl1c/atl1c_main.c | 6 +- drivers/net/atl1e/atl1e_main.c | 6 +- drivers/net/atlx/atl2.c | 6 +- drivers/net/atlx/atlx.c | 6 +- drivers/net/atp.c | 6 +- drivers/net/au1000_eth.c | 6 +- drivers/net/b44.c | 6 +- drivers/net/bcm63xx_enet.c | 6 +- drivers/net/benet/be_cmds.c | 6 +- drivers/net/bfin_mac.c | 6 +- drivers/net/bmac.c | 12 +- drivers/net/bnx2.c | 5 +- drivers/net/bnx2x_main.c | 18 +- drivers/net/bonding/bond_main.c | 157 +++++-------- drivers/net/bonding/bonding.h | 2 +- drivers/net/cassini.c | 12 +- drivers/net/chelsio/pm3393.c | 7 +- drivers/net/cpmac.c | 16 +- drivers/net/cris/eth_v10.c | 6 +- drivers/net/cxgb3/xgmac.c | 8 +- drivers/net/davinci_emac.c | 7 +- drivers/net/declance.c | 6 +- drivers/net/defxx.c | 6 +- drivers/net/depca.c | 6 +- drivers/net/dl2k.c | 6 +- drivers/net/dm9000.c | 6 +- drivers/net/e100.c | 6 +- drivers/net/e1000/e1000_main.c | 7 +- drivers/net/e1000e/netdev.c | 7 +- drivers/net/eepro.c | 6 +- drivers/net/eexpress.c | 6 +- drivers/net/ehea/ehea_main.c | 6 +- drivers/net/enic/enic_main.c | 6 +- drivers/net/epic100.c | 6 +- drivers/net/ethoc.c | 6 +- drivers/net/ewrk3.c | 6 +- drivers/net/fealnx.c | 6 +- drivers/net/fec.c | 10 +- drivers/net/fec_mpc52xx.c | 6 +- drivers/net/forcedeth.c | 10 +- drivers/net/fs_enet/mac-fcc.c | 6 +- drivers/net/fs_enet/mac-fec.c | 6 +- drivers/net/fs_enet/mac-scc.c | 6 +- drivers/net/gianfar.c | 9 +- drivers/net/greth.c | 6 +- drivers/net/hamachi.c | 8 +- drivers/net/hp100.c | 6 +- drivers/net/ibm_newemac/core.c | 11 +- drivers/net/ibmlana.c | 8 +- drivers/net/ibmveth.c | 6 +- drivers/net/igb/igb_main.c | 6 +- drivers/net/igbvf/netdev.c | 6 +- drivers/net/ioc3-eth.c | 6 +- drivers/net/ipg.c | 6 +- drivers/net/iseries_veth.c | 6 +- drivers/net/ixgb/ixgb_main.c | 6 +- drivers/net/ixgbe/ixgbe_common.c | 6 +- drivers/net/ixgbevf/vf.c | 6 +- drivers/net/jme.c | 6 +- drivers/net/korina.c | 6 +- drivers/net/ks8851.c | 6 +- drivers/net/ks8851_mll.c | 12 +- drivers/net/ksz884x.c | 11 +- drivers/net/lib82596.c | 6 +- drivers/net/lib8390.c | 6 +- drivers/net/ll_temac_main.c | 16 +- drivers/net/lp486e.c | 6 +- drivers/net/macb.c | 6 +- drivers/net/mace.c | 6 +- drivers/net/macmace.c | 6 +- drivers/net/mlx4/en_netdev.c | 6 +- drivers/net/mv643xx_eth.c | 6 +- drivers/net/myri10ge/myri10ge.c | 8 +- drivers/net/natsemi.c | 6 +- drivers/net/netxen/netxen_nic_hw.c | 12 +- drivers/net/ni52.c | 6 +- drivers/net/niu.c | 5 +- drivers/net/octeon/octeon_mgmt.c | 6 +- drivers/net/pci-skeleton.c | 6 +- drivers/net/pcmcia/axnet_cs.c | 6 +- drivers/net/pcmcia/fmvj18x_cs.c | 6 +- drivers/net/pcmcia/nmclan_cs.c | 6 +- drivers/net/pcmcia/smc91c92_cs.c | 8 +- drivers/net/pcmcia/xirc2ps_cs.c | 6 +- drivers/net/pcnet32.c | 6 +- drivers/net/ps3_gelic_net.c | 6 +- drivers/net/qlcnic/qlcnic_hw.c | 6 +- drivers/net/qlge/qlge_main.c | 6 +- drivers/net/r6040.c | 10 +- drivers/net/r8169.c | 6 +- drivers/net/s2io.c | 8 +- drivers/net/sb1250-mac.c | 6 +- drivers/net/sc92031.c | 6 +- drivers/net/sfc/efx.c | 6 +- drivers/net/sis190.c | 6 +- drivers/net/sis900.c | 10 +- drivers/net/skfp/skfddi.c | 12 +- drivers/net/skge.c | 12 +- drivers/net/sky2.c | 6 +- drivers/net/smc911x.c | 13 +- drivers/net/smc9194.c | 13 +- drivers/net/smc91x.c | 8 +- drivers/net/smsc911x.c | 6 +- drivers/net/smsc9420.c | 6 +- drivers/net/sonic.c | 6 +- drivers/net/spider_net.c | 6 +- drivers/net/starfire.c | 10 +- drivers/net/stmmac/dwmac100.c | 6 +- drivers/net/stmmac/dwmac1000_core.c | 6 +- drivers/net/sun3_82586.c | 6 +- drivers/net/sunbmac.c | 6 +- drivers/net/sundance.c | 6 +- drivers/net/sungem.c | 6 +- drivers/net/sunhme.c | 12 +- drivers/net/sunlance.c | 6 +- drivers/net/sunqe.c | 6 +- drivers/net/sunvnet.c | 8 +- drivers/net/tc35815.c | 6 +- drivers/net/tehuti.c | 6 +- drivers/net/tg3.c | 6 +- drivers/net/tlan.c | 8 +- drivers/net/tokenring/3c359.c | 12 +- drivers/net/tokenring/ibmtr.c | 12 +- drivers/net/tokenring/lanstreamer.c | 12 +- drivers/net/tokenring/olympic.c | 12 +- drivers/net/tokenring/tms380tr.c | 12 +- drivers/net/tsi108_eth.c | 6 +- drivers/net/tulip/de2104x.c | 12 +- drivers/net/tulip/de4x5.c | 10 +- drivers/net/tulip/dmfe.c | 12 +- drivers/net/tulip/tulip_core.c | 27 ++- drivers/net/tulip/uli526x.c | 6 +- drivers/net/tulip/winbond-840.c | 12 +- drivers/net/typhoon.c | 6 +- drivers/net/ucc_geth.c | 8 +- drivers/net/usb/asix.c | 16 +- drivers/net/usb/catc.c | 6 +- drivers/net/usb/dm9601.c | 6 +- drivers/net/usb/mcs7830.c | 6 +- drivers/net/usb/smsc75xx.c | 6 +- drivers/net/usb/smsc95xx.c | 6 +- drivers/net/via-rhine.c | 6 +- drivers/net/via-velocity.c | 6 +- drivers/net/virtio_net.c | 5 +- drivers/net/vmxnet3/vmxnet3_drv.c | 6 +- drivers/net/vxge/vxge-main.c | 6 +- drivers/net/wireless/adm8211.c | 12 +- drivers/net/wireless/ath/ar9170/main.c | 14 +- drivers/net/wireless/ath/ath5k/base.c | 17 +- drivers/net/wireless/libertas/main.c | 12 +- drivers/net/wireless/libertas_tf/main.c | 14 +- drivers/net/wireless/mwl8k.c | 24 +- drivers/net/wireless/orinoco/hw.c | 6 +- drivers/net/wireless/orinoco/hw.h | 1 - drivers/net/wireless/ray_cs.c | 12 +- drivers/net/wireless/rndis_wlan.c | 6 +- drivers/net/wireless/rtl818x/rtl8180_dev.c | 6 +- drivers/net/wireless/rtl818x/rtl8187_dev.c | 4 +- drivers/net/wireless/wl12xx/wl1271_main.c | 25 +- drivers/net/wireless/zd1201.c | 6 +- drivers/net/wireless/zd1211rw/zd_mac.c | 13 +- drivers/net/yellowfin.c | 12 +- drivers/s390/net/qeth_l2_main.c | 5 +- drivers/scsi/fcoe/fcoe.c | 4 +- drivers/staging/arlan/arlan-main.c | 9 +- drivers/staging/et131x/et131x_netdev.c | 6 +- drivers/staging/slicoss/slicoss.c | 6 +- drivers/staging/vt6655/device_main.c | 6 +- drivers/staging/vt6656/main_usb.c | 6 +- drivers/staging/wavelan/wavelan.c | 10 +- drivers/staging/wavelan/wavelan_cs.c | 12 +- drivers/staging/winbond/wbusb.c | 6 +- drivers/staging/wlags49_h2/wl_netdev.c | 12 +- include/linux/netdevice.h | 82 +++---- include/net/mac80211.h | 2 +- net/802/garp.c | 4 +- net/appletalk/ddp.c | 2 +- net/bluetooth/bnep/netdev.c | 8 +- net/core/Makefile | 5 +- net/core/dev.c | 145 +----------- net/core/dev_addr_lists.c | 305 +++++++++++++++++++++++-- net/core/dev_mcast.c | 232 ------------------- net/decnet/dn_dev.c | 12 +- net/ipv4/igmp.c | 4 +- net/ipv4/netfilter/ipt_CLUSTERIP.c | 4 +- net/ipv6/mcast.c | 4 +- net/mac80211/driver-ops.h | 8 +- net/mac80211/ieee80211_i.h | 3 +- net/mac80211/iface.c | 6 +- net/mac80211/main.c | 2 +- net/packet/af_packet.c | 4 +- 208 files changed, 1137 insertions(+), 1327 deletions(-) delete mode 100644 net/core/dev_mcast.c (limited to 'include/linux') diff --git a/drivers/infiniband/hw/nes/nes_nic.c b/drivers/infiniband/hw/nes/nes_nic.c index 91fdde382e8..ce9ef6bc865 100644 --- a/drivers/infiniband/hw/nes/nes_nic.c +++ b/drivers/infiniband/hw/nes/nes_nic.c @@ -876,7 +876,7 @@ static void nes_netdev_set_multicast_list(struct net_device *netdev) if (!mc_all_on) { char *addrs; int i; - struct dev_mc_list *mcaddr; + struct netdev_hw_addr *ha; addrs = kmalloc(ETH_ALEN * mc_count, GFP_ATOMIC); if (!addrs) { @@ -884,9 +884,8 @@ static void nes_netdev_set_multicast_list(struct net_device *netdev) goto unlock; } i = 0; - netdev_for_each_mc_addr(mcaddr, netdev) - memcpy(get_addr(addrs, i++), - mcaddr->dmi_addr, ETH_ALEN); + netdev_for_each_mc_addr(ha, netdev) + memcpy(get_addr(addrs, i++), ha->addr, ETH_ALEN); perfect_filter_register_address = NES_IDX_PERFECT_FILTER_LOW + pft_entries_preallocated * 0x8; diff --git a/drivers/infiniband/ulp/ipoib/ipoib_multicast.c b/drivers/infiniband/ulp/ipoib/ipoib_multicast.c index 19eba3c877c..c8a0f7dab5b 100644 --- a/drivers/infiniband/ulp/ipoib/ipoib_multicast.c +++ b/drivers/infiniband/ulp/ipoib/ipoib_multicast.c @@ -783,7 +783,7 @@ void ipoib_mcast_restart_task(struct work_struct *work) struct ipoib_dev_priv *priv = container_of(work, struct ipoib_dev_priv, restart_task); struct net_device *dev = priv->dev; - struct dev_mc_list *mclist; + struct netdev_hw_addr *ha; struct ipoib_mcast *mcast, *tmcast; LIST_HEAD(remove_list); unsigned long flags; @@ -808,14 +808,13 @@ void ipoib_mcast_restart_task(struct work_struct *work) clear_bit(IPOIB_MCAST_FLAG_FOUND, &mcast->flags); /* Mark all of the entries that are found or don't exist */ - netdev_for_each_mc_addr(mclist, dev) { + netdev_for_each_mc_addr(ha, dev) { union ib_gid mgid; - if (!ipoib_mcast_addr_is_valid(mclist->dmi_addr, - dev->broadcast)) + if (!ipoib_mcast_addr_is_valid(ha->addr, dev->broadcast)) continue; - memcpy(mgid.raw, mclist->dmi_addr + 4, sizeof mgid); + memcpy(mgid.raw, ha->addr + 4, sizeof mgid); mcast = __ipoib_mcast_find(dev, &mgid); if (!mcast || test_bit(IPOIB_MCAST_FLAG_SENDONLY, &mcast->flags)) { diff --git a/drivers/media/dvb/dvb-core/dvb_net.c b/drivers/media/dvb/dvb-core/dvb_net.c index dba1c84058b..cccea412088 100644 --- a/drivers/media/dvb/dvb-core/dvb_net.c +++ b/drivers/media/dvb/dvb-core/dvb_net.c @@ -1109,14 +1109,14 @@ static int dvb_net_feed_stop(struct net_device *dev) } -static int dvb_set_mc_filter (struct net_device *dev, struct dev_mc_list *mc) +static int dvb_set_mc_filter(struct net_device *dev, unsigned char *addr) { struct dvb_net_priv *priv = netdev_priv(dev); if (priv->multi_num == DVB_NET_MULTICAST_MAX) return -ENOMEM; - memcpy(priv->multi_macs[priv->multi_num], mc->dmi_addr, 6); + memcpy(priv->multi_macs[priv->multi_num], addr, ETH_ALEN); priv->multi_num++; return 0; @@ -1140,7 +1140,7 @@ static void wq_set_multicast_list (struct work_struct *work) dprintk("%s: allmulti mode\n", dev->name); priv->rx_mode = RX_MODE_ALL_MULTI; } else if (!netdev_mc_empty(dev)) { - struct dev_mc_list *mc; + struct netdev_hw_addr *ha; dprintk("%s: set_mc_list, %d entries\n", dev->name, netdev_mc_count(dev)); @@ -1148,8 +1148,8 @@ static void wq_set_multicast_list (struct work_struct *work) priv->rx_mode = RX_MODE_MULTI; priv->multi_num = 0; - netdev_for_each_mc_addr(mc, dev) - dvb_set_mc_filter(dev, mc); + netdev_for_each_mc_addr(ha, dev) + dvb_set_mc_filter(dev, ha->addr); } netif_addr_unlock_bh(dev); diff --git a/drivers/net/3c505.c b/drivers/net/3c505.c index 04b5bba1902..81c8b31e629 100644 --- a/drivers/net/3c505.c +++ b/drivers/net/3c505.c @@ -1216,7 +1216,7 @@ static int elp_close(struct net_device *dev) static void elp_set_mc_list(struct net_device *dev) { elp_device *adapter = netdev_priv(dev); - struct dev_mc_list *dmi; + struct netdev_hw_addr *ha; int i; unsigned long flags; @@ -1231,8 +1231,9 @@ static void elp_set_mc_list(struct net_device *dev) adapter->tx_pcb.command = CMD_LOAD_MULTICAST_LIST; adapter->tx_pcb.length = 6 * netdev_mc_count(dev); i = 0; - netdev_for_each_mc_addr(dmi, dev) - memcpy(adapter->tx_pcb.data.multicast[i++], dmi->dmi_addr, 6); + netdev_for_each_mc_addr(ha, dev) + memcpy(adapter->tx_pcb.data.multicast[i++], + ha->addr, 6); adapter->got[CMD_LOAD_MULTICAST_LIST] = 0; if (!send_pcb(dev, &adapter->tx_pcb)) pr_err("%s: couldn't send set_multicast command\n", dev->name); diff --git a/drivers/net/3c523.c b/drivers/net/3c523.c index beed4fa10c6..966cb12e1e0 100644 --- a/drivers/net/3c523.c +++ b/drivers/net/3c523.c @@ -625,7 +625,7 @@ static int init586(struct net_device *dev) volatile struct iasetup_cmd_struct *ias_cmd; volatile struct tdr_cmd_struct *tdr_cmd; volatile struct mcsetup_cmd_struct *mc_cmd; - struct dev_mc_list *dmi; + struct netdev_hw_addr *ha; int num_addrs = netdev_mc_count(dev); ptr = (void *) ((char *) p->scb + sizeof(struct scb_struct)); @@ -788,8 +788,9 @@ static int init586(struct net_device *dev) mc_cmd->cmd_link = 0xffff; mc_cmd->mc_cnt = num_addrs * 6; i = 0; - netdev_for_each_mc_addr(dmi, dev) - memcpy((char *) mc_cmd->mc_list[i++], dmi->dmi_addr, 6); + netdev_for_each_mc_addr(ha, dev) + memcpy((char *) mc_cmd->mc_list[i++], + ha->addr, 6); p->scb->cbl_offset = make16(mc_cmd); p->scb->cmd = CUC_START; elmc_id_attn586(); diff --git a/drivers/net/3c527.c b/drivers/net/3c527.c index 5c07b147ec9..38395dfa496 100644 --- a/drivers/net/3c527.c +++ b/drivers/net/3c527.c @@ -1533,7 +1533,7 @@ static void do_mc32_set_multicast_list(struct net_device *dev, int retry) { unsigned char block[62]; unsigned char *bp; - struct dev_mc_list *dmc; + struct netdev_hw_addr *ha; if(retry==0) lp->mc_list_valid = 0; @@ -1543,8 +1543,8 @@ static void do_mc32_set_multicast_list(struct net_device *dev, int retry) block[0]=netdev_mc_count(dev); bp=block+2; - netdev_for_each_mc_addr(dmc, dev) { - memcpy(bp, dmc->dmi_addr, 6); + netdev_for_each_mc_addr(ha, dev) { + memcpy(bp, ha->addr, 6); bp+=6; } if(mc32_command_nowait(dev, 2, block, diff --git a/drivers/net/7990.c b/drivers/net/7990.c index 4e9a5a20b6a..818837d8ffe 100644 --- a/drivers/net/7990.c +++ b/drivers/net/7990.c @@ -595,7 +595,7 @@ static void lance_load_multicast (struct net_device *dev) struct lance_private *lp = netdev_priv(dev); volatile struct lance_init_block *ib = lp->init_block; volatile u16 *mcast_table = (u16 *)&ib->filter; - struct dev_mc_list *dmi; + struct netdev_hw_addr *ha; char *addrs; u32 crc; @@ -610,8 +610,8 @@ static void lance_load_multicast (struct net_device *dev) ib->filter [1] = 0; /* Add addresses */ - netdev_for_each_mc_addr(dmi, dev) { - addrs = dmi->dmi_addr; + netdev_for_each_mc_addr(ha, dev) { + addrs = ha->addr; /* multicast address? */ if (!(*addrs & 1)) diff --git a/drivers/net/8139cp.c b/drivers/net/8139cp.c index 3d4406b1665..e4e2aef1101 100644 --- a/drivers/net/8139cp.c +++ b/drivers/net/8139cp.c @@ -909,11 +909,11 @@ static void __cp_set_rx_mode (struct net_device *dev) rx_mode = AcceptBroadcast | AcceptMulticast | AcceptMyPhys; mc_filter[1] = mc_filter[0] = 0xffffffff; } else { - struct dev_mc_list *mclist; + struct netdev_hw_addr *ha; rx_mode = AcceptBroadcast | AcceptMyPhys; mc_filter[1] = mc_filter[0] = 0; - netdev_for_each_mc_addr(mclist, dev) { - int bit_nr = ether_crc(ETH_ALEN, mclist->dmi_addr) >> 26; + netdev_for_each_mc_addr(ha, dev) { + int bit_nr = ether_crc(ETH_ALEN, ha->addr) >> 26; mc_filter[bit_nr >> 5] |= 1 << (bit_nr & 31); rx_mode |= AcceptMulticast; diff --git a/drivers/net/8139too.c b/drivers/net/8139too.c index b4efc913978..f61784c3c4a 100644 --- a/drivers/net/8139too.c +++ b/drivers/net/8139too.c @@ -2502,11 +2502,11 @@ static void __set_rx_mode (struct net_device *dev) rx_mode = AcceptBroadcast | AcceptMulticast | AcceptMyPhys; mc_filter[1] = mc_filter[0] = 0xffffffff; } else { - struct dev_mc_list *mclist; + struct netdev_hw_addr *ha; rx_mode = AcceptBroadcast | AcceptMyPhys; mc_filter[1] = mc_filter[0] = 0; - netdev_for_each_mc_addr(mclist, dev) { - int bit_nr = ether_crc(ETH_ALEN, mclist->dmi_addr) >> 26; + netdev_for_each_mc_addr(ha, dev) { + int bit_nr = ether_crc(ETH_ALEN, ha->addr) >> 26; mc_filter[bit_nr >> 5] |= 1 << (bit_nr & 31); rx_mode |= AcceptMulticast; diff --git a/drivers/net/82596.c b/drivers/net/82596.c index f94d17d78bb..3a28b1f451d 100644 --- a/drivers/net/82596.c +++ b/drivers/net/82596.c @@ -1542,7 +1542,7 @@ static void set_multicast_list(struct net_device *dev) } if (!netdev_mc_empty(dev)) { - struct dev_mc_list *dmi; + struct netdev_hw_addr *ha; unsigned char *cp; struct mc_cmd *cmd; @@ -1552,10 +1552,10 @@ static void set_multicast_list(struct net_device *dev) cmd->cmd.command = CmdMulticastList; cmd->mc_cnt = cnt * ETH_ALEN; cp = cmd->mc_addrs; - netdev_for_each_mc_addr(dmi, dev) { + netdev_for_each_mc_addr(ha, dev) { if (!cnt--) break; - memcpy(cp, dmi->dmi_addr, ETH_ALEN); + memcpy(cp, ha->addr, ETH_ALEN); if (i596_debug > 1) DEB(DEB_MULTI,printk(KERN_INFO "%s: Adding address %pM\n", dev->name, cp)); diff --git a/drivers/net/a2065.c b/drivers/net/a2065.c index bd4d829eca1..7cce643793c 100644 --- a/drivers/net/a2065.c +++ b/drivers/net/a2065.c @@ -603,7 +603,7 @@ static void lance_load_multicast (struct net_device *dev) struct lance_private *lp = netdev_priv(dev); volatile struct lance_init_block *ib = lp->init_block; volatile u16 *mcast_table = (u16 *)&ib->filter; - struct dev_mc_list *dmi; + struct netdev_hw_addr *ha; char *addrs; u32 crc; @@ -618,8 +618,8 @@ static void lance_load_multicast (struct net_device *dev) ib->filter [1] = 0; /* Add addresses */ - netdev_for_each_mc_addr(dmi, dev) { - addrs = dmi->dmi_addr; + netdev_for_each_mc_addr(ha, dev) { + addrs = ha->addr; /* multicast address? */ if (!(*addrs & 1)) diff --git a/drivers/net/amd8111e.c b/drivers/net/amd8111e.c index b8a59d255b4..d002c764a26 100644 --- a/drivers/net/amd8111e.c +++ b/drivers/net/amd8111e.c @@ -1377,7 +1377,7 @@ list to the device. */ static void amd8111e_set_multicast_list(struct net_device *dev) { - struct dev_mc_list *mc_ptr; + struct netdev_hw_addr *ha; struct amd8111e_priv *lp = netdev_priv(dev); u32 mc_filter[2] ; int bit_num; @@ -1408,8 +1408,8 @@ static void amd8111e_set_multicast_list(struct net_device *dev) /* load all the multicast addresses in the logic filter */ lp->options |= OPTION_MULTICAST_ENABLE; mc_filter[1] = mc_filter[0] = 0; - netdev_for_each_mc_addr(mc_ptr, dev) { - bit_num = (ether_crc_le(ETH_ALEN, mc_ptr->dmi_addr) >> 26) & 0x3f; + netdev_for_each_mc_addr(ha, dev) { + bit_num = (ether_crc_le(ETH_ALEN, ha->addr) >> 26) & 0x3f; mc_filter[bit_num >> 5] |= 1 << (bit_num & 31); } amd8111e_writeq(*(u64*)mc_filter,lp->mmio+ LADRF); diff --git a/drivers/net/arm/am79c961a.c b/drivers/net/arm/am79c961a.c index f1f58c5e27b..a4b5b08276f 100644 --- a/drivers/net/arm/am79c961a.c +++ b/drivers/net/arm/am79c961a.c @@ -383,12 +383,12 @@ static void am79c961_setmulticastlist (struct net_device *dev) } else if (dev->flags & IFF_ALLMULTI) { memset(multi_hash, 0xff, sizeof(multi_hash)); } else { - struct dev_mc_list *dmi; + struct netdev_hw_addr *ha; memset(multi_hash, 0x00, sizeof(multi_hash)); - netdev_for_each_mc_addr(dmi, dev) - am79c961_mc_hash(dmi->dmi_addr, multi_hash); + netdev_for_each_mc_addr(ha, dev) + am79c961_mc_hash(ha->addr, multi_hash); } spin_lock_irqsave(&priv->chip_lock, flags); diff --git a/drivers/net/arm/at91_ether.c b/drivers/net/arm/at91_ether.c index 8b23d5a175b..f31e8b6cbf7 100644 --- a/drivers/net/arm/at91_ether.c +++ b/drivers/net/arm/at91_ether.c @@ -556,14 +556,14 @@ static int hash_get_index(__u8 *addr) */ static void at91ether_sethashtable(struct net_device *dev) { - struct dev_mc_list *curr; + struct netdev_hw_addr *ha; unsigned long mc_filter[2]; unsigned int bitnr; mc_filter[0] = mc_filter[1] = 0; - netdev_for_each_mc_addr(curr, dev) { - bitnr = hash_get_index(curr->dmi_addr); + netdev_for_each_mc_addr(ha, dev) { + bitnr = hash_get_index(ha->addr); mc_filter[bitnr >> 5] |= 1 << (bitnr & 31); } diff --git a/drivers/net/arm/ixp4xx_eth.c b/drivers/net/arm/ixp4xx_eth.c index 6e2ae1d06df..f9d168775d0 100644 --- a/drivers/net/arm/ixp4xx_eth.c +++ b/drivers/net/arm/ixp4xx_eth.c @@ -735,7 +735,7 @@ static int eth_xmit(struct sk_buff *skb, struct net_device *dev) static void eth_set_mcast_list(struct net_device *dev) { struct port *port = netdev_priv(dev); - struct dev_mc_list *mclist; + struct netdev_hw_addr *ha; u8 diffs[ETH_ALEN], *addr; int i; @@ -748,11 +748,11 @@ static void eth_set_mcast_list(struct net_device *dev) memset(diffs, 0, ETH_ALEN); addr = NULL; - netdev_for_each_mc_addr(mclist, dev) { + netdev_for_each_mc_addr(ha, dev) { if (!addr) - addr = mclist->dmi_addr; /* first MAC address */ + addr = ha->addr; /* first MAC address */ for (i = 0; i < ETH_ALEN; i++) - diffs[i] |= addr[i] ^ mclist->dmi_addr[i]; + diffs[i] |= addr[i] ^ ha->addr[i]; } for (i = 0; i < ETH_ALEN; i++) { diff --git a/drivers/net/arm/ks8695net.c b/drivers/net/arm/ks8695net.c index e7810b74f39..6ec245c6394 100644 --- a/drivers/net/arm/ks8695net.c +++ b/drivers/net/arm/ks8695net.c @@ -331,16 +331,16 @@ ks8695_init_partial_multicast(struct ks8695_priv *ksp, { u32 low, high; int i; - struct dev_mc_list *dmi; + struct netdev_hw_addr *ha; i = 0; - netdev_for_each_mc_addr(dmi, ndev) { + netdev_for_each_mc_addr(ha, ndev) { /* Ran out of space in chip? */ BUG_ON(i == KS8695_NR_ADDRESSES); - low = (dmi->dmi_addr[2] << 24) | (dmi->dmi_addr[3] << 16) | - (dmi->dmi_addr[4] << 8) | (dmi->dmi_addr[5]); - high = (dmi->dmi_addr[0] << 8) | (dmi->dmi_addr[1]); + low = (ha->addr[2] << 24) | (ha->addr[3] << 16) | + (ha->addr[4] << 8) | (ha->addr[5]); + high = (ha->addr[0] << 8) | (ha->addr[1]); ks8695_writereg(ksp, KS8695_AAL_(i), low); ks8695_writereg(ksp, KS8695_AAH_(i), AAH_E | high); diff --git a/drivers/net/at1700.c b/drivers/net/at1700.c index 309843ab886..e2a549a60e2 100644 --- a/drivers/net/at1700.c +++ b/drivers/net/at1700.c @@ -848,12 +848,12 @@ set_rx_mode(struct net_device *dev) memset(mc_filter, 0x00, sizeof(mc_filter)); outb(1, ioaddr + RX_MODE); /* Ignore almost all multicasts. */ } else { - struct dev_mc_list *mclist; + struct netdev_hw_addr *ha; memset(mc_filter, 0, sizeof(mc_filter)); - netdev_for_each_mc_addr(mclist, dev) { + netdev_for_each_mc_addr(ha, dev) { unsigned int bit = - ether_crc_le(ETH_ALEN, mclist->dmi_addr) >> 26; + ether_crc_le(ETH_ALEN, ha->addr) >> 26; mc_filter[bit >> 3] |= (1 << bit); } outb(0x02, ioaddr + RX_MODE); /* Use normal mode. */ diff --git a/drivers/net/atl1c/atl1c_main.c b/drivers/net/atl1c/atl1c_main.c index a5508e1b261..3d7051135c3 100644 --- a/drivers/net/atl1c/atl1c_main.c +++ b/drivers/net/atl1c/atl1c_main.c @@ -354,7 +354,7 @@ static void atl1c_set_multi(struct net_device *netdev) { struct atl1c_adapter *adapter = netdev_priv(netdev); struct atl1c_hw *hw = &adapter->hw; - struct dev_mc_list *mc_ptr; + struct netdev_hw_addr *ha; u32 mac_ctrl_data; u32 hash_value; @@ -377,8 +377,8 @@ static void atl1c_set_multi(struct net_device *netdev) AT_WRITE_REG_ARRAY(hw, REG_RX_HASH_TABLE, 1, 0); /* comoute mc addresses' hash value ,and put it into hash table */ - netdev_for_each_mc_addr(mc_ptr, netdev) { - hash_value = atl1c_hash_mc_addr(hw, mc_ptr->dmi_addr); + netdev_for_each_mc_addr(ha, netdev) { + hash_value = atl1c_hash_mc_addr(hw, ha->addr); atl1c_hash_set(hw, hash_value); } } diff --git a/drivers/net/atl1e/atl1e_main.c b/drivers/net/atl1e/atl1e_main.c index 7231b577912..b6605d433e9 100644 --- a/drivers/net/atl1e/atl1e_main.c +++ b/drivers/net/atl1e/atl1e_main.c @@ -284,7 +284,7 @@ static void atl1e_set_multi(struct net_device *netdev) { struct atl1e_adapter *adapter = netdev_priv(netdev); struct atl1e_hw *hw = &adapter->hw; - struct dev_mc_list *mc_ptr; + struct netdev_hw_addr *ha; u32 mac_ctrl_data = 0; u32 hash_value; @@ -307,8 +307,8 @@ static void atl1e_set_multi(struct net_device *netdev) AT_WRITE_REG_ARRAY(hw, REG_RX_HASH_TABLE, 1, 0); /* comoute mc addresses' hash value ,and put it into hash table */ - netdev_for_each_mc_addr(mc_ptr, netdev) { - hash_value = atl1e_hash_mc_addr(hw, mc_ptr->dmi_addr); + netdev_for_each_mc_addr(ha, netdev) { + hash_value = atl1e_hash_mc_addr(hw, ha->addr); atl1e_hash_set(hw, hash_value); } } diff --git a/drivers/net/atlx/atl2.c b/drivers/net/atlx/atl2.c index 199f2c9ce74..078d9d1b427 100644 --- a/drivers/net/atlx/atl2.c +++ b/drivers/net/atlx/atl2.c @@ -135,7 +135,7 @@ static void atl2_set_multi(struct net_device *netdev) { struct atl2_adapter *adapter = netdev_priv(netdev); struct atl2_hw *hw = &adapter->hw; - struct dev_mc_list *mc_ptr; + struct netdev_hw_addr *ha; u32 rctl; u32 hash_value; @@ -157,8 +157,8 @@ static void atl2_set_multi(struct net_device *netdev) ATL2_WRITE_REG_ARRAY(hw, REG_RX_HASH_TABLE, 1, 0); /* comoute mc addresses' hash value ,and put it into hash table */ - netdev_for_each_mc_addr(mc_ptr, netdev) { - hash_value = atl2_hash_mc_addr(hw, mc_ptr->dmi_addr); + netdev_for_each_mc_addr(ha, netdev) { + hash_value = atl2_hash_mc_addr(hw, ha->addr); atl2_hash_set(hw, hash_value); } } diff --git a/drivers/net/atlx/atlx.c b/drivers/net/atlx/atlx.c index 72f3306352e..f979ea2d6d3 100644 --- a/drivers/net/atlx/atlx.c +++ b/drivers/net/atlx/atlx.c @@ -123,7 +123,7 @@ static void atlx_set_multi(struct net_device *netdev) { struct atlx_adapter *adapter = netdev_priv(netdev); struct atlx_hw *hw = &adapter->hw; - struct dev_mc_list *mc_ptr; + struct netdev_hw_addr *ha; u32 rctl; u32 hash_value; @@ -144,8 +144,8 @@ static void atlx_set_multi(struct net_device *netdev) iowrite32(0, (hw->hw_addr + REG_RX_HASH_TABLE) + (1 << 2)); /* compute mc addresses' hash value ,and put it into hash table */ - netdev_for_each_mc_addr(mc_ptr, netdev) { - hash_value = atlx_hash_mc_addr(hw, mc_ptr->dmi_addr); + netdev_for_each_mc_addr(ha, netdev) { + hash_value = atlx_hash_mc_addr(hw, ha->addr); atlx_hash_set(hw, hash_value); } } diff --git a/drivers/net/atp.c b/drivers/net/atp.c index 6ad16205dc1..0d730c8329d 100644 --- a/drivers/net/atp.c +++ b/drivers/net/atp.c @@ -883,11 +883,11 @@ static void set_rx_mode_8012(struct net_device *dev) memset(mc_filter, 0xff, sizeof(mc_filter)); new_mode = CMR2h_Normal; } else { - struct dev_mc_list *mclist; + struct netdev_hw_addr *ha; memset(mc_filter, 0, sizeof(mc_filter)); - netdev_for_each_mc_addr(mclist, dev) { - int filterbit = ether_crc_le(ETH_ALEN, mclist->dmi_addr) & 0x3f; + netdev_for_each_mc_addr(ha, dev) { + int filterbit = ether_crc_le(ETH_ALEN, ha->addr) & 0x3f; mc_filter[filterbit >> 5] |= 1 << (filterbit & 31); } new_mode = CMR2h_Normal; diff --git a/drivers/net/au1000_eth.c b/drivers/net/au1000_eth.c index 4da191b87b0..29631593cc2 100644 --- a/drivers/net/au1000_eth.c +++ b/drivers/net/au1000_eth.c @@ -957,12 +957,12 @@ static void au1000_multicast_list(struct net_device *dev) aup->mac->control &= ~MAC_PROMISCUOUS; printk(KERN_INFO "%s: Pass all multicast\n", dev->name); } else { - struct dev_mc_list *mclist; + struct netdev_hw_addr *ha; u32 mc_filter[2]; /* Multicast hash filter */ mc_filter[1] = mc_filter[0] = 0; - netdev_for_each_mc_addr(mclist, dev) - set_bit(ether_crc(ETH_ALEN, mclist->dmi_addr)>>26, + netdev_for_each_mc_addr(ha, dev) + set_bit(ether_crc(ETH_ALEN, ha->addr)>>26, (long *)mc_filter); aup->mac->multi_hash_high = mc_filter[1]; aup->mac->multi_hash_low = mc_filter[0]; diff --git a/drivers/net/b44.c b/drivers/net/b44.c index 332c6035628..b2c5fd7b63a 100644 --- a/drivers/net/b44.c +++ b/drivers/net/b44.c @@ -1680,15 +1680,15 @@ static struct net_device_stats *b44_get_stats(struct net_device *dev) static int __b44_load_mcast(struct b44 *bp, struct net_device *dev) { - struct dev_mc_list *mclist; + struct netdev_hw_addr *ha; int i, num_ents; num_ents = min_t(int, netdev_mc_count(dev), B44_MCAST_TABLE_SIZE); i = 0; - netdev_for_each_mc_addr(mclist, dev) { + netdev_for_each_mc_addr(ha, dev) { if (i == num_ents) break; - __b44_cam_write(bp, mclist->dmi_addr, i++ + 1); + __b44_cam_write(bp, ha->addr, i++ + 1); } return i+1; } diff --git a/drivers/net/bcm63xx_enet.c b/drivers/net/bcm63xx_enet.c index 37eb8021de1..51733404478 100644 --- a/drivers/net/bcm63xx_enet.c +++ b/drivers/net/bcm63xx_enet.c @@ -603,7 +603,7 @@ static int bcm_enet_set_mac_address(struct net_device *dev, void *p) static void bcm_enet_set_multicast_list(struct net_device *dev) { struct bcm_enet_priv *priv; - struct dev_mc_list *mc_list; + struct netdev_hw_addr *ha; u32 val; int i; @@ -631,14 +631,14 @@ static void bcm_enet_set_multicast_list(struct net_device *dev) } i = 0; - netdev_for_each_mc_addr(mc_list, dev) { + netdev_for_each_mc_addr(ha, dev) { u8 *dmi_addr; u32 tmp; if (i == 3) break; /* update perfect match registers */ - dmi_addr = mc_list->dmi_addr; + dmi_addr = ha->addr; tmp = (dmi_addr[2] << 24) | (dmi_addr[3] << 16) | (dmi_addr[4] << 8) | dmi_addr[5]; enet_writel(priv, tmp, ENET_PML_REG(i + 1)); diff --git a/drivers/net/benet/be_cmds.c b/drivers/net/benet/be_cmds.c index 9f53d9e86e2..61a9afdb83f 100644 --- a/drivers/net/benet/be_cmds.c +++ b/drivers/net/benet/be_cmds.c @@ -1159,13 +1159,13 @@ int be_cmd_multicast_set(struct be_adapter *adapter, u32 if_id, req->interface_id = if_id; if (netdev) { int i; - struct dev_mc_list *mc; + struct netdev_hw_addr *ha; req->num_mac = cpu_to_le16(netdev_mc_count(netdev)); i = 0; - netdev_for_each_mc_addr(mc, netdev) - memcpy(req->mac[i].byte, mc->dmi_addr, ETH_ALEN); + netdev_for_each_mc_addr(ha, netdev) + memcpy(req->mac[i].byte, ha->addr, ETH_ALEN); } else { req->promiscuous = 1; } diff --git a/drivers/net/bfin_mac.c b/drivers/net/bfin_mac.c index 587f93cf03f..c488cea8f45 100644 --- a/drivers/net/bfin_mac.c +++ b/drivers/net/bfin_mac.c @@ -812,14 +812,14 @@ static void bfin_mac_timeout(struct net_device *dev) static void bfin_mac_multicast_hash(struct net_device *dev) { u32 emac_hashhi, emac_hashlo; - struct dev_mc_list *dmi; + struct netdev_hw_addr *ha; char *addrs; u32 crc; emac_hashhi = emac_hashlo = 0; - netdev_for_each_mc_addr(dmi, dev) { - addrs = dmi->dmi_addr; + netdev_for_each_mc_addr(ha, dev) { + addrs = ha->addr; /* skip non-multicast addresses */ if (!(*addrs & 1)) diff --git a/drivers/net/bmac.c b/drivers/net/bmac.c index 119468e7632..1245e983576 100644 --- a/drivers/net/bmac.c +++ b/drivers/net/bmac.c @@ -971,7 +971,7 @@ bmac_remove_multi(struct net_device *dev, */ static void bmac_set_multicast(struct net_device *dev) { - struct dev_mc_list *dmi; + struct netdev_hw_addr *ha; struct bmac_data *bp = netdev_priv(dev); int num_addrs = netdev_mc_count(dev); unsigned short rx_cfg; @@ -1000,8 +1000,8 @@ static void bmac_set_multicast(struct net_device *dev) rx_cfg = bmac_rx_on(dev, 0, 0); XXDEBUG(("bmac: multi disabled, rx_cfg=%#08x\n", rx_cfg)); } else { - netdev_for_each_mc_addr(dmi, dev) - bmac_addhash(bp, dmi->dmi_addr); + netdev_for_each_mc_addr(ha, dev) + bmac_addhash(bp, ha->addr); bmac_update_hash_table_mask(dev, bp); rx_cfg = bmac_rx_on(dev, 1, 0); XXDEBUG(("bmac: multi enabled, rx_cfg=%#08x\n", rx_cfg)); @@ -1015,7 +1015,7 @@ static void bmac_set_multicast(struct net_device *dev) static void bmac_set_multicast(struct net_device *dev) { - struct dev_mc_list *dmi; + struct netdev_hw_addr *ha; char *addrs; int i; unsigned short rx_cfg; @@ -1039,8 +1039,8 @@ static void bmac_set_multicast(struct net_device *dev) for(i = 0; i < 4; i++) hash_table[i] = 0; - netdev_for_each_mc_addr(dmi, dev) { - addrs = dmi->dmi_addr; + netdev_for_each_mc_addr(ha, dev) { + addrs = ha->addr; if(!(*addrs & 1)) continue; diff --git a/drivers/net/bnx2.c b/drivers/net/bnx2.c index 381887ba677..0b69ffb7951 100644 --- a/drivers/net/bnx2.c +++ b/drivers/net/bnx2.c @@ -3544,7 +3544,6 @@ bnx2_set_rx_mode(struct net_device *dev) } else { /* Accept one or more multicast(s). */ - struct dev_mc_list *mclist; u32 mc_filter[NUM_MC_HASH_REGISTERS]; u32 regidx; u32 bit; @@ -3552,8 +3551,8 @@ bnx2_set_rx_mode(struct net_device *dev) memset(mc_filter, 0, 4 * NUM_MC_HASH_REGISTERS); - netdev_for_each_mc_addr(mclist, dev) { - crc = ether_crc_le(ETH_ALEN, mclist->dmi_addr); + netdev_for_each_mc_addr(ha, dev) { + crc = ether_crc_le(ETH_ALEN, ha->addr); bit = crc & 0xff; regidx = (bit & 0xe0) >> 5; bit &= 0x1f; diff --git a/drivers/net/bnx2x_main.c b/drivers/net/bnx2x_main.c index f4ea99d06c7..fa9275c2ef5 100644 --- a/drivers/net/bnx2x_main.c +++ b/drivers/net/bnx2x_main.c @@ -11496,21 +11496,21 @@ static void bnx2x_set_rx_mode(struct net_device *dev) else { /* some multicasts */ if (CHIP_IS_E1(bp)) { int i, old, offset; - struct dev_mc_list *mclist; + struct netdev_hw_addr *ha; struct mac_configuration_cmd *config = bnx2x_sp(bp, mcast_config); i = 0; - netdev_for_each_mc_addr(mclist, dev) { + netdev_for_each_mc_addr(ha, dev) { config->config_table[i]. cam_entry.msb_mac_addr = - swab16(*(u16 *)&mclist->dmi_addr[0]); + swab16(*(u16 *)&ha->addr[0]); config->config_table[i]. cam_entry.middle_mac_addr = - swab16(*(u16 *)&mclist->dmi_addr[2]); + swab16(*(u16 *)&ha->addr[2]); config->config_table[i]. cam_entry.lsb_mac_addr = - swab16(*(u16 *)&mclist->dmi_addr[4]); + swab16(*(u16 *)&ha->addr[4]); config->config_table[i].cam_entry.flags = cpu_to_le16(port); config->config_table[i]. @@ -11564,18 +11564,18 @@ static void bnx2x_set_rx_mode(struct net_device *dev) 0); } else { /* E1H */ /* Accept one or more multicasts */ - struct dev_mc_list *mclist; + struct netdev_hw_addr *ha; u32 mc_filter[MC_HASH_SIZE]; u32 crc, bit, regidx; int i; memset(mc_filter, 0, 4 * MC_HASH_SIZE); - netdev_for_each_mc_addr(mclist, dev) { + netdev_for_each_mc_addr(ha, dev) { DP(NETIF_MSG_IFUP, "Adding mcast MAC: %pM\n", - mclist->dmi_addr); + ha->addr); - crc = crc32c_le(0, mclist->dmi_addr, ETH_ALEN); + crc = crc32c_le(0, ha->addr, ETH_ALEN); bit = (crc >> 24) & 0xff; regidx = bit >> 5; bit &= 0x1f; diff --git a/drivers/net/bonding/bond_main.c b/drivers/net/bonding/bond_main.c index d6ae63b2cf0..22682f1c847 100644 --- a/drivers/net/bonding/bond_main.c +++ b/drivers/net/bonding/bond_main.c @@ -761,32 +761,6 @@ static int bond_check_dev_link(struct bonding *bond, /*----------------------------- Multicast list ------------------------------*/ -/* - * Returns 0 if dmi1 and dmi2 are the same, non-0 otherwise - */ -static inline int bond_is_dmi_same(const struct dev_mc_list *dmi1, - const struct dev_mc_list *dmi2) -{ - return memcmp(dmi1->dmi_addr, dmi2->dmi_addr, dmi1->dmi_addrlen) == 0 && - dmi1->dmi_addrlen == dmi2->dmi_addrlen; -} - -/* - * returns dmi entry if found, NULL otherwise - */ -static struct dev_mc_list *bond_mc_list_find_dmi(struct dev_mc_list *dmi, - struct dev_mc_list *mc_list) -{ - struct dev_mc_list *idmi; - - for (idmi = mc_list; idmi; idmi = idmi->next) { - if (bond_is_dmi_same(dmi, idmi)) - return idmi; - } - - return NULL; -} - /* * Push the promiscuity flag down to appropriate slaves */ @@ -839,18 +813,18 @@ static int bond_set_allmulti(struct bonding *bond, int inc) * Add a Multicast address to slaves * according to mode */ -static void bond_mc_add(struct bonding *bond, void *addr, int alen) +static void bond_mc_add(struct bonding *bond, void *addr) { if (USES_PRIMARY(bond->params.mode)) { /* write lock already acquired */ if (bond->curr_active_slave) - dev_mc_add(bond->curr_active_slave->dev, addr, alen, 0); + dev_mc_add(bond->curr_active_slave->dev, addr); } else { struct slave *slave; int i; bond_for_each_slave(bond, slave, i) - dev_mc_add(slave->dev, addr, alen, 0); + dev_mc_add(slave->dev, addr); } } @@ -858,18 +832,17 @@ static void bond_mc_add(struct bonding *bond, void *addr, int alen) * Remove a multicast address from slave * according to mode */ -static void bond_mc_delete(struct bonding *bond, void *addr, int alen) +static void bond_mc_del(struct bonding *bond, void *addr) { if (USES_PRIMARY(bond->params.mode)) { /* write lock already acquired */ if (bond->curr_active_slave) - dev_mc_delete(bond->curr_active_slave->dev, addr, - alen, 0); + dev_mc_del(bond->curr_active_slave->dev, addr); } else { struct slave *slave; int i; bond_for_each_slave(bond, slave, i) { - dev_mc_delete(slave->dev, addr, alen, 0); + dev_mc_del(slave->dev, addr); } } } @@ -895,50 +868,6 @@ static void bond_resend_igmp_join_requests(struct bonding *bond) rcu_read_unlock(); } -/* - * Totally destroys the mc_list in bond - */ -static void bond_mc_list_destroy(struct bonding *bond) -{ - struct dev_mc_list *dmi; - - dmi = bond->mc_list; - while (dmi) { - bond->mc_list = dmi->next; - kfree(dmi); - dmi = bond->mc_list; - } - - bond->mc_list = NULL; -} - -/* - * Copy all the Multicast addresses from src to the bonding device dst - */ -static int bond_mc_list_copy(struct dev_mc_list *mc_list, struct bonding *bond, - gfp_t gfp_flag) -{ - struct dev_mc_list *dmi, *new_dmi; - - for (dmi = mc_list; dmi; dmi = dmi->next) { - new_dmi = kmalloc(sizeof(struct dev_mc_list), gfp_flag); - - if (!new_dmi) { - /* FIXME: Potential memory leak !!! */ - return -ENOMEM; - } - - new_dmi->next = bond->mc_list; - bond->mc_list = new_dmi; - new_dmi->dmi_addrlen = dmi->dmi_addrlen; - memcpy(new_dmi->dmi_addr, dmi->dmi_addr, dmi->dmi_addrlen); - new_dmi->dmi_users = dmi->dmi_users; - new_dmi->dmi_gusers = dmi->dmi_gusers; - } - - return 0; -} - /* * flush all members of flush->mc_list from device dev->mc_list */ @@ -946,16 +875,16 @@ static void bond_mc_list_flush(struct net_device *bond_dev, struct net_device *slave_dev) { struct bonding *bond = netdev_priv(bond_dev); - struct dev_mc_list *dmi; + struct netdev_hw_addr *ha; - for (dmi = bond_dev->mc_list; dmi; dmi = dmi->next) - dev_mc_delete(slave_dev, dmi->dmi_addr, dmi->dmi_addrlen, 0); + netdev_for_each_mc_addr(ha, bond_dev) + dev_mc_del(slave_dev, ha->addr); if (bond->params.mode == BOND_MODE_8023AD) { /* del lacpdu mc addr from mc list */ u8 lacpdu_multicast[ETH_ALEN] = MULTICAST_LACPDU_ADDR; - dev_mc_delete(slave_dev, lacpdu_multicast, ETH_ALEN, 0); + dev_mc_del(slave_dev, lacpdu_multicast); } } @@ -969,7 +898,7 @@ static void bond_mc_list_flush(struct net_device *bond_dev, static void bond_mc_swap(struct bonding *bond, struct slave *new_active, struct slave *old_active) { - struct dev_mc_list *dmi; + struct netdev_hw_addr *ha; if (!USES_PRIMARY(bond->params.mode)) /* nothing to do - mc list is already up-to-date on @@ -984,9 +913,8 @@ static void bond_mc_swap(struct bonding *bond, struct slave *new_active, if (bond->dev->flags & IFF_ALLMULTI) dev_set_allmulti(old_active->dev, -1); - for (dmi = bond->dev->mc_list; dmi; dmi = dmi->next) - dev_mc_delete(old_active->dev, dmi->dmi_addr, - dmi->dmi_addrlen, 0); + netdev_for_each_mc_addr(ha, bond->dev) + dev_mc_del(old_active->dev, ha->addr); } if (new_active) { @@ -997,9 +925,8 @@ static void bond_mc_swap(struct bonding *bond, struct slave *new_active, if (bond->dev->flags & IFF_ALLMULTI) dev_set_allmulti(new_active->dev, 1); - for (dmi = bond->dev->mc_list; dmi; dmi = dmi->next) - dev_mc_add(new_active->dev, dmi->dmi_addr, - dmi->dmi_addrlen, 0); + netdev_for_each_mc_addr(ha, bond->dev) + dev_mc_add(new_active->dev, ha->addr); bond_resend_igmp_join_requests(bond); } } @@ -1406,7 +1333,7 @@ int bond_enslave(struct net_device *bond_dev, struct net_device *slave_dev) struct bonding *bond = netdev_priv(bond_dev); const struct net_device_ops *slave_ops = slave_dev->netdev_ops; struct slave *new_slave = NULL; - struct dev_mc_list *dmi; + struct netdev_hw_addr *ha; struct sockaddr addr; int link_reporting; int old_features = bond_dev->features; @@ -1492,7 +1419,7 @@ int bond_enslave(struct net_device *bond_dev, struct net_device *slave_dev) /* Flush unicast and multicast addresses */ dev_uc_flush(bond_dev); - dev_addr_discard(bond_dev); + dev_mc_flush(bond_dev); if (slave_dev->type != ARPHRD_ETHER) bond_setup_by_slave(bond_dev, slave_dev); @@ -1601,9 +1528,8 @@ int bond_enslave(struct net_device *bond_dev, struct net_device *slave_dev) netif_addr_lock_bh(bond_dev); /* upload master's mc_list to new slave */ - for (dmi = bond_dev->mc_list; dmi; dmi = dmi->next) - dev_mc_add(slave_dev, dmi->dmi_addr, - dmi->dmi_addrlen, 0); + netdev_for_each_mc_addr(ha, bond_dev) + dev_mc_add(slave_dev, ha->addr); netif_addr_unlock_bh(bond_dev); } @@ -1611,7 +1537,7 @@ int bond_enslave(struct net_device *bond_dev, struct net_device *slave_dev) /* add lacpdu mc addr to mc list */ u8 lacpdu_multicast[ETH_ALEN] = MULTICAST_LACPDU_ADDR; - dev_mc_add(slave_dev, lacpdu_multicast, ETH_ALEN, 0); + dev_mc_add(slave_dev, lacpdu_multicast); } bond_add_vlans_on_slave(bond, slave_dev); @@ -3913,10 +3839,24 @@ static int bond_do_ioctl(struct net_device *bond_dev, struct ifreq *ifr, int cmd return res; } +static bool bond_addr_in_mc_list(unsigned char *addr, + struct netdev_hw_addr_list *list, + int addrlen) +{ + struct netdev_hw_addr *ha; + + netdev_hw_addr_list_for_each(ha, list) + if (!memcmp(ha->addr, addr, addrlen)) + return true; + + return false; +} + static void bond_set_multicast_list(struct net_device *bond_dev) { struct bonding *bond = netdev_priv(bond_dev); - struct dev_mc_list *dmi; + struct netdev_hw_addr *ha; + bool found; /* * Do promisc before checking multicast_mode @@ -3951,20 +3891,25 @@ static void bond_set_multicast_list(struct net_device *bond_dev) bond->flags = bond_dev->flags; /* looking for addresses to add to slaves' mc list */ - for (dmi = bond_dev->mc_list; dmi; dmi = dmi->next) { - if (!bond_mc_list_find_dmi(dmi, bond->mc_list)) - bond_mc_add(bond, dmi->dmi_addr, dmi->dmi_addrlen); + netdev_for_each_mc_addr(ha, bond_dev) { + found = bond_addr_in_mc_list(ha->addr, &bond->mc_list, + bond_dev->addr_len); + if (!found) + bond_mc_add(bond, ha->addr); } /* looking for addresses to delete from slaves' list */ - for (dmi = bond->mc_list; dmi; dmi = dmi->next) { - if (!bond_mc_list_find_dmi(dmi, bond_dev->mc_list)) - bond_mc_delete(bond, dmi->dmi_addr, dmi->dmi_addrlen); + netdev_hw_addr_list_for_each(ha, &bond->mc_list) { + found = bond_addr_in_mc_list(ha->addr, &bond_dev->mc, + bond_dev->addr_len); + if (!found) + bond_mc_del(bond, ha->addr); } /* save master's multicast list */ - bond_mc_list_destroy(bond); - bond_mc_list_copy(bond_dev->mc_list, bond, GFP_ATOMIC); + __hw_addr_flush(&bond->mc_list); + __hw_addr_add_multiple(&bond->mc_list, &bond_dev->mc, + bond_dev->addr_len, NETDEV_HW_ADDR_T_MULTICAST); read_unlock(&bond->lock); } @@ -4534,9 +4479,7 @@ static void bond_uninit(struct net_device *bond_dev) if (bond->wq) destroy_workqueue(bond->wq); - netif_addr_lock_bh(bond_dev); - bond_mc_list_destroy(bond); - netif_addr_unlock_bh(bond_dev); + __hw_addr_flush(&bond->mc_list); } /*------------------------- Module initialization ---------------------------*/ @@ -4908,6 +4851,8 @@ static int bond_init(struct net_device *bond_dev) list_add_tail(&bond->bond_list, &bn->dev_list); bond_prepare_sysfs_group(bond); + + __hw_addr_init(&bond->mc_list); return 0; } diff --git a/drivers/net/bonding/bonding.h b/drivers/net/bonding/bonding.h index 257a7a4dfce..2aa33672059 100644 --- a/drivers/net/bonding/bonding.h +++ b/drivers/net/bonding/bonding.h @@ -202,7 +202,7 @@ struct bonding { char proc_file_name[IFNAMSIZ]; #endif /* CONFIG_PROC_FS */ struct list_head bond_list; - struct dev_mc_list *mc_list; + struct netdev_hw_addr_list mc_list; int (*xmit_hash_policy)(struct sk_buff *, int); __be32 master_ip; u16 flags; diff --git a/drivers/net/cassini.c b/drivers/net/cassini.c index 9bd155e4111..bd857a20a75 100644 --- a/drivers/net/cassini.c +++ b/drivers/net/cassini.c @@ -2957,20 +2957,20 @@ static void cas_process_mc_list(struct cas *cp) { u16 hash_table[16]; u32 crc; - struct dev_mc_list *dmi; + struct netdev_hw_addr *ha; int i = 1; memset(hash_table, 0, sizeof(hash_table)); - netdev_for_each_mc_addr(dmi, cp->dev) { + netdev_for_each_mc_addr(ha, cp->dev) { if (i <= CAS_MC_EXACT_MATCH_SIZE) { /* use the alternate mac address registers for the * first 15 multicast addresses */ - writel((dmi->dmi_addr[4] << 8) | dmi->dmi_addr[5], + writel((ha->addr[4] << 8) | ha->addr[5], cp->regs + REG_MAC_ADDRN(i*3 + 0)); - writel((dmi->dmi_addr[2] << 8) | dmi->dmi_addr[3], + writel((ha->addr[2] << 8) | ha->addr[3], cp->regs + REG_MAC_ADDRN(i*3 + 1)); - writel((dmi->dmi_addr[0] << 8) | dmi->dmi_addr[1], + writel((ha->addr[0] << 8) | ha->addr[1], cp->regs + REG_MAC_ADDRN(i*3 + 2)); i++; } @@ -2978,7 +2978,7 @@ static void cas_process_mc_list(struct cas *cp) /* use hw hash table for the next series of * multicast addresses */ - crc = ether_crc_le(ETH_ALEN, dmi->dmi_addr); + crc = ether_crc_le(ETH_ALEN, ha->addr); crc >>= 24; hash_table[crc >> 4] |= 1 << (15 - (crc & 0xf)); } diff --git a/drivers/net/chelsio/pm3393.c b/drivers/net/chelsio/pm3393.c index a6eb30a6e2b..13fd9628db1 100644 --- a/drivers/net/chelsio/pm3393.c +++ b/drivers/net/chelsio/pm3393.c @@ -376,12 +376,13 @@ static int pm3393_set_rx_mode(struct cmac *cmac, struct t1_rx_mode *rm) rx_mode |= SUNI1x10GEXP_BITMSK_RXXG_MHASH_EN; } else if (t1_rx_mode_mc_cnt(rm)) { /* Accept one or more multicast(s). */ - struct dev_mc_list *dmi; + struct netdev_hw_addr *ha; int bit; u16 mc_filter[4] = { 0, }; - netdev_for_each_mc_addr(dmi, t1_get_netdev(rm)) { - bit = (ether_crc(ETH_ALEN, dmi->dmi_addr) >> 23) & 0x3f; /* bit[23:28] */ + netdev_for_each_mc_addr(ha, t1_get_netdev(rm)) { + /* bit[23:28] */ + bit = (ether_crc(ETH_ALEN, ha->addr) >> 23) & 0x3f; mc_filter[bit >> 4] |= 1 << (bit & 0xf); } pmwrite(cmac, SUNI1x10GEXP_REG_RXXG_MULTICAST_HASH_LOW, mc_filter[0]); diff --git a/drivers/net/cpmac.c b/drivers/net/cpmac.c index 60777fd90b3..bdfff784645 100644 --- a/drivers/net/cpmac.c +++ b/drivers/net/cpmac.c @@ -328,7 +328,7 @@ static int cpmac_config(struct net_device *dev, struct ifmap *map) static void cpmac_set_multicast_list(struct net_device *dev) { - struct dev_mc_list *iter; + struct netdev_hw_addr *ha; u8 tmp; u32 mbp, bit, hash[2] = { 0, }; struct cpmac_priv *priv = netdev_priv(dev); @@ -348,19 +348,19 @@ static void cpmac_set_multicast_list(struct net_device *dev) * cpmac uses some strange mac address hashing * (not crc32) */ - netdev_for_each_mc_addr(iter, dev) { + netdev_for_each_mc_addr(ha, dev) { bit = 0; - tmp = iter->dmi_addr[0]; + tmp = ha->addr[0]; bit ^= (tmp >> 2) ^ (tmp << 4); - tmp = iter->dmi_addr[1]; + tmp = ha->addr[1]; bit ^= (tmp >> 4) ^ (tmp << 2); - tmp = iter->dmi_addr[2]; + tmp = ha->addr[2]; bit ^= (tmp >> 6) ^ tmp; - tmp = iter->dmi_addr[3]; + tmp = ha->addr[3]; bit ^= (tmp >> 2) ^ (tmp << 4); - tmp = iter->dmi_addr[4]; + tmp = ha->addr[4]; bit ^= (tmp >> 4) ^ (tmp << 2); - tmp = iter->dmi_addr[5]; + tmp = ha->addr[5]; bit ^= (tmp >> 6) ^ tmp; bit &= 0x3f; hash[bit / 32] |= 1 << (bit % 32); diff --git a/drivers/net/cris/eth_v10.c b/drivers/net/cris/eth_v10.c index dd24aadb778..59110bc119a 100644 --- a/drivers/net/cris/eth_v10.c +++ b/drivers/net/cris/eth_v10.c @@ -1596,16 +1596,16 @@ set_multicast_list(struct net_device *dev) } else { /* MC mode, receive normal and MC packets */ char hash_ix; - struct dev_mc_list *dmi; + struct netdev_hw_addr *ha; char *baddr; lo_bits = 0x00000000ul; hi_bits = 0x00000000ul; - netdev_for_each_mc_addr(dmi, dev) { + netdev_for_each_mc_addr(ha, dev) { /* Calculate the hash index for the GA registers */ hash_ix = 0; - baddr = dmi->dmi_addr; + baddr = ha->addr; hash_ix ^= (*baddr) & 0x3f; hash_ix ^= ((*baddr) >> 6) & 0x03; ++baddr; diff --git a/drivers/net/cxgb3/xgmac.c b/drivers/net/cxgb3/xgmac.c index c142a2132e9..3af19a55037 100644 --- a/drivers/net/cxgb3/xgmac.c +++ b/drivers/net/cxgb3/xgmac.c @@ -311,16 +311,16 @@ int t3_mac_set_rx_mode(struct cmac *mac, struct net_device *dev) if (dev->flags & IFF_ALLMULTI) hash_lo = hash_hi = 0xffffffff; else { - struct dev_mc_list *dmi; + struct netdev_hw_addr *ha; int exact_addr_idx = mac->nucast; hash_lo = hash_hi = 0; - netdev_for_each_mc_addr(dmi, dev) + netdev_for_each_mc_addr(ha, dev) if (exact_addr_idx < EXACT_ADDR_FILTERS) set_addr_filter(mac, exact_addr_idx++, - dmi->dmi_addr); + ha->addr); else { - int hash = hash_hw_addr(dmi->dmi_addr); + int hash = hash_hw_addr(ha->addr); if (hash < 32) hash_lo |= (1 << hash); diff --git a/drivers/net/davinci_emac.c b/drivers/net/davinci_emac.c index 2b8edd2efbf..1f9df5c6a75 100644 --- a/drivers/net/davinci_emac.c +++ b/drivers/net/davinci_emac.c @@ -952,13 +952,14 @@ static void emac_dev_mcast_set(struct net_device *ndev) emac_add_mcast(priv, EMAC_ALL_MULTI_SET, NULL); } if (!netdev_mc_empty(ndev)) { - struct dev_mc_list *mc_ptr; + struct netdev_hw_addr *ha; + mbp_enable = (mbp_enable | EMAC_MBP_RXMCAST); emac_add_mcast(priv, EMAC_ALL_MULTI_CLR, NULL); /* program multicast address list into EMAC hardware */ - netdev_for_each_mc_addr(mc_ptr, ndev) { + netdev_for_each_mc_addr(ha, ndev) { emac_add_mcast(priv, EMAC_MULTICAST_ADD, - (u8 *) mc_ptr->dmi_addr); + (u8 *) ha->addr); } } else { mbp_enable = (mbp_enable & ~EMAC_MBP_RXMCAST); diff --git a/drivers/net/declance.c b/drivers/net/declance.c index 8cf3cc6f20e..fb3f0984c28 100644 --- a/drivers/net/declance.c +++ b/drivers/net/declance.c @@ -940,7 +940,7 @@ static void lance_load_multicast(struct net_device *dev) { struct lance_private *lp = netdev_priv(dev); volatile u16 *ib = (volatile u16 *)dev->mem_start; - struct dev_mc_list *dmi; + struct netdev_hw_addr *ha; char *addrs; u32 crc; @@ -959,8 +959,8 @@ static void lance_load_multicast(struct net_device *dev) *lib_ptr(ib, filter[3], lp->type) = 0; /* Add addresses */ - netdev_for_each_mc_addr(dmi, dev) { - addrs = dmi->dmi_addr; + netdev_for_each_mc_addr(ha, dev) { + addrs = ha->addr; /* multicast address? */ if (!(*addrs & 1)) diff --git a/drivers/net/defxx.c b/drivers/net/defxx.c index ed53a8d45f4..e5667c55844 100644 --- a/drivers/net/defxx.c +++ b/drivers/net/defxx.c @@ -2195,7 +2195,7 @@ static void dfx_ctl_set_multicast_list(struct net_device *dev) { DFX_board_t *bp = netdev_priv(dev); int i; /* used as index in for loop */ - struct dev_mc_list *dmi; /* ptr to multicast addr entry */ + struct netdev_hw_addr *ha; /* Enable LLC frame promiscuous mode, if necessary */ @@ -2241,9 +2241,9 @@ static void dfx_ctl_set_multicast_list(struct net_device *dev) /* Copy addresses to multicast address table, then update adapter CAM */ i = 0; - netdev_for_each_mc_addr(dmi, dev) + netdev_for_each_mc_addr(ha, dev) memcpy(&bp->mc_table[i++ * FDDI_K_ALEN], - dmi->dmi_addr, FDDI_K_ALEN); + ha->addr, FDDI_K_ALEN); if (dfx_ctl_update_cam(bp) != DFX_K_SUCCESS) { diff --git a/drivers/net/depca.c b/drivers/net/depca.c index 744c1928dfc..a88300a0d1e 100644 --- a/drivers/net/depca.c +++ b/drivers/net/depca.c @@ -1272,7 +1272,7 @@ static void set_multicast_list(struct net_device *dev) static void SetMulticastFilter(struct net_device *dev) { struct depca_private *lp = netdev_priv(dev); - struct dev_mc_list *dmi; + struct netdev_hw_addr *ha; char *addrs; int i, j, bit, byte; u16 hashcode; @@ -1287,8 +1287,8 @@ static void SetMulticastFilter(struct net_device *dev) lp->init_block.mcast_table[i] = 0; } /* Add multicast addresses */ - netdev_for_each_mc_addr(dmi, dev) { - addrs = dmi->dmi_addr; + netdev_for_each_mc_addr(ha, dev) { + addrs = ha->addr; if ((*addrs & 0x01) == 1) { /* multicast address? */ crc = ether_crc(ETH_ALEN, addrs); hashcode = (crc & 1); /* hashcode is 6 LSb of CRC ... */ diff --git a/drivers/net/dl2k.c b/drivers/net/dl2k.c index b05bad82982..6579225dbd9 100644 --- a/drivers/net/dl2k.c +++ b/drivers/net/dl2k.c @@ -1132,14 +1132,14 @@ set_multicast (struct net_device *dev) /* Receive broadcast and multicast frames */ rx_mode = ReceiveBroadcast | ReceiveMulticast | ReceiveUnicast; } else if (!netdev_mc_empty(dev)) { - struct dev_mc_list *mclist; + struct netdev_hw_addr *ha; /* Receive broadcast frames and multicast frames filtering by Hashtable */ rx_mode = ReceiveBroadcast | ReceiveMulticastHash | ReceiveUnicast; - netdev_for_each_mc_addr(mclist, dev) { + netdev_for_each_mc_addr(ha, dev) { int bit, index = 0; - int crc = ether_crc_le (ETH_ALEN, mclist->dmi_addr); + int crc = ether_crc_le(ETH_ALEN, ha->addr); /* The inverted high significant 6 bits of CRC are used as an index to hashtable */ for (bit = 0; bit < 6; bit++) diff --git a/drivers/net/dm9000.c b/drivers/net/dm9000.c index 1c67f1138ca..989f2beb123 100644 --- a/drivers/net/dm9000.c +++ b/drivers/net/dm9000.c @@ -724,7 +724,7 @@ static void dm9000_hash_table(struct net_device *dev) { board_info_t *db = netdev_priv(dev); - struct dev_mc_list *mcptr; + struct netdev_hw_addr *ha; int i, oft; u32 hash_val; u16 hash_table[4]; @@ -752,8 +752,8 @@ dm9000_hash_table(struct net_device *dev) rcr |= RCR_ALL; /* the multicast address in Hash Table : 64 bits */ - netdev_for_each_mc_addr(mcptr, dev) { - hash_val = ether_crc_le(6, mcptr->dmi_addr) & 0x3f; + netdev_for_each_mc_addr(ha, dev) { + hash_val = ether_crc_le(6, ha->addr) & 0x3f; hash_table[hash_val / 16] |= (u16) 1 << (hash_val % 16); } diff --git a/drivers/net/e100.c b/drivers/net/e100.c index c0cd5765668..3e8d0005540 100644 --- a/drivers/net/e100.c +++ b/drivers/net/e100.c @@ -1545,16 +1545,16 @@ static int e100_hw_init(struct nic *nic) static void e100_multi(struct nic *nic, struct cb *cb, struct sk_buff *skb) { struct net_device *netdev = nic->netdev; - struct dev_mc_list *list; + struct netdev_hw_addr *ha; u16 i, count = min(netdev_mc_count(netdev), E100_MAX_MULTICAST_ADDRS); cb->command = cpu_to_le16(cb_multi); cb->u.multi.count = cpu_to_le16(count * ETH_ALEN); i = 0; - netdev_for_each_mc_addr(list, netdev) { + netdev_for_each_mc_addr(ha, netdev) { if (i == count) break; - memcpy(&cb->u.multi.addr[i++ * ETH_ALEN], &list->dmi_addr, + memcpy(&cb->u.multi.addr[i++ * ETH_ALEN], &ha->addr, ETH_ALEN); } } diff --git a/drivers/net/e1000/e1000_main.c b/drivers/net/e1000/e1000_main.c index 8be6faee43e..41330349b07 100644 --- a/drivers/net/e1000/e1000_main.c +++ b/drivers/net/e1000/e1000_main.c @@ -2101,7 +2101,6 @@ static void e1000_set_rx_mode(struct net_device *netdev) struct e1000_hw *hw = &adapter->hw; struct netdev_hw_addr *ha; bool use_uc = false; - struct dev_addr_list *mc_ptr; u32 rctl; u32 hash_value; int i, rar_entries = E1000_RAR_ENTRIES; @@ -2161,17 +2160,17 @@ static void e1000_set_rx_mode(struct net_device *netdev) WARN_ON(i == rar_entries); - netdev_for_each_mc_addr(mc_ptr, netdev) { + netdev_for_each_mc_addr(ha, netdev) { if (i == rar_entries) { /* load any remaining addresses into the hash table */ u32 hash_reg, hash_bit, mta; - hash_value = e1000_hash_mc_addr(hw, mc_ptr->da_addr); + hash_value = e1000_hash_mc_addr(hw, ha->addr); hash_reg = (hash_value >> 5) & 0x7F; hash_bit = hash_value & 0x1F; mta = (1 << hash_bit); mcarray[hash_reg] |= mta; } else { - e1000_rar_set(hw, mc_ptr->da_addr, i++); + e1000_rar_set(hw, ha->addr, i++); } } diff --git a/drivers/net/e1000e/netdev.c b/drivers/net/e1000e/netdev.c index 5304959ae1f..02f7d20f3c8 100644 --- a/drivers/net/e1000e/netdev.c +++ b/drivers/net/e1000e/netdev.c @@ -2567,7 +2567,7 @@ static void e1000_set_multi(struct net_device *netdev) { struct e1000_adapter *adapter = netdev_priv(netdev); struct e1000_hw *hw = &adapter->hw; - struct dev_mc_list *mc_ptr; + struct netdev_hw_addr *ha; u8 *mta_list; u32 rctl; int i; @@ -2599,9 +2599,8 @@ static void e1000_set_multi(struct net_device *netdev) /* prepare a packed array of only addresses. */ i = 0; - netdev_for_each_mc_addr(mc_ptr, netdev) - memcpy(mta_list + (i++ * ETH_ALEN), - mc_ptr->dmi_addr, ETH_ALEN); + netdev_for_each_mc_addr(ha, netdev) + memcpy(mta_list + (i++ * ETH_ALEN), ha->addr, ETH_ALEN); e1000_update_mc_addr_list(hw, mta_list, i); kfree(mta_list); diff --git a/drivers/net/eepro.c b/drivers/net/eepro.c index 15d6266b80c..b01e6997403 100644 --- a/drivers/net/eepro.c +++ b/drivers/net/eepro.c @@ -1287,7 +1287,7 @@ set_multicast_list(struct net_device *dev) struct eepro_local *lp = netdev_priv(dev); short ioaddr = dev->base_addr; unsigned short mode; - struct dev_mc_list *dmi; + struct netdev_hw_addr *ha; int mc_count = netdev_mc_count(dev); if (dev->flags&(IFF_ALLMULTI|IFF_PROMISC) || mc_count > 63) @@ -1332,8 +1332,8 @@ set_multicast_list(struct net_device *dev) outw(0, ioaddr + IO_PORT); outw(6 * (mc_count + 1), ioaddr + IO_PORT); - netdev_for_each_mc_addr(dmi, dev) { - eaddrs = (unsigned short *) dmi->dmi_addr; + netdev_for_each_mc_addr(ha, dev) { + eaddrs = (unsigned short *) ha->addr; outw(*eaddrs++, ioaddr + IO_PORT); outw(*eaddrs++, ioaddr + IO_PORT); outw(*eaddrs++, ioaddr + IO_PORT); diff --git a/drivers/net/eexpress.c b/drivers/net/eexpress.c index 7013dc8a6cb..b3882fd8db6 100644 --- a/drivers/net/eexpress.c +++ b/drivers/net/eexpress.c @@ -1576,7 +1576,7 @@ static void eexp_hw_init586(struct net_device *dev) static void eexp_setup_filter(struct net_device *dev) { - struct dev_mc_list *dmi; + struct netdev_hw_addr *ha; unsigned short ioaddr = dev->base_addr; int count = netdev_mc_count(dev); int i; @@ -1589,8 +1589,8 @@ static void eexp_setup_filter(struct net_device *dev) outw(CONF_NR_MULTICAST & ~31, ioaddr+SM_PTR); outw(6*count, ioaddr+SHADOW(CONF_NR_MULTICAST)); i = 0; - netdev_for_each_mc_addr(dmi, dev) { - unsigned short *data = (unsigned short *) dmi->dmi_addr; + netdev_for_each_mc_addr(ha, dev) { + unsigned short *data = (unsigned short *) ha->addr; if (i == count) break; diff --git a/drivers/net/ehea/ehea_main.c b/drivers/net/ehea/ehea_main.c index b004eaba3d7..b97411aaa77 100644 --- a/drivers/net/ehea/ehea_main.c +++ b/drivers/net/ehea/ehea_main.c @@ -1966,7 +1966,7 @@ static void ehea_add_multicast_entry(struct ehea_port *port, u8 *mc_mac_addr) static void ehea_set_multicast_list(struct net_device *dev) { struct ehea_port *port = netdev_priv(dev); - struct dev_mc_list *k_mcl_entry; + struct netdev_hw_addr *ha; int ret; if (dev->flags & IFF_PROMISC) { @@ -1997,8 +1997,8 @@ static void ehea_set_multicast_list(struct net_device *dev) goto out; } - netdev_for_each_mc_addr(k_mcl_entry, dev) - ehea_add_multicast_entry(port, k_mcl_entry->dmi_addr); + netdev_for_each_mc_addr(ha, dev) + ehea_add_multicast_entry(port, ha->addr); } out: diff --git a/drivers/net/enic/enic_main.c b/drivers/net/enic/enic_main.c index 6d70c349c95..1232887c243 100644 --- a/drivers/net/enic/enic_main.c +++ b/drivers/net/enic/enic_main.c @@ -822,7 +822,7 @@ static int enic_set_mac_addr(struct net_device *netdev, char *addr) static void enic_set_multicast_list(struct net_device *netdev) { struct enic *enic = netdev_priv(netdev); - struct dev_mc_list *list; + struct netdev_hw_addr *ha; int directed = 1; int multicast = (netdev->flags & IFF_MULTICAST) ? 1 : 0; int broadcast = (netdev->flags & IFF_BROADCAST) ? 1 : 0; @@ -852,10 +852,10 @@ static void enic_set_multicast_list(struct net_device *netdev) */ i = 0; - netdev_for_each_mc_addr(list, netdev) { + netdev_for_each_mc_addr(ha, netdev) { if (i == mc_count) break; - memcpy(mc_addr[i++], list->dmi_addr, ETH_ALEN); + memcpy(mc_addr[i++], ha->addr, ETH_ALEN); } for (i = 0; i < enic->mc_count; i++) { diff --git a/drivers/net/epic100.c b/drivers/net/epic100.c index 39c271b6be4..f6584a1ad3b 100644 --- a/drivers/net/epic100.c +++ b/drivers/net/epic100.c @@ -1400,12 +1400,12 @@ static void set_rx_mode(struct net_device *dev) outl(0x0004, ioaddr + RxCtrl); return; } else { /* Never executed, for now. */ - struct dev_mc_list *mclist; + struct netdev_hw_addr *ha; memset(mc_filter, 0, sizeof(mc_filter)); - netdev_for_each_mc_addr(mclist, dev) { + netdev_for_each_mc_addr(ha, dev) { unsigned int bit_nr = - ether_crc_le(ETH_ALEN, mclist->dmi_addr) & 0x3f; + ether_crc_le(ETH_ALEN, ha->addr) & 0x3f; mc_filter[bit_nr >> 3] |= (1 << bit_nr); } } diff --git a/drivers/net/ethoc.c b/drivers/net/ethoc.c index 209742304e2..f6be5aeaf94 100644 --- a/drivers/net/ethoc.c +++ b/drivers/net/ethoc.c @@ -755,7 +755,7 @@ static void ethoc_set_multicast_list(struct net_device *dev) { struct ethoc *priv = netdev_priv(dev); u32 mode = ethoc_read(priv, MODER); - struct dev_mc_list *mc; + struct netdev_hw_addr *ha; u32 hash[2] = { 0, 0 }; /* set loopback mode if requested */ @@ -783,8 +783,8 @@ static void ethoc_set_multicast_list(struct net_device *dev) hash[0] = 0xffffffff; hash[1] = 0xffffffff; } else { - netdev_for_each_mc_addr(mc, dev) { - u32 crc = ether_crc(ETH_ALEN, mc->dmi_addr); + netdev_for_each_mc_addr(ha, dev) { + u32 crc = ether_crc(ETH_ALEN, ha->addr); int bit = (crc >> 26) & 0x3f; hash[bit >> 5] |= 1 << (bit & 0x1f); } diff --git a/drivers/net/ewrk3.c b/drivers/net/ewrk3.c index a2bade58688..11ba70f4997 100644 --- a/drivers/net/ewrk3.c +++ b/drivers/net/ewrk3.c @@ -1169,7 +1169,7 @@ static void set_multicast_list(struct net_device *dev) static void SetMulticastFilter(struct net_device *dev) { struct ewrk3_private *lp = netdev_priv(dev); - struct dev_mc_list *dmi; + struct netdev_hw_addr *ha; u_long iobase = dev->base_addr; int i; char *addrs, bit, byte; @@ -1213,8 +1213,8 @@ static void SetMulticastFilter(struct net_device *dev) } /* Update table */ - netdev_for_each_mc_addr(dmi, dev) { - addrs = dmi->dmi_addr; + netdev_for_each_mc_addr(ha, dev) { + addrs = ha->addr; if ((*addrs & 0x01) == 1) { /* multicast address? */ crc = ether_crc_le(ETH_ALEN, addrs); hashcode = crc & ((1 << 9) - 1); /* hashcode is 9 LSb of CRC */ diff --git a/drivers/net/fealnx.c b/drivers/net/fealnx.c index 9d5ad08a119..e8a2705237b 100644 --- a/drivers/net/fealnx.c +++ b/drivers/net/fealnx.c @@ -1792,12 +1792,12 @@ static void __set_rx_mode(struct net_device *dev) memset(mc_filter, 0xff, sizeof(mc_filter)); rx_mode = CR_W_AB | CR_W_AM; } else { - struct dev_mc_list *mclist; + struct netdev_hw_addr *ha; memset(mc_filter, 0, sizeof(mc_filter)); - netdev_for_each_mc_addr(mclist, dev) { + netdev_for_each_mc_addr(ha, dev) { unsigned int bit; - bit = (ether_crc(ETH_ALEN, mclist->dmi_addr) >> 26) ^ 0x3F; + bit = (ether_crc(ETH_ALEN, ha->addr) >> 26) ^ 0x3F; mc_filter[bit >> 5] |= (1 << bit); } rx_mode = CR_W_AB | CR_W_AM; diff --git a/drivers/net/fec.c b/drivers/net/fec.c index 848eb1968ab..2b1651aee13 100644 --- a/drivers/net/fec.c +++ b/drivers/net/fec.c @@ -954,7 +954,7 @@ fec_enet_close(struct net_device *dev) static void set_multicast_list(struct net_device *dev) { struct fec_enet_private *fep = netdev_priv(dev); - struct dev_mc_list *dmi; + struct netdev_hw_addr *ha; unsigned int i, bit, data, crc, tmp; unsigned char hash; @@ -984,16 +984,16 @@ static void set_multicast_list(struct net_device *dev) writel(0, fep->hwp + FEC_GRP_HASH_TABLE_HIGH); writel(0, fep->hwp + FEC_GRP_HASH_TABLE_LOW); - netdev_for_each_mc_addr(dmi, dev) { + netdev_for_each_mc_addr(ha, dev) { /* Only support group multicast for now */ - if (!(dmi->dmi_addr[0] & 1)) + if (!(ha->addr[0] & 1)) continue; /* calculate crc32 value of mac address */ crc = 0xffffffff; - for (i = 0; i < dmi->dmi_addrlen; i++) { - data = dmi->dmi_addr[i]; + for (i = 0; i < dev->addr_len; i++) { + data = ha->addr[i]; for (bit = 0; bit < 8; bit++, data >>= 1) { crc = (crc >> 1) ^ (((crc ^ data) & 1) ? CRC32_POLY : 0); diff --git a/drivers/net/fec_mpc52xx.c b/drivers/net/fec_mpc52xx.c index 704155e2bdd..667ba1391b9 100644 --- a/drivers/net/fec_mpc52xx.c +++ b/drivers/net/fec_mpc52xx.c @@ -574,12 +574,12 @@ static void mpc52xx_fec_set_multicast_list(struct net_device *dev) out_be32(&fec->gaddr2, 0xffffffff); } else { u32 crc; - struct dev_mc_list *dmi; + struct netdev_hw_addr *ha; u32 gaddr1 = 0x00000000; u32 gaddr2 = 0x00000000; - netdev_for_each_mc_addr(dmi, dev) { - crc = ether_crc_le(6, dmi->dmi_addr) >> 26; + netdev_for_each_mc_addr(ha, dev) { + crc = ether_crc_le(6, ha->addr) >> 26; if (crc >= 32) gaddr1 |= 1 << (crc-32); else diff --git a/drivers/net/forcedeth.c b/drivers/net/forcedeth.c index ca05e566202..6a2b64f0a7d 100644 --- a/drivers/net/forcedeth.c +++ b/drivers/net/forcedeth.c @@ -3103,12 +3103,14 @@ static void nv_set_multicast(struct net_device *dev) if (dev->flags & IFF_ALLMULTI) { alwaysOn[0] = alwaysOn[1] = alwaysOff[0] = alwaysOff[1] = 0; } else { - struct dev_mc_list *walk; + struct netdev_hw_addr *ha; - netdev_for_each_mc_addr(walk, dev) { + netdev_for_each_mc_addr(ha, dev) { + unsigned char *addr = ha->addr; u32 a, b; - a = le32_to_cpu(*(__le32 *) walk->dmi_addr); - b = le16_to_cpu(*(__le16 *) (&walk->dmi_addr[4])); + + a = le32_to_cpu(*(__le32 *) addr); + b = le16_to_cpu(*(__le16 *) (&addr[4])); alwaysOn[0] &= a; alwaysOff[0] &= ~a; alwaysOn[1] &= b; diff --git a/drivers/net/fs_enet/mac-fcc.c b/drivers/net/fs_enet/mac-fcc.c index cf4f674f9e2..b3bad7c15d0 100644 --- a/drivers/net/fs_enet/mac-fcc.c +++ b/drivers/net/fs_enet/mac-fcc.c @@ -231,12 +231,12 @@ static void set_multicast_finish(struct net_device *dev) static void set_multicast_list(struct net_device *dev) { - struct dev_mc_list *pmc; + struct netdev_hw_addr *ha; if ((dev->flags & IFF_PROMISC) == 0) { set_multicast_start(dev); - netdev_for_each_mc_addr(pmc, dev) - set_multicast_one(dev, pmc->dmi_addr); + netdev_for_each_mc_addr(ha, dev) + set_multicast_one(dev, ha->addr); set_multicast_finish(dev); } else set_promiscuous_mode(dev); diff --git a/drivers/net/fs_enet/mac-fec.c b/drivers/net/fs_enet/mac-fec.c index cd2c6cca5f2..75974c6d201 100644 --- a/drivers/net/fs_enet/mac-fec.c +++ b/drivers/net/fs_enet/mac-fec.c @@ -232,12 +232,12 @@ static void set_multicast_finish(struct net_device *dev) static void set_multicast_list(struct net_device *dev) { - struct dev_mc_list *pmc; + struct netdev_hw_addr *ha; if ((dev->flags & IFF_PROMISC) == 0) { set_multicast_start(dev); - netdev_for_each_mc_addr(pmc, dev) - set_multicast_one(dev, pmc->dmi_addr); + netdev_for_each_mc_addr(ha, dev) + set_multicast_one(dev, ha->addr); set_multicast_finish(dev); } else set_promiscuous_mode(dev); diff --git a/drivers/net/fs_enet/mac-scc.c b/drivers/net/fs_enet/mac-scc.c index c490a466cae..0ab6a346a19 100644 --- a/drivers/net/fs_enet/mac-scc.c +++ b/drivers/net/fs_enet/mac-scc.c @@ -224,12 +224,12 @@ static void set_multicast_finish(struct net_device *dev) static void set_multicast_list(struct net_device *dev) { - struct dev_mc_list *pmc; + struct netdev_hw_addr *ha; if ((dev->flags & IFF_PROMISC) == 0) { set_multicast_start(dev); - netdev_for_each_mc_addr(pmc, dev) - set_multicast_one(dev, pmc->dmi_addr); + netdev_for_each_mc_addr(ha, dev) + set_multicast_one(dev, ha->addr); set_multicast_finish(dev); } else set_promiscuous_mode(dev); diff --git a/drivers/net/gianfar.c b/drivers/net/gianfar.c index b6715553cf1..fdd26c2b1a2 100644 --- a/drivers/net/gianfar.c +++ b/drivers/net/gianfar.c @@ -2797,7 +2797,7 @@ static void adjust_link(struct net_device *dev) * whenever dev->flags is changed */ static void gfar_set_multi(struct net_device *dev) { - struct dev_mc_list *mc_ptr; + struct netdev_hw_addr *ha; struct gfar_private *priv = netdev_priv(dev); struct gfar __iomem *regs = priv->gfargrp[0].regs; u32 tempval; @@ -2870,13 +2870,12 @@ static void gfar_set_multi(struct net_device *dev) return; /* Parse the list, and set the appropriate bits */ - netdev_for_each_mc_addr(mc_ptr, dev) { + netdev_for_each_mc_addr(ha, dev) { if (idx < em_num) { - gfar_set_mac_for_addr(dev, idx, - mc_ptr->dmi_addr); + gfar_set_mac_for_addr(dev, idx, ha->addr); idx++; } else - gfar_set_hash_for_addr(dev, mc_ptr->dmi_addr); + gfar_set_hash_for_addr(dev, ha->addr); } } diff --git a/drivers/net/greth.c b/drivers/net/greth.c index c5e0d28a6de..fbe6ab6b919 100644 --- a/drivers/net/greth.c +++ b/drivers/net/greth.c @@ -988,7 +988,7 @@ static u32 greth_hash_get_index(__u8 *addr) static void greth_set_hash_filter(struct net_device *dev) { - struct dev_mc_list *curr; + struct netdev_hw_addr *ha; struct greth_private *greth = netdev_priv(dev); struct greth_regs *regs = (struct greth_regs *) greth->regs; u32 mc_filter[2]; @@ -996,8 +996,8 @@ static void greth_set_hash_filter(struct net_device *dev) mc_filter[0] = mc_filter[1] = 0; - netdev_for_each_mc_addr(curr, dev) { - bitnr = greth_hash_get_index(curr->dmi_addr); + netdev_for_each_mc_addr(ha, dev) { + bitnr = greth_hash_get_index(ha->addr); mc_filter[bitnr >> 5] |= 1 << (bitnr & 31); } diff --git a/drivers/net/hamachi.c b/drivers/net/hamachi.c index 373546dd083..2bfcca6d180 100644 --- a/drivers/net/hamachi.c +++ b/drivers/net/hamachi.c @@ -1858,12 +1858,12 @@ static void set_rx_mode(struct net_device *dev) /* Too many to match, or accept all multicasts. */ writew(0x000B, ioaddr + AddrMode); } else if (!netdev_mc_empty(dev)) { /* Must use the CAM filter. */ - struct dev_mc_list *mclist; + struct netdev_hw_addr *ha; int i = 0; - netdev_for_each_mc_addr(mclist, dev) { - writel(*(u32*)(mclist->dmi_addr), ioaddr + 0x100 + i*8); - writel(0x20000 | (*(u16*)&mclist->dmi_addr[4]), + netdev_for_each_mc_addr(ha, dev) { + writel(*(u32 *)(ha->addr), ioaddr + 0x100 + i*8); + writel(0x20000 | (*(u16 *)&ha->addr[4]), ioaddr + 0x104 + i*8); i++; } diff --git a/drivers/net/hp100.c b/drivers/net/hp100.c index b766a69bf0c..86b2b433234 100644 --- a/drivers/net/hp100.c +++ b/drivers/net/hp100.c @@ -2100,15 +2100,15 @@ static void hp100_set_multicast_list(struct net_device *dev) } else { int i, idx; u_char *addrs; - struct dev_mc_list *dmi; + struct netdev_hw_addr *ha; memset(&lp->hash_bytes, 0x00, 8); #ifdef HP100_DEBUG printk("hp100: %s: computing hash filter - mc_count = %i\n", dev->name, netdev_mc_count(dev)); #endif - netdev_for_each_mc_addr(dmi, dev) { - addrs = dmi->dmi_addr; + netdev_for_each_mc_addr(ha, dev) { + addrs = ha->addr; if ((*addrs & 0x01) == 0x01) { /* multicast address? */ #ifdef HP100_DEBUG printk("hp100: %s: multicast = %pM, ", diff --git a/drivers/net/ibm_newemac/core.c b/drivers/net/ibm_newemac/core.c index d8533a4ef82..40c78507ef1 100644 --- a/drivers/net/ibm_newemac/core.c +++ b/drivers/net/ibm_newemac/core.c @@ -388,18 +388,19 @@ static void emac_hash_mc(struct emac_instance *dev) const int regs = EMAC_XAHT_REGS(dev); u32 *gaht_base = emac_gaht_base(dev); u32 gaht_temp[regs]; - struct dev_mc_list *dmi; + struct netdev_hw_addr *ha; int i; DBG(dev, "hash_mc %d" NL, netdev_mc_count(dev->ndev)); memset(gaht_temp, 0, sizeof (gaht_temp)); - netdev_for_each_mc_addr(dmi, dev->ndev) { + netdev_for_each_mc_addr(ha, dev->ndev) { int slot, reg, mask; - DBG2(dev, "mc %pM" NL, dmi->dmi_addr); + DBG2(dev, "mc %pM" NL, ha->addr); - slot = EMAC_XAHT_CRC_TO_SLOT(dev, ether_crc(ETH_ALEN, dmi->dmi_addr)); + slot = EMAC_XAHT_CRC_TO_SLOT(dev, + ether_crc(ETH_ALEN, ha->addr)); reg = EMAC_XAHT_SLOT_TO_REG(dev, slot); mask = EMAC_XAHT_SLOT_TO_MASK(dev, slot); @@ -1176,7 +1177,7 @@ static int emac_open(struct net_device *ndev) netif_carrier_on(dev->ndev); /* Required for Pause packet support in EMAC */ - dev_mc_add(ndev, default_mcast_addr, sizeof(default_mcast_addr), 1); + dev_mc_add_global(ndev, default_mcast_addr); emac_configure(dev); mal_poll_add(dev->mal, &dev->commac); diff --git a/drivers/net/ibmlana.c b/drivers/net/ibmlana.c index b5d0f4e973f..76949e08ee8 100644 --- a/drivers/net/ibmlana.c +++ b/drivers/net/ibmlana.c @@ -385,7 +385,7 @@ static void InitBoard(struct net_device *dev) int camcnt; camentry_t cams[16]; u32 cammask; - struct dev_mc_list *mcptr; + struct netdev_hw_addr *ha; u16 rcrval; /* reset the SONIC */ @@ -420,8 +420,8 @@ static void InitBoard(struct net_device *dev) /* start putting the multicast addresses into the CAM list. Stop if it is full. */ - netdev_for_each_mc_addr(mcptr, dev) { - putcam(cams, &camcnt, mcptr->dmi_addr); + netdev_for_each_mc_addr(ha, dev) { + putcam(cams, &camcnt, ha->addr); if (camcnt == 16) break; } @@ -479,7 +479,7 @@ static void InitBoard(struct net_device *dev) /* if still multicast addresses left or ALLMULTI is set, set the multicast enable bit */ - if ((dev->flags & IFF_ALLMULTI) || (mcptr != NULL)) + if ((dev->flags & IFF_ALLMULTI) || netdev_mc_count(dev) > camcnt) rcrval |= RCREG_AMC; /* promiscous mode ? */ diff --git a/drivers/net/ibmveth.c b/drivers/net/ibmveth.c index 0bc777bac9b..f468590ed45 100644 --- a/drivers/net/ibmveth.c +++ b/drivers/net/ibmveth.c @@ -1072,7 +1072,7 @@ static void ibmveth_set_multicast_list(struct net_device *netdev) ibmveth_error_printk("h_multicast_ctrl rc=%ld when entering promisc mode\n", lpar_rc); } } else { - struct dev_mc_list *mclist; + struct netdev_hw_addr *ha; /* clear the filter table & disable filtering */ lpar_rc = h_multicast_ctrl(adapter->vdev->unit_address, IbmVethMcastEnableRecv | @@ -1083,10 +1083,10 @@ static void ibmveth_set_multicast_list(struct net_device *netdev) ibmveth_error_printk("h_multicast_ctrl rc=%ld when attempting to clear filter table\n", lpar_rc); } /* add the addresses to the filter table */ - netdev_for_each_mc_addr(mclist, netdev) { + netdev_for_each_mc_addr(ha, netdev) { // add the multicast address to the filter table unsigned long mcast_addr = 0; - memcpy(((char *)&mcast_addr)+2, mclist->dmi_addr, 6); + memcpy(((char *)&mcast_addr)+2, ha->addr, 6); lpar_rc = h_multicast_ctrl(adapter->vdev->unit_address, IbmVethMcastAddFilter, mcast_addr); diff --git a/drivers/net/igb/igb_main.c b/drivers/net/igb/igb_main.c index ea875709f05..78cc742e233 100644 --- a/drivers/net/igb/igb_main.c +++ b/drivers/net/igb/igb_main.c @@ -2864,7 +2864,7 @@ static int igb_write_mc_addr_list(struct net_device *netdev) { struct igb_adapter *adapter = netdev_priv(netdev); struct e1000_hw *hw = &adapter->hw; - struct dev_mc_list *mc_ptr; + struct netdev_hw_addr *ha; u8 *mta_list; int i; @@ -2881,8 +2881,8 @@ static int igb_write_mc_addr_list(struct net_device *netdev) /* The shared function expects a packed array of only addresses. */ i = 0; - netdev_for_each_mc_addr(mc_ptr, netdev) - memcpy(mta_list + (i++ * ETH_ALEN), mc_ptr->dmi_addr, ETH_ALEN); + netdev_for_each_mc_addr(ha, netdev) + memcpy(mta_list + (i++ * ETH_ALEN), ha->addr, ETH_ALEN); igb_update_mc_addr_list(hw, mta_list, i); kfree(mta_list); diff --git a/drivers/net/igbvf/netdev.c b/drivers/net/igbvf/netdev.c index a77afd8a14b..ea8abf5c1ef 100644 --- a/drivers/net/igbvf/netdev.c +++ b/drivers/net/igbvf/netdev.c @@ -1399,7 +1399,7 @@ static void igbvf_set_multi(struct net_device *netdev) { struct igbvf_adapter *adapter = netdev_priv(netdev); struct e1000_hw *hw = &adapter->hw; - struct dev_mc_list *mc_ptr; + struct netdev_hw_addr *ha; u8 *mta_list = NULL; int i; @@ -1414,8 +1414,8 @@ static void igbvf_set_multi(struct net_device *netdev) /* prepare a packed array of only addresses. */ i = 0; - netdev_for_each_mc_addr(mc_ptr, netdev) - memcpy(mta_list + (i++ * ETH_ALEN), mc_ptr->dmi_addr, ETH_ALEN); + netdev_for_each_mc_addr(ha, netdev) + memcpy(mta_list + (i++ * ETH_ALEN), ha->addr, ETH_ALEN); hw->mac.ops.update_mc_addr_list(hw, mta_list, i, 0, 0); kfree(mta_list); diff --git a/drivers/net/ioc3-eth.c b/drivers/net/ioc3-eth.c index 70871b9b045..57d873da978 100644 --- a/drivers/net/ioc3-eth.c +++ b/drivers/net/ioc3-eth.c @@ -1664,7 +1664,7 @@ static int ioc3_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) static void ioc3_set_multicast_list(struct net_device *dev) { - struct dev_mc_list *dmi; + struct netdev_hw_addr *ha; struct ioc3_private *ip = netdev_priv(dev); struct ioc3 *ioc3 = ip->regs; u64 ehar = 0; @@ -1688,8 +1688,8 @@ static void ioc3_set_multicast_list(struct net_device *dev) ip->ehar_h = 0xffffffff; ip->ehar_l = 0xffffffff; } else { - netdev_for_each_mc_addr(dmi, dev) { - char *addr = dmi->dmi_addr; + netdev_for_each_mc_addr(ha, dev) { + char *addr = ha->addr; if (!(*addr & 1)) continue; diff --git a/drivers/net/ipg.c b/drivers/net/ipg.c index 0d7ad3f2d0f..67cfc7d9d89 100644 --- a/drivers/net/ipg.c +++ b/drivers/net/ipg.c @@ -569,7 +569,7 @@ static int ipg_config_autoneg(struct net_device *dev) static void ipg_nic_set_multicast_list(struct net_device *dev) { void __iomem *ioaddr = ipg_ioaddr(dev); - struct dev_mc_list *mc_list_ptr; + struct netdev_hw_addr *ha; unsigned int hashindex; u32 hashtable[2]; u8 receivemode; @@ -608,9 +608,9 @@ static void ipg_nic_set_multicast_list(struct net_device *dev) hashtable[1] = 0x00000000; /* Cycle through all multicast addresses to filter. */ - netdev_for_each_mc_addr(mc_list_ptr, dev) { + netdev_for_each_mc_addr(ha, dev) { /* Calculate CRC result for each multicast address. */ - hashindex = crc32_le(0xffffffff, mc_list_ptr->dmi_addr, + hashindex = crc32_le(0xffffffff, ha->addr, ETH_ALEN); /* Use only the least significant 6 bits. */ diff --git a/drivers/net/iseries_veth.c b/drivers/net/iseries_veth.c index e6e972d9b7c..cd65b8629bc 100644 --- a/drivers/net/iseries_veth.c +++ b/drivers/net/iseries_veth.c @@ -961,15 +961,15 @@ static void veth_set_multicast_list(struct net_device *dev) (netdev_mc_count(dev) > VETH_MAX_MCAST)) { port->promiscuous = 1; } else { - struct dev_mc_list *dmi; + struct netdev_hw_addr *ha; port->promiscuous = 0; /* Update table */ port->num_mcast = 0; - netdev_for_each_mc_addr(dmi, dev) { - u8 *addr = dmi->dmi_addr; + netdev_for_each_mc_addr(ha, dev) { + u8 *addr = ha->addr; u64 xaddr = 0; if (addr[0] & 0x01) {/* multicast address? */ diff --git a/drivers/net/ixgb/ixgb_main.c b/drivers/net/ixgb/ixgb_main.c index c9fef65cb98..912dd1d5772 100644 --- a/drivers/net/ixgb/ixgb_main.c +++ b/drivers/net/ixgb/ixgb_main.c @@ -1058,7 +1058,7 @@ ixgb_set_multi(struct net_device *netdev) { struct ixgb_adapter *adapter = netdev_priv(netdev); struct ixgb_hw *hw = &adapter->hw; - struct dev_mc_list *mc_ptr; + struct netdev_hw_addr *ha; u32 rctl; int i; @@ -1089,9 +1089,9 @@ ixgb_set_multi(struct net_device *netdev) IXGB_WRITE_REG(hw, RCTL, rctl); i = 0; - netdev_for_each_mc_addr(mc_ptr, netdev) + netdev_for_each_mc_addr(ha, netdev) memcpy(&mta[i++ * IXGB_ETH_LENGTH_OF_ADDRESS], - mc_ptr->dmi_addr, IXGB_ETH_LENGTH_OF_ADDRESS); + ha->addr, IXGB_ETH_LENGTH_OF_ADDRESS); ixgb_mc_addr_list_update(hw, mta, netdev_mc_count(netdev), 0); } diff --git a/drivers/net/ixgbe/ixgbe_common.c b/drivers/net/ixgbe/ixgbe_common.c index 4d1c3a42945..6eb5814ca7d 100644 --- a/drivers/net/ixgbe/ixgbe_common.c +++ b/drivers/net/ixgbe/ixgbe_common.c @@ -1494,7 +1494,7 @@ static void ixgbe_set_mta(struct ixgbe_hw *hw, u8 *mc_addr) s32 ixgbe_update_mc_addr_list_generic(struct ixgbe_hw *hw, struct net_device *netdev) { - struct dev_addr_list *dmi; + struct netdev_hw_addr *ha; u32 i; /* @@ -1510,9 +1510,9 @@ s32 ixgbe_update_mc_addr_list_generic(struct ixgbe_hw *hw, IXGBE_WRITE_REG(hw, IXGBE_MTA(i), 0); /* Add the new addresses */ - netdev_for_each_mc_addr(dmi, netdev) { + netdev_for_each_mc_addr(ha, netdev) { hw_dbg(hw, " Adding the multicast addresses:\n"); - ixgbe_set_mta(hw, dmi->dmi_addr); + ixgbe_set_mta(hw, ha->addr); } /* Enable mta */ diff --git a/drivers/net/ixgbevf/vf.c b/drivers/net/ixgbevf/vf.c index f457c52b5ed..852e9c4fd93 100644 --- a/drivers/net/ixgbevf/vf.c +++ b/drivers/net/ixgbevf/vf.c @@ -259,7 +259,7 @@ static s32 ixgbevf_set_rar_vf(struct ixgbe_hw *hw, u32 index, u8 *addr, static s32 ixgbevf_update_mc_addr_list_vf(struct ixgbe_hw *hw, struct net_device *netdev) { - struct dev_addr_list *dmi; + struct netdev_hw_addr *ha; struct ixgbe_mbx_info *mbx = &hw->mbx; u32 msgbuf[IXGBE_VFMAILBOX_SIZE]; u16 *vector_list = (u16 *)&msgbuf[1]; @@ -281,10 +281,10 @@ static s32 ixgbevf_update_mc_addr_list_vf(struct ixgbe_hw *hw, msgbuf[0] |= cnt << IXGBE_VT_MSGINFO_SHIFT; i = 0; - netdev_for_each_mc_addr(dmi, netdev) { + netdev_for_each_mc_addr(ha, netdev) { if (i == cnt) break; - vector_list[i++] = ixgbevf_mta_vector(hw, dmi->dmi_addr); + vector_list[i++] = ixgbevf_mta_vector(hw, ha->addr); } mbx->ops.write_posted(hw, msgbuf, IXGBE_VFMAILBOX_SIZE); diff --git a/drivers/net/jme.c b/drivers/net/jme.c index c0b59a55538..10e816d2caf 100644 --- a/drivers/net/jme.c +++ b/drivers/net/jme.c @@ -2009,12 +2009,12 @@ jme_set_multi(struct net_device *netdev) } else if (netdev->flags & IFF_ALLMULTI) { jme->reg_rxmcs |= RXMCS_ALLMULFRAME; } else if (netdev->flags & IFF_MULTICAST) { - struct dev_mc_list *mclist; + struct netdev_hw_addr *ha; int bit_nr; jme->reg_rxmcs |= RXMCS_MULFRAME | RXMCS_MULFILTERED; - netdev_for_each_mc_addr(mclist, netdev) { - bit_nr = ether_crc(ETH_ALEN, mclist->dmi_addr) & 0x3F; + netdev_for_each_mc_addr(ha, netdev) { + bit_nr = ether_crc(ETH_ALEN, ha->addr) & 0x3F; mc_hash[bit_nr >> 5] |= 1 << (bit_nr & 0x1F); } diff --git a/drivers/net/korina.c b/drivers/net/korina.c index edaedc7aa03..26bf1b76b99 100644 --- a/drivers/net/korina.c +++ b/drivers/net/korina.c @@ -482,7 +482,7 @@ static void korina_multicast_list(struct net_device *dev) { struct korina_private *lp = netdev_priv(dev); unsigned long flags; - struct dev_mc_list *dmi; + struct netdev_hw_addr *ha; u32 recognise = ETH_ARC_AB; /* always accept broadcasts */ int i; @@ -502,8 +502,8 @@ static void korina_multicast_list(struct net_device *dev) for (i = 0; i < 4; i++) hash_table[i] = 0; - netdev_for_each_mc_addr(dmi, dev) { - char *addrs = dmi->dmi_addr; + netdev_for_each_mc_addr(ha, dev) { + char *addrs = ha->addr; if (!(*addrs & 1)) continue; diff --git a/drivers/net/ks8851.c b/drivers/net/ks8851.c index 66be4e449f0..4dcd61f81ec 100644 --- a/drivers/net/ks8851.c +++ b/drivers/net/ks8851.c @@ -956,13 +956,13 @@ static void ks8851_set_rx_mode(struct net_device *dev) rxctrl.rxcr1 = (RXCR1_RXME | RXCR1_RXAE | RXCR1_RXPAFMA | RXCR1_RXMAFMA); } else if (dev->flags & IFF_MULTICAST && !netdev_mc_empty(dev)) { - struct dev_mc_list *mcptr; + struct netdev_hw_addr *ha; u32 crc; /* accept some multicast */ - netdev_for_each_mc_addr(mcptr, dev) { - crc = ether_crc(ETH_ALEN, mcptr->dmi_addr); + netdev_for_each_mc_addr(ha, dev) { + crc = ether_crc(ETH_ALEN, ha->addr); crc >>= (32 - 6); /* get top six bits */ rxctrl.mchash[crc >> 4] |= (1 << (crc & 0xf)); diff --git a/drivers/net/ks8851_mll.c b/drivers/net/ks8851_mll.c index ee3fe30b2ad..70a3d98f4bd 100644 --- a/drivers/net/ks8851_mll.c +++ b/drivers/net/ks8851_mll.c @@ -362,7 +362,6 @@ static u8 KS_DEFAULT_MAC_ADDRESS[] = { 0x00, 0x10, 0xA1, 0x86, 0x95, 0x11 }; #define MAX_MCAST_LST 32 #define HW_MCAST_SIZE 8 -#define MAC_ADDR_LEN 6 /** * union ks_tx_hdr - tx header data @@ -450,7 +449,7 @@ struct ks_net { u16 promiscuous; u16 all_mcast; u16 mcast_lst_size; - u8 mcast_lst[MAX_MCAST_LST][MAC_ADDR_LEN]; + u8 mcast_lst[MAX_MCAST_LST][ETH_ALEN]; u8 mcast_bits[HW_MCAST_SIZE]; u8 mac_addr[6]; u8 fid; @@ -1170,7 +1169,7 @@ static void ks_set_mcast(struct ks_net *ks, u16 mcast) static void ks_set_rx_mode(struct net_device *netdev) { struct ks_net *ks = netdev_priv(netdev); - struct dev_mc_list *ptr; + struct netdev_hw_addr *ha; /* Turn on/off promiscuous mode. */ if ((netdev->flags & IFF_PROMISC) == IFF_PROMISC) @@ -1187,13 +1186,12 @@ static void ks_set_rx_mode(struct net_device *netdev) if (netdev_mc_count(netdev) <= MAX_MCAST_LST) { int i = 0; - netdev_for_each_mc_addr(ptr, netdev) { - if (!(*ptr->dmi_addr & 1)) + netdev_for_each_mc_addr(ha, netdev) { + if (!(*ha->addr & 1)) continue; if (i >= MAX_MCAST_LST) break; - memcpy(ks->mcast_lst[i++], ptr->dmi_addr, - MAC_ADDR_LEN); + memcpy(ks->mcast_lst[i++], ha->addr, ETH_ALEN); } ks->mcast_lst_size = (u8)i; ks_set_grpaddr(ks); diff --git a/drivers/net/ksz884x.c b/drivers/net/ksz884x.c index b843bf7d8c1..34876952161 100644 --- a/drivers/net/ksz884x.c +++ b/drivers/net/ksz884x.c @@ -5763,7 +5763,7 @@ static void netdev_set_rx_mode(struct net_device *dev) struct dev_priv *priv = netdev_priv(dev); struct dev_info *hw_priv = priv->adapter; struct ksz_hw *hw = &hw_priv->hw; - struct dev_mc_list *mc_ptr; + struct netdev_hw_addr *ha; int multicast = (dev->flags & IFF_ALLMULTI); dev_set_promiscuous(dev, priv, hw, (dev->flags & IFF_PROMISC)); @@ -5780,7 +5780,7 @@ static void netdev_set_rx_mode(struct net_device *dev) int i = 0; /* List too big to support so turn on all multicast mode. */ - if (dev->mc_count > MAX_MULTICAST_LIST) { + if (netdev_mc_count(dev) > MAX_MULTICAST_LIST) { if (MAX_MULTICAST_LIST != hw->multi_list_size) { hw->multi_list_size = MAX_MULTICAST_LIST; ++hw->all_multi; @@ -5789,13 +5789,12 @@ static void netdev_set_rx_mode(struct net_device *dev) return; } - netdev_for_each_mc_addr(mc_ptr, dev) { - if (!(*mc_ptr->dmi_addr & 1)) + netdev_for_each_mc_addr(ha, dev) { + if (!(*ha->addr & 1)) continue; if (i >= MAX_MULTICAST_LIST) break; - memcpy(hw->multi_list[i++], mc_ptr->dmi_addr, - MAC_ADDR_LEN); + memcpy(hw->multi_list[i++], ha->addr, MAC_ADDR_LEN); } hw->multi_list_size = (u8) i; hw_set_grp_addr(hw); diff --git a/drivers/net/lib82596.c b/drivers/net/lib82596.c index 443c39a3732..fddaf921885 100644 --- a/drivers/net/lib82596.c +++ b/drivers/net/lib82596.c @@ -1388,7 +1388,7 @@ static void set_multicast_list(struct net_device *dev) } if (!netdev_mc_empty(dev)) { - struct dev_mc_list *dmi; + struct netdev_hw_addr *ha; unsigned char *cp; struct mc_cmd *cmd; @@ -1396,10 +1396,10 @@ static void set_multicast_list(struct net_device *dev) cmd->cmd.command = SWAP16(CmdMulticastList); cmd->mc_cnt = SWAP16(netdev_mc_count(dev) * 6); cp = cmd->mc_addrs; - netdev_for_each_mc_addr(dmi, dev) { + netdev_for_each_mc_addr(ha, dev) { if (!cnt--) break; - memcpy(cp, dmi->dmi_addr, 6); + memcpy(cp, ha->addr, 6); if (i596_debug > 1) DEB(DEB_MULTI, printk(KERN_DEBUG diff --git a/drivers/net/lib8390.c b/drivers/net/lib8390.c index 56f66f48540..526dc9cbc3c 100644 --- a/drivers/net/lib8390.c +++ b/drivers/net/lib8390.c @@ -905,10 +905,10 @@ static struct net_device_stats *__ei_get_stats(struct net_device *dev) static inline void make_mc_bits(u8 *bits, struct net_device *dev) { - struct dev_mc_list *dmi; + struct netdev_hw_addr *ha; - netdev_for_each_mc_addr(dmi, dev) { - u32 crc = ether_crc(ETH_ALEN, dmi->dmi_addr); + netdev_for_each_mc_addr(ha, dev) { + u32 crc = ether_crc(ETH_ALEN, ha->addr); /* * The 8390 uses the 6 most significant bits of the * CRC to index the multicast table. diff --git a/drivers/net/ll_temac_main.c b/drivers/net/ll_temac_main.c index a18e3485476..30474d6b15c 100644 --- a/drivers/net/ll_temac_main.c +++ b/drivers/net/ll_temac_main.c @@ -250,20 +250,20 @@ static void temac_set_multicast_list(struct net_device *ndev) temac_indirect_out32(lp, XTE_AFM_OFFSET, XTE_AFM_EPPRM_MASK); dev_info(&ndev->dev, "Promiscuous mode enabled.\n"); } else if (!netdev_mc_empty(ndev)) { - struct dev_mc_list *mclist; + struct netdev_hw_addr *ha; i = 0; - netdev_for_each_mc_addr(mclist, ndev) { + netdev_for_each_mc_addr(ha, ndev) { if (i >= MULTICAST_CAM_TABLE_NUM) break; - multi_addr_msw = ((mclist->dmi_addr[3] << 24) | - (mclist->dmi_addr[2] << 16) | - (mclist->dmi_addr[1] << 8) | - (mclist->dmi_addr[0])); + multi_addr_msw = ((ha->addr[3] << 24) | + (ha->addr[2] << 16) | + (ha->addr[1] << 8) | + (ha->addr[0])); temac_indirect_out32(lp, XTE_MAW0_OFFSET, multi_addr_msw); - multi_addr_lsw = ((mclist->dmi_addr[5] << 8) | - (mclist->dmi_addr[4]) | (i << 16)); + multi_addr_lsw = ((ha->addr[5] << 8) | + (ha->addr[4]) | (i << 16)); temac_indirect_out32(lp, XTE_MAW1_OFFSET, multi_addr_lsw); i++; diff --git a/drivers/net/lp486e.c b/drivers/net/lp486e.c index 3e3cc04defd..72379c5439d 100644 --- a/drivers/net/lp486e.c +++ b/drivers/net/lp486e.c @@ -1256,7 +1256,7 @@ static void set_multicast_list(struct net_device *dev) { dev->name, netdev_mc_count(dev)); if (!netdev_mc_empty(dev)) { - struct dev_mc_list *dmi; + struct netdev_hw_addr *ha; char *cp; cmd = kmalloc(sizeof(struct i596_cmd) + 2 + netdev_mc_count(dev) * 6, GFP_ATOMIC); @@ -1267,8 +1267,8 @@ static void set_multicast_list(struct net_device *dev) { cmd->command = CmdMulticastList; *((unsigned short *) (cmd + 1)) = netdev_mc_count(dev) * 6; cp = ((char *)(cmd + 1))+2; - netdev_for_each_mc_addr(dmi, dev) { - memcpy(cp, dmi->dmi_addr, 6); + netdev_for_each_mc_addr(ha, dev) { + memcpy(cp, ha->addr, 6); cp += 6; } if (i596_debug & LOG_SRCDST) diff --git a/drivers/net/macb.c b/drivers/net/macb.c index c8a18a6203c..eab121945d7 100644 --- a/drivers/net/macb.c +++ b/drivers/net/macb.c @@ -882,15 +882,15 @@ static int hash_get_index(__u8 *addr) */ static void macb_sethashtable(struct net_device *dev) { - struct dev_mc_list *curr; + struct netdev_hw_addr *ha; unsigned long mc_filter[2]; unsigned int bitnr; struct macb *bp = netdev_priv(dev); mc_filter[0] = mc_filter[1] = 0; - netdev_for_each_mc_addr(curr, dev) { - bitnr = hash_get_index(curr->dmi_addr); + netdev_for_each_mc_addr(ha, dev) { + bitnr = hash_get_index(ha->addr); mc_filter[bitnr >> 5] |= 1 << (bitnr & 31); } diff --git a/drivers/net/mace.c b/drivers/net/mace.c index ab5f0bf6d1a..2328a7399dc 100644 --- a/drivers/net/mace.c +++ b/drivers/net/mace.c @@ -598,7 +598,7 @@ static void mace_set_multicast(struct net_device *dev) mp->maccc |= PROM; } else { unsigned char multicast_filter[8]; - struct dev_mc_list *dmi; + struct netdev_hw_addr *ha; if (dev->flags & IFF_ALLMULTI) { for (i = 0; i < 8; i++) @@ -606,8 +606,8 @@ static void mace_set_multicast(struct net_device *dev) } else { for (i = 0; i < 8; i++) multicast_filter[i] = 0; - netdev_for_each_mc_addr(dmi, dev) { - crc = ether_crc_le(6, dmi->dmi_addr); + netdev_for_each_mc_addr(ha, dev) { + crc = ether_crc_le(6, ha->addr); i = crc >> 26; /* bit number in multicast_filter */ multicast_filter[i >> 3] |= 1 << (i & 7); } diff --git a/drivers/net/macmace.c b/drivers/net/macmace.c index 13ba8f4afb7..8a50c67e592 100644 --- a/drivers/net/macmace.c +++ b/drivers/net/macmace.c @@ -508,7 +508,7 @@ static void mace_set_multicast(struct net_device *dev) mb->maccc |= PROM; } else { unsigned char multicast_filter[8]; - struct dev_mc_list *dmi; + struct netdev_hw_addr *ha; if (dev->flags & IFF_ALLMULTI) { for (i = 0; i < 8; i++) { @@ -517,8 +517,8 @@ static void mace_set_multicast(struct net_device *dev) } else { for (i = 0; i < 8; i++) multicast_filter[i] = 0; - netdev_for_each_mc_addr(dmi, dev) { - crc = ether_crc_le(6, dmi->dmi_addr); + netdev_for_each_mc_addr(ha, dev) { + crc = ether_crc_le(6, ha->addr); /* bit number in multicast_filter */ i = crc >> 26; multicast_filter[i >> 3] |= 1 << (i & 7); diff --git a/drivers/net/mlx4/en_netdev.c b/drivers/net/mlx4/en_netdev.c index 5eb96fe6ec5..455464223b4 100644 --- a/drivers/net/mlx4/en_netdev.c +++ b/drivers/net/mlx4/en_netdev.c @@ -168,7 +168,7 @@ static void mlx4_en_clear_list(struct net_device *dev) static void mlx4_en_cache_mclist(struct net_device *dev) { struct mlx4_en_priv *priv = netdev_priv(dev); - struct dev_mc_list *mclist; + struct netdev_hw_addr *ha; char *mc_addrs; int mc_addrs_cnt = netdev_mc_count(dev); int i; @@ -179,8 +179,8 @@ static void mlx4_en_cache_mclist(struct net_device *dev) return; } i = 0; - netdev_for_each_mc_addr(mclist, dev) - memcpy(mc_addrs + i++ * ETH_ALEN, mclist->dmi_addr, ETH_ALEN); + netdev_for_each_mc_addr(ha, dev) + memcpy(mc_addrs + i++ * ETH_ALEN, ha->addr, ETH_ALEN); priv->mc_addrs = mc_addrs; priv->mc_addrs_cnt = mc_addrs_cnt; } diff --git a/drivers/net/mv643xx_eth.c b/drivers/net/mv643xx_eth.c index c97b6e4365a..d5ebe43b0e6 100644 --- a/drivers/net/mv643xx_eth.c +++ b/drivers/net/mv643xx_eth.c @@ -1769,7 +1769,7 @@ static void mv643xx_eth_program_multicast_filter(struct net_device *dev) struct mv643xx_eth_private *mp = netdev_priv(dev); u32 *mc_spec; u32 *mc_other; - struct dev_addr_list *addr; + struct netdev_hw_addr *ha; int i; if (dev->flags & (IFF_PROMISC | IFF_ALLMULTI)) { @@ -1794,8 +1794,8 @@ oom: memset(mc_spec, 0, 0x100); memset(mc_other, 0, 0x100); - netdev_for_each_mc_addr(addr, dev) { - u8 *a = addr->da_addr; + netdev_for_each_mc_addr(ha, dev) { + u8 *a = ha->addr; u32 *table; int entry; diff --git a/drivers/net/myri10ge/myri10ge.c b/drivers/net/myri10ge/myri10ge.c index e84dd3ee9c5..6d33adf988d 100644 --- a/drivers/net/myri10ge/myri10ge.c +++ b/drivers/net/myri10ge/myri10ge.c @@ -3001,7 +3001,7 @@ static void myri10ge_set_multicast_list(struct net_device *dev) { struct myri10ge_priv *mgp = netdev_priv(dev); struct myri10ge_cmd cmd; - struct dev_mc_list *mc_list; + struct netdev_hw_addr *ha; __be32 data[2] = { 0, 0 }; int err; @@ -3038,8 +3038,8 @@ static void myri10ge_set_multicast_list(struct net_device *dev) } /* Walk the multicast list, and add each address */ - netdev_for_each_mc_addr(mc_list, dev) { - memcpy(data, &mc_list->dmi_addr, 6); + netdev_for_each_mc_addr(ha, dev) { + memcpy(data, &ha->addr, 6); cmd.data0 = ntohl(data[0]); cmd.data1 = ntohl(data[1]); err = myri10ge_send_cmd(mgp, MXGEFW_JOIN_MULTICAST_GROUP, @@ -3047,7 +3047,7 @@ static void myri10ge_set_multicast_list(struct net_device *dev) if (err != 0) { netdev_err(dev, "Failed MXGEFW_JOIN_MULTICAST_GROUP, error status:%d %pM\n", - err, mc_list->dmi_addr); + err, ha->addr); goto abort; } } diff --git a/drivers/net/natsemi.c b/drivers/net/natsemi.c index e5203878324..9250bf6573e 100644 --- a/drivers/net/natsemi.c +++ b/drivers/net/natsemi.c @@ -2493,12 +2493,12 @@ static void __set_rx_mode(struct net_device *dev) rx_mode = RxFilterEnable | AcceptBroadcast | AcceptAllMulticast | AcceptMyPhys; } else { - struct dev_mc_list *mclist; + struct netdev_hw_addr *ha; int i; memset(mc_filter, 0, sizeof(mc_filter)); - netdev_for_each_mc_addr(mclist, dev) { - int b = (ether_crc(ETH_ALEN, mclist->dmi_addr) >> 23) & 0x1ff; + netdev_for_each_mc_addr(ha, dev) { + int b = (ether_crc(ETH_ALEN, ha->addr) >> 23) & 0x1ff; mc_filter[b/8] |= (1 << (b & 0x07)); } rx_mode = RxFilterEnable | AcceptBroadcast diff --git a/drivers/net/netxen/netxen_nic_hw.c b/drivers/net/netxen/netxen_nic_hw.c index a945591298a..781ca893ee0 100644 --- a/drivers/net/netxen/netxen_nic_hw.c +++ b/drivers/net/netxen/netxen_nic_hw.c @@ -537,7 +537,7 @@ netxen_nic_set_mcast_addr(struct netxen_adapter *adapter, void netxen_p2_nic_set_multi(struct net_device *netdev) { struct netxen_adapter *adapter = netdev_priv(netdev); - struct dev_mc_list *mc_ptr; + struct netdev_hw_addr *ha; u8 null_addr[6]; int i; @@ -571,8 +571,8 @@ void netxen_p2_nic_set_multi(struct net_device *netdev) netxen_nic_enable_mcast_filter(adapter); i = 0; - netdev_for_each_mc_addr(mc_ptr, netdev) - netxen_nic_set_mcast_addr(adapter, i++, mc_ptr->dmi_addr); + netdev_for_each_mc_addr(ha, netdev) + netxen_nic_set_mcast_addr(adapter, i++, ha->addr); /* Clear out remaining addresses */ while (i < adapter->max_mc_count) @@ -680,7 +680,7 @@ static int nx_p3_nic_add_mac(struct netxen_adapter *adapter, void netxen_p3_nic_set_multi(struct net_device *netdev) { struct netxen_adapter *adapter = netdev_priv(netdev); - struct dev_mc_list *mc_ptr; + struct netdev_hw_addr *ha; u8 bcast_addr[ETH_ALEN] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; u32 mode = VPORT_MISS_MODE_DROP; LIST_HEAD(del_list); @@ -707,8 +707,8 @@ void netxen_p3_nic_set_multi(struct net_device *netdev) } if (!netdev_mc_empty(netdev)) { - netdev_for_each_mc_addr(mc_ptr, netdev) - nx_p3_nic_add_mac(adapter, mc_ptr->dmi_addr, &del_list); + netdev_for_each_mc_addr(ha, netdev) + nx_p3_nic_add_mac(adapter, ha->addr, &del_list); } send_fw_cmd: diff --git a/drivers/net/ni52.c b/drivers/net/ni52.c index 05c29c2cef2..a76fabe2629 100644 --- a/drivers/net/ni52.c +++ b/drivers/net/ni52.c @@ -596,7 +596,7 @@ static int init586(struct net_device *dev) struct iasetup_cmd_struct __iomem *ias_cmd; struct tdr_cmd_struct __iomem *tdr_cmd; struct mcsetup_cmd_struct __iomem *mc_cmd; - struct dev_mc_list *dmi; + struct netdev_hw_addr *ha; int num_addrs = netdev_mc_count(dev); ptr = p->scb + 1; @@ -725,8 +725,8 @@ static int init586(struct net_device *dev) writew(num_addrs * 6, &mc_cmd->mc_cnt); i = 0; - netdev_for_each_mc_addr(dmi, dev) - memcpy_toio(mc_cmd->mc_list[i++], dmi->dmi_addr, 6); + netdev_for_each_mc_addr(ha, dev) + memcpy_toio(mc_cmd->mc_list[i++], ha->addr, 6); writew(make16(mc_cmd), &p->scb->cbl_offset); writeb(CUC_START, &p->scb->cmd_cuc); diff --git a/drivers/net/niu.c b/drivers/net/niu.c index 0678f3106cb..7b52c466cf4 100644 --- a/drivers/net/niu.c +++ b/drivers/net/niu.c @@ -6313,7 +6313,6 @@ static void niu_set_rx_mode(struct net_device *dev) { struct niu *np = netdev_priv(dev); int i, alt_cnt, err; - struct dev_addr_list *addr; struct netdev_hw_addr *ha; unsigned long flags; u16 hash[16] = { 0, }; @@ -6365,8 +6364,8 @@ static void niu_set_rx_mode(struct net_device *dev) for (i = 0; i < 16; i++) hash[i] = 0xffff; } else if (!netdev_mc_empty(dev)) { - netdev_for_each_mc_addr(addr, dev) { - u32 crc = ether_crc_le(ETH_ALEN, addr->da_addr); + netdev_for_each_mc_addr(ha, dev) { + u32 crc = ether_crc_le(ETH_ALEN, ha->addr); crc >>= 24; hash[crc >> 4] |= (1 << (15 - (crc & 0xf))); diff --git a/drivers/net/octeon/octeon_mgmt.c b/drivers/net/octeon/octeon_mgmt.c index be368e5cbf7..ee894ed35f7 100644 --- a/drivers/net/octeon/octeon_mgmt.c +++ b/drivers/net/octeon/octeon_mgmt.c @@ -474,7 +474,7 @@ static void octeon_mgmt_set_rx_filtering(struct net_device *netdev) unsigned int cam_mode = 1; /* 1 - Accept on CAM match */ unsigned int multicast_mode = 1; /* 1 - Reject all multicast. */ struct octeon_mgmt_cam_state cam_state; - struct dev_addr_list *list; + struct netdev_hw_addr *ha; struct list_head *pos; int available_cam_entries; @@ -510,8 +510,8 @@ static void octeon_mgmt_set_rx_filtering(struct net_device *netdev) } } if (multicast_mode == 0) { - netdev_for_each_mc_addr(list, netdev) - octeon_mgmt_cam_state_add(&cam_state, list->da_addr); + netdev_for_each_mc_addr(ha, netdev) + octeon_mgmt_cam_state_add(&cam_state, ha->addr); } diff --git a/drivers/net/pci-skeleton.c b/drivers/net/pci-skeleton.c index 36785853a14..dc3b4c7914f 100644 --- a/drivers/net/pci-skeleton.c +++ b/drivers/net/pci-skeleton.c @@ -1813,12 +1813,12 @@ static void netdrv_set_rx_mode(struct net_device *dev) rx_mode = AcceptBroadcast | AcceptMulticast | AcceptMyPhys; mc_filter[1] = mc_filter[0] = 0xffffffff; } else { - struct dev_mc_list *mclist; + struct netdev_hw_addr *ha; rx_mode = AcceptBroadcast | AcceptMulticast | AcceptMyPhys; mc_filter[1] = mc_filter[0] = 0; - netdev_for_each_mc_addr(mclist, dev) { - int bit_nr = ether_crc(ETH_ALEN, mclist->dmi_addr) >> 26; + netdev_for_each_mc_addr(ha, dev) { + int bit_nr = ether_crc(ETH_ALEN, ha->addr) >> 26; mc_filter[bit_nr >> 5] |= 1 << (bit_nr & 31); } diff --git a/drivers/net/pcmcia/axnet_cs.c b/drivers/net/pcmcia/axnet_cs.c index 09291e60d30..333cb3344d5 100644 --- a/drivers/net/pcmcia/axnet_cs.c +++ b/drivers/net/pcmcia/axnet_cs.c @@ -1623,11 +1623,11 @@ static struct net_device_stats *get_stats(struct net_device *dev) static inline void make_mc_bits(u8 *bits, struct net_device *dev) { - struct dev_mc_list *dmi; + struct netdev_hw_addr *ha; u32 crc; - netdev_for_each_mc_addr(dmi, dev) { - crc = ether_crc(ETH_ALEN, dmi->dmi_addr); + netdev_for_each_mc_addr(ha, dev) { + crc = ether_crc(ETH_ALEN, ha->addr); /* * The 8390 uses the 6 most significant bits of the * CRC to index the multicast table. diff --git a/drivers/net/pcmcia/fmvj18x_cs.c b/drivers/net/pcmcia/fmvj18x_cs.c index b9dc80b9d04..6734f7d6da9 100644 --- a/drivers/net/pcmcia/fmvj18x_cs.c +++ b/drivers/net/pcmcia/fmvj18x_cs.c @@ -1196,11 +1196,11 @@ static void set_rx_mode(struct net_device *dev) memset(mc_filter, 0x00, sizeof(mc_filter)); outb(1, ioaddr + RX_MODE); /* Ignore almost all multicasts. */ } else { - struct dev_mc_list *mclist; + struct netdev_hw_addr *ha; memset(mc_filter, 0, sizeof(mc_filter)); - netdev_for_each_mc_addr(mclist, dev) { - unsigned int bit = ether_crc_le(ETH_ALEN, mclist->dmi_addr) >> 26; + netdev_for_each_mc_addr(ha, dev) { + unsigned int bit = ether_crc_le(ETH_ALEN, ha->addr) >> 26; mc_filter[bit >> 3] |= (1 << (bit & 7)); } outb(2, ioaddr + RX_MODE); /* Use normal mode. */ diff --git a/drivers/net/pcmcia/nmclan_cs.c b/drivers/net/pcmcia/nmclan_cs.c index c717b143f11..c516c199635 100644 --- a/drivers/net/pcmcia/nmclan_cs.c +++ b/drivers/net/pcmcia/nmclan_cs.c @@ -1475,7 +1475,7 @@ static void set_multicast_list(struct net_device *dev) { mace_private *lp = netdev_priv(dev); int adr[ETHER_ADDR_LEN] = {0}; /* Ethernet address */ - struct dev_mc_list *dmi; + struct netdev_hw_addr *ha; #ifdef PCMCIA_DEBUG { @@ -1495,8 +1495,8 @@ static void set_multicast_list(struct net_device *dev) if (num_addrs > 0) { /* Calculate multicast logical address filter */ memset(lp->multicast_ladrf, 0, MACE_LADRF_LEN); - netdev_for_each_mc_addr(dmi, dev) { - memcpy(adr, dmi->dmi_addr, ETHER_ADDR_LEN); + netdev_for_each_mc_addr(ha, dev) { + memcpy(adr, ha->addr, ETHER_ADDR_LEN); BuildLAF(lp->multicast_ladrf, adr); } } diff --git a/drivers/net/pcmcia/smc91c92_cs.c b/drivers/net/pcmcia/smc91c92_cs.c index 5adc662c4bf..f45c626003a 100644 --- a/drivers/net/pcmcia/smc91c92_cs.c +++ b/drivers/net/pcmcia/smc91c92_cs.c @@ -1616,12 +1616,12 @@ static void set_rx_mode(struct net_device *dev) rx_cfg_setting = RxStripCRC | RxEnable | RxAllMulti; else { if (!netdev_mc_empty(dev)) { - struct dev_mc_list *mc_addr; + struct netdev_hw_addr *ha; - netdev_for_each_mc_addr(mc_addr, dev) { - u_int position = ether_crc(6, mc_addr->dmi_addr); + netdev_for_each_mc_addr(ha, dev) { + u_int position = ether_crc(6, ha->addr); #ifndef final_version /* Verify multicast address. */ - if ((mc_addr->dmi_addr[0] & 1) == 0) + if ((ha->addr[0] & 1) == 0) continue; #endif multicast_table[position >> 29] |= 1 << ((position >> 26) & 7); diff --git a/drivers/net/pcmcia/xirc2ps_cs.c b/drivers/net/pcmcia/xirc2ps_cs.c index 4d1802e457b..656be931207 100644 --- a/drivers/net/pcmcia/xirc2ps_cs.c +++ b/drivers/net/pcmcia/xirc2ps_cs.c @@ -1398,7 +1398,7 @@ static void set_addresses(struct net_device *dev) { unsigned int ioaddr = dev->base_addr; local_info_t *lp = netdev_priv(dev); - struct dev_mc_list *dmi; + struct netdev_hw_addr *ha; struct set_address_info sa_info; int i; @@ -1413,10 +1413,10 @@ static void set_addresses(struct net_device *dev) set_address(&sa_info, dev->dev_addr); i = 0; - netdev_for_each_mc_addr(dmi, dev) { + netdev_for_each_mc_addr(ha, dev) { if (i++ == 9) break; - set_address(&sa_info, dmi->dmi_addr); + set_address(&sa_info, ha->addr); } while (i++ < 9) set_address(&sa_info, dev->dev_addr); diff --git a/drivers/net/pcnet32.c b/drivers/net/pcnet32.c index 084d78dd163..a2254f749a9 100644 --- a/drivers/net/pcnet32.c +++ b/drivers/net/pcnet32.c @@ -2590,7 +2590,7 @@ static void pcnet32_load_multicast(struct net_device *dev) struct pcnet32_private *lp = netdev_priv(dev); volatile struct pcnet32_init_block *ib = lp->init_block; volatile __le16 *mcast_table = (__le16 *)ib->filter; - struct dev_mc_list *dmi; + struct netdev_hw_addr *ha; unsigned long ioaddr = dev->base_addr; char *addrs; int i; @@ -2611,8 +2611,8 @@ static void pcnet32_load_multicast(struct net_device *dev) ib->filter[1] = 0; /* Add addresses */ - netdev_for_each_mc_addr(dmi, dev) { - addrs = dmi->dmi_addr; + netdev_for_each_mc_addr(ha, dev) { + addrs = ha->addr; /* multicast address? */ if (!(*addrs & 1)) diff --git a/drivers/net/ps3_gelic_net.c b/drivers/net/ps3_gelic_net.c index a602da6418c..fed5df9c5ea 100644 --- a/drivers/net/ps3_gelic_net.c +++ b/drivers/net/ps3_gelic_net.c @@ -546,7 +546,7 @@ out: void gelic_net_set_multi(struct net_device *netdev) { struct gelic_card *card = netdev_card(netdev); - struct dev_mc_list *mc; + struct netdev_hw_addr *ha; unsigned int i; uint8_t *p; u64 addr; @@ -580,9 +580,9 @@ void gelic_net_set_multi(struct net_device *netdev) } /* set multicast addresses */ - netdev_for_each_mc_addr(mc, netdev) { + netdev_for_each_mc_addr(ha, netdev) { addr = 0; - p = mc->dmi_addr; + p = ha->addr; for (i = 0; i < ETH_ALEN; i++) { addr <<= 8; addr |= *p++; diff --git a/drivers/net/qlcnic/qlcnic_hw.c b/drivers/net/qlcnic/qlcnic_hw.c index 14c999ab422..9a1daa4ebe2 100644 --- a/drivers/net/qlcnic/qlcnic_hw.c +++ b/drivers/net/qlcnic/qlcnic_hw.c @@ -415,7 +415,7 @@ static int qlcnic_nic_add_mac(struct qlcnic_adapter *adapter, u8 *addr) void qlcnic_set_multi(struct net_device *netdev) { struct qlcnic_adapter *adapter = netdev_priv(netdev); - struct dev_mc_list *mc_ptr; + struct netdev_hw_addr *ha; u8 bcast_addr[ETH_ALEN] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; u32 mode = VPORT_MISS_MODE_DROP; @@ -434,8 +434,8 @@ void qlcnic_set_multi(struct net_device *netdev) } if (!netdev_mc_empty(netdev)) { - netdev_for_each_mc_addr(mc_ptr, netdev) { - qlcnic_nic_add_mac(adapter, mc_ptr->dmi_addr); + netdev_for_each_mc_addr(ha, netdev) { + qlcnic_nic_add_mac(adapter, ha->addr); } } diff --git a/drivers/net/qlge/qlge_main.c b/drivers/net/qlge/qlge_main.c index fd34f266c0a..76df96813a7 100644 --- a/drivers/net/qlge/qlge_main.c +++ b/drivers/net/qlge/qlge_main.c @@ -4207,7 +4207,7 @@ static struct net_device_stats *qlge_get_stats(struct net_device static void qlge_set_multicast_list(struct net_device *ndev) { struct ql_adapter *qdev = (struct ql_adapter *)netdev_priv(ndev); - struct dev_mc_list *mc_ptr; + struct netdev_hw_addr *ha; int i, status; status = ql_sem_spinlock(qdev, SEM_RT_IDX_MASK); @@ -4271,8 +4271,8 @@ static void qlge_set_multicast_list(struct net_device *ndev) if (status) goto exit; i = 0; - netdev_for_each_mc_addr(mc_ptr, ndev) { - if (ql_set_mac_addr_reg(qdev, (u8 *) mc_ptr->dmi_addr, + netdev_for_each_mc_addr(ha, ndev) { + if (ql_set_mac_addr_reg(qdev, (u8 *) ha->addr, MAC_ADDR_TYPE_MULTI_MAC, i)) { netif_err(qdev, hw, qdev->ndev, "Failed to loadmulticast address.\n"); diff --git a/drivers/net/r6040.c b/drivers/net/r6040.c index 15d5373dc8f..f5a0e963e68 100644 --- a/drivers/net/r6040.c +++ b/drivers/net/r6040.c @@ -938,7 +938,7 @@ static void r6040_multicast_list(struct net_device *dev) u16 *adrp; u16 reg; unsigned long flags; - struct dev_mc_list *dmi; + struct netdev_hw_addr *ha; int i; /* MAC Address */ @@ -973,8 +973,8 @@ static void r6040_multicast_list(struct net_device *dev) for (i = 0; i < 4; i++) hash_table[i] = 0; - netdev_for_each_mc_addr(dmi, dev) { - char *addrs = dmi->dmi_addr; + netdev_for_each_mc_addr(ha, dev) { + char *addrs = ha->addr; if (!(*addrs & 1)) continue; @@ -994,9 +994,9 @@ static void r6040_multicast_list(struct net_device *dev) } /* Multicast Address 1~4 case */ i = 0; - netdev_for_each_mc_addr(dmi, dev) { + netdev_for_each_mc_addr(ha, dev) { if (i < MCAST_MAX) { - adrp = (u16 *) dmi->dmi_addr; + adrp = (u16 *) ha->addr; iowrite16(adrp[0], ioaddr + MID_1L + 8 * i); iowrite16(adrp[1], ioaddr + MID_1M + 8 * i); iowrite16(adrp[2], ioaddr + MID_1H + 8 * i); diff --git a/drivers/net/r8169.c b/drivers/net/r8169.c index f7ffa5d8ffe..64cd250f642 100644 --- a/drivers/net/r8169.c +++ b/drivers/net/r8169.c @@ -4785,12 +4785,12 @@ static void rtl_set_rx_mode(struct net_device *dev) rx_mode = AcceptBroadcast | AcceptMulticast | AcceptMyPhys; mc_filter[1] = mc_filter[0] = 0xffffffff; } else { - struct dev_mc_list *mclist; + struct netdev_hw_addr *ha; rx_mode = AcceptBroadcast | AcceptMyPhys; mc_filter[1] = mc_filter[0] = 0; - netdev_for_each_mc_addr(mclist, dev) { - int bit_nr = ether_crc(ETH_ALEN, mclist->dmi_addr) >> 26; + netdev_for_each_mc_addr(ha, dev) { + int bit_nr = ether_crc(ETH_ALEN, ha->addr) >> 26; mc_filter[bit_nr >> 5] |= 1 << (bit_nr & 31); rx_mode |= AcceptMulticast; } diff --git a/drivers/net/s2io.c b/drivers/net/s2io.c index 2eb7f8a0d92..ad5a6a873b2 100644 --- a/drivers/net/s2io.c +++ b/drivers/net/s2io.c @@ -4964,7 +4964,7 @@ static struct net_device_stats *s2io_get_stats(struct net_device *dev) static void s2io_set_multicast(struct net_device *dev) { int i, j, prev_cnt; - struct dev_mc_list *mclist; + struct netdev_hw_addr *ha; struct s2io_nic *sp = netdev_priv(dev); struct XENA_dev_config __iomem *bar0 = sp->bar0; u64 val64 = 0, multi_mac = 0x010203040506ULL, mask = @@ -5093,12 +5093,12 @@ static void s2io_set_multicast(struct net_device *dev) /* Create the new Rx filter list and update the same in H/W. */ i = 0; - netdev_for_each_mc_addr(mclist, dev) { - memcpy(sp->usr_addrs[i].addr, mclist->dmi_addr, + netdev_for_each_mc_addr(ha, dev) { + memcpy(sp->usr_addrs[i].addr, ha->addr, ETH_ALEN); mac_addr = 0; for (j = 0; j < ETH_ALEN; j++) { - mac_addr |= mclist->dmi_addr[j]; + mac_addr |= ha->addr[j]; mac_addr <<= 8; } mac_addr >>= 8; diff --git a/drivers/net/sb1250-mac.c b/drivers/net/sb1250-mac.c index 9944e5d662c..f7de960ca06 100644 --- a/drivers/net/sb1250-mac.c +++ b/drivers/net/sb1250-mac.c @@ -2112,7 +2112,7 @@ static void sbmac_setmulti(struct sbmac_softc *sc) uint64_t reg; void __iomem *port; int idx; - struct dev_mc_list *mclist; + struct netdev_hw_addr *ha; struct net_device *dev = sc->sbm_dev; /* @@ -2161,10 +2161,10 @@ static void sbmac_setmulti(struct sbmac_softc *sc) * XXX if the table overflows */ idx = 1; /* skip station address */ - netdev_for_each_mc_addr(mclist, dev) { + netdev_for_each_mc_addr(ha, dev) { if (idx == MAC_ADDR_COUNT) break; - reg = sbmac_addr2reg(mclist->dmi_addr); + reg = sbmac_addr2reg(ha->addr); port = sc->sbm_base + R_MAC_ADDR_BASE+(idx * sizeof(uint64_t)); __raw_writeq(reg, port); idx++; diff --git a/drivers/net/sc92031.c b/drivers/net/sc92031.c index d87c4787fff..1b326058893 100644 --- a/drivers/net/sc92031.c +++ b/drivers/net/sc92031.c @@ -433,13 +433,13 @@ static void _sc92031_set_mar(struct net_device *dev) (dev->flags & IFF_ALLMULTI)) mar0 = mar1 = 0xffffffff; else if (dev->flags & IFF_MULTICAST) { - struct dev_mc_list *mc_list; + struct netdev_hw_addr *ha; - netdev_for_each_mc_addr(mc_list, dev) { + netdev_for_each_mc_addr(ha, dev) { u32 crc; unsigned bit = 0; - crc = ~ether_crc(ETH_ALEN, mc_list->dmi_addr); + crc = ~ether_crc(ETH_ALEN, ha->addr); crc >>= 24; if (crc & 0x01) bit |= 0x02; diff --git a/drivers/net/sfc/efx.c b/drivers/net/sfc/efx.c index 88f2fb193ab..e07b82b266d 100644 --- a/drivers/net/sfc/efx.c +++ b/drivers/net/sfc/efx.c @@ -1602,7 +1602,7 @@ static int efx_set_mac_address(struct net_device *net_dev, void *data) static void efx_set_multicast_list(struct net_device *net_dev) { struct efx_nic *efx = netdev_priv(net_dev); - struct dev_mc_list *mc_list; + struct netdev_hw_addr *ha; union efx_multicast_hash *mc_hash = &efx->multicast_hash; u32 crc; int bit; @@ -1614,8 +1614,8 @@ static void efx_set_multicast_list(struct net_device *net_dev) memset(mc_hash, 0xff, sizeof(*mc_hash)); } else { memset(mc_hash, 0x00, sizeof(*mc_hash)); - netdev_for_each_mc_addr(mc_list, net_dev) { - crc = ether_crc_le(ETH_ALEN, mc_list->dmi_addr); + netdev_for_each_mc_addr(ha, net_dev) { + crc = ether_crc_le(ETH_ALEN, ha->addr); bit = crc & (EFX_MCAST_HASH_ENTRIES - 1); set_bit_le(bit, mc_hash->byte); } diff --git a/drivers/net/sis190.c b/drivers/net/sis190.c index 760d9e83a46..6a05b93ae6c 100644 --- a/drivers/net/sis190.c +++ b/drivers/net/sis190.c @@ -848,13 +848,13 @@ static void sis190_set_rx_mode(struct net_device *dev) rx_mode = AcceptBroadcast | AcceptMulticast | AcceptMyPhys; mc_filter[1] = mc_filter[0] = 0xffffffff; } else { - struct dev_mc_list *mclist; + struct netdev_hw_addr *ha; rx_mode = AcceptBroadcast | AcceptMyPhys; mc_filter[1] = mc_filter[0] = 0; - netdev_for_each_mc_addr(mclist, dev) { + netdev_for_each_mc_addr(ha, dev) { int bit_nr = - ether_crc(ETH_ALEN, mclist->dmi_addr) & 0x3f; + ether_crc(ETH_ALEN, ha->addr) & 0x3f; mc_filter[bit_nr >> 5] |= 1 << (bit_nr & 31); rx_mode |= AcceptMulticast; } diff --git a/drivers/net/sis900.c b/drivers/net/sis900.c index bc7ff411aad..6293592635b 100644 --- a/drivers/net/sis900.c +++ b/drivers/net/sis900.c @@ -2298,12 +2298,14 @@ static void set_rx_mode(struct net_device *net_dev) /* Accept Broadcast packet, destination address matchs our * MAC address, use Receive Filter to reject unwanted MCAST * packets */ - struct dev_mc_list *mclist; + struct netdev_hw_addr *ha; rx_mode = RFAAB; - netdev_for_each_mc_addr(mclist, net_dev) { - unsigned int bit_nr = - sis900_mcast_bitnr(mclist->dmi_addr, sis_priv->chipset_rev); + netdev_for_each_mc_addr(ha, net_dev) { + unsigned int bit_nr; + + bit_nr = sis900_mcast_bitnr(ha->addr, + sis_priv->chipset_rev); mc_filter[bit_nr >> 4] |= (1 << (bit_nr & 0xf)); } } diff --git a/drivers/net/skfp/skfddi.c b/drivers/net/skfp/skfddi.c index 1921a54ea99..7e5b9f310fb 100644 --- a/drivers/net/skfp/skfddi.c +++ b/drivers/net/skfp/skfddi.c @@ -852,7 +852,7 @@ static void skfp_ctl_set_multicast_list(struct net_device *dev) static void skfp_ctl_set_multicast_list_wo_lock(struct net_device *dev) { struct s_smc *smc = netdev_priv(dev); - struct dev_mc_list *dmi; + struct netdev_hw_addr *ha; /* Enable promiscuous mode, if necessary */ if (dev->flags & IFF_PROMISC) { @@ -876,13 +876,13 @@ static void skfp_ctl_set_multicast_list_wo_lock(struct net_device *dev) /* use exact filtering */ // point to first multicast addr - netdev_for_each_mc_addr(dmi, dev) { - mac_add_multicast(smc, - (struct fddi_addr *)dmi->dmi_addr, - 1); + netdev_for_each_mc_addr(ha, dev) { + mac_add_multicast(smc, + (struct fddi_addr *)ha->addr, + 1); pr_debug(KERN_INFO "ENABLE MC ADDRESS: %pMF\n", - dmi->dmi_addr); + ha->addr); } } else { // more MC addresses than HW supports diff --git a/drivers/net/skge.c b/drivers/net/skge.c index bd8c5e8413b..de5ef3877ba 100644 --- a/drivers/net/skge.c +++ b/drivers/net/skge.c @@ -2917,7 +2917,7 @@ static void genesis_set_multicast(struct net_device *dev) struct skge_port *skge = netdev_priv(dev); struct skge_hw *hw = skge->hw; int port = skge->port; - struct dev_mc_list *list; + struct netdev_hw_addr *ha; u32 mode; u8 filter[8]; @@ -2937,8 +2937,8 @@ static void genesis_set_multicast(struct net_device *dev) skge->flow_status == FLOW_STAT_SYMMETRIC) genesis_add_filter(filter, pause_mc_addr); - netdev_for_each_mc_addr(list, dev) - genesis_add_filter(filter, list->dmi_addr); + netdev_for_each_mc_addr(ha, dev) + genesis_add_filter(filter, ha->addr); } xm_write32(hw, port, XM_MODE, mode); @@ -2956,7 +2956,7 @@ static void yukon_set_multicast(struct net_device *dev) struct skge_port *skge = netdev_priv(dev); struct skge_hw *hw = skge->hw; int port = skge->port; - struct dev_mc_list *list; + struct netdev_hw_addr *ha; int rx_pause = (skge->flow_status == FLOW_STAT_REM_SEND || skge->flow_status == FLOW_STAT_SYMMETRIC); u16 reg; @@ -2979,8 +2979,8 @@ static void yukon_set_multicast(struct net_device *dev) if (rx_pause) yukon_add_filter(filter, pause_mc_addr); - netdev_for_each_mc_addr(list, dev) - yukon_add_filter(filter, list->dmi_addr); + netdev_for_each_mc_addr(ha, dev) + yukon_add_filter(filter, ha->addr); } diff --git a/drivers/net/sky2.c b/drivers/net/sky2.c index f1c0ec308bf..5b97edb7a35 100644 --- a/drivers/net/sky2.c +++ b/drivers/net/sky2.c @@ -3627,7 +3627,7 @@ static void sky2_set_multicast(struct net_device *dev) struct sky2_port *sky2 = netdev_priv(dev); struct sky2_hw *hw = sky2->hw; unsigned port = sky2->port; - struct dev_mc_list *list; + struct netdev_hw_addr *ha; u16 reg; u8 filter[8]; int rx_pause; @@ -3651,8 +3651,8 @@ static void sky2_set_multicast(struct net_device *dev) if (rx_pause) sky2_add_filter(filter, pause_mc_addr); - netdev_for_each_mc_addr(list, dev) - sky2_add_filter(filter, list->dmi_addr); + netdev_for_each_mc_addr(ha, dev) + sky2_add_filter(filter, ha->addr); } gma_write16(hw, port, GM_MC_ADDR_H1, diff --git a/drivers/net/smc911x.c b/drivers/net/smc911x.c index 0f97b5af91e..6278734c735 100644 --- a/drivers/net/smc911x.c +++ b/drivers/net/smc911x.c @@ -1341,7 +1341,7 @@ static void smc911x_set_multicast_list(struct net_device *dev) * within that register. */ else if (!netdev_mc_empty(dev)) { - struct dev_mc_list *cur_addr; + struct netdev_hw_addr *ha; /* Set the Hash perfec mode */ mcr |= MAC_CR_HPFILT_; @@ -1349,19 +1349,16 @@ static void smc911x_set_multicast_list(struct net_device *dev) /* start with a table of all zeros: reject all */ memset(multicast_table, 0, sizeof(multicast_table)); - netdev_for_each_mc_addr(cur_addr, dev) { + netdev_for_each_mc_addr(ha, dev) { u32 position; - /* do we have a pointer here? */ - if (!cur_addr) - break; /* make sure this is a multicast address - shouldn't this be a given if we have it here ? */ - if (!(*cur_addr->dmi_addr & 1)) - continue; + if (!(*ha->addr & 1)) + continue; /* upper 6 bits are used as hash index */ - position = ether_crc(ETH_ALEN, cur_addr->dmi_addr)>>26; + position = ether_crc(ETH_ALEN, ha->addr)>>26; multicast_table[position>>5] |= 1 << (position&0x1f); } diff --git a/drivers/net/smc9194.c b/drivers/net/smc9194.c index ee1487791d5..9e5e9e792e8 100644 --- a/drivers/net/smc9194.c +++ b/drivers/net/smc9194.c @@ -417,7 +417,7 @@ static void smc_shutdown( int ioaddr ) /* - . Function: smc_setmulticast( int ioaddr, int count, dev_mc_list * adds ) + . Function: smc_setmulticast( int ioaddr, struct net_device *dev ) . Purpose: . This sets the internal hardware table to filter out unwanted multicast . packets before they take up memory. @@ -438,26 +438,23 @@ static void smc_setmulticast(int ioaddr, struct net_device *dev) { int i; unsigned char multicast_table[ 8 ]; - struct dev_mc_list *cur_addr; + struct netdev_hw_addr *ha; /* table for flipping the order of 3 bits */ unsigned char invert3[] = { 0, 4, 2, 6, 1, 5, 3, 7 }; /* start with a table of all zeros: reject all */ memset( multicast_table, 0, sizeof( multicast_table ) ); - netdev_for_each_mc_addr(cur_addr, dev) { + netdev_for_each_mc_addr(ha, dev) { int position; - /* do we have a pointer here? */ - if ( !cur_addr ) - break; /* make sure this is a multicast address - shouldn't this be a given if we have it here ? */ - if ( !( *cur_addr->dmi_addr & 1 ) ) + if (!(*ha->addr & 1)) continue; /* only use the low order bits */ - position = ether_crc_le(6, cur_addr->dmi_addr) & 0x3f; + position = ether_crc_le(6, ha->addr) & 0x3f; /* do some messy swapping to put the bit in the right spot */ multicast_table[invert3[position&7]] |= diff --git a/drivers/net/smc91x.c b/drivers/net/smc91x.c index 2e8133809dc..20c75657763 100644 --- a/drivers/net/smc91x.c +++ b/drivers/net/smc91x.c @@ -1413,7 +1413,7 @@ static void smc_set_multicast_list(struct net_device *dev) * within that register. */ else if (!netdev_mc_empty(dev)) { - struct dev_mc_list *cur_addr; + struct netdev_hw_addr *ha; /* table for flipping the order of 3 bits */ static const unsigned char invert3[] = {0, 4, 2, 6, 1, 5, 3, 7}; @@ -1421,16 +1421,16 @@ static void smc_set_multicast_list(struct net_device *dev) /* start with a table of all zeros: reject all */ memset(multicast_table, 0, sizeof(multicast_table)); - netdev_for_each_mc_addr(cur_addr, dev) { + netdev_for_each_mc_addr(ha, dev) { int position; /* make sure this is a multicast address - shouldn't this be a given if we have it here ? */ - if (!(*cur_addr->dmi_addr & 1)) + if (!(*ha->addr & 1)) continue; /* only use the low order bits */ - position = crc32_le(~0, cur_addr->dmi_addr, 6) & 0x3f; + position = crc32_le(~0, ha->addr, 6) & 0x3f; /* do some messy swapping to put the bit in the right spot */ multicast_table[invert3[position&7]] |= diff --git a/drivers/net/smsc911x.c b/drivers/net/smsc911x.c index 4fd1d8b3878..d6e3a12d65d 100644 --- a/drivers/net/smsc911x.c +++ b/drivers/net/smsc911x.c @@ -1383,13 +1383,13 @@ static void smsc911x_set_multicast_list(struct net_device *dev) /* Enabling specific multicast addresses */ unsigned int hash_high = 0; unsigned int hash_low = 0; - struct dev_mc_list *mc_list; + struct netdev_hw_addr *ha; pdata->set_bits_mask = MAC_CR_HPFILT_; pdata->clear_bits_mask = (MAC_CR_PRMS_ | MAC_CR_MCPAS_); - netdev_for_each_mc_addr(mc_list, dev) { - unsigned int bitnum = smsc911x_hash(mc_list->dmi_addr); + netdev_for_each_mc_addr(ha, dev) { + unsigned int bitnum = smsc911x_hash(ha->addr); unsigned int mask = 0x01 << (bitnum & 0x1F); if (bitnum & 0x20) diff --git a/drivers/net/smsc9420.c b/drivers/net/smsc9420.c index 34fa10d8ad4..5409ec1a5fd 100644 --- a/drivers/net/smsc9420.c +++ b/drivers/net/smsc9420.c @@ -1063,12 +1063,12 @@ static void smsc9420_set_multicast_list(struct net_device *dev) mac_cr |= MAC_CR_MCPAS_; mac_cr &= (~MAC_CR_HPFILT_); } else if (!netdev_mc_empty(dev)) { - struct dev_mc_list *mc_list; + struct netdev_hw_addr *ha; u32 hash_lo = 0, hash_hi = 0; smsc_dbg(HW, "Multicast filter enabled"); - netdev_for_each_mc_addr(mc_list, dev) { - u32 bit_num = smsc9420_hash(mc_list->dmi_addr); + netdev_for_each_mc_addr(ha, dev) { + u32 bit_num = smsc9420_hash(ha->addr); u32 mask = 1 << (bit_num & 0x1F); if (bit_num & 0x20) diff --git a/drivers/net/sonic.c b/drivers/net/sonic.c index 287c251075e..e5d67327d70 100644 --- a/drivers/net/sonic.c +++ b/drivers/net/sonic.c @@ -531,7 +531,7 @@ static void sonic_multicast_list(struct net_device *dev) { struct sonic_local *lp = netdev_priv(dev); unsigned int rcr; - struct dev_mc_list *dmi; + struct netdev_hw_addr *ha; unsigned char *addr; int i; @@ -550,8 +550,8 @@ static void sonic_multicast_list(struct net_device *dev) netdev_mc_count(dev)); sonic_set_cam_enable(dev, 1); /* always enable our own address */ i = 1; - netdev_for_each_mc_addr(dmi, dev) { - addr = dmi->dmi_addr; + netdev_for_each_mc_addr(ha, dev) { + addr = ha->addr; sonic_cda_put(dev, i, SONIC_CD_CAP0, addr[1] << 8 | addr[0]); sonic_cda_put(dev, i, SONIC_CD_CAP1, addr[3] << 8 | addr[2]); sonic_cda_put(dev, i, SONIC_CD_CAP2, addr[5] << 8 | addr[4]); diff --git a/drivers/net/spider_net.c b/drivers/net/spider_net.c index 5ba9d989f8f..c04c8f9a2d9 100644 --- a/drivers/net/spider_net.c +++ b/drivers/net/spider_net.c @@ -625,7 +625,7 @@ spider_net_get_multicast_hash(struct net_device *netdev, __u8 *addr) static void spider_net_set_multi(struct net_device *netdev) { - struct dev_mc_list *mc; + struct netdev_hw_addr *ha; u8 hash; int i; u32 reg; @@ -646,8 +646,8 @@ spider_net_set_multi(struct net_device *netdev) hash = spider_net_get_multicast_hash(netdev, netdev->broadcast); */ set_bit(0xfd, bitmask); - netdev_for_each_mc_addr(mc, netdev) { - hash = spider_net_get_multicast_hash(netdev, mc->dmi_addr); + netdev_for_each_mc_addr(ha, netdev) { + hash = spider_net_get_multicast_hash(netdev, ha->addr); set_bit(hash, bitmask); } diff --git a/drivers/net/starfire.c b/drivers/net/starfire.c index 6dfa6989901..8a6d27cdc0b 100644 --- a/drivers/net/starfire.c +++ b/drivers/net/starfire.c @@ -1766,7 +1766,7 @@ static void set_rx_mode(struct net_device *dev) struct netdev_private *np = netdev_priv(dev); void __iomem *ioaddr = np->base; u32 rx_mode = MinVLANPrio; - struct dev_mc_list *mclist; + struct netdev_hw_addr *ha; int i; #ifdef VLAN_SUPPORT @@ -1804,8 +1804,8 @@ static void set_rx_mode(struct net_device *dev) /* Use the 16 element perfect filter, skip first two entries. */ void __iomem *filter_addr = ioaddr + PerfFilterTable + 2 * 16; __be16 *eaddrs; - netdev_for_each_mc_addr(mclist, dev) { - eaddrs = (__be16 *)mclist->dmi_addr; + netdev_for_each_mc_addr(ha, dev) { + eaddrs = (__be16 *) ha->addr; writew(be16_to_cpu(eaddrs[2]), filter_addr); filter_addr += 4; writew(be16_to_cpu(eaddrs[1]), filter_addr); filter_addr += 4; writew(be16_to_cpu(eaddrs[0]), filter_addr); filter_addr += 8; @@ -1825,10 +1825,10 @@ static void set_rx_mode(struct net_device *dev) __le16 mc_filter[32] __attribute__ ((aligned(sizeof(long)))); /* Multicast hash filter */ memset(mc_filter, 0, sizeof(mc_filter)); - netdev_for_each_mc_addr(mclist, dev) { + netdev_for_each_mc_addr(ha, dev) { /* The chip uses the upper 9 CRC bits as index into the hash table */ - int bit_nr = ether_crc_le(ETH_ALEN, mclist->dmi_addr) >> 23; + int bit_nr = ether_crc_le(ETH_ALEN, ha->addr) >> 23; __le32 *fptr = (__le32 *) &mc_filter[(bit_nr >> 4) & ~1]; *fptr |= cpu_to_le32(1 << (bit_nr & 31)); diff --git a/drivers/net/stmmac/dwmac100.c b/drivers/net/stmmac/dwmac100.c index a183de29c39..c0a1c9df3ac 100644 --- a/drivers/net/stmmac/dwmac100.c +++ b/drivers/net/stmmac/dwmac100.c @@ -316,7 +316,7 @@ static void dwmac100_set_filter(struct net_device *dev) MAC_CONTROL_HO | MAC_CONTROL_HP); } else { u32 mc_filter[2]; - struct dev_mc_list *mclist; + struct netdev_hw_addr *ha; /* Perfect filter mode for physical address and Hash filter for multicast */ @@ -325,11 +325,11 @@ static void dwmac100_set_filter(struct net_device *dev) MAC_CONTROL_IF | MAC_CONTROL_HO); memset(mc_filter, 0, sizeof(mc_filter)); - netdev_for_each_mc_addr(mclist, dev) { + netdev_for_each_mc_addr(ha, dev) { /* The upper 6 bits of the calculated CRC are used to * index the contens of the hash table */ int bit_nr = - ether_crc(ETH_ALEN, mclist->dmi_addr) >> 26; + ether_crc(ETH_ALEN, ha->addr) >> 26; /* The most significant bit determines the register to * use (H/L) while the other 5 bits determine the bit * within the register. */ diff --git a/drivers/net/stmmac/dwmac1000_core.c b/drivers/net/stmmac/dwmac1000_core.c index a6538ae4694..bf73fda6adf 100644 --- a/drivers/net/stmmac/dwmac1000_core.c +++ b/drivers/net/stmmac/dwmac1000_core.c @@ -94,17 +94,17 @@ static void dwmac1000_set_filter(struct net_device *dev) writel(0xffffffff, ioaddr + GMAC_HASH_LOW); } else if (!netdev_mc_empty(dev)) { u32 mc_filter[2]; - struct dev_mc_list *mclist; + struct netdev_hw_addr *ha; /* Hash filter for multicast */ value = GMAC_FRAME_FILTER_HMC; memset(mc_filter, 0, sizeof(mc_filter)); - netdev_for_each_mc_addr(mclist, dev) { + netdev_for_each_mc_addr(ha, dev) { /* The upper 6 bits of the calculated CRC are used to index the contens of the hash table */ int bit_nr = - bitrev32(~crc32_le(~0, mclist->dmi_addr, 6)) >> 26; + bitrev32(~crc32_le(~0, ha->addr, 6)) >> 26; /* The most significant bit determines the register to * use (H/L) while the other 5 bits determine the bit * within the register. */ diff --git a/drivers/net/sun3_82586.c b/drivers/net/sun3_82586.c index 2f6a760e5f2..2cfa065c39c 100644 --- a/drivers/net/sun3_82586.c +++ b/drivers/net/sun3_82586.c @@ -413,7 +413,7 @@ static int init586(struct net_device *dev) volatile struct iasetup_cmd_struct *ias_cmd; volatile struct tdr_cmd_struct *tdr_cmd; volatile struct mcsetup_cmd_struct *mc_cmd; - struct dev_mc_list *dmi; + struct netdev_hw_addr *ha; int num_addrs=netdev_mc_count(dev); ptr = (void *) ((char *)p->scb + sizeof(struct scb_struct)); @@ -537,9 +537,9 @@ static int init586(struct net_device *dev) mc_cmd->mc_cnt = swab16(num_addrs * 6); i = 0; - netdev_for_each_mc_addr(dmi, dev) + netdev_for_each_mc_addr(ha, dev) memcpy((char *) mc_cmd->mc_list[i++], - dmi->dmi_addr, ETH_ALEN); + ha->addr, ETH_ALEN); p->scb->cbl_offset = make16(mc_cmd); p->scb->cmd_cuc = CUC_START; diff --git a/drivers/net/sunbmac.c b/drivers/net/sunbmac.c index a0bd361d5ec..4dd159b4116 100644 --- a/drivers/net/sunbmac.c +++ b/drivers/net/sunbmac.c @@ -999,7 +999,7 @@ static void bigmac_set_multicast(struct net_device *dev) { struct bigmac *bp = netdev_priv(dev); void __iomem *bregs = bp->bregs; - struct dev_mc_list *dmi; + struct netdev_hw_addr *ha; char *addrs; int i; u32 tmp, crc; @@ -1028,8 +1028,8 @@ static void bigmac_set_multicast(struct net_device *dev) for (i = 0; i < 4; i++) hash_table[i] = 0; - netdev_for_each_mc_addr(dmi, dev) { - addrs = dmi->dmi_addr; + netdev_for_each_mc_addr(ha, dev) { + addrs = ha->addr; if (!(*addrs & 1)) continue; diff --git a/drivers/net/sundance.c b/drivers/net/sundance.c index a855934dfc3..47e8cce10d1 100644 --- a/drivers/net/sundance.c +++ b/drivers/net/sundance.c @@ -1523,13 +1523,13 @@ static void set_rx_mode(struct net_device *dev) memset(mc_filter, 0xff, sizeof(mc_filter)); rx_mode = AcceptBroadcast | AcceptMulticast | AcceptMyPhys; } else if (!netdev_mc_empty(dev)) { - struct dev_mc_list *mclist; + struct netdev_hw_addr *ha; int bit; int index; int crc; memset (mc_filter, 0, sizeof (mc_filter)); - netdev_for_each_mc_addr(mclist, dev) { - crc = ether_crc_le (ETH_ALEN, mclist->dmi_addr); + netdev_for_each_mc_addr(ha, dev) { + crc = ether_crc_le(ETH_ALEN, ha->addr); for (index=0, bit=0; bit < 6; bit++, crc <<= 1) if (crc & 0x80000000) index |= 1 << bit; mc_filter[index/16] |= (1 << (index % 16)); diff --git a/drivers/net/sungem.c b/drivers/net/sungem.c index 70196bc5fe6..58a27541ae3 100644 --- a/drivers/net/sungem.c +++ b/drivers/net/sungem.c @@ -1846,12 +1846,12 @@ static u32 gem_setup_multicast(struct gem *gp) } else { u16 hash_table[16]; u32 crc; - struct dev_mc_list *dmi; + struct netdev_hw_addr *ha; int i; memset(hash_table, 0, sizeof(hash_table)); - netdev_for_each_mc_addr(dmi, gp->dev) { - char *addrs = dmi->dmi_addr; + netdev_for_each_mc_addr(ha, gp->dev) { + char *addrs = ha->addr; if (!(*addrs & 1)) continue; diff --git a/drivers/net/sunhme.c b/drivers/net/sunhme.c index b17dbb11bd6..20deb14e98f 100644 --- a/drivers/net/sunhme.c +++ b/drivers/net/sunhme.c @@ -1523,13 +1523,13 @@ static int happy_meal_init(struct happy_meal *hp) hme_write32(hp, bregs + BMAC_HTABLE3, 0xffff); } else if ((hp->dev->flags & IFF_PROMISC) == 0) { u16 hash_table[4]; - struct dev_mc_list *dmi; + struct netdev_hw_addr *ha; char *addrs; u32 crc; memset(hash_table, 0, sizeof(hash_table)); - netdev_for_each_mc_addr(dmi, hp->dev) { - addrs = dmi->dmi_addr; + netdev_for_each_mc_addr(ha, hp->dev) { + addrs = ha->addr; if (!(*addrs & 1)) continue; @@ -2362,7 +2362,7 @@ static void happy_meal_set_multicast(struct net_device *dev) { struct happy_meal *hp = netdev_priv(dev); void __iomem *bregs = hp->bigmacregs; - struct dev_mc_list *dmi; + struct netdev_hw_addr *ha; char *addrs; u32 crc; @@ -2380,8 +2380,8 @@ static void happy_meal_set_multicast(struct net_device *dev) u16 hash_table[4]; memset(hash_table, 0, sizeof(hash_table)); - netdev_for_each_mc_addr(dmi, dev) { - addrs = dmi->dmi_addr; + netdev_for_each_mc_addr(ha, dev) { + addrs = ha->addr; if (!(*addrs & 1)) continue; diff --git a/drivers/net/sunlance.c b/drivers/net/sunlance.c index d7c73f478ef..674570584bd 100644 --- a/drivers/net/sunlance.c +++ b/drivers/net/sunlance.c @@ -1170,7 +1170,7 @@ static int lance_start_xmit(struct sk_buff *skb, struct net_device *dev) static void lance_load_multicast(struct net_device *dev) { struct lance_private *lp = netdev_priv(dev); - struct dev_mc_list *dmi; + struct netdev_hw_addr *ha; char *addrs; u32 crc; u32 val; @@ -1195,8 +1195,8 @@ static void lance_load_multicast(struct net_device *dev) return; /* Add addresses */ - netdev_for_each_mc_addr(dmi, dev) { - addrs = dmi->dmi_addr; + netdev_for_each_mc_addr(ha, dev) { + addrs = ha->addr; /* multicast address? */ if (!(*addrs & 1)) diff --git a/drivers/net/sunqe.c b/drivers/net/sunqe.c index be637dce944..239f0977219 100644 --- a/drivers/net/sunqe.c +++ b/drivers/net/sunqe.c @@ -627,7 +627,7 @@ static int qe_start_xmit(struct sk_buff *skb, struct net_device *dev) static void qe_set_multicast(struct net_device *dev) { struct sunqe *qep = netdev_priv(dev); - struct dev_mc_list *dmi; + struct netdev_hw_addr *ha; u8 new_mconfig = qep->mconfig; char *addrs; int i; @@ -651,8 +651,8 @@ static void qe_set_multicast(struct net_device *dev) u8 *hbytes = (unsigned char *) &hash_table[0]; memset(hash_table, 0, sizeof(hash_table)); - netdev_for_each_mc_addr(dmi, dev) { - addrs = dmi->dmi_addr; + netdev_for_each_mc_addr(ha, dev) { + addrs = ha->addr; if (!(*addrs & 1)) continue; diff --git a/drivers/net/sunvnet.c b/drivers/net/sunvnet.c index 6b1b7cea7f6..6cf8b06be5c 100644 --- a/drivers/net/sunvnet.c +++ b/drivers/net/sunvnet.c @@ -763,12 +763,12 @@ static struct vnet_mcast_entry *__vnet_mc_find(struct vnet *vp, u8 *addr) static void __update_mc_list(struct vnet *vp, struct net_device *dev) { - struct dev_addr_list *p; + struct netdev_hw_addr *ha; - netdev_for_each_mc_addr(p, dev) { + netdev_for_each_mc_addr(ha, dev) { struct vnet_mcast_entry *m; - m = __vnet_mc_find(vp, p->dmi_addr); + m = __vnet_mc_find(vp, ha->addr); if (m) { m->hit = 1; continue; @@ -778,7 +778,7 @@ static void __update_mc_list(struct vnet *vp, struct net_device *dev) m = kzalloc(sizeof(*m), GFP_ATOMIC); if (!m) continue; - memcpy(m->addr, p->dmi_addr, ETH_ALEN); + memcpy(m->addr, ha->addr, ETH_ALEN); m->hit = 1; m->next = vp->mcast_list; diff --git a/drivers/net/tc35815.c b/drivers/net/tc35815.c index 49bd84c0d58..36149ddace4 100644 --- a/drivers/net/tc35815.c +++ b/drivers/net/tc35815.c @@ -1954,16 +1954,16 @@ tc35815_set_multicast_list(struct net_device *dev) /* Disable promiscuous mode, use normal mode. */ tc_writel(CAM_CompEn | CAM_BroadAcc | CAM_GroupAcc, &tr->CAM_Ctl); } else if (!netdev_mc_empty(dev)) { - struct dev_mc_list *cur_addr; + struct netdev_hw_addr *ha; int i; int ena_bits = CAM_Ena_Bit(CAM_ENTRY_SOURCE); tc_writel(0, &tr->CAM_Ctl); /* Walk the address list, and load the filter */ i = 0; - netdev_for_each_mc_addr(cur_addr, dev) { + netdev_for_each_mc_addr(ha, dev) { /* entry 0,1 is reserved. */ - tc35815_set_cam_entry(dev, i + 2, cur_addr->dmi_addr); + tc35815_set_cam_entry(dev, i + 2, ha->addr); ena_bits |= CAM_Ena_Bit(i + 2); i++; } diff --git a/drivers/net/tehuti.c b/drivers/net/tehuti.c index 068a47174fc..a38aede5c8d 100644 --- a/drivers/net/tehuti.c +++ b/drivers/net/tehuti.c @@ -808,7 +808,7 @@ static void bdx_setmulti(struct net_device *ndev) WRITE_REG(priv, regRX_MCST_HASH0 + i * 4, ~0); } else if (!netdev_mc_empty(ndev)) { u8 hash; - struct dev_mc_list *mclist; + struct netdev_hw_addr *ha; u32 reg, val; /* set IMF to deny all multicast frames */ @@ -825,10 +825,10 @@ static void bdx_setmulti(struct net_device *ndev) * into RX_MAC_MCST regs. we skip this phase now and accept ALL * multicast frames throu IMF */ /* accept the rest of addresses throu IMF */ - netdev_for_each_mc_addr(mclist, ndev) { + netdev_for_each_mc_addr(ha, ndev) { hash = 0; for (i = 0; i < ETH_ALEN; i++) - hash ^= mclist->dmi_addr[i]; + hash ^= ha->addr[i]; reg = regRX_MCST_HASH0 + ((hash >> 5) << 2); val = READ_REG(priv, reg); val |= (1 << (hash % 32)); diff --git a/drivers/net/tg3.c b/drivers/net/tg3.c index 22cf1c446de..aff11f84f52 100644 --- a/drivers/net/tg3.c +++ b/drivers/net/tg3.c @@ -9457,14 +9457,14 @@ static void __tg3_set_rx_mode(struct net_device *dev) tg3_set_multi (tp, 0); } else { /* Accept one or more multicast(s). */ - struct dev_mc_list *mclist; + struct netdev_hw_addr *ha; u32 mc_filter[4] = { 0, }; u32 regidx; u32 bit; u32 crc; - netdev_for_each_mc_addr(mclist, dev) { - crc = calc_crc (mclist->dmi_addr, ETH_ALEN); + netdev_for_each_mc_addr(ha, dev) { + crc = calc_crc(ha->addr, ETH_ALEN); bit = ~crc & 0x7f; regidx = (bit & 0x60) >> 5; bit &= 0x1f; diff --git a/drivers/net/tlan.c b/drivers/net/tlan.c index 05ea30a94e8..8ffec22b74b 100644 --- a/drivers/net/tlan.c +++ b/drivers/net/tlan.c @@ -1314,7 +1314,7 @@ static struct net_device_stats *TLan_GetStats( struct net_device *dev ) static void TLan_SetMulticastList( struct net_device *dev ) { - struct dev_mc_list *dmi; + struct netdev_hw_addr *ha; u32 hash1 = 0; u32 hash2 = 0; int i; @@ -1336,12 +1336,12 @@ static void TLan_SetMulticastList( struct net_device *dev ) TLan_DioWrite32( dev->base_addr, TLAN_HASH_2, 0xFFFFFFFF ); } else { i = 0; - netdev_for_each_mc_addr(dmi, dev) { + netdev_for_each_mc_addr(ha, dev) { if ( i < 3 ) { TLan_SetMac( dev, i + 1, - (char *) &dmi->dmi_addr ); + (char *) &ha->addr); } else { - offset = TLan_HashFunc( (u8 *) &dmi->dmi_addr ); + offset = TLan_HashFunc((u8 *)&ha->addr); if ( offset < 32 ) hash1 |= ( 1 << offset ); else diff --git a/drivers/net/tokenring/3c359.c b/drivers/net/tokenring/3c359.c index 8c54d89e60d..4673e38c52a 100644 --- a/drivers/net/tokenring/3c359.c +++ b/drivers/net/tokenring/3c359.c @@ -1390,7 +1390,7 @@ static int xl_close(struct net_device *dev) static void xl_set_rx_mode(struct net_device *dev) { struct xl_private *xl_priv = netdev_priv(dev); - struct dev_mc_list *dmi; + struct netdev_hw_addr *ha; unsigned char dev_mc_address[4] ; u16 options ; @@ -1407,11 +1407,11 @@ static void xl_set_rx_mode(struct net_device *dev) dev_mc_address[0] = dev_mc_address[1] = dev_mc_address[2] = dev_mc_address[3] = 0 ; - netdev_for_each_mc_addr(dmi, dev) { - dev_mc_address[0] |= dmi->dmi_addr[2] ; - dev_mc_address[1] |= dmi->dmi_addr[3] ; - dev_mc_address[2] |= dmi->dmi_addr[4] ; - dev_mc_address[3] |= dmi->dmi_addr[5] ; + netdev_for_each_mc_addr(ha, dev) { + dev_mc_address[0] |= ha->addr[2]; + dev_mc_address[1] |= ha->addr[3]; + dev_mc_address[2] |= ha->addr[4]; + dev_mc_address[3] |= ha->addr[5]; } if (memcmp(xl_priv->xl_functional_addr,dev_mc_address,4) != 0) { /* Options have changed, run the command */ diff --git a/drivers/net/tokenring/ibmtr.c b/drivers/net/tokenring/ibmtr.c index 1a0967246e2..eebdaae2432 100644 --- a/drivers/net/tokenring/ibmtr.c +++ b/drivers/net/tokenring/ibmtr.c @@ -986,7 +986,7 @@ static void open_sap(unsigned char type, struct net_device *dev) static void tok_set_multicast_list(struct net_device *dev) { struct tok_info *ti = netdev_priv(dev); - struct dev_mc_list *mclist; + struct netdev_hw_addr *ha; unsigned char address[4]; int i; @@ -995,11 +995,11 @@ static void tok_set_multicast_list(struct net_device *dev) /*BMS ifconfig tr down or hot unplug a PCMCIA card ??hownowbrowncow*/ if (/*BMSHELPdev->start == 0 ||*/ ti->open_status != OPEN) return; address[0] = address[1] = address[2] = address[3] = 0; - netdev_for_each_mc_addr(mclist, dev) { - address[0] |= mclist->dmi_addr[2]; - address[1] |= mclist->dmi_addr[3]; - address[2] |= mclist->dmi_addr[4]; - address[3] |= mclist->dmi_addr[5]; + netdev_for_each_mc_addr(ha, dev) { + address[0] |= ha->addr[2]; + address[1] |= ha->addr[3]; + address[2] |= ha->addr[4]; + address[3] |= ha->addr[5]; } SET_PAGE(ti->srb_page); for (i = 0; i < sizeof(struct srb_set_funct_addr); i++) diff --git a/drivers/net/tokenring/lanstreamer.c b/drivers/net/tokenring/lanstreamer.c index 01c780f25e9..88c893100c2 100644 --- a/drivers/net/tokenring/lanstreamer.c +++ b/drivers/net/tokenring/lanstreamer.c @@ -1266,7 +1266,7 @@ static void streamer_set_rx_mode(struct net_device *dev) netdev_priv(dev); __u8 __iomem *streamer_mmio = streamer_priv->streamer_mmio; __u8 options = 0; - struct dev_mc_list *dmi; + struct netdev_hw_addr *ha; unsigned char dev_mc_address[5]; writel(streamer_priv->srb, streamer_mmio + LAPA); @@ -1302,11 +1302,11 @@ static void streamer_set_rx_mode(struct net_device *dev) writel(streamer_priv->srb,streamer_mmio+LAPA); dev_mc_address[0] = dev_mc_address[1] = dev_mc_address[2] = dev_mc_address[3] = 0 ; - netdev_for_each_mc_addr(dmi, dev) { - dev_mc_address[0] |= dmi->dmi_addr[2] ; - dev_mc_address[1] |= dmi->dmi_addr[3] ; - dev_mc_address[2] |= dmi->dmi_addr[4] ; - dev_mc_address[3] |= dmi->dmi_addr[5] ; + netdev_for_each_mc_addr(ha, dev) { + dev_mc_address[0] |= ha->addr[2]; + dev_mc_address[1] |= ha->addr[3]; + dev_mc_address[2] |= ha->addr[4]; + dev_mc_address[3] |= ha->addr[5]; } writew(htons(SRB_SET_FUNC_ADDRESS << 8),streamer_mmio+LAPDINC); diff --git a/drivers/net/tokenring/olympic.c b/drivers/net/tokenring/olympic.c index 3decaf4b6cf..3d2fbe60b46 100644 --- a/drivers/net/tokenring/olympic.c +++ b/drivers/net/tokenring/olympic.c @@ -1139,7 +1139,7 @@ static void olympic_set_rx_mode(struct net_device *dev) u8 __iomem *olympic_mmio = olympic_priv->olympic_mmio ; u8 options = 0; u8 __iomem *srb; - struct dev_mc_list *dmi; + struct netdev_hw_addr *ha; unsigned char dev_mc_address[4] ; writel(olympic_priv->srb,olympic_mmio+LAPA); @@ -1177,11 +1177,11 @@ static void olympic_set_rx_mode(struct net_device *dev) dev_mc_address[0] = dev_mc_address[1] = dev_mc_address[2] = dev_mc_address[3] = 0 ; - netdev_for_each_mc_addr(dmi, dev) { - dev_mc_address[0] |= dmi->dmi_addr[2] ; - dev_mc_address[1] |= dmi->dmi_addr[3] ; - dev_mc_address[2] |= dmi->dmi_addr[4] ; - dev_mc_address[3] |= dmi->dmi_addr[5] ; + netdev_for_each_mc_addr(ha, dev) { + dev_mc_address[0] |= ha->addr[2]; + dev_mc_address[1] |= ha->addr[3]; + dev_mc_address[2] |= ha->addr[4]; + dev_mc_address[3] |= ha->addr[5]; } writeb(SRB_SET_FUNC_ADDRESS,srb+0); diff --git a/drivers/net/tokenring/tms380tr.c b/drivers/net/tokenring/tms380tr.c index b545e20d289..9fa2c36ff90 100644 --- a/drivers/net/tokenring/tms380tr.c +++ b/drivers/net/tokenring/tms380tr.c @@ -1212,17 +1212,17 @@ static void tms380tr_set_multicast_list(struct net_device *dev) } else { - struct dev_mc_list *mclist; + struct netdev_hw_addr *ha; - netdev_for_each_mc_addr(mclist, dev) { + netdev_for_each_mc_addr(ha, dev) { ((char *)(&tp->ocpl.FunctAddr))[0] |= - mclist->dmi_addr[2]; + ha->addr[2]; ((char *)(&tp->ocpl.FunctAddr))[1] |= - mclist->dmi_addr[3]; + ha->addr[3]; ((char *)(&tp->ocpl.FunctAddr))[2] |= - mclist->dmi_addr[4]; + ha->addr[4]; ((char *)(&tp->ocpl.FunctAddr))[3] |= - mclist->dmi_addr[5]; + ha->addr[5]; } } tms380tr_exec_cmd(dev, OC_SET_FUNCT_ADDR); diff --git a/drivers/net/tsi108_eth.c b/drivers/net/tsi108_eth.c index 80333a4d328..1366541c30a 100644 --- a/drivers/net/tsi108_eth.c +++ b/drivers/net/tsi108_eth.c @@ -1186,15 +1186,15 @@ static void tsi108_set_rx_mode(struct net_device *dev) if (dev->flags & IFF_ALLMULTI || !netdev_mc_empty(dev)) { int i; - struct dev_mc_list *mc; + struct netdev_hw_addr *ha; rxcfg |= TSI108_EC_RXCFG_MFE | TSI108_EC_RXCFG_MC_HASH; memset(data->mc_hash, 0, sizeof(data->mc_hash)); - netdev_for_each_mc_addr(mc, dev) { + netdev_for_each_mc_addr(ha, dev) { u32 hash, crc; - crc = ether_crc(6, mc->dmi_addr); + crc = ether_crc(6, ha->addr); hash = crc >> 23; __set_bit(hash, &data->mc_hash[0]); } diff --git a/drivers/net/tulip/de2104x.c b/drivers/net/tulip/de2104x.c index cb429723b2c..a68b9176462 100644 --- a/drivers/net/tulip/de2104x.c +++ b/drivers/net/tulip/de2104x.c @@ -670,15 +670,15 @@ static void build_setup_frame_hash(u16 *setup_frm, struct net_device *dev) { struct de_private *de = netdev_priv(dev); u16 hash_table[32]; - struct dev_mc_list *mclist; + struct netdev_hw_addr *ha; int i; u16 *eaddrs; memset(hash_table, 0, sizeof(hash_table)); set_bit_le(255, hash_table); /* Broadcast entry */ /* This should work on big-endian machines as well. */ - netdev_for_each_mc_addr(mclist, dev) { - int index = ether_crc_le(ETH_ALEN, mclist->dmi_addr) & 0x1ff; + netdev_for_each_mc_addr(ha, dev) { + int index = ether_crc_le(ETH_ALEN, ha->addr) & 0x1ff; set_bit_le(index, hash_table); } @@ -699,13 +699,13 @@ static void build_setup_frame_hash(u16 *setup_frm, struct net_device *dev) static void build_setup_frame_perfect(u16 *setup_frm, struct net_device *dev) { struct de_private *de = netdev_priv(dev); - struct dev_mc_list *mclist; + struct netdev_hw_addr *ha; u16 *eaddrs; /* We have <= 14 addresses so we can use the wonderful 16 address perfect filtering of the Tulip. */ - netdev_for_each_mc_addr(mclist, dev) { - eaddrs = (u16 *)mclist->dmi_addr; + netdev_for_each_mc_addr(ha, dev) { + eaddrs = (u16 *) ha->addr; *setup_frm++ = *eaddrs; *setup_frm++ = *eaddrs++; *setup_frm++ = *eaddrs; *setup_frm++ = *eaddrs++; *setup_frm++ = *eaddrs; *setup_frm++ = *eaddrs++; diff --git a/drivers/net/tulip/de4x5.c b/drivers/net/tulip/de4x5.c index 55ade8949a6..cac4bcc8318 100644 --- a/drivers/net/tulip/de4x5.c +++ b/drivers/net/tulip/de4x5.c @@ -1951,7 +1951,7 @@ static void SetMulticastFilter(struct net_device *dev) { struct de4x5_private *lp = netdev_priv(dev); - struct dev_mc_list *dmi; + struct netdev_hw_addr *ha; u_long iobase = dev->base_addr; int i, bit, byte; u16 hashcode; @@ -1966,8 +1966,8 @@ SetMulticastFilter(struct net_device *dev) if ((dev->flags & IFF_ALLMULTI) || (netdev_mc_count(dev) > 14)) { omr |= OMR_PM; /* Pass all multicasts */ } else if (lp->setup_f == HASH_PERF) { /* Hash Filtering */ - netdev_for_each_mc_addr(dmi, dev) { - addrs = dmi->dmi_addr; + netdev_for_each_mc_addr(ha, dev) { + addrs = ha->addr; if ((*addrs & 0x01) == 1) { /* multicast address? */ crc = ether_crc_le(ETH_ALEN, addrs); hashcode = crc & HASH_BITS; /* hashcode is 9 LSb of CRC */ @@ -1983,8 +1983,8 @@ SetMulticastFilter(struct net_device *dev) } } } else { /* Perfect filtering */ - netdev_for_each_mc_addr(dmi, dev) { - addrs = dmi->dmi_addr; + netdev_for_each_mc_addr(ha, dev) { + addrs = ha->addr; for (i=0; ibase_addr+0xc0; /* ID Table */ u32 hash_val; @@ -1478,8 +1478,8 @@ static void dm9132_id_table(struct DEVICE *dev) hash_table[3] = 0x8000; /* the multicast address in Hash Table : 64 bits */ - netdev_for_each_mc_addr(mcptr, dev) { - hash_val = cal_CRC((char *) mcptr->dmi_addr, 6, 0) & 0x3f; + netdev_for_each_mc_addr(ha, dev) { + hash_val = cal_CRC((char *) ha->addr, 6, 0) & 0x3f; hash_table[hash_val / 16] |= (u16) 1 << (hash_val % 16); } @@ -1497,7 +1497,7 @@ static void dm9132_id_table(struct DEVICE *dev) static void send_filter_frame(struct DEVICE *dev) { struct dmfe_board_info *db = netdev_priv(dev); - struct dev_mc_list *mcptr; + struct netdev_hw_addr *ha; struct tx_desc *txptr; u16 * addrptr; u32 * suptr; @@ -1520,8 +1520,8 @@ static void send_filter_frame(struct DEVICE *dev) *suptr++ = 0xffff; /* fit the multicast address */ - netdev_for_each_mc_addr(mcptr, dev) { - addrptr = (u16 *) mcptr->dmi_addr; + netdev_for_each_mc_addr(ha, dev) { + addrptr = (u16 *) ha->addr; *suptr++ = addrptr[0]; *suptr++ = addrptr[1]; *suptr++ = addrptr[2]; diff --git a/drivers/net/tulip/tulip_core.c b/drivers/net/tulip/tulip_core.c index 7f544ef2f5f..c4b7cd726b6 100644 --- a/drivers/net/tulip/tulip_core.c +++ b/drivers/net/tulip/tulip_core.c @@ -990,15 +990,15 @@ static void build_setup_frame_hash(u16 *setup_frm, struct net_device *dev) { struct tulip_private *tp = netdev_priv(dev); u16 hash_table[32]; - struct dev_mc_list *mclist; + struct netdev_hw_addr *ha; int i; u16 *eaddrs; memset(hash_table, 0, sizeof(hash_table)); set_bit_le(255, hash_table); /* Broadcast entry */ /* This should work on big-endian machines as well. */ - netdev_for_each_mc_addr(mclist, dev) { - int index = ether_crc_le(ETH_ALEN, mclist->dmi_addr) & 0x1ff; + netdev_for_each_mc_addr(ha, dev) { + int index = ether_crc_le(ETH_ALEN, ha->addr) & 0x1ff; set_bit_le(index, hash_table); } @@ -1018,13 +1018,13 @@ static void build_setup_frame_hash(u16 *setup_frm, struct net_device *dev) static void build_setup_frame_perfect(u16 *setup_frm, struct net_device *dev) { struct tulip_private *tp = netdev_priv(dev); - struct dev_mc_list *mclist; + struct netdev_hw_addr *ha; u16 *eaddrs; /* We have <= 14 addresses so we can use the wonderful 16 address perfect filtering of the Tulip. */ - netdev_for_each_mc_addr(mclist, dev) { - eaddrs = (u16 *)mclist->dmi_addr; + netdev_for_each_mc_addr(ha, dev) { + eaddrs = (u16 *) ha->addr; *setup_frm++ = *eaddrs; *setup_frm++ = *eaddrs++; *setup_frm++ = *eaddrs; *setup_frm++ = *eaddrs++; *setup_frm++ = *eaddrs; *setup_frm++ = *eaddrs++; @@ -1061,7 +1061,7 @@ static void set_rx_mode(struct net_device *dev) } else if (tp->flags & MC_HASH_ONLY) { /* Some work-alikes have only a 64-entry hash filter table. */ /* Should verify correctness on big-endian/__powerpc__ */ - struct dev_mc_list *mclist; + struct netdev_hw_addr *ha; if (netdev_mc_count(dev) > 64) { /* Arbitrary non-effective limit. */ tp->csr6 |= AcceptAllMulticast; @@ -1069,18 +1069,21 @@ static void set_rx_mode(struct net_device *dev) } else { u32 mc_filter[2] = {0, 0}; /* Multicast hash filter */ int filterbit; - netdev_for_each_mc_addr(mclist, dev) { + netdev_for_each_mc_addr(ha, dev) { if (tp->flags & COMET_MAC_ADDR) - filterbit = ether_crc_le(ETH_ALEN, mclist->dmi_addr); + filterbit = ether_crc_le(ETH_ALEN, + ha->addr); else - filterbit = ether_crc(ETH_ALEN, mclist->dmi_addr) >> 26; + filterbit = ether_crc(ETH_ALEN, + ha->addr) >> 26; filterbit &= 0x3f; mc_filter[filterbit >> 5] |= 1 << (filterbit & 31); if (tulip_debug > 2) dev_info(&dev->dev, "Added filter for %pM %08x bit %d\n", - mclist->dmi_addr, - ether_crc(ETH_ALEN, mclist->dmi_addr), filterbit); + ha->addr, + ether_crc(ETH_ALEN, ha->addr), + filterbit); } if (mc_filter[0] == tp->mc_filter[0] && mc_filter[1] == tp->mc_filter[1]) diff --git a/drivers/net/tulip/uli526x.c b/drivers/net/tulip/uli526x.c index 0ab05af237e..b79d908fe34 100644 --- a/drivers/net/tulip/uli526x.c +++ b/drivers/net/tulip/uli526x.c @@ -1392,7 +1392,7 @@ static void update_cr6(u32 cr6_data, unsigned long ioaddr) static void send_filter_frame(struct net_device *dev, int mc_cnt) { struct uli526x_board_info *db = netdev_priv(dev); - struct dev_mc_list *mcptr; + struct netdev_hw_addr *ha; struct tx_desc *txptr; u16 * addrptr; u32 * suptr; @@ -1415,8 +1415,8 @@ static void send_filter_frame(struct net_device *dev, int mc_cnt) *suptr++ = 0xffff << FLT_SHIFT; /* fit the multicast address */ - netdev_for_each_mc_addr(mcptr, dev) { - addrptr = (u16 *) mcptr->dmi_addr; + netdev_for_each_mc_addr(ha, dev) { + addrptr = (u16 *) ha->addr; *suptr++ = addrptr[0] << FLT_SHIFT; *suptr++ = addrptr[1] << FLT_SHIFT; *suptr++ = addrptr[2] << FLT_SHIFT; diff --git a/drivers/net/tulip/winbond-840.c b/drivers/net/tulip/winbond-840.c index 304f43866c4..3e3822c98a0 100644 --- a/drivers/net/tulip/winbond-840.c +++ b/drivers/net/tulip/winbond-840.c @@ -1367,13 +1367,15 @@ static u32 __set_rx_mode(struct net_device *dev) memset(mc_filter, 0xff, sizeof(mc_filter)); rx_mode = RxAcceptBroadcast | AcceptMulticast | AcceptMyPhys; } else { - struct dev_mc_list *mclist; + struct netdev_hw_addr *ha; memset(mc_filter, 0, sizeof(mc_filter)); - netdev_for_each_mc_addr(mclist, dev) { - int filterbit = (ether_crc(ETH_ALEN, mclist->dmi_addr) >> 26) ^ 0x3F; - filterbit &= 0x3f; - mc_filter[filterbit >> 5] |= 1 << (filterbit & 31); + netdev_for_each_mc_addr(ha, dev) { + int filbit; + + filbit = (ether_crc(ETH_ALEN, ha->addr) >> 26) ^ 0x3F; + filbit &= 0x3f; + mc_filter[filbit >> 5] |= 1 << (filbit & 31); } rx_mode = RxAcceptBroadcast | AcceptMulticast | AcceptMyPhys; } diff --git a/drivers/net/typhoon.c b/drivers/net/typhoon.c index cd24e5f2b2a..3053f85aa06 100644 --- a/drivers/net/typhoon.c +++ b/drivers/net/typhoon.c @@ -921,11 +921,11 @@ typhoon_set_rx_mode(struct net_device *dev) /* Too many to match, or accept all multicasts. */ filter |= TYPHOON_RX_FILTER_ALL_MCAST; } else if (!netdev_mc_empty(dev)) { - struct dev_mc_list *mclist; + struct netdev_hw_addr *ha; memset(mc_filter, 0, sizeof(mc_filter)); - netdev_for_each_mc_addr(mclist, dev) { - int bit = ether_crc(ETH_ALEN, mclist->dmi_addr) & 0x3f; + netdev_for_each_mc_addr(ha, dev) { + int bit = ether_crc(ETH_ALEN, ha->addr) & 0x3f; mc_filter[bit >> 5] |= 1 << (bit & 0x1f); } diff --git a/drivers/net/ucc_geth.c b/drivers/net/ucc_geth.c index 75743a76bbe..081f76bff34 100644 --- a/drivers/net/ucc_geth.c +++ b/drivers/net/ucc_geth.c @@ -1999,7 +1999,7 @@ static void ucc_geth_memclean(struct ucc_geth_private *ugeth) static void ucc_geth_set_multi(struct net_device *dev) { struct ucc_geth_private *ugeth; - struct dev_mc_list *dmi; + struct netdev_hw_addr *ha; struct ucc_fast __iomem *uf_regs; struct ucc_geth_82xx_address_filtering_pram __iomem *p_82xx_addr_filt; @@ -2028,16 +2028,16 @@ static void ucc_geth_set_multi(struct net_device *dev) out_be32(&p_82xx_addr_filt->gaddr_h, 0x0); out_be32(&p_82xx_addr_filt->gaddr_l, 0x0); - netdev_for_each_mc_addr(dmi, dev) { + netdev_for_each_mc_addr(ha, dev) { /* Only support group multicast for now. */ - if (!(dmi->dmi_addr[0] & 1)) + if (!(ha->addr[0] & 1)) continue; /* Ask CPM to run CRC and set bit in * filter mask. */ - hw_add_addr_in_hash(ugeth, dmi->dmi_addr); + hw_add_addr_in_hash(ugeth, ha->addr); } } } diff --git a/drivers/net/usb/asix.c b/drivers/net/usb/asix.c index 9e05639435f..763364f0972 100644 --- a/drivers/net/usb/asix.c +++ b/drivers/net/usb/asix.c @@ -557,16 +557,14 @@ static void asix_set_multicast(struct net_device *net) * for our 8 byte filter buffer * to avoid allocating memory that * is tricky to free later */ - struct dev_mc_list *mc_list; + struct netdev_hw_addr *ha; u32 crc_bits; memset(data->multi_filter, 0, AX_MCAST_FILTER_SIZE); /* Build the multicast hash filter. */ - netdev_for_each_mc_addr(mc_list, net) { - crc_bits = - ether_crc(ETH_ALEN, - mc_list->dmi_addr) >> 26; + netdev_for_each_mc_addr(ha, net) { + crc_bits = ether_crc(ETH_ALEN, ha->addr) >> 26; data->multi_filter[crc_bits >> 3] |= 1 << (crc_bits & 7); } @@ -793,16 +791,14 @@ static void ax88172_set_multicast(struct net_device *net) * for our 8 byte filter buffer * to avoid allocating memory that * is tricky to free later */ - struct dev_mc_list *mc_list; + struct netdev_hw_addr *ha; u32 crc_bits; memset(data->multi_filter, 0, AX_MCAST_FILTER_SIZE); /* Build the multicast hash filter. */ - netdev_for_each_mc_addr(mc_list, net) { - crc_bits = - ether_crc(ETH_ALEN, - mc_list->dmi_addr) >> 26; + netdev_for_each_mc_addr(ha, net) { + crc_bits = ether_crc(ETH_ALEN, ha->addr) >> 26; data->multi_filter[crc_bits >> 3] |= 1 << (crc_bits & 7); } diff --git a/drivers/net/usb/catc.c b/drivers/net/usb/catc.c index 96f1ebe0d34..0c48ff97f47 100644 --- a/drivers/net/usb/catc.c +++ b/drivers/net/usb/catc.c @@ -629,7 +629,7 @@ static void catc_multicast(unsigned char *addr, u8 *multicast) static void catc_set_multicast_list(struct net_device *netdev) { struct catc *catc = netdev_priv(netdev); - struct dev_mc_list *mc; + struct netdev_hw_addr *ha; u8 broadcast[6]; u8 rx = RxEnable | RxPolarity | RxMultiCast; @@ -647,8 +647,8 @@ static void catc_set_multicast_list(struct net_device *netdev) if (netdev->flags & IFF_ALLMULTI) { memset(catc->multicast, 0xff, 64); } else { - netdev_for_each_mc_addr(mc, netdev) { - u32 crc = ether_crc_le(6, mc->dmi_addr); + netdev_for_each_mc_addr(ha, netdev) { + u32 crc = ether_crc_le(6, ha->addr); if (!catc->is_f5u011) { catc->multicast[(crc >> 3) & 0x3f] |= 1 << (crc & 7); } else { diff --git a/drivers/net/usb/dm9601.c b/drivers/net/usb/dm9601.c index 269339769f4..4eb1fb31ff0 100644 --- a/drivers/net/usb/dm9601.c +++ b/drivers/net/usb/dm9601.c @@ -386,10 +386,10 @@ static void dm9601_set_multicast(struct net_device *net) netdev_mc_count(net) > DM_MAX_MCAST) { rx_ctl |= 0x04; } else if (!netdev_mc_empty(net)) { - struct dev_mc_list *mc_list; + struct netdev_hw_addr *ha; - netdev_for_each_mc_addr(mc_list, net) { - u32 crc = ether_crc(ETH_ALEN, mc_list->dmi_addr) >> 26; + netdev_for_each_mc_addr(ha, net) { + u32 crc = ether_crc(ETH_ALEN, ha->addr) >> 26; hashes[crc >> 3] |= 1 << (crc & 0x7); } } diff --git a/drivers/net/usb/mcs7830.c b/drivers/net/usb/mcs7830.c index 70978219e98..0dc92c8ba4b 100644 --- a/drivers/net/usb/mcs7830.c +++ b/drivers/net/usb/mcs7830.c @@ -452,12 +452,12 @@ static void mcs7830_data_set_multicast(struct net_device *net) * for our 8 byte filter buffer * to avoid allocating memory that * is tricky to free later */ - struct dev_mc_list *mc_list; + struct netdev_hw_addr *ha; u32 crc_bits; /* Build the multicast hash filter. */ - netdev_for_each_mc_addr(mc_list, net) { - crc_bits = ether_crc(ETH_ALEN, mc_list->dmi_addr) >> 26; + netdev_for_each_mc_addr(ha, net) { + crc_bits = ether_crc(ETH_ALEN, ha->addr) >> 26; data->multi_filter[crc_bits >> 3] |= 1 << (crc_bits & 7); } } diff --git a/drivers/net/usb/smsc75xx.c b/drivers/net/usb/smsc75xx.c index 300e3e764fa..b8b00d06ea7 100644 --- a/drivers/net/usb/smsc75xx.c +++ b/drivers/net/usb/smsc75xx.c @@ -444,14 +444,14 @@ static void smsc75xx_set_multicast(struct net_device *netdev) netif_dbg(dev, drv, dev->net, "receive all multicast enabled"); pdata->rfe_ctl |= RFE_CTL_AM | RFE_CTL_DPF; } else if (!netdev_mc_empty(dev->net)) { - struct dev_mc_list *mc_list; + struct netdev_hw_addr *ha; netif_dbg(dev, drv, dev->net, "receive multicast hash filter"); pdata->rfe_ctl |= RFE_CTL_MHF | RFE_CTL_DPF; - netdev_for_each_mc_addr(mc_list, netdev) { - u32 bitnum = smsc75xx_hash(mc_list->dmi_addr); + netdev_for_each_mc_addr(ha, netdev) { + u32 bitnum = smsc75xx_hash(ha->addr); pdata->multicast_hash_table[bitnum / 32] |= (1 << (bitnum % 32)); } diff --git a/drivers/net/usb/smsc95xx.c b/drivers/net/usb/smsc95xx.c index 73f9a31cf94..ccd55ca3a06 100644 --- a/drivers/net/usb/smsc95xx.c +++ b/drivers/net/usb/smsc95xx.c @@ -384,13 +384,13 @@ static void smsc95xx_set_multicast(struct net_device *netdev) pdata->mac_cr |= MAC_CR_MCPAS_; pdata->mac_cr &= ~(MAC_CR_PRMS_ | MAC_CR_HPFILT_); } else if (!netdev_mc_empty(dev->net)) { - struct dev_mc_list *mc_list; + struct netdev_hw_addr *ha; pdata->mac_cr |= MAC_CR_HPFILT_; pdata->mac_cr &= ~(MAC_CR_PRMS_ | MAC_CR_MCPAS_); - netdev_for_each_mc_addr(mc_list, netdev) { - u32 bitnum = smsc95xx_hash(mc_list->dmi_addr); + netdev_for_each_mc_addr(ha, netdev) { + u32 bitnum = smsc95xx_hash(ha->addr); u32 mask = 0x01 << (bitnum & 0x1F); if (bitnum & 0x20) hash_hi |= mask; diff --git a/drivers/net/via-rhine.c b/drivers/net/via-rhine.c index 50f881aa393..d9133c62a2f 100644 --- a/drivers/net/via-rhine.c +++ b/drivers/net/via-rhine.c @@ -1704,11 +1704,11 @@ static void rhine_set_rx_mode(struct net_device *dev) iowrite32(0xffffffff, ioaddr + MulticastFilter1); rx_mode = 0x0C; } else { - struct dev_mc_list *mclist; + struct netdev_hw_addr *ha; memset(mc_filter, 0, sizeof(mc_filter)); - netdev_for_each_mc_addr(mclist, dev) { - int bit_nr = ether_crc(ETH_ALEN, mclist->dmi_addr) >> 26; + netdev_for_each_mc_addr(ha, dev) { + int bit_nr = ether_crc(ETH_ALEN, ha->addr) >> 26; mc_filter[bit_nr >> 5] |= 1 << (bit_nr & 31); } diff --git a/drivers/net/via-velocity.c b/drivers/net/via-velocity.c index 3a486f3bad3..91f3b841288 100644 --- a/drivers/net/via-velocity.c +++ b/drivers/net/via-velocity.c @@ -1126,7 +1126,7 @@ static void velocity_set_multi(struct net_device *dev) struct mac_regs __iomem *regs = vptr->mac_regs; u8 rx_mode; int i; - struct dev_mc_list *mclist; + struct netdev_hw_addr *ha; if (dev->flags & IFF_PROMISC) { /* Set promiscuous. */ writel(0xffffffff, ®s->MARCAM[0]); @@ -1142,8 +1142,8 @@ static void velocity_set_multi(struct net_device *dev) mac_get_cam_mask(regs, vptr->mCAMmask); i = 0; - netdev_for_each_mc_addr(mclist, dev) { - mac_set_cam(regs, i + offset, mclist->dmi_addr); + netdev_for_each_mc_addr(ha, dev) { + mac_set_cam(regs, i + offset, ha->addr); vptr->mCAMmask[(offset + i) / 8] |= 1 << ((offset + i) & 7); i++; } diff --git a/drivers/net/virtio_net.c b/drivers/net/virtio_net.c index 3f5be35dea2..ecec9a8527f 100644 --- a/drivers/net/virtio_net.c +++ b/drivers/net/virtio_net.c @@ -721,7 +721,6 @@ static void virtnet_set_rx_mode(struct net_device *dev) struct scatterlist sg[2]; u8 promisc, allmulti; struct virtio_net_ctrl_mac *mac_data; - struct dev_addr_list *addr; struct netdev_hw_addr *ha; int uc_count; int mc_count; @@ -778,8 +777,8 @@ static void virtnet_set_rx_mode(struct net_device *dev) mac_data->entries = mc_count; i = 0; - netdev_for_each_mc_addr(addr, dev) - memcpy(&mac_data->macs[i++][0], addr->da_addr, ETH_ALEN); + netdev_for_each_mc_addr(ha, dev) + memcpy(&mac_data->macs[i++][0], ha->addr, ETH_ALEN); sg_set_buf(&sg[1], mac_data, sizeof(mac_data->entries) + (mc_count * ETH_ALEN)); diff --git a/drivers/net/vmxnet3/vmxnet3_drv.c b/drivers/net/vmxnet3/vmxnet3_drv.c index cff3485d967..58dfa367bf8 100644 --- a/drivers/net/vmxnet3/vmxnet3_drv.c +++ b/drivers/net/vmxnet3/vmxnet3_drv.c @@ -1675,11 +1675,11 @@ vmxnet3_copy_mc(struct net_device *netdev) /* We may be called with BH disabled */ buf = kmalloc(sz, GFP_ATOMIC); if (buf) { - struct dev_mc_list *mc; + struct netdev_hw_addr *ha; int i = 0; - netdev_for_each_mc_addr(mc, netdev) - memcpy(buf + i++ * ETH_ALEN, mc->dmi_addr, + netdev_for_each_mc_addr(ha, netdev) + memcpy(buf + i++ * ETH_ALEN, ha->addr, ETH_ALEN); } } diff --git a/drivers/net/vxge/vxge-main.c b/drivers/net/vxge/vxge-main.c index e5f2d3ee0df..37836a10d09 100644 --- a/drivers/net/vxge/vxge-main.c +++ b/drivers/net/vxge/vxge-main.c @@ -1117,7 +1117,7 @@ vxge_tx_term(void *dtrh, enum vxge_hw_txdl_state state, void *userdata) */ static void vxge_set_multicast(struct net_device *dev) { - struct dev_mc_list *mclist; + struct netdev_hw_addr *ha; struct vxgedev *vdev; int i, mcast_cnt = 0; struct __vxge_hw_device *hldev; @@ -1217,8 +1217,8 @@ static void vxge_set_multicast(struct net_device *dev) } /* Add new ones */ - netdev_for_each_mc_addr(mclist, dev) { - memcpy(mac_info.macaddr, mclist->dmi_addr, ETH_ALEN); + netdev_for_each_mc_addr(ha, dev) { + memcpy(mac_info.macaddr, ha->addr, ETH_ALEN); for (vpath_idx = 0; vpath_idx < vdev->no_of_vpath; vpath_idx++) { mac_info.vpath_no = vpath_idx; diff --git a/drivers/net/wireless/adm8211.c b/drivers/net/wireless/adm8211.c index 547912e6843..2277998b726 100644 --- a/drivers/net/wireless/adm8211.c +++ b/drivers/net/wireless/adm8211.c @@ -1317,21 +1317,19 @@ static void adm8211_bss_info_changed(struct ieee80211_hw *dev, } static u64 adm8211_prepare_multicast(struct ieee80211_hw *hw, - int mc_count, struct dev_addr_list *mclist) + struct netdev_hw_addr_list *mc_list) { - unsigned int bit_nr, i; + unsigned int bit_nr; u32 mc_filter[2]; + struct netdev_hw_addr *ha; mc_filter[1] = mc_filter[0] = 0; - for (i = 0; i < mc_count; i++) { - if (!mclist) - break; - bit_nr = ether_crc(ETH_ALEN, mclist->dmi_addr) >> 26; + netdev_hw_addr_list_for_each(ha, mc_list) { + bit_nr = ether_crc(ETH_ALEN, ha->addr) >> 26; bit_nr &= 0x3F; mc_filter[bit_nr >> 5] |= 1 << (bit_nr & 31); - mclist = mclist->next; } return mc_filter[0] | ((u64)(mc_filter[1]) << 32); diff --git a/drivers/net/wireless/ath/ar9170/main.c b/drivers/net/wireless/ath/ar9170/main.c index 257c734733d..b43d4b006d7 100644 --- a/drivers/net/wireless/ath/ar9170/main.c +++ b/drivers/net/wireless/ath/ar9170/main.c @@ -2045,21 +2045,17 @@ out: return err; } -static u64 ar9170_op_prepare_multicast(struct ieee80211_hw *hw, int mc_count, - struct dev_addr_list *mclist) +static u64 ar9170_op_prepare_multicast(struct ieee80211_hw *hw, + struct netdev_hw_addr_list *mc_list) { u64 mchash; - int i; + struct netdev_hw_addr *ha; /* always get broadcast frames */ mchash = 1ULL << (0xff >> 2); - for (i = 0; i < mc_count; i++) { - if (WARN_ON(!mclist)) - break; - mchash |= 1ULL << (mclist->dmi_addr[5] >> 2); - mclist = mclist->next; - } + netdev_hw_addr_list_for_each(ha, mc_list) + mchash |= 1ULL << (ha->addr[5] >> 2); return mchash; } diff --git a/drivers/net/wireless/ath/ath5k/base.c b/drivers/net/wireless/ath/ath5k/base.c index b142a78ed1e..53a2340f52b 100644 --- a/drivers/net/wireless/ath/ath5k/base.c +++ b/drivers/net/wireless/ath/ath5k/base.c @@ -230,7 +230,7 @@ static void ath5k_remove_interface(struct ieee80211_hw *hw, struct ieee80211_vif *vif); static int ath5k_config(struct ieee80211_hw *hw, u32 changed); static u64 ath5k_prepare_multicast(struct ieee80211_hw *hw, - int mc_count, struct dev_addr_list *mc_list); + struct netdev_hw_addr_list *mc_list); static void ath5k_configure_filter(struct ieee80211_hw *hw, unsigned int changed_flags, unsigned int *new_flags, @@ -2999,22 +2999,20 @@ unlock: } static u64 ath5k_prepare_multicast(struct ieee80211_hw *hw, - int mc_count, struct dev_addr_list *mclist) + struct netdev_hw_addr_list *mc_list) { u32 mfilt[2], val; - int i; u8 pos; + struct netdev_hw_addr *ha; mfilt[0] = 0; mfilt[1] = 1; - for (i = 0; i < mc_count; i++) { - if (!mclist) - break; + netdev_hw_addr_list_for_each(ha, mc_list) { /* calculate XOR of eight 6-bit values */ - val = get_unaligned_le32(mclist->dmi_addr + 0); + val = get_unaligned_le32(ha->addr + 0); pos = (val >> 18) ^ (val >> 12) ^ (val >> 6) ^ val; - val = get_unaligned_le32(mclist->dmi_addr + 3); + val = get_unaligned_le32(ha->addr + 3); pos ^= (val >> 18) ^ (val >> 12) ^ (val >> 6) ^ val; pos &= 0x3f; mfilt[pos / 32] |= (1 << (pos % 32)); @@ -3022,8 +3020,7 @@ static u64 ath5k_prepare_multicast(struct ieee80211_hw *hw, * but not sure, needs testing, if we do use this we'd * neet to inform below to not reset the mcast */ /* ath5k_hw_set_mcast_filterindex(ah, - * mclist->dmi_addr[5]); */ - mclist = mclist->next; + * ha->addr[5]); */ } return ((u64)(mfilt[1]) << 32) | mfilt[0]; diff --git a/drivers/net/wireless/libertas/main.c b/drivers/net/wireless/libertas/main.c index 3c889f43d90..ff7b0d0cca5 100644 --- a/drivers/net/wireless/libertas/main.c +++ b/drivers/net/wireless/libertas/main.c @@ -318,7 +318,7 @@ static int lbs_add_mcast_addrs(struct cmd_ds_mac_multicast_adr *cmd, struct net_device *dev, int nr_addrs) { int i = nr_addrs; - struct dev_mc_list *mc_list; + struct netdev_hw_addr *ha; int cnt; if ((dev->flags & (IFF_UP|IFF_MULTICAST)) != (IFF_UP|IFF_MULTICAST)) @@ -326,19 +326,19 @@ static int lbs_add_mcast_addrs(struct cmd_ds_mac_multicast_adr *cmd, netif_addr_lock_bh(dev); cnt = netdev_mc_count(dev); - netdev_for_each_mc_addr(mc_list, dev) { - if (mac_in_list(cmd->maclist, nr_addrs, mc_list->dmi_addr)) { + netdev_for_each_mc_addr(ha, dev) { + if (mac_in_list(cmd->maclist, nr_addrs, ha->addr)) { lbs_deb_net("mcast address %s:%pM skipped\n", dev->name, - mc_list->dmi_addr); + ha->addr); cnt--; continue; } if (i == MRVDRV_MAX_MULTICAST_LIST_SIZE) break; - memcpy(&cmd->maclist[6*i], mc_list->dmi_addr, ETH_ALEN); + memcpy(&cmd->maclist[6*i], ha->addr, ETH_ALEN); lbs_deb_net("mcast address %s:%pM added to filter\n", dev->name, - mc_list->dmi_addr); + ha->addr); i++; cnt--; } diff --git a/drivers/net/wireless/libertas_tf/main.c b/drivers/net/wireless/libertas_tf/main.c index 6ab30033c26..c20eef6b95c 100644 --- a/drivers/net/wireless/libertas_tf/main.c +++ b/drivers/net/wireless/libertas_tf/main.c @@ -367,22 +367,20 @@ static int lbtf_op_config(struct ieee80211_hw *hw, u32 changed) } static u64 lbtf_op_prepare_multicast(struct ieee80211_hw *hw, - int mc_count, struct dev_addr_list *mclist) + struct netdev_hw_addr_list *mc_list) { struct lbtf_private *priv = hw->priv; int i; + struct netdev_hw_addr *ha; + int mc_count = netdev_hw_addr_list_count(mc_list); if (!mc_count || mc_count > MRVDRV_MAX_MULTICAST_LIST_SIZE) return mc_count; priv->nr_of_multicastmacaddr = mc_count; - for (i = 0; i < mc_count; i++) { - if (!mclist) - break; - memcpy(&priv->multicastlist[i], mclist->da_addr, - ETH_ALEN); - mclist = mclist->next; - } + i = 0; + netdev_hw_addr_list_for_each(ha, mc_list) + memcpy(&priv->multicastlist[i++], ha->addr, ETH_ALEN); return mc_count; } diff --git a/drivers/net/wireless/mwl8k.c b/drivers/net/wireless/mwl8k.c index ac65e13eb0d..6599fd15e67 100644 --- a/drivers/net/wireless/mwl8k.c +++ b/drivers/net/wireless/mwl8k.c @@ -1938,11 +1938,15 @@ struct mwl8k_cmd_mac_multicast_adr { static struct mwl8k_cmd_pkt * __mwl8k_cmd_mac_multicast_adr(struct ieee80211_hw *hw, int allmulti, - int mc_count, struct dev_addr_list *mclist) + struct netdev_hw_addr_list *mc_list) { struct mwl8k_priv *priv = hw->priv; struct mwl8k_cmd_mac_multicast_adr *cmd; int size; + int mc_count = 0; + + if (mc_list) + mc_count = netdev_hw_addr_list_count(mc_list); if (allmulti || mc_count > priv->num_mcaddrs) { allmulti = 1; @@ -1963,17 +1967,13 @@ __mwl8k_cmd_mac_multicast_adr(struct ieee80211_hw *hw, int allmulti, if (allmulti) { cmd->action |= cpu_to_le16(MWL8K_ENABLE_RX_ALL_MULTICAST); } else if (mc_count) { - int i; + struct netdev_hw_addr *ha; + int i = 0; cmd->action |= cpu_to_le16(MWL8K_ENABLE_RX_MULTICAST); cmd->numaddr = cpu_to_le16(mc_count); - for (i = 0; i < mc_count && mclist; i++) { - if (mclist->da_addrlen != ETH_ALEN) { - kfree(cmd); - return NULL; - } - memcpy(cmd->addr[i], mclist->da_addr, ETH_ALEN); - mclist = mclist->next; + netdev_hw_addr_list_for_each(ha, mc_list) { + memcpy(cmd->addr[i], ha->addr, ETH_ALEN); } } @@ -3552,7 +3552,7 @@ mwl8k_bss_info_changed(struct ieee80211_hw *hw, struct ieee80211_vif *vif, } static u64 mwl8k_prepare_multicast(struct ieee80211_hw *hw, - int mc_count, struct dev_addr_list *mclist) + struct netdev_hw_addr_list *mc_list) { struct mwl8k_cmd_pkt *cmd; @@ -3563,7 +3563,7 @@ static u64 mwl8k_prepare_multicast(struct ieee80211_hw *hw, * we'll end up throwing this packet away and creating a new * one in mwl8k_configure_filter(). */ - cmd = __mwl8k_cmd_mac_multicast_adr(hw, 0, mc_count, mclist); + cmd = __mwl8k_cmd_mac_multicast_adr(hw, 0, mc_list); return (unsigned long)cmd; } @@ -3686,7 +3686,7 @@ static void mwl8k_configure_filter(struct ieee80211_hw *hw, */ if (*total_flags & FIF_ALLMULTI) { kfree(cmd); - cmd = __mwl8k_cmd_mac_multicast_adr(hw, 1, 0, NULL); + cmd = __mwl8k_cmd_mac_multicast_adr(hw, 1, NULL); } if (cmd != NULL) { diff --git a/drivers/net/wireless/orinoco/hw.c b/drivers/net/wireless/orinoco/hw.c index 883b8f86862..9f657afaa3e 100644 --- a/drivers/net/wireless/orinoco/hw.c +++ b/drivers/net/wireless/orinoco/hw.c @@ -1056,14 +1056,14 @@ int __orinoco_hw_set_multicast_list(struct orinoco_private *priv, * group address if either we want to multicast, or if we were * multicasting and want to stop */ if (!promisc && (mc_count || priv->mc_count)) { - struct dev_mc_list *p; + struct netdev_hw_addr *ha; struct hermes_multicast mclist; int i = 0; - netdev_for_each_mc_addr(p, dev) { + netdev_for_each_mc_addr(ha, dev) { if (i == mc_count) break; - memcpy(mclist.addr[i++], p->dmi_addr, ETH_ALEN); + memcpy(mclist.addr[i++], ha->addr, ETH_ALEN); } err = hermes_write_ltv(hw, USER_BAP, diff --git a/drivers/net/wireless/orinoco/hw.h b/drivers/net/wireless/orinoco/hw.h index 9799a1d14a6..97af71e7995 100644 --- a/drivers/net/wireless/orinoco/hw.h +++ b/drivers/net/wireless/orinoco/hw.h @@ -22,7 +22,6 @@ /* Forward declarations */ struct orinoco_private; -struct dev_addr_list; int determine_fw_capabilities(struct orinoco_private *priv, char *fw_name, size_t fw_name_len, u32 *hw_ver); diff --git a/drivers/net/wireless/ray_cs.c b/drivers/net/wireless/ray_cs.c index 4f5bdb528ef..54680a3a5ac 100644 --- a/drivers/net/wireless/ray_cs.c +++ b/drivers/net/wireless/ray_cs.c @@ -1875,17 +1875,17 @@ static void ray_update_multi_list(struct net_device *dev, int all) writeb(0xff, &pccs->var); local->num_multi = 0xff; } else { - struct dev_mc_list *dmi; + struct netdev_hw_addr *ha; int i = 0; /* Copy the kernel's list of MC addresses to card */ - netdev_for_each_mc_addr(dmi, dev) { - memcpy_toio(p, dmi->dmi_addr, ETH_ALEN); + netdev_for_each_mc_addr(ha, dev) { + memcpy_toio(p, ha->addr, ETH_ALEN); dev_dbg(&link->dev, "ray_update_multi add addr %02x%02x%02x%02x%02x%02x\n", - dmi->dmi_addr[0], dmi->dmi_addr[1], - dmi->dmi_addr[2], dmi->dmi_addr[3], - dmi->dmi_addr[4], dmi->dmi_addr[5]); + ha->addr[0], ha->addr[1], + ha->addr[2], ha->addr[3], + ha->addr[4], ha->addr[5]); p += ETH_ALEN; i++; } diff --git a/drivers/net/wireless/rndis_wlan.c b/drivers/net/wireless/rndis_wlan.c index aceb95ef727..8ab9f094747 100644 --- a/drivers/net/wireless/rndis_wlan.c +++ b/drivers/net/wireless/rndis_wlan.c @@ -1545,7 +1545,7 @@ static int remove_key(struct usbnet *usbdev, int index, const u8 *bssid) static void set_multicast_list(struct usbnet *usbdev) { struct rndis_wlan_private *priv = get_rndis_wlan_priv(usbdev); - struct dev_mc_list *mclist; + struct netdev_hw_addr *ha; __le32 filter, basefilter; int ret; char *mc_addrs = NULL; @@ -1584,9 +1584,9 @@ static void set_multicast_list(struct usbnet *usbdev) return; } - netdev_for_each_mc_addr(mclist, usbdev->net) + netdev_for_each_mc_addr(ha, usbdev->net) memcpy(mc_addrs + i++ * ETH_ALEN, - mclist->dmi_addr, ETH_ALEN); + ha->addr, ETH_ALEN); } netif_addr_unlock_bh(usbdev->net); diff --git a/drivers/net/wireless/rtl818x/rtl8180_dev.c b/drivers/net/wireless/rtl818x/rtl8180_dev.c index 2b928ecf47b..fb8a62f2b9b 100644 --- a/drivers/net/wireless/rtl818x/rtl8180_dev.c +++ b/drivers/net/wireless/rtl818x/rtl8180_dev.c @@ -723,10 +723,10 @@ static void rtl8180_bss_info_changed(struct ieee80211_hw *dev, priv->rf->conf_erp(dev, info); } -static u64 rtl8180_prepare_multicast(struct ieee80211_hw *dev, int mc_count, - struct dev_addr_list *mc_list) +static u64 rtl8180_prepare_multicast(struct ieee80211_hw *dev, + struct netdev_hw_addr_list *mc_list) { - return mc_count; + return netdev_hw_addr_list_count(mc_list); } static void rtl8180_configure_filter(struct ieee80211_hw *dev, diff --git a/drivers/net/wireless/rtl818x/rtl8187_dev.c b/drivers/net/wireless/rtl818x/rtl8187_dev.c index 0fb850e0c65..441d817ed48 100644 --- a/drivers/net/wireless/rtl818x/rtl8187_dev.c +++ b/drivers/net/wireless/rtl818x/rtl8187_dev.c @@ -1193,9 +1193,9 @@ static void rtl8187_bss_info_changed(struct ieee80211_hw *dev, } static u64 rtl8187_prepare_multicast(struct ieee80211_hw *dev, - int mc_count, struct dev_addr_list *mc_list) + struct netdev_hw_addr_list *mc_list) { - return mc_count; + return netdev_hw_addr_list_count(mc_list); } static void rtl8187_configure_filter(struct ieee80211_hw *dev, diff --git a/drivers/net/wireless/wl12xx/wl1271_main.c b/drivers/net/wireless/wl12xx/wl1271_main.c index 3daba6c0c77..6036d0206fe 100644 --- a/drivers/net/wireless/wl12xx/wl1271_main.c +++ b/drivers/net/wireless/wl12xx/wl1271_main.c @@ -1266,11 +1266,11 @@ struct wl1271_filter_params { u8 mc_list[ACX_MC_ADDRESS_GROUP_MAX][ETH_ALEN]; }; -static u64 wl1271_op_prepare_multicast(struct ieee80211_hw *hw, int mc_count, - struct dev_addr_list *mc_list) +static u64 wl1271_op_prepare_multicast(struct ieee80211_hw *hw, + struct netdev_hw_addr_list *mc_list) { struct wl1271_filter_params *fp; - int i; + struct netdev_hw_addr *ha; fp = kzalloc(sizeof(*fp), GFP_ATOMIC); if (!fp) { @@ -1279,21 +1279,16 @@ static u64 wl1271_op_prepare_multicast(struct ieee80211_hw *hw, int mc_count, } /* update multicast filtering parameters */ - fp->enabled = true; - if (mc_count > ACX_MC_ADDRESS_GROUP_MAX) { - mc_count = 0; - fp->enabled = false; - } - fp->mc_list_length = 0; - for (i = 0; i < mc_count; i++) { - if (mc_list->da_addrlen == ETH_ALEN) { + if (netdev_hw_addr_list_count(mc_list) > ACX_MC_ADDRESS_GROUP_MAX) { + fp->enabled = false; + } else { + fp->enabled = true; + netdev_hw_addr_list_for_each(ha, mc_list) { memcpy(fp->mc_list[fp->mc_list_length], - mc_list->da_addr, ETH_ALEN); + ha->addr, ETH_ALEN); fp->mc_list_length++; - } else - wl1271_warning("Unknown mc address length."); - mc_list = mc_list->next; + } } return (u64)(unsigned long)fp; diff --git a/drivers/net/wireless/zd1201.c b/drivers/net/wireless/zd1201.c index 6917286edca..74530b2d672 100644 --- a/drivers/net/wireless/zd1201.c +++ b/drivers/net/wireless/zd1201.c @@ -875,7 +875,7 @@ static struct iw_statistics *zd1201_get_wireless_stats(struct net_device *dev) static void zd1201_set_multicast(struct net_device *dev) { struct zd1201 *zd = netdev_priv(dev); - struct dev_mc_list *mc; + struct netdev_hw_addr *ha; unsigned char reqbuf[ETH_ALEN*ZD1201_MAXMULTI]; int i; @@ -883,8 +883,8 @@ static void zd1201_set_multicast(struct net_device *dev) return; i = 0; - netdev_for_each_mc_addr(mc, dev) - memcpy(reqbuf + i++ * ETH_ALEN, mc->dmi_addr, ETH_ALEN); + netdev_for_each_mc_addr(ha, dev) + memcpy(reqbuf + i++ * ETH_ALEN, ha->addr, ETH_ALEN); zd1201_setconfig(zd, ZD1201_RID_CNFGROUPADDRESS, reqbuf, netdev_mc_count(dev) * ETH_ALEN, 0); } diff --git a/drivers/net/wireless/zd1211rw/zd_mac.c b/drivers/net/wireless/zd1211rw/zd_mac.c index 00e09e26c82..6d95e4d74d7 100644 --- a/drivers/net/wireless/zd1211rw/zd_mac.c +++ b/drivers/net/wireless/zd1211rw/zd_mac.c @@ -947,20 +947,17 @@ static void set_rx_filter_handler(struct work_struct *work) } static u64 zd_op_prepare_multicast(struct ieee80211_hw *hw, - int mc_count, struct dev_addr_list *mclist) + struct netdev_hw_addr_list *mc_list) { struct zd_mac *mac = zd_hw_mac(hw); struct zd_mc_hash hash; - int i; + struct netdev_hw_addr *ha; zd_mc_clear(&hash); - for (i = 0; i < mc_count; i++) { - if (!mclist) - break; - dev_dbg_f(zd_mac_dev(mac), "mc addr %pM\n", mclist->dmi_addr); - zd_mc_add_addr(&hash, mclist->dmi_addr); - mclist = mclist->next; + netdev_hw_addr_list_for_each(ha, mc_list) { + dev_dbg_f(zd_mac_dev(mac), "mc addr %pM\n", ha->addr); + zd_mc_add_addr(&hash, ha->addr); } return hash.low | ((u64)hash.high << 32); diff --git a/drivers/net/yellowfin.c b/drivers/net/yellowfin.c index 7d4107f5eeb..34c91cf5d83 100644 --- a/drivers/net/yellowfin.c +++ b/drivers/net/yellowfin.c @@ -1300,25 +1300,25 @@ static void set_rx_mode(struct net_device *dev) /* Too many to filter well, or accept all multicasts. */ iowrite16(0x000B, ioaddr + AddrMode); } else if (!netdev_mc_empty(dev)) { /* Must use the multicast hash table. */ - struct dev_mc_list *mclist; + struct netdev_hw_addr *ha; u16 hash_table[4]; int i; memset(hash_table, 0, sizeof(hash_table)); - netdev_for_each_mc_addr(mclist, dev) { + netdev_for_each_mc_addr(ha, dev) { unsigned int bit; /* Due to a bug in the early chip versions, multiple filter slots must be set for each address. */ if (yp->drv_flags & HasMulticastBug) { - bit = (ether_crc_le(3, mclist->dmi_addr) >> 3) & 0x3f; + bit = (ether_crc_le(3, ha->addr) >> 3) & 0x3f; hash_table[bit >> 4] |= (1 << bit); - bit = (ether_crc_le(4, mclist->dmi_addr) >> 3) & 0x3f; + bit = (ether_crc_le(4, ha->addr) >> 3) & 0x3f; hash_table[bit >> 4] |= (1 << bit); - bit = (ether_crc_le(5, mclist->dmi_addr) >> 3) & 0x3f; + bit = (ether_crc_le(5, ha->addr) >> 3) & 0x3f; hash_table[bit >> 4] |= (1 << bit); } - bit = (ether_crc_le(6, mclist->dmi_addr) >> 3) & 0x3f; + bit = (ether_crc_le(6, ha->addr) >> 3) & 0x3f; hash_table[bit >> 4] |= (1 << bit); } /* Copy the hash table to the chip. */ diff --git a/drivers/s390/net/qeth_l2_main.c b/drivers/s390/net/qeth_l2_main.c index 7576ad5a833..945f3e0a9f0 100644 --- a/drivers/s390/net/qeth_l2_main.c +++ b/drivers/s390/net/qeth_l2_main.c @@ -607,7 +607,6 @@ static int qeth_l2_set_mac_address(struct net_device *dev, void *p) static void qeth_l2_set_multicast_list(struct net_device *dev) { struct qeth_card *card = dev->ml_priv; - struct dev_addr_list *dm; struct netdev_hw_addr *ha; if (card->info.type == QETH_CARD_TYPE_OSN) @@ -619,8 +618,8 @@ static void qeth_l2_set_multicast_list(struct net_device *dev) return; qeth_l2_del_all_mc(card); spin_lock_bh(&card->mclock); - netdev_for_each_mc_addr(dm, dev) - qeth_l2_add_mc(card, dm->da_addr, 0); + netdev_for_each_mc_addr(ha, dev) + qeth_l2_add_mc(card, ha->addr, 0); netdev_for_each_uc_addr(ha, dev) qeth_l2_add_mc(card, ha->addr, 1); diff --git a/drivers/scsi/fcoe/fcoe.c b/drivers/scsi/fcoe/fcoe.c index de33e38a405..37de40e0168 100644 --- a/drivers/scsi/fcoe/fcoe.c +++ b/drivers/scsi/fcoe/fcoe.c @@ -311,7 +311,7 @@ static int fcoe_interface_setup(struct fcoe_interface *fcoe, dev_uc_add(netdev, flogi_maddr); if (fip->spma) dev_uc_add(netdev, fip->ctl_src_addr); - dev_mc_add(netdev, FIP_ALL_ENODE_MACS, ETH_ALEN, 0); + dev_mc_add(netdev, FIP_ALL_ENODE_MACS); /* * setup the receive function from ethernet driver @@ -397,7 +397,7 @@ void fcoe_interface_cleanup(struct fcoe_interface *fcoe) dev_uc_del(netdev, flogi_maddr); if (fip->spma) dev_uc_del(netdev, fip->ctl_src_addr); - dev_mc_delete(netdev, FIP_ALL_ENODE_MACS, ETH_ALEN, 0); + dev_mc_del(netdev, FIP_ALL_ENODE_MACS); /* Tell the LLD we are done w/ FCoE */ ops = netdev->netdev_ops; diff --git a/drivers/staging/arlan/arlan-main.c b/drivers/staging/arlan/arlan-main.c index 88fdd53cf5d..80284522c42 100644 --- a/drivers/staging/arlan/arlan-main.c +++ b/drivers/staging/arlan/arlan-main.c @@ -1458,7 +1458,7 @@ static void arlan_rx_interrupt(struct net_device *dev, u_char rxStatus, u_short !netdev_mc_empty(dev)) { char hw_dst_addr[6]; - struct dev_mc_list *dmi; + struct netdev_hw_addr *ha; int i; memcpy_fromio(hw_dst_addr, arlan->ultimateDestAddress, 6); @@ -1469,12 +1469,13 @@ static void arlan_rx_interrupt(struct net_device *dev, u_char rxStatus, u_short printk(KERN_ERR "%s mcast 0x0100 \n", dev->name); else if (hw_dst_addr[1] == 0x40) printk(KERN_ERR "%s m/bcast 0x0140 \n", dev->name); - netdev_for_each_mc_entry(dmi, dev) { + netdev_for_each_mc_entry(ha, dev) { if (arlan_debug & ARLAN_DEBUG_HEADER_DUMP) printk(KERN_ERR "%s mcl %pM\n", - dev->name, dmi->dmi_addr); + dev->name, + ha->addr); for (i = 0; i < 6; i++) - if (dmi->dmi_addr[i] != hw_dst_addr[i]) + if (ha->addr[i] != hw_dst_addr[i]) break; if (i == 6) break; diff --git a/drivers/staging/et131x/et131x_netdev.c b/drivers/staging/et131x/et131x_netdev.c index 40f8954dde4..2fb89cddef1 100644 --- a/drivers/staging/et131x/et131x_netdev.c +++ b/drivers/staging/et131x/et131x_netdev.c @@ -405,7 +405,7 @@ void et131x_multicast(struct net_device *netdev) struct et131x_adapter *adapter = netdev_priv(netdev); uint32_t PacketFilter = 0; unsigned long flags; - struct dev_mc_list *mclist; + struct netdev_hw_addr *ha; int i; spin_lock_irqsave(&adapter->Lock, flags); @@ -450,10 +450,10 @@ void et131x_multicast(struct net_device *netdev) /* Set values in the private adapter struct */ i = 0; - netdev_for_each_mc_addr(mclist, netdev) { + netdev_for_each_mc_addr(ha, netdev) { if (i == NIC_MAX_MCAST_LIST) break; - memcpy(adapter->MCList[i++], mclist->dmi_addr, ETH_ALEN); + memcpy(adapter->MCList[i++], ha->addr, ETH_ALEN); } adapter->MCAddressCount = i; diff --git a/drivers/staging/slicoss/slicoss.c b/drivers/staging/slicoss/slicoss.c index 7daeced317c..bebf0fd2af8 100644 --- a/drivers/staging/slicoss/slicoss.c +++ b/drivers/staging/slicoss/slicoss.c @@ -1367,12 +1367,12 @@ static void slic_mcast_set_list(struct net_device *dev) struct adapter *adapter = netdev_priv(dev); int status = STATUS_SUCCESS; char *addresses; - struct dev_mc_list *mc_list; + struct netdev_hw_addr *ha; ASSERT(adapter); - netdev_for_each_mc_addr(mc_list, dev) { - addresses = (char *) &mc_list->dmi_addr; + netdev_for_each_mc_addr(ha, dev) { + addresses = (char *) &ha->addr; status = slic_mcast_add_list(adapter, addresses); if (status != STATUS_SUCCESS) break; diff --git a/drivers/staging/vt6655/device_main.c b/drivers/staging/vt6655/device_main.c index 1d643653a7e..b698de40a60 100644 --- a/drivers/staging/vt6655/device_main.c +++ b/drivers/staging/vt6655/device_main.c @@ -3079,7 +3079,7 @@ static void device_set_multi(struct net_device *dev) { PSMgmtObject pMgmt = pDevice->pMgmt; u32 mc_filter[2]; - struct dev_mc_list *mclist; + struct netdev_hw_addr *ha; VNSvInPortB(pDevice->PortOffset + MAC_REG_RCR, &(pDevice->byRxMode)); @@ -3099,8 +3099,8 @@ static void device_set_multi(struct net_device *dev) { } else { memset(mc_filter, 0, sizeof(mc_filter)); - netdev_for_each_mc_addr(mclist, dev) { - int bit_nr = ether_crc(ETH_ALEN, mclist->dmi_addr) >> 26; + netdev_for_each_mc_addr(ha, dev) { + int bit_nr = ether_crc(ETH_ALEN, ha->addr) >> 26; mc_filter[bit_nr >> 5] |= cpu_to_le32(1 << (bit_nr & 31)); } MACvSelectPage1(pDevice->PortOffset); diff --git a/drivers/staging/vt6656/main_usb.c b/drivers/staging/vt6656/main_usb.c index a8e1adbc959..49270db98fb 100644 --- a/drivers/staging/vt6656/main_usb.c +++ b/drivers/staging/vt6656/main_usb.c @@ -1596,7 +1596,7 @@ static void device_set_multi(struct net_device *dev) { PSMgmtObject pMgmt = &(pDevice->sMgmtObj); u32 mc_filter[2]; int ii; - struct dev_mc_list *mclist; + struct netdev_hw_addr *ha; BYTE pbyData[8] = {0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff}; BYTE byTmpMode = 0; int rc; @@ -1632,8 +1632,8 @@ static void device_set_multi(struct net_device *dev) { } else { memset(mc_filter, 0, sizeof(mc_filter)); - netdev_for_each_mc_addr(mclist, dev) { - int bit_nr = ether_crc(ETH_ALEN, mclist->dmi_addr) >> 26; + netdev_for_each_mc_addr(ha, dev) { + int bit_nr = ether_crc(ETH_ALEN, ha->addr) >> 26; mc_filter[bit_nr >> 5] |= cpu_to_le32(1 << (bit_nr & 31)); } for (ii = 0; ii < 4; ii++) { diff --git a/drivers/staging/wavelan/wavelan.c b/drivers/staging/wavelan/wavelan.c index 54ca63196fd..f44ef351647 100644 --- a/drivers/staging/wavelan/wavelan.c +++ b/drivers/staging/wavelan/wavelan.c @@ -3419,7 +3419,7 @@ static void wv_82586_config(struct net_device * dev) ac_cfg_t cfg; /* Configure action */ ac_ias_t ias; /* IA-setup action */ ac_mcs_t mcs; /* Multicast setup */ - struct dev_mc_list *dmi; + struct netdev_hw_addr *ha; #ifdef DEBUG_CONFIG_TRACE printk(KERN_DEBUG "%s: ->wv_82586_config()\n", dev->name); @@ -3531,16 +3531,16 @@ static void wv_82586_config(struct net_device * dev) /* Any address to set? */ if (lp->mc_count) { - netdev_for_each_mc_addr(dmi, dev) - outsw(PIOP1(ioaddr), (u16 *) dmi->dmi_addr, + netdev_for_each_mc_addr(ha, dev) + outsw(PIOP1(ioaddr), (u16 *) ha->addr, WAVELAN_ADDR_SIZE >> 1); #ifdef DEBUG_CONFIG_INFO printk(KERN_DEBUG "%s: wv_82586_config(): set %d multicast addresses:\n", dev->name, lp->mc_count); - netdev_for_each_mc_addr(dmi, dev) - printk(KERN_DEBUG " %pM\n", dmi->dmi_addr); + netdev_for_each_mc_addr(ha, dev) + printk(KERN_DEBUG " %pM\n", ha->addr); #endif } diff --git a/drivers/staging/wavelan/wavelan_cs.c b/drivers/staging/wavelan/wavelan_cs.c index 04f691d127b..a90132a204e 100644 --- a/drivers/staging/wavelan/wavelan_cs.c +++ b/drivers/staging/wavelan/wavelan_cs.c @@ -3591,20 +3591,20 @@ wv_82593_config(struct net_device * dev) /* If roaming is enabled, join the "Beacon Request" multicast group... */ /* But only if it's not in there already! */ if(do_roaming) - dev_mc_add(dev,WAVELAN_BEACON_ADDRESS, WAVELAN_ADDR_SIZE, 1); + dev_mc_add(dev, WAVELAN_BEACON_ADDRESS); #endif /* WAVELAN_ROAMING */ /* If any multicast address to set */ if(lp->mc_count) { - struct dev_mc_list *dmi; + struct netdev_hw_addr *ha; int addrs_len = WAVELAN_ADDR_SIZE * lp->mc_count; #ifdef DEBUG_CONFIG_INFO printk(KERN_DEBUG "%s: wv_hw_config(): set %d multicast addresses:\n", dev->name, lp->mc_count); - netdev_for_each_mc_addr(dmi, dev) - printk(KERN_DEBUG " %pM\n", dmi->dmi_addr); + netdev_for_each_mc_addr(ha, dev) + printk(KERN_DEBUG " %pM\n", ha->addr); #endif /* Initialize adapter's ethernet multicast addresses */ @@ -3612,8 +3612,8 @@ wv_82593_config(struct net_device * dev) outb(((TX_BASE >> 8) & PIORH_MASK) | PIORH_SEL_TX, PIORH(base)); outb(addrs_len & 0xff, PIOP(base)); /* byte count lsb */ outb((addrs_len >> 8), PIOP(base)); /* byte count msb */ - netdev_for_each_mc_addr(dmi, dev) - outsb(PIOP(base), dmi->dmi_addr, dmi->dmi_addrlen); + netdev_for_each_mc_addr(ha, dev) + outsb(PIOP(base), ha->addr, dev->addr_len); /* reset transmit DMA pointer */ hacr_write_slow(base, HACR_PWR_STAT | HACR_TX_DMA_RESET); diff --git a/drivers/staging/winbond/wbusb.c b/drivers/staging/winbond/wbusb.c index 3482eec1865..5d9499bba9c 100644 --- a/drivers/staging/winbond/wbusb.c +++ b/drivers/staging/winbond/wbusb.c @@ -92,10 +92,10 @@ static int wbsoft_get_stats(struct ieee80211_hw *hw, return 0; } -static u64 wbsoft_prepare_multicast(struct ieee80211_hw *hw, int mc_count, - struct dev_addr_list *mc_list) +static u64 wbsoft_prepare_multicast(struct ieee80211_hw *hw, + struct netdev_hw_addr_list *mc_list) { - return mc_count; + return netdev_hw_addr_list_count(mc_list); } static void wbsoft_configure_filter(struct ieee80211_hw *dev, diff --git a/drivers/staging/wlags49_h2/wl_netdev.c b/drivers/staging/wlags49_h2/wl_netdev.c index fa082d90fca..d7532e89f5f 100644 --- a/drivers/staging/wlags49_h2/wl_netdev.c +++ b/drivers/staging/wlags49_h2/wl_netdev.c @@ -1049,7 +1049,7 @@ void wl_multicast( struct net_device *dev ) //;?seems reasonable that even an AP-only driver could afford this small additional footprint int x; - struct dev_mc_list *mclist; + struct netdev_hw_addr *ha; struct wl_private *lp = wl_priv(dev); unsigned long flags; /*------------------------------------------------------------------------*/ @@ -1072,9 +1072,9 @@ void wl_multicast( struct net_device *dev ) DBG_PRINT( " mc_count: %d\n", netdev_mc_count(dev)); - netdev_for_each_mc_addr(mclist, dev) - DBG_PRINT( " %s (%d)\n", DbgHwAddr(mclist->dmi_addr), - mclist->dmi_addrlen ); + netdev_for_each_mc_addr(ha, dev) + DBG_PRINT(" %s (%d)\n", DbgHwAddr(ha->addr), + dev->addr_len); } #endif /* DBG */ @@ -1119,9 +1119,9 @@ void wl_multicast( struct net_device *dev ) lp->ltvRecord.typ = CFG_GROUP_ADDR; x = 0; - netdev_for_each_mc_addr(mclist, dev) + netdev_for_each_mc_addr(ha, dev) memcpy(&(lp->ltvRecord.u.u8[x++ * ETH_ALEN]), - mclist->dmi_addr, ETH_ALEN); + ha->addr, ETH_ALEN); DBG_PRINT( "Setting multicast list\n" ); hcf_put_info( &( lp->hcfCtx ), (LTVP)&( lp->ltvRecord )); } else { diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index 60f0c83192f..a343a21ba8b 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -228,25 +228,6 @@ struct netif_rx_stats { DECLARE_PER_CPU(struct netif_rx_stats, netdev_rx_stat); -struct dev_addr_list { - struct dev_addr_list *next; - u8 da_addr[MAX_ADDR_LEN]; - u8 da_addrlen; - u8 da_synced; - int da_users; - int da_gusers; -}; - -/* - * We tag multicasts with these structures. - */ - -#define dev_mc_list dev_addr_list -#define dmi_addr da_addr -#define dmi_addrlen da_addrlen -#define dmi_users da_users -#define dmi_gusers da_gusers - struct netdev_hw_addr { struct list_head list; unsigned char addr[MAX_ADDR_LEN]; @@ -255,8 +236,10 @@ struct netdev_hw_addr { #define NETDEV_HW_ADDR_T_SAN 2 #define NETDEV_HW_ADDR_T_SLAVE 3 #define NETDEV_HW_ADDR_T_UNICAST 4 +#define NETDEV_HW_ADDR_T_MULTICAST 5 int refcount; bool synced; + bool global_use; struct rcu_head rcu_head; }; @@ -265,16 +248,20 @@ struct netdev_hw_addr_list { int count; }; -#define netdev_uc_count(dev) ((dev)->uc.count) -#define netdev_uc_empty(dev) ((dev)->uc.count == 0) -#define netdev_for_each_uc_addr(ha, dev) \ - list_for_each_entry(ha, &dev->uc.list, list) +#define netdev_hw_addr_list_count(l) ((l)->count) +#define netdev_hw_addr_list_empty(l) (netdev_hw_addr_list_count(l) == 0) +#define netdev_hw_addr_list_for_each(ha, l) \ + list_for_each_entry(ha, &(l)->list, list) -#define netdev_mc_count(dev) ((dev)->mc_count) -#define netdev_mc_empty(dev) (netdev_mc_count(dev) == 0) +#define netdev_uc_count(dev) netdev_hw_addr_list_count(&(dev)->uc) +#define netdev_uc_empty(dev) netdev_hw_addr_list_empty(&(dev)->uc) +#define netdev_for_each_uc_addr(ha, dev) \ + netdev_hw_addr_list_for_each(ha, &(dev)->uc) +#define netdev_mc_count(dev) netdev_hw_addr_list_count(&(dev)->mc) +#define netdev_mc_empty(dev) netdev_hw_addr_list_empty(&(dev)->mc) #define netdev_for_each_mc_addr(mclist, dev) \ - for (mclist = dev->mc_list; mclist; mclist = mclist->next) + netdev_hw_addr_list_for_each(ha, &(dev)->mc) struct hh_cache { struct hh_cache *hh_next; /* Next entry */ @@ -862,12 +849,10 @@ struct net_device { unsigned char addr_len; /* hardware address length */ unsigned short dev_id; /* for shared network cards */ - struct netdev_hw_addr_list uc; /* Secondary unicast - mac addresses */ - int uc_promisc; spinlock_t addr_list_lock; - struct dev_addr_list *mc_list; /* Multicast mac addresses */ - int mc_count; /* Number of installed mcasts */ + struct netdev_hw_addr_list uc; /* Unicast mac addresses */ + struct netdev_hw_addr_list mc; /* Multicast mac addresses */ + int uc_promisc; unsigned int promiscuity; unsigned int allmulti; @@ -1980,6 +1965,22 @@ extern struct net_device *alloc_netdev_mq(int sizeof_priv, const char *name, extern int register_netdev(struct net_device *dev); extern void unregister_netdev(struct net_device *dev); +/* General hardware address lists handling functions */ +extern int __hw_addr_add_multiple(struct netdev_hw_addr_list *to_list, + struct netdev_hw_addr_list *from_list, + int addr_len, unsigned char addr_type); +extern void __hw_addr_del_multiple(struct netdev_hw_addr_list *to_list, + struct netdev_hw_addr_list *from_list, + int addr_len, unsigned char addr_type); +extern int __hw_addr_sync(struct netdev_hw_addr_list *to_list, + struct netdev_hw_addr_list *from_list, + int addr_len); +extern void __hw_addr_unsync(struct netdev_hw_addr_list *to_list, + struct netdev_hw_addr_list *from_list, + int addr_len); +extern void __hw_addr_flush(struct netdev_hw_addr_list *list); +extern void __hw_addr_init(struct netdev_hw_addr_list *list); + /* Functions used for device addresses handling */ extern int dev_addr_add(struct net_device *dev, unsigned char *addr, unsigned char addr_type); @@ -2002,18 +2003,19 @@ extern void dev_uc_unsync(struct net_device *to, struct net_device *from); extern void dev_uc_flush(struct net_device *dev); extern void dev_uc_init(struct net_device *dev); +/* Functions used for multicast addresses handling */ +extern int dev_mc_add(struct net_device *dev, unsigned char *addr); +extern int dev_mc_add_global(struct net_device *dev, unsigned char *addr); +extern int dev_mc_del(struct net_device *dev, unsigned char *addr); +extern int dev_mc_del_global(struct net_device *dev, unsigned char *addr); +extern int dev_mc_sync(struct net_device *to, struct net_device *from); +extern void dev_mc_unsync(struct net_device *to, struct net_device *from); +extern void dev_mc_flush(struct net_device *dev); +extern void dev_mc_init(struct net_device *dev); + /* Functions used for secondary unicast and multicast support */ extern void dev_set_rx_mode(struct net_device *dev); extern void __dev_set_rx_mode(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 int dev_mc_sync(struct net_device *to, struct net_device *from); -extern void dev_mc_unsync(struct net_device *to, struct net_device *from); -extern void dev_addr_discard(struct net_device *dev); -extern int __dev_addr_delete(struct dev_addr_list **list, int *count, void *addr, int alen, int all); -extern int __dev_addr_add(struct dev_addr_list **list, int *count, void *addr, int alen, int newonly); -extern int __dev_addr_sync(struct dev_addr_list **to, int *to_count, struct dev_addr_list **from, int *from_count); -extern void __dev_addr_unsync(struct dev_addr_list **to, int *to_count, struct dev_addr_list **from, int *from_count); extern int dev_set_promiscuity(struct net_device *dev, int inc); extern int dev_set_allmulti(struct net_device *dev, int inc); extern void netdev_state_change(struct net_device *dev); diff --git a/include/net/mac80211.h b/include/net/mac80211.h index 1a8f50af49a..20823d04e03 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h @@ -1626,7 +1626,7 @@ struct ieee80211_ops { struct ieee80211_bss_conf *info, u32 changed); u64 (*prepare_multicast)(struct ieee80211_hw *hw, - int mc_count, struct dev_addr_list *mc_list); + struct netdev_hw_addr_list *mc_list); void (*configure_filter)(struct ieee80211_hw *hw, unsigned int changed_flags, unsigned int *total_flags, diff --git a/net/802/garp.c b/net/802/garp.c index 1dcb0660c49..78cff9ec2cb 100644 --- a/net/802/garp.c +++ b/net/802/garp.c @@ -575,7 +575,7 @@ int garp_init_applicant(struct net_device *dev, struct garp_application *appl) if (!app) goto err2; - err = dev_mc_add(dev, appl->proto.group_address, ETH_ALEN, 0); + err = dev_mc_add(dev, appl->proto.group_address); if (err < 0) goto err3; @@ -615,7 +615,7 @@ void garp_uninit_applicant(struct net_device *dev, struct garp_application *appl garp_pdu_queue(app); garp_queue_xmit(app); - dev_mc_delete(dev, appl->proto.group_address, ETH_ALEN, 0); + dev_mc_del(dev, appl->proto.group_address); kfree(app); garp_release_port(dev); } diff --git a/net/appletalk/ddp.c b/net/appletalk/ddp.c index 9fc4da56fb1..1d15a60b23a 100644 --- a/net/appletalk/ddp.c +++ b/net/appletalk/ddp.c @@ -781,7 +781,7 @@ static int atif_ioctl(int cmd, void __user *arg) atrtr_create(&rtdef, dev); } } - dev_mc_add(dev, aarp_mcast, 6, 1); + dev_mc_add_global(dev, aarp_mcast); return 0; case SIOCGIFADDR: diff --git a/net/bluetooth/bnep/netdev.c b/net/bluetooth/bnep/netdev.c index 326ab453edb..260a9507e54 100644 --- a/net/bluetooth/bnep/netdev.c +++ b/net/bluetooth/bnep/netdev.c @@ -87,7 +87,7 @@ static void bnep_net_set_mc_list(struct net_device *dev) memcpy(__skb_put(skb, ETH_ALEN), dev->broadcast, ETH_ALEN); r->len = htons(ETH_ALEN * 2); } else { - struct dev_mc_list *dmi; + struct netdev_hw_addr *ha; int i, len = skb->len; if (dev->flags & IFF_BROADCAST) { @@ -98,11 +98,11 @@ static void bnep_net_set_mc_list(struct net_device *dev) /* FIXME: We should group addresses here. */ i = 0; - netdev_for_each_mc_addr(dmi, dev) { + netdev_for_each_mc_addr(ha, dev) { if (i == BNEP_MAX_MULTICAST_FILTERS) break; - memcpy(__skb_put(skb, ETH_ALEN), dmi->dmi_addr, ETH_ALEN); - memcpy(__skb_put(skb, ETH_ALEN), dmi->dmi_addr, ETH_ALEN); + memcpy(__skb_put(skb, ETH_ALEN), ha->addr, ETH_ALEN); + memcpy(__skb_put(skb, ETH_ALEN), ha->addr, ETH_ALEN); } r->len = htons(skb->len - len); } diff --git a/net/core/Makefile b/net/core/Makefile index 0a899f1aadb..51c3eec850e 100644 --- a/net/core/Makefile +++ b/net/core/Makefile @@ -7,9 +7,8 @@ obj-y := sock.o request_sock.o skbuff.o iovec.o datagram.o stream.o scm.o \ obj-$(CONFIG_SYSCTL) += sysctl_net_core.o -obj-y += dev.o ethtool.o dev_mcast.o dst.o netevent.o \ - neighbour.o rtnetlink.o utils.o link_watch.o filter.o \ - dev_addr_lists.o +obj-y += dev.o ethtool.o dev_addr_lists.o dst.o netevent.o \ + neighbour.o rtnetlink.o utils.o link_watch.o filter.o obj-$(CONFIG_XFRM) += flow.o obj-y += net-sysfs.o diff --git a/net/core/dev.c b/net/core/dev.c index 949c62dba71..2a9b7dd0bb6 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -3968,140 +3968,6 @@ void dev_set_rx_mode(struct net_device *dev) netif_addr_unlock_bh(dev); } -/* multicast addresses handling functions */ - -int __dev_addr_delete(struct dev_addr_list **list, int *count, - void *addr, int alen, int glbl) -{ - struct dev_addr_list *da; - - for (; (da = *list) != NULL; list = &da->next) { - if (memcmp(da->da_addr, addr, da->da_addrlen) == 0 && - alen == da->da_addrlen) { - if (glbl) { - int old_glbl = da->da_gusers; - da->da_gusers = 0; - if (old_glbl == 0) - break; - } - if (--da->da_users) - return 0; - - *list = da->next; - kfree(da); - (*count)--; - return 0; - } - } - return -ENOENT; -} - -int __dev_addr_add(struct dev_addr_list **list, int *count, - void *addr, int alen, int glbl) -{ - struct dev_addr_list *da; - - for (da = *list; da != NULL; da = da->next) { - if (memcmp(da->da_addr, addr, da->da_addrlen) == 0 && - da->da_addrlen == alen) { - if (glbl) { - int old_glbl = da->da_gusers; - da->da_gusers = 1; - if (old_glbl) - return 0; - } - da->da_users++; - return 0; - } - } - - da = kzalloc(sizeof(*da), GFP_ATOMIC); - if (da == NULL) - return -ENOMEM; - memcpy(da->da_addr, addr, alen); - da->da_addrlen = alen; - da->da_users = 1; - da->da_gusers = glbl ? 1 : 0; - da->next = *list; - *list = da; - (*count)++; - return 0; -} - - -int __dev_addr_sync(struct dev_addr_list **to, int *to_count, - struct dev_addr_list **from, int *from_count) -{ - struct dev_addr_list *da, *next; - int err = 0; - - da = *from; - while (da != NULL) { - next = da->next; - if (!da->da_synced) { - err = __dev_addr_add(to, to_count, - da->da_addr, da->da_addrlen, 0); - if (err < 0) - break; - da->da_synced = 1; - da->da_users++; - } else if (da->da_users == 1) { - __dev_addr_delete(to, to_count, - da->da_addr, da->da_addrlen, 0); - __dev_addr_delete(from, from_count, - da->da_addr, da->da_addrlen, 0); - } - da = next; - } - return err; -} -EXPORT_SYMBOL_GPL(__dev_addr_sync); - -void __dev_addr_unsync(struct dev_addr_list **to, int *to_count, - struct dev_addr_list **from, int *from_count) -{ - struct dev_addr_list *da, *next; - - da = *from; - while (da != NULL) { - next = da->next; - if (da->da_synced) { - __dev_addr_delete(to, to_count, - da->da_addr, da->da_addrlen, 0); - da->da_synced = 0; - __dev_addr_delete(from, from_count, - da->da_addr, da->da_addrlen, 0); - } - da = next; - } -} -EXPORT_SYMBOL_GPL(__dev_addr_unsync); - -static void __dev_addr_discard(struct dev_addr_list **list) -{ - struct dev_addr_list *tmp; - - while (*list != NULL) { - tmp = *list; - *list = tmp->next; - if (tmp->da_users > tmp->da_gusers) - printk("__dev_addr_discard: address leakage! " - "da_users=%d\n", tmp->da_users); - kfree(tmp); - } -} - -void dev_addr_discard(struct net_device *dev) -{ - netif_addr_lock_bh(dev); - - __dev_addr_discard(&dev->mc_list); - netdev_mc_count(dev) = 0; - - netif_addr_unlock_bh(dev); -} -EXPORT_SYMBOL(dev_addr_discard); - /** * dev_get_flags - get flags reported to userspace * @dev: device @@ -4412,8 +4278,7 @@ static int dev_ifsioc(struct net *net, struct ifreq *ifr, unsigned int cmd) return -EINVAL; if (!netif_device_present(dev)) return -ENODEV; - return dev_mc_add(dev, ifr->ifr_hwaddr.sa_data, - dev->addr_len, 1); + return dev_mc_add_global(dev, ifr->ifr_hwaddr.sa_data); case SIOCDELMULTI: if ((!ops->ndo_set_multicast_list && !ops->ndo_set_rx_mode) || @@ -4421,8 +4286,7 @@ static int dev_ifsioc(struct net *net, struct ifreq *ifr, unsigned int cmd) return -EINVAL; if (!netif_device_present(dev)) return -ENODEV; - return dev_mc_delete(dev, ifr->ifr_hwaddr.sa_data, - dev->addr_len, 1); + return dev_mc_del_global(dev, ifr->ifr_hwaddr.sa_data); case SIOCSIFTXQLEN: if (ifr->ifr_qlen < 0) @@ -4730,7 +4594,7 @@ static void rollback_registered_many(struct list_head *head) * Flush the unicast and multicast chains */ dev_uc_flush(dev); - dev_addr_discard(dev); + dev_mc_flush(dev); if (dev->netdev_ops->ndo_uninit) dev->netdev_ops->ndo_uninit(dev); @@ -5310,6 +5174,7 @@ struct net_device *alloc_netdev_mq(int sizeof_priv, const char *name, if (dev_addr_init(dev)) goto free_rx; + dev_mc_init(dev); dev_uc_init(dev); dev_net_set(dev, &init_net); @@ -5545,7 +5410,7 @@ int dev_change_net_namespace(struct net_device *dev, struct net *net, const char * Flush the unicast and multicast chains */ dev_uc_flush(dev); - dev_addr_discard(dev); + dev_mc_flush(dev); netdev_unregister_kobject(dev); diff --git a/net/core/dev_addr_lists.c b/net/core/dev_addr_lists.c index 7e52b6d18ad..37d5975e18a 100644 --- a/net/core/dev_addr_lists.c +++ b/net/core/dev_addr_lists.c @@ -19,8 +19,9 @@ * General list handling functions */ -static int __hw_addr_add(struct netdev_hw_addr_list *list, unsigned char *addr, - int addr_len, unsigned char addr_type) +static int __hw_addr_add_ex(struct netdev_hw_addr_list *list, + unsigned char *addr, int addr_len, + unsigned char addr_type, bool global) { struct netdev_hw_addr *ha; int alloc_size; @@ -31,6 +32,13 @@ static int __hw_addr_add(struct netdev_hw_addr_list *list, unsigned char *addr, list_for_each_entry(ha, &list->list, list) { if (!memcmp(ha->addr, addr, addr_len) && ha->type == addr_type) { + if (global) { + /* check if addr is already used as global */ + if (ha->global_use) + return 0; + else + ha->global_use = true; + } ha->refcount++; return 0; } @@ -46,12 +54,19 @@ static int __hw_addr_add(struct netdev_hw_addr_list *list, unsigned char *addr, memcpy(ha->addr, addr, addr_len); ha->type = addr_type; ha->refcount = 1; + ha->global_use = global; ha->synced = false; list_add_tail_rcu(&ha->list, &list->list); list->count++; return 0; } +static int __hw_addr_add(struct netdev_hw_addr_list *list, unsigned char *addr, + int addr_len, unsigned char addr_type) +{ + return __hw_addr_add_ex(list, addr, addr_len, addr_type, false); +} + static void ha_rcu_free(struct rcu_head *head) { struct netdev_hw_addr *ha; @@ -60,14 +75,21 @@ static void ha_rcu_free(struct rcu_head *head) kfree(ha); } -static int __hw_addr_del(struct netdev_hw_addr_list *list, unsigned char *addr, - int addr_len, unsigned char addr_type) +static int __hw_addr_del_ex(struct netdev_hw_addr_list *list, + unsigned char *addr, int addr_len, + unsigned char addr_type, bool global) { struct netdev_hw_addr *ha; list_for_each_entry(ha, &list->list, list) { if (!memcmp(ha->addr, addr, addr_len) && (ha->type == addr_type || !addr_type)) { + if (global) { + if (!ha->global_use) + break; + else + ha->global_use = false; + } if (--ha->refcount) return 0; list_del_rcu(&ha->list); @@ -79,10 +101,15 @@ static int __hw_addr_del(struct netdev_hw_addr_list *list, unsigned char *addr, return -ENOENT; } -static int __hw_addr_add_multiple(struct netdev_hw_addr_list *to_list, - struct netdev_hw_addr_list *from_list, - int addr_len, - unsigned char addr_type) +static int __hw_addr_del(struct netdev_hw_addr_list *list, unsigned char *addr, + int addr_len, unsigned char addr_type) +{ + return __hw_addr_del_ex(list, addr, addr_len, addr_type, false); +} + +int __hw_addr_add_multiple(struct netdev_hw_addr_list *to_list, + struct netdev_hw_addr_list *from_list, + int addr_len, unsigned char addr_type) { int err; struct netdev_hw_addr *ha, *ha2; @@ -105,11 +132,11 @@ unroll: } return err; } +EXPORT_SYMBOL(__hw_addr_add_multiple); -static void __hw_addr_del_multiple(struct netdev_hw_addr_list *to_list, - struct netdev_hw_addr_list *from_list, - int addr_len, - unsigned char addr_type) +void __hw_addr_del_multiple(struct netdev_hw_addr_list *to_list, + struct netdev_hw_addr_list *from_list, + int addr_len, unsigned char addr_type) { struct netdev_hw_addr *ha; unsigned char type; @@ -119,10 +146,11 @@ static void __hw_addr_del_multiple(struct netdev_hw_addr_list *to_list, __hw_addr_del(to_list, ha->addr, addr_len, addr_type); } } +EXPORT_SYMBOL(__hw_addr_del_multiple); -static int __hw_addr_sync(struct netdev_hw_addr_list *to_list, - struct netdev_hw_addr_list *from_list, - int addr_len) +int __hw_addr_sync(struct netdev_hw_addr_list *to_list, + struct netdev_hw_addr_list *from_list, + int addr_len) { int err = 0; struct netdev_hw_addr *ha, *tmp; @@ -142,10 +170,11 @@ static int __hw_addr_sync(struct netdev_hw_addr_list *to_list, } return err; } +EXPORT_SYMBOL(__hw_addr_sync); -static void __hw_addr_unsync(struct netdev_hw_addr_list *to_list, - struct netdev_hw_addr_list *from_list, - int addr_len) +void __hw_addr_unsync(struct netdev_hw_addr_list *to_list, + struct netdev_hw_addr_list *from_list, + int addr_len) { struct netdev_hw_addr *ha, *tmp; @@ -159,8 +188,9 @@ static void __hw_addr_unsync(struct netdev_hw_addr_list *to_list, } } } +EXPORT_SYMBOL(__hw_addr_unsync); -static void __hw_addr_flush(struct netdev_hw_addr_list *list) +void __hw_addr_flush(struct netdev_hw_addr_list *list) { struct netdev_hw_addr *ha, *tmp; @@ -170,12 +200,14 @@ static void __hw_addr_flush(struct netdev_hw_addr_list *list) } list->count = 0; } +EXPORT_SYMBOL(__hw_addr_flush); -static void __hw_addr_init(struct netdev_hw_addr_list *list) +void __hw_addr_init(struct netdev_hw_addr_list *list) { INIT_LIST_HEAD(&list->list); list->count = 0; } +EXPORT_SYMBOL(__hw_addr_init); /* * Device addresses handling functions @@ -475,4 +507,235 @@ EXPORT_SYMBOL(dev_uc_init); * Multicast list handling functions */ -/* To be filled here */ +static int __dev_mc_add(struct net_device *dev, unsigned char *addr, + bool global) +{ + int err; + + netif_addr_lock_bh(dev); + err = __hw_addr_add_ex(&dev->mc, addr, dev->addr_len, + NETDEV_HW_ADDR_T_MULTICAST, global); + if (!err) + __dev_set_rx_mode(dev); + netif_addr_unlock_bh(dev); + return err; +} +/** + * dev_mc_add - Add a multicast address + * @dev: device + * @addr: address to add + * + * Add a multicast address to the device or increase + * the reference count if it already exists. + */ +int dev_mc_add(struct net_device *dev, unsigned char *addr) +{ + return __dev_mc_add(dev, addr, false); +} +EXPORT_SYMBOL(dev_mc_add); + +/** + * dev_mc_add_global - Add a global multicast address + * @dev: device + * @addr: address to add + * + * Add a global multicast address to the device. + */ +int dev_mc_add_global(struct net_device *dev, unsigned char *addr) +{ + return __dev_mc_add(dev, addr, true); +} +EXPORT_SYMBOL(dev_mc_add_global); + +static int __dev_mc_del(struct net_device *dev, unsigned char *addr, + bool global) +{ + int err; + + netif_addr_lock_bh(dev); + err = __hw_addr_del_ex(&dev->mc, addr, dev->addr_len, + NETDEV_HW_ADDR_T_MULTICAST, global); + if (!err) + __dev_set_rx_mode(dev); + netif_addr_unlock_bh(dev); + return err; +} + +/** + * dev_mc_del - Delete a multicast address. + * @dev: device + * @addr: address to delete + * + * Release reference to a multicast address and remove it + * from the device if the reference count drops to zero. + */ +int dev_mc_del(struct net_device *dev, unsigned char *addr) +{ + return __dev_mc_del(dev, addr, false); +} +EXPORT_SYMBOL(dev_mc_del); + +/** + * dev_mc_del_global - Delete a global multicast address. + * @dev: device + * @addr: address to delete + * + * Release reference to a multicast address and remove it + * from the device if the reference count drops to zero. + */ +int dev_mc_del_global(struct net_device *dev, unsigned char *addr) +{ + return __dev_mc_del(dev, addr, true); +} +EXPORT_SYMBOL(dev_mc_del_global); + +/** + * dev_mc_sync - Synchronize device's unicast list to another device + * @to: destination device + * @from: source device + * + * Add newly added addresses to the destination device and release + * addresses that have no users left. The source device must be + * locked by netif_tx_lock_bh. + * + * This function is intended to be called from the dev->set_multicast_list + * or dev->set_rx_mode function of layered software devices. + */ +int dev_mc_sync(struct net_device *to, struct net_device *from) +{ + int err = 0; + + if (to->addr_len != from->addr_len) + return -EINVAL; + + netif_addr_lock_bh(to); + err = __hw_addr_sync(&to->mc, &from->mc, to->addr_len); + if (!err) + __dev_set_rx_mode(to); + netif_addr_unlock_bh(to); + return err; +} +EXPORT_SYMBOL(dev_mc_sync); + +/** + * dev_mc_unsync - Remove synchronized addresses from the destination device + * @to: destination device + * @from: source device + * + * Remove all addresses that were added to the destination device by + * dev_mc_sync(). This function is intended to be called from the + * dev->stop function of layered software devices. + */ +void dev_mc_unsync(struct net_device *to, struct net_device *from) +{ + if (to->addr_len != from->addr_len) + return; + + netif_addr_lock_bh(from); + netif_addr_lock(to); + __hw_addr_unsync(&to->mc, &from->mc, to->addr_len); + __dev_set_rx_mode(to); + netif_addr_unlock(to); + netif_addr_unlock_bh(from); +} +EXPORT_SYMBOL(dev_mc_unsync); + +/** + * dev_mc_flush - Flush multicast addresses + * @dev: device + * + * Flush multicast addresses. + */ +void dev_mc_flush(struct net_device *dev) +{ + netif_addr_lock_bh(dev); + __hw_addr_flush(&dev->mc); + netif_addr_unlock_bh(dev); +} +EXPORT_SYMBOL(dev_mc_flush); + +/** + * dev_mc_flush - Init multicast address list + * @dev: device + * + * Init multicast address list. + */ +void dev_mc_init(struct net_device *dev) +{ + __hw_addr_init(&dev->mc); +} +EXPORT_SYMBOL(dev_mc_init); + +#ifdef CONFIG_PROC_FS +#include +#include + +static int dev_mc_seq_show(struct seq_file *seq, void *v) +{ + struct netdev_hw_addr *ha; + struct net_device *dev = v; + + if (v == SEQ_START_TOKEN) + return 0; + + netif_addr_lock_bh(dev); + netdev_for_each_mc_addr(ha, dev) { + int i; + + seq_printf(seq, "%-4d %-15s %-5d %-5d ", dev->ifindex, + dev->name, ha->refcount, ha->global_use); + + for (i = 0; i < dev->addr_len; i++) + seq_printf(seq, "%02x", ha->addr[i]); + + seq_putc(seq, '\n'); + } + netif_addr_unlock_bh(dev); + return 0; +} + +static const struct seq_operations dev_mc_seq_ops = { + .start = dev_seq_start, + .next = dev_seq_next, + .stop = dev_seq_stop, + .show = dev_mc_seq_show, +}; + +static int dev_mc_seq_open(struct inode *inode, struct file *file) +{ + return seq_open_net(inode, file, &dev_mc_seq_ops, + sizeof(struct seq_net_private)); +} + +static const struct file_operations dev_mc_seq_fops = { + .owner = THIS_MODULE, + .open = dev_mc_seq_open, + .read = seq_read, + .llseek = seq_lseek, + .release = seq_release_net, +}; + +#endif + +static int __net_init dev_mc_net_init(struct net *net) +{ + if (!proc_net_fops_create(net, "dev_mcast", 0, &dev_mc_seq_fops)) + return -ENOMEM; + return 0; +} + +static void __net_exit dev_mc_net_exit(struct net *net) +{ + proc_net_remove(net, "dev_mcast"); +} + +static struct pernet_operations __net_initdata dev_mc_net_ops = { + .init = dev_mc_net_init, + .exit = dev_mc_net_exit, +}; + +void __init dev_mcast_init(void) +{ + register_pernet_subsys(&dev_mc_net_ops); +} + diff --git a/net/core/dev_mcast.c b/net/core/dev_mcast.c deleted file mode 100644 index 3dc295beb48..00000000000 --- a/net/core/dev_mcast.c +++ /dev/null @@ -1,232 +0,0 @@ -/* - * Linux NET3: Multicast List maintenance. - * - * Authors: - * Tim Kordas - * Richard Underwood - * - * Stir fried together from the IP multicast and CAP patches above - * Alan Cox - * - * Fixes: - * Alan Cox : Update the device on a real delete - * rather than any time but... - * Alan Cox : IFF_ALLMULTI support. - * Alan Cox : New format set_multicast_list() calls. - * Gleb Natapov : Remove dev_mc_lock. - * - * 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. - */ - -#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 - - -/* - * Device multicast list maintenance. - * - * This is used both by IP and by the user level maintenance functions. - * Unlike BSD we maintain a usage count on a given multicast address so - * that a casual user application can add/delete multicasts used by - * protocols without doing damage to the protocols when it deletes the - * entries. It also helps IP as it tracks overlapping maps. - * - * Device mc lists are changed by bh at least if IPv6 is enabled, - * so that it must be bh protected. - * - * We block accesses to device mc filters with netif_tx_lock. - */ - -/* - * Delete a device level multicast - */ - -int dev_mc_delete(struct net_device *dev, void *addr, int alen, int glbl) -{ - int err; - - netif_addr_lock_bh(dev); - err = __dev_addr_delete(&dev->mc_list, &dev->mc_count, - addr, alen, glbl); - if (!err) { - /* - * We have altered the list, so the card - * loaded filter is now wrong. Fix it - */ - - __dev_set_rx_mode(dev); - } - netif_addr_unlock_bh(dev); - return err; -} - -/* - * Add a device level multicast - */ - -int dev_mc_add(struct net_device *dev, void *addr, int alen, int glbl) -{ - int err; - - netif_addr_lock_bh(dev); - if (alen != dev->addr_len) - err = -EINVAL; - else - err = __dev_addr_add(&dev->mc_list, &dev->mc_count, addr, alen, glbl); - if (!err) - __dev_set_rx_mode(dev); - netif_addr_unlock_bh(dev); - return err; -} - -/** - * dev_mc_sync - Synchronize device's multicast list to another device - * @to: destination device - * @from: source device - * - * Add newly added addresses to the destination device and release - * addresses that have no users left. The source device must be - * locked by netif_tx_lock_bh. - * - * This function is intended to be called from the dev->set_multicast_list - * or dev->set_rx_mode function of layered software devices. - */ -int dev_mc_sync(struct net_device *to, struct net_device *from) -{ - int err = 0; - - netif_addr_lock_bh(to); - err = __dev_addr_sync(&to->mc_list, &to->mc_count, - &from->mc_list, &from->mc_count); - if (!err) - __dev_set_rx_mode(to); - netif_addr_unlock_bh(to); - - return err; -} -EXPORT_SYMBOL(dev_mc_sync); - - -/** - * dev_mc_unsync - Remove synchronized addresses from the destination - * device - * @to: destination device - * @from: source device - * - * Remove all addresses that were added to the destination device by - * dev_mc_sync(). This function is intended to be called from the - * dev->stop function of layered software devices. - */ -void dev_mc_unsync(struct net_device *to, struct net_device *from) -{ - netif_addr_lock_bh(from); - netif_addr_lock(to); - - __dev_addr_unsync(&to->mc_list, &to->mc_count, - &from->mc_list, &from->mc_count); - __dev_set_rx_mode(to); - - netif_addr_unlock(to); - netif_addr_unlock_bh(from); -} -EXPORT_SYMBOL(dev_mc_unsync); - -#ifdef CONFIG_PROC_FS -static int dev_mc_seq_show(struct seq_file *seq, void *v) -{ - struct dev_addr_list *m; - struct net_device *dev = v; - - if (v == SEQ_START_TOKEN) - return 0; - - netif_addr_lock_bh(dev); - for (m = dev->mc_list; m; m = m->next) { - int i; - - seq_printf(seq, "%-4d %-15s %-5d %-5d ", dev->ifindex, - dev->name, m->dmi_users, m->dmi_gusers); - - for (i = 0; i < m->dmi_addrlen; i++) - seq_printf(seq, "%02x", m->dmi_addr[i]); - - seq_putc(seq, '\n'); - } - netif_addr_unlock_bh(dev); - return 0; -} - -static const struct seq_operations dev_mc_seq_ops = { - .start = dev_seq_start, - .next = dev_seq_next, - .stop = dev_seq_stop, - .show = dev_mc_seq_show, -}; - -static int dev_mc_seq_open(struct inode *inode, struct file *file) -{ - return seq_open_net(inode, file, &dev_mc_seq_ops, - sizeof(struct seq_net_private)); -} - -static const struct file_operations dev_mc_seq_fops = { - .owner = THIS_MODULE, - .open = dev_mc_seq_open, - .read = seq_read, - .llseek = seq_lseek, - .release = seq_release_net, -}; - -#endif - -static int __net_init dev_mc_net_init(struct net *net) -{ - if (!proc_net_fops_create(net, "dev_mcast", 0, &dev_mc_seq_fops)) - return -ENOMEM; - return 0; -} - -static void __net_exit dev_mc_net_exit(struct net *net) -{ - proc_net_remove(net, "dev_mcast"); -} - -static struct pernet_operations __net_initdata dev_mc_net_ops = { - .init = dev_mc_net_init, - .exit = dev_mc_net_exit, -}; - -void __init dev_mcast_init(void) -{ - register_pernet_subsys(&dev_mc_net_ops); -} - -EXPORT_SYMBOL(dev_mc_add); -EXPORT_SYMBOL(dev_mc_delete); diff --git a/net/decnet/dn_dev.c b/net/decnet/dn_dev.c index 238af093495..f3e4734d207 100644 --- a/net/decnet/dn_dev.c +++ b/net/decnet/dn_dev.c @@ -349,7 +349,7 @@ static void dn_dev_del_ifa(struct dn_dev *dn_db, struct dn_ifaddr **ifap, int de if (dn_db->dev->type == ARPHRD_ETHER) { if (ifa1->ifa_local != dn_eth2dn(dev->dev_addr)) { dn_dn2eth(mac_addr, ifa1->ifa_local); - dev_mc_delete(dev, mac_addr, ETH_ALEN, 0); + dev_mc_del(dev, mac_addr); } } @@ -380,7 +380,7 @@ static int dn_dev_insert_ifa(struct dn_dev *dn_db, struct dn_ifaddr *ifa) if (dev->type == ARPHRD_ETHER) { if (ifa->ifa_local != dn_eth2dn(dev->dev_addr)) { dn_dn2eth(mac_addr, ifa->ifa_local); - dev_mc_add(dev, mac_addr, ETH_ALEN, 0); + dev_mc_add(dev, mac_addr); } } @@ -1000,9 +1000,9 @@ static int dn_eth_up(struct net_device *dev) struct dn_dev *dn_db = dev->dn_ptr; if (dn_db->parms.forwarding == 0) - dev_mc_add(dev, dn_rt_all_end_mcast, ETH_ALEN, 0); + dev_mc_add(dev, dn_rt_all_end_mcast); else - dev_mc_add(dev, dn_rt_all_rt_mcast, ETH_ALEN, 0); + dev_mc_add(dev, dn_rt_all_rt_mcast); dn_db->use_long = 1; @@ -1014,9 +1014,9 @@ static void dn_eth_down(struct net_device *dev) struct dn_dev *dn_db = dev->dn_ptr; if (dn_db->parms.forwarding == 0) - dev_mc_delete(dev, dn_rt_all_end_mcast, ETH_ALEN, 0); + dev_mc_del(dev, dn_rt_all_end_mcast); else - dev_mc_delete(dev, dn_rt_all_rt_mcast, ETH_ALEN, 0); + dev_mc_del(dev, dn_rt_all_rt_mcast); } static void dn_dev_set_timer(struct net_device *dev); diff --git a/net/ipv4/igmp.c b/net/ipv4/igmp.c index 63bf298ca10..51824c42b77 100644 --- a/net/ipv4/igmp.c +++ b/net/ipv4/igmp.c @@ -997,7 +997,7 @@ static void ip_mc_filter_add(struct in_device *in_dev, __be32 addr) --ANK */ if (arp_mc_map(addr, buf, dev, 0) == 0) - dev_mc_add(dev, buf, dev->addr_len, 0); + dev_mc_add(dev, buf); } /* @@ -1010,7 +1010,7 @@ static void ip_mc_filter_del(struct in_device *in_dev, __be32 addr) struct net_device *dev = in_dev->dev; if (arp_mc_map(addr, buf, dev, 0) == 0) - dev_mc_delete(dev, buf, dev->addr_len, 0); + dev_mc_del(dev, buf); } #ifdef CONFIG_IP_MULTICAST diff --git a/net/ipv4/netfilter/ipt_CLUSTERIP.c b/net/ipv4/netfilter/ipt_CLUSTERIP.c index 0886f96c736..a2208b7b313 100644 --- a/net/ipv4/netfilter/ipt_CLUSTERIP.c +++ b/net/ipv4/netfilter/ipt_CLUSTERIP.c @@ -87,7 +87,7 @@ clusterip_config_entry_put(struct clusterip_config *c) list_del(&c->list); write_unlock_bh(&clusterip_lock); - dev_mc_delete(c->dev, c->clustermac, ETH_ALEN, 0); + dev_mc_del(c->dev, c->clustermac); dev_put(c->dev); /* In case anyone still accesses the file, the open/close @@ -396,7 +396,7 @@ static bool clusterip_tg_check(const struct xt_tgchk_param *par) dev_put(dev); return false; } - dev_mc_add(config->dev,config->clustermac, ETH_ALEN, 0); + dev_mc_add(config->dev, config->clustermac); } } cipinfo->config = config; diff --git a/net/ipv6/mcast.c b/net/ipv6/mcast.c index bcd97191596..37d1868c006 100644 --- a/net/ipv6/mcast.c +++ b/net/ipv6/mcast.c @@ -714,7 +714,7 @@ static void igmp6_group_added(struct ifmcaddr6 *mc) if (!(mc->mca_flags&MAF_LOADED)) { mc->mca_flags |= MAF_LOADED; if (ndisc_mc_map(&mc->mca_addr, buf, dev, 0) == 0) - dev_mc_add(dev, buf, dev->addr_len, 0); + dev_mc_add(dev, buf); } spin_unlock_bh(&mc->mca_lock); @@ -740,7 +740,7 @@ static void igmp6_group_dropped(struct ifmcaddr6 *mc) if (mc->mca_flags&MAF_LOADED) { mc->mca_flags &= ~MAF_LOADED; if (ndisc_mc_map(&mc->mca_addr, buf, dev, 0) == 0) - dev_mc_delete(dev, buf, dev->addr_len, 0); + dev_mc_del(dev, buf); } if (mc->mca_flags & MAF_NOREPORT) diff --git a/net/mac80211/driver-ops.h b/net/mac80211/driver-ops.h index c3d844093a2..9179196da26 100644 --- a/net/mac80211/driver-ops.h +++ b/net/mac80211/driver-ops.h @@ -84,16 +84,14 @@ static inline void drv_bss_info_changed(struct ieee80211_local *local, } static inline u64 drv_prepare_multicast(struct ieee80211_local *local, - int mc_count, - struct dev_addr_list *mc_list) + struct netdev_hw_addr_list *mc_list) { u64 ret = 0; if (local->ops->prepare_multicast) - ret = local->ops->prepare_multicast(&local->hw, mc_count, - mc_list); + ret = local->ops->prepare_multicast(&local->hw, mc_list); - trace_drv_prepare_multicast(local, mc_count, ret); + trace_drv_prepare_multicast(local, mc_list->count, ret); return ret; } diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index ab369e2a528..7fdacf9408b 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -646,8 +646,7 @@ struct ieee80211_local { struct work_struct recalc_smps; /* aggregated multicast list */ - struct dev_addr_list *mc_list; - int mc_count; + struct netdev_hw_addr_list mc_list; bool tim_in_locked_section; /* see ieee80211_beacon_get() */ diff --git a/net/mac80211/iface.c b/net/mac80211/iface.c index b4ec59a8dc0..00f3a93c6b0 100644 --- a/net/mac80211/iface.c +++ b/net/mac80211/iface.c @@ -412,8 +412,7 @@ static int ieee80211_stop(struct net_device *dev) netif_addr_lock_bh(dev); spin_lock_bh(&local->filter_lock); - __dev_addr_unsync(&local->mc_list, &local->mc_count, - &dev->mc_list, &dev->mc_count); + __hw_addr_unsync(&local->mc_list, &dev->mc, dev->addr_len); spin_unlock_bh(&local->filter_lock); netif_addr_unlock_bh(dev); @@ -596,8 +595,7 @@ static void ieee80211_set_multicast_list(struct net_device *dev) sdata->flags ^= IEEE80211_SDATA_PROMISC; } spin_lock_bh(&local->filter_lock); - __dev_addr_sync(&local->mc_list, &local->mc_count, - &dev->mc_list, &dev->mc_count); + __hw_addr_sync(&local->mc_list, &dev->mc, dev->addr_len); spin_unlock_bh(&local->filter_lock); ieee80211_queue_work(&local->hw, &local->reconfig_filter); } diff --git a/net/mac80211/main.c b/net/mac80211/main.c index 06c33b68d8e..84ad249a4e2 100644 --- a/net/mac80211/main.c +++ b/net/mac80211/main.c @@ -71,7 +71,7 @@ void ieee80211_configure_filter(struct ieee80211_local *local) spin_lock_bh(&local->filter_lock); changed_flags = local->filter_flags ^ new_flags; - mc = drv_prepare_multicast(local, local->mc_count, local->mc_list); + mc = drv_prepare_multicast(local, &local->mc_list); spin_unlock_bh(&local->filter_lock); /* be a bit nasty */ diff --git a/net/packet/af_packet.c b/net/packet/af_packet.c index 48c1e0ae565..b0f037cc899 100644 --- a/net/packet/af_packet.c +++ b/net/packet/af_packet.c @@ -1691,9 +1691,9 @@ static int packet_dev_mc(struct net_device *dev, struct packet_mclist *i, if (i->alen != dev->addr_len) return -EINVAL; if (what > 0) - return dev_mc_add(dev, i->addr, i->alen, 0); + return dev_mc_add(dev, i->addr); else - return dev_mc_delete(dev, i->addr, i->alen, 0); + return dev_mc_del(dev, i->addr); break; case PACKET_MR_PROMISC: return dev_set_promiscuity(dev, what); -- cgit v1.2.3 From fd558d186df2c13a22455373858bae634a4795af Mon Sep 17 00:00:00 2001 From: James Chapman Date: Fri, 2 Apr 2010 06:18:33 +0000 Subject: l2tp: Split pppol2tp patch into separate l2tp and ppp parts This patch splits the pppol2tp driver into separate L2TP and PPP parts to prepare for L2TPv3 support. In L2TPv3, protocols other than PPP can be carried, so this split creates a common L2TP core that will handle the common L2TP bits which protocol support modules such as PPP will use. Note that the existing pppol2tp module is split into l2tp_core and l2tp_ppp by this change. There are no feature changes here. Internally, however, there are significant changes, mostly to handle the separation of PPP-specific data from the L2TP session and to provide hooks in the core for modules like PPP to access. Signed-off-by: James Chapman Reviewed-by: Randy Dunlap Signed-off-by: David S. Miller --- drivers/net/Kconfig | 7 +- include/linux/if_pppol2tp.h | 2 +- net/Kconfig | 1 + net/Makefile | 2 +- net/l2tp/Kconfig | 28 + net/l2tp/Makefile | 5 +- net/l2tp/l2tp_core.c | 1227 ++++++++++++++++++++ net/l2tp/l2tp_core.h | 254 ++++ net/l2tp/l2tp_ppp.c | 1658 ++++++++++++++++++++++++++ net/l2tp/pppol2tp.c | 2680 ------------------------------------------- 10 files changed, 3175 insertions(+), 2689 deletions(-) create mode 100644 net/l2tp/Kconfig create mode 100644 net/l2tp/l2tp_core.c create mode 100644 net/l2tp/l2tp_core.h create mode 100644 net/l2tp/l2tp_ppp.c delete mode 100644 net/l2tp/pppol2tp.c (limited to 'include/linux') diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig index 2cffcb84a24..a55bff26a26 100644 --- a/drivers/net/Kconfig +++ b/drivers/net/Kconfig @@ -3158,17 +3158,12 @@ config PPPOATM config PPPOL2TP tristate "PPP over L2TP (EXPERIMENTAL)" - depends on EXPERIMENTAL && PPP && INET + depends on EXPERIMENTAL && L2TP && PPP help Support for PPP-over-L2TP socket family. L2TP is a protocol used by ISPs and enterprises to tunnel PPP traffic over UDP tunnels. L2TP is replacing PPTP for VPN uses. - This kernel component handles only L2TP data packets: a - userland daemon handles L2TP the control protocol (tunnel - and session setup). One such daemon is OpenL2TP - (http://openl2tp.sourceforge.net/). - config SLIP tristate "SLIP (serial line) support" ---help--- diff --git a/include/linux/if_pppol2tp.h b/include/linux/if_pppol2tp.h index c58baea4a25..1a1fb6e5d93 100644 --- a/include/linux/if_pppol2tp.h +++ b/include/linux/if_pppol2tp.h @@ -2,7 +2,7 @@ * Linux PPP over L2TP (PPPoL2TP) Socket Implementation (RFC 2661) * * This file supplies definitions required by the PPP over L2TP driver - * (pppol2tp.c). All version information wrt this file is located in pppol2tp.c + * (l2tp_ppp.c). All version information wrt this file is located in l2tp_ppp.c * * License: * This program is free software; you can redistribute it and/or diff --git a/net/Kconfig b/net/Kconfig index e10d55c8ee5..0d68b40fc0e 100644 --- a/net/Kconfig +++ b/net/Kconfig @@ -186,6 +186,7 @@ source "net/sctp/Kconfig" source "net/rds/Kconfig" source "net/tipc/Kconfig" source "net/atm/Kconfig" +source "net/l2tp/Kconfig" source "net/802/Kconfig" source "net/bridge/Kconfig" source "net/dsa/Kconfig" diff --git a/net/Makefile b/net/Makefile index 13ca77e0eb0..cb7bdc1210c 100644 --- a/net/Makefile +++ b/net/Makefile @@ -40,7 +40,7 @@ obj-$(CONFIG_BT) += bluetooth/ obj-$(CONFIG_SUNRPC) += sunrpc/ obj-$(CONFIG_AF_RXRPC) += rxrpc/ obj-$(CONFIG_ATM) += atm/ -obj-$(CONFIG_PPPOL2TP) += l2tp/ +obj-$(CONFIG_L2TP) += l2tp/ obj-$(CONFIG_DECNET) += decnet/ obj-$(CONFIG_ECONET) += econet/ obj-$(CONFIG_PHONET) += phonet/ diff --git a/net/l2tp/Kconfig b/net/l2tp/Kconfig new file mode 100644 index 00000000000..ec88c5cdc39 --- /dev/null +++ b/net/l2tp/Kconfig @@ -0,0 +1,28 @@ +# +# Layer Two Tunneling Protocol (L2TP) +# + +menuconfig L2TP + tristate "Layer Two Tunneling Protocol (L2TP)" + depends on INET + ---help--- + Layer Two Tunneling Protocol + + From RFC 2661 . + + L2TP facilitates the tunneling of packets across an + intervening network in a way that is as transparent as + possible to both end-users and applications. + + L2TP is often used to tunnel PPP traffic over IP + tunnels. One IP tunnel may carry thousands of individual PPP + connections. L2TP is also used as a VPN protocol, popular + with home workers to connect to their offices. + + The kernel component handles only L2TP data packets: a + userland daemon handles L2TP the control protocol (tunnel + and session setup). One such daemon is OpenL2TP + (http://openl2tp.org/). + + If you don't need L2TP, say N. To compile all L2TP code as + modules, choose M here. diff --git a/net/l2tp/Makefile b/net/l2tp/Makefile index 9af41e898a0..c91f208b169 100644 --- a/net/l2tp/Makefile +++ b/net/l2tp/Makefile @@ -2,4 +2,7 @@ # Makefile for the L2TP. # -obj-$(CONFIG_PPPOL2TP) += pppol2tp.o +obj-$(CONFIG_L2TP) += l2tp_core.o + +# Build l2tp as modules if L2TP is M +obj-$(subst y,$(CONFIG_L2TP),$(CONFIG_PPPOL2TP)) += l2tp_ppp.o diff --git a/net/l2tp/l2tp_core.c b/net/l2tp/l2tp_core.c new file mode 100644 index 00000000000..4b6da368989 --- /dev/null +++ b/net/l2tp/l2tp_core.c @@ -0,0 +1,1227 @@ +/* + * L2TP core. + * + * Copyright (c) 2008,2009,2010 Katalix Systems Ltd + * + * This file contains some code of the original L2TPv2 pppol2tp + * driver, which has the following copyright: + * + * Authors: Martijn van Oosterhout + * James Chapman (jchapman@katalix.com) + * Contributors: + * Michal Ostrowski + * Arnaldo Carvalho de Melo + * David S. Miller (davem@redhat.com) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#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 "l2tp_core.h" + +#define L2TP_DRV_VERSION "V2.0" + +/* L2TP header constants */ +#define L2TP_HDRFLAG_T 0x8000 +#define L2TP_HDRFLAG_L 0x4000 +#define L2TP_HDRFLAG_S 0x0800 +#define L2TP_HDRFLAG_O 0x0200 +#define L2TP_HDRFLAG_P 0x0100 + +#define L2TP_HDR_VER_MASK 0x000F +#define L2TP_HDR_VER_2 0x0002 + +/* L2TPv3 default L2-specific sublayer */ +#define L2TP_SLFLAG_S 0x40000000 +#define L2TP_SL_SEQ_MASK 0x00ffffff + +#define L2TP_HDR_SIZE_SEQ 10 +#define L2TP_HDR_SIZE_NOSEQ 6 + +/* Default trace flags */ +#define L2TP_DEFAULT_DEBUG_FLAGS 0 + +#define PRINTK(_mask, _type, _lvl, _fmt, args...) \ + do { \ + if ((_mask) & (_type)) \ + printk(_lvl "L2TP: " _fmt, ##args); \ + } while (0) + +/* Private data stored for received packets in the skb. + */ +struct l2tp_skb_cb { + u16 ns; + u16 has_seq; + u16 length; + unsigned long expires; +}; + +#define L2TP_SKB_CB(skb) ((struct l2tp_skb_cb *) &skb->cb[sizeof(struct inet_skb_parm)]) + +static atomic_t l2tp_tunnel_count; +static atomic_t l2tp_session_count; + +/* per-net private data for this module */ +static unsigned int l2tp_net_id; +struct l2tp_net { + struct list_head l2tp_tunnel_list; + rwlock_t l2tp_tunnel_list_lock; +}; + +static inline struct l2tp_net *l2tp_pernet(struct net *net) +{ + BUG_ON(!net); + + return net_generic(net, l2tp_net_id); +} + +/* Session hash list. + * The session_id SHOULD be random according to RFC2661, but several + * L2TP implementations (Cisco and Microsoft) use incrementing + * session_ids. So we do a real hash on the session_id, rather than a + * simple bitmask. + */ +static inline struct hlist_head * +l2tp_session_id_hash(struct l2tp_tunnel *tunnel, u32 session_id) +{ + return &tunnel->session_hlist[hash_32(session_id, L2TP_HASH_BITS)]; +} + +/* Lookup a session by id + */ +struct l2tp_session *l2tp_session_find(struct l2tp_tunnel *tunnel, u32 session_id) +{ + struct hlist_head *session_list = + l2tp_session_id_hash(tunnel, session_id); + struct l2tp_session *session; + struct hlist_node *walk; + + read_lock_bh(&tunnel->hlist_lock); + hlist_for_each_entry(session, walk, session_list, hlist) { + if (session->session_id == session_id) { + read_unlock_bh(&tunnel->hlist_lock); + return session; + } + } + read_unlock_bh(&tunnel->hlist_lock); + + return NULL; +} +EXPORT_SYMBOL_GPL(l2tp_session_find); + +struct l2tp_session *l2tp_session_find_nth(struct l2tp_tunnel *tunnel, int nth) +{ + int hash; + struct hlist_node *walk; + struct l2tp_session *session; + int count = 0; + + read_lock_bh(&tunnel->hlist_lock); + for (hash = 0; hash < L2TP_HASH_SIZE; hash++) { + hlist_for_each_entry(session, walk, &tunnel->session_hlist[hash], hlist) { + if (++count > nth) { + read_unlock_bh(&tunnel->hlist_lock); + return session; + } + } + } + + read_unlock_bh(&tunnel->hlist_lock); + + return NULL; +} +EXPORT_SYMBOL_GPL(l2tp_session_find_nth); + +/* Lookup a tunnel by id + */ +struct l2tp_tunnel *l2tp_tunnel_find(struct net *net, u32 tunnel_id) +{ + struct l2tp_tunnel *tunnel; + struct l2tp_net *pn = l2tp_pernet(net); + + read_lock_bh(&pn->l2tp_tunnel_list_lock); + list_for_each_entry(tunnel, &pn->l2tp_tunnel_list, list) { + if (tunnel->tunnel_id == tunnel_id) { + read_unlock_bh(&pn->l2tp_tunnel_list_lock); + return tunnel; + } + } + read_unlock_bh(&pn->l2tp_tunnel_list_lock); + + return NULL; +} +EXPORT_SYMBOL_GPL(l2tp_tunnel_find); + +struct l2tp_tunnel *l2tp_tunnel_find_nth(struct net *net, int nth) +{ + struct l2tp_net *pn = l2tp_pernet(net); + struct l2tp_tunnel *tunnel; + int count = 0; + + read_lock_bh(&pn->l2tp_tunnel_list_lock); + list_for_each_entry(tunnel, &pn->l2tp_tunnel_list, list) { + if (++count > nth) { + read_unlock_bh(&pn->l2tp_tunnel_list_lock); + return tunnel; + } + } + + read_unlock_bh(&pn->l2tp_tunnel_list_lock); + + return NULL; +} +EXPORT_SYMBOL_GPL(l2tp_tunnel_find_nth); + +/***************************************************************************** + * Receive data handling + *****************************************************************************/ + +/* Queue a skb in order. We come here only if the skb has an L2TP sequence + * number. + */ +static void l2tp_recv_queue_skb(struct l2tp_session *session, struct sk_buff *skb) +{ + struct sk_buff *skbp; + struct sk_buff *tmp; + u16 ns = L2TP_SKB_CB(skb)->ns; + + spin_lock_bh(&session->reorder_q.lock); + skb_queue_walk_safe(&session->reorder_q, skbp, tmp) { + if (L2TP_SKB_CB(skbp)->ns > ns) { + __skb_queue_before(&session->reorder_q, skbp, skb); + PRINTK(session->debug, L2TP_MSG_SEQ, KERN_DEBUG, + "%s: pkt %hu, inserted before %hu, reorder_q len=%d\n", + session->name, ns, L2TP_SKB_CB(skbp)->ns, + skb_queue_len(&session->reorder_q)); + session->stats.rx_oos_packets++; + goto out; + } + } + + __skb_queue_tail(&session->reorder_q, skb); + +out: + spin_unlock_bh(&session->reorder_q.lock); +} + +/* Dequeue a single skb. + */ +static void l2tp_recv_dequeue_skb(struct l2tp_session *session, struct sk_buff *skb) +{ + struct l2tp_tunnel *tunnel = session->tunnel; + int length = L2TP_SKB_CB(skb)->length; + + /* We're about to requeue the skb, so return resources + * to its current owner (a socket receive buffer). + */ + skb_orphan(skb); + + tunnel->stats.rx_packets++; + tunnel->stats.rx_bytes += length; + session->stats.rx_packets++; + session->stats.rx_bytes += length; + + if (L2TP_SKB_CB(skb)->has_seq) { + /* Bump our Nr */ + session->nr++; + PRINTK(session->debug, L2TP_MSG_SEQ, KERN_DEBUG, + "%s: updated nr to %hu\n", session->name, session->nr); + } + + /* call private receive handler */ + if (session->recv_skb != NULL) + (*session->recv_skb)(session, skb, L2TP_SKB_CB(skb)->length); + else + kfree_skb(skb); + + if (session->deref) + (*session->deref)(session); +} + +/* Dequeue skbs from the session's reorder_q, subject to packet order. + * Skbs that have been in the queue for too long are simply discarded. + */ +static void l2tp_recv_dequeue(struct l2tp_session *session) +{ + struct sk_buff *skb; + struct sk_buff *tmp; + + /* If the pkt at the head of the queue has the nr that we + * expect to send up next, dequeue it and any other + * in-sequence packets behind it. + */ + spin_lock_bh(&session->reorder_q.lock); + skb_queue_walk_safe(&session->reorder_q, skb, tmp) { + if (time_after(jiffies, L2TP_SKB_CB(skb)->expires)) { + session->stats.rx_seq_discards++; + session->stats.rx_errors++; + PRINTK(session->debug, L2TP_MSG_SEQ, KERN_DEBUG, + "%s: oos pkt %hu len %d discarded (too old), " + "waiting for %hu, reorder_q_len=%d\n", + session->name, L2TP_SKB_CB(skb)->ns, + L2TP_SKB_CB(skb)->length, session->nr, + skb_queue_len(&session->reorder_q)); + __skb_unlink(skb, &session->reorder_q); + kfree_skb(skb); + if (session->deref) + (*session->deref)(session); + continue; + } + + if (L2TP_SKB_CB(skb)->has_seq) { + if (L2TP_SKB_CB(skb)->ns != session->nr) { + PRINTK(session->debug, L2TP_MSG_SEQ, KERN_DEBUG, + "%s: holding oos pkt %hu len %d, " + "waiting for %hu, reorder_q_len=%d\n", + session->name, L2TP_SKB_CB(skb)->ns, + L2TP_SKB_CB(skb)->length, session->nr, + skb_queue_len(&session->reorder_q)); + goto out; + } + } + __skb_unlink(skb, &session->reorder_q); + + /* Process the skb. We release the queue lock while we + * do so to let other contexts process the queue. + */ + spin_unlock_bh(&session->reorder_q.lock); + l2tp_recv_dequeue_skb(session, skb); + spin_lock_bh(&session->reorder_q.lock); + } + +out: + spin_unlock_bh(&session->reorder_q.lock); +} + +static inline int l2tp_verify_udp_checksum(struct sock *sk, + struct sk_buff *skb) +{ + struct udphdr *uh = udp_hdr(skb); + u16 ulen = ntohs(uh->len); + struct inet_sock *inet; + __wsum psum; + + if (sk->sk_no_check || skb_csum_unnecessary(skb) || !uh->check) + return 0; + + inet = inet_sk(sk); + psum = csum_tcpudp_nofold(inet->inet_saddr, inet->inet_daddr, ulen, + IPPROTO_UDP, 0); + + if ((skb->ip_summed == CHECKSUM_COMPLETE) && + !csum_fold(csum_add(psum, skb->csum))) + return 0; + + skb->csum = psum; + + return __skb_checksum_complete(skb); +} + +/* Internal UDP receive frame. Do the real work of receiving an L2TP data frame + * here. The skb is not on a list when we get here. + * Returns 0 if the packet was a data packet and was successfully passed on. + * Returns 1 if the packet was not a good data packet and could not be + * forwarded. All such packets are passed up to userspace to deal with. + */ +int l2tp_udp_recv_core(struct l2tp_tunnel *tunnel, struct sk_buff *skb, + int (*payload_hook)(struct sk_buff *skb)) +{ + struct l2tp_session *session = NULL; + unsigned char *ptr, *optr; + u16 hdrflags; + u32 tunnel_id, session_id; + int length; + int offset; + u16 version; + u16 ns, nr; + + if (tunnel->sock && l2tp_verify_udp_checksum(tunnel->sock, skb)) + goto discard_bad_csum; + + /* UDP always verifies the packet length. */ + __skb_pull(skb, sizeof(struct udphdr)); + + /* Short packet? */ + if (!pskb_may_pull(skb, L2TP_HDR_SIZE_SEQ)) { + PRINTK(tunnel->debug, L2TP_MSG_DATA, KERN_INFO, + "%s: recv short packet (len=%d)\n", tunnel->name, skb->len); + goto error; + } + + /* Point to L2TP header */ + optr = ptr = skb->data; + + /* Trace packet contents, if enabled */ + if (tunnel->debug & L2TP_MSG_DATA) { + length = min(32u, skb->len); + if (!pskb_may_pull(skb, length)) + goto error; + + printk(KERN_DEBUG "%s: recv: ", tunnel->name); + + offset = 0; + do { + printk(" %02X", ptr[offset]); + } while (++offset < length); + + printk("\n"); + } + + /* Get L2TP header flags */ + hdrflags = ntohs(*(__be16 *)ptr); + + /* Check protocol version */ + version = hdrflags & L2TP_HDR_VER_MASK; + if (version != tunnel->version) { + PRINTK(tunnel->debug, L2TP_MSG_DATA, KERN_INFO, + "%s: recv protocol version mismatch: got %d expected %d\n", + tunnel->name, version, tunnel->version); + goto error; + } + + /* Get length of L2TP packet */ + length = skb->len; + + /* If type is control packet, it is handled by userspace. */ + if (hdrflags & L2TP_HDRFLAG_T) { + PRINTK(tunnel->debug, L2TP_MSG_DATA, KERN_DEBUG, + "%s: recv control packet, len=%d\n", tunnel->name, length); + goto error; + } + + /* Skip flags */ + ptr += 2; + + /* If length is present, skip it */ + if (hdrflags & L2TP_HDRFLAG_L) + ptr += 2; + + /* Extract tunnel and session ID */ + tunnel_id = ntohs(*(__be16 *) ptr); + ptr += 2; + session_id = ntohs(*(__be16 *) ptr); + ptr += 2; + + /* Find the session context */ + session = l2tp_session_find(tunnel, session_id); + if (!session) { + /* Not found? Pass to userspace to deal with */ + PRINTK(tunnel->debug, L2TP_MSG_DATA, KERN_INFO, + "%s: no session found (%hu/%hu). Passing up.\n", + tunnel->name, tunnel_id, session_id); + goto error; + } + + /* The ref count is increased since we now hold a pointer to + * the session. Take care to decrement the refcnt when exiting + * this function from now on... + */ + l2tp_session_inc_refcount(session); + if (session->ref) + (*session->ref)(session); + + /* Handle the optional sequence numbers. Sequence numbers are + * in different places for L2TPv2 and L2TPv3. + * + * If we are the LAC, enable/disable sequence numbers under + * the control of the LNS. If no sequence numbers present but + * we were expecting them, discard frame. + */ + ns = nr = 0; + L2TP_SKB_CB(skb)->has_seq = 0; + if (hdrflags & L2TP_HDRFLAG_S) { + ns = (u16) ntohs(*(__be16 *) ptr); + ptr += 2; + nr = ntohs(*(__be16 *) ptr); + ptr += 2; + + /* Store L2TP info in the skb */ + L2TP_SKB_CB(skb)->ns = ns; + L2TP_SKB_CB(skb)->has_seq = 1; + + PRINTK(session->debug, L2TP_MSG_SEQ, KERN_DEBUG, + "%s: recv data ns=%hu, nr=%hu, session nr=%hu\n", + session->name, ns, nr, session->nr); + } + + if (L2TP_SKB_CB(skb)->has_seq) { + /* Received a packet with sequence numbers. If we're the LNS, + * check if we sre sending sequence numbers and if not, + * configure it so. + */ + if ((!session->lns_mode) && (!session->send_seq)) { + PRINTK(session->debug, L2TP_MSG_SEQ, KERN_INFO, + "%s: requested to enable seq numbers by LNS\n", + session->name); + session->send_seq = -1; + } + } else { + /* No sequence numbers. + * If user has configured mandatory sequence numbers, discard. + */ + if (session->recv_seq) { + PRINTK(session->debug, L2TP_MSG_SEQ, KERN_WARNING, + "%s: recv data has no seq numbers when required. " + "Discarding\n", session->name); + session->stats.rx_seq_discards++; + goto discard; + } + + /* If we're the LAC and we're sending sequence numbers, the + * LNS has requested that we no longer send sequence numbers. + * If we're the LNS and we're sending sequence numbers, the + * LAC is broken. Discard the frame. + */ + if ((!session->lns_mode) && (session->send_seq)) { + PRINTK(session->debug, L2TP_MSG_SEQ, KERN_INFO, + "%s: requested to disable seq numbers by LNS\n", + session->name); + session->send_seq = 0; + } else if (session->send_seq) { + PRINTK(session->debug, L2TP_MSG_SEQ, KERN_WARNING, + "%s: recv data has no seq numbers when required. " + "Discarding\n", session->name); + session->stats.rx_seq_discards++; + goto discard; + } + } + + /* If offset bit set, skip it. */ + if (hdrflags & L2TP_HDRFLAG_O) { + offset = ntohs(*(__be16 *)ptr); + ptr += 2 + offset; + } + + offset = ptr - optr; + if (!pskb_may_pull(skb, offset)) + goto discard; + + __skb_pull(skb, offset); + + /* If caller wants to process the payload before we queue the + * packet, do so now. + */ + if (payload_hook) + if ((*payload_hook)(skb)) + goto discard; + + /* Prepare skb for adding to the session's reorder_q. Hold + * packets for max reorder_timeout or 1 second if not + * reordering. + */ + L2TP_SKB_CB(skb)->length = length; + L2TP_SKB_CB(skb)->expires = jiffies + + (session->reorder_timeout ? session->reorder_timeout : HZ); + + /* Add packet to the session's receive queue. Reordering is done here, if + * enabled. Saved L2TP protocol info is stored in skb->sb[]. + */ + if (L2TP_SKB_CB(skb)->has_seq) { + if (session->reorder_timeout != 0) { + /* Packet reordering enabled. Add skb to session's + * reorder queue, in order of ns. + */ + l2tp_recv_queue_skb(session, skb); + } else { + /* Packet reordering disabled. Discard out-of-sequence + * packets + */ + if (L2TP_SKB_CB(skb)->ns != session->nr) { + session->stats.rx_seq_discards++; + PRINTK(session->debug, L2TP_MSG_SEQ, KERN_DEBUG, + "%s: oos pkt %hu len %d discarded, " + "waiting for %hu, reorder_q_len=%d\n", + session->name, L2TP_SKB_CB(skb)->ns, + L2TP_SKB_CB(skb)->length, session->nr, + skb_queue_len(&session->reorder_q)); + goto discard; + } + skb_queue_tail(&session->reorder_q, skb); + } + } else { + /* No sequence numbers. Add the skb to the tail of the + * reorder queue. This ensures that it will be + * delivered after all previous sequenced skbs. + */ + skb_queue_tail(&session->reorder_q, skb); + } + + /* Try to dequeue as many skbs from reorder_q as we can. */ + l2tp_recv_dequeue(session); + + l2tp_session_dec_refcount(session); + + return 0; + +discard: + session->stats.rx_errors++; + kfree_skb(skb); + + if (session->deref) + (*session->deref)(session); + + l2tp_session_dec_refcount(session); + + return 0; + +discard_bad_csum: + LIMIT_NETDEBUG("%s: UDP: bad checksum\n", tunnel->name); + UDP_INC_STATS_USER(tunnel->l2tp_net, UDP_MIB_INERRORS, 0); + tunnel->stats.rx_errors++; + kfree_skb(skb); + + return 0; + +error: + /* Put UDP header back */ + __skb_push(skb, sizeof(struct udphdr)); + + return 1; +} +EXPORT_SYMBOL_GPL(l2tp_udp_recv_core); + +/* UDP encapsulation receive handler. See net/ipv4/udp.c. + * Return codes: + * 0 : success. + * <0: error + * >0: skb should be passed up to userspace as UDP. + */ +int l2tp_udp_encap_recv(struct sock *sk, struct sk_buff *skb) +{ + struct l2tp_tunnel *tunnel; + + tunnel = l2tp_sock_to_tunnel(sk); + if (tunnel == NULL) + goto pass_up; + + PRINTK(tunnel->debug, L2TP_MSG_DATA, KERN_DEBUG, + "%s: received %d bytes\n", tunnel->name, skb->len); + + if (l2tp_udp_recv_core(tunnel, skb, tunnel->recv_payload_hook)) + goto pass_up_put; + + sock_put(sk); + return 0; + +pass_up_put: + sock_put(sk); +pass_up: + return 1; +} +EXPORT_SYMBOL_GPL(l2tp_udp_encap_recv); + +/************************************************************************ + * Transmit handling + ***********************************************************************/ + +/* Build an L2TP header for the session into the buffer provided. + */ +static void l2tp_build_l2tpv2_header(struct l2tp_tunnel *tunnel, + struct l2tp_session *session, + void *buf) +{ + __be16 *bufp = buf; + u16 flags = L2TP_HDR_VER_2; + u32 tunnel_id = tunnel->peer_tunnel_id; + u32 session_id = session->peer_session_id; + + if (session->send_seq) + flags |= L2TP_HDRFLAG_S; + + /* Setup L2TP header. */ + *bufp++ = htons(flags); + *bufp++ = htons(tunnel_id); + *bufp++ = htons(session_id); + if (session->send_seq) { + *bufp++ = htons(session->ns); + *bufp++ = 0; + session->ns++; + PRINTK(session->debug, L2TP_MSG_SEQ, KERN_DEBUG, + "%s: updated ns to %hu\n", session->name, session->ns); + } +} + +void l2tp_build_l2tp_header(struct l2tp_session *session, void *buf) +{ + struct l2tp_tunnel *tunnel = session->tunnel; + + BUG_ON(tunnel->version != L2TP_HDR_VER_2); + l2tp_build_l2tpv2_header(tunnel, session, buf); +} +EXPORT_SYMBOL_GPL(l2tp_build_l2tp_header); + +int l2tp_xmit_core(struct l2tp_session *session, struct sk_buff *skb, size_t data_len) +{ + struct l2tp_tunnel *tunnel = session->tunnel; + unsigned int len = skb->len; + int error; + + /* Debug */ + if (session->send_seq) + PRINTK(session->debug, L2TP_MSG_DATA, KERN_DEBUG, + "%s: send %Zd bytes, ns=%hu\n", session->name, + data_len, session->ns - 1); + else + PRINTK(session->debug, L2TP_MSG_DATA, KERN_DEBUG, + "%s: send %Zd bytes\n", session->name, data_len); + + if (session->debug & L2TP_MSG_DATA) { + int i; + unsigned char *datap = skb->data + sizeof(struct udphdr); + + printk(KERN_DEBUG "%s: xmit:", session->name); + for (i = 0; i < (len - sizeof(struct udphdr)); i++) { + printk(" %02X", *datap++); + if (i == 31) { + printk(" ..."); + break; + } + } + printk("\n"); + } + + /* Queue the packet to IP for output */ + error = ip_queue_xmit(skb, 1); + + /* Update stats */ + if (error >= 0) { + tunnel->stats.tx_packets++; + tunnel->stats.tx_bytes += len; + session->stats.tx_packets++; + session->stats.tx_bytes += len; + } else { + tunnel->stats.tx_errors++; + session->stats.tx_errors++; + } + + return 0; +} +EXPORT_SYMBOL_GPL(l2tp_xmit_core); + +/* Automatically called when the skb is freed. + */ +static void l2tp_sock_wfree(struct sk_buff *skb) +{ + sock_put(skb->sk); +} + +/* For data skbs that we transmit, we associate with the tunnel socket + * but don't do accounting. + */ +static inline void l2tp_skb_set_owner_w(struct sk_buff *skb, struct sock *sk) +{ + sock_hold(sk); + skb->sk = sk; + skb->destructor = l2tp_sock_wfree; +} + +/* If caller requires the skb to have a ppp header, the header must be + * inserted in the skb data before calling this function. + */ +int l2tp_xmit_skb(struct l2tp_session *session, struct sk_buff *skb, int hdr_len) +{ + int data_len = skb->len; + struct sock *sk = session->tunnel->sock; + struct udphdr *uh; + unsigned int udp_len; + struct inet_sock *inet; + __wsum csum; + int old_headroom; + int new_headroom; + int headroom; + + /* Check that there's enough headroom in the skb to insert IP, + * UDP and L2TP headers. If not enough, expand it to + * make room. Adjust truesize. + */ + headroom = NET_SKB_PAD + sizeof(struct iphdr) + + sizeof(struct udphdr) + hdr_len; + old_headroom = skb_headroom(skb); + if (skb_cow_head(skb, headroom)) + goto abort; + + new_headroom = skb_headroom(skb); + skb_orphan(skb); + skb->truesize += new_headroom - old_headroom; + + /* Setup L2TP header */ + l2tp_build_l2tp_header(session, __skb_push(skb, hdr_len)); + udp_len = sizeof(struct udphdr) + hdr_len + data_len; + + /* Setup UDP header */ + inet = inet_sk(sk); + __skb_push(skb, sizeof(*uh)); + skb_reset_transport_header(skb); + uh = udp_hdr(skb); + uh->source = inet->inet_sport; + uh->dest = inet->inet_dport; + uh->len = htons(udp_len); + + uh->check = 0; + + memset(&(IPCB(skb)->opt), 0, sizeof(IPCB(skb)->opt)); + IPCB(skb)->flags &= ~(IPSKB_XFRM_TUNNEL_SIZE | IPSKB_XFRM_TRANSFORMED | + IPSKB_REROUTED); + nf_reset(skb); + + /* Get routing info from the tunnel socket */ + skb_dst_drop(skb); + skb_dst_set(skb, dst_clone(__sk_dst_get(sk))); + l2tp_skb_set_owner_w(skb, sk); + + /* Calculate UDP checksum if configured to do so */ + if (sk->sk_no_check == UDP_CSUM_NOXMIT) + skb->ip_summed = CHECKSUM_NONE; + else if ((skb_dst(skb) && skb_dst(skb)->dev) && + (!(skb_dst(skb)->dev->features & NETIF_F_V4_CSUM))) { + skb->ip_summed = CHECKSUM_COMPLETE; + csum = skb_checksum(skb, 0, udp_len, 0); + uh->check = csum_tcpudp_magic(inet->inet_saddr, + inet->inet_daddr, + udp_len, IPPROTO_UDP, csum); + if (uh->check == 0) + uh->check = CSUM_MANGLED_0; + } else { + skb->ip_summed = CHECKSUM_PARTIAL; + skb->csum_start = skb_transport_header(skb) - skb->head; + skb->csum_offset = offsetof(struct udphdr, check); + uh->check = ~csum_tcpudp_magic(inet->inet_saddr, + inet->inet_daddr, + udp_len, IPPROTO_UDP, 0); + } + + l2tp_xmit_core(session, skb, data_len); + +abort: + return 0; +} +EXPORT_SYMBOL_GPL(l2tp_xmit_skb); + +/***************************************************************************** + * Tinnel and session create/destroy. + *****************************************************************************/ + +/* Tunnel socket destruct hook. + * The tunnel context is deleted only when all session sockets have been + * closed. + */ +void l2tp_tunnel_destruct(struct sock *sk) +{ + struct l2tp_tunnel *tunnel; + + tunnel = sk->sk_user_data; + if (tunnel == NULL) + goto end; + + PRINTK(tunnel->debug, L2TP_MSG_CONTROL, KERN_INFO, + "%s: closing...\n", tunnel->name); + + /* Close all sessions */ + l2tp_tunnel_closeall(tunnel); + + /* No longer an encapsulation socket. See net/ipv4/udp.c */ + (udp_sk(sk))->encap_type = 0; + (udp_sk(sk))->encap_rcv = NULL; + + /* Remove hooks into tunnel socket */ + tunnel->sock = NULL; + sk->sk_destruct = tunnel->old_sk_destruct; + sk->sk_user_data = NULL; + + /* Call the original destructor */ + if (sk->sk_destruct) + (*sk->sk_destruct)(sk); + + /* We're finished with the socket */ + l2tp_tunnel_dec_refcount(tunnel); + +end: + return; +} +EXPORT_SYMBOL(l2tp_tunnel_destruct); + +/* When the tunnel is closed, all the attached sessions need to go too. + */ +void l2tp_tunnel_closeall(struct l2tp_tunnel *tunnel) +{ + int hash; + struct hlist_node *walk; + struct hlist_node *tmp; + struct l2tp_session *session; + + BUG_ON(tunnel == NULL); + + PRINTK(tunnel->debug, L2TP_MSG_CONTROL, KERN_INFO, + "%s: closing all sessions...\n", tunnel->name); + + write_lock_bh(&tunnel->hlist_lock); + for (hash = 0; hash < L2TP_HASH_SIZE; hash++) { +again: + hlist_for_each_safe(walk, tmp, &tunnel->session_hlist[hash]) { + session = hlist_entry(walk, struct l2tp_session, hlist); + + PRINTK(session->debug, L2TP_MSG_CONTROL, KERN_INFO, + "%s: closing session\n", session->name); + + hlist_del_init(&session->hlist); + + /* Since we should hold the sock lock while + * doing any unbinding, we need to release the + * lock we're holding before taking that lock. + * Hold a reference to the sock so it doesn't + * disappear as we're jumping between locks. + */ + if (session->ref != NULL) + (*session->ref)(session); + + write_unlock_bh(&tunnel->hlist_lock); + + if (session->session_close != NULL) + (*session->session_close)(session); + + if (session->deref != NULL) + (*session->deref)(session); + + write_lock_bh(&tunnel->hlist_lock); + + /* Now restart from the beginning of this hash + * chain. We always remove a session from the + * list so we are guaranteed to make forward + * progress. + */ + goto again; + } + } + write_unlock_bh(&tunnel->hlist_lock); +} +EXPORT_SYMBOL_GPL(l2tp_tunnel_closeall); + +/* Really kill the tunnel. + * Come here only when all sessions have been cleared from the tunnel. + */ +void l2tp_tunnel_free(struct l2tp_tunnel *tunnel) +{ + struct l2tp_net *pn = l2tp_pernet(tunnel->l2tp_net); + + BUG_ON(atomic_read(&tunnel->ref_count) != 0); + BUG_ON(tunnel->sock != NULL); + + PRINTK(tunnel->debug, L2TP_MSG_CONTROL, KERN_INFO, + "%s: free...\n", tunnel->name); + + /* Remove from tunnel list */ + write_lock_bh(&pn->l2tp_tunnel_list_lock); + list_del_init(&tunnel->list); + write_unlock_bh(&pn->l2tp_tunnel_list_lock); + + atomic_dec(&l2tp_tunnel_count); + kfree(tunnel); +} +EXPORT_SYMBOL_GPL(l2tp_tunnel_free); + +int l2tp_tunnel_create(struct net *net, int fd, int version, u32 tunnel_id, u32 peer_tunnel_id, struct l2tp_tunnel_cfg *cfg, struct l2tp_tunnel **tunnelp) +{ + struct l2tp_tunnel *tunnel = NULL; + int err; + struct socket *sock = NULL; + struct sock *sk = NULL; + struct l2tp_net *pn; + + /* Get the tunnel socket from the fd, which was opened by + * the userspace L2TP daemon. + */ + err = -EBADF; + sock = sockfd_lookup(fd, &err); + if (!sock) { + printk(KERN_ERR "tunl %hu: sockfd_lookup(fd=%d) returned %d\n", + tunnel_id, fd, err); + goto err; + } + + sk = sock->sk; + + /* Quick sanity checks */ + err = -EPROTONOSUPPORT; + if (sk->sk_protocol != IPPROTO_UDP) { + printk(KERN_ERR "tunl %hu: fd %d wrong protocol, got %d, expected %d\n", + tunnel_id, fd, sk->sk_protocol, IPPROTO_UDP); + goto err; + } + err = -EAFNOSUPPORT; + if (sock->ops->family != AF_INET) { + printk(KERN_ERR "tunl %hu: fd %d wrong family, got %d, expected %d\n", + tunnel_id, fd, sock->ops->family, AF_INET); + goto err; + } + + /* Check if this socket has already been prepped */ + tunnel = (struct l2tp_tunnel *)sk->sk_user_data; + if (tunnel != NULL) { + /* This socket has already been prepped */ + err = -EBUSY; + goto err; + } + + if (version != L2TP_HDR_VER_2) + goto err; + + tunnel = kzalloc(sizeof(struct l2tp_tunnel), GFP_KERNEL); + if (tunnel == NULL) { + err = -ENOMEM; + goto err; + } + + tunnel->version = version; + tunnel->tunnel_id = tunnel_id; + tunnel->peer_tunnel_id = peer_tunnel_id; + tunnel->debug = L2TP_DEFAULT_DEBUG_FLAGS; + + tunnel->magic = L2TP_TUNNEL_MAGIC; + sprintf(&tunnel->name[0], "tunl %u", tunnel_id); + rwlock_init(&tunnel->hlist_lock); + + /* The net we belong to */ + tunnel->l2tp_net = net; + pn = l2tp_pernet(net); + + if (cfg) + tunnel->debug = cfg->debug; + + /* Mark socket as an encapsulation socket. See net/ipv4/udp.c */ + udp_sk(sk)->encap_type = UDP_ENCAP_L2TPINUDP; + udp_sk(sk)->encap_rcv = l2tp_udp_encap_recv; + + sk->sk_user_data = tunnel; + + /* Hook on the tunnel socket destructor so that we can cleanup + * if the tunnel socket goes away. + */ + tunnel->old_sk_destruct = sk->sk_destruct; + sk->sk_destruct = &l2tp_tunnel_destruct; + tunnel->sock = sk; + sk->sk_allocation = GFP_ATOMIC; + + /* Add tunnel to our list */ + INIT_LIST_HEAD(&tunnel->list); + write_lock_bh(&pn->l2tp_tunnel_list_lock); + list_add(&tunnel->list, &pn->l2tp_tunnel_list); + write_unlock_bh(&pn->l2tp_tunnel_list_lock); + atomic_inc(&l2tp_tunnel_count); + + /* Bump the reference count. The tunnel context is deleted + * only when this drops to zero. + */ + l2tp_tunnel_inc_refcount(tunnel); + + err = 0; +err: + if (tunnelp) + *tunnelp = tunnel; + + if (sock) + sockfd_put(sock); + + return err; +} +EXPORT_SYMBOL_GPL(l2tp_tunnel_create); + +/* Really kill the session. + */ +void l2tp_session_free(struct l2tp_session *session) +{ + struct l2tp_tunnel *tunnel; + + BUG_ON(atomic_read(&session->ref_count) != 0); + + tunnel = session->tunnel; + if (tunnel != NULL) { + BUG_ON(tunnel->magic != L2TP_TUNNEL_MAGIC); + + /* Delete the session from the hash */ + write_lock_bh(&tunnel->hlist_lock); + hlist_del_init(&session->hlist); + write_unlock_bh(&tunnel->hlist_lock); + + if (session->session_id != 0) + atomic_dec(&l2tp_session_count); + + sock_put(tunnel->sock); + + /* This will delete the tunnel context if this + * is the last session on the tunnel. + */ + session->tunnel = NULL; + l2tp_tunnel_dec_refcount(tunnel); + } + + kfree(session); + + return; +} +EXPORT_SYMBOL_GPL(l2tp_session_free); + +struct l2tp_session *l2tp_session_create(int priv_size, struct l2tp_tunnel *tunnel, u32 session_id, u32 peer_session_id, struct l2tp_session_cfg *cfg) +{ + struct l2tp_session *session; + + session = kzalloc(sizeof(struct l2tp_session) + priv_size, GFP_KERNEL); + if (session != NULL) { + session->magic = L2TP_SESSION_MAGIC; + session->tunnel = tunnel; + + session->session_id = session_id; + session->peer_session_id = peer_session_id; + + sprintf(&session->name[0], "sess %u/%u", + tunnel->tunnel_id, session->session_id); + + skb_queue_head_init(&session->reorder_q); + + INIT_HLIST_NODE(&session->hlist); + + /* Inherit debug options from tunnel */ + session->debug = tunnel->debug; + + if (cfg) { + session->debug = cfg->debug; + session->hdr_len = cfg->hdr_len; + session->mtu = cfg->mtu; + session->mru = cfg->mru; + session->send_seq = cfg->send_seq; + session->recv_seq = cfg->recv_seq; + session->lns_mode = cfg->lns_mode; + } + + /* Bump the reference count. The session context is deleted + * only when this drops to zero. + */ + l2tp_session_inc_refcount(session); + l2tp_tunnel_inc_refcount(tunnel); + + /* Ensure tunnel socket isn't deleted */ + sock_hold(tunnel->sock); + + /* Add session to the tunnel's hash list */ + write_lock_bh(&tunnel->hlist_lock); + hlist_add_head(&session->hlist, + l2tp_session_id_hash(tunnel, session_id)); + write_unlock_bh(&tunnel->hlist_lock); + + /* Ignore management session in session count value */ + if (session->session_id != 0) + atomic_inc(&l2tp_session_count); + } + + return session; +} +EXPORT_SYMBOL_GPL(l2tp_session_create); + +/***************************************************************************** + * Init and cleanup + *****************************************************************************/ + +static __net_init int l2tp_init_net(struct net *net) +{ + struct l2tp_net *pn; + int err; + + pn = kzalloc(sizeof(*pn), GFP_KERNEL); + if (!pn) + return -ENOMEM; + + INIT_LIST_HEAD(&pn->l2tp_tunnel_list); + rwlock_init(&pn->l2tp_tunnel_list_lock); + + err = net_assign_generic(net, l2tp_net_id, pn); + if (err) + goto out; + + return 0; + +out: + kfree(pn); + return err; +} + +static __net_exit void l2tp_exit_net(struct net *net) +{ + struct l2tp_net *pn; + + pn = net_generic(net, l2tp_net_id); + /* + * if someone has cached our net then + * further net_generic call will return NULL + */ + net_assign_generic(net, l2tp_net_id, NULL); + kfree(pn); +} + +static struct pernet_operations l2tp_net_ops = { + .init = l2tp_init_net, + .exit = l2tp_exit_net, + .id = &l2tp_net_id, + .size = sizeof(struct l2tp_net), +}; + +static int __init l2tp_init(void) +{ + int rc = 0; + + rc = register_pernet_device(&l2tp_net_ops); + if (rc) + goto out; + + printk(KERN_INFO "L2TP core driver, %s\n", L2TP_DRV_VERSION); + +out: + return rc; +} + +static void __exit l2tp_exit(void) +{ + unregister_pernet_device(&l2tp_net_ops); +} + +module_init(l2tp_init); +module_exit(l2tp_exit); + +MODULE_AUTHOR("James Chapman "); +MODULE_DESCRIPTION("L2TP core"); +MODULE_LICENSE("GPL"); +MODULE_VERSION(L2TP_DRV_VERSION); + diff --git a/net/l2tp/l2tp_core.h b/net/l2tp/l2tp_core.h new file mode 100644 index 00000000000..2efe1a3ada9 --- /dev/null +++ b/net/l2tp/l2tp_core.h @@ -0,0 +1,254 @@ +/* + * L2TP internal definitions. + * + * Copyright (c) 2008,2009 Katalix Systems Ltd + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef _L2TP_CORE_H_ +#define _L2TP_CORE_H_ + +/* Just some random numbers */ +#define L2TP_TUNNEL_MAGIC 0x42114DDA +#define L2TP_SESSION_MAGIC 0x0C04EB7D + +#define L2TP_HASH_BITS 4 +#define L2TP_HASH_SIZE (1 << L2TP_HASH_BITS) + +/* Debug message categories for the DEBUG socket option */ +enum { + L2TP_MSG_DEBUG = (1 << 0), /* verbose debug (if + * compiled in) */ + L2TP_MSG_CONTROL = (1 << 1), /* userspace - kernel + * interface */ + L2TP_MSG_SEQ = (1 << 2), /* sequence numbers */ + L2TP_MSG_DATA = (1 << 3), /* data packets */ +}; + +struct sk_buff; + +struct l2tp_stats { + u64 tx_packets; + u64 tx_bytes; + u64 tx_errors; + u64 rx_packets; + u64 rx_bytes; + u64 rx_seq_discards; + u64 rx_oos_packets; + u64 rx_errors; +}; + +struct l2tp_tunnel; + +/* Describes a session. Contains information to determine incoming + * packets and transmit outgoing ones. + */ +struct l2tp_session_cfg { + unsigned data_seq:2; /* data sequencing level + * 0 => none, 1 => IP only, + * 2 => all + */ + unsigned recv_seq:1; /* expect receive packets with + * sequence numbers? */ + unsigned send_seq:1; /* send packets with sequence + * numbers? */ + unsigned lns_mode:1; /* behave as LNS? LAC enables + * sequence numbers under + * control of LNS. */ + int debug; /* bitmask of debug message + * categories */ + int offset; /* offset to payload */ + int reorder_timeout; /* configured reorder timeout + * (in jiffies) */ + int mtu; + int mru; + int hdr_len; +}; + +struct l2tp_session { + int magic; /* should be + * L2TP_SESSION_MAGIC */ + + struct l2tp_tunnel *tunnel; /* back pointer to tunnel + * context */ + u32 session_id; + u32 peer_session_id; + u16 nr; /* session NR state (receive) */ + u16 ns; /* session NR state (send) */ + struct sk_buff_head reorder_q; /* receive reorder queue */ + struct hlist_node hlist; /* Hash list node */ + atomic_t ref_count; + + char name[32]; /* for logging */ + unsigned data_seq:2; /* data sequencing level + * 0 => none, 1 => IP only, + * 2 => all + */ + unsigned recv_seq:1; /* expect receive packets with + * sequence numbers? */ + unsigned send_seq:1; /* send packets with sequence + * numbers? */ + unsigned lns_mode:1; /* behave as LNS? LAC enables + * sequence numbers under + * control of LNS. */ + int debug; /* bitmask of debug message + * categories */ + int reorder_timeout; /* configured reorder timeout + * (in jiffies) */ + int mtu; + int mru; + int hdr_len; + struct l2tp_stats stats; + + void (*recv_skb)(struct l2tp_session *session, struct sk_buff *skb, int data_len); + void (*session_close)(struct l2tp_session *session); + void (*ref)(struct l2tp_session *session); + void (*deref)(struct l2tp_session *session); + + uint8_t priv[0]; /* private data */ +}; + +/* Describes the tunnel. It contains info to track all the associated + * sessions so incoming packets can be sorted out + */ +struct l2tp_tunnel_cfg { + int debug; /* bitmask of debug message + * categories */ +}; + +struct l2tp_tunnel { + int magic; /* Should be L2TP_TUNNEL_MAGIC */ + rwlock_t hlist_lock; /* protect session_hlist */ + struct hlist_head session_hlist[L2TP_HASH_SIZE]; + /* hashed list of sessions, + * hashed by id */ + u32 tunnel_id; + u32 peer_tunnel_id; + int version; /* 2=>L2TPv2, 3=>L2TPv3 */ + + char name[20]; /* for logging */ + int debug; /* bitmask of debug message + * categories */ + int hdr_len; + struct l2tp_stats stats; + + struct list_head list; /* Keep a list of all tunnels */ + struct net *l2tp_net; /* the net we belong to */ + + atomic_t ref_count; + + int (*recv_payload_hook)(struct sk_buff *skb); + void (*old_sk_destruct)(struct sock *); + struct sock *sock; /* Parent socket */ + int fd; + + uint8_t priv[0]; /* private data */ +}; + +static inline void *l2tp_tunnel_priv(struct l2tp_tunnel *tunnel) +{ + return &tunnel->priv[0]; +} + +static inline void *l2tp_session_priv(struct l2tp_session *session) +{ + return &session->priv[0]; +} + +static inline struct l2tp_tunnel *l2tp_sock_to_tunnel(struct sock *sk) +{ + struct l2tp_tunnel *tunnel; + + if (sk == NULL) + return NULL; + + sock_hold(sk); + tunnel = (struct l2tp_tunnel *)(sk->sk_user_data); + if (tunnel == NULL) { + sock_put(sk); + goto out; + } + + BUG_ON(tunnel->magic != L2TP_TUNNEL_MAGIC); + +out: + return tunnel; +} + +extern struct l2tp_session *l2tp_session_find(struct l2tp_tunnel *tunnel, u32 session_id); +extern struct l2tp_session *l2tp_session_find_nth(struct l2tp_tunnel *tunnel, int nth); +extern struct l2tp_tunnel *l2tp_tunnel_find(struct net *net, u32 tunnel_id); +extern struct l2tp_tunnel *l2tp_tunnel_find_nth(struct net *net, int nth); + +extern int l2tp_tunnel_create(struct net *net, int fd, int version, u32 tunnel_id, u32 peer_tunnel_id, struct l2tp_tunnel_cfg *cfg, struct l2tp_tunnel **tunnelp); +extern struct l2tp_session *l2tp_session_create(int priv_size, struct l2tp_tunnel *tunnel, u32 session_id, u32 peer_session_id, struct l2tp_session_cfg *cfg); +extern void l2tp_tunnel_free(struct l2tp_tunnel *tunnel); +extern void l2tp_session_free(struct l2tp_session *session); +extern int l2tp_udp_recv_core(struct l2tp_tunnel *tunnel, struct sk_buff *skb, int (*payload_hook)(struct sk_buff *skb)); +extern int l2tp_udp_encap_recv(struct sock *sk, struct sk_buff *skb); + +extern void l2tp_build_l2tp_header(struct l2tp_session *session, void *buf); +extern int l2tp_xmit_core(struct l2tp_session *session, struct sk_buff *skb, size_t data_len); +extern int l2tp_xmit_skb(struct l2tp_session *session, struct sk_buff *skb, int hdr_len); +extern void l2tp_tunnel_destruct(struct sock *sk); +extern void l2tp_tunnel_closeall(struct l2tp_tunnel *tunnel); + +/* Tunnel reference counts. Incremented per session that is added to + * the tunnel. + */ +static inline void l2tp_tunnel_inc_refcount_1(struct l2tp_tunnel *tunnel) +{ + atomic_inc(&tunnel->ref_count); +} + +static inline void l2tp_tunnel_dec_refcount_1(struct l2tp_tunnel *tunnel) +{ + if (atomic_dec_and_test(&tunnel->ref_count)) + l2tp_tunnel_free(tunnel); +} +#ifdef L2TP_REFCNT_DEBUG +#define l2tp_tunnel_inc_refcount(_t) do { \ + printk(KERN_DEBUG "l2tp_tunnel_inc_refcount: %s:%d %s: cnt=%d\n", __func__, __LINE__, (_t)->name, atomic_read(&_t->ref_count)); \ + l2tp_tunnel_inc_refcount_1(_t); \ + } while (0) +#define l2tp_tunnel_dec_refcount(_t) do { \ + printk(KERN_DEBUG "l2tp_tunnel_dec_refcount: %s:%d %s: cnt=%d\n", __func__, __LINE__, (_t)->name, atomic_read(&_t->ref_count)); \ + l2tp_tunnel_dec_refcount_1(_t); \ + } while (0) +#else +#define l2tp_tunnel_inc_refcount(t) l2tp_tunnel_inc_refcount_1(t) +#define l2tp_tunnel_dec_refcount(t) l2tp_tunnel_dec_refcount_1(t) +#endif + +/* Session reference counts. Incremented when code obtains a reference + * to a session. + */ +static inline void l2tp_session_inc_refcount_1(struct l2tp_session *session) +{ + atomic_inc(&session->ref_count); +} + +static inline void l2tp_session_dec_refcount_1(struct l2tp_session *session) +{ + if (atomic_dec_and_test(&session->ref_count)) + l2tp_session_free(session); +} + +#ifdef L2TP_REFCNT_DEBUG +#define l2tp_session_inc_refcount(_s) do { \ + printk(KERN_DEBUG "l2tp_session_inc_refcount: %s:%d %s: cnt=%d\n", __func__, __LINE__, (_s)->name, atomic_read(&_s->ref_count)); \ + l2tp_session_inc_refcount_1(_s); \ + } while (0) +#define l2tp_session_dec_refcount(_s) do { \ + printk(KERN_DEBUG "l2tp_session_dec_refcount: %s:%d %s: cnt=%d\n", __func__, __LINE__, (_s)->name, atomic_read(&_s->ref_count)); \ + l2tp_session_dec_refcount_1(_s); \ + } while (0) +#else +#define l2tp_session_inc_refcount(s) l2tp_session_inc_refcount_1(s) +#define l2tp_session_dec_refcount(s) l2tp_session_dec_refcount_1(s) +#endif + +#endif /* _L2TP_CORE_H_ */ diff --git a/net/l2tp/l2tp_ppp.c b/net/l2tp/l2tp_ppp.c new file mode 100644 index 00000000000..baac072761a --- /dev/null +++ b/net/l2tp/l2tp_ppp.c @@ -0,0 +1,1658 @@ +/***************************************************************************** + * Linux PPP over L2TP (PPPoX/PPPoL2TP) Sockets + * + * PPPoX --- Generic PPP encapsulation socket family + * PPPoL2TP --- PPP over L2TP (RFC 2661) + * + * Version: 2.0.0 + * + * Authors: James Chapman (jchapman@katalix.com) + * + * Based on original work by Martijn van Oosterhout + * + * License: + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + * + */ + +/* This driver handles only L2TP data frames; control frames are handled by a + * userspace application. + * + * To send data in an L2TP session, userspace opens a PPPoL2TP socket and + * attaches it to a bound UDP socket with local tunnel_id / session_id and + * peer tunnel_id / session_id set. Data can then be sent or received using + * regular socket sendmsg() / recvmsg() calls. Kernel parameters of the socket + * can be read or modified using ioctl() or [gs]etsockopt() calls. + * + * When a PPPoL2TP socket is connected with local and peer session_id values + * zero, the socket is treated as a special tunnel management socket. + * + * Here's example userspace code to create a socket for sending/receiving data + * over an L2TP session:- + * + * struct sockaddr_pppol2tp sax; + * int fd; + * int session_fd; + * + * fd = socket(AF_PPPOX, SOCK_DGRAM, PX_PROTO_OL2TP); + * + * sax.sa_family = AF_PPPOX; + * sax.sa_protocol = PX_PROTO_OL2TP; + * sax.pppol2tp.fd = tunnel_fd; // bound UDP socket + * sax.pppol2tp.addr.sin_addr.s_addr = addr->sin_addr.s_addr; + * sax.pppol2tp.addr.sin_port = addr->sin_port; + * sax.pppol2tp.addr.sin_family = AF_INET; + * sax.pppol2tp.s_tunnel = tunnel_id; + * sax.pppol2tp.s_session = session_id; + * sax.pppol2tp.d_tunnel = peer_tunnel_id; + * sax.pppol2tp.d_session = peer_session_id; + * + * session_fd = connect(fd, (struct sockaddr *)&sax, sizeof(sax)); + * + * A pppd plugin that allows PPP traffic to be carried over L2TP using + * this driver is available from the OpenL2TP project at + * http://openl2tp.sourceforge.net. + */ + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "l2tp_core.h" + +#define PPPOL2TP_DRV_VERSION "V2.0" + +/* Space for UDP, L2TP and PPP headers */ +#define PPPOL2TP_HEADER_OVERHEAD 40 + +#define PRINTK(_mask, _type, _lvl, _fmt, args...) \ + do { \ + if ((_mask) & (_type)) \ + printk(_lvl "PPPOL2TP: " _fmt, ##args); \ + } while (0) + +/* Number of bytes to build transmit L2TP headers. + * Unfortunately the size is different depending on whether sequence numbers + * are enabled. + */ +#define PPPOL2TP_L2TP_HDR_SIZE_SEQ 10 +#define PPPOL2TP_L2TP_HDR_SIZE_NOSEQ 6 + +/* Private data of each session. This data lives at the end of struct + * l2tp_session, referenced via session->priv[]. + */ +struct pppol2tp_session { + int owner; /* pid that opened the socket */ + + struct sock *sock; /* Pointer to the session + * PPPoX socket */ + struct sock *tunnel_sock; /* Pointer to the tunnel UDP + * socket */ + int flags; /* accessed by PPPIOCGFLAGS. + * Unused. */ +}; + +static int pppol2tp_xmit(struct ppp_channel *chan, struct sk_buff *skb); + +static struct ppp_channel_ops pppol2tp_chan_ops = { pppol2tp_xmit , NULL }; +static const struct proto_ops pppol2tp_ops; + +/* Helpers to obtain tunnel/session contexts from sockets. + */ +static inline struct l2tp_session *pppol2tp_sock_to_session(struct sock *sk) +{ + struct l2tp_session *session; + + if (sk == NULL) + return NULL; + + sock_hold(sk); + session = (struct l2tp_session *)(sk->sk_user_data); + if (session == NULL) { + sock_put(sk); + goto out; + } + + BUG_ON(session->magic != L2TP_SESSION_MAGIC); + +out: + return session; +} + +/***************************************************************************** + * Receive data handling + *****************************************************************************/ + +static int pppol2tp_recv_payload_hook(struct sk_buff *skb) +{ + /* Skip PPP header, if present. In testing, Microsoft L2TP clients + * don't send the PPP header (PPP header compression enabled), but + * other clients can include the header. So we cope with both cases + * here. The PPP header is always FF03 when using L2TP. + * + * Note that skb->data[] isn't dereferenced from a u16 ptr here since + * the field may be unaligned. + */ + if (!pskb_may_pull(skb, 2)) + return 1; + + if ((skb->data[0] == 0xff) && (skb->data[1] == 0x03)) + skb_pull(skb, 2); + + return 0; +} + +/* Receive message. This is the recvmsg for the PPPoL2TP socket. + */ +static int pppol2tp_recvmsg(struct kiocb *iocb, struct socket *sock, + struct msghdr *msg, size_t len, + int flags) +{ + int err; + struct sk_buff *skb; + struct sock *sk = sock->sk; + + err = -EIO; + if (sk->sk_state & PPPOX_BOUND) + goto end; + + msg->msg_namelen = 0; + + err = 0; + skb = skb_recv_datagram(sk, flags & ~MSG_DONTWAIT, + flags & MSG_DONTWAIT, &err); + if (!skb) + goto end; + + if (len > skb->len) + len = skb->len; + else if (len < skb->len) + msg->msg_flags |= MSG_TRUNC; + + err = skb_copy_datagram_iovec(skb, 0, msg->msg_iov, len); + if (likely(err == 0)) + err = len; + + kfree_skb(skb); +end: + return err; +} + +static void pppol2tp_recv(struct l2tp_session *session, struct sk_buff *skb, int data_len) +{ + struct pppol2tp_session *ps = l2tp_session_priv(session); + struct sock *sk = NULL; + + /* If the socket is bound, send it in to PPP's input queue. Otherwise + * queue it on the session socket. + */ + sk = ps->sock; + if (sk == NULL) + goto no_sock; + + if (sk->sk_state & PPPOX_BOUND) { + struct pppox_sock *po; + PRINTK(session->debug, PPPOL2TP_MSG_DATA, KERN_DEBUG, + "%s: recv %d byte data frame, passing to ppp\n", + session->name, data_len); + + /* We need to forget all info related to the L2TP packet + * gathered in the skb as we are going to reuse the same + * skb for the inner packet. + * Namely we need to: + * - reset xfrm (IPSec) information as it applies to + * the outer L2TP packet and not to the inner one + * - release the dst to force a route lookup on the inner + * IP packet since skb->dst currently points to the dst + * of the UDP tunnel + * - reset netfilter information as it doesn't apply + * to the inner packet either + */ + secpath_reset(skb); + skb_dst_drop(skb); + nf_reset(skb); + + po = pppox_sk(sk); + ppp_input(&po->chan, skb); + } else { + PRINTK(session->debug, PPPOL2TP_MSG_DATA, KERN_INFO, + "%s: socket not bound\n", session->name); + + /* Not bound. Nothing we can do, so discard. */ + session->stats.rx_errors++; + kfree_skb(skb); + } + + return; + +no_sock: + PRINTK(session->debug, PPPOL2TP_MSG_DATA, KERN_INFO, + "%s: no socket\n", session->name); + kfree_skb(skb); +} + +static void pppol2tp_session_sock_hold(struct l2tp_session *session) +{ + struct pppol2tp_session *ps = l2tp_session_priv(session); + + if (ps->sock) + sock_hold(ps->sock); +} + +static void pppol2tp_session_sock_put(struct l2tp_session *session) +{ + struct pppol2tp_session *ps = l2tp_session_priv(session); + + if (ps->sock) + sock_put(ps->sock); +} + +/************************************************************************ + * Transmit handling + ***********************************************************************/ + +/* Tell how big L2TP headers are for a particular session. This + * depends on whether sequence numbers are being used. + */ +static inline int pppol2tp_l2tp_header_len(struct l2tp_session *session) +{ + if (session->send_seq) + return PPPOL2TP_L2TP_HDR_SIZE_SEQ; + + return PPPOL2TP_L2TP_HDR_SIZE_NOSEQ; +} + +/* This is the sendmsg for the PPPoL2TP pppol2tp_session socket. We come here + * when a user application does a sendmsg() on the session socket. L2TP and + * PPP headers must be inserted into the user's data. + */ +static int pppol2tp_sendmsg(struct kiocb *iocb, struct socket *sock, struct msghdr *m, + size_t total_len) +{ + static const unsigned char ppph[2] = { 0xff, 0x03 }; + struct sock *sk = sock->sk; + struct sk_buff *skb; + int error; + struct l2tp_session *session; + struct l2tp_tunnel *tunnel; + struct pppol2tp_session *ps; + + error = -ENOTCONN; + if (sock_flag(sk, SOCK_DEAD) || !(sk->sk_state & PPPOX_CONNECTED)) + goto error; + + /* Get session and tunnel contexts */ + error = -EBADF; + session = pppol2tp_sock_to_session(sk); + if (session == NULL) + goto error; + + ps = l2tp_session_priv(session); + tunnel = l2tp_sock_to_tunnel(ps->tunnel_sock); + if (tunnel == NULL) + goto error_put_sess; + + /* Allocate a socket buffer */ + error = -ENOMEM; + skb = sock_wmalloc(sk, NET_SKB_PAD + sizeof(struct iphdr) + + sizeof(struct udphdr) + session->hdr_len + + sizeof(ppph) + total_len, + 0, GFP_KERNEL); + if (!skb) + goto error_put_sess_tun; + + /* Reserve space for headers. */ + skb_reserve(skb, NET_SKB_PAD); + skb_reset_network_header(skb); + skb_reserve(skb, sizeof(struct iphdr)); + skb_reset_transport_header(skb); + skb_reserve(skb, sizeof(struct udphdr)); + + /* Add PPP header */ + skb->data[0] = ppph[0]; + skb->data[1] = ppph[1]; + skb_put(skb, 2); + + /* Copy user data into skb */ + error = memcpy_fromiovec(skb->data, m->msg_iov, total_len); + if (error < 0) { + kfree_skb(skb); + goto error_put_sess_tun; + } + skb_put(skb, total_len); + + l2tp_xmit_skb(session, skb, session->hdr_len); + + sock_put(ps->tunnel_sock); + + return error; + +error_put_sess_tun: + sock_put(ps->tunnel_sock); +error_put_sess: + sock_put(sk); +error: + return error; +} + +/* Transmit function called by generic PPP driver. Sends PPP frame + * over PPPoL2TP socket. + * + * This is almost the same as pppol2tp_sendmsg(), but rather than + * being called with a msghdr from userspace, it is called with a skb + * from the kernel. + * + * The supplied skb from ppp doesn't have enough headroom for the + * insertion of L2TP, UDP and IP headers so we need to allocate more + * headroom in the skb. This will create a cloned skb. But we must be + * careful in the error case because the caller will expect to free + * the skb it supplied, not our cloned skb. So we take care to always + * leave the original skb unfreed if we return an error. + */ +static int pppol2tp_xmit(struct ppp_channel *chan, struct sk_buff *skb) +{ + static const u8 ppph[2] = { 0xff, 0x03 }; + struct sock *sk = (struct sock *) chan->private; + struct sock *sk_tun; + int hdr_len; + struct l2tp_session *session; + struct l2tp_tunnel *tunnel; + struct pppol2tp_session *ps; + int old_headroom; + int new_headroom; + + if (sock_flag(sk, SOCK_DEAD) || !(sk->sk_state & PPPOX_CONNECTED)) + goto abort; + + /* Get session and tunnel contexts from the socket */ + session = pppol2tp_sock_to_session(sk); + if (session == NULL) + goto abort; + + ps = l2tp_session_priv(session); + sk_tun = ps->tunnel_sock; + if (sk_tun == NULL) + goto abort_put_sess; + tunnel = l2tp_sock_to_tunnel(sk_tun); + if (tunnel == NULL) + goto abort_put_sess; + + /* What header length is configured for this session? */ + hdr_len = pppol2tp_l2tp_header_len(session); + + old_headroom = skb_headroom(skb); + if (skb_cow_head(skb, sizeof(ppph))) + goto abort_put_sess_tun; + + new_headroom = skb_headroom(skb); + skb->truesize += new_headroom - old_headroom; + + /* Setup PPP header */ + __skb_push(skb, sizeof(ppph)); + skb->data[0] = ppph[0]; + skb->data[1] = ppph[1]; + + l2tp_xmit_skb(session, skb, hdr_len); + + sock_put(sk_tun); + sock_put(sk); + return 1; + +abort_put_sess_tun: + sock_put(sk_tun); +abort_put_sess: + sock_put(sk); +abort: + /* Free the original skb */ + kfree_skb(skb); + return 1; +} + +/***************************************************************************** + * Session (and tunnel control) socket create/destroy. + *****************************************************************************/ + +/* Called by l2tp_core when a session socket is being closed. + */ +static void pppol2tp_session_close(struct l2tp_session *session) +{ + struct pppol2tp_session *ps = l2tp_session_priv(session); + struct sock *sk = ps->sock; + struct sk_buff *skb; + + BUG_ON(session->magic != L2TP_SESSION_MAGIC); + + if (session->session_id == 0) + goto out; + + if (sk != NULL) { + lock_sock(sk); + + if (sk->sk_state & (PPPOX_CONNECTED | PPPOX_BOUND)) { + pppox_unbind_sock(sk); + sk->sk_state = PPPOX_DEAD; + sk->sk_state_change(sk); + } + + /* Purge any queued data */ + skb_queue_purge(&sk->sk_receive_queue); + skb_queue_purge(&sk->sk_write_queue); + while ((skb = skb_dequeue(&session->reorder_q))) { + kfree_skb(skb); + sock_put(sk); + } + + release_sock(sk); + } + +out: + return; +} + +/* Really kill the session socket. (Called from sock_put() if + * refcnt == 0.) + */ +static void pppol2tp_session_destruct(struct sock *sk) +{ + struct l2tp_session *session; + + if (sk->sk_user_data != NULL) { + session = sk->sk_user_data; + if (session == NULL) + goto out; + + sk->sk_user_data = NULL; + BUG_ON(session->magic != L2TP_SESSION_MAGIC); + l2tp_session_dec_refcount(session); + } + +out: + return; +} + +/* Called when the PPPoX socket (session) is closed. + */ +static int pppol2tp_release(struct socket *sock) +{ + struct sock *sk = sock->sk; + struct l2tp_session *session; + int error; + + if (!sk) + return 0; + + error = -EBADF; + lock_sock(sk); + if (sock_flag(sk, SOCK_DEAD) != 0) + goto error; + + pppox_unbind_sock(sk); + + /* Signal the death of the socket. */ + sk->sk_state = PPPOX_DEAD; + sock_orphan(sk); + sock->sk = NULL; + + session = pppol2tp_sock_to_session(sk); + + /* Purge any queued data */ + skb_queue_purge(&sk->sk_receive_queue); + skb_queue_purge(&sk->sk_write_queue); + if (session != NULL) { + struct sk_buff *skb; + while ((skb = skb_dequeue(&session->reorder_q))) { + kfree_skb(skb); + sock_put(sk); + } + sock_put(sk); + } + + release_sock(sk); + + /* This will delete the session context via + * pppol2tp_session_destruct() if the socket's refcnt drops to + * zero. + */ + sock_put(sk); + + return 0; + +error: + release_sock(sk); + return error; +} + +static struct proto pppol2tp_sk_proto = { + .name = "PPPOL2TP", + .owner = THIS_MODULE, + .obj_size = sizeof(struct pppox_sock), +}; + +static int pppol2tp_backlog_recv(struct sock *sk, struct sk_buff *skb) +{ + int rc; + + rc = l2tp_udp_encap_recv(sk, skb); + if (rc) + kfree_skb(skb); + + return NET_RX_SUCCESS; +} + +/* socket() handler. Initialize a new struct sock. + */ +static int pppol2tp_create(struct net *net, struct socket *sock) +{ + int error = -ENOMEM; + struct sock *sk; + + sk = sk_alloc(net, PF_PPPOX, GFP_KERNEL, &pppol2tp_sk_proto); + if (!sk) + goto out; + + sock_init_data(sock, sk); + + sock->state = SS_UNCONNECTED; + sock->ops = &pppol2tp_ops; + + sk->sk_backlog_rcv = pppol2tp_backlog_recv; + sk->sk_protocol = PX_PROTO_OL2TP; + sk->sk_family = PF_PPPOX; + sk->sk_state = PPPOX_NONE; + sk->sk_type = SOCK_STREAM; + sk->sk_destruct = pppol2tp_session_destruct; + + error = 0; + +out: + return error; +} + +/* connect() handler. Attach a PPPoX socket to a tunnel UDP socket + */ +static int pppol2tp_connect(struct socket *sock, struct sockaddr *uservaddr, + int sockaddr_len, int flags) +{ + struct sock *sk = sock->sk; + struct sockaddr_pppol2tp *sp = (struct sockaddr_pppol2tp *) uservaddr; + struct pppox_sock *po = pppox_sk(sk); + struct l2tp_session *session = NULL; + struct l2tp_tunnel *tunnel; + struct pppol2tp_session *ps; + struct dst_entry *dst; + struct l2tp_session_cfg cfg = { 0, }; + int error = 0; + + lock_sock(sk); + + error = -EINVAL; + if (sp->sa_protocol != PX_PROTO_OL2TP) + goto end; + + /* Check for already bound sockets */ + error = -EBUSY; + if (sk->sk_state & PPPOX_CONNECTED) + goto end; + + /* We don't supporting rebinding anyway */ + error = -EALREADY; + if (sk->sk_user_data) + goto end; /* socket is already attached */ + + /* Don't bind if s_tunnel is 0 */ + error = -EINVAL; + if (sp->pppol2tp.s_tunnel == 0) + goto end; + + /* Special case: create tunnel context if s_session and + * d_session is 0. Otherwise look up tunnel using supplied + * tunnel id. + */ + if ((sp->pppol2tp.s_session == 0) && (sp->pppol2tp.d_session == 0)) { + error = l2tp_tunnel_create(sock_net(sk), sp->pppol2tp.fd, 2, sp->pppol2tp.s_tunnel, sp->pppol2tp.d_tunnel, NULL, &tunnel); + if (error < 0) + goto end; + } else { + tunnel = l2tp_tunnel_find(sock_net(sk), sp->pppol2tp.s_tunnel); + + /* Error if we can't find the tunnel */ + error = -ENOENT; + if (tunnel == NULL) + goto end; + + /* Error if socket is not prepped */ + if (tunnel->sock == NULL) + goto end; + } + + if (tunnel->recv_payload_hook == NULL) + tunnel->recv_payload_hook = pppol2tp_recv_payload_hook; + + /* Check that this session doesn't already exist */ + error = -EEXIST; + session = l2tp_session_find(tunnel, sp->pppol2tp.s_session); + if (session != NULL) + goto end; + + /* Default MTU must allow space for UDP/L2TP/PPP + * headers. + */ + cfg.mtu = cfg.mru = 1500 - PPPOL2TP_HEADER_OVERHEAD; + cfg.hdr_len = PPPOL2TP_L2TP_HDR_SIZE_NOSEQ; + cfg.debug = tunnel->debug; + + /* Allocate and initialize a new session context. */ + session = l2tp_session_create(sizeof(struct pppol2tp_session), + tunnel, sp->pppol2tp.s_session, + sp->pppol2tp.d_session, &cfg); + if (session == NULL) { + error = -ENOMEM; + goto end; + } + + ps = l2tp_session_priv(session); + ps->owner = current->pid; + ps->sock = sk; + ps->tunnel_sock = tunnel->sock; + + session->recv_skb = pppol2tp_recv; + session->session_close = pppol2tp_session_close; + + /* We need to know each time a skb is dropped from the reorder + * queue. + */ + session->ref = pppol2tp_session_sock_hold; + session->deref = pppol2tp_session_sock_put; + + /* If PMTU discovery was enabled, use the MTU that was discovered */ + dst = sk_dst_get(sk); + if (dst != NULL) { + u32 pmtu = dst_mtu(__sk_dst_get(sk)); + if (pmtu != 0) + session->mtu = session->mru = pmtu - + PPPOL2TP_HEADER_OVERHEAD; + dst_release(dst); + } + + /* Special case: if source & dest session_id == 0x0000, this + * socket is being created to manage the tunnel. Just set up + * the internal context for use by ioctl() and sockopt() + * handlers. + */ + if ((session->session_id == 0) && + (session->peer_session_id == 0)) { + error = 0; + goto out_no_ppp; + } + + /* The only header we need to worry about is the L2TP + * header. This size is different depending on whether + * sequence numbers are enabled for the data channel. + */ + po->chan.hdrlen = PPPOL2TP_L2TP_HDR_SIZE_NOSEQ; + + po->chan.private = sk; + po->chan.ops = &pppol2tp_chan_ops; + po->chan.mtu = session->mtu; + + error = ppp_register_net_channel(sock_net(sk), &po->chan); + if (error) + goto end; + +out_no_ppp: + /* This is how we get the session context from the socket. */ + sk->sk_user_data = session; + sk->sk_state = PPPOX_CONNECTED; + PRINTK(session->debug, PPPOL2TP_MSG_CONTROL, KERN_INFO, + "%s: created\n", session->name); + +end: + release_sock(sk); + + return error; +} + +/* getname() support. + */ +static int pppol2tp_getname(struct socket *sock, struct sockaddr *uaddr, + int *usockaddr_len, int peer) +{ + int len = sizeof(struct sockaddr_pppol2tp); + struct sockaddr_pppol2tp sp; + int error = 0; + struct l2tp_session *session; + struct l2tp_tunnel *tunnel; + struct sock *sk = sock->sk; + struct inet_sock *inet; + struct pppol2tp_session *pls; + + error = -ENOTCONN; + if (sk == NULL) + goto end; + if (sk->sk_state != PPPOX_CONNECTED) + goto end; + + error = -EBADF; + session = pppol2tp_sock_to_session(sk); + if (session == NULL) + goto end; + + pls = l2tp_session_priv(session); + tunnel = l2tp_sock_to_tunnel(pls->tunnel_sock); + if (tunnel == NULL) { + error = -EBADF; + goto end_put_sess; + } + + memset(&sp, 0, len); + sp.sa_family = AF_PPPOX; + sp.sa_protocol = PX_PROTO_OL2TP; + sp.pppol2tp.fd = tunnel->fd; + sp.pppol2tp.pid = pls->owner; + sp.pppol2tp.s_tunnel = tunnel->tunnel_id; + sp.pppol2tp.d_tunnel = tunnel->peer_tunnel_id; + sp.pppol2tp.s_session = session->session_id; + sp.pppol2tp.d_session = session->peer_session_id; + inet = inet_sk(sk); + sp.pppol2tp.addr.sin_family = AF_INET; + sp.pppol2tp.addr.sin_port = inet->inet_dport; + sp.pppol2tp.addr.sin_addr.s_addr = inet->inet_daddr; + + memcpy(uaddr, &sp, len); + + *usockaddr_len = len; + + sock_put(pls->tunnel_sock); +end_put_sess: + sock_put(sk); + error = 0; + +end: + return error; +} + +/**************************************************************************** + * ioctl() handlers. + * + * The PPPoX socket is created for L2TP sessions: tunnels have their own UDP + * sockets. However, in order to control kernel tunnel features, we allow + * userspace to create a special "tunnel" PPPoX socket which is used for + * control only. Tunnel PPPoX sockets have session_id == 0 and simply allow + * the user application to issue L2TP setsockopt(), getsockopt() and ioctl() + * calls. + ****************************************************************************/ + +static void pppol2tp_copy_stats(struct pppol2tp_ioc_stats *dest, + struct l2tp_stats *stats) +{ + dest->tx_packets = stats->tx_packets; + dest->tx_bytes = stats->tx_bytes; + dest->tx_errors = stats->tx_errors; + dest->rx_packets = stats->rx_packets; + dest->rx_bytes = stats->rx_bytes; + dest->rx_seq_discards = stats->rx_seq_discards; + dest->rx_oos_packets = stats->rx_oos_packets; + dest->rx_errors = stats->rx_errors; +} + +/* Session ioctl helper. + */ +static int pppol2tp_session_ioctl(struct l2tp_session *session, + unsigned int cmd, unsigned long arg) +{ + struct ifreq ifr; + int err = 0; + struct sock *sk; + int val = (int) arg; + struct pppol2tp_session *ps = l2tp_session_priv(session); + struct l2tp_tunnel *tunnel = session->tunnel; + struct pppol2tp_ioc_stats stats; + + PRINTK(session->debug, PPPOL2TP_MSG_CONTROL, KERN_DEBUG, + "%s: pppol2tp_session_ioctl(cmd=%#x, arg=%#lx)\n", + session->name, cmd, arg); + + sk = ps->sock; + sock_hold(sk); + + switch (cmd) { + case SIOCGIFMTU: + err = -ENXIO; + if (!(sk->sk_state & PPPOX_CONNECTED)) + break; + + err = -EFAULT; + if (copy_from_user(&ifr, (void __user *) arg, sizeof(struct ifreq))) + break; + ifr.ifr_mtu = session->mtu; + if (copy_to_user((void __user *) arg, &ifr, sizeof(struct ifreq))) + break; + + PRINTK(session->debug, PPPOL2TP_MSG_CONTROL, KERN_INFO, + "%s: get mtu=%d\n", session->name, session->mtu); + err = 0; + break; + + case SIOCSIFMTU: + err = -ENXIO; + if (!(sk->sk_state & PPPOX_CONNECTED)) + break; + + err = -EFAULT; + if (copy_from_user(&ifr, (void __user *) arg, sizeof(struct ifreq))) + break; + + session->mtu = ifr.ifr_mtu; + + PRINTK(session->debug, PPPOL2TP_MSG_CONTROL, KERN_INFO, + "%s: set mtu=%d\n", session->name, session->mtu); + err = 0; + break; + + case PPPIOCGMRU: + err = -ENXIO; + if (!(sk->sk_state & PPPOX_CONNECTED)) + break; + + err = -EFAULT; + if (put_user(session->mru, (int __user *) arg)) + break; + + PRINTK(session->debug, PPPOL2TP_MSG_CONTROL, KERN_INFO, + "%s: get mru=%d\n", session->name, session->mru); + err = 0; + break; + + case PPPIOCSMRU: + err = -ENXIO; + if (!(sk->sk_state & PPPOX_CONNECTED)) + break; + + err = -EFAULT; + if (get_user(val, (int __user *) arg)) + break; + + session->mru = val; + PRINTK(session->debug, PPPOL2TP_MSG_CONTROL, KERN_INFO, + "%s: set mru=%d\n", session->name, session->mru); + err = 0; + break; + + case PPPIOCGFLAGS: + err = -EFAULT; + if (put_user(ps->flags, (int __user *) arg)) + break; + + PRINTK(session->debug, PPPOL2TP_MSG_CONTROL, KERN_INFO, + "%s: get flags=%d\n", session->name, ps->flags); + err = 0; + break; + + case PPPIOCSFLAGS: + err = -EFAULT; + if (get_user(val, (int __user *) arg)) + break; + ps->flags = val; + PRINTK(session->debug, PPPOL2TP_MSG_CONTROL, KERN_INFO, + "%s: set flags=%d\n", session->name, ps->flags); + err = 0; + break; + + case PPPIOCGL2TPSTATS: + err = -ENXIO; + if (!(sk->sk_state & PPPOX_CONNECTED)) + break; + + memset(&stats, 0, sizeof(stats)); + stats.tunnel_id = tunnel->tunnel_id; + stats.session_id = session->session_id; + pppol2tp_copy_stats(&stats, &session->stats); + if (copy_to_user((void __user *) arg, &stats, + sizeof(stats))) + break; + PRINTK(session->debug, PPPOL2TP_MSG_CONTROL, KERN_INFO, + "%s: get L2TP stats\n", session->name); + err = 0; + break; + + default: + err = -ENOSYS; + break; + } + + sock_put(sk); + + return err; +} + +/* Tunnel ioctl helper. + * + * Note the special handling for PPPIOCGL2TPSTATS below. If the ioctl data + * specifies a session_id, the session ioctl handler is called. This allows an + * application to retrieve session stats via a tunnel socket. + */ +static int pppol2tp_tunnel_ioctl(struct l2tp_tunnel *tunnel, + unsigned int cmd, unsigned long arg) +{ + int err = 0; + struct sock *sk; + struct pppol2tp_ioc_stats stats; + + PRINTK(tunnel->debug, PPPOL2TP_MSG_CONTROL, KERN_DEBUG, + "%s: pppol2tp_tunnel_ioctl(cmd=%#x, arg=%#lx)\n", + tunnel->name, cmd, arg); + + sk = tunnel->sock; + sock_hold(sk); + + switch (cmd) { + case PPPIOCGL2TPSTATS: + err = -ENXIO; + if (!(sk->sk_state & PPPOX_CONNECTED)) + break; + + if (copy_from_user(&stats, (void __user *) arg, + sizeof(stats))) { + err = -EFAULT; + break; + } + if (stats.session_id != 0) { + /* resend to session ioctl handler */ + struct l2tp_session *session = + l2tp_session_find(tunnel, stats.session_id); + if (session != NULL) + err = pppol2tp_session_ioctl(session, cmd, arg); + else + err = -EBADR; + break; + } +#ifdef CONFIG_XFRM + stats.using_ipsec = (sk->sk_policy[0] || sk->sk_policy[1]) ? 1 : 0; +#endif + pppol2tp_copy_stats(&stats, &tunnel->stats); + if (copy_to_user((void __user *) arg, &stats, sizeof(stats))) { + err = -EFAULT; + break; + } + PRINTK(tunnel->debug, PPPOL2TP_MSG_CONTROL, KERN_INFO, + "%s: get L2TP stats\n", tunnel->name); + err = 0; + break; + + default: + err = -ENOSYS; + break; + } + + sock_put(sk); + + return err; +} + +/* Main ioctl() handler. + * Dispatch to tunnel or session helpers depending on the socket. + */ +static int pppol2tp_ioctl(struct socket *sock, unsigned int cmd, + unsigned long arg) +{ + struct sock *sk = sock->sk; + struct l2tp_session *session; + struct l2tp_tunnel *tunnel; + struct pppol2tp_session *ps; + int err; + + if (!sk) + return 0; + + err = -EBADF; + if (sock_flag(sk, SOCK_DEAD) != 0) + goto end; + + err = -ENOTCONN; + if ((sk->sk_user_data == NULL) || + (!(sk->sk_state & (PPPOX_CONNECTED | PPPOX_BOUND)))) + goto end; + + /* Get session context from the socket */ + err = -EBADF; + session = pppol2tp_sock_to_session(sk); + if (session == NULL) + goto end; + + /* Special case: if session's session_id is zero, treat ioctl as a + * tunnel ioctl + */ + ps = l2tp_session_priv(session); + if ((session->session_id == 0) && + (session->peer_session_id == 0)) { + err = -EBADF; + tunnel = l2tp_sock_to_tunnel(ps->tunnel_sock); + if (tunnel == NULL) + goto end_put_sess; + + err = pppol2tp_tunnel_ioctl(tunnel, cmd, arg); + sock_put(ps->tunnel_sock); + goto end_put_sess; + } + + err = pppol2tp_session_ioctl(session, cmd, arg); + +end_put_sess: + sock_put(sk); +end: + return err; +} + +/***************************************************************************** + * setsockopt() / getsockopt() support. + * + * The PPPoX socket is created for L2TP sessions: tunnels have their own UDP + * sockets. In order to control kernel tunnel features, we allow userspace to + * create a special "tunnel" PPPoX socket which is used for control only. + * Tunnel PPPoX sockets have session_id == 0 and simply allow the user + * application to issue L2TP setsockopt(), getsockopt() and ioctl() calls. + *****************************************************************************/ + +/* Tunnel setsockopt() helper. + */ +static int pppol2tp_tunnel_setsockopt(struct sock *sk, + struct l2tp_tunnel *tunnel, + int optname, int val) +{ + int err = 0; + + switch (optname) { + case PPPOL2TP_SO_DEBUG: + tunnel->debug = val; + PRINTK(tunnel->debug, PPPOL2TP_MSG_CONTROL, KERN_INFO, + "%s: set debug=%x\n", tunnel->name, tunnel->debug); + break; + + default: + err = -ENOPROTOOPT; + break; + } + + return err; +} + +/* Session setsockopt helper. + */ +static int pppol2tp_session_setsockopt(struct sock *sk, + struct l2tp_session *session, + int optname, int val) +{ + int err = 0; + struct pppol2tp_session *ps = l2tp_session_priv(session); + + switch (optname) { + case PPPOL2TP_SO_RECVSEQ: + if ((val != 0) && (val != 1)) { + err = -EINVAL; + break; + } + session->recv_seq = val ? -1 : 0; + PRINTK(session->debug, PPPOL2TP_MSG_CONTROL, KERN_INFO, + "%s: set recv_seq=%d\n", session->name, session->recv_seq); + break; + + case PPPOL2TP_SO_SENDSEQ: + if ((val != 0) && (val != 1)) { + err = -EINVAL; + break; + } + session->send_seq = val ? -1 : 0; + { + struct sock *ssk = ps->sock; + struct pppox_sock *po = pppox_sk(ssk); + po->chan.hdrlen = val ? PPPOL2TP_L2TP_HDR_SIZE_SEQ : + PPPOL2TP_L2TP_HDR_SIZE_NOSEQ; + } + PRINTK(session->debug, PPPOL2TP_MSG_CONTROL, KERN_INFO, + "%s: set send_seq=%d\n", session->name, session->send_seq); + break; + + case PPPOL2TP_SO_LNSMODE: + if ((val != 0) && (val != 1)) { + err = -EINVAL; + break; + } + session->lns_mode = val ? -1 : 0; + PRINTK(session->debug, PPPOL2TP_MSG_CONTROL, KERN_INFO, + "%s: set lns_mode=%d\n", session->name, session->lns_mode); + break; + + case PPPOL2TP_SO_DEBUG: + session->debug = val; + PRINTK(session->debug, PPPOL2TP_MSG_CONTROL, KERN_INFO, + "%s: set debug=%x\n", session->name, session->debug); + break; + + case PPPOL2TP_SO_REORDERTO: + session->reorder_timeout = msecs_to_jiffies(val); + PRINTK(session->debug, PPPOL2TP_MSG_CONTROL, KERN_INFO, + "%s: set reorder_timeout=%d\n", session->name, session->reorder_timeout); + break; + + default: + err = -ENOPROTOOPT; + break; + } + + return err; +} + +/* Main setsockopt() entry point. + * Does API checks, then calls either the tunnel or session setsockopt + * handler, according to whether the PPPoL2TP socket is a for a regular + * session or the special tunnel type. + */ +static int pppol2tp_setsockopt(struct socket *sock, int level, int optname, + char __user *optval, unsigned int optlen) +{ + struct sock *sk = sock->sk; + struct l2tp_session *session; + struct l2tp_tunnel *tunnel; + struct pppol2tp_session *ps; + int val; + int err; + + if (level != SOL_PPPOL2TP) + return udp_prot.setsockopt(sk, level, optname, optval, optlen); + + if (optlen < sizeof(int)) + return -EINVAL; + + if (get_user(val, (int __user *)optval)) + return -EFAULT; + + err = -ENOTCONN; + if (sk->sk_user_data == NULL) + goto end; + + /* Get session context from the socket */ + err = -EBADF; + session = pppol2tp_sock_to_session(sk); + if (session == NULL) + goto end; + + /* Special case: if session_id == 0x0000, treat as operation on tunnel + */ + ps = l2tp_session_priv(session); + if ((session->session_id == 0) && + (session->peer_session_id == 0)) { + err = -EBADF; + tunnel = l2tp_sock_to_tunnel(ps->tunnel_sock); + if (tunnel == NULL) + goto end_put_sess; + + err = pppol2tp_tunnel_setsockopt(sk, tunnel, optname, val); + sock_put(ps->tunnel_sock); + } else + err = pppol2tp_session_setsockopt(sk, session, optname, val); + + err = 0; + +end_put_sess: + sock_put(sk); +end: + return err; +} + +/* Tunnel getsockopt helper. Called with sock locked. + */ +static int pppol2tp_tunnel_getsockopt(struct sock *sk, + struct l2tp_tunnel *tunnel, + int optname, int *val) +{ + int err = 0; + + switch (optname) { + case PPPOL2TP_SO_DEBUG: + *val = tunnel->debug; + PRINTK(tunnel->debug, PPPOL2TP_MSG_CONTROL, KERN_INFO, + "%s: get debug=%x\n", tunnel->name, tunnel->debug); + break; + + default: + err = -ENOPROTOOPT; + break; + } + + return err; +} + +/* Session getsockopt helper. Called with sock locked. + */ +static int pppol2tp_session_getsockopt(struct sock *sk, + struct l2tp_session *session, + int optname, int *val) +{ + int err = 0; + + switch (optname) { + case PPPOL2TP_SO_RECVSEQ: + *val = session->recv_seq; + PRINTK(session->debug, PPPOL2TP_MSG_CONTROL, KERN_INFO, + "%s: get recv_seq=%d\n", session->name, *val); + break; + + case PPPOL2TP_SO_SENDSEQ: + *val = session->send_seq; + PRINTK(session->debug, PPPOL2TP_MSG_CONTROL, KERN_INFO, + "%s: get send_seq=%d\n", session->name, *val); + break; + + case PPPOL2TP_SO_LNSMODE: + *val = session->lns_mode; + PRINTK(session->debug, PPPOL2TP_MSG_CONTROL, KERN_INFO, + "%s: get lns_mode=%d\n", session->name, *val); + break; + + case PPPOL2TP_SO_DEBUG: + *val = session->debug; + PRINTK(session->debug, PPPOL2TP_MSG_CONTROL, KERN_INFO, + "%s: get debug=%d\n", session->name, *val); + break; + + case PPPOL2TP_SO_REORDERTO: + *val = (int) jiffies_to_msecs(session->reorder_timeout); + PRINTK(session->debug, PPPOL2TP_MSG_CONTROL, KERN_INFO, + "%s: get reorder_timeout=%d\n", session->name, *val); + break; + + default: + err = -ENOPROTOOPT; + } + + return err; +} + +/* Main getsockopt() entry point. + * Does API checks, then calls either the tunnel or session getsockopt + * handler, according to whether the PPPoX socket is a for a regular session + * or the special tunnel type. + */ +static int pppol2tp_getsockopt(struct socket *sock, int level, + int optname, char __user *optval, int __user *optlen) +{ + struct sock *sk = sock->sk; + struct l2tp_session *session; + struct l2tp_tunnel *tunnel; + int val, len; + int err; + struct pppol2tp_session *ps; + + if (level != SOL_PPPOL2TP) + return udp_prot.getsockopt(sk, level, optname, optval, optlen); + + if (get_user(len, (int __user *) optlen)) + return -EFAULT; + + len = min_t(unsigned int, len, sizeof(int)); + + if (len < 0) + return -EINVAL; + + err = -ENOTCONN; + if (sk->sk_user_data == NULL) + goto end; + + /* Get the session context */ + err = -EBADF; + session = pppol2tp_sock_to_session(sk); + if (session == NULL) + goto end; + + /* Special case: if session_id == 0x0000, treat as operation on tunnel */ + ps = l2tp_session_priv(session); + if ((session->session_id == 0) && + (session->peer_session_id == 0)) { + err = -EBADF; + tunnel = l2tp_sock_to_tunnel(ps->tunnel_sock); + if (tunnel == NULL) + goto end_put_sess; + + err = pppol2tp_tunnel_getsockopt(sk, tunnel, optname, &val); + sock_put(ps->tunnel_sock); + } else + err = pppol2tp_session_getsockopt(sk, session, optname, &val); + + err = -EFAULT; + if (put_user(len, (int __user *) optlen)) + goto end_put_sess; + + if (copy_to_user((void __user *) optval, &val, len)) + goto end_put_sess; + + err = 0; + +end_put_sess: + sock_put(sk); +end: + return err; +} + +/***************************************************************************** + * /proc filesystem for debug + *****************************************************************************/ + +static unsigned int pppol2tp_net_id; + +#ifdef CONFIG_PROC_FS + +struct pppol2tp_seq_data { + struct seq_net_private p; + int tunnel_idx; /* current tunnel */ + int session_idx; /* index of session within current tunnel */ + struct l2tp_tunnel *tunnel; + struct l2tp_session *session; /* NULL means get next tunnel */ +}; + +static void pppol2tp_next_tunnel(struct net *net, struct pppol2tp_seq_data *pd) +{ + pd->tunnel = l2tp_tunnel_find_nth(net, pd->tunnel_idx); + pd->tunnel_idx++; +} + +static void pppol2tp_next_session(struct net *net, struct pppol2tp_seq_data *pd) +{ + pd->session = l2tp_session_find_nth(pd->tunnel, pd->session_idx); + pd->session_idx++; + if (pd->session == NULL) { + pd->session_idx = 0; + pppol2tp_next_tunnel(net, pd); + } +} + +static void *pppol2tp_seq_start(struct seq_file *m, loff_t *offs) +{ + struct pppol2tp_seq_data *pd = SEQ_START_TOKEN; + loff_t pos = *offs; + struct net *net; + + if (!pos) + goto out; + + BUG_ON(m->private == NULL); + pd = m->private; + net = seq_file_net(m); + + if (pd->tunnel == NULL) + pppol2tp_next_tunnel(net, pd); + else + pppol2tp_next_session(net, pd); + + /* NULL tunnel and session indicates end of list */ + if ((pd->tunnel == NULL) && (pd->session == NULL)) + pd = NULL; + +out: + return pd; +} + +static void *pppol2tp_seq_next(struct seq_file *m, void *v, loff_t *pos) +{ + (*pos)++; + return NULL; +} + +static void pppol2tp_seq_stop(struct seq_file *p, void *v) +{ + /* nothing to do */ +} + +static void pppol2tp_seq_tunnel_show(struct seq_file *m, void *v) +{ + struct l2tp_tunnel *tunnel = v; + + seq_printf(m, "\nTUNNEL '%s', %c %d\n", + tunnel->name, + (tunnel == tunnel->sock->sk_user_data) ? 'Y' : 'N', + atomic_read(&tunnel->ref_count) - 1); + seq_printf(m, " %08x %llu/%llu/%llu %llu/%llu/%llu\n", + tunnel->debug, + (unsigned long long)tunnel->stats.tx_packets, + (unsigned long long)tunnel->stats.tx_bytes, + (unsigned long long)tunnel->stats.tx_errors, + (unsigned long long)tunnel->stats.rx_packets, + (unsigned long long)tunnel->stats.rx_bytes, + (unsigned long long)tunnel->stats.rx_errors); +} + +static void pppol2tp_seq_session_show(struct seq_file *m, void *v) +{ + struct l2tp_session *session = v; + struct l2tp_tunnel *tunnel = session->tunnel; + struct pppol2tp_session *ps = l2tp_session_priv(session); + u32 ip = 0; + u16 port = 0; + + if (tunnel->sock) { + struct inet_sock *inet = inet_sk(tunnel->sock); + ip = ntohl(inet->inet_saddr); + port = ntohs(inet->inet_sport); + } + + seq_printf(m, " SESSION '%s' %08X/%d %04X/%04X -> " + "%04X/%04X %d %c\n", + session->name, ip, port, + tunnel->tunnel_id, + session->session_id, + tunnel->peer_tunnel_id, + session->peer_session_id, + ps->sock->sk_state, + (session == ps->sock->sk_user_data) ? + 'Y' : 'N'); + seq_printf(m, " %d/%d/%c/%c/%s %08x %u\n", + session->mtu, session->mru, + session->recv_seq ? 'R' : '-', + session->send_seq ? 'S' : '-', + session->lns_mode ? "LNS" : "LAC", + session->debug, + jiffies_to_msecs(session->reorder_timeout)); + seq_printf(m, " %hu/%hu %llu/%llu/%llu %llu/%llu/%llu\n", + session->nr, session->ns, + (unsigned long long)session->stats.tx_packets, + (unsigned long long)session->stats.tx_bytes, + (unsigned long long)session->stats.tx_errors, + (unsigned long long)session->stats.rx_packets, + (unsigned long long)session->stats.rx_bytes, + (unsigned long long)session->stats.rx_errors); +} + +static int pppol2tp_seq_show(struct seq_file *m, void *v) +{ + struct pppol2tp_seq_data *pd = v; + + /* display header on line 1 */ + if (v == SEQ_START_TOKEN) { + seq_puts(m, "PPPoL2TP driver info, " PPPOL2TP_DRV_VERSION "\n"); + seq_puts(m, "TUNNEL name, user-data-ok session-count\n"); + seq_puts(m, " debug tx-pkts/bytes/errs rx-pkts/bytes/errs\n"); + seq_puts(m, " SESSION name, addr/port src-tid/sid " + "dest-tid/sid state user-data-ok\n"); + seq_puts(m, " mtu/mru/rcvseq/sendseq/lns debug reorderto\n"); + seq_puts(m, " nr/ns tx-pkts/bytes/errs rx-pkts/bytes/errs\n"); + goto out; + } + + /* Show the tunnel or session context. + */ + if (pd->session == NULL) + pppol2tp_seq_tunnel_show(m, pd->tunnel); + else + pppol2tp_seq_session_show(m, pd->session); + +out: + return 0; +} + +static const struct seq_operations pppol2tp_seq_ops = { + .start = pppol2tp_seq_start, + .next = pppol2tp_seq_next, + .stop = pppol2tp_seq_stop, + .show = pppol2tp_seq_show, +}; + +/* Called when our /proc file is opened. We allocate data for use when + * iterating our tunnel / session contexts and store it in the private + * data of the seq_file. + */ +static int pppol2tp_proc_open(struct inode *inode, struct file *file) +{ + return seq_open_net(inode, file, &pppol2tp_seq_ops, + sizeof(struct pppol2tp_seq_data)); +} + +static const struct file_operations pppol2tp_proc_fops = { + .owner = THIS_MODULE, + .open = pppol2tp_proc_open, + .read = seq_read, + .llseek = seq_lseek, + .release = seq_release_net, +}; + +#endif /* CONFIG_PROC_FS */ + +/***************************************************************************** + * Network namespace + *****************************************************************************/ + +static __net_init int pppol2tp_init_net(struct net *net) +{ + struct proc_dir_entry *pde; + int err = 0; + + pde = proc_net_fops_create(net, "pppol2tp", S_IRUGO, &pppol2tp_proc_fops); + if (!pde) { + err = -ENOMEM; + goto out; + } + +out: + return err; +} + +static __net_exit void pppol2tp_exit_net(struct net *net) +{ + proc_net_remove(net, "pppol2tp"); +} + +static struct pernet_operations pppol2tp_net_ops = { + .init = pppol2tp_init_net, + .exit = pppol2tp_exit_net, + .id = &pppol2tp_net_id, +}; + +/***************************************************************************** + * Init and cleanup + *****************************************************************************/ + +static const struct proto_ops pppol2tp_ops = { + .family = AF_PPPOX, + .owner = THIS_MODULE, + .release = pppol2tp_release, + .bind = sock_no_bind, + .connect = pppol2tp_connect, + .socketpair = sock_no_socketpair, + .accept = sock_no_accept, + .getname = pppol2tp_getname, + .poll = datagram_poll, + .listen = sock_no_listen, + .shutdown = sock_no_shutdown, + .setsockopt = pppol2tp_setsockopt, + .getsockopt = pppol2tp_getsockopt, + .sendmsg = pppol2tp_sendmsg, + .recvmsg = pppol2tp_recvmsg, + .mmap = sock_no_mmap, + .ioctl = pppox_ioctl, +}; + +static struct pppox_proto pppol2tp_proto = { + .create = pppol2tp_create, + .ioctl = pppol2tp_ioctl +}; + +static int __init pppol2tp_init(void) +{ + int err; + + err = register_pernet_device(&pppol2tp_net_ops); + if (err) + goto out; + + err = proto_register(&pppol2tp_sk_proto, 0); + if (err) + goto out_unregister_pppol2tp_pernet; + + err = register_pppox_proto(PX_PROTO_OL2TP, &pppol2tp_proto); + if (err) + goto out_unregister_pppol2tp_proto; + + printk(KERN_INFO "PPPoL2TP kernel driver, %s\n", + PPPOL2TP_DRV_VERSION); + +out: + return err; +out_unregister_pppol2tp_proto: + proto_unregister(&pppol2tp_sk_proto); +out_unregister_pppol2tp_pernet: + unregister_pernet_device(&pppol2tp_net_ops); + goto out; +} + +static void __exit pppol2tp_exit(void) +{ + unregister_pppox_proto(PX_PROTO_OL2TP); + proto_unregister(&pppol2tp_sk_proto); + unregister_pernet_device(&pppol2tp_net_ops); +} + +module_init(pppol2tp_init); +module_exit(pppol2tp_exit); + +MODULE_AUTHOR("James Chapman "); +MODULE_DESCRIPTION("PPP over L2TP over UDP"); +MODULE_LICENSE("GPL"); +MODULE_VERSION(PPPOL2TP_DRV_VERSION); diff --git a/net/l2tp/pppol2tp.c b/net/l2tp/pppol2tp.c deleted file mode 100644 index 449a9825200..00000000000 --- a/net/l2tp/pppol2tp.c +++ /dev/null @@ -1,2680 +0,0 @@ -/***************************************************************************** - * Linux PPP over L2TP (PPPoX/PPPoL2TP) Sockets - * - * PPPoX --- Generic PPP encapsulation socket family - * PPPoL2TP --- PPP over L2TP (RFC 2661) - * - * Version: 1.0.0 - * - * Authors: Martijn van Oosterhout - * James Chapman (jchapman@katalix.com) - * Contributors: - * Michal Ostrowski - * Arnaldo Carvalho de Melo - * David S. Miller (davem@redhat.com) - * - * License: - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version - * 2 of the License, or (at your option) any later version. - * - */ - -/* This driver handles only L2TP data frames; control frames are handled by a - * userspace application. - * - * To send data in an L2TP session, userspace opens a PPPoL2TP socket and - * attaches it to a bound UDP socket with local tunnel_id / session_id and - * peer tunnel_id / session_id set. Data can then be sent or received using - * regular socket sendmsg() / recvmsg() calls. Kernel parameters of the socket - * can be read or modified using ioctl() or [gs]etsockopt() calls. - * - * When a PPPoL2TP socket is connected with local and peer session_id values - * zero, the socket is treated as a special tunnel management socket. - * - * Here's example userspace code to create a socket for sending/receiving data - * over an L2TP session:- - * - * struct sockaddr_pppol2tp sax; - * int fd; - * int session_fd; - * - * fd = socket(AF_PPPOX, SOCK_DGRAM, PX_PROTO_OL2TP); - * - * sax.sa_family = AF_PPPOX; - * sax.sa_protocol = PX_PROTO_OL2TP; - * sax.pppol2tp.fd = tunnel_fd; // bound UDP socket - * sax.pppol2tp.addr.sin_addr.s_addr = addr->sin_addr.s_addr; - * sax.pppol2tp.addr.sin_port = addr->sin_port; - * sax.pppol2tp.addr.sin_family = AF_INET; - * sax.pppol2tp.s_tunnel = tunnel_id; - * sax.pppol2tp.s_session = session_id; - * sax.pppol2tp.d_tunnel = peer_tunnel_id; - * sax.pppol2tp.d_session = peer_session_id; - * - * session_fd = connect(fd, (struct sockaddr *)&sax, sizeof(sax)); - * - * A pppd plugin that allows PPP traffic to be carried over L2TP using - * this driver is available from the OpenL2TP project at - * http://openl2tp.sourceforge.net. - */ - -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include - - -#define PPPOL2TP_DRV_VERSION "V1.0" - -/* L2TP header constants */ -#define L2TP_HDRFLAG_T 0x8000 -#define L2TP_HDRFLAG_L 0x4000 -#define L2TP_HDRFLAG_S 0x0800 -#define L2TP_HDRFLAG_O 0x0200 -#define L2TP_HDRFLAG_P 0x0100 - -#define L2TP_HDR_VER_MASK 0x000F -#define L2TP_HDR_VER 0x0002 - -/* Space for UDP, L2TP and PPP headers */ -#define PPPOL2TP_HEADER_OVERHEAD 40 - -/* Just some random numbers */ -#define L2TP_TUNNEL_MAGIC 0x42114DDA -#define L2TP_SESSION_MAGIC 0x0C04EB7D - -#define PPPOL2TP_HASH_BITS 4 -#define PPPOL2TP_HASH_SIZE (1 << PPPOL2TP_HASH_BITS) - -/* Default trace flags */ -#define PPPOL2TP_DEFAULT_DEBUG_FLAGS 0 - -#define PRINTK(_mask, _type, _lvl, _fmt, args...) \ - do { \ - if ((_mask) & (_type)) \ - printk(_lvl "PPPOL2TP: " _fmt, ##args); \ - } while(0) - -/* Number of bytes to build transmit L2TP headers. - * Unfortunately the size is different depending on whether sequence numbers - * are enabled. - */ -#define PPPOL2TP_L2TP_HDR_SIZE_SEQ 10 -#define PPPOL2TP_L2TP_HDR_SIZE_NOSEQ 6 - -struct pppol2tp_tunnel; - -/* Describes a session. It is the sk_user_data field in the PPPoL2TP - * socket. Contains information to determine incoming packets and transmit - * outgoing ones. - */ -struct pppol2tp_session -{ - int magic; /* should be - * L2TP_SESSION_MAGIC */ - int owner; /* pid that opened the socket */ - - struct sock *sock; /* Pointer to the session - * PPPoX socket */ - struct sock *tunnel_sock; /* Pointer to the tunnel UDP - * socket */ - - struct pppol2tp_addr tunnel_addr; /* Description of tunnel */ - - struct pppol2tp_tunnel *tunnel; /* back pointer to tunnel - * context */ - - char name[20]; /* "sess xxxxx/yyyyy", where - * x=tunnel_id, y=session_id */ - int mtu; - int mru; - int flags; /* accessed by PPPIOCGFLAGS. - * Unused. */ - unsigned recv_seq:1; /* expect receive packets with - * sequence numbers? */ - unsigned send_seq:1; /* send packets with sequence - * numbers? */ - unsigned lns_mode:1; /* behave as LNS? LAC enables - * sequence numbers under - * control of LNS. */ - int debug; /* bitmask of debug message - * categories */ - int reorder_timeout; /* configured reorder timeout - * (in jiffies) */ - u16 nr; /* session NR state (receive) */ - u16 ns; /* session NR state (send) */ - struct sk_buff_head reorder_q; /* receive reorder queue */ - struct pppol2tp_ioc_stats stats; - struct hlist_node hlist; /* Hash list node */ -}; - -/* The sk_user_data field of the tunnel's UDP socket. It contains info to track - * all the associated sessions so incoming packets can be sorted out - */ -struct pppol2tp_tunnel -{ - int magic; /* Should be L2TP_TUNNEL_MAGIC */ - rwlock_t hlist_lock; /* protect session_hlist */ - struct hlist_head session_hlist[PPPOL2TP_HASH_SIZE]; - /* hashed list of sessions, - * hashed by id */ - int debug; /* bitmask of debug message - * categories */ - char name[12]; /* "tunl xxxxx" */ - struct pppol2tp_ioc_stats stats; - - void (*old_sk_destruct)(struct sock *); - - struct sock *sock; /* Parent socket */ - struct list_head list; /* Keep a list of all open - * prepared sockets */ - struct net *pppol2tp_net; /* the net we belong to */ - - atomic_t ref_count; -}; - -/* Private data stored for received packets in the skb. - */ -struct pppol2tp_skb_cb { - u16 ns; - u16 nr; - u16 has_seq; - u16 length; - unsigned long expires; -}; - -#define PPPOL2TP_SKB_CB(skb) ((struct pppol2tp_skb_cb *) &skb->cb[sizeof(struct inet_skb_parm)]) - -static int pppol2tp_xmit(struct ppp_channel *chan, struct sk_buff *skb); -static void pppol2tp_tunnel_free(struct pppol2tp_tunnel *tunnel); - -static atomic_t pppol2tp_tunnel_count; -static atomic_t pppol2tp_session_count; -static struct ppp_channel_ops pppol2tp_chan_ops = { pppol2tp_xmit , NULL }; -static const struct proto_ops pppol2tp_ops; - -/* per-net private data for this module */ -static int pppol2tp_net_id __read_mostly; -struct pppol2tp_net { - struct list_head pppol2tp_tunnel_list; - rwlock_t pppol2tp_tunnel_list_lock; -}; - -static inline struct pppol2tp_net *pppol2tp_pernet(struct net *net) -{ - BUG_ON(!net); - - return net_generic(net, pppol2tp_net_id); -} - -/* Helpers to obtain tunnel/session contexts from sockets. - */ -static inline struct pppol2tp_session *pppol2tp_sock_to_session(struct sock *sk) -{ - struct pppol2tp_session *session; - - if (sk == NULL) - return NULL; - - sock_hold(sk); - session = (struct pppol2tp_session *)(sk->sk_user_data); - if (session == NULL) { - sock_put(sk); - goto out; - } - - BUG_ON(session->magic != L2TP_SESSION_MAGIC); -out: - return session; -} - -static inline struct pppol2tp_tunnel *pppol2tp_sock_to_tunnel(struct sock *sk) -{ - struct pppol2tp_tunnel *tunnel; - - if (sk == NULL) - return NULL; - - sock_hold(sk); - tunnel = (struct pppol2tp_tunnel *)(sk->sk_user_data); - if (tunnel == NULL) { - sock_put(sk); - goto out; - } - - BUG_ON(tunnel->magic != L2TP_TUNNEL_MAGIC); -out: - return tunnel; -} - -/* Tunnel reference counts. Incremented per session that is added to - * the tunnel. - */ -static inline void pppol2tp_tunnel_inc_refcount(struct pppol2tp_tunnel *tunnel) -{ - atomic_inc(&tunnel->ref_count); -} - -static inline void pppol2tp_tunnel_dec_refcount(struct pppol2tp_tunnel *tunnel) -{ - if (atomic_dec_and_test(&tunnel->ref_count)) - pppol2tp_tunnel_free(tunnel); -} - -/* Session hash list. - * The session_id SHOULD be random according to RFC2661, but several - * L2TP implementations (Cisco and Microsoft) use incrementing - * session_ids. So we do a real hash on the session_id, rather than a - * simple bitmask. - */ -static inline struct hlist_head * -pppol2tp_session_id_hash(struct pppol2tp_tunnel *tunnel, u16 session_id) -{ - unsigned long hash_val = (unsigned long) session_id; - return &tunnel->session_hlist[hash_long(hash_val, PPPOL2TP_HASH_BITS)]; -} - -/* Lookup a session by id - */ -static struct pppol2tp_session * -pppol2tp_session_find(struct pppol2tp_tunnel *tunnel, u16 session_id) -{ - struct hlist_head *session_list = - pppol2tp_session_id_hash(tunnel, session_id); - struct pppol2tp_session *session; - struct hlist_node *walk; - - read_lock_bh(&tunnel->hlist_lock); - hlist_for_each_entry(session, walk, session_list, hlist) { - if (session->tunnel_addr.s_session == session_id) { - read_unlock_bh(&tunnel->hlist_lock); - return session; - } - } - read_unlock_bh(&tunnel->hlist_lock); - - return NULL; -} - -/* Lookup a tunnel by id - */ -static struct pppol2tp_tunnel *pppol2tp_tunnel_find(struct net *net, u16 tunnel_id) -{ - struct pppol2tp_tunnel *tunnel; - struct pppol2tp_net *pn = pppol2tp_pernet(net); - - read_lock_bh(&pn->pppol2tp_tunnel_list_lock); - list_for_each_entry(tunnel, &pn->pppol2tp_tunnel_list, list) { - if (tunnel->stats.tunnel_id == tunnel_id) { - read_unlock_bh(&pn->pppol2tp_tunnel_list_lock); - return tunnel; - } - } - read_unlock_bh(&pn->pppol2tp_tunnel_list_lock); - - return NULL; -} - -/***************************************************************************** - * Receive data handling - *****************************************************************************/ - -/* Queue a skb in order. We come here only if the skb has an L2TP sequence - * number. - */ -static void pppol2tp_recv_queue_skb(struct pppol2tp_session *session, struct sk_buff *skb) -{ - struct sk_buff *skbp; - struct sk_buff *tmp; - u16 ns = PPPOL2TP_SKB_CB(skb)->ns; - - spin_lock_bh(&session->reorder_q.lock); - skb_queue_walk_safe(&session->reorder_q, skbp, tmp) { - if (PPPOL2TP_SKB_CB(skbp)->ns > ns) { - __skb_queue_before(&session->reorder_q, skbp, skb); - PRINTK(session->debug, PPPOL2TP_MSG_SEQ, KERN_DEBUG, - "%s: pkt %hu, inserted before %hu, reorder_q len=%d\n", - session->name, ns, PPPOL2TP_SKB_CB(skbp)->ns, - skb_queue_len(&session->reorder_q)); - session->stats.rx_oos_packets++; - goto out; - } - } - - __skb_queue_tail(&session->reorder_q, skb); - -out: - spin_unlock_bh(&session->reorder_q.lock); -} - -/* Dequeue a single skb. - */ -static void pppol2tp_recv_dequeue_skb(struct pppol2tp_session *session, struct sk_buff *skb) -{ - struct pppol2tp_tunnel *tunnel = session->tunnel; - int length = PPPOL2TP_SKB_CB(skb)->length; - struct sock *session_sock = NULL; - - /* We're about to requeue the skb, so return resources - * to its current owner (a socket receive buffer). - */ - skb_orphan(skb); - - tunnel->stats.rx_packets++; - tunnel->stats.rx_bytes += length; - session->stats.rx_packets++; - session->stats.rx_bytes += length; - - if (PPPOL2TP_SKB_CB(skb)->has_seq) { - /* Bump our Nr */ - session->nr++; - PRINTK(session->debug, PPPOL2TP_MSG_SEQ, KERN_DEBUG, - "%s: updated nr to %hu\n", session->name, session->nr); - } - - /* If the socket is bound, send it in to PPP's input queue. Otherwise - * queue it on the session socket. - */ - session_sock = session->sock; - if (session_sock->sk_state & PPPOX_BOUND) { - struct pppox_sock *po; - PRINTK(session->debug, PPPOL2TP_MSG_DATA, KERN_DEBUG, - "%s: recv %d byte data frame, passing to ppp\n", - session->name, length); - - /* We need to forget all info related to the L2TP packet - * gathered in the skb as we are going to reuse the same - * skb for the inner packet. - * Namely we need to: - * - reset xfrm (IPSec) information as it applies to - * the outer L2TP packet and not to the inner one - * - release the dst to force a route lookup on the inner - * IP packet since skb->dst currently points to the dst - * of the UDP tunnel - * - reset netfilter information as it doesn't apply - * to the inner packet either - */ - secpath_reset(skb); - skb_dst_drop(skb); - nf_reset(skb); - - po = pppox_sk(session_sock); - ppp_input(&po->chan, skb); - } else { - PRINTK(session->debug, PPPOL2TP_MSG_DATA, KERN_INFO, - "%s: socket not bound\n", session->name); - - /* Not bound. Nothing we can do, so discard. */ - session->stats.rx_errors++; - kfree_skb(skb); - } - - sock_put(session->sock); -} - -/* Dequeue skbs from the session's reorder_q, subject to packet order. - * Skbs that have been in the queue for too long are simply discarded. - */ -static void pppol2tp_recv_dequeue(struct pppol2tp_session *session) -{ - struct sk_buff *skb; - struct sk_buff *tmp; - - /* If the pkt at the head of the queue has the nr that we - * expect to send up next, dequeue it and any other - * in-sequence packets behind it. - */ - spin_lock_bh(&session->reorder_q.lock); - skb_queue_walk_safe(&session->reorder_q, skb, tmp) { - if (time_after(jiffies, PPPOL2TP_SKB_CB(skb)->expires)) { - session->stats.rx_seq_discards++; - session->stats.rx_errors++; - PRINTK(session->debug, PPPOL2TP_MSG_SEQ, KERN_DEBUG, - "%s: oos pkt %hu len %d discarded (too old), " - "waiting for %hu, reorder_q_len=%d\n", - session->name, PPPOL2TP_SKB_CB(skb)->ns, - PPPOL2TP_SKB_CB(skb)->length, session->nr, - skb_queue_len(&session->reorder_q)); - __skb_unlink(skb, &session->reorder_q); - kfree_skb(skb); - sock_put(session->sock); - continue; - } - - if (PPPOL2TP_SKB_CB(skb)->has_seq) { - if (PPPOL2TP_SKB_CB(skb)->ns != session->nr) { - PRINTK(session->debug, PPPOL2TP_MSG_SEQ, KERN_DEBUG, - "%s: holding oos pkt %hu len %d, " - "waiting for %hu, reorder_q_len=%d\n", - session->name, PPPOL2TP_SKB_CB(skb)->ns, - PPPOL2TP_SKB_CB(skb)->length, session->nr, - skb_queue_len(&session->reorder_q)); - goto out; - } - } - __skb_unlink(skb, &session->reorder_q); - - /* Process the skb. We release the queue lock while we - * do so to let other contexts process the queue. - */ - spin_unlock_bh(&session->reorder_q.lock); - pppol2tp_recv_dequeue_skb(session, skb); - spin_lock_bh(&session->reorder_q.lock); - } - -out: - spin_unlock_bh(&session->reorder_q.lock); -} - -static inline int pppol2tp_verify_udp_checksum(struct sock *sk, - struct sk_buff *skb) -{ - struct udphdr *uh = udp_hdr(skb); - u16 ulen = ntohs(uh->len); - struct inet_sock *inet; - __wsum psum; - - if (sk->sk_no_check || skb_csum_unnecessary(skb) || !uh->check) - return 0; - - inet = inet_sk(sk); - psum = csum_tcpudp_nofold(inet->inet_saddr, inet->inet_daddr, ulen, - IPPROTO_UDP, 0); - - if ((skb->ip_summed == CHECKSUM_COMPLETE) && - !csum_fold(csum_add(psum, skb->csum))) - return 0; - - skb->csum = psum; - - return __skb_checksum_complete(skb); -} - -/* Internal receive frame. Do the real work of receiving an L2TP data frame - * here. The skb is not on a list when we get here. - * Returns 0 if the packet was a data packet and was successfully passed on. - * Returns 1 if the packet was not a good data packet and could not be - * forwarded. All such packets are passed up to userspace to deal with. - */ -static int pppol2tp_recv_core(struct sock *sock, struct sk_buff *skb) -{ - struct pppol2tp_session *session = NULL; - struct pppol2tp_tunnel *tunnel; - unsigned char *ptr, *optr; - u16 hdrflags; - u16 tunnel_id, session_id; - int length; - int offset; - - tunnel = pppol2tp_sock_to_tunnel(sock); - if (tunnel == NULL) - goto no_tunnel; - - if (tunnel->sock && pppol2tp_verify_udp_checksum(tunnel->sock, skb)) - goto discard_bad_csum; - - /* UDP always verifies the packet length. */ - __skb_pull(skb, sizeof(struct udphdr)); - - /* Short packet? */ - if (!pskb_may_pull(skb, 12)) { - PRINTK(tunnel->debug, PPPOL2TP_MSG_DATA, KERN_INFO, - "%s: recv short packet (len=%d)\n", tunnel->name, skb->len); - goto error; - } - - /* Point to L2TP header */ - optr = ptr = skb->data; - - /* Get L2TP header flags */ - hdrflags = ntohs(*(__be16*)ptr); - - /* Trace packet contents, if enabled */ - if (tunnel->debug & PPPOL2TP_MSG_DATA) { - length = min(16u, skb->len); - if (!pskb_may_pull(skb, length)) - goto error; - - printk(KERN_DEBUG "%s: recv: ", tunnel->name); - - offset = 0; - do { - printk(" %02X", ptr[offset]); - } while (++offset < length); - - printk("\n"); - } - - /* Get length of L2TP packet */ - length = skb->len; - - /* If type is control packet, it is handled by userspace. */ - if (hdrflags & L2TP_HDRFLAG_T) { - PRINTK(tunnel->debug, PPPOL2TP_MSG_DATA, KERN_DEBUG, - "%s: recv control packet, len=%d\n", tunnel->name, length); - goto error; - } - - /* Skip flags */ - ptr += 2; - - /* If length is present, skip it */ - if (hdrflags & L2TP_HDRFLAG_L) - ptr += 2; - - /* Extract tunnel and session ID */ - tunnel_id = ntohs(*(__be16 *) ptr); - ptr += 2; - session_id = ntohs(*(__be16 *) ptr); - ptr += 2; - - /* Find the session context */ - session = pppol2tp_session_find(tunnel, session_id); - if (!session) { - /* Not found? Pass to userspace to deal with */ - PRINTK(tunnel->debug, PPPOL2TP_MSG_DATA, KERN_INFO, - "%s: no socket found (%hu/%hu). Passing up.\n", - tunnel->name, tunnel_id, session_id); - goto error; - } - sock_hold(session->sock); - - /* The ref count on the socket was increased by the above call since - * we now hold a pointer to the session. Take care to do sock_put() - * when exiting this function from now on... - */ - - /* Handle the optional sequence numbers. If we are the LAC, - * enable/disable sequence numbers under the control of the LNS. If - * no sequence numbers present but we were expecting them, discard - * frame. - */ - if (hdrflags & L2TP_HDRFLAG_S) { - u16 ns, nr; - ns = ntohs(*(__be16 *) ptr); - ptr += 2; - nr = ntohs(*(__be16 *) ptr); - ptr += 2; - - /* Received a packet with sequence numbers. If we're the LNS, - * check if we sre sending sequence numbers and if not, - * configure it so. - */ - if ((!session->lns_mode) && (!session->send_seq)) { - PRINTK(session->debug, PPPOL2TP_MSG_SEQ, KERN_INFO, - "%s: requested to enable seq numbers by LNS\n", - session->name); - session->send_seq = -1; - } - - /* Store L2TP info in the skb */ - PPPOL2TP_SKB_CB(skb)->ns = ns; - PPPOL2TP_SKB_CB(skb)->nr = nr; - PPPOL2TP_SKB_CB(skb)->has_seq = 1; - - PRINTK(session->debug, PPPOL2TP_MSG_SEQ, KERN_DEBUG, - "%s: recv data ns=%hu, nr=%hu, session nr=%hu\n", - session->name, ns, nr, session->nr); - } else { - /* No sequence numbers. - * If user has configured mandatory sequence numbers, discard. - */ - if (session->recv_seq) { - PRINTK(session->debug, PPPOL2TP_MSG_SEQ, KERN_WARNING, - "%s: recv data has no seq numbers when required. " - "Discarding\n", session->name); - session->stats.rx_seq_discards++; - goto discard; - } - - /* If we're the LAC and we're sending sequence numbers, the - * LNS has requested that we no longer send sequence numbers. - * If we're the LNS and we're sending sequence numbers, the - * LAC is broken. Discard the frame. - */ - if ((!session->lns_mode) && (session->send_seq)) { - PRINTK(session->debug, PPPOL2TP_MSG_SEQ, KERN_INFO, - "%s: requested to disable seq numbers by LNS\n", - session->name); - session->send_seq = 0; - } else if (session->send_seq) { - PRINTK(session->debug, PPPOL2TP_MSG_SEQ, KERN_WARNING, - "%s: recv data has no seq numbers when required. " - "Discarding\n", session->name); - session->stats.rx_seq_discards++; - goto discard; - } - - /* Store L2TP info in the skb */ - PPPOL2TP_SKB_CB(skb)->has_seq = 0; - } - - /* If offset bit set, skip it. */ - if (hdrflags & L2TP_HDRFLAG_O) { - offset = ntohs(*(__be16 *)ptr); - ptr += 2 + offset; - } - - offset = ptr - optr; - if (!pskb_may_pull(skb, offset)) - goto discard; - - __skb_pull(skb, offset); - - /* Skip PPP header, if present. In testing, Microsoft L2TP clients - * don't send the PPP header (PPP header compression enabled), but - * other clients can include the header. So we cope with both cases - * here. The PPP header is always FF03 when using L2TP. - * - * Note that skb->data[] isn't dereferenced from a u16 ptr here since - * the field may be unaligned. - */ - if (!pskb_may_pull(skb, 2)) - goto discard; - - if ((skb->data[0] == 0xff) && (skb->data[1] == 0x03)) - skb_pull(skb, 2); - - /* Prepare skb for adding to the session's reorder_q. Hold - * packets for max reorder_timeout or 1 second if not - * reordering. - */ - PPPOL2TP_SKB_CB(skb)->length = length; - PPPOL2TP_SKB_CB(skb)->expires = jiffies + - (session->reorder_timeout ? session->reorder_timeout : HZ); - - /* Add packet to the session's receive queue. Reordering is done here, if - * enabled. Saved L2TP protocol info is stored in skb->sb[]. - */ - if (PPPOL2TP_SKB_CB(skb)->has_seq) { - if (session->reorder_timeout != 0) { - /* Packet reordering enabled. Add skb to session's - * reorder queue, in order of ns. - */ - pppol2tp_recv_queue_skb(session, skb); - } else { - /* Packet reordering disabled. Discard out-of-sequence - * packets - */ - if (PPPOL2TP_SKB_CB(skb)->ns != session->nr) { - session->stats.rx_seq_discards++; - PRINTK(session->debug, PPPOL2TP_MSG_SEQ, KERN_DEBUG, - "%s: oos pkt %hu len %d discarded, " - "waiting for %hu, reorder_q_len=%d\n", - session->name, PPPOL2TP_SKB_CB(skb)->ns, - PPPOL2TP_SKB_CB(skb)->length, session->nr, - skb_queue_len(&session->reorder_q)); - goto discard; - } - skb_queue_tail(&session->reorder_q, skb); - } - } else { - /* No sequence numbers. Add the skb to the tail of the - * reorder queue. This ensures that it will be - * delivered after all previous sequenced skbs. - */ - skb_queue_tail(&session->reorder_q, skb); - } - - /* Try to dequeue as many skbs from reorder_q as we can. */ - pppol2tp_recv_dequeue(session); - sock_put(sock); - - return 0; - -discard: - session->stats.rx_errors++; - kfree_skb(skb); - sock_put(session->sock); - sock_put(sock); - - return 0; - -discard_bad_csum: - LIMIT_NETDEBUG("%s: UDP: bad checksum\n", tunnel->name); - UDP_INC_STATS_USER(&init_net, UDP_MIB_INERRORS, 0); - tunnel->stats.rx_errors++; - kfree_skb(skb); - sock_put(sock); - - return 0; - -error: - /* Put UDP header back */ - __skb_push(skb, sizeof(struct udphdr)); - sock_put(sock); - -no_tunnel: - return 1; -} - -/* UDP encapsulation receive handler. See net/ipv4/udp.c. - * Return codes: - * 0 : success. - * <0: error - * >0: skb should be passed up to userspace as UDP. - */ -static int pppol2tp_udp_encap_recv(struct sock *sk, struct sk_buff *skb) -{ - struct pppol2tp_tunnel *tunnel; - - tunnel = pppol2tp_sock_to_tunnel(sk); - if (tunnel == NULL) - goto pass_up; - - PRINTK(tunnel->debug, PPPOL2TP_MSG_DATA, KERN_DEBUG, - "%s: received %d bytes\n", tunnel->name, skb->len); - - if (pppol2tp_recv_core(sk, skb)) - goto pass_up_put; - - sock_put(sk); - return 0; - -pass_up_put: - sock_put(sk); -pass_up: - return 1; -} - -/* Receive message. This is the recvmsg for the PPPoL2TP socket. - */ -static int pppol2tp_recvmsg(struct kiocb *iocb, struct socket *sock, - struct msghdr *msg, size_t len, - int flags) -{ - int err; - struct sk_buff *skb; - struct sock *sk = sock->sk; - - err = -EIO; - if (sk->sk_state & PPPOX_BOUND) - goto end; - - msg->msg_namelen = 0; - - err = 0; - skb = skb_recv_datagram(sk, flags & ~MSG_DONTWAIT, - flags & MSG_DONTWAIT, &err); - if (!skb) - goto end; - - if (len > skb->len) - len = skb->len; - else if (len < skb->len) - msg->msg_flags |= MSG_TRUNC; - - err = skb_copy_datagram_iovec(skb, 0, msg->msg_iov, len); - if (likely(err == 0)) - err = len; - - kfree_skb(skb); -end: - return err; -} - -/************************************************************************ - * Transmit handling - ***********************************************************************/ - -/* Tell how big L2TP headers are for a particular session. This - * depends on whether sequence numbers are being used. - */ -static inline int pppol2tp_l2tp_header_len(struct pppol2tp_session *session) -{ - if (session->send_seq) - return PPPOL2TP_L2TP_HDR_SIZE_SEQ; - - return PPPOL2TP_L2TP_HDR_SIZE_NOSEQ; -} - -/* Build an L2TP header for the session into the buffer provided. - */ -static void pppol2tp_build_l2tp_header(struct pppol2tp_session *session, - void *buf) -{ - __be16 *bufp = buf; - u16 flags = L2TP_HDR_VER; - - if (session->send_seq) - flags |= L2TP_HDRFLAG_S; - - /* Setup L2TP header. - * FIXME: Can this ever be unaligned? Is direct dereferencing of - * 16-bit header fields safe here for all architectures? - */ - *bufp++ = htons(flags); - *bufp++ = htons(session->tunnel_addr.d_tunnel); - *bufp++ = htons(session->tunnel_addr.d_session); - if (session->send_seq) { - *bufp++ = htons(session->ns); - *bufp++ = 0; - session->ns++; - PRINTK(session->debug, PPPOL2TP_MSG_SEQ, KERN_DEBUG, - "%s: updated ns to %hu\n", session->name, session->ns); - } -} - -/* This is the sendmsg for the PPPoL2TP pppol2tp_session socket. We come here - * when a user application does a sendmsg() on the session socket. L2TP and - * PPP headers must be inserted into the user's data. - */ -static int pppol2tp_sendmsg(struct kiocb *iocb, struct socket *sock, struct msghdr *m, - size_t total_len) -{ - static const unsigned char ppph[2] = { 0xff, 0x03 }; - struct sock *sk = sock->sk; - struct inet_sock *inet; - __wsum csum; - struct sk_buff *skb; - int error; - int hdr_len; - struct pppol2tp_session *session; - struct pppol2tp_tunnel *tunnel; - struct udphdr *uh; - unsigned int len; - struct sock *sk_tun; - u16 udp_len; - - error = -ENOTCONN; - if (sock_flag(sk, SOCK_DEAD) || !(sk->sk_state & PPPOX_CONNECTED)) - goto error; - - /* Get session and tunnel contexts */ - error = -EBADF; - session = pppol2tp_sock_to_session(sk); - if (session == NULL) - goto error; - - sk_tun = session->tunnel_sock; - tunnel = pppol2tp_sock_to_tunnel(sk_tun); - if (tunnel == NULL) - goto error_put_sess; - - /* What header length is configured for this session? */ - hdr_len = pppol2tp_l2tp_header_len(session); - - /* Allocate a socket buffer */ - error = -ENOMEM; - skb = sock_wmalloc(sk, NET_SKB_PAD + sizeof(struct iphdr) + - sizeof(struct udphdr) + hdr_len + - sizeof(ppph) + total_len, - 0, GFP_KERNEL); - if (!skb) - goto error_put_sess_tun; - - /* Reserve space for headers. */ - skb_reserve(skb, NET_SKB_PAD); - skb_reset_network_header(skb); - skb_reserve(skb, sizeof(struct iphdr)); - skb_reset_transport_header(skb); - - /* Build UDP header */ - inet = inet_sk(sk_tun); - udp_len = hdr_len + sizeof(ppph) + total_len; - uh = (struct udphdr *) skb->data; - uh->source = inet->inet_sport; - uh->dest = inet->inet_dport; - uh->len = htons(udp_len); - uh->check = 0; - skb_put(skb, sizeof(struct udphdr)); - - /* Build L2TP header */ - pppol2tp_build_l2tp_header(session, skb->data); - skb_put(skb, hdr_len); - - /* Add PPP header */ - skb->data[0] = ppph[0]; - skb->data[1] = ppph[1]; - skb_put(skb, 2); - - /* Copy user data into skb */ - error = memcpy_fromiovec(skb->data, m->msg_iov, total_len); - if (error < 0) { - kfree_skb(skb); - goto error_put_sess_tun; - } - skb_put(skb, total_len); - - /* Calculate UDP checksum if configured to do so */ - if (sk_tun->sk_no_check == UDP_CSUM_NOXMIT) - skb->ip_summed = CHECKSUM_NONE; - else if (!(skb_dst(skb)->dev->features & NETIF_F_V4_CSUM)) { - skb->ip_summed = CHECKSUM_COMPLETE; - csum = skb_checksum(skb, 0, udp_len, 0); - uh->check = csum_tcpudp_magic(inet->inet_saddr, - inet->inet_daddr, - udp_len, IPPROTO_UDP, csum); - if (uh->check == 0) - uh->check = CSUM_MANGLED_0; - } else { - skb->ip_summed = CHECKSUM_PARTIAL; - skb->csum_start = skb_transport_header(skb) - skb->head; - skb->csum_offset = offsetof(struct udphdr, check); - uh->check = ~csum_tcpudp_magic(inet->inet_saddr, - inet->inet_daddr, - udp_len, IPPROTO_UDP, 0); - } - - /* Debug */ - if (session->send_seq) - PRINTK(session->debug, PPPOL2TP_MSG_DATA, KERN_DEBUG, - "%s: send %Zd bytes, ns=%hu\n", session->name, - total_len, session->ns - 1); - else - PRINTK(session->debug, PPPOL2TP_MSG_DATA, KERN_DEBUG, - "%s: send %Zd bytes\n", session->name, total_len); - - if (session->debug & PPPOL2TP_MSG_DATA) { - int i; - unsigned char *datap = skb->data; - - printk(KERN_DEBUG "%s: xmit:", session->name); - for (i = 0; i < total_len; i++) { - printk(" %02X", *datap++); - if (i == 15) { - printk(" ..."); - break; - } - } - printk("\n"); - } - - /* Queue the packet to IP for output */ - len = skb->len; - error = ip_queue_xmit(skb, 1); - - /* Update stats */ - if (error >= 0) { - tunnel->stats.tx_packets++; - tunnel->stats.tx_bytes += len; - session->stats.tx_packets++; - session->stats.tx_bytes += len; - } else { - tunnel->stats.tx_errors++; - session->stats.tx_errors++; - } - - return error; - -error_put_sess_tun: - sock_put(session->tunnel_sock); -error_put_sess: - sock_put(sk); -error: - return error; -} - -/* Automatically called when the skb is freed. - */ -static void pppol2tp_sock_wfree(struct sk_buff *skb) -{ - sock_put(skb->sk); -} - -/* For data skbs that we transmit, we associate with the tunnel socket - * but don't do accounting. - */ -static inline void pppol2tp_skb_set_owner_w(struct sk_buff *skb, struct sock *sk) -{ - sock_hold(sk); - skb->sk = sk; - skb->destructor = pppol2tp_sock_wfree; -} - -/* Transmit function called by generic PPP driver. Sends PPP frame - * over PPPoL2TP socket. - * - * This is almost the same as pppol2tp_sendmsg(), but rather than - * being called with a msghdr from userspace, it is called with a skb - * from the kernel. - * - * The supplied skb from ppp doesn't have enough headroom for the - * insertion of L2TP, UDP and IP headers so we need to allocate more - * headroom in the skb. This will create a cloned skb. But we must be - * careful in the error case because the caller will expect to free - * the skb it supplied, not our cloned skb. So we take care to always - * leave the original skb unfreed if we return an error. - */ -static int pppol2tp_xmit(struct ppp_channel *chan, struct sk_buff *skb) -{ - static const u8 ppph[2] = { 0xff, 0x03 }; - struct sock *sk = (struct sock *) chan->private; - struct sock *sk_tun; - int hdr_len; - u16 udp_len; - struct pppol2tp_session *session; - struct pppol2tp_tunnel *tunnel; - int rc; - int headroom; - int data_len = skb->len; - struct inet_sock *inet; - __wsum csum; - struct udphdr *uh; - unsigned int len; - int old_headroom; - int new_headroom; - - if (sock_flag(sk, SOCK_DEAD) || !(sk->sk_state & PPPOX_CONNECTED)) - goto abort; - - /* Get session and tunnel contexts from the socket */ - session = pppol2tp_sock_to_session(sk); - if (session == NULL) - goto abort; - - sk_tun = session->tunnel_sock; - if (sk_tun == NULL) - goto abort_put_sess; - tunnel = pppol2tp_sock_to_tunnel(sk_tun); - if (tunnel == NULL) - goto abort_put_sess; - - /* What header length is configured for this session? */ - hdr_len = pppol2tp_l2tp_header_len(session); - - /* Check that there's enough headroom in the skb to insert IP, - * UDP and L2TP and PPP headers. If not enough, expand it to - * make room. Adjust truesize. - */ - headroom = NET_SKB_PAD + sizeof(struct iphdr) + - sizeof(struct udphdr) + hdr_len + sizeof(ppph); - old_headroom = skb_headroom(skb); - if (skb_cow_head(skb, headroom)) - goto abort_put_sess_tun; - - new_headroom = skb_headroom(skb); - skb_orphan(skb); - skb->truesize += new_headroom - old_headroom; - - /* Setup PPP header */ - __skb_push(skb, sizeof(ppph)); - skb->data[0] = ppph[0]; - skb->data[1] = ppph[1]; - - /* Setup L2TP header */ - pppol2tp_build_l2tp_header(session, __skb_push(skb, hdr_len)); - - udp_len = sizeof(struct udphdr) + hdr_len + sizeof(ppph) + data_len; - - /* Setup UDP header */ - inet = inet_sk(sk_tun); - __skb_push(skb, sizeof(*uh)); - skb_reset_transport_header(skb); - uh = udp_hdr(skb); - uh->source = inet->inet_sport; - uh->dest = inet->inet_dport; - uh->len = htons(udp_len); - uh->check = 0; - - /* Debug */ - if (session->send_seq) - PRINTK(session->debug, PPPOL2TP_MSG_DATA, KERN_DEBUG, - "%s: send %d bytes, ns=%hu\n", session->name, - data_len, session->ns - 1); - else - PRINTK(session->debug, PPPOL2TP_MSG_DATA, KERN_DEBUG, - "%s: send %d bytes\n", session->name, data_len); - - if (session->debug & PPPOL2TP_MSG_DATA) { - int i; - unsigned char *datap = skb->data; - - printk(KERN_DEBUG "%s: xmit:", session->name); - for (i = 0; i < data_len; i++) { - printk(" %02X", *datap++); - if (i == 31) { - printk(" ..."); - break; - } - } - printk("\n"); - } - - memset(&(IPCB(skb)->opt), 0, sizeof(IPCB(skb)->opt)); - IPCB(skb)->flags &= ~(IPSKB_XFRM_TUNNEL_SIZE | IPSKB_XFRM_TRANSFORMED | - IPSKB_REROUTED); - nf_reset(skb); - - /* Get routing info from the tunnel socket */ - skb_dst_drop(skb); - skb_dst_set(skb, dst_clone(__sk_dst_get(sk_tun))); - pppol2tp_skb_set_owner_w(skb, sk_tun); - - /* Calculate UDP checksum if configured to do so */ - if (sk_tun->sk_no_check == UDP_CSUM_NOXMIT) - skb->ip_summed = CHECKSUM_NONE; - else if ((skb_dst(skb) && skb_dst(skb)->dev) && - (!(skb_dst(skb)->dev->features & NETIF_F_V4_CSUM))) { - skb->ip_summed = CHECKSUM_COMPLETE; - csum = skb_checksum(skb, 0, udp_len, 0); - uh->check = csum_tcpudp_magic(inet->inet_saddr, - inet->inet_daddr, - udp_len, IPPROTO_UDP, csum); - if (uh->check == 0) - uh->check = CSUM_MANGLED_0; - } else { - skb->ip_summed = CHECKSUM_PARTIAL; - skb->csum_start = skb_transport_header(skb) - skb->head; - skb->csum_offset = offsetof(struct udphdr, check); - uh->check = ~csum_tcpudp_magic(inet->inet_saddr, - inet->inet_daddr, - udp_len, IPPROTO_UDP, 0); - } - - /* Queue the packet to IP for output */ - len = skb->len; - rc = ip_queue_xmit(skb, 1); - - /* Update stats */ - if (rc >= 0) { - tunnel->stats.tx_packets++; - tunnel->stats.tx_bytes += len; - session->stats.tx_packets++; - session->stats.tx_bytes += len; - } else { - tunnel->stats.tx_errors++; - session->stats.tx_errors++; - } - - sock_put(sk_tun); - sock_put(sk); - return 1; - -abort_put_sess_tun: - sock_put(sk_tun); -abort_put_sess: - sock_put(sk); -abort: - /* Free the original skb */ - kfree_skb(skb); - return 1; -} - -/***************************************************************************** - * Session (and tunnel control) socket create/destroy. - *****************************************************************************/ - -/* When the tunnel UDP socket is closed, all the attached sockets need to go - * too. - */ -static void pppol2tp_tunnel_closeall(struct pppol2tp_tunnel *tunnel) -{ - int hash; - struct hlist_node *walk; - struct hlist_node *tmp; - struct pppol2tp_session *session; - struct sock *sk; - - BUG_ON(tunnel == NULL); - - PRINTK(tunnel->debug, PPPOL2TP_MSG_CONTROL, KERN_INFO, - "%s: closing all sessions...\n", tunnel->name); - - write_lock_bh(&tunnel->hlist_lock); - for (hash = 0; hash < PPPOL2TP_HASH_SIZE; hash++) { -again: - hlist_for_each_safe(walk, tmp, &tunnel->session_hlist[hash]) { - struct sk_buff *skb; - - session = hlist_entry(walk, struct pppol2tp_session, hlist); - - sk = session->sock; - - PRINTK(session->debug, PPPOL2TP_MSG_CONTROL, KERN_INFO, - "%s: closing session\n", session->name); - - hlist_del_init(&session->hlist); - - /* Since we should hold the sock lock while - * doing any unbinding, we need to release the - * lock we're holding before taking that lock. - * Hold a reference to the sock so it doesn't - * disappear as we're jumping between locks. - */ - sock_hold(sk); - write_unlock_bh(&tunnel->hlist_lock); - lock_sock(sk); - - if (sk->sk_state & (PPPOX_CONNECTED | PPPOX_BOUND)) { - pppox_unbind_sock(sk); - sk->sk_state = PPPOX_DEAD; - sk->sk_state_change(sk); - } - - /* Purge any queued data */ - skb_queue_purge(&sk->sk_receive_queue); - skb_queue_purge(&sk->sk_write_queue); - while ((skb = skb_dequeue(&session->reorder_q))) { - kfree_skb(skb); - sock_put(sk); - } - - release_sock(sk); - sock_put(sk); - - /* Now restart from the beginning of this hash - * chain. We always remove a session from the - * list so we are guaranteed to make forward - * progress. - */ - write_lock_bh(&tunnel->hlist_lock); - goto again; - } - } - write_unlock_bh(&tunnel->hlist_lock); -} - -/* Really kill the tunnel. - * Come here only when all sessions have been cleared from the tunnel. - */ -static void pppol2tp_tunnel_free(struct pppol2tp_tunnel *tunnel) -{ - struct pppol2tp_net *pn = pppol2tp_pernet(tunnel->pppol2tp_net); - - /* Remove from socket list */ - write_lock_bh(&pn->pppol2tp_tunnel_list_lock); - list_del_init(&tunnel->list); - write_unlock_bh(&pn->pppol2tp_tunnel_list_lock); - - atomic_dec(&pppol2tp_tunnel_count); - kfree(tunnel); -} - -/* Tunnel UDP socket destruct hook. - * The tunnel context is deleted only when all session sockets have been - * closed. - */ -static void pppol2tp_tunnel_destruct(struct sock *sk) -{ - struct pppol2tp_tunnel *tunnel; - - tunnel = sk->sk_user_data; - if (tunnel == NULL) - goto end; - - PRINTK(tunnel->debug, PPPOL2TP_MSG_CONTROL, KERN_INFO, - "%s: closing...\n", tunnel->name); - - /* Close all sessions */ - pppol2tp_tunnel_closeall(tunnel); - - /* No longer an encapsulation socket. See net/ipv4/udp.c */ - (udp_sk(sk))->encap_type = 0; - (udp_sk(sk))->encap_rcv = NULL; - - /* Remove hooks into tunnel socket */ - tunnel->sock = NULL; - sk->sk_destruct = tunnel->old_sk_destruct; - sk->sk_user_data = NULL; - - /* Call original (UDP) socket descructor */ - if (sk->sk_destruct != NULL) - (*sk->sk_destruct)(sk); - - pppol2tp_tunnel_dec_refcount(tunnel); - -end: - return; -} - -/* Really kill the session socket. (Called from sock_put() if - * refcnt == 0.) - */ -static void pppol2tp_session_destruct(struct sock *sk) -{ - struct pppol2tp_session *session = NULL; - - if (sk->sk_user_data != NULL) { - struct pppol2tp_tunnel *tunnel; - - session = sk->sk_user_data; - if (session == NULL) - goto out; - - BUG_ON(session->magic != L2TP_SESSION_MAGIC); - - /* Don't use pppol2tp_sock_to_tunnel() here to - * get the tunnel context because the tunnel - * socket might have already been closed (its - * sk->sk_user_data will be NULL) so use the - * session's private tunnel ptr instead. - */ - tunnel = session->tunnel; - if (tunnel != NULL) { - BUG_ON(tunnel->magic != L2TP_TUNNEL_MAGIC); - - /* If session_id is zero, this is a null - * session context, which was created for a - * socket that is being used only to manage - * tunnels. - */ - if (session->tunnel_addr.s_session != 0) { - /* Delete the session socket from the - * hash - */ - write_lock_bh(&tunnel->hlist_lock); - hlist_del_init(&session->hlist); - write_unlock_bh(&tunnel->hlist_lock); - - atomic_dec(&pppol2tp_session_count); - } - - /* This will delete the tunnel context if this - * is the last session on the tunnel. - */ - session->tunnel = NULL; - session->tunnel_sock = NULL; - pppol2tp_tunnel_dec_refcount(tunnel); - } - } - - kfree(session); -out: - return; -} - -/* Called when the PPPoX socket (session) is closed. - */ -static int pppol2tp_release(struct socket *sock) -{ - struct sock *sk = sock->sk; - struct pppol2tp_session *session; - int error; - - if (!sk) - return 0; - - error = -EBADF; - lock_sock(sk); - if (sock_flag(sk, SOCK_DEAD) != 0) - goto error; - - pppox_unbind_sock(sk); - - /* Signal the death of the socket. */ - sk->sk_state = PPPOX_DEAD; - sock_orphan(sk); - sock->sk = NULL; - - session = pppol2tp_sock_to_session(sk); - - /* Purge any queued data */ - skb_queue_purge(&sk->sk_receive_queue); - skb_queue_purge(&sk->sk_write_queue); - if (session != NULL) { - struct sk_buff *skb; - while ((skb = skb_dequeue(&session->reorder_q))) { - kfree_skb(skb); - sock_put(sk); - } - sock_put(sk); - } - - release_sock(sk); - - /* This will delete the session context via - * pppol2tp_session_destruct() if the socket's refcnt drops to - * zero. - */ - sock_put(sk); - - return 0; - -error: - release_sock(sk); - return error; -} - -/* Internal function to prepare a tunnel (UDP) socket to have PPPoX - * sockets attached to it. - */ -static struct sock *pppol2tp_prepare_tunnel_socket(struct net *net, - int fd, u16 tunnel_id, int *error) -{ - int err; - struct socket *sock = NULL; - struct sock *sk; - struct pppol2tp_tunnel *tunnel; - struct pppol2tp_net *pn; - struct sock *ret = NULL; - - /* Get the tunnel UDP socket from the fd, which was opened by - * the userspace L2TP daemon. - */ - err = -EBADF; - sock = sockfd_lookup(fd, &err); - if (!sock) { - PRINTK(-1, PPPOL2TP_MSG_CONTROL, KERN_ERR, - "tunl %hu: sockfd_lookup(fd=%d) returned %d\n", - tunnel_id, fd, err); - goto err; - } - - sk = sock->sk; - - /* Quick sanity checks */ - err = -EPROTONOSUPPORT; - if (sk->sk_protocol != IPPROTO_UDP) { - PRINTK(-1, PPPOL2TP_MSG_CONTROL, KERN_ERR, - "tunl %hu: fd %d wrong protocol, got %d, expected %d\n", - tunnel_id, fd, sk->sk_protocol, IPPROTO_UDP); - goto err; - } - err = -EAFNOSUPPORT; - if (sock->ops->family != AF_INET) { - PRINTK(-1, PPPOL2TP_MSG_CONTROL, KERN_ERR, - "tunl %hu: fd %d wrong family, got %d, expected %d\n", - tunnel_id, fd, sock->ops->family, AF_INET); - goto err; - } - - err = -ENOTCONN; - - /* Check if this socket has already been prepped */ - tunnel = (struct pppol2tp_tunnel *)sk->sk_user_data; - if (tunnel != NULL) { - /* User-data field already set */ - err = -EBUSY; - BUG_ON(tunnel->magic != L2TP_TUNNEL_MAGIC); - - /* This socket has already been prepped */ - ret = tunnel->sock; - goto out; - } - - /* This socket is available and needs prepping. Create a new tunnel - * context and init it. - */ - sk->sk_user_data = tunnel = kzalloc(sizeof(struct pppol2tp_tunnel), GFP_KERNEL); - if (sk->sk_user_data == NULL) { - err = -ENOMEM; - goto err; - } - - tunnel->magic = L2TP_TUNNEL_MAGIC; - sprintf(&tunnel->name[0], "tunl %hu", tunnel_id); - - tunnel->stats.tunnel_id = tunnel_id; - tunnel->debug = PPPOL2TP_DEFAULT_DEBUG_FLAGS; - - /* Hook on the tunnel socket destructor so that we can cleanup - * if the tunnel socket goes away. - */ - tunnel->old_sk_destruct = sk->sk_destruct; - sk->sk_destruct = pppol2tp_tunnel_destruct; - - tunnel->sock = sk; - sk->sk_allocation = GFP_ATOMIC; - - /* Misc init */ - rwlock_init(&tunnel->hlist_lock); - - /* The net we belong to */ - tunnel->pppol2tp_net = net; - pn = pppol2tp_pernet(net); - - /* Add tunnel to our list */ - INIT_LIST_HEAD(&tunnel->list); - write_lock_bh(&pn->pppol2tp_tunnel_list_lock); - list_add(&tunnel->list, &pn->pppol2tp_tunnel_list); - write_unlock_bh(&pn->pppol2tp_tunnel_list_lock); - atomic_inc(&pppol2tp_tunnel_count); - - /* Bump the reference count. The tunnel context is deleted - * only when this drops to zero. - */ - pppol2tp_tunnel_inc_refcount(tunnel); - - /* Mark socket as an encapsulation socket. See net/ipv4/udp.c */ - (udp_sk(sk))->encap_type = UDP_ENCAP_L2TPINUDP; - (udp_sk(sk))->encap_rcv = pppol2tp_udp_encap_recv; - - ret = tunnel->sock; - - *error = 0; -out: - if (sock) - sockfd_put(sock); - - return ret; - -err: - *error = err; - goto out; -} - -static struct proto pppol2tp_sk_proto = { - .name = "PPPOL2TP", - .owner = THIS_MODULE, - .obj_size = sizeof(struct pppox_sock), -}; - -/* socket() handler. Initialize a new struct sock. - */ -static int pppol2tp_create(struct net *net, struct socket *sock) -{ - int error = -ENOMEM; - struct sock *sk; - - sk = sk_alloc(net, PF_PPPOX, GFP_KERNEL, &pppol2tp_sk_proto); - if (!sk) - goto out; - - sock_init_data(sock, sk); - - sock->state = SS_UNCONNECTED; - sock->ops = &pppol2tp_ops; - - sk->sk_backlog_rcv = pppol2tp_recv_core; - sk->sk_protocol = PX_PROTO_OL2TP; - sk->sk_family = PF_PPPOX; - sk->sk_state = PPPOX_NONE; - sk->sk_type = SOCK_STREAM; - sk->sk_destruct = pppol2tp_session_destruct; - - error = 0; - -out: - return error; -} - -/* connect() handler. Attach a PPPoX socket to a tunnel UDP socket - */ -static int pppol2tp_connect(struct socket *sock, struct sockaddr *uservaddr, - int sockaddr_len, int flags) -{ - struct sock *sk = sock->sk; - struct sockaddr_pppol2tp *sp = (struct sockaddr_pppol2tp *) uservaddr; - struct pppox_sock *po = pppox_sk(sk); - struct sock *tunnel_sock = NULL; - struct pppol2tp_session *session = NULL; - struct pppol2tp_tunnel *tunnel; - struct dst_entry *dst; - int error = 0; - - lock_sock(sk); - - error = -EINVAL; - if (sp->sa_protocol != PX_PROTO_OL2TP) - goto end; - - /* Check for already bound sockets */ - error = -EBUSY; - if (sk->sk_state & PPPOX_CONNECTED) - goto end; - - /* We don't supporting rebinding anyway */ - error = -EALREADY; - if (sk->sk_user_data) - goto end; /* socket is already attached */ - - /* Don't bind if s_tunnel is 0 */ - error = -EINVAL; - if (sp->pppol2tp.s_tunnel == 0) - goto end; - - /* Special case: prepare tunnel socket if s_session and - * d_session is 0. Otherwise look up tunnel using supplied - * tunnel id. - */ - if ((sp->pppol2tp.s_session == 0) && (sp->pppol2tp.d_session == 0)) { - tunnel_sock = pppol2tp_prepare_tunnel_socket(sock_net(sk), - sp->pppol2tp.fd, - sp->pppol2tp.s_tunnel, - &error); - if (tunnel_sock == NULL) - goto end; - - sock_hold(tunnel_sock); - tunnel = tunnel_sock->sk_user_data; - } else { - tunnel = pppol2tp_tunnel_find(sock_net(sk), sp->pppol2tp.s_tunnel); - - /* Error if we can't find the tunnel */ - error = -ENOENT; - if (tunnel == NULL) - goto end; - - tunnel_sock = tunnel->sock; - } - - /* Check that this session doesn't already exist */ - error = -EEXIST; - session = pppol2tp_session_find(tunnel, sp->pppol2tp.s_session); - if (session != NULL) - goto end; - - /* Allocate and initialize a new session context. */ - session = kzalloc(sizeof(struct pppol2tp_session), GFP_KERNEL); - if (session == NULL) { - error = -ENOMEM; - goto end; - } - - skb_queue_head_init(&session->reorder_q); - - session->magic = L2TP_SESSION_MAGIC; - session->owner = current->pid; - session->sock = sk; - session->tunnel = tunnel; - session->tunnel_sock = tunnel_sock; - session->tunnel_addr = sp->pppol2tp; - sprintf(&session->name[0], "sess %hu/%hu", - session->tunnel_addr.s_tunnel, - session->tunnel_addr.s_session); - - session->stats.tunnel_id = session->tunnel_addr.s_tunnel; - session->stats.session_id = session->tunnel_addr.s_session; - - INIT_HLIST_NODE(&session->hlist); - - /* Inherit debug options from tunnel */ - session->debug = tunnel->debug; - - /* Default MTU must allow space for UDP/L2TP/PPP - * headers. - */ - session->mtu = session->mru = 1500 - PPPOL2TP_HEADER_OVERHEAD; - - /* If PMTU discovery was enabled, use the MTU that was discovered */ - dst = sk_dst_get(sk); - if (dst != NULL) { - u32 pmtu = dst_mtu(__sk_dst_get(sk)); - if (pmtu != 0) - session->mtu = session->mru = pmtu - - PPPOL2TP_HEADER_OVERHEAD; - dst_release(dst); - } - - /* Special case: if source & dest session_id == 0x0000, this socket is - * being created to manage the tunnel. Don't add the session to the - * session hash list, just set up the internal context for use by - * ioctl() and sockopt() handlers. - */ - if ((session->tunnel_addr.s_session == 0) && - (session->tunnel_addr.d_session == 0)) { - error = 0; - sk->sk_user_data = session; - goto out_no_ppp; - } - - /* Get tunnel context from the tunnel socket */ - tunnel = pppol2tp_sock_to_tunnel(tunnel_sock); - if (tunnel == NULL) { - error = -EBADF; - goto end; - } - - /* Right now, because we don't have a way to push the incoming skb's - * straight through the UDP layer, the only header we need to worry - * about is the L2TP header. This size is different depending on - * whether sequence numbers are enabled for the data channel. - */ - po->chan.hdrlen = PPPOL2TP_L2TP_HDR_SIZE_NOSEQ; - - po->chan.private = sk; - po->chan.ops = &pppol2tp_chan_ops; - po->chan.mtu = session->mtu; - - error = ppp_register_net_channel(sock_net(sk), &po->chan); - if (error) - goto end_put_tun; - - /* This is how we get the session context from the socket. */ - sk->sk_user_data = session; - - /* Add session to the tunnel's hash list */ - write_lock_bh(&tunnel->hlist_lock); - hlist_add_head(&session->hlist, - pppol2tp_session_id_hash(tunnel, - session->tunnel_addr.s_session)); - write_unlock_bh(&tunnel->hlist_lock); - - atomic_inc(&pppol2tp_session_count); - -out_no_ppp: - pppol2tp_tunnel_inc_refcount(tunnel); - sk->sk_state = PPPOX_CONNECTED; - PRINTK(session->debug, PPPOL2TP_MSG_CONTROL, KERN_INFO, - "%s: created\n", session->name); - -end_put_tun: - sock_put(tunnel_sock); -end: - release_sock(sk); - - if (error != 0) { - if (session) - PRINTK(session->debug, - PPPOL2TP_MSG_CONTROL, KERN_WARNING, - "%s: connect failed: %d\n", - session->name, error); - else - PRINTK(-1, PPPOL2TP_MSG_CONTROL, KERN_WARNING, - "connect failed: %d\n", error); - } - - return error; -} - -/* getname() support. - */ -static int pppol2tp_getname(struct socket *sock, struct sockaddr *uaddr, - int *usockaddr_len, int peer) -{ - int len = sizeof(struct sockaddr_pppol2tp); - struct sockaddr_pppol2tp sp; - int error = 0; - struct pppol2tp_session *session; - - error = -ENOTCONN; - if (sock->sk->sk_state != PPPOX_CONNECTED) - goto end; - - session = pppol2tp_sock_to_session(sock->sk); - if (session == NULL) { - error = -EBADF; - goto end; - } - - sp.sa_family = AF_PPPOX; - sp.sa_protocol = PX_PROTO_OL2TP; - memcpy(&sp.pppol2tp, &session->tunnel_addr, - sizeof(struct pppol2tp_addr)); - - memcpy(uaddr, &sp, len); - - *usockaddr_len = len; - - error = 0; - sock_put(sock->sk); - -end: - return error; -} - -/**************************************************************************** - * ioctl() handlers. - * - * The PPPoX socket is created for L2TP sessions: tunnels have their own UDP - * sockets. However, in order to control kernel tunnel features, we allow - * userspace to create a special "tunnel" PPPoX socket which is used for - * control only. Tunnel PPPoX sockets have session_id == 0 and simply allow - * the user application to issue L2TP setsockopt(), getsockopt() and ioctl() - * calls. - ****************************************************************************/ - -/* Session ioctl helper. - */ -static int pppol2tp_session_ioctl(struct pppol2tp_session *session, - unsigned int cmd, unsigned long arg) -{ - struct ifreq ifr; - int err = 0; - struct sock *sk = session->sock; - int val = (int) arg; - - PRINTK(session->debug, PPPOL2TP_MSG_CONTROL, KERN_DEBUG, - "%s: pppol2tp_session_ioctl(cmd=%#x, arg=%#lx)\n", - session->name, cmd, arg); - - sock_hold(sk); - - switch (cmd) { - case SIOCGIFMTU: - err = -ENXIO; - if (!(sk->sk_state & PPPOX_CONNECTED)) - break; - - err = -EFAULT; - if (copy_from_user(&ifr, (void __user *) arg, sizeof(struct ifreq))) - break; - ifr.ifr_mtu = session->mtu; - if (copy_to_user((void __user *) arg, &ifr, sizeof(struct ifreq))) - break; - - PRINTK(session->debug, PPPOL2TP_MSG_CONTROL, KERN_INFO, - "%s: get mtu=%d\n", session->name, session->mtu); - err = 0; - break; - - case SIOCSIFMTU: - err = -ENXIO; - if (!(sk->sk_state & PPPOX_CONNECTED)) - break; - - err = -EFAULT; - if (copy_from_user(&ifr, (void __user *) arg, sizeof(struct ifreq))) - break; - - session->mtu = ifr.ifr_mtu; - - PRINTK(session->debug, PPPOL2TP_MSG_CONTROL, KERN_INFO, - "%s: set mtu=%d\n", session->name, session->mtu); - err = 0; - break; - - case PPPIOCGMRU: - err = -ENXIO; - if (!(sk->sk_state & PPPOX_CONNECTED)) - break; - - err = -EFAULT; - if (put_user(session->mru, (int __user *) arg)) - break; - - PRINTK(session->debug, PPPOL2TP_MSG_CONTROL, KERN_INFO, - "%s: get mru=%d\n", session->name, session->mru); - err = 0; - break; - - case PPPIOCSMRU: - err = -ENXIO; - if (!(sk->sk_state & PPPOX_CONNECTED)) - break; - - err = -EFAULT; - if (get_user(val,(int __user *) arg)) - break; - - session->mru = val; - PRINTK(session->debug, PPPOL2TP_MSG_CONTROL, KERN_INFO, - "%s: set mru=%d\n", session->name, session->mru); - err = 0; - break; - - case PPPIOCGFLAGS: - err = -EFAULT; - if (put_user(session->flags, (int __user *) arg)) - break; - - PRINTK(session->debug, PPPOL2TP_MSG_CONTROL, KERN_INFO, - "%s: get flags=%d\n", session->name, session->flags); - err = 0; - break; - - case PPPIOCSFLAGS: - err = -EFAULT; - if (get_user(val, (int __user *) arg)) - break; - session->flags = val; - PRINTK(session->debug, PPPOL2TP_MSG_CONTROL, KERN_INFO, - "%s: set flags=%d\n", session->name, session->flags); - err = 0; - break; - - case PPPIOCGL2TPSTATS: - err = -ENXIO; - if (!(sk->sk_state & PPPOX_CONNECTED)) - break; - - if (copy_to_user((void __user *) arg, &session->stats, - sizeof(session->stats))) - break; - PRINTK(session->debug, PPPOL2TP_MSG_CONTROL, KERN_INFO, - "%s: get L2TP stats\n", session->name); - err = 0; - break; - - default: - err = -ENOSYS; - break; - } - - sock_put(sk); - - return err; -} - -/* Tunnel ioctl helper. - * - * Note the special handling for PPPIOCGL2TPSTATS below. If the ioctl data - * specifies a session_id, the session ioctl handler is called. This allows an - * application to retrieve session stats via a tunnel socket. - */ -static int pppol2tp_tunnel_ioctl(struct pppol2tp_tunnel *tunnel, - unsigned int cmd, unsigned long arg) -{ - int err = 0; - struct sock *sk = tunnel->sock; - struct pppol2tp_ioc_stats stats_req; - - PRINTK(tunnel->debug, PPPOL2TP_MSG_CONTROL, KERN_DEBUG, - "%s: pppol2tp_tunnel_ioctl(cmd=%#x, arg=%#lx)\n", tunnel->name, - cmd, arg); - - sock_hold(sk); - - switch (cmd) { - case PPPIOCGL2TPSTATS: - err = -ENXIO; - if (!(sk->sk_state & PPPOX_CONNECTED)) - break; - - if (copy_from_user(&stats_req, (void __user *) arg, - sizeof(stats_req))) { - err = -EFAULT; - break; - } - if (stats_req.session_id != 0) { - /* resend to session ioctl handler */ - struct pppol2tp_session *session = - pppol2tp_session_find(tunnel, stats_req.session_id); - if (session != NULL) - err = pppol2tp_session_ioctl(session, cmd, arg); - else - err = -EBADR; - break; - } -#ifdef CONFIG_XFRM - tunnel->stats.using_ipsec = (sk->sk_policy[0] || sk->sk_policy[1]) ? 1 : 0; -#endif - if (copy_to_user((void __user *) arg, &tunnel->stats, - sizeof(tunnel->stats))) { - err = -EFAULT; - break; - } - PRINTK(tunnel->debug, PPPOL2TP_MSG_CONTROL, KERN_INFO, - "%s: get L2TP stats\n", tunnel->name); - err = 0; - break; - - default: - err = -ENOSYS; - break; - } - - sock_put(sk); - - return err; -} - -/* Main ioctl() handler. - * Dispatch to tunnel or session helpers depending on the socket. - */ -static int pppol2tp_ioctl(struct socket *sock, unsigned int cmd, - unsigned long arg) -{ - struct sock *sk = sock->sk; - struct pppol2tp_session *session; - struct pppol2tp_tunnel *tunnel; - int err; - - if (!sk) - return 0; - - err = -EBADF; - if (sock_flag(sk, SOCK_DEAD) != 0) - goto end; - - err = -ENOTCONN; - if ((sk->sk_user_data == NULL) || - (!(sk->sk_state & (PPPOX_CONNECTED | PPPOX_BOUND)))) - goto end; - - /* Get session context from the socket */ - err = -EBADF; - session = pppol2tp_sock_to_session(sk); - if (session == NULL) - goto end; - - /* Special case: if session's session_id is zero, treat ioctl as a - * tunnel ioctl - */ - if ((session->tunnel_addr.s_session == 0) && - (session->tunnel_addr.d_session == 0)) { - err = -EBADF; - tunnel = pppol2tp_sock_to_tunnel(session->tunnel_sock); - if (tunnel == NULL) - goto end_put_sess; - - err = pppol2tp_tunnel_ioctl(tunnel, cmd, arg); - sock_put(session->tunnel_sock); - goto end_put_sess; - } - - err = pppol2tp_session_ioctl(session, cmd, arg); - -end_put_sess: - sock_put(sk); -end: - return err; -} - -/***************************************************************************** - * setsockopt() / getsockopt() support. - * - * The PPPoX socket is created for L2TP sessions: tunnels have their own UDP - * sockets. In order to control kernel tunnel features, we allow userspace to - * create a special "tunnel" PPPoX socket which is used for control only. - * Tunnel PPPoX sockets have session_id == 0 and simply allow the user - * application to issue L2TP setsockopt(), getsockopt() and ioctl() calls. - *****************************************************************************/ - -/* Tunnel setsockopt() helper. - */ -static int pppol2tp_tunnel_setsockopt(struct sock *sk, - struct pppol2tp_tunnel *tunnel, - int optname, int val) -{ - int err = 0; - - switch (optname) { - case PPPOL2TP_SO_DEBUG: - tunnel->debug = val; - PRINTK(tunnel->debug, PPPOL2TP_MSG_CONTROL, KERN_INFO, - "%s: set debug=%x\n", tunnel->name, tunnel->debug); - break; - - default: - err = -ENOPROTOOPT; - break; - } - - return err; -} - -/* Session setsockopt helper. - */ -static int pppol2tp_session_setsockopt(struct sock *sk, - struct pppol2tp_session *session, - int optname, int val) -{ - int err = 0; - - switch (optname) { - case PPPOL2TP_SO_RECVSEQ: - if ((val != 0) && (val != 1)) { - err = -EINVAL; - break; - } - session->recv_seq = val ? -1 : 0; - PRINTK(session->debug, PPPOL2TP_MSG_CONTROL, KERN_INFO, - "%s: set recv_seq=%d\n", session->name, - session->recv_seq); - break; - - case PPPOL2TP_SO_SENDSEQ: - if ((val != 0) && (val != 1)) { - err = -EINVAL; - break; - } - session->send_seq = val ? -1 : 0; - { - struct sock *ssk = session->sock; - struct pppox_sock *po = pppox_sk(ssk); - po->chan.hdrlen = val ? PPPOL2TP_L2TP_HDR_SIZE_SEQ : - PPPOL2TP_L2TP_HDR_SIZE_NOSEQ; - } - PRINTK(session->debug, PPPOL2TP_MSG_CONTROL, KERN_INFO, - "%s: set send_seq=%d\n", session->name, session->send_seq); - break; - - case PPPOL2TP_SO_LNSMODE: - if ((val != 0) && (val != 1)) { - err = -EINVAL; - break; - } - session->lns_mode = val ? -1 : 0; - PRINTK(session->debug, PPPOL2TP_MSG_CONTROL, KERN_INFO, - "%s: set lns_mode=%d\n", session->name, - session->lns_mode); - break; - - case PPPOL2TP_SO_DEBUG: - session->debug = val; - PRINTK(session->debug, PPPOL2TP_MSG_CONTROL, KERN_INFO, - "%s: set debug=%x\n", session->name, session->debug); - break; - - case PPPOL2TP_SO_REORDERTO: - session->reorder_timeout = msecs_to_jiffies(val); - PRINTK(session->debug, PPPOL2TP_MSG_CONTROL, KERN_INFO, - "%s: set reorder_timeout=%d\n", session->name, - session->reorder_timeout); - break; - - default: - err = -ENOPROTOOPT; - break; - } - - return err; -} - -/* Main setsockopt() entry point. - * Does API checks, then calls either the tunnel or session setsockopt - * handler, according to whether the PPPoL2TP socket is a for a regular - * session or the special tunnel type. - */ -static int pppol2tp_setsockopt(struct socket *sock, int level, int optname, - char __user *optval, unsigned int optlen) -{ - struct sock *sk = sock->sk; - struct pppol2tp_session *session = sk->sk_user_data; - struct pppol2tp_tunnel *tunnel; - int val; - int err; - - if (level != SOL_PPPOL2TP) - return udp_prot.setsockopt(sk, level, optname, optval, optlen); - - if (optlen < sizeof(int)) - return -EINVAL; - - if (get_user(val, (int __user *)optval)) - return -EFAULT; - - err = -ENOTCONN; - if (sk->sk_user_data == NULL) - goto end; - - /* Get session context from the socket */ - err = -EBADF; - session = pppol2tp_sock_to_session(sk); - if (session == NULL) - goto end; - - /* Special case: if session_id == 0x0000, treat as operation on tunnel - */ - if ((session->tunnel_addr.s_session == 0) && - (session->tunnel_addr.d_session == 0)) { - err = -EBADF; - tunnel = pppol2tp_sock_to_tunnel(session->tunnel_sock); - if (tunnel == NULL) - goto end_put_sess; - - err = pppol2tp_tunnel_setsockopt(sk, tunnel, optname, val); - sock_put(session->tunnel_sock); - } else - err = pppol2tp_session_setsockopt(sk, session, optname, val); - - err = 0; - -end_put_sess: - sock_put(sk); -end: - return err; -} - -/* Tunnel getsockopt helper. Called with sock locked. - */ -static int pppol2tp_tunnel_getsockopt(struct sock *sk, - struct pppol2tp_tunnel *tunnel, - int optname, int *val) -{ - int err = 0; - - switch (optname) { - case PPPOL2TP_SO_DEBUG: - *val = tunnel->debug; - PRINTK(tunnel->debug, PPPOL2TP_MSG_CONTROL, KERN_INFO, - "%s: get debug=%x\n", tunnel->name, tunnel->debug); - break; - - default: - err = -ENOPROTOOPT; - break; - } - - return err; -} - -/* Session getsockopt helper. Called with sock locked. - */ -static int pppol2tp_session_getsockopt(struct sock *sk, - struct pppol2tp_session *session, - int optname, int *val) -{ - int err = 0; - - switch (optname) { - case PPPOL2TP_SO_RECVSEQ: - *val = session->recv_seq; - PRINTK(session->debug, PPPOL2TP_MSG_CONTROL, KERN_INFO, - "%s: get recv_seq=%d\n", session->name, *val); - break; - - case PPPOL2TP_SO_SENDSEQ: - *val = session->send_seq; - PRINTK(session->debug, PPPOL2TP_MSG_CONTROL, KERN_INFO, - "%s: get send_seq=%d\n", session->name, *val); - break; - - case PPPOL2TP_SO_LNSMODE: - *val = session->lns_mode; - PRINTK(session->debug, PPPOL2TP_MSG_CONTROL, KERN_INFO, - "%s: get lns_mode=%d\n", session->name, *val); - break; - - case PPPOL2TP_SO_DEBUG: - *val = session->debug; - PRINTK(session->debug, PPPOL2TP_MSG_CONTROL, KERN_INFO, - "%s: get debug=%d\n", session->name, *val); - break; - - case PPPOL2TP_SO_REORDERTO: - *val = (int) jiffies_to_msecs(session->reorder_timeout); - PRINTK(session->debug, PPPOL2TP_MSG_CONTROL, KERN_INFO, - "%s: get reorder_timeout=%d\n", session->name, *val); - break; - - default: - err = -ENOPROTOOPT; - } - - return err; -} - -/* Main getsockopt() entry point. - * Does API checks, then calls either the tunnel or session getsockopt - * handler, according to whether the PPPoX socket is a for a regular session - * or the special tunnel type. - */ -static int pppol2tp_getsockopt(struct socket *sock, int level, - int optname, char __user *optval, int __user *optlen) -{ - struct sock *sk = sock->sk; - struct pppol2tp_session *session = sk->sk_user_data; - struct pppol2tp_tunnel *tunnel; - int val, len; - int err; - - if (level != SOL_PPPOL2TP) - return udp_prot.getsockopt(sk, level, optname, optval, optlen); - - if (get_user(len, (int __user *) optlen)) - return -EFAULT; - - len = min_t(unsigned int, len, sizeof(int)); - - if (len < 0) - return -EINVAL; - - err = -ENOTCONN; - if (sk->sk_user_data == NULL) - goto end; - - /* Get the session context */ - err = -EBADF; - session = pppol2tp_sock_to_session(sk); - if (session == NULL) - goto end; - - /* Special case: if session_id == 0x0000, treat as operation on tunnel */ - if ((session->tunnel_addr.s_session == 0) && - (session->tunnel_addr.d_session == 0)) { - err = -EBADF; - tunnel = pppol2tp_sock_to_tunnel(session->tunnel_sock); - if (tunnel == NULL) - goto end_put_sess; - - err = pppol2tp_tunnel_getsockopt(sk, tunnel, optname, &val); - sock_put(session->tunnel_sock); - } else - err = pppol2tp_session_getsockopt(sk, session, optname, &val); - - err = -EFAULT; - if (put_user(len, (int __user *) optlen)) - goto end_put_sess; - - if (copy_to_user((void __user *) optval, &val, len)) - goto end_put_sess; - - err = 0; - -end_put_sess: - sock_put(sk); -end: - return err; -} - -/***************************************************************************** - * /proc filesystem for debug - *****************************************************************************/ - -#ifdef CONFIG_PROC_FS - -#include - -struct pppol2tp_seq_data { - struct seq_net_private p; - struct pppol2tp_tunnel *tunnel; /* current tunnel */ - struct pppol2tp_session *session; /* NULL means get first session in tunnel */ -}; - -static struct pppol2tp_session *next_session(struct pppol2tp_tunnel *tunnel, struct pppol2tp_session *curr) -{ - struct pppol2tp_session *session = NULL; - struct hlist_node *walk; - int found = 0; - int next = 0; - int i; - - read_lock_bh(&tunnel->hlist_lock); - for (i = 0; i < PPPOL2TP_HASH_SIZE; i++) { - hlist_for_each_entry(session, walk, &tunnel->session_hlist[i], hlist) { - if (curr == NULL) { - found = 1; - goto out; - } - if (session == curr) { - next = 1; - continue; - } - if (next) { - found = 1; - goto out; - } - } - } -out: - read_unlock_bh(&tunnel->hlist_lock); - if (!found) - session = NULL; - - return session; -} - -static struct pppol2tp_tunnel *next_tunnel(struct pppol2tp_net *pn, - struct pppol2tp_tunnel *curr) -{ - struct pppol2tp_tunnel *tunnel = NULL; - - read_lock_bh(&pn->pppol2tp_tunnel_list_lock); - if (list_is_last(&curr->list, &pn->pppol2tp_tunnel_list)) { - goto out; - } - tunnel = list_entry(curr->list.next, struct pppol2tp_tunnel, list); -out: - read_unlock_bh(&pn->pppol2tp_tunnel_list_lock); - - return tunnel; -} - -static void *pppol2tp_seq_start(struct seq_file *m, loff_t *offs) -{ - struct pppol2tp_seq_data *pd = SEQ_START_TOKEN; - struct pppol2tp_net *pn; - loff_t pos = *offs; - - if (!pos) - goto out; - - BUG_ON(m->private == NULL); - pd = m->private; - pn = pppol2tp_pernet(seq_file_net(m)); - - if (pd->tunnel == NULL) { - if (!list_empty(&pn->pppol2tp_tunnel_list)) - pd->tunnel = list_entry(pn->pppol2tp_tunnel_list.next, struct pppol2tp_tunnel, list); - } else { - pd->session = next_session(pd->tunnel, pd->session); - if (pd->session == NULL) { - pd->tunnel = next_tunnel(pn, pd->tunnel); - } - } - - /* NULL tunnel and session indicates end of list */ - if ((pd->tunnel == NULL) && (pd->session == NULL)) - pd = NULL; - -out: - return pd; -} - -static void *pppol2tp_seq_next(struct seq_file *m, void *v, loff_t *pos) -{ - (*pos)++; - return NULL; -} - -static void pppol2tp_seq_stop(struct seq_file *p, void *v) -{ - /* nothing to do */ -} - -static void pppol2tp_seq_tunnel_show(struct seq_file *m, void *v) -{ - struct pppol2tp_tunnel *tunnel = v; - - seq_printf(m, "\nTUNNEL '%s', %c %d\n", - tunnel->name, - (tunnel == tunnel->sock->sk_user_data) ? 'Y':'N', - atomic_read(&tunnel->ref_count) - 1); - seq_printf(m, " %08x %llu/%llu/%llu %llu/%llu/%llu\n", - tunnel->debug, - (unsigned long long)tunnel->stats.tx_packets, - (unsigned long long)tunnel->stats.tx_bytes, - (unsigned long long)tunnel->stats.tx_errors, - (unsigned long long)tunnel->stats.rx_packets, - (unsigned long long)tunnel->stats.rx_bytes, - (unsigned long long)tunnel->stats.rx_errors); -} - -static void pppol2tp_seq_session_show(struct seq_file *m, void *v) -{ - struct pppol2tp_session *session = v; - - seq_printf(m, " SESSION '%s' %08X/%d %04X/%04X -> " - "%04X/%04X %d %c\n", - session->name, - ntohl(session->tunnel_addr.addr.sin_addr.s_addr), - ntohs(session->tunnel_addr.addr.sin_port), - session->tunnel_addr.s_tunnel, - session->tunnel_addr.s_session, - session->tunnel_addr.d_tunnel, - session->tunnel_addr.d_session, - session->sock->sk_state, - (session == session->sock->sk_user_data) ? - 'Y' : 'N'); - seq_printf(m, " %d/%d/%c/%c/%s %08x %u\n", - session->mtu, session->mru, - session->recv_seq ? 'R' : '-', - session->send_seq ? 'S' : '-', - session->lns_mode ? "LNS" : "LAC", - session->debug, - jiffies_to_msecs(session->reorder_timeout)); - seq_printf(m, " %hu/%hu %llu/%llu/%llu %llu/%llu/%llu\n", - session->nr, session->ns, - (unsigned long long)session->stats.tx_packets, - (unsigned long long)session->stats.tx_bytes, - (unsigned long long)session->stats.tx_errors, - (unsigned long long)session->stats.rx_packets, - (unsigned long long)session->stats.rx_bytes, - (unsigned long long)session->stats.rx_errors); -} - -static int pppol2tp_seq_show(struct seq_file *m, void *v) -{ - struct pppol2tp_seq_data *pd = v; - - /* display header on line 1 */ - if (v == SEQ_START_TOKEN) { - seq_puts(m, "PPPoL2TP driver info, " PPPOL2TP_DRV_VERSION "\n"); - seq_puts(m, "TUNNEL name, user-data-ok session-count\n"); - seq_puts(m, " debug tx-pkts/bytes/errs rx-pkts/bytes/errs\n"); - seq_puts(m, " SESSION name, addr/port src-tid/sid " - "dest-tid/sid state user-data-ok\n"); - seq_puts(m, " mtu/mru/rcvseq/sendseq/lns debug reorderto\n"); - seq_puts(m, " nr/ns tx-pkts/bytes/errs rx-pkts/bytes/errs\n"); - goto out; - } - - /* Show the tunnel or session context. - */ - if (pd->session == NULL) - pppol2tp_seq_tunnel_show(m, pd->tunnel); - else - pppol2tp_seq_session_show(m, pd->session); - -out: - return 0; -} - -static const struct seq_operations pppol2tp_seq_ops = { - .start = pppol2tp_seq_start, - .next = pppol2tp_seq_next, - .stop = pppol2tp_seq_stop, - .show = pppol2tp_seq_show, -}; - -/* Called when our /proc file is opened. We allocate data for use when - * iterating our tunnel / session contexts and store it in the private - * data of the seq_file. - */ -static int pppol2tp_proc_open(struct inode *inode, struct file *file) -{ - return seq_open_net(inode, file, &pppol2tp_seq_ops, - sizeof(struct pppol2tp_seq_data)); -} - -static const struct file_operations pppol2tp_proc_fops = { - .owner = THIS_MODULE, - .open = pppol2tp_proc_open, - .read = seq_read, - .llseek = seq_lseek, - .release = seq_release_net, -}; - -#endif /* CONFIG_PROC_FS */ - -/***************************************************************************** - * Init and cleanup - *****************************************************************************/ - -static const struct proto_ops pppol2tp_ops = { - .family = AF_PPPOX, - .owner = THIS_MODULE, - .release = pppol2tp_release, - .bind = sock_no_bind, - .connect = pppol2tp_connect, - .socketpair = sock_no_socketpair, - .accept = sock_no_accept, - .getname = pppol2tp_getname, - .poll = datagram_poll, - .listen = sock_no_listen, - .shutdown = sock_no_shutdown, - .setsockopt = pppol2tp_setsockopt, - .getsockopt = pppol2tp_getsockopt, - .sendmsg = pppol2tp_sendmsg, - .recvmsg = pppol2tp_recvmsg, - .mmap = sock_no_mmap, - .ioctl = pppox_ioctl, -}; - -static struct pppox_proto pppol2tp_proto = { - .create = pppol2tp_create, - .ioctl = pppol2tp_ioctl -}; - -static __net_init int pppol2tp_init_net(struct net *net) -{ - struct pppol2tp_net *pn = pppol2tp_pernet(net); - struct proc_dir_entry *pde; - - INIT_LIST_HEAD(&pn->pppol2tp_tunnel_list); - rwlock_init(&pn->pppol2tp_tunnel_list_lock); - - pde = proc_net_fops_create(net, "pppol2tp", S_IRUGO, &pppol2tp_proc_fops); -#ifdef CONFIG_PROC_FS - if (!pde) - return -ENOMEM; -#endif - - return 0; -} - -static __net_exit void pppol2tp_exit_net(struct net *net) -{ - proc_net_remove(net, "pppol2tp"); -} - -static struct pernet_operations pppol2tp_net_ops = { - .init = pppol2tp_init_net, - .exit = pppol2tp_exit_net, - .id = &pppol2tp_net_id, - .size = sizeof(struct pppol2tp_net), -}; - -static int __init pppol2tp_init(void) -{ - int err; - - err = proto_register(&pppol2tp_sk_proto, 0); - if (err) - goto out; - err = register_pppox_proto(PX_PROTO_OL2TP, &pppol2tp_proto); - if (err) - goto out_unregister_pppol2tp_proto; - - err = register_pernet_device(&pppol2tp_net_ops); - if (err) - goto out_unregister_pppox_proto; - - printk(KERN_INFO "PPPoL2TP kernel driver, %s\n", - PPPOL2TP_DRV_VERSION); - -out: - return err; -out_unregister_pppox_proto: - unregister_pppox_proto(PX_PROTO_OL2TP); -out_unregister_pppol2tp_proto: - proto_unregister(&pppol2tp_sk_proto); - goto out; -} - -static void __exit pppol2tp_exit(void) -{ - unregister_pppox_proto(PX_PROTO_OL2TP); - unregister_pernet_device(&pppol2tp_net_ops); - proto_unregister(&pppol2tp_sk_proto); -} - -module_init(pppol2tp_init); -module_exit(pppol2tp_exit); - -MODULE_AUTHOR("Martijn van Oosterhout , " - "James Chapman "); -MODULE_DESCRIPTION("PPP over L2TP over UDP"); -MODULE_LICENSE("GPL"); -MODULE_VERSION(PPPOL2TP_DRV_VERSION); -- cgit v1.2.3 From 63f96072f9ba430aa348bc987c3d5a4f48bae301 Mon Sep 17 00:00:00 2001 From: James Chapman Date: Fri, 2 Apr 2010 06:18:39 +0000 Subject: ppp: Add ppp_dev_name() exported function ppp_dev_name() gives PPP users visibility of a ppp channel's device name. This can be used by L2TP drivers to dump the assigned PPP interface name. Signed-off-by: James Chapman Reviewed-by: Randy Dunlap Signed-off-by: David S. Miller --- drivers/net/ppp_generic.c | 19 +++++++++++++++++++ include/linux/ppp_channel.h | 3 +++ 2 files changed, 22 insertions(+) (limited to 'include/linux') diff --git a/drivers/net/ppp_generic.c b/drivers/net/ppp_generic.c index 6d61602208c..8a0dd8a843a 100644 --- a/drivers/net/ppp_generic.c +++ b/drivers/net/ppp_generic.c @@ -2162,6 +2162,24 @@ int ppp_unit_number(struct ppp_channel *chan) return unit; } +/* + * Return the PPP device interface name of a channel. + */ +char *ppp_dev_name(struct ppp_channel *chan) +{ + struct channel *pch = chan->ppp; + char *name = NULL; + + if (pch) { + read_lock_bh(&pch->upl); + if (pch->ppp && pch->ppp->dev) + name = pch->ppp->dev->name; + read_unlock_bh(&pch->upl); + } + return name; +} + + /* * Disconnect a channel from the generic layer. * This must be called in process context. @@ -2890,6 +2908,7 @@ EXPORT_SYMBOL(ppp_register_channel); EXPORT_SYMBOL(ppp_unregister_channel); EXPORT_SYMBOL(ppp_channel_index); EXPORT_SYMBOL(ppp_unit_number); +EXPORT_SYMBOL(ppp_dev_name); EXPORT_SYMBOL(ppp_input); EXPORT_SYMBOL(ppp_input_error); EXPORT_SYMBOL(ppp_output_wakeup); diff --git a/include/linux/ppp_channel.h b/include/linux/ppp_channel.h index 0d3fa63e90e..bff98ec1bfe 100644 --- a/include/linux/ppp_channel.h +++ b/include/linux/ppp_channel.h @@ -72,6 +72,9 @@ extern int ppp_channel_index(struct ppp_channel *); /* Get the unit number associated with a channel, or -1 if none */ extern int ppp_unit_number(struct ppp_channel *); +/* Get the device name associated with a channel, or NULL if none */ +extern char *ppp_dev_name(struct ppp_channel *); + /* * SMP locking notes: * The channel code must ensure that when it calls ppp_unregister_channel, -- cgit v1.2.3 From e0d4435f93905f517003cfa7328a36ea19788147 Mon Sep 17 00:00:00 2001 From: James Chapman Date: Fri, 2 Apr 2010 06:18:54 +0000 Subject: l2tp: Update PPP-over-L2TP driver to work over L2TPv3 This patch makes changes to the L2TP PPP code for L2TPv3. The existing code has some assumptions about the L2TP header which are broken by L2TPv3. Also the sockaddr_pppol2tp structure of the original code is too small to support the increased size of the L2TPv3 tunnel and session id, so a new sockaddr_pppol2tpv3 structure is needed. In the socket calls, the size of this structure is used to tell if the operation is for L2TPv2 or L2TPv3. Signed-off-by: James Chapman Reviewed-by: Randy Dunlap Signed-off-by: David S. Miller --- include/linux/if_pppol2tp.h | 14 ++++++ include/linux/if_pppox.h | 9 ++++ net/l2tp/l2tp_ppp.c | 120 +++++++++++++++++++++++++++----------------- 3 files changed, 97 insertions(+), 46 deletions(-) (limited to 'include/linux') diff --git a/include/linux/if_pppol2tp.h b/include/linux/if_pppol2tp.h index 1a1fb6e5d93..184bc556620 100644 --- a/include/linux/if_pppol2tp.h +++ b/include/linux/if_pppol2tp.h @@ -35,6 +35,20 @@ struct pppol2tp_addr { __u16 d_tunnel, d_session; /* For sending outgoing packets */ }; +/* The L2TPv3 protocol changes tunnel and session ids from 16 to 32 + * bits. So we need a different sockaddr structure. + */ +struct pppol2tpv3_addr { + pid_t pid; /* pid that owns the fd. + * 0 => current */ + int fd; /* FD of UDP or IP socket to use */ + + struct sockaddr_in addr; /* IP address and port to send to */ + + __u32 s_tunnel, s_session; /* For matching incoming packets */ + __u32 d_tunnel, d_session; /* For sending outgoing packets */ +}; + /* Socket options: * DEBUG - bitmask of debug message categories * SENDSEQ - 0 => don't send packets with sequence numbers diff --git a/include/linux/if_pppox.h b/include/linux/if_pppox.h index 90b5fae5d71..a6577af0c4e 100644 --- a/include/linux/if_pppox.h +++ b/include/linux/if_pppox.h @@ -72,6 +72,15 @@ struct sockaddr_pppol2tp { struct pppol2tp_addr pppol2tp; }__attribute__ ((packed)); +/* The L2TPv3 protocol changes tunnel and session ids from 16 to 32 + * bits. So we need a different sockaddr structure. + */ +struct sockaddr_pppol2tpv3 { + sa_family_t sa_family; /* address family, AF_PPPOX */ + unsigned int sa_protocol; /* protocol identifier */ + struct pppol2tpv3_addr pppol2tp; +} __attribute__ ((packed)); + /********************************************************************* * * ioctl interface for defining forwarding of connections diff --git a/net/l2tp/l2tp_ppp.c b/net/l2tp/l2tp_ppp.c index bee5b1413ec..e5b53126654 100644 --- a/net/l2tp/l2tp_ppp.c +++ b/net/l2tp/l2tp_ppp.c @@ -291,17 +291,6 @@ static void pppol2tp_session_sock_put(struct l2tp_session *session) * Transmit handling ***********************************************************************/ -/* Tell how big L2TP headers are for a particular session. This - * depends on whether sequence numbers are being used. - */ -static inline int pppol2tp_l2tp_header_len(struct l2tp_session *session) -{ - if (session->send_seq) - return PPPOL2TP_L2TP_HDR_SIZE_SEQ; - - return PPPOL2TP_L2TP_HDR_SIZE_NOSEQ; -} - /* This is the sendmsg for the PPPoL2TP pppol2tp_session socket. We come here * when a user application does a sendmsg() on the session socket. L2TP and * PPP headers must be inserted into the user's data. @@ -394,7 +383,6 @@ static int pppol2tp_xmit(struct ppp_channel *chan, struct sk_buff *skb) static const u8 ppph[2] = { 0xff, 0x03 }; struct sock *sk = (struct sock *) chan->private; struct sock *sk_tun; - int hdr_len; struct l2tp_session *session; struct l2tp_tunnel *tunnel; struct pppol2tp_session *ps; @@ -417,9 +405,6 @@ static int pppol2tp_xmit(struct ppp_channel *chan, struct sk_buff *skb) if (tunnel == NULL) goto abort_put_sess; - /* What header length is configured for this session? */ - hdr_len = pppol2tp_l2tp_header_len(session); - old_headroom = skb_headroom(skb); if (skb_cow_head(skb, sizeof(ppph))) goto abort_put_sess_tun; @@ -432,7 +417,7 @@ static int pppol2tp_xmit(struct ppp_channel *chan, struct sk_buff *skb) skb->data[0] = ppph[0]; skb->data[1] = ppph[1]; - l2tp_xmit_skb(session, skb, hdr_len); + l2tp_xmit_skb(session, skb, session->hdr_len); sock_put(sk_tun); sock_put(sk); @@ -615,6 +600,7 @@ static int pppol2tp_connect(struct socket *sock, struct sockaddr *uservaddr, { struct sock *sk = sock->sk; struct sockaddr_pppol2tp *sp = (struct sockaddr_pppol2tp *) uservaddr; + struct sockaddr_pppol2tpv3 *sp3 = (struct sockaddr_pppol2tpv3 *) uservaddr; struct pppox_sock *po = pppox_sk(sk); struct l2tp_session *session = NULL; struct l2tp_tunnel *tunnel; @@ -622,6 +608,10 @@ static int pppol2tp_connect(struct socket *sock, struct sockaddr *uservaddr, struct dst_entry *dst; struct l2tp_session_cfg cfg = { 0, }; int error = 0; + u32 tunnel_id, peer_tunnel_id; + u32 session_id, peer_session_id; + int ver = 2; + int fd; lock_sock(sk); @@ -639,21 +629,40 @@ static int pppol2tp_connect(struct socket *sock, struct sockaddr *uservaddr, if (sk->sk_user_data) goto end; /* socket is already attached */ - /* Don't bind if s_tunnel is 0 */ + /* Get params from socket address. Handle L2TPv2 and L2TPv3 */ + if (sockaddr_len == sizeof(struct sockaddr_pppol2tp)) { + fd = sp->pppol2tp.fd; + tunnel_id = sp->pppol2tp.s_tunnel; + peer_tunnel_id = sp->pppol2tp.d_tunnel; + session_id = sp->pppol2tp.s_session; + peer_session_id = sp->pppol2tp.d_session; + } else if (sockaddr_len == sizeof(struct sockaddr_pppol2tpv3)) { + ver = 3; + fd = sp3->pppol2tp.fd; + tunnel_id = sp3->pppol2tp.s_tunnel; + peer_tunnel_id = sp3->pppol2tp.d_tunnel; + session_id = sp3->pppol2tp.s_session; + peer_session_id = sp3->pppol2tp.d_session; + } else { + error = -EINVAL; + goto end; /* bad socket address */ + } + + /* Don't bind if tunnel_id is 0 */ error = -EINVAL; - if (sp->pppol2tp.s_tunnel == 0) + if (tunnel_id == 0) goto end; - /* Special case: create tunnel context if s_session and - * d_session is 0. Otherwise look up tunnel using supplied + /* Special case: create tunnel context if session_id and + * peer_session_id is 0. Otherwise look up tunnel using supplied * tunnel id. */ - if ((sp->pppol2tp.s_session == 0) && (sp->pppol2tp.d_session == 0)) { - error = l2tp_tunnel_create(sock_net(sk), sp->pppol2tp.fd, 2, sp->pppol2tp.s_tunnel, sp->pppol2tp.d_tunnel, NULL, &tunnel); + if ((session_id == 0) && (peer_session_id == 0)) { + error = l2tp_tunnel_create(sock_net(sk), fd, ver, tunnel_id, peer_tunnel_id, NULL, &tunnel); if (error < 0) goto end; } else { - tunnel = l2tp_tunnel_find(sock_net(sk), sp->pppol2tp.s_tunnel); + tunnel = l2tp_tunnel_find(sock_net(sk), tunnel_id); /* Error if we can't find the tunnel */ error = -ENOENT; @@ -670,20 +679,21 @@ static int pppol2tp_connect(struct socket *sock, struct sockaddr *uservaddr, /* Check that this session doesn't already exist */ error = -EEXIST; - session = l2tp_session_find(sock_net(sk), tunnel, sp->pppol2tp.s_session); + session = l2tp_session_find(sock_net(sk), tunnel, session_id); if (session != NULL) goto end; - /* Default MTU must allow space for UDP/L2TP/PPP - * headers. - */ - cfg.mtu = cfg.mru = 1500 - PPPOL2TP_HEADER_OVERHEAD; + /* Default MTU values. */ + if (cfg.mtu == 0) + cfg.mtu = 1500 - PPPOL2TP_HEADER_OVERHEAD; + if (cfg.mru == 0) + cfg.mru = cfg.mtu; cfg.debug = tunnel->debug; /* Allocate and initialize a new session context. */ session = l2tp_session_create(sizeof(struct pppol2tp_session), - tunnel, sp->pppol2tp.s_session, - sp->pppol2tp.d_session, &cfg); + tunnel, session_id, + peer_session_id, &cfg); if (session == NULL) { error = -ENOMEM; goto end; @@ -756,8 +766,7 @@ end: static int pppol2tp_getname(struct socket *sock, struct sockaddr *uaddr, int *usockaddr_len, int peer) { - int len = sizeof(struct sockaddr_pppol2tp); - struct sockaddr_pppol2tp sp; + int len = 0; int error = 0; struct l2tp_session *session; struct l2tp_tunnel *tunnel; @@ -783,21 +792,40 @@ static int pppol2tp_getname(struct socket *sock, struct sockaddr *uaddr, goto end_put_sess; } - memset(&sp, 0, len); - sp.sa_family = AF_PPPOX; - sp.sa_protocol = PX_PROTO_OL2TP; - sp.pppol2tp.fd = tunnel->fd; - sp.pppol2tp.pid = pls->owner; - sp.pppol2tp.s_tunnel = tunnel->tunnel_id; - sp.pppol2tp.d_tunnel = tunnel->peer_tunnel_id; - sp.pppol2tp.s_session = session->session_id; - sp.pppol2tp.d_session = session->peer_session_id; inet = inet_sk(sk); - sp.pppol2tp.addr.sin_family = AF_INET; - sp.pppol2tp.addr.sin_port = inet->inet_dport; - sp.pppol2tp.addr.sin_addr.s_addr = inet->inet_daddr; - - memcpy(uaddr, &sp, len); + if (tunnel->version == 2) { + struct sockaddr_pppol2tp sp; + len = sizeof(sp); + memset(&sp, 0, len); + sp.sa_family = AF_PPPOX; + sp.sa_protocol = PX_PROTO_OL2TP; + sp.pppol2tp.fd = tunnel->fd; + sp.pppol2tp.pid = pls->owner; + sp.pppol2tp.s_tunnel = tunnel->tunnel_id; + sp.pppol2tp.d_tunnel = tunnel->peer_tunnel_id; + sp.pppol2tp.s_session = session->session_id; + sp.pppol2tp.d_session = session->peer_session_id; + sp.pppol2tp.addr.sin_family = AF_INET; + sp.pppol2tp.addr.sin_port = inet->inet_dport; + sp.pppol2tp.addr.sin_addr.s_addr = inet->inet_daddr; + memcpy(uaddr, &sp, len); + } else if (tunnel->version == 3) { + struct sockaddr_pppol2tpv3 sp; + len = sizeof(sp); + memset(&sp, 0, len); + sp.sa_family = AF_PPPOX; + sp.sa_protocol = PX_PROTO_OL2TP; + sp.pppol2tp.fd = tunnel->fd; + sp.pppol2tp.pid = pls->owner; + sp.pppol2tp.s_tunnel = tunnel->tunnel_id; + sp.pppol2tp.d_tunnel = tunnel->peer_tunnel_id; + sp.pppol2tp.s_session = session->session_id; + sp.pppol2tp.d_session = session->peer_session_id; + sp.pppol2tp.addr.sin_family = AF_INET; + sp.pppol2tp.addr.sin_port = inet->inet_dport; + sp.pppol2tp.addr.sin_addr.s_addr = inet->inet_daddr; + memcpy(uaddr, &sp, len); + } *usockaddr_len = len; -- cgit v1.2.3 From 0d76751fad7739014485ba5bd388d4f1b4fd4143 Mon Sep 17 00:00:00 2001 From: James Chapman Date: Fri, 2 Apr 2010 06:19:00 +0000 Subject: l2tp: Add L2TPv3 IP encapsulation (no UDP) support This patch adds a new L2TPIP socket family and modifies the core to handle the case where there is no UDP header in the L2TP packet. L2TP/IP uses IP protocol 115. Since L2TP/UDP and L2TP/IP packets differ in layout, the datapath packet handling code needs changes too. Userspace uses an L2TPIP socket instead of a UDP socket when IP encapsulation is required. We can't use raw sockets for this because the semantics of raw sockets don't lend themselves to the socket-per-tunnel model - we need to Signed-off-by: David S. Miller --- include/linux/l2tp.h | 38 +++ net/l2tp/Kconfig | 17 ++ net/l2tp/Makefile | 1 + net/l2tp/l2tp_core.c | 163 ++++++++----- net/l2tp/l2tp_core.h | 7 + net/l2tp/l2tp_ip.c | 679 +++++++++++++++++++++++++++++++++++++++++++++++++++ net/l2tp/l2tp_ppp.c | 7 +- 7 files changed, 850 insertions(+), 62 deletions(-) create mode 100644 include/linux/l2tp.h create mode 100644 net/l2tp/l2tp_ip.c (limited to 'include/linux') diff --git a/include/linux/l2tp.h b/include/linux/l2tp.h new file mode 100644 index 00000000000..deff7bca4e0 --- /dev/null +++ b/include/linux/l2tp.h @@ -0,0 +1,38 @@ +/* + * L2TP-over-IP socket for L2TPv3. + * + * Author: James Chapman + */ + +#ifndef _LINUX_L2TP_H_ +#define _LINUX_L2TP_H_ + +#include +#ifdef __KERNEL__ +#include +#include +#endif + +#define IPPROTO_L2TP 115 + +/** + * struct sockaddr_l2tpip - the sockaddr structure for L2TP-over-IP sockets + * @l2tp_family: address family number AF_L2TPIP. + * @l2tp_addr: protocol specific address information + * @l2tp_conn_id: connection id of tunnel + */ +struct sockaddr_l2tpip { + /* The first fields must match struct sockaddr_in */ + sa_family_t l2tp_family; /* AF_INET */ + __be16 l2tp_unused; /* INET port number (unused) */ + struct in_addr l2tp_addr; /* Internet address */ + + __u32 l2tp_conn_id; /* Connection ID of tunnel */ + + /* Pad to size of `struct sockaddr'. */ + unsigned char __pad[sizeof(struct sockaddr) - sizeof(sa_family_t) - + sizeof(__be16) - sizeof(struct in_addr) - + sizeof(__u32)]; +}; + +#endif diff --git a/net/l2tp/Kconfig b/net/l2tp/Kconfig index d60758d6047..0a11ccf2d95 100644 --- a/net/l2tp/Kconfig +++ b/net/l2tp/Kconfig @@ -51,3 +51,20 @@ config L2TP_V3 If you are connecting to L2TPv3 equipment, or you want to tunnel raw ethernet frames using L2TP, say Y here. If unsure, say N. + +config L2TP_IP + tristate "L2TP IP encapsulation for L2TPv3" + depends on L2TP_V3 + help + Support for L2TP-over-IP socket family. + + The L2TPv3 protocol defines two possible encapsulations for + L2TP frames, namely UDP and plain IP (without UDP). This + driver provides a new L2TPIP socket family with which + userspace L2TPv3 daemons may create L2TP/IP tunnel sockets + when UDP encapsulation is not required. When L2TP is carried + in IP packets, it used IP protocol number 115, so this port + must be enabled in firewalls. + + To compile this driver as a module, choose M here. The module + will be called l2tp_ip. diff --git a/net/l2tp/Makefile b/net/l2tp/Makefile index c91f208b169..ef28b16f7d6 100644 --- a/net/l2tp/Makefile +++ b/net/l2tp/Makefile @@ -6,3 +6,4 @@ obj-$(CONFIG_L2TP) += l2tp_core.o # Build l2tp as modules if L2TP is M obj-$(subst y,$(CONFIG_L2TP),$(CONFIG_PPPOL2TP)) += l2tp_ppp.o +obj-$(subst y,$(CONFIG_L2TP),$(CONFIG_L2TP_IP)) += l2tp_ip.o diff --git a/net/l2tp/l2tp_core.c b/net/l2tp/l2tp_core.c index 0eee1a65f1b..1739d04367e 100644 --- a/net/l2tp/l2tp_core.c +++ b/net/l2tp/l2tp_core.c @@ -36,8 +36,10 @@ #include #include #include +#include #include #include +#include #include #include #include @@ -48,6 +50,7 @@ #include #include #include +#include #include #include @@ -849,15 +852,21 @@ static int l2tp_build_l2tpv2_header(struct l2tp_session *session, void *buf) static int l2tp_build_l2tpv3_header(struct l2tp_session *session, void *buf) { + struct l2tp_tunnel *tunnel = session->tunnel; char *bufp = buf; char *optr = bufp; - u16 flags = L2TP_HDR_VER_3; - /* Setup L2TP header. */ - *((__be16 *) bufp) = htons(flags); - bufp += 2; - *((__be16 *) bufp) = 0; - bufp += 2; + /* Setup L2TP header. The header differs slightly for UDP and + * IP encapsulations. For UDP, there is 4 bytes of flags. + */ + if (tunnel->encap == L2TP_ENCAPTYPE_UDP) { + u16 flags = L2TP_HDR_VER_3; + *((__be16 *) bufp) = htons(flags); + bufp += 2; + *((__be16 *) bufp) = 0; + bufp += 2; + } + *((__be32 *) bufp) = htonl(session->peer_session_id); bufp += 4; if (session->cookie_len) { @@ -902,10 +911,11 @@ int l2tp_xmit_core(struct l2tp_session *session, struct sk_buff *skb, size_t dat if (session->debug & L2TP_MSG_DATA) { int i; - unsigned char *datap = skb->data + sizeof(struct udphdr); + int uhlen = (tunnel->encap == L2TP_ENCAPTYPE_UDP) ? sizeof(struct udphdr) : 0; + unsigned char *datap = skb->data + uhlen; printk(KERN_DEBUG "%s: xmit:", session->name); - for (i = 0; i < (len - sizeof(struct udphdr)); i++) { + for (i = 0; i < (len - uhlen); i++) { printk(" %02X", *datap++); if (i == 31) { printk(" ..."); @@ -956,21 +966,23 @@ static inline void l2tp_skb_set_owner_w(struct sk_buff *skb, struct sock *sk) int l2tp_xmit_skb(struct l2tp_session *session, struct sk_buff *skb, int hdr_len) { int data_len = skb->len; - struct sock *sk = session->tunnel->sock; + struct l2tp_tunnel *tunnel = session->tunnel; + struct sock *sk = tunnel->sock; struct udphdr *uh; - unsigned int udp_len; struct inet_sock *inet; __wsum csum; int old_headroom; int new_headroom; int headroom; + int uhlen = (tunnel->encap == L2TP_ENCAPTYPE_UDP) ? sizeof(struct udphdr) : 0; + int udp_len; /* Check that there's enough headroom in the skb to insert IP, * UDP and L2TP headers. If not enough, expand it to * make room. Adjust truesize. */ headroom = NET_SKB_PAD + sizeof(struct iphdr) + - sizeof(struct udphdr) + hdr_len; + uhlen + hdr_len; old_headroom = skb_headroom(skb); if (skb_cow_head(skb, headroom)) goto abort; @@ -981,18 +993,8 @@ int l2tp_xmit_skb(struct l2tp_session *session, struct sk_buff *skb, int hdr_len /* Setup L2TP header */ session->build_header(session, __skb_push(skb, hdr_len)); - udp_len = sizeof(struct udphdr) + hdr_len + data_len; - - /* Setup UDP header */ - inet = inet_sk(sk); - __skb_push(skb, sizeof(*uh)); - skb_reset_transport_header(skb); - uh = udp_hdr(skb); - uh->source = inet->inet_sport; - uh->dest = inet->inet_dport; - uh->len = htons(udp_len); - uh->check = 0; + /* Reset skb netfilter state */ memset(&(IPCB(skb)->opt), 0, sizeof(IPCB(skb)->opt)); IPCB(skb)->flags &= ~(IPSKB_XFRM_TUNNEL_SIZE | IPSKB_XFRM_TRANSFORMED | IPSKB_REROUTED); @@ -1001,29 +1003,48 @@ int l2tp_xmit_skb(struct l2tp_session *session, struct sk_buff *skb, int hdr_len /* Get routing info from the tunnel socket */ skb_dst_drop(skb); skb_dst_set(skb, dst_clone(__sk_dst_get(sk))); - l2tp_skb_set_owner_w(skb, sk); - /* Calculate UDP checksum if configured to do so */ - if (sk->sk_no_check == UDP_CSUM_NOXMIT) - skb->ip_summed = CHECKSUM_NONE; - else if ((skb_dst(skb) && skb_dst(skb)->dev) && - (!(skb_dst(skb)->dev->features & NETIF_F_V4_CSUM))) { - skb->ip_summed = CHECKSUM_COMPLETE; - csum = skb_checksum(skb, 0, udp_len, 0); - uh->check = csum_tcpudp_magic(inet->inet_saddr, - inet->inet_daddr, - udp_len, IPPROTO_UDP, csum); - if (uh->check == 0) - uh->check = CSUM_MANGLED_0; - } else { - skb->ip_summed = CHECKSUM_PARTIAL; - skb->csum_start = skb_transport_header(skb) - skb->head; - skb->csum_offset = offsetof(struct udphdr, check); - uh->check = ~csum_tcpudp_magic(inet->inet_saddr, - inet->inet_daddr, - udp_len, IPPROTO_UDP, 0); + switch (tunnel->encap) { + case L2TP_ENCAPTYPE_UDP: + /* Setup UDP header */ + inet = inet_sk(sk); + __skb_push(skb, sizeof(*uh)); + skb_reset_transport_header(skb); + uh = udp_hdr(skb); + uh->source = inet->inet_sport; + uh->dest = inet->inet_dport; + udp_len = uhlen + hdr_len + data_len; + uh->len = htons(udp_len); + uh->check = 0; + + /* Calculate UDP checksum if configured to do so */ + if (sk->sk_no_check == UDP_CSUM_NOXMIT) + skb->ip_summed = CHECKSUM_NONE; + else if ((skb_dst(skb) && skb_dst(skb)->dev) && + (!(skb_dst(skb)->dev->features & NETIF_F_V4_CSUM))) { + skb->ip_summed = CHECKSUM_COMPLETE; + csum = skb_checksum(skb, 0, udp_len, 0); + uh->check = csum_tcpudp_magic(inet->inet_saddr, + inet->inet_daddr, + udp_len, IPPROTO_UDP, csum); + if (uh->check == 0) + uh->check = CSUM_MANGLED_0; + } else { + skb->ip_summed = CHECKSUM_PARTIAL; + skb->csum_start = skb_transport_header(skb) - skb->head; + skb->csum_offset = offsetof(struct udphdr, check); + uh->check = ~csum_tcpudp_magic(inet->inet_saddr, + inet->inet_daddr, + udp_len, IPPROTO_UDP, 0); + } + break; + + case L2TP_ENCAPTYPE_IP: + break; } + l2tp_skb_set_owner_w(skb, sk); + l2tp_xmit_core(session, skb, data_len); abort: @@ -1053,9 +1074,15 @@ void l2tp_tunnel_destruct(struct sock *sk) /* Close all sessions */ l2tp_tunnel_closeall(tunnel); - /* No longer an encapsulation socket. See net/ipv4/udp.c */ - (udp_sk(sk))->encap_type = 0; - (udp_sk(sk))->encap_rcv = NULL; + switch (tunnel->encap) { + case L2TP_ENCAPTYPE_UDP: + /* No longer an encapsulation socket. See net/ipv4/udp.c */ + (udp_sk(sk))->encap_type = 0; + (udp_sk(sk))->encap_rcv = NULL; + break; + case L2TP_ENCAPTYPE_IP: + break; + } /* Remove hooks into tunnel socket */ tunnel->sock = NULL; @@ -1168,6 +1195,7 @@ int l2tp_tunnel_create(struct net *net, int fd, int version, u32 tunnel_id, u32 struct socket *sock = NULL; struct sock *sk = NULL; struct l2tp_net *pn; + enum l2tp_encap_type encap = L2TP_ENCAPTYPE_UDP; /* Get the tunnel socket from the fd, which was opened by * the userspace L2TP daemon. @@ -1182,18 +1210,27 @@ int l2tp_tunnel_create(struct net *net, int fd, int version, u32 tunnel_id, u32 sk = sock->sk; + if (cfg != NULL) + encap = cfg->encap; + /* Quick sanity checks */ - err = -EPROTONOSUPPORT; - if (sk->sk_protocol != IPPROTO_UDP) { - printk(KERN_ERR "tunl %hu: fd %d wrong protocol, got %d, expected %d\n", - tunnel_id, fd, sk->sk_protocol, IPPROTO_UDP); - goto err; - } - err = -EAFNOSUPPORT; - if (sock->ops->family != AF_INET) { - printk(KERN_ERR "tunl %hu: fd %d wrong family, got %d, expected %d\n", - tunnel_id, fd, sock->ops->family, AF_INET); - goto err; + switch (encap) { + case L2TP_ENCAPTYPE_UDP: + err = -EPROTONOSUPPORT; + if (sk->sk_protocol != IPPROTO_UDP) { + printk(KERN_ERR "tunl %hu: fd %d wrong protocol, got %d, expected %d\n", + tunnel_id, fd, sk->sk_protocol, IPPROTO_UDP); + goto err; + } + break; + case L2TP_ENCAPTYPE_IP: + err = -EPROTONOSUPPORT; + if (sk->sk_protocol != IPPROTO_L2TP) { + printk(KERN_ERR "tunl %hu: fd %d wrong protocol, got %d, expected %d\n", + tunnel_id, fd, sk->sk_protocol, IPPROTO_L2TP); + goto err; + } + break; } /* Check if this socket has already been prepped */ @@ -1223,12 +1260,16 @@ int l2tp_tunnel_create(struct net *net, int fd, int version, u32 tunnel_id, u32 tunnel->l2tp_net = net; pn = l2tp_pernet(net); - if (cfg) + if (cfg != NULL) tunnel->debug = cfg->debug; /* Mark socket as an encapsulation socket. See net/ipv4/udp.c */ - udp_sk(sk)->encap_type = UDP_ENCAP_L2TPINUDP; - udp_sk(sk)->encap_rcv = l2tp_udp_encap_recv; + tunnel->encap = encap; + if (encap == L2TP_ENCAPTYPE_UDP) { + /* Mark socket as an encapsulation socket. See net/ipv4/udp.c */ + udp_sk(sk)->encap_type = UDP_ENCAP_L2TPINUDP; + udp_sk(sk)->encap_rcv = l2tp_udp_encap_recv; + } sk->sk_user_data = tunnel; @@ -1318,7 +1359,9 @@ void l2tp_session_set_header_len(struct l2tp_session *session, int version) if (session->send_seq) session->hdr_len += 4; } else { - session->hdr_len = 8 + session->cookie_len + session->l2specific_len + session->offset; + session->hdr_len = 4 + session->cookie_len + session->l2specific_len + session->offset; + if (session->tunnel->encap == L2TP_ENCAPTYPE_UDP) + session->hdr_len += 4; } } diff --git a/net/l2tp/l2tp_core.h b/net/l2tp/l2tp_core.h index 5c53eb2a8ad..d2395984645 100644 --- a/net/l2tp/l2tp_core.h +++ b/net/l2tp/l2tp_core.h @@ -48,6 +48,11 @@ enum l2tp_l2spec_type { L2TP_L2SPECTYPE_DEFAULT, }; +enum l2tp_encap_type { + L2TP_ENCAPTYPE_UDP, + L2TP_ENCAPTYPE_IP, +}; + struct sk_buff; struct l2tp_stats { @@ -155,6 +160,7 @@ struct l2tp_session { struct l2tp_tunnel_cfg { int debug; /* bitmask of debug message * categories */ + enum l2tp_encap_type encap; }; struct l2tp_tunnel { @@ -170,6 +176,7 @@ struct l2tp_tunnel { char name[20]; /* for logging */ int debug; /* bitmask of debug message * categories */ + enum l2tp_encap_type encap; struct l2tp_stats stats; struct list_head list; /* Keep a list of all tunnels */ diff --git a/net/l2tp/l2tp_ip.c b/net/l2tp/l2tp_ip.c new file mode 100644 index 00000000000..75bf784ba18 --- /dev/null +++ b/net/l2tp/l2tp_ip.c @@ -0,0 +1,679 @@ +/* + * L2TPv3 IP encapsulation support + * + * Copyright (c) 2008,2009,2010 Katalix Systems Ltd + * + * 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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "l2tp_core.h" + +struct l2tp_ip_sock { + /* inet_sock has to be the first member of l2tp_ip_sock */ + struct inet_sock inet; + + __u32 conn_id; + __u32 peer_conn_id; + + __u64 tx_packets; + __u64 tx_bytes; + __u64 tx_errors; + __u64 rx_packets; + __u64 rx_bytes; + __u64 rx_errors; +}; + +static DEFINE_RWLOCK(l2tp_ip_lock); +static struct hlist_head l2tp_ip_table; +static struct hlist_head l2tp_ip_bind_table; + +static inline struct l2tp_ip_sock *l2tp_ip_sk(const struct sock *sk) +{ + return (struct l2tp_ip_sock *)sk; +} + +static struct sock *__l2tp_ip_bind_lookup(struct net *net, __be32 laddr, int dif, u32 tunnel_id) +{ + struct hlist_node *node; + struct sock *sk; + + sk_for_each_bound(sk, node, &l2tp_ip_bind_table) { + struct inet_sock *inet = inet_sk(sk); + struct l2tp_ip_sock *l2tp = l2tp_ip_sk(sk); + + if (l2tp == NULL) + continue; + + if ((l2tp->conn_id == tunnel_id) && +#ifdef CONFIG_NET_NS + (sk->sk_net == net) && +#endif + !(inet->inet_rcv_saddr && inet->inet_rcv_saddr != laddr) && + !(sk->sk_bound_dev_if && sk->sk_bound_dev_if != dif)) + goto found; + } + + sk = NULL; +found: + return sk; +} + +static inline struct sock *l2tp_ip_bind_lookup(struct net *net, __be32 laddr, int dif, u32 tunnel_id) +{ + struct sock *sk = __l2tp_ip_bind_lookup(net, laddr, dif, tunnel_id); + if (sk) + sock_hold(sk); + + return sk; +} + +/* When processing receive frames, there are two cases to + * consider. Data frames consist of a non-zero session-id and an + * optional cookie. Control frames consist of a regular L2TP header + * preceded by 32-bits of zeros. + * + * L2TPv3 Session Header Over IP + * + * 0 1 2 3 + * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Session ID | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Cookie (optional, maximum 64 bits)... + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * + * L2TPv3 Control Message Header Over IP + * + * 0 1 2 3 + * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | (32 bits of zeros) | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * |T|L|x|x|S|x|x|x|x|x|x|x| Ver | Length | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Control Connection ID | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Ns | Nr | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * + * All control frames are passed to userspace. + */ +static int l2tp_ip_recv(struct sk_buff *skb) +{ + struct sock *sk; + u32 session_id; + u32 tunnel_id; + unsigned char *ptr, *optr; + struct l2tp_session *session; + struct l2tp_tunnel *tunnel = NULL; + int length; + int offset; + + /* Point to L2TP header */ + optr = ptr = skb->data; + + if (!pskb_may_pull(skb, 4)) + goto discard; + + session_id = ntohl(*((__be32 *) ptr)); + ptr += 4; + + /* RFC3931: L2TP/IP packets have the first 4 bytes containing + * the session_id. If it is 0, the packet is a L2TP control + * frame and the session_id value can be discarded. + */ + if (session_id == 0) { + __skb_pull(skb, 4); + goto pass_up; + } + + /* Ok, this is a data packet. Lookup the session. */ + session = l2tp_session_find(&init_net, NULL, session_id); + if (session == NULL) + goto discard; + + tunnel = session->tunnel; + if (tunnel == NULL) + goto discard; + + /* Trace packet contents, if enabled */ + if (tunnel->debug & L2TP_MSG_DATA) { + length = min(32u, skb->len); + if (!pskb_may_pull(skb, length)) + goto discard; + + printk(KERN_DEBUG "%s: ip recv: ", tunnel->name); + + offset = 0; + do { + printk(" %02X", ptr[offset]); + } while (++offset < length); + + printk("\n"); + } + + l2tp_recv_common(session, skb, ptr, optr, 0, skb->len, tunnel->recv_payload_hook); + + return 0; + +pass_up: + /* Get the tunnel_id from the L2TP header */ + if (!pskb_may_pull(skb, 12)) + goto discard; + + if ((skb->data[0] & 0xc0) != 0xc0) + goto discard; + + tunnel_id = ntohl(*(__be32 *) &skb->data[4]); + tunnel = l2tp_tunnel_find(&init_net, tunnel_id); + if (tunnel != NULL) + sk = tunnel->sock; + else { + struct iphdr *iph = (struct iphdr *) skb_network_header(skb); + + read_lock_bh(&l2tp_ip_lock); + sk = __l2tp_ip_bind_lookup(&init_net, iph->daddr, 0, tunnel_id); + read_unlock_bh(&l2tp_ip_lock); + } + + if (sk == NULL) + goto discard; + + sock_hold(sk); + + if (!xfrm4_policy_check(sk, XFRM_POLICY_IN, skb)) + goto discard_put; + + nf_reset(skb); + + return sk_receive_skb(sk, skb, 1); + +discard_put: + sock_put(sk); + +discard: + kfree_skb(skb); + return 0; +} + +static int l2tp_ip_open(struct sock *sk) +{ + /* Prevent autobind. We don't have ports. */ + inet_sk(sk)->inet_num = IPPROTO_L2TP; + + write_lock_bh(&l2tp_ip_lock); + sk_add_node(sk, &l2tp_ip_table); + write_unlock_bh(&l2tp_ip_lock); + + return 0; +} + +static void l2tp_ip_close(struct sock *sk, long timeout) +{ + write_lock_bh(&l2tp_ip_lock); + hlist_del_init(&sk->sk_bind_node); + hlist_del_init(&sk->sk_node); + write_unlock_bh(&l2tp_ip_lock); + sk_common_release(sk); +} + +static void l2tp_ip_destroy_sock(struct sock *sk) +{ + struct sk_buff *skb; + + while ((skb = __skb_dequeue_tail(&sk->sk_write_queue)) != NULL) + kfree_skb(skb); + + sk_refcnt_debug_dec(sk); +} + +static int l2tp_ip_bind(struct sock *sk, struct sockaddr *uaddr, int addr_len) +{ + struct inet_sock *inet = inet_sk(sk); + struct sockaddr_l2tpip *addr = (struct sockaddr_l2tpip *) uaddr; + int ret = -EINVAL; + int chk_addr_ret; + + ret = -EADDRINUSE; + read_lock_bh(&l2tp_ip_lock); + if (__l2tp_ip_bind_lookup(&init_net, addr->l2tp_addr.s_addr, sk->sk_bound_dev_if, addr->l2tp_conn_id)) + goto out_in_use; + + read_unlock_bh(&l2tp_ip_lock); + + lock_sock(sk); + if (sk->sk_state != TCP_CLOSE || addr_len < sizeof(struct sockaddr_l2tpip)) + goto out; + + chk_addr_ret = inet_addr_type(&init_net, addr->l2tp_addr.s_addr); + ret = -EADDRNOTAVAIL; + if (addr->l2tp_addr.s_addr && chk_addr_ret != RTN_LOCAL && + chk_addr_ret != RTN_MULTICAST && chk_addr_ret != RTN_BROADCAST) + goto out; + + inet->inet_rcv_saddr = inet->inet_saddr = addr->l2tp_addr.s_addr; + if (chk_addr_ret == RTN_MULTICAST || chk_addr_ret == RTN_BROADCAST) + inet->inet_saddr = 0; /* Use device */ + sk_dst_reset(sk); + + l2tp_ip_sk(sk)->conn_id = addr->l2tp_conn_id; + + write_lock_bh(&l2tp_ip_lock); + sk_add_bind_node(sk, &l2tp_ip_bind_table); + sk_del_node_init(sk); + write_unlock_bh(&l2tp_ip_lock); + ret = 0; +out: + release_sock(sk); + + return ret; + +out_in_use: + read_unlock_bh(&l2tp_ip_lock); + + return ret; +} + +static int l2tp_ip_connect(struct sock *sk, struct sockaddr *uaddr, int addr_len) +{ + int rc; + struct inet_sock *inet = inet_sk(sk); + struct sockaddr_l2tpip *lsa = (struct sockaddr_l2tpip *) uaddr; + struct rtable *rt; + __be32 saddr; + int oif; + + rc = -EINVAL; + if (addr_len < sizeof(*lsa)) + goto out; + + rc = -EAFNOSUPPORT; + if (lsa->l2tp_family != AF_INET) + goto out; + + sk_dst_reset(sk); + + oif = sk->sk_bound_dev_if; + saddr = inet->inet_saddr; + + rc = -EINVAL; + if (ipv4_is_multicast(lsa->l2tp_addr.s_addr)) + goto out; + + rc = ip_route_connect(&rt, lsa->l2tp_addr.s_addr, saddr, + RT_CONN_FLAGS(sk), oif, + IPPROTO_L2TP, + 0, 0, sk, 1); + if (rc) { + if (rc == -ENETUNREACH) + IP_INC_STATS_BH(&init_net, IPSTATS_MIB_OUTNOROUTES); + goto out; + } + + rc = -ENETUNREACH; + if (rt->rt_flags & (RTCF_MULTICAST | RTCF_BROADCAST)) { + ip_rt_put(rt); + goto out; + } + + l2tp_ip_sk(sk)->peer_conn_id = lsa->l2tp_conn_id; + + if (!inet->inet_saddr) + inet->inet_saddr = rt->rt_src; + if (!inet->inet_rcv_saddr) + inet->inet_rcv_saddr = rt->rt_src; + inet->inet_daddr = rt->rt_dst; + sk->sk_state = TCP_ESTABLISHED; + inet->inet_id = jiffies; + + sk_dst_set(sk, &rt->u.dst); + + write_lock_bh(&l2tp_ip_lock); + hlist_del_init(&sk->sk_bind_node); + sk_add_bind_node(sk, &l2tp_ip_bind_table); + write_unlock_bh(&l2tp_ip_lock); + + rc = 0; +out: + return rc; +} + +static int l2tp_ip_getname(struct socket *sock, struct sockaddr *uaddr, + int *uaddr_len, int peer) +{ + struct sock *sk = sock->sk; + struct inet_sock *inet = inet_sk(sk); + struct l2tp_ip_sock *lsk = l2tp_ip_sk(sk); + struct sockaddr_l2tpip *lsa = (struct sockaddr_l2tpip *)uaddr; + + memset(lsa, 0, sizeof(*lsa)); + lsa->l2tp_family = AF_INET; + if (peer) { + if (!inet->inet_dport) + return -ENOTCONN; + lsa->l2tp_conn_id = lsk->peer_conn_id; + lsa->l2tp_addr.s_addr = inet->inet_daddr; + } else { + __be32 addr = inet->inet_rcv_saddr; + if (!addr) + addr = inet->inet_saddr; + lsa->l2tp_conn_id = lsk->conn_id; + lsa->l2tp_addr.s_addr = addr; + } + *uaddr_len = sizeof(*lsa); + return 0; +} + +static int l2tp_ip_backlog_recv(struct sock *sk, struct sk_buff *skb) +{ + int rc; + + if (!xfrm4_policy_check(sk, XFRM_POLICY_IN, skb)) + goto drop; + + nf_reset(skb); + + /* Charge it to the socket, dropping if the queue is full. */ + rc = sock_queue_rcv_skb(sk, skb); + if (rc < 0) + goto drop; + + return 0; + +drop: + IP_INC_STATS(&init_net, IPSTATS_MIB_INDISCARDS); + kfree_skb(skb); + return -1; +} + +/* Userspace will call sendmsg() on the tunnel socket to send L2TP + * control frames. + */ +static int l2tp_ip_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg, size_t len) +{ + struct sk_buff *skb; + int rc; + struct l2tp_ip_sock *lsa = l2tp_ip_sk(sk); + struct inet_sock *inet = inet_sk(sk); + struct ip_options *opt = inet->opt; + struct rtable *rt = NULL; + int connected = 0; + __be32 daddr; + + if (sock_flag(sk, SOCK_DEAD)) + return -ENOTCONN; + + /* Get and verify the address. */ + if (msg->msg_name) { + struct sockaddr_l2tpip *lip = (struct sockaddr_l2tpip *) msg->msg_name; + if (msg->msg_namelen < sizeof(*lip)) + return -EINVAL; + + if (lip->l2tp_family != AF_INET) { + if (lip->l2tp_family != AF_UNSPEC) + return -EAFNOSUPPORT; + } + + daddr = lip->l2tp_addr.s_addr; + } else { + if (sk->sk_state != TCP_ESTABLISHED) + return -EDESTADDRREQ; + + daddr = inet->inet_daddr; + connected = 1; + } + + /* Allocate a socket buffer */ + rc = -ENOMEM; + skb = sock_wmalloc(sk, 2 + NET_SKB_PAD + sizeof(struct iphdr) + + 4 + len, 0, GFP_KERNEL); + if (!skb) + goto error; + + /* Reserve space for headers, putting IP header on 4-byte boundary. */ + skb_reserve(skb, 2 + NET_SKB_PAD); + skb_reset_network_header(skb); + skb_reserve(skb, sizeof(struct iphdr)); + skb_reset_transport_header(skb); + + /* Insert 0 session_id */ + *((__be32 *) skb_put(skb, 4)) = 0; + + /* Copy user data into skb */ + rc = memcpy_fromiovec(skb_put(skb, len), msg->msg_iov, len); + if (rc < 0) { + kfree_skb(skb); + goto error; + } + + if (connected) + rt = (struct rtable *) __sk_dst_check(sk, 0); + + if (rt == NULL) { + /* Use correct destination address if we have options. */ + if (opt && opt->srr) + daddr = opt->faddr; + + { + struct flowi fl = { .oif = sk->sk_bound_dev_if, + .nl_u = { .ip4_u = { + .daddr = daddr, + .saddr = inet->inet_saddr, + .tos = RT_CONN_FLAGS(sk) } }, + .proto = sk->sk_protocol, + .flags = inet_sk_flowi_flags(sk), + .uli_u = { .ports = { + .sport = inet->inet_sport, + .dport = inet->inet_dport } } }; + + /* If this fails, retransmit mechanism of transport layer will + * keep trying until route appears or the connection times + * itself out. + */ + security_sk_classify_flow(sk, &fl); + if (ip_route_output_flow(sock_net(sk), &rt, &fl, sk, 0)) + goto no_route; + } + sk_setup_caps(sk, &rt->u.dst); + } + skb_dst_set(skb, dst_clone(&rt->u.dst)); + + /* Queue the packet to IP for output */ + rc = ip_queue_xmit(skb, 0); + +error: + /* Update stats */ + if (rc >= 0) { + lsa->tx_packets++; + lsa->tx_bytes += len; + rc = len; + } else { + lsa->tx_errors++; + } + + return rc; + +no_route: + IP_INC_STATS(sock_net(sk), IPSTATS_MIB_OUTNOROUTES); + kfree_skb(skb); + return -EHOSTUNREACH; +} + +static int l2tp_ip_recvmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg, + size_t len, int noblock, int flags, int *addr_len) +{ + struct inet_sock *inet = inet_sk(sk); + struct l2tp_ip_sock *lsk = l2tp_ip_sk(sk); + size_t copied = 0; + int err = -EOPNOTSUPP; + struct sockaddr_in *sin = (struct sockaddr_in *)msg->msg_name; + struct sk_buff *skb; + + if (flags & MSG_OOB) + goto out; + + if (addr_len) + *addr_len = sizeof(*sin); + + skb = skb_recv_datagram(sk, flags, noblock, &err); + if (!skb) + goto out; + + copied = skb->len; + if (len < copied) { + msg->msg_flags |= MSG_TRUNC; + copied = len; + } + + err = skb_copy_datagram_iovec(skb, 0, msg->msg_iov, copied); + if (err) + goto done; + + sock_recv_timestamp(msg, sk, skb); + + /* Copy the address. */ + if (sin) { + sin->sin_family = AF_INET; + sin->sin_addr.s_addr = ip_hdr(skb)->saddr; + sin->sin_port = 0; + memset(&sin->sin_zero, 0, sizeof(sin->sin_zero)); + } + if (inet->cmsg_flags) + ip_cmsg_recv(msg, skb); + if (flags & MSG_TRUNC) + copied = skb->len; +done: + skb_free_datagram(sk, skb); +out: + if (err) { + lsk->rx_errors++; + return err; + } + + lsk->rx_packets++; + lsk->rx_bytes += copied; + + return copied; +} + +struct proto l2tp_ip_prot = { + .name = "L2TP/IP", + .owner = THIS_MODULE, + .init = l2tp_ip_open, + .close = l2tp_ip_close, + .bind = l2tp_ip_bind, + .connect = l2tp_ip_connect, + .disconnect = udp_disconnect, + .ioctl = udp_ioctl, + .destroy = l2tp_ip_destroy_sock, + .setsockopt = ip_setsockopt, + .getsockopt = ip_getsockopt, + .sendmsg = l2tp_ip_sendmsg, + .recvmsg = l2tp_ip_recvmsg, + .backlog_rcv = l2tp_ip_backlog_recv, + .hash = inet_hash, + .unhash = inet_unhash, + .obj_size = sizeof(struct l2tp_ip_sock), +#ifdef CONFIG_COMPAT + .compat_setsockopt = compat_ip_setsockopt, + .compat_getsockopt = compat_ip_getsockopt, +#endif +}; + +static const struct proto_ops l2tp_ip_ops = { + .family = PF_INET, + .owner = THIS_MODULE, + .release = inet_release, + .bind = inet_bind, + .connect = inet_dgram_connect, + .socketpair = sock_no_socketpair, + .accept = sock_no_accept, + .getname = l2tp_ip_getname, + .poll = datagram_poll, + .ioctl = inet_ioctl, + .listen = sock_no_listen, + .shutdown = inet_shutdown, + .setsockopt = sock_common_setsockopt, + .getsockopt = sock_common_getsockopt, + .sendmsg = inet_sendmsg, + .recvmsg = sock_common_recvmsg, + .mmap = sock_no_mmap, + .sendpage = sock_no_sendpage, +#ifdef CONFIG_COMPAT + .compat_setsockopt = compat_sock_common_setsockopt, + .compat_getsockopt = compat_sock_common_getsockopt, +#endif +}; + +static struct inet_protosw l2tp_ip_protosw = { + .type = SOCK_DGRAM, + .protocol = IPPROTO_L2TP, + .prot = &l2tp_ip_prot, + .ops = &l2tp_ip_ops, + .no_check = 0, +}; + +static struct net_protocol l2tp_ip_protocol __read_mostly = { + .handler = l2tp_ip_recv, +}; + +static int __init l2tp_ip_init(void) +{ + int err; + + printk(KERN_INFO "L2TP IP encapsulation support (L2TPv3)\n"); + + err = proto_register(&l2tp_ip_prot, 1); + if (err != 0) + goto out; + + err = inet_add_protocol(&l2tp_ip_protocol, IPPROTO_L2TP); + if (err) + goto out1; + + inet_register_protosw(&l2tp_ip_protosw); + return 0; + +out1: + proto_unregister(&l2tp_ip_prot); +out: + return err; +} + +static void __exit l2tp_ip_exit(void) +{ + inet_unregister_protosw(&l2tp_ip_protosw); + inet_del_protocol(&l2tp_ip_protocol, IPPROTO_L2TP); + proto_unregister(&l2tp_ip_prot); +} + +module_init(l2tp_ip_init); +module_exit(l2tp_ip_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("James Chapman "); +MODULE_DESCRIPTION("L2TP over IP"); +MODULE_VERSION("1.0"); +MODULE_ALIAS_NET_PF_PROTO_TYPE(PF_INET, SOCK_DGRAM, IPPROTO_L2TP); diff --git a/net/l2tp/l2tp_ppp.c b/net/l2tp/l2tp_ppp.c index e5b53126654..63fc62baeeb 100644 --- a/net/l2tp/l2tp_ppp.c +++ b/net/l2tp/l2tp_ppp.c @@ -305,6 +305,7 @@ static int pppol2tp_sendmsg(struct kiocb *iocb, struct socket *sock, struct msgh struct l2tp_session *session; struct l2tp_tunnel *tunnel; struct pppol2tp_session *ps; + int uhlen; error = -ENOTCONN; if (sock_flag(sk, SOCK_DEAD) || !(sk->sk_state & PPPOX_CONNECTED)) @@ -321,10 +322,12 @@ static int pppol2tp_sendmsg(struct kiocb *iocb, struct socket *sock, struct msgh if (tunnel == NULL) goto error_put_sess; + uhlen = (tunnel->encap == L2TP_ENCAPTYPE_UDP) ? sizeof(struct udphdr) : 0; + /* Allocate a socket buffer */ error = -ENOMEM; skb = sock_wmalloc(sk, NET_SKB_PAD + sizeof(struct iphdr) + - sizeof(struct udphdr) + session->hdr_len + + uhlen + session->hdr_len + sizeof(ppph) + total_len, 0, GFP_KERNEL); if (!skb) @@ -335,7 +338,7 @@ static int pppol2tp_sendmsg(struct kiocb *iocb, struct socket *sock, struct msgh skb_reset_network_header(skb); skb_reserve(skb, sizeof(struct iphdr)); skb_reset_transport_header(skb); - skb_reserve(skb, sizeof(struct udphdr)); + skb_reserve(skb, uhlen); /* Add PPP header */ skb->data[0] = ppph[0]; -- cgit v1.2.3 From f408e0ce40270559ef80f231843c93baa9947bc5 Mon Sep 17 00:00:00 2001 From: James Chapman Date: Fri, 2 Apr 2010 06:19:05 +0000 Subject: netlink: Export genl_lock() API for use by modules This lets kernel modules which use genl netlink APIs serialize netlink processing. Signed-off-by: James Chapman Reviewed-by: Randy Dunlap Signed-off-by: David S. Miller --- include/linux/genetlink.h | 8 ++++++++ net/netlink/genetlink.c | 6 ++++-- 2 files changed, 12 insertions(+), 2 deletions(-) (limited to 'include/linux') diff --git a/include/linux/genetlink.h b/include/linux/genetlink.h index b834ef6d59f..61549b26ad6 100644 --- a/include/linux/genetlink.h +++ b/include/linux/genetlink.h @@ -80,4 +80,12 @@ enum { #define CTRL_ATTR_MCAST_GRP_MAX (__CTRL_ATTR_MCAST_GRP_MAX - 1) +#ifdef __KERNEL__ + +/* All generic netlink requests are serialized by a global lock. */ +extern void genl_lock(void); +extern void genl_unlock(void); + +#endif /* __KERNEL__ */ + #endif /* __LINUX_GENERIC_NETLINK_H */ diff --git a/net/netlink/genetlink.c b/net/netlink/genetlink.c index a4b6e148c5d..a28fda7420d 100644 --- a/net/netlink/genetlink.c +++ b/net/netlink/genetlink.c @@ -20,15 +20,17 @@ static DEFINE_MUTEX(genl_mutex); /* serialization of message processing */ -static inline void genl_lock(void) +void genl_lock(void) { mutex_lock(&genl_mutex); } +EXPORT_SYMBOL(genl_lock); -static inline void genl_unlock(void) +void genl_unlock(void) { mutex_unlock(&genl_mutex); } +EXPORT_SYMBOL(genl_unlock); #define GENL_FAM_TAB_SIZE 16 #define GENL_FAM_TAB_MASK (GENL_FAM_TAB_SIZE - 1) -- cgit v1.2.3 From 309795f4bec2d69cd507a631f82065c2198a0825 Mon Sep 17 00:00:00 2001 From: James Chapman Date: Fri, 2 Apr 2010 06:19:10 +0000 Subject: l2tp: Add netlink control API for L2TP In L2TPv3, we need to create/delete/modify/query L2TP tunnel and session contexts. The number of parameters is significant. So let's use netlink. Userspace uses this API to control L2TP tunnel/session contexts in the kernel. The previous pppol2tp driver was managed using [gs]etsockopt(). This API is retained for backwards compatibility. Unlike L2TPv2 which carries only PPP frames, L2TPv3 can carry raw ethernet frames or other frame types and these do not always have an associated socket family. Therefore, we need a way to use L2TP sessions that doesn't require a socket type for each supported frame type. Hence netlink is used. Signed-off-by: James Chapman Reviewed-by: Randy Dunlap Signed-off-by: David S. Miller --- include/linux/l2tp.h | 125 ++++++++ net/l2tp/Makefile | 1 + net/l2tp/l2tp_core.c | 61 +++- net/l2tp/l2tp_core.h | 34 +- net/l2tp/l2tp_netlink.c | 830 ++++++++++++++++++++++++++++++++++++++++++++++++ net/l2tp/l2tp_ppp.c | 162 ++++++++-- 6 files changed, 1169 insertions(+), 44 deletions(-) create mode 100644 net/l2tp/l2tp_netlink.c (limited to 'include/linux') diff --git a/include/linux/l2tp.h b/include/linux/l2tp.h index deff7bca4e0..4bdb31df8e7 100644 --- a/include/linux/l2tp.h +++ b/include/linux/l2tp.h @@ -11,6 +11,8 @@ #ifdef __KERNEL__ #include #include +#else +#include #endif #define IPPROTO_L2TP 115 @@ -21,6 +23,7 @@ * @l2tp_addr: protocol specific address information * @l2tp_conn_id: connection id of tunnel */ +#define __SOCK_SIZE__ 16 /* sizeof(struct sockaddr) */ struct sockaddr_l2tpip { /* The first fields must match struct sockaddr_in */ sa_family_t l2tp_family; /* AF_INET */ @@ -35,4 +38,126 @@ struct sockaddr_l2tpip { sizeof(__u32)]; }; +/***************************************************************************** + * NETLINK_GENERIC netlink family. + *****************************************************************************/ + +/* + * Commands. + * Valid TLVs of each command are:- + * TUNNEL_CREATE - CONN_ID, pw_type, netns, ifname, ipinfo, udpinfo, udpcsum, vlanid + * TUNNEL_DELETE - CONN_ID + * TUNNEL_MODIFY - CONN_ID, udpcsum + * TUNNEL_GETSTATS - CONN_ID, (stats) + * TUNNEL_GET - CONN_ID, (...) + * SESSION_CREATE - SESSION_ID, PW_TYPE, offset, data_seq, cookie, peer_cookie, offset, l2spec + * SESSION_DELETE - SESSION_ID + * SESSION_MODIFY - SESSION_ID, data_seq + * SESSION_GET - SESSION_ID, (...) + * SESSION_GETSTATS - SESSION_ID, (stats) + * + */ +enum { + L2TP_CMD_NOOP, + L2TP_CMD_TUNNEL_CREATE, + L2TP_CMD_TUNNEL_DELETE, + L2TP_CMD_TUNNEL_MODIFY, + L2TP_CMD_TUNNEL_GET, + L2TP_CMD_SESSION_CREATE, + L2TP_CMD_SESSION_DELETE, + L2TP_CMD_SESSION_MODIFY, + L2TP_CMD_SESSION_GET, + __L2TP_CMD_MAX, +}; + +#define L2TP_CMD_MAX (__L2TP_CMD_MAX - 1) + +/* + * ATTR types defined for L2TP + */ +enum { + L2TP_ATTR_NONE, /* no data */ + L2TP_ATTR_PW_TYPE, /* u16, enum l2tp_pwtype */ + L2TP_ATTR_ENCAP_TYPE, /* u16, enum l2tp_encap_type */ + L2TP_ATTR_OFFSET, /* u16 */ + L2TP_ATTR_DATA_SEQ, /* u16 */ + L2TP_ATTR_L2SPEC_TYPE, /* u8, enum l2tp_l2spec_type */ + L2TP_ATTR_L2SPEC_LEN, /* u8, enum l2tp_l2spec_type */ + L2TP_ATTR_PROTO_VERSION, /* u8 */ + L2TP_ATTR_IFNAME, /* string */ + L2TP_ATTR_CONN_ID, /* u32 */ + L2TP_ATTR_PEER_CONN_ID, /* u32 */ + L2TP_ATTR_SESSION_ID, /* u32 */ + L2TP_ATTR_PEER_SESSION_ID, /* u32 */ + L2TP_ATTR_UDP_CSUM, /* u8 */ + L2TP_ATTR_VLAN_ID, /* u16 */ + L2TP_ATTR_COOKIE, /* 0, 4 or 8 bytes */ + L2TP_ATTR_PEER_COOKIE, /* 0, 4 or 8 bytes */ + L2TP_ATTR_DEBUG, /* u32 */ + L2TP_ATTR_RECV_SEQ, /* u8 */ + L2TP_ATTR_SEND_SEQ, /* u8 */ + L2TP_ATTR_LNS_MODE, /* u8 */ + L2TP_ATTR_USING_IPSEC, /* u8 */ + L2TP_ATTR_RECV_TIMEOUT, /* msec */ + L2TP_ATTR_FD, /* int */ + L2TP_ATTR_IP_SADDR, /* u32 */ + L2TP_ATTR_IP_DADDR, /* u32 */ + L2TP_ATTR_UDP_SPORT, /* u16 */ + L2TP_ATTR_UDP_DPORT, /* u16 */ + L2TP_ATTR_MTU, /* u16 */ + L2TP_ATTR_MRU, /* u16 */ + L2TP_ATTR_STATS, /* nested */ + __L2TP_ATTR_MAX, +}; + +#define L2TP_ATTR_MAX (__L2TP_ATTR_MAX - 1) + +/* Nested in L2TP_ATTR_STATS */ +enum { + L2TP_ATTR_STATS_NONE, /* no data */ + L2TP_ATTR_TX_PACKETS, /* u64 */ + L2TP_ATTR_TX_BYTES, /* u64 */ + L2TP_ATTR_TX_ERRORS, /* u64 */ + L2TP_ATTR_RX_PACKETS, /* u64 */ + L2TP_ATTR_RX_BYTES, /* u64 */ + L2TP_ATTR_RX_SEQ_DISCARDS, /* u64 */ + L2TP_ATTR_RX_OOS_PACKETS, /* u64 */ + L2TP_ATTR_RX_ERRORS, /* u64 */ + __L2TP_ATTR_STATS_MAX, +}; + +#define L2TP_ATTR_STATS_MAX (__L2TP_ATTR_STATS_MAX - 1) + +enum l2tp_pwtype { + L2TP_PWTYPE_NONE = 0x0000, + L2TP_PWTYPE_ETH_VLAN = 0x0004, + L2TP_PWTYPE_ETH = 0x0005, + L2TP_PWTYPE_PPP = 0x0007, + L2TP_PWTYPE_PPP_AC = 0x0008, + L2TP_PWTYPE_IP = 0x000b, + __L2TP_PWTYPE_MAX +}; + +enum l2tp_l2spec_type { + L2TP_L2SPECTYPE_NONE, + L2TP_L2SPECTYPE_DEFAULT, +}; + +enum l2tp_encap_type { + L2TP_ENCAPTYPE_UDP, + L2TP_ENCAPTYPE_IP, +}; + +enum l2tp_seqmode { + L2TP_SEQ_NONE = 0, + L2TP_SEQ_IP = 1, + L2TP_SEQ_ALL = 2, +}; + +/* + * NETLINK_GENERIC related info + */ +#define L2TP_GENL_NAME "l2tp" +#define L2TP_GENL_VERSION 0x1 + #endif diff --git a/net/l2tp/Makefile b/net/l2tp/Makefile index ef28b16f7d6..2c4a14b673a 100644 --- a/net/l2tp/Makefile +++ b/net/l2tp/Makefile @@ -7,3 +7,4 @@ obj-$(CONFIG_L2TP) += l2tp_core.o # Build l2tp as modules if L2TP is M obj-$(subst y,$(CONFIG_L2TP),$(CONFIG_PPPOL2TP)) += l2tp_ppp.o obj-$(subst y,$(CONFIG_L2TP),$(CONFIG_L2TP_IP)) += l2tp_ip.o +obj-$(subst y,$(CONFIG_L2TP),$(CONFIG_L2TP_V3)) += l2tp_netlink.o diff --git a/net/l2tp/l2tp_core.c b/net/l2tp/l2tp_core.c index 1739d04367e..fbd1f2119fe 100644 --- a/net/l2tp/l2tp_core.c +++ b/net/l2tp/l2tp_core.c @@ -49,6 +49,7 @@ #include #include #include +#include #include #include @@ -214,6 +215,32 @@ struct l2tp_session *l2tp_session_find_nth(struct l2tp_tunnel *tunnel, int nth) } EXPORT_SYMBOL_GPL(l2tp_session_find_nth); +/* Lookup a session by interface name. + * This is very inefficient but is only used by management interfaces. + */ +struct l2tp_session *l2tp_session_find_by_ifname(struct net *net, char *ifname) +{ + struct l2tp_net *pn = l2tp_pernet(net); + int hash; + struct hlist_node *walk; + struct l2tp_session *session; + + read_lock_bh(&pn->l2tp_session_hlist_lock); + for (hash = 0; hash < L2TP_HASH_SIZE_2; hash++) { + hlist_for_each_entry(session, walk, &pn->l2tp_session_hlist[hash], global_hlist) { + if (!strcmp(session->ifname, ifname)) { + read_unlock_bh(&pn->l2tp_session_hlist_lock); + return session; + } + } + } + + read_unlock_bh(&pn->l2tp_session_hlist_lock); + + return NULL; +} +EXPORT_SYMBOL_GPL(l2tp_session_find_by_ifname); + /* Lookup a tunnel by id */ struct l2tp_tunnel *l2tp_tunnel_find(struct net *net, u32 tunnel_id) @@ -758,7 +785,7 @@ int l2tp_udp_recv_core(struct l2tp_tunnel *tunnel, struct sk_buff *skb, /* Find the session context */ session = l2tp_session_find(tunnel->l2tp_net, tunnel, session_id); - if (!session) { + if (!session || !session->recv_skb) { /* Not found? Pass to userspace to deal with */ PRINTK(tunnel->debug, L2TP_MSG_DATA, KERN_INFO, "%s: no session found (%u/%u). Passing up.\n", @@ -1305,6 +1332,23 @@ err: } EXPORT_SYMBOL_GPL(l2tp_tunnel_create); +/* This function is used by the netlink TUNNEL_DELETE command. + */ +int l2tp_tunnel_delete(struct l2tp_tunnel *tunnel) +{ + int err = 0; + + /* Force the tunnel socket to close. This will eventually + * cause the tunnel to be deleted via the normal socket close + * mechanisms when userspace closes the tunnel socket. + */ + if ((tunnel->sock != NULL) && (tunnel->sock->sk_socket != NULL)) + err = inet_shutdown(tunnel->sock->sk_socket, 2); + + return err; +} +EXPORT_SYMBOL_GPL(l2tp_tunnel_delete); + /* Really kill the session. */ void l2tp_session_free(struct l2tp_session *session) @@ -1349,6 +1393,21 @@ void l2tp_session_free(struct l2tp_session *session) } EXPORT_SYMBOL_GPL(l2tp_session_free); +/* This function is used by the netlink SESSION_DELETE command and by + pseudowire modules. + */ +int l2tp_session_delete(struct l2tp_session *session) +{ + if (session->session_close != NULL) + (*session->session_close)(session); + + l2tp_session_dec_refcount(session); + + return 0; +} +EXPORT_SYMBOL_GPL(l2tp_session_delete); + + /* We come here whenever a session's send_seq, cookie_len or * l2specific_len parameters are set. */ diff --git a/net/l2tp/l2tp_core.h b/net/l2tp/l2tp_core.h index d2395984645..2974d9ade16 100644 --- a/net/l2tp/l2tp_core.h +++ b/net/l2tp/l2tp_core.h @@ -33,26 +33,6 @@ enum { L2TP_MSG_DATA = (1 << 3), /* data packets */ }; -enum l2tp_pwtype { - L2TP_PWTYPE_NONE = 0x0000, - L2TP_PWTYPE_ETH_VLAN = 0x0004, - L2TP_PWTYPE_ETH = 0x0005, - L2TP_PWTYPE_PPP = 0x0007, - L2TP_PWTYPE_PPP_AC = 0x0008, - L2TP_PWTYPE_IP = 0x000b, - __L2TP_PWTYPE_MAX -}; - -enum l2tp_l2spec_type { - L2TP_L2SPECTYPE_NONE, - L2TP_L2SPECTYPE_DEFAULT, -}; - -enum l2tp_encap_type { - L2TP_ENCAPTYPE_UDP, - L2TP_ENCAPTYPE_IP, -}; - struct sk_buff; struct l2tp_stats { @@ -87,6 +67,7 @@ struct l2tp_session_cfg { * control of LNS. */ int debug; /* bitmask of debug message * categories */ + u16 vlan_id; /* VLAN pseudowire only */ u16 offset; /* offset to payload */ u16 l2specific_len; /* Layer 2 specific length */ u16 l2specific_type; /* Layer 2 specific type */ @@ -98,6 +79,7 @@ struct l2tp_session_cfg { * (in jiffies) */ int mtu; int mru; + char *ifname; }; struct l2tp_session { @@ -124,6 +106,7 @@ struct l2tp_session { atomic_t ref_count; char name[32]; /* for logging */ + char ifname[IFNAMSIZ]; unsigned data_seq:2; /* data sequencing level * 0 => none, 1 => IP only, * 2 => all @@ -192,6 +175,11 @@ struct l2tp_tunnel { uint8_t priv[0]; /* private data */ }; +struct l2tp_nl_cmd_ops { + int (*session_create)(struct net *net, u32 tunnel_id, u32 session_id, u32 peer_session_id, struct l2tp_session_cfg *cfg); + int (*session_delete)(struct l2tp_session *session); +}; + static inline void *l2tp_tunnel_priv(struct l2tp_tunnel *tunnel) { return &tunnel->priv[0]; @@ -224,11 +212,14 @@ out: extern struct l2tp_session *l2tp_session_find(struct net *net, struct l2tp_tunnel *tunnel, u32 session_id); extern struct l2tp_session *l2tp_session_find_nth(struct l2tp_tunnel *tunnel, int nth); +extern struct l2tp_session *l2tp_session_find_by_ifname(struct net *net, char *ifname); extern struct l2tp_tunnel *l2tp_tunnel_find(struct net *net, u32 tunnel_id); extern struct l2tp_tunnel *l2tp_tunnel_find_nth(struct net *net, int nth); extern int l2tp_tunnel_create(struct net *net, int fd, int version, u32 tunnel_id, u32 peer_tunnel_id, struct l2tp_tunnel_cfg *cfg, struct l2tp_tunnel **tunnelp); +extern int l2tp_tunnel_delete(struct l2tp_tunnel *tunnel); extern struct l2tp_session *l2tp_session_create(int priv_size, struct l2tp_tunnel *tunnel, u32 session_id, u32 peer_session_id, struct l2tp_session_cfg *cfg); +extern int l2tp_session_delete(struct l2tp_session *session); extern void l2tp_tunnel_free(struct l2tp_tunnel *tunnel); extern void l2tp_session_free(struct l2tp_session *session); extern void l2tp_recv_common(struct l2tp_session *session, struct sk_buff *skb, unsigned char *ptr, unsigned char *optr, u16 hdrflags, int length, int (*payload_hook)(struct sk_buff *skb)); @@ -241,6 +232,9 @@ extern void l2tp_tunnel_destruct(struct sock *sk); extern void l2tp_tunnel_closeall(struct l2tp_tunnel *tunnel); extern void l2tp_session_set_header_len(struct l2tp_session *session, int version); +extern int l2tp_nl_register_ops(enum l2tp_pwtype pw_type, const struct l2tp_nl_cmd_ops *ops); +extern void l2tp_nl_unregister_ops(enum l2tp_pwtype pw_type); + /* Tunnel reference counts. Incremented per session that is added to * the tunnel. */ diff --git a/net/l2tp/l2tp_netlink.c b/net/l2tp/l2tp_netlink.c new file mode 100644 index 00000000000..3d0f7f6f748 --- /dev/null +++ b/net/l2tp/l2tp_netlink.c @@ -0,0 +1,830 @@ +/* + * L2TP netlink layer, for management + * + * Copyright (c) 2008,2009,2010 Katalix Systems Ltd + * + * Partly based on the IrDA nelink implementation + * (see net/irda/irnetlink.c) which is: + * Copyright (c) 2007 Samuel Ortiz + * which is in turn partly based on the wireless netlink code: + * Copyright 2006 Johannes Berg + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "l2tp_core.h" + + +static struct genl_family l2tp_nl_family = { + .id = GENL_ID_GENERATE, + .name = L2TP_GENL_NAME, + .version = L2TP_GENL_VERSION, + .hdrsize = 0, + .maxattr = L2TP_ATTR_MAX, +}; + +/* Accessed under genl lock */ +static const struct l2tp_nl_cmd_ops *l2tp_nl_cmd_ops[__L2TP_PWTYPE_MAX]; + +static struct l2tp_session *l2tp_nl_session_find(struct genl_info *info) +{ + u32 tunnel_id; + u32 session_id; + char *ifname; + struct l2tp_tunnel *tunnel; + struct l2tp_session *session = NULL; + struct net *net = genl_info_net(info); + + if (info->attrs[L2TP_ATTR_IFNAME]) { + ifname = nla_data(info->attrs[L2TP_ATTR_IFNAME]); + session = l2tp_session_find_by_ifname(net, ifname); + } else if ((info->attrs[L2TP_ATTR_SESSION_ID]) && + (info->attrs[L2TP_ATTR_CONN_ID])) { + tunnel_id = nla_get_u32(info->attrs[L2TP_ATTR_CONN_ID]); + session_id = nla_get_u32(info->attrs[L2TP_ATTR_SESSION_ID]); + tunnel = l2tp_tunnel_find(net, tunnel_id); + if (tunnel) + session = l2tp_session_find(net, tunnel, session_id); + } + + return session; +} + +static int l2tp_nl_cmd_noop(struct sk_buff *skb, struct genl_info *info) +{ + struct sk_buff *msg; + void *hdr; + int ret = -ENOBUFS; + + msg = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL); + if (!msg) { + ret = -ENOMEM; + goto out; + } + + hdr = genlmsg_put(msg, info->snd_pid, info->snd_seq, + &l2tp_nl_family, 0, L2TP_CMD_NOOP); + if (IS_ERR(hdr)) { + ret = PTR_ERR(hdr); + goto err_out; + } + + genlmsg_end(msg, hdr); + + return genlmsg_unicast(genl_info_net(info), msg, info->snd_pid); + +err_out: + nlmsg_free(msg); + +out: + return ret; +} + +static int l2tp_nl_cmd_tunnel_create(struct sk_buff *skb, struct genl_info *info) +{ + u32 tunnel_id; + u32 peer_tunnel_id; + int proto_version; + int fd; + int ret = 0; + struct l2tp_tunnel_cfg cfg = { 0, }; + struct l2tp_tunnel *tunnel; + struct net *net = genl_info_net(info); + + if (!info->attrs[L2TP_ATTR_CONN_ID]) { + ret = -EINVAL; + goto out; + } + tunnel_id = nla_get_u32(info->attrs[L2TP_ATTR_CONN_ID]); + + if (!info->attrs[L2TP_ATTR_PEER_CONN_ID]) { + ret = -EINVAL; + goto out; + } + peer_tunnel_id = nla_get_u32(info->attrs[L2TP_ATTR_PEER_CONN_ID]); + + if (!info->attrs[L2TP_ATTR_PROTO_VERSION]) { + ret = -EINVAL; + goto out; + } + proto_version = nla_get_u8(info->attrs[L2TP_ATTR_PROTO_VERSION]); + + if (!info->attrs[L2TP_ATTR_ENCAP_TYPE]) { + ret = -EINVAL; + goto out; + } + cfg.encap = nla_get_u16(info->attrs[L2TP_ATTR_ENCAP_TYPE]); + + if (!info->attrs[L2TP_ATTR_FD]) { + ret = -EINVAL; + goto out; + } + fd = nla_get_u32(info->attrs[L2TP_ATTR_FD]); + + if (info->attrs[L2TP_ATTR_DEBUG]) + cfg.debug = nla_get_u32(info->attrs[L2TP_ATTR_DEBUG]); + + tunnel = l2tp_tunnel_find(net, tunnel_id); + if (tunnel != NULL) { + ret = -EEXIST; + goto out; + } + + ret = -EINVAL; + switch (cfg.encap) { + case L2TP_ENCAPTYPE_UDP: + case L2TP_ENCAPTYPE_IP: + ret = l2tp_tunnel_create(net, fd, proto_version, tunnel_id, + peer_tunnel_id, &cfg, &tunnel); + break; + } + +out: + return ret; +} + +static int l2tp_nl_cmd_tunnel_delete(struct sk_buff *skb, struct genl_info *info) +{ + struct l2tp_tunnel *tunnel; + u32 tunnel_id; + int ret = 0; + struct net *net = genl_info_net(info); + + if (!info->attrs[L2TP_ATTR_CONN_ID]) { + ret = -EINVAL; + goto out; + } + tunnel_id = nla_get_u32(info->attrs[L2TP_ATTR_CONN_ID]); + + tunnel = l2tp_tunnel_find(net, tunnel_id); + if (tunnel == NULL) { + ret = -ENODEV; + goto out; + } + + (void) l2tp_tunnel_delete(tunnel); + +out: + return ret; +} + +static int l2tp_nl_cmd_tunnel_modify(struct sk_buff *skb, struct genl_info *info) +{ + struct l2tp_tunnel *tunnel; + u32 tunnel_id; + int ret = 0; + struct net *net = genl_info_net(info); + + if (!info->attrs[L2TP_ATTR_CONN_ID]) { + ret = -EINVAL; + goto out; + } + tunnel_id = nla_get_u32(info->attrs[L2TP_ATTR_CONN_ID]); + + tunnel = l2tp_tunnel_find(net, tunnel_id); + if (tunnel == NULL) { + ret = -ENODEV; + goto out; + } + + if (info->attrs[L2TP_ATTR_DEBUG]) + tunnel->debug = nla_get_u32(info->attrs[L2TP_ATTR_DEBUG]); + +out: + return ret; +} + +static int l2tp_nl_tunnel_send(struct sk_buff *skb, u32 pid, u32 seq, int flags, + struct l2tp_tunnel *tunnel) +{ + void *hdr; + struct nlattr *nest; + struct sock *sk = NULL; + struct inet_sock *inet; + + hdr = genlmsg_put(skb, pid, seq, &l2tp_nl_family, flags, + L2TP_CMD_TUNNEL_GET); + if (IS_ERR(hdr)) + return PTR_ERR(hdr); + + NLA_PUT_U8(skb, L2TP_ATTR_PROTO_VERSION, tunnel->version); + NLA_PUT_U32(skb, L2TP_ATTR_CONN_ID, tunnel->tunnel_id); + NLA_PUT_U32(skb, L2TP_ATTR_PEER_CONN_ID, tunnel->peer_tunnel_id); + NLA_PUT_U32(skb, L2TP_ATTR_DEBUG, tunnel->debug); + NLA_PUT_U16(skb, L2TP_ATTR_ENCAP_TYPE, tunnel->encap); + + nest = nla_nest_start(skb, L2TP_ATTR_STATS); + if (nest == NULL) + goto nla_put_failure; + + NLA_PUT_U64(skb, L2TP_ATTR_TX_PACKETS, tunnel->stats.tx_packets); + NLA_PUT_U64(skb, L2TP_ATTR_TX_BYTES, tunnel->stats.tx_bytes); + NLA_PUT_U64(skb, L2TP_ATTR_TX_ERRORS, tunnel->stats.tx_errors); + NLA_PUT_U64(skb, L2TP_ATTR_RX_PACKETS, tunnel->stats.rx_packets); + NLA_PUT_U64(skb, L2TP_ATTR_RX_BYTES, tunnel->stats.rx_bytes); + NLA_PUT_U64(skb, L2TP_ATTR_RX_SEQ_DISCARDS, tunnel->stats.rx_seq_discards); + NLA_PUT_U64(skb, L2TP_ATTR_RX_OOS_PACKETS, tunnel->stats.rx_oos_packets); + NLA_PUT_U64(skb, L2TP_ATTR_RX_ERRORS, tunnel->stats.rx_errors); + nla_nest_end(skb, nest); + + sk = tunnel->sock; + if (!sk) + goto out; + + inet = inet_sk(sk); + + switch (tunnel->encap) { + case L2TP_ENCAPTYPE_UDP: + NLA_PUT_U16(skb, L2TP_ATTR_UDP_SPORT, ntohs(inet->inet_sport)); + NLA_PUT_U16(skb, L2TP_ATTR_UDP_DPORT, ntohs(inet->inet_dport)); + NLA_PUT_U8(skb, L2TP_ATTR_UDP_CSUM, (sk->sk_no_check != UDP_CSUM_NOXMIT)); + /* NOBREAK */ + case L2TP_ENCAPTYPE_IP: + NLA_PUT_BE32(skb, L2TP_ATTR_IP_SADDR, inet->inet_saddr); + NLA_PUT_BE32(skb, L2TP_ATTR_IP_DADDR, inet->inet_daddr); + break; + } + +out: + return genlmsg_end(skb, hdr); + +nla_put_failure: + genlmsg_cancel(skb, hdr); + return -1; +} + +static int l2tp_nl_cmd_tunnel_get(struct sk_buff *skb, struct genl_info *info) +{ + struct l2tp_tunnel *tunnel; + struct sk_buff *msg; + u32 tunnel_id; + int ret = -ENOBUFS; + struct net *net = genl_info_net(info); + + if (!info->attrs[L2TP_ATTR_CONN_ID]) { + ret = -EINVAL; + goto out; + } + + tunnel_id = nla_get_u32(info->attrs[L2TP_ATTR_CONN_ID]); + + tunnel = l2tp_tunnel_find(net, tunnel_id); + if (tunnel == NULL) { + ret = -ENODEV; + goto out; + } + + msg = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL); + if (!msg) { + ret = -ENOMEM; + goto out; + } + + ret = l2tp_nl_tunnel_send(msg, info->snd_pid, info->snd_seq, + NLM_F_ACK, tunnel); + if (ret < 0) + goto err_out; + + return genlmsg_unicast(net, msg, info->snd_pid); + +err_out: + nlmsg_free(msg); + +out: + return ret; +} + +static int l2tp_nl_cmd_tunnel_dump(struct sk_buff *skb, struct netlink_callback *cb) +{ + int ti = cb->args[0]; + struct l2tp_tunnel *tunnel; + struct net *net = sock_net(skb->sk); + + for (;;) { + tunnel = l2tp_tunnel_find_nth(net, ti); + if (tunnel == NULL) + goto out; + + if (l2tp_nl_tunnel_send(skb, NETLINK_CB(cb->skb).pid, + cb->nlh->nlmsg_seq, NLM_F_MULTI, + tunnel) <= 0) + goto out; + + ti++; + } + +out: + cb->args[0] = ti; + + return skb->len; +} + +static int l2tp_nl_cmd_session_create(struct sk_buff *skb, struct genl_info *info) +{ + u32 tunnel_id = 0; + u32 session_id; + u32 peer_session_id; + int ret = 0; + struct l2tp_tunnel *tunnel; + struct l2tp_session *session; + struct l2tp_session_cfg cfg = { 0, }; + struct net *net = genl_info_net(info); + + if (!info->attrs[L2TP_ATTR_CONN_ID]) { + ret = -EINVAL; + goto out; + } + tunnel_id = nla_get_u32(info->attrs[L2TP_ATTR_CONN_ID]); + tunnel = l2tp_tunnel_find(net, tunnel_id); + if (!tunnel) { + ret = -ENODEV; + goto out; + } + + if (!info->attrs[L2TP_ATTR_SESSION_ID]) { + ret = -EINVAL; + goto out; + } + session_id = nla_get_u32(info->attrs[L2TP_ATTR_SESSION_ID]); + session = l2tp_session_find(net, tunnel, session_id); + if (session) { + ret = -EEXIST; + goto out; + } + + if (!info->attrs[L2TP_ATTR_PEER_SESSION_ID]) { + ret = -EINVAL; + goto out; + } + peer_session_id = nla_get_u32(info->attrs[L2TP_ATTR_PEER_SESSION_ID]); + + if (!info->attrs[L2TP_ATTR_PW_TYPE]) { + ret = -EINVAL; + goto out; + } + cfg.pw_type = nla_get_u16(info->attrs[L2TP_ATTR_PW_TYPE]); + if (cfg.pw_type >= __L2TP_PWTYPE_MAX) { + ret = -EINVAL; + goto out; + } + + if (tunnel->version > 2) { + if (info->attrs[L2TP_ATTR_OFFSET]) + cfg.offset = nla_get_u16(info->attrs[L2TP_ATTR_OFFSET]); + + if (info->attrs[L2TP_ATTR_DATA_SEQ]) + cfg.data_seq = nla_get_u8(info->attrs[L2TP_ATTR_DATA_SEQ]); + + cfg.l2specific_type = L2TP_L2SPECTYPE_DEFAULT; + if (info->attrs[L2TP_ATTR_L2SPEC_TYPE]) + cfg.l2specific_type = nla_get_u8(info->attrs[L2TP_ATTR_L2SPEC_TYPE]); + + cfg.l2specific_len = 4; + if (info->attrs[L2TP_ATTR_L2SPEC_LEN]) + cfg.l2specific_len = nla_get_u8(info->attrs[L2TP_ATTR_L2SPEC_LEN]); + + if (info->attrs[L2TP_ATTR_COOKIE]) { + u16 len = nla_len(info->attrs[L2TP_ATTR_COOKIE]); + if (len > 8) { + ret = -EINVAL; + goto out; + } + cfg.cookie_len = len; + memcpy(&cfg.cookie[0], nla_data(info->attrs[L2TP_ATTR_COOKIE]), len); + } + if (info->attrs[L2TP_ATTR_PEER_COOKIE]) { + u16 len = nla_len(info->attrs[L2TP_ATTR_PEER_COOKIE]); + if (len > 8) { + ret = -EINVAL; + goto out; + } + cfg.peer_cookie_len = len; + memcpy(&cfg.peer_cookie[0], nla_data(info->attrs[L2TP_ATTR_PEER_COOKIE]), len); + } + if (info->attrs[L2TP_ATTR_IFNAME]) + cfg.ifname = nla_data(info->attrs[L2TP_ATTR_IFNAME]); + + if (info->attrs[L2TP_ATTR_VLAN_ID]) + cfg.vlan_id = nla_get_u16(info->attrs[L2TP_ATTR_VLAN_ID]); + } + + if (info->attrs[L2TP_ATTR_DEBUG]) + cfg.debug = nla_get_u32(info->attrs[L2TP_ATTR_DEBUG]); + + if (info->attrs[L2TP_ATTR_RECV_SEQ]) + cfg.recv_seq = nla_get_u8(info->attrs[L2TP_ATTR_RECV_SEQ]); + + if (info->attrs[L2TP_ATTR_SEND_SEQ]) + cfg.send_seq = nla_get_u8(info->attrs[L2TP_ATTR_SEND_SEQ]); + + if (info->attrs[L2TP_ATTR_LNS_MODE]) + cfg.lns_mode = nla_get_u8(info->attrs[L2TP_ATTR_LNS_MODE]); + + if (info->attrs[L2TP_ATTR_RECV_TIMEOUT]) + cfg.reorder_timeout = nla_get_msecs(info->attrs[L2TP_ATTR_RECV_TIMEOUT]); + + if (info->attrs[L2TP_ATTR_MTU]) + cfg.mtu = nla_get_u16(info->attrs[L2TP_ATTR_MTU]); + + if (info->attrs[L2TP_ATTR_MRU]) + cfg.mru = nla_get_u16(info->attrs[L2TP_ATTR_MRU]); + + if ((l2tp_nl_cmd_ops[cfg.pw_type] == NULL) || + (l2tp_nl_cmd_ops[cfg.pw_type]->session_create == NULL)) { + ret = -EPROTONOSUPPORT; + goto out; + } + + /* Check that pseudowire-specific params are present */ + switch (cfg.pw_type) { + case L2TP_PWTYPE_NONE: + break; + case L2TP_PWTYPE_ETH_VLAN: + if (!info->attrs[L2TP_ATTR_VLAN_ID]) { + ret = -EINVAL; + goto out; + } + break; + case L2TP_PWTYPE_ETH: + break; + case L2TP_PWTYPE_PPP: + case L2TP_PWTYPE_PPP_AC: + break; + case L2TP_PWTYPE_IP: + default: + ret = -EPROTONOSUPPORT; + break; + } + + ret = -EPROTONOSUPPORT; + if (l2tp_nl_cmd_ops[cfg.pw_type]->session_create) + ret = (*l2tp_nl_cmd_ops[cfg.pw_type]->session_create)(net, tunnel_id, + session_id, peer_session_id, &cfg); + +out: + return ret; +} + +static int l2tp_nl_cmd_session_delete(struct sk_buff *skb, struct genl_info *info) +{ + int ret = 0; + struct l2tp_session *session; + u16 pw_type; + + session = l2tp_nl_session_find(info); + if (session == NULL) { + ret = -ENODEV; + goto out; + } + + pw_type = session->pwtype; + if (pw_type < __L2TP_PWTYPE_MAX) + if (l2tp_nl_cmd_ops[pw_type] && l2tp_nl_cmd_ops[pw_type]->session_delete) + ret = (*l2tp_nl_cmd_ops[pw_type]->session_delete)(session); + +out: + return ret; +} + +static int l2tp_nl_cmd_session_modify(struct sk_buff *skb, struct genl_info *info) +{ + int ret = 0; + struct l2tp_session *session; + + session = l2tp_nl_session_find(info); + if (session == NULL) { + ret = -ENODEV; + goto out; + } + + if (info->attrs[L2TP_ATTR_DEBUG]) + session->debug = nla_get_u32(info->attrs[L2TP_ATTR_DEBUG]); + + if (info->attrs[L2TP_ATTR_DATA_SEQ]) + session->data_seq = nla_get_u8(info->attrs[L2TP_ATTR_DATA_SEQ]); + + if (info->attrs[L2TP_ATTR_RECV_SEQ]) + session->recv_seq = nla_get_u8(info->attrs[L2TP_ATTR_RECV_SEQ]); + + if (info->attrs[L2TP_ATTR_SEND_SEQ]) + session->send_seq = nla_get_u8(info->attrs[L2TP_ATTR_SEND_SEQ]); + + if (info->attrs[L2TP_ATTR_LNS_MODE]) + session->lns_mode = nla_get_u8(info->attrs[L2TP_ATTR_LNS_MODE]); + + if (info->attrs[L2TP_ATTR_RECV_TIMEOUT]) + session->reorder_timeout = nla_get_msecs(info->attrs[L2TP_ATTR_RECV_TIMEOUT]); + + if (info->attrs[L2TP_ATTR_MTU]) + session->mtu = nla_get_u16(info->attrs[L2TP_ATTR_MTU]); + + if (info->attrs[L2TP_ATTR_MRU]) + session->mru = nla_get_u16(info->attrs[L2TP_ATTR_MRU]); + +out: + return ret; +} + +static int l2tp_nl_session_send(struct sk_buff *skb, u32 pid, u32 seq, int flags, + struct l2tp_session *session) +{ + void *hdr; + struct nlattr *nest; + struct l2tp_tunnel *tunnel = session->tunnel; + struct sock *sk = NULL; + + sk = tunnel->sock; + + hdr = genlmsg_put(skb, pid, seq, &l2tp_nl_family, flags, L2TP_CMD_SESSION_GET); + if (IS_ERR(hdr)) + return PTR_ERR(hdr); + + NLA_PUT_U32(skb, L2TP_ATTR_CONN_ID, tunnel->tunnel_id); + NLA_PUT_U32(skb, L2TP_ATTR_SESSION_ID, session->session_id); + NLA_PUT_U32(skb, L2TP_ATTR_PEER_CONN_ID, tunnel->peer_tunnel_id); + NLA_PUT_U32(skb, L2TP_ATTR_PEER_SESSION_ID, session->peer_session_id); + NLA_PUT_U32(skb, L2TP_ATTR_DEBUG, session->debug); + NLA_PUT_U16(skb, L2TP_ATTR_PW_TYPE, session->pwtype); + NLA_PUT_U16(skb, L2TP_ATTR_MTU, session->mtu); + if (session->mru) + NLA_PUT_U16(skb, L2TP_ATTR_MRU, session->mru); + + if (session->ifname && session->ifname[0]) + NLA_PUT_STRING(skb, L2TP_ATTR_IFNAME, session->ifname); + if (session->cookie_len) + NLA_PUT(skb, L2TP_ATTR_COOKIE, session->cookie_len, &session->cookie[0]); + if (session->peer_cookie_len) + NLA_PUT(skb, L2TP_ATTR_PEER_COOKIE, session->peer_cookie_len, &session->peer_cookie[0]); + NLA_PUT_U8(skb, L2TP_ATTR_RECV_SEQ, session->recv_seq); + NLA_PUT_U8(skb, L2TP_ATTR_SEND_SEQ, session->send_seq); + NLA_PUT_U8(skb, L2TP_ATTR_LNS_MODE, session->lns_mode); +#ifdef CONFIG_XFRM + if ((sk) && (sk->sk_policy[0] || sk->sk_policy[1])) + NLA_PUT_U8(skb, L2TP_ATTR_USING_IPSEC, 1); +#endif + if (session->reorder_timeout) + NLA_PUT_MSECS(skb, L2TP_ATTR_RECV_TIMEOUT, session->reorder_timeout); + + nest = nla_nest_start(skb, L2TP_ATTR_STATS); + if (nest == NULL) + goto nla_put_failure; + NLA_PUT_U64(skb, L2TP_ATTR_TX_PACKETS, session->stats.tx_packets); + NLA_PUT_U64(skb, L2TP_ATTR_TX_BYTES, session->stats.tx_bytes); + NLA_PUT_U64(skb, L2TP_ATTR_TX_ERRORS, session->stats.tx_errors); + NLA_PUT_U64(skb, L2TP_ATTR_RX_PACKETS, session->stats.rx_packets); + NLA_PUT_U64(skb, L2TP_ATTR_RX_BYTES, session->stats.rx_bytes); + NLA_PUT_U64(skb, L2TP_ATTR_RX_SEQ_DISCARDS, session->stats.rx_seq_discards); + NLA_PUT_U64(skb, L2TP_ATTR_RX_OOS_PACKETS, session->stats.rx_oos_packets); + NLA_PUT_U64(skb, L2TP_ATTR_RX_ERRORS, session->stats.rx_errors); + nla_nest_end(skb, nest); + + return genlmsg_end(skb, hdr); + + nla_put_failure: + genlmsg_cancel(skb, hdr); + return -1; +} + +static int l2tp_nl_cmd_session_get(struct sk_buff *skb, struct genl_info *info) +{ + struct l2tp_session *session; + struct sk_buff *msg; + int ret; + + session = l2tp_nl_session_find(info); + if (session == NULL) { + ret = -ENODEV; + goto out; + } + + msg = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL); + if (!msg) { + ret = -ENOMEM; + goto out; + } + + ret = l2tp_nl_session_send(msg, info->snd_pid, info->snd_seq, + 0, session); + if (ret < 0) + goto err_out; + + return genlmsg_unicast(genl_info_net(info), msg, info->snd_pid); + +err_out: + nlmsg_free(msg); + +out: + return ret; +} + +static int l2tp_nl_cmd_session_dump(struct sk_buff *skb, struct netlink_callback *cb) +{ + struct net *net = sock_net(skb->sk); + struct l2tp_session *session; + struct l2tp_tunnel *tunnel = NULL; + int ti = cb->args[0]; + int si = cb->args[1]; + + for (;;) { + if (tunnel == NULL) { + tunnel = l2tp_tunnel_find_nth(net, ti); + if (tunnel == NULL) + goto out; + } + + session = l2tp_session_find_nth(tunnel, si); + if (session == NULL) { + ti++; + tunnel = NULL; + si = 0; + continue; + } + + if (l2tp_nl_session_send(skb, NETLINK_CB(cb->skb).pid, + cb->nlh->nlmsg_seq, NLM_F_MULTI, + session) <= 0) + break; + + si++; + } + +out: + cb->args[0] = ti; + cb->args[1] = si; + + return skb->len; +} + +static struct nla_policy l2tp_nl_policy[L2TP_ATTR_MAX + 1] = { + [L2TP_ATTR_NONE] = { .type = NLA_UNSPEC, }, + [L2TP_ATTR_PW_TYPE] = { .type = NLA_U16, }, + [L2TP_ATTR_ENCAP_TYPE] = { .type = NLA_U16, }, + [L2TP_ATTR_OFFSET] = { .type = NLA_U16, }, + [L2TP_ATTR_DATA_SEQ] = { .type = NLA_U8, }, + [L2TP_ATTR_L2SPEC_TYPE] = { .type = NLA_U8, }, + [L2TP_ATTR_L2SPEC_LEN] = { .type = NLA_U8, }, + [L2TP_ATTR_PROTO_VERSION] = { .type = NLA_U8, }, + [L2TP_ATTR_CONN_ID] = { .type = NLA_U32, }, + [L2TP_ATTR_PEER_CONN_ID] = { .type = NLA_U32, }, + [L2TP_ATTR_SESSION_ID] = { .type = NLA_U32, }, + [L2TP_ATTR_PEER_SESSION_ID] = { .type = NLA_U32, }, + [L2TP_ATTR_UDP_CSUM] = { .type = NLA_U8, }, + [L2TP_ATTR_VLAN_ID] = { .type = NLA_U16, }, + [L2TP_ATTR_DEBUG] = { .type = NLA_U32, }, + [L2TP_ATTR_RECV_SEQ] = { .type = NLA_U8, }, + [L2TP_ATTR_SEND_SEQ] = { .type = NLA_U8, }, + [L2TP_ATTR_LNS_MODE] = { .type = NLA_U8, }, + [L2TP_ATTR_USING_IPSEC] = { .type = NLA_U8, }, + [L2TP_ATTR_RECV_TIMEOUT] = { .type = NLA_MSECS, }, + [L2TP_ATTR_FD] = { .type = NLA_U32, }, + [L2TP_ATTR_IP_SADDR] = { .type = NLA_U32, }, + [L2TP_ATTR_IP_DADDR] = { .type = NLA_U32, }, + [L2TP_ATTR_UDP_SPORT] = { .type = NLA_U16, }, + [L2TP_ATTR_UDP_DPORT] = { .type = NLA_U16, }, + [L2TP_ATTR_MTU] = { .type = NLA_U16, }, + [L2TP_ATTR_MRU] = { .type = NLA_U16, }, + [L2TP_ATTR_STATS] = { .type = NLA_NESTED, }, + [L2TP_ATTR_IFNAME] = { + .type = NLA_NUL_STRING, + .len = IFNAMSIZ - 1, + }, + [L2TP_ATTR_COOKIE] = { + .type = NLA_BINARY, + .len = 8, + }, + [L2TP_ATTR_PEER_COOKIE] = { + .type = NLA_BINARY, + .len = 8, + }, +}; + +static struct genl_ops l2tp_nl_ops[] = { + { + .cmd = L2TP_CMD_NOOP, + .doit = l2tp_nl_cmd_noop, + .policy = l2tp_nl_policy, + /* can be retrieved by unprivileged users */ + }, + { + .cmd = L2TP_CMD_TUNNEL_CREATE, + .doit = l2tp_nl_cmd_tunnel_create, + .policy = l2tp_nl_policy, + .flags = GENL_ADMIN_PERM, + }, + { + .cmd = L2TP_CMD_TUNNEL_DELETE, + .doit = l2tp_nl_cmd_tunnel_delete, + .policy = l2tp_nl_policy, + .flags = GENL_ADMIN_PERM, + }, + { + .cmd = L2TP_CMD_TUNNEL_MODIFY, + .doit = l2tp_nl_cmd_tunnel_modify, + .policy = l2tp_nl_policy, + .flags = GENL_ADMIN_PERM, + }, + { + .cmd = L2TP_CMD_TUNNEL_GET, + .doit = l2tp_nl_cmd_tunnel_get, + .dumpit = l2tp_nl_cmd_tunnel_dump, + .policy = l2tp_nl_policy, + .flags = GENL_ADMIN_PERM, + }, + { + .cmd = L2TP_CMD_SESSION_CREATE, + .doit = l2tp_nl_cmd_session_create, + .policy = l2tp_nl_policy, + .flags = GENL_ADMIN_PERM, + }, + { + .cmd = L2TP_CMD_SESSION_DELETE, + .doit = l2tp_nl_cmd_session_delete, + .policy = l2tp_nl_policy, + .flags = GENL_ADMIN_PERM, + }, + { + .cmd = L2TP_CMD_SESSION_MODIFY, + .doit = l2tp_nl_cmd_session_modify, + .policy = l2tp_nl_policy, + .flags = GENL_ADMIN_PERM, + }, + { + .cmd = L2TP_CMD_SESSION_GET, + .doit = l2tp_nl_cmd_session_get, + .dumpit = l2tp_nl_cmd_session_dump, + .policy = l2tp_nl_policy, + .flags = GENL_ADMIN_PERM, + }, +}; + +int l2tp_nl_register_ops(enum l2tp_pwtype pw_type, const struct l2tp_nl_cmd_ops *ops) +{ + int ret; + + ret = -EINVAL; + if (pw_type >= __L2TP_PWTYPE_MAX) + goto err; + + genl_lock(); + ret = -EBUSY; + if (l2tp_nl_cmd_ops[pw_type]) + goto out; + + l2tp_nl_cmd_ops[pw_type] = ops; + +out: + genl_unlock(); +err: + return 0; +} +EXPORT_SYMBOL_GPL(l2tp_nl_register_ops); + +void l2tp_nl_unregister_ops(enum l2tp_pwtype pw_type) +{ + if (pw_type < __L2TP_PWTYPE_MAX) { + genl_lock(); + l2tp_nl_cmd_ops[pw_type] = NULL; + genl_unlock(); + } +} +EXPORT_SYMBOL_GPL(l2tp_nl_unregister_ops); + +static int l2tp_nl_init(void) +{ + int err; + + printk(KERN_INFO "L2TP netlink interface\n"); + err = genl_register_family_with_ops(&l2tp_nl_family, l2tp_nl_ops, + ARRAY_SIZE(l2tp_nl_ops)); + + return err; +} + +static void l2tp_nl_cleanup(void) +{ + genl_unregister_family(&l2tp_nl_family); +} + +module_init(l2tp_nl_init); +module_exit(l2tp_nl_cleanup); + +MODULE_AUTHOR("James Chapman "); +MODULE_DESCRIPTION("L2TP netlink"); +MODULE_LICENSE("GPL"); +MODULE_VERSION("1.0"); +MODULE_ALIAS("net-pf-" __stringify(PF_NETLINK) "-proto-" \ + __stringify(NETLINK_GENERIC) "-type-" "l2tp") diff --git a/net/l2tp/l2tp_ppp.c b/net/l2tp/l2tp_ppp.c index 63fc62baeeb..d64f081f2b1 100644 --- a/net/l2tp/l2tp_ppp.c +++ b/net/l2tp/l2tp_ppp.c @@ -87,6 +87,7 @@ #include #include #include +#include #include #include #include @@ -656,17 +657,23 @@ static int pppol2tp_connect(struct socket *sock, struct sockaddr *uservaddr, if (tunnel_id == 0) goto end; + tunnel = l2tp_tunnel_find(sock_net(sk), tunnel_id); + /* Special case: create tunnel context if session_id and * peer_session_id is 0. Otherwise look up tunnel using supplied * tunnel id. */ if ((session_id == 0) && (peer_session_id == 0)) { - error = l2tp_tunnel_create(sock_net(sk), fd, ver, tunnel_id, peer_tunnel_id, NULL, &tunnel); - if (error < 0) - goto end; + if (tunnel == NULL) { + struct l2tp_tunnel_cfg tcfg = { + .encap = L2TP_ENCAPTYPE_UDP, + .debug = 0, + }; + error = l2tp_tunnel_create(sock_net(sk), fd, ver, tunnel_id, peer_tunnel_id, &tcfg, &tunnel); + if (error < 0) + goto end; + } } else { - tunnel = l2tp_tunnel_find(sock_net(sk), tunnel_id); - /* Error if we can't find the tunnel */ error = -ENOENT; if (tunnel == NULL) @@ -680,28 +687,46 @@ static int pppol2tp_connect(struct socket *sock, struct sockaddr *uservaddr, if (tunnel->recv_payload_hook == NULL) tunnel->recv_payload_hook = pppol2tp_recv_payload_hook; - /* Check that this session doesn't already exist */ - error = -EEXIST; - session = l2tp_session_find(sock_net(sk), tunnel, session_id); - if (session != NULL) - goto end; - - /* Default MTU values. */ - if (cfg.mtu == 0) - cfg.mtu = 1500 - PPPOL2TP_HEADER_OVERHEAD; - if (cfg.mru == 0) - cfg.mru = cfg.mtu; - cfg.debug = tunnel->debug; + if (tunnel->peer_tunnel_id == 0) { + if (ver == 2) + tunnel->peer_tunnel_id = sp->pppol2tp.d_tunnel; + else + tunnel->peer_tunnel_id = sp3->pppol2tp.d_tunnel; + } - /* Allocate and initialize a new session context. */ - session = l2tp_session_create(sizeof(struct pppol2tp_session), - tunnel, session_id, - peer_session_id, &cfg); + /* Create session if it doesn't already exist. We handle the + * case where a session was previously created by the netlink + * interface by checking that the session doesn't already have + * a socket and its tunnel socket are what we expect. If any + * of those checks fail, return EEXIST to the caller. + */ + session = l2tp_session_find(sock_net(sk), tunnel, session_id); if (session == NULL) { - error = -ENOMEM; - goto end; + /* Default MTU must allow space for UDP/L2TP/PPP + * headers. + */ + cfg.mtu = cfg.mru = 1500 - PPPOL2TP_HEADER_OVERHEAD; + + /* Allocate and initialize a new session context. */ + session = l2tp_session_create(sizeof(struct pppol2tp_session), + tunnel, session_id, + peer_session_id, &cfg); + if (session == NULL) { + error = -ENOMEM; + goto end; + } + } else { + ps = l2tp_session_priv(session); + error = -EEXIST; + if (ps->sock != NULL) + goto end; + + /* consistency checks */ + if (ps->tunnel_sock != tunnel->sock) + goto end; } + /* Associate session with its PPPoL2TP socket */ ps = l2tp_session_priv(session); ps->owner = current->pid; ps->sock = sk; @@ -764,6 +789,74 @@ end: return error; } +#ifdef CONFIG_L2TP_V3 + +/* Called when creating sessions via the netlink interface. + */ +static int pppol2tp_session_create(struct net *net, u32 tunnel_id, u32 session_id, u32 peer_session_id, struct l2tp_session_cfg *cfg) +{ + int error; + struct l2tp_tunnel *tunnel; + struct l2tp_session *session; + struct pppol2tp_session *ps; + + tunnel = l2tp_tunnel_find(net, tunnel_id); + + /* Error if we can't find the tunnel */ + error = -ENOENT; + if (tunnel == NULL) + goto out; + + /* Error if tunnel socket is not prepped */ + if (tunnel->sock == NULL) + goto out; + + /* Check that this session doesn't already exist */ + error = -EEXIST; + session = l2tp_session_find(net, tunnel, session_id); + if (session != NULL) + goto out; + + /* Default MTU values. */ + if (cfg->mtu == 0) + cfg->mtu = 1500 - PPPOL2TP_HEADER_OVERHEAD; + if (cfg->mru == 0) + cfg->mru = cfg->mtu; + + /* Allocate and initialize a new session context. */ + error = -ENOMEM; + session = l2tp_session_create(sizeof(struct pppol2tp_session), + tunnel, session_id, + peer_session_id, cfg); + if (session == NULL) + goto out; + + ps = l2tp_session_priv(session); + ps->tunnel_sock = tunnel->sock; + + PRINTK(session->debug, PPPOL2TP_MSG_CONTROL, KERN_INFO, + "%s: created\n", session->name); + + error = 0; + +out: + return error; +} + +/* Called when deleting sessions via the netlink interface. + */ +static int pppol2tp_session_delete(struct l2tp_session *session) +{ + struct pppol2tp_session *ps = l2tp_session_priv(session); + + if (ps->sock == NULL) + l2tp_session_dec_refcount(session); + + return 0; +} + +#endif /* CONFIG_L2TP_V3 */ + /* getname() support. */ static int pppol2tp_getname(struct socket *sock, struct sockaddr *uaddr, @@ -1660,6 +1753,15 @@ static struct pppox_proto pppol2tp_proto = { .ioctl = pppol2tp_ioctl }; +#ifdef CONFIG_L2TP_V3 + +static const struct l2tp_nl_cmd_ops pppol2tp_nl_cmd_ops = { + .session_create = pppol2tp_session_create, + .session_delete = pppol2tp_session_delete, +}; + +#endif /* CONFIG_L2TP_V3 */ + static int __init pppol2tp_init(void) { int err; @@ -1676,11 +1778,22 @@ static int __init pppol2tp_init(void) if (err) goto out_unregister_pppol2tp_proto; +#ifdef CONFIG_L2TP_V3 + err = l2tp_nl_register_ops(L2TP_PWTYPE_PPP, &pppol2tp_nl_cmd_ops); + if (err) + goto out_unregister_pppox; +#endif + printk(KERN_INFO "PPPoL2TP kernel driver, %s\n", PPPOL2TP_DRV_VERSION); out: return err; + +#ifdef CONFIG_L2TP_V3 +out_unregister_pppox: + unregister_pppox_proto(PX_PROTO_OL2TP); +#endif out_unregister_pppol2tp_proto: proto_unregister(&pppol2tp_sk_proto); out_unregister_pppol2tp_pernet: @@ -1690,6 +1803,9 @@ out_unregister_pppol2tp_pernet: static void __exit pppol2tp_exit(void) { +#ifdef CONFIG_L2TP_V3 + l2tp_nl_unregister_ops(L2TP_PWTYPE_PPP); +#endif unregister_pppox_proto(PX_PROTO_OL2TP); proto_unregister(&pppol2tp_sk_proto); unregister_pernet_device(&pppol2tp_net_ops); -- cgit v1.2.3 From 1f8438a853667d48055ad38384c63e94b32c6578 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Sat, 3 Apr 2010 15:09:04 -0700 Subject: icmp: Account for ICMP out errors When ip_append() fails because of socket limit or memory shortage, increment ICMP_MIB_OUTERRORS counter, so that "netstat -s" can report these errors. LANG=C netstat -s | grep "ICMP messages failed" 0 ICMP messages failed For IPV6, implement ICMP6_MIB_OUTERRORS counter as well. # grep Icmp6OutErrors /proc/net/dev_snmp6/* /proc/net/dev_snmp6/eth0:Icmp6OutErrors 0 /proc/net/dev_snmp6/lo:Icmp6OutErrors 0 Signed-off-by: Eric Dumazet Signed-off-by: David S. Miller --- include/linux/snmp.h | 1 + net/ipv4/icmp.c | 5 +++-- net/ipv6/icmp.c | 2 ++ net/ipv6/proc.c | 1 + 4 files changed, 7 insertions(+), 2 deletions(-) (limited to 'include/linux') diff --git a/include/linux/snmp.h b/include/linux/snmp.h index d2a9aa3c6c8..52797714ade 100644 --- a/include/linux/snmp.h +++ b/include/linux/snmp.h @@ -100,6 +100,7 @@ enum ICMP6_MIB_INMSGS, /* InMsgs */ ICMP6_MIB_INERRORS, /* InErrors */ ICMP6_MIB_OUTMSGS, /* OutMsgs */ + ICMP6_MIB_OUTERRORS, /* OutErrors */ __ICMP6_MIB_MAX }; diff --git a/net/ipv4/icmp.c b/net/ipv4/icmp.c index 4b4c2bcd15d..d2aa7438c52 100644 --- a/net/ipv4/icmp.c +++ b/net/ipv4/icmp.c @@ -330,9 +330,10 @@ static void icmp_push_reply(struct icmp_bxm *icmp_param, if (ip_append_data(sk, icmp_glue_bits, icmp_param, icmp_param->data_len+icmp_param->head_len, icmp_param->head_len, - ipc, rt, MSG_DONTWAIT) < 0) + ipc, rt, MSG_DONTWAIT) < 0) { + ICMP_INC_STATS_BH(sock_net(sk), ICMP_MIB_OUTERRORS); ip_flush_pending_frames(sk); - else if ((skb = skb_peek(&sk->sk_write_queue)) != NULL) { + } else if ((skb = skb_peek(&sk->sk_write_queue)) != NULL) { struct icmphdr *icmph = icmp_hdr(skb); __wsum csum = 0; struct sk_buff *skb1; diff --git a/net/ipv6/icmp.c b/net/ipv6/icmp.c index eb9abe24bdf..a00c18aa6c8 100644 --- a/net/ipv6/icmp.c +++ b/net/ipv6/icmp.c @@ -482,6 +482,7 @@ route_done: np->tclass, NULL, &fl, (struct rt6_info*)dst, MSG_DONTWAIT); if (err) { + ICMP6_INC_STATS_BH(net, idev, ICMP6_MIB_OUTMSGS); ip6_flush_pending_frames(sk); goto out_put; } @@ -562,6 +563,7 @@ static void icmpv6_echo_reply(struct sk_buff *skb) (struct rt6_info*)dst, MSG_DONTWAIT); if (err) { + ICMP6_INC_STATS_BH(net, idev, ICMP6_MIB_OUTMSGS); ip6_flush_pending_frames(sk); goto out_put; } diff --git a/net/ipv6/proc.c b/net/ipv6/proc.c index 58344c0fbd1..458eabfbe13 100644 --- a/net/ipv6/proc.c +++ b/net/ipv6/proc.c @@ -97,6 +97,7 @@ static const struct snmp_mib snmp6_icmp6_list[] = { SNMP_MIB_ITEM("Icmp6InMsgs", ICMP6_MIB_INMSGS), SNMP_MIB_ITEM("Icmp6InErrors", ICMP6_MIB_INERRORS), SNMP_MIB_ITEM("Icmp6OutMsgs", ICMP6_MIB_OUTMSGS), + SNMP_MIB_ITEM("Icmp6OutErrors", ICMP6_MIB_OUTERRORS), SNMP_MIB_SENTINEL }; -- cgit v1.2.3 From 8a64c0f6b7ec7f758c4ef445e49f479e27fa2236 Mon Sep 17 00:00:00 2001 From: Daniel Mack Date: Tue, 6 Apr 2010 10:52:44 +0200 Subject: libertas/sdio: 8686: set ECSI bit for 1-bit transfers When operating in 1-bit mode, SDAT1 is used as dedicated interrupt line. However, the 8686 will only drive this line when the ECSI bit is set in the CCCR_IF register. Thanks to Alagu Sankar for pointing me in the right direction. Signed-off-by: Daniel Mack Cc: Alagu Sankar Cc: Volker Ernst Cc: Dan Williams Cc: John W. Linville Cc: Holger Schurig Cc: Bing Zhao Cc: libertas-dev@lists.infradead.org Cc: linux-wireless@vger.kernel.org Cc: linux-mmc@vger.kernel.org Acked-by: Dan Williams Signed-off-by: John W. Linville --- drivers/net/wireless/libertas/if_sdio.c | 22 ++++++++++++++++++++++ include/linux/mmc/sdio.h | 2 ++ 2 files changed, 24 insertions(+) (limited to 'include/linux') diff --git a/drivers/net/wireless/libertas/if_sdio.c b/drivers/net/wireless/libertas/if_sdio.c index 7a73f625273..33206a98a57 100644 --- a/drivers/net/wireless/libertas/if_sdio.c +++ b/drivers/net/wireless/libertas/if_sdio.c @@ -34,6 +34,8 @@ #include #include #include +#include +#include #include "host.h" #include "decl.h" @@ -942,6 +944,7 @@ static int if_sdio_probe(struct sdio_func *func, int ret, i; unsigned int model; struct if_sdio_packet *packet; + struct mmc_host *host = func->card->host; lbs_deb_enter(LBS_DEB_SDIO); @@ -1022,6 +1025,25 @@ static int if_sdio_probe(struct sdio_func *func, if (ret) goto disable; + /* For 1-bit transfers to the 8686 model, we need to enable the + * interrupt flag in the CCCR register. Set the MMC_QUIRK_LENIENT_FN0 + * bit to allow access to non-vendor registers. */ + if ((card->model == IF_SDIO_MODEL_8686) && + (host->caps & MMC_CAP_SDIO_IRQ) && + (host->ios.bus_width == MMC_BUS_WIDTH_1)) { + u8 reg; + + func->card->quirks |= MMC_QUIRK_LENIENT_FN0; + reg = sdio_f0_readb(func, SDIO_CCCR_IF, &ret); + if (ret) + goto release_int; + + reg |= SDIO_BUS_ECSI; + sdio_f0_writeb(func, reg, SDIO_CCCR_IF, &ret); + if (ret) + goto release_int; + } + card->ioport = sdio_readb(func, IF_SDIO_IOPORT, &ret); if (ret) goto release_int; diff --git a/include/linux/mmc/sdio.h b/include/linux/mmc/sdio.h index 47ba464f517..118f0295a57 100644 --- a/include/linux/mmc/sdio.h +++ b/include/linux/mmc/sdio.h @@ -94,6 +94,8 @@ #define SDIO_BUS_WIDTH_1BIT 0x00 #define SDIO_BUS_WIDTH_4BIT 0x02 +#define SDIO_BUS_ECSI 0x20 /* Enable continuous SPI interrupt */ +#define SDIO_BUS_SCSI 0x40 /* Support continuous SPI interrupt */ #define SDIO_BUS_CD_DISABLE 0x80 /* disable pull-up on DAT3 (pin 1) */ -- cgit v1.2.3 From c6537d6742985da1fbf12ae26cde6a096fd35b5c Mon Sep 17 00:00:00 2001 From: Jon Paul Maloy Date: Tue, 6 Apr 2010 11:40:52 +0000 Subject: TIPC: Updated topology subscription protocol according to latest spec This patch makes it explicit in the API that all fields in subscriptions and events exchanged with the Topology Server must be in network byte order. It also ensures that all fields of a subscription are compared when cancelling a subscription, in order to avoid inadvertent cancelling of the wrong subscription. Finally, the tipc module version is updated to 2.0.0, to reflect the API change. Signed-off-by: Jon Maloy Signed-off-by: David S. Miller --- include/linux/tipc.h | 30 ++++++++++++------------------ net/tipc/core.c | 2 +- net/tipc/subscr.c | 15 ++++++++++----- 3 files changed, 23 insertions(+), 24 deletions(-) (limited to 'include/linux') diff --git a/include/linux/tipc.h b/include/linux/tipc.h index 3d92396639d..9536d8aeadf 100644 --- a/include/linux/tipc.h +++ b/include/linux/tipc.h @@ -127,23 +127,17 @@ static inline unsigned int tipc_node(__u32 addr) * TIPC topology subscription service definitions */ -#define TIPC_SUB_PORTS 0x01 /* filter for port availability */ -#define TIPC_SUB_SERVICE 0x02 /* filter for service availability */ -#define TIPC_SUB_CANCEL 0x04 /* cancel a subscription */ -#if 0 -/* The following filter options are not currently implemented */ -#define TIPC_SUB_NO_BIND_EVTS 0x04 /* filter out "publish" events */ -#define TIPC_SUB_NO_UNBIND_EVTS 0x08 /* filter out "withdraw" events */ -#define TIPC_SUB_SINGLE_EVT 0x10 /* expire after first event */ -#endif +#define TIPC_SUB_SERVICE 0x00 /* Filter for service availability */ +#define TIPC_SUB_PORTS 0x01 /* Filter for port availability */ +#define TIPC_SUB_CANCEL 0x04 /* Cancel a subscription */ #define TIPC_WAIT_FOREVER ~0 /* timeout for permanent subscription */ struct tipc_subscr { - struct tipc_name_seq seq; /* name sequence of interest */ - __u32 timeout; /* subscription duration (in ms) */ - __u32 filter; /* bitmask of filter options */ - char usr_handle[8]; /* available for subscriber use */ + struct tipc_name_seq seq; /* NBO. Name sequence of interest */ + __u32 timeout; /* NBO. Subscription duration (in ms) */ + __u32 filter; /* NBO. Bitmask of filter options */ + char usr_handle[8]; /* Opaque. Available for subscriber use */ }; #define TIPC_PUBLISHED 1 /* publication event */ @@ -151,11 +145,11 @@ struct tipc_subscr { #define TIPC_SUBSCR_TIMEOUT 3 /* subscription timeout event */ struct tipc_event { - __u32 event; /* event type */ - __u32 found_lower; /* matching name seq instances */ - __u32 found_upper; /* " " " " */ - struct tipc_portid port; /* associated port */ - struct tipc_subscr s; /* associated subscription */ + __u32 event; /* NBO. Event type, as defined above */ + __u32 found_lower; /* NBO. Matching name seq instances */ + __u32 found_upper; /* " " " " " */ + struct tipc_portid port; /* NBO. Associated port */ + struct tipc_subscr s; /* Original, associated subscription */ }; /* diff --git a/net/tipc/core.c b/net/tipc/core.c index 52c571fedbe..4e84c8431f3 100644 --- a/net/tipc/core.c +++ b/net/tipc/core.c @@ -49,7 +49,7 @@ #include "config.h" -#define TIPC_MOD_VER "1.6.4" +#define TIPC_MOD_VER "2.0.0" #ifndef CONFIG_TIPC_ZONES #define CONFIG_TIPC_ZONES 3 diff --git a/net/tipc/subscr.c b/net/tipc/subscr.c index ff123e56114..ab6eab4c45e 100644 --- a/net/tipc/subscr.c +++ b/net/tipc/subscr.c @@ -274,7 +274,7 @@ static void subscr_cancel(struct tipc_subscr *s, { struct subscription *sub; struct subscription *sub_temp; - __u32 type, lower, upper; + __u32 type, lower, upper, timeout, filter; int found = 0; /* Find first matching subscription, exit if not found */ @@ -282,12 +282,18 @@ static void subscr_cancel(struct tipc_subscr *s, type = ntohl(s->seq.type); lower = ntohl(s->seq.lower); upper = ntohl(s->seq.upper); + timeout = ntohl(s->timeout); + filter = ntohl(s->filter) & ~TIPC_SUB_CANCEL; list_for_each_entry_safe(sub, sub_temp, &subscriber->subscription_list, subscription_list) { if ((type == sub->seq.type) && (lower == sub->seq.lower) && - (upper == sub->seq.upper)) { + (upper == sub->seq.upper) && + (timeout == sub->timeout) && + (filter == sub->filter) && + !memcmp(s->usr_handle,sub->evt.s.usr_handle, + sizeof(s->usr_handle)) ){ found = 1; break; } @@ -304,7 +310,7 @@ static void subscr_cancel(struct tipc_subscr *s, k_term_timer(&sub->timer); spin_lock_bh(subscriber->lock); } - dbg("Cancel: removing sub %u,%u,%u from subscriber %x list\n", + dbg("Cancel: removing sub %u,%u,%u from subscriber %p list\n", sub->seq.type, sub->seq.lower, sub->seq.upper, subscriber); subscr_del(sub); } @@ -352,8 +358,7 @@ static struct subscription *subscr_subscribe(struct tipc_subscr *s, sub->seq.upper = ntohl(s->seq.upper); sub->timeout = ntohl(s->timeout); sub->filter = ntohl(s->filter); - if ((!(sub->filter & TIPC_SUB_PORTS) == - !(sub->filter & TIPC_SUB_SERVICE)) || + if ((sub->filter && (sub->filter != TIPC_SUB_PORTS)) || (sub->seq.lower > sub->seq.upper)) { warn("Subscription rejected, illegal request\n"); kfree(sub); -- cgit v1.2.3 From d5cdfacb35ed886271d1ccfffbded98d3447da17 Mon Sep 17 00:00:00 2001 From: Jouni Malinen Date: Sun, 4 Apr 2010 09:37:19 +0300 Subject: cfg80211: Add local-state-change-only auth/deauth/disassoc cfg80211 is quite strict on allowing authentication and association commands only in certain states. In order to meet these requirements, user space applications may need to clear authentication or association state in some cases. Currently, this can be done with deauth/disassoc command, but that ends up sending out Deauthentication or Disassociation frame unnecessarily. Add a new nl80211 attribute to allow this sending of the frame be skipped, but with all other deauth/disassoc operations being completed. Similar state change is also needed for IEEE 802.11r FT protocol in the FT-over-DS case which does not use Authentication frame exchange in a transition to another BSS. For this to work with cfg80211, an authentication entry needs to be created for the target BSS without sending out an Authentication frame. The nl80211 authentication command can be used for this purpose, too, with the new attribute to indicate that the command is only for changing local state. This enables wpa_supplicant to complete FT-over-DS transition successfully. Signed-off-by: Jouni Malinen Signed-off-by: John W. Linville --- include/linux/nl80211.h | 8 ++++++++ include/net/cfg80211.h | 11 +++++++++++ net/mac80211/mlme.c | 23 +++++++++++++++-------- net/wireless/core.h | 15 ++++++++++----- net/wireless/mlme.c | 39 ++++++++++++++++++++++++++++----------- net/wireless/nl80211.c | 19 ++++++++++++++++--- net/wireless/sme.c | 15 +++++++++------ 7 files changed, 97 insertions(+), 33 deletions(-) (limited to 'include/linux') diff --git a/include/linux/nl80211.h b/include/linux/nl80211.h index daf6a3432b9..2ea3edeee7a 100644 --- a/include/linux/nl80211.h +++ b/include/linux/nl80211.h @@ -703,6 +703,12 @@ enum nl80211_commands { * @NL80211_ATTR_CQM: connection quality monitor configuration in a * nested attribute with %NL80211_ATTR_CQM_* sub-attributes. * + * @NL80211_ATTR_LOCAL_STATE_CHANGE: Flag attribute to indicate that a command + * is requesting a local authentication/association state change without + * invoking actual management frame exchange. This can be used with + * NL80211_CMD_AUTHENTICATE, NL80211_CMD_DEAUTHENTICATE, + * NL80211_CMD_DISASSOCIATE. + * * @NL80211_ATTR_MAX: highest attribute number currently defined * @__NL80211_ATTR_AFTER_LAST: internal use */ @@ -856,6 +862,8 @@ enum nl80211_attrs { NL80211_ATTR_CQM, + NL80211_ATTR_LOCAL_STATE_CHANGE, + /* add attributes here, update the policy in nl80211.c */ __NL80211_ATTR_AFTER_LAST, diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index 868cfd3b972..37cebd3aa0f 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -704,6 +704,10 @@ struct cfg80211_crypto_settings { * @key_len: length of WEP key for shared key authentication * @key_idx: index of WEP key for shared key authentication * @key: WEP key for shared key authentication + * @local_state_change: This is a request for a local state only, i.e., no + * Authentication frame is to be transmitted and authentication state is + * to be changed without having to wait for a response from the peer STA + * (AP). */ struct cfg80211_auth_request { struct cfg80211_bss *bss; @@ -712,6 +716,7 @@ struct cfg80211_auth_request { enum nl80211_auth_type auth_type; const u8 *key; u8 key_len, key_idx; + bool local_state_change; }; /** @@ -744,12 +749,15 @@ struct cfg80211_assoc_request { * @ie: Extra IEs to add to Deauthentication frame or %NULL * @ie_len: Length of ie buffer in octets * @reason_code: The reason code for the deauthentication + * @local_state_change: This is a request for a local state only, i.e., no + * Deauthentication frame is to be transmitted. */ struct cfg80211_deauth_request { struct cfg80211_bss *bss; const u8 *ie; size_t ie_len; u16 reason_code; + bool local_state_change; }; /** @@ -762,12 +770,15 @@ struct cfg80211_deauth_request { * @ie: Extra IEs to add to Disassociation frame or %NULL * @ie_len: Length of ie buffer in octets * @reason_code: The reason code for the disassociation + * @local_state_change: This is a request for a local state only, i.e., no + * Disassociation frame is to be transmitted. */ struct cfg80211_disassoc_request { struct cfg80211_bss *bss; const u8 *ie; size_t ie_len; u16 reason_code; + bool local_state_change; }; /** diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index 57a3c62139e..4c189d0be4a 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -210,7 +210,7 @@ static u32 ieee80211_enable_ht(struct ieee80211_sub_if_data *sdata, static void ieee80211_send_deauth_disassoc(struct ieee80211_sub_if_data *sdata, const u8 *bssid, u16 stype, u16 reason, - void *cookie) + void *cookie, bool send_frame) { struct ieee80211_local *local = sdata->local; struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; @@ -247,7 +247,11 @@ static void ieee80211_send_deauth_disassoc(struct ieee80211_sub_if_data *sdata, cfg80211_send_disassoc(sdata->dev, (u8 *)mgmt, skb->len); if (!(ifmgd->flags & IEEE80211_STA_MFP_ENABLED)) IEEE80211_SKB_CB(skb)->flags |= IEEE80211_TX_INTFL_DONT_ENCRYPT; - ieee80211_tx_skb(sdata, skb); + + if (send_frame) + ieee80211_tx_skb(sdata, skb); + else + kfree_skb(skb); } void ieee80211_send_pspoll(struct ieee80211_local *local, @@ -980,7 +984,7 @@ static void __ieee80211_connection_loss(struct ieee80211_sub_if_data *sdata) ieee80211_send_deauth_disassoc(sdata, bssid, IEEE80211_STYPE_DEAUTH, WLAN_REASON_DISASSOC_DUE_TO_INACTIVITY, - NULL); + NULL, true); } void ieee80211_beacon_connection_loss_work(struct work_struct *work) @@ -1724,7 +1728,7 @@ static void ieee80211_sta_work(struct work_struct *work) ieee80211_send_deauth_disassoc(sdata, bssid, IEEE80211_STYPE_DEAUTH, WLAN_REASON_DISASSOC_DUE_TO_INACTIVITY, - NULL); + NULL, true); mutex_lock(&ifmgd->mtx); } } @@ -1908,6 +1912,9 @@ int ieee80211_mgd_auth(struct ieee80211_sub_if_data *sdata, struct ieee80211_work *wk; u16 auth_alg; + if (req->local_state_change) + return 0; /* no need to update mac80211 state */ + switch (req->auth_type) { case NL80211_AUTHTYPE_OPEN_SYSTEM: auth_alg = WLAN_AUTH_OPEN; @@ -2163,9 +2170,9 @@ int ieee80211_mgd_deauth(struct ieee80211_sub_if_data *sdata, printk(KERN_DEBUG "%s: deauthenticating from %pM by local choice (reason=%d)\n", sdata->name, bssid, req->reason_code); - ieee80211_send_deauth_disassoc(sdata, bssid, - IEEE80211_STYPE_DEAUTH, req->reason_code, - cookie); + ieee80211_send_deauth_disassoc(sdata, bssid, IEEE80211_STYPE_DEAUTH, + req->reason_code, cookie, + !req->local_state_change); ieee80211_recalc_idle(sdata->local); @@ -2202,7 +2209,7 @@ int ieee80211_mgd_disassoc(struct ieee80211_sub_if_data *sdata, ieee80211_send_deauth_disassoc(sdata, req->bss->bssid, IEEE80211_STYPE_DISASSOC, req->reason_code, - cookie); + cookie, !req->local_state_change); sta_info_destroy_addr(sdata, bssid); ieee80211_recalc_idle(sdata->local); diff --git a/net/wireless/core.h b/net/wireless/core.h index d52da913145..b2234b436ea 100644 --- a/net/wireless/core.h +++ b/net/wireless/core.h @@ -293,13 +293,15 @@ int __cfg80211_mlme_auth(struct cfg80211_registered_device *rdev, const u8 *bssid, const u8 *ssid, int ssid_len, const u8 *ie, int ie_len, - const u8 *key, int key_len, int key_idx); + const u8 *key, int key_len, int key_idx, + bool local_state_change); int cfg80211_mlme_auth(struct cfg80211_registered_device *rdev, struct net_device *dev, struct ieee80211_channel *chan, enum nl80211_auth_type auth_type, const u8 *bssid, const u8 *ssid, int ssid_len, const u8 *ie, int ie_len, - const u8 *key, int key_len, int key_idx); + const u8 *key, int key_len, int key_idx, + bool local_state_change); int __cfg80211_mlme_assoc(struct cfg80211_registered_device *rdev, struct net_device *dev, struct ieee80211_channel *chan, @@ -315,13 +317,16 @@ int cfg80211_mlme_assoc(struct cfg80211_registered_device *rdev, struct cfg80211_crypto_settings *crypt); int __cfg80211_mlme_deauth(struct cfg80211_registered_device *rdev, struct net_device *dev, const u8 *bssid, - const u8 *ie, int ie_len, u16 reason); + const u8 *ie, int ie_len, u16 reason, + bool local_state_change); int cfg80211_mlme_deauth(struct cfg80211_registered_device *rdev, struct net_device *dev, const u8 *bssid, - const u8 *ie, int ie_len, u16 reason); + const u8 *ie, int ie_len, u16 reason, + bool local_state_change); int cfg80211_mlme_disassoc(struct cfg80211_registered_device *rdev, struct net_device *dev, const u8 *bssid, - const u8 *ie, int ie_len, u16 reason); + const u8 *ie, int ie_len, u16 reason, + bool local_state_change); void cfg80211_mlme_down(struct cfg80211_registered_device *rdev, struct net_device *dev); void __cfg80211_connect_result(struct net_device *dev, const u8 *bssid, diff --git a/net/wireless/mlme.c b/net/wireless/mlme.c index 0855f0d3234..387dd2a27d2 100644 --- a/net/wireless/mlme.c +++ b/net/wireless/mlme.c @@ -377,7 +377,8 @@ int __cfg80211_mlme_auth(struct cfg80211_registered_device *rdev, const u8 *bssid, const u8 *ssid, int ssid_len, const u8 *ie, int ie_len, - const u8 *key, int key_len, int key_idx) + const u8 *key, int key_len, int key_idx, + bool local_state_change) { struct wireless_dev *wdev = dev->ieee80211_ptr; struct cfg80211_auth_request req; @@ -407,6 +408,7 @@ int __cfg80211_mlme_auth(struct cfg80211_registered_device *rdev, memset(&req, 0, sizeof(req)); + req.local_state_change = local_state_change; req.ie = ie; req.ie_len = ie_len; req.auth_type = auth_type; @@ -433,12 +435,18 @@ int __cfg80211_mlme_auth(struct cfg80211_registered_device *rdev, goto out; } - wdev->authtry_bsses[slot] = bss; + if (local_state_change) + wdev->auth_bsses[slot] = bss; + else + wdev->authtry_bsses[slot] = bss; cfg80211_hold_bss(bss); err = rdev->ops->auth(&rdev->wiphy, dev, &req); if (err) { - wdev->authtry_bsses[slot] = NULL; + if (local_state_change) + wdev->auth_bsses[slot] = NULL; + else + wdev->authtry_bsses[slot] = NULL; cfg80211_unhold_bss(bss); } @@ -453,14 +461,15 @@ int cfg80211_mlme_auth(struct cfg80211_registered_device *rdev, enum nl80211_auth_type auth_type, const u8 *bssid, const u8 *ssid, int ssid_len, const u8 *ie, int ie_len, - const u8 *key, int key_len, int key_idx) + const u8 *key, int key_len, int key_idx, + bool local_state_change) { int err; wdev_lock(dev->ieee80211_ptr); err = __cfg80211_mlme_auth(rdev, dev, chan, auth_type, bssid, ssid, ssid_len, ie, ie_len, - key, key_len, key_idx); + key, key_len, key_idx, local_state_change); wdev_unlock(dev->ieee80211_ptr); return err; @@ -554,7 +563,8 @@ int cfg80211_mlme_assoc(struct cfg80211_registered_device *rdev, int __cfg80211_mlme_deauth(struct cfg80211_registered_device *rdev, struct net_device *dev, const u8 *bssid, - const u8 *ie, int ie_len, u16 reason) + const u8 *ie, int ie_len, u16 reason, + bool local_state_change) { struct wireless_dev *wdev = dev->ieee80211_ptr; struct cfg80211_deauth_request req; @@ -564,6 +574,7 @@ int __cfg80211_mlme_deauth(struct cfg80211_registered_device *rdev, memset(&req, 0, sizeof(req)); req.reason_code = reason; + req.local_state_change = local_state_change; req.ie = ie; req.ie_len = ie_len; if (wdev->current_bss && @@ -590,13 +601,15 @@ int __cfg80211_mlme_deauth(struct cfg80211_registered_device *rdev, int cfg80211_mlme_deauth(struct cfg80211_registered_device *rdev, struct net_device *dev, const u8 *bssid, - const u8 *ie, int ie_len, u16 reason) + const u8 *ie, int ie_len, u16 reason, + bool local_state_change) { struct wireless_dev *wdev = dev->ieee80211_ptr; int err; wdev_lock(wdev); - err = __cfg80211_mlme_deauth(rdev, dev, bssid, ie, ie_len, reason); + err = __cfg80211_mlme_deauth(rdev, dev, bssid, ie, ie_len, reason, + local_state_change); wdev_unlock(wdev); return err; @@ -604,7 +617,8 @@ int cfg80211_mlme_deauth(struct cfg80211_registered_device *rdev, static int __cfg80211_mlme_disassoc(struct cfg80211_registered_device *rdev, struct net_device *dev, const u8 *bssid, - const u8 *ie, int ie_len, u16 reason) + const u8 *ie, int ie_len, u16 reason, + bool local_state_change) { struct wireless_dev *wdev = dev->ieee80211_ptr; struct cfg80211_disassoc_request req; @@ -619,6 +633,7 @@ static int __cfg80211_mlme_disassoc(struct cfg80211_registered_device *rdev, memset(&req, 0, sizeof(req)); req.reason_code = reason; + req.local_state_change = local_state_change; req.ie = ie; req.ie_len = ie_len; if (memcmp(wdev->current_bss->pub.bssid, bssid, ETH_ALEN) == 0) @@ -631,13 +646,15 @@ static int __cfg80211_mlme_disassoc(struct cfg80211_registered_device *rdev, int cfg80211_mlme_disassoc(struct cfg80211_registered_device *rdev, struct net_device *dev, const u8 *bssid, - const u8 *ie, int ie_len, u16 reason) + const u8 *ie, int ie_len, u16 reason, + bool local_state_change) { struct wireless_dev *wdev = dev->ieee80211_ptr; int err; wdev_lock(wdev); - err = __cfg80211_mlme_disassoc(rdev, dev, bssid, ie, ie_len, reason); + err = __cfg80211_mlme_disassoc(rdev, dev, bssid, ie, ie_len, reason, + local_state_change); wdev_unlock(wdev); return err; diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 95149f30340..df5505b3930 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -150,6 +150,7 @@ static const struct nla_policy nl80211_policy[NL80211_ATTR_MAX+1] = { [NL80211_ATTR_FRAME_MATCH] = { .type = NLA_BINARY, }, [NL80211_ATTR_PS_STATE] = { .type = NLA_U32 }, [NL80211_ATTR_CQM] = { .type = NLA_NESTED, }, + [NL80211_ATTR_LOCAL_STATE_CHANGE] = { .type = NLA_FLAG }, }; /* policy for the attributes */ @@ -3393,6 +3394,7 @@ static int nl80211_authenticate(struct sk_buff *skb, struct genl_info *info) int err, ssid_len, ie_len = 0; enum nl80211_auth_type auth_type; struct key_parse key; + bool local_state_change; if (!is_valid_ie_attr(info->attrs[NL80211_ATTR_IE])) return -EINVAL; @@ -3471,9 +3473,12 @@ static int nl80211_authenticate(struct sk_buff *skb, struct genl_info *info) goto out; } + local_state_change = !!info->attrs[NL80211_ATTR_LOCAL_STATE_CHANGE]; + err = cfg80211_mlme_auth(rdev, dev, chan, auth_type, bssid, ssid, ssid_len, ie, ie_len, - key.p.key, key.p.key_len, key.idx); + key.p.key, key.p.key_len, key.idx, + local_state_change); out: cfg80211_unlock_rdev(rdev); @@ -3650,6 +3655,7 @@ static int nl80211_deauthenticate(struct sk_buff *skb, struct genl_info *info) const u8 *ie = NULL, *bssid; int err, ie_len = 0; u16 reason_code; + bool local_state_change; if (!is_valid_ie_attr(info->attrs[NL80211_ATTR_IE])) return -EINVAL; @@ -3695,7 +3701,10 @@ static int nl80211_deauthenticate(struct sk_buff *skb, struct genl_info *info) ie_len = nla_len(info->attrs[NL80211_ATTR_IE]); } - err = cfg80211_mlme_deauth(rdev, dev, bssid, ie, ie_len, reason_code); + local_state_change = !!info->attrs[NL80211_ATTR_LOCAL_STATE_CHANGE]; + + err = cfg80211_mlme_deauth(rdev, dev, bssid, ie, ie_len, reason_code, + local_state_change); out: cfg80211_unlock_rdev(rdev); @@ -3712,6 +3721,7 @@ static int nl80211_disassociate(struct sk_buff *skb, struct genl_info *info) const u8 *ie = NULL, *bssid; int err, ie_len = 0; u16 reason_code; + bool local_state_change; if (!is_valid_ie_attr(info->attrs[NL80211_ATTR_IE])) return -EINVAL; @@ -3757,7 +3767,10 @@ static int nl80211_disassociate(struct sk_buff *skb, struct genl_info *info) ie_len = nla_len(info->attrs[NL80211_ATTR_IE]); } - err = cfg80211_mlme_disassoc(rdev, dev, bssid, ie, ie_len, reason_code); + local_state_change = !!info->attrs[NL80211_ATTR_LOCAL_STATE_CHANGE]; + + err = cfg80211_mlme_disassoc(rdev, dev, bssid, ie, ie_len, reason_code, + local_state_change); out: cfg80211_unlock_rdev(rdev); diff --git a/net/wireless/sme.c b/net/wireless/sme.c index 17fde0da1b0..17465777eb4 100644 --- a/net/wireless/sme.c +++ b/net/wireless/sme.c @@ -170,7 +170,7 @@ static int cfg80211_conn_do_work(struct wireless_dev *wdev) params->ssid, params->ssid_len, NULL, 0, params->key, params->key_len, - params->key_idx); + params->key_idx, false); case CFG80211_CONN_ASSOCIATE_NEXT: BUG_ON(!rdev->ops->assoc); wdev->conn->state = CFG80211_CONN_ASSOCIATING; @@ -185,12 +185,13 @@ static int cfg80211_conn_do_work(struct wireless_dev *wdev) if (err) __cfg80211_mlme_deauth(rdev, wdev->netdev, params->bssid, NULL, 0, - WLAN_REASON_DEAUTH_LEAVING); + WLAN_REASON_DEAUTH_LEAVING, + false); return err; case CFG80211_CONN_DEAUTH_ASSOC_FAIL: __cfg80211_mlme_deauth(rdev, wdev->netdev, params->bssid, NULL, 0, - WLAN_REASON_DEAUTH_LEAVING); + WLAN_REASON_DEAUTH_LEAVING, false); /* return an error so that we call __cfg80211_connect_result() */ return -EINVAL; default: @@ -675,7 +676,8 @@ void __cfg80211_disconnected(struct net_device *dev, const u8 *ie, continue; bssid = wdev->auth_bsses[i]->pub.bssid; ret = __cfg80211_mlme_deauth(rdev, dev, bssid, NULL, 0, - WLAN_REASON_DEAUTH_LEAVING); + WLAN_REASON_DEAUTH_LEAVING, + false); WARN(ret, "deauth failed: %d\n", ret); } } @@ -934,7 +936,7 @@ int __cfg80211_disconnect(struct cfg80211_registered_device *rdev, /* wdev->conn->params.bssid must be set if > SCANNING */ err = __cfg80211_mlme_deauth(rdev, dev, wdev->conn->params.bssid, - NULL, 0, reason); + NULL, 0, reason, false); if (err) return err; } else { @@ -990,7 +992,8 @@ void cfg80211_sme_disassoc(struct net_device *dev, int idx) memcpy(bssid, wdev->auth_bsses[idx]->pub.bssid, ETH_ALEN); if (__cfg80211_mlme_deauth(rdev, dev, bssid, - NULL, 0, WLAN_REASON_DEAUTH_LEAVING)) { + NULL, 0, WLAN_REASON_DEAUTH_LEAVING, + false)) { /* whatever -- assume gone anyway */ cfg80211_unhold_bss(wdev->auth_bsses[idx]); cfg80211_put_bss(&wdev->auth_bsses[idx]->pub); -- cgit v1.2.3 From 098a607091426e79178b9a6c318d993fea131791 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Tue, 6 Apr 2010 11:18:47 +0200 Subject: mac80211: clean up/fix aggregation code The aggregation code has a number of quirks, like inventing an unneeded WLAN_BACK_TIMER value and leaking memory under certain circumstances during station destruction. Fix these issues by using the regular aggregation session teardown code and blocking new aggregation sessions, all before the station is really destructed. As a side effect, this gets rid of the long code block to destroy aggregation safely. Additionally, rename tid_state_rx which can only have the values IDLE and OPERATIONAL to tid_active_rx to make it easier to understand that there is no bitwise stuff going on on the RX side -- the TX side remains because it needs to keep track of the driver and peer states. Signed-off-by: Johannes Berg Signed-off-by: John W. Linville --- include/linux/ieee80211.h | 1 - net/mac80211/agg-rx.c | 48 +++++++++++++++++--------------------- net/mac80211/debugfs_sta.c | 10 ++++---- net/mac80211/rx.c | 5 ++-- net/mac80211/sta_info.c | 58 ++++++++-------------------------------------- net/mac80211/sta_info.h | 6 ++--- 6 files changed, 40 insertions(+), 88 deletions(-) (limited to 'include/linux') diff --git a/include/linux/ieee80211.h b/include/linux/ieee80211.h index 19984958ab7..e9e03b02cb0 100644 --- a/include/linux/ieee80211.h +++ b/include/linux/ieee80211.h @@ -1324,7 +1324,6 @@ enum ieee80211_back_actioncode { enum ieee80211_back_parties { WLAN_BACK_RECIPIENT = 0, WLAN_BACK_INITIATOR = 1, - WLAN_BACK_TIMER = 2, }; /* SA Query action */ diff --git a/net/mac80211/agg-rx.c b/net/mac80211/agg-rx.c index 7d87f446f03..53233ab50f6 100644 --- a/net/mac80211/agg-rx.c +++ b/net/mac80211/agg-rx.c @@ -22,19 +22,20 @@ void __ieee80211_stop_rx_ba_session(struct sta_info *sta, u16 tid, u16 initiator, u16 reason) { struct ieee80211_local *local = sta->local; + struct tid_ampdu_rx *tid_rx; int i; - /* check if TID is in operational state */ spin_lock_bh(&sta->lock); - if (sta->ampdu_mlme.tid_state_rx[tid] != HT_AGG_STATE_OPERATIONAL) { + + /* check if TID is in operational state */ + if (!sta->ampdu_mlme.tid_active_rx[tid]) { spin_unlock_bh(&sta->lock); return; } - sta->ampdu_mlme.tid_state_rx[tid] = - HT_AGG_STATE_REQ_STOP_BA_MSK | - (initiator << HT_AGG_STATE_INITIATOR_SHIFT); - spin_unlock_bh(&sta->lock); + sta->ampdu_mlme.tid_active_rx[tid] = false; + + tid_rx = sta->ampdu_mlme.tid_rx[tid]; #ifdef CONFIG_MAC80211_HT_DEBUG printk(KERN_DEBUG "Rx BA session stop requested for %pM tid %u\n", @@ -46,37 +47,30 @@ void __ieee80211_stop_rx_ba_session(struct sta_info *sta, u16 tid, printk(KERN_DEBUG "HW problem - can not stop rx " "aggregation for tid %d\n", tid); - /* shutdown timer has not expired */ - if (initiator != WLAN_BACK_TIMER) - del_timer_sync(&sta->ampdu_mlme.tid_rx[tid]->session_timer); - /* check if this is a self generated aggregation halt */ - if (initiator == WLAN_BACK_RECIPIENT || initiator == WLAN_BACK_TIMER) + if (initiator == WLAN_BACK_RECIPIENT) ieee80211_send_delba(sta->sdata, sta->sta.addr, tid, 0, reason); /* free the reordering buffer */ - for (i = 0; i < sta->ampdu_mlme.tid_rx[tid]->buf_size; i++) { - if (sta->ampdu_mlme.tid_rx[tid]->reorder_buf[i]) { + for (i = 0; i < tid_rx->buf_size; i++) { + if (tid_rx->reorder_buf[i]) { /* release the reordered frames */ - dev_kfree_skb(sta->ampdu_mlme.tid_rx[tid]->reorder_buf[i]); - sta->ampdu_mlme.tid_rx[tid]->stored_mpdu_num--; - sta->ampdu_mlme.tid_rx[tid]->reorder_buf[i] = NULL; + dev_kfree_skb(tid_rx->reorder_buf[i]); + tid_rx->stored_mpdu_num--; + tid_rx->reorder_buf[i] = NULL; } } - spin_lock_bh(&sta->lock); /* free resources */ - kfree(sta->ampdu_mlme.tid_rx[tid]->reorder_buf); - kfree(sta->ampdu_mlme.tid_rx[tid]->reorder_time); - - if (!sta->ampdu_mlme.tid_rx[tid]->shutdown) { - kfree(sta->ampdu_mlme.tid_rx[tid]); - sta->ampdu_mlme.tid_rx[tid] = NULL; - } + kfree(tid_rx->reorder_buf); + kfree(tid_rx->reorder_time); + sta->ampdu_mlme.tid_rx[tid] = NULL; - sta->ampdu_mlme.tid_state_rx[tid] = HT_AGG_STATE_IDLE; spin_unlock_bh(&sta->lock); + + del_timer_sync(&tid_rx->session_timer); + kfree(tid_rx); } /* @@ -211,7 +205,7 @@ void ieee80211_process_addba_request(struct ieee80211_local *local, /* examine state machine */ spin_lock_bh(&sta->lock); - if (sta->ampdu_mlme.tid_state_rx[tid] != HT_AGG_STATE_IDLE) { + if (sta->ampdu_mlme.tid_active_rx[tid]) { #ifdef CONFIG_MAC80211_HT_DEBUG if (net_ratelimit()) printk(KERN_DEBUG "unexpected AddBA Req from " @@ -273,7 +267,7 @@ void ieee80211_process_addba_request(struct ieee80211_local *local, } /* change state and send addba resp */ - sta->ampdu_mlme.tid_state_rx[tid] = HT_AGG_STATE_OPERATIONAL; + sta->ampdu_mlme.tid_active_rx[tid] = true; tid_agg_rx->dialog_token = dialog_token; tid_agg_rx->ssn = start_seq_num; tid_agg_rx->head_seq_num = start_seq_num; diff --git a/net/mac80211/debugfs_sta.c b/net/mac80211/debugfs_sta.c index 23e72003457..740ff6c5b92 100644 --- a/net/mac80211/debugfs_sta.c +++ b/net/mac80211/debugfs_sta.c @@ -119,7 +119,7 @@ STA_OPS(last_seq_ctrl); static ssize_t sta_agg_status_read(struct file *file, char __user *userbuf, size_t count, loff_t *ppos) { - char buf[64 + STA_TID_NUM * 40], *p = buf; + char buf[71 + STA_TID_NUM * 40], *p = buf; int i; struct sta_info *sta = file->private_data; @@ -127,16 +127,16 @@ static ssize_t sta_agg_status_read(struct file *file, char __user *userbuf, p += scnprintf(p, sizeof(buf) + buf - p, "next dialog_token: %#02x\n", sta->ampdu_mlme.dialog_token_allocator + 1); p += scnprintf(p, sizeof(buf) + buf - p, - "TID\t\tRX\tDTKN\tSSN\t\tTX\tDTKN\tSSN\tpending\n"); + "TID\t\tRX active\tDTKN\tSSN\t\tTX\tDTKN\tSSN\tpending\n"); for (i = 0; i < STA_TID_NUM; i++) { p += scnprintf(p, sizeof(buf) + buf - p, "%02d", i); p += scnprintf(p, sizeof(buf) + buf - p, "\t\t%x", - sta->ampdu_mlme.tid_state_rx[i]); + sta->ampdu_mlme.tid_active_rx[i]); p += scnprintf(p, sizeof(buf) + buf - p, "\t%#.2x", - sta->ampdu_mlme.tid_state_rx[i] ? + sta->ampdu_mlme.tid_active_rx[i] ? sta->ampdu_mlme.tid_rx[i]->dialog_token : 0); p += scnprintf(p, sizeof(buf) + buf - p, "\t%#.3x", - sta->ampdu_mlme.tid_state_rx[i] ? + sta->ampdu_mlme.tid_active_rx[i] ? sta->ampdu_mlme.tid_rx[i]->ssn : 0); p += scnprintf(p, sizeof(buf) + buf - p, "\t\t%x", diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c index c02e43b50ac..62053fa711f 100644 --- a/net/mac80211/rx.c +++ b/net/mac80211/rx.c @@ -720,7 +720,7 @@ static void ieee80211_rx_reorder_ampdu(struct ieee80211_rx_data *rx, tid = *ieee80211_get_qos_ctl(hdr) & IEEE80211_QOS_CTL_TID_MASK; - if (sta->ampdu_mlme.tid_state_rx[tid] != HT_AGG_STATE_OPERATIONAL) + if (!sta->ampdu_mlme.tid_active_rx[tid]) goto dont_reorder; tid_agg_rx = sta->ampdu_mlme.tid_rx[tid]; @@ -1805,8 +1805,7 @@ ieee80211_rx_h_ctrl(struct ieee80211_rx_data *rx, struct sk_buff_head *frames) if (!rx->sta) return RX_DROP_MONITOR; tid = le16_to_cpu(bar->control) >> 12; - if (rx->sta->ampdu_mlme.tid_state_rx[tid] - != HT_AGG_STATE_OPERATIONAL) + if (!rx->sta->ampdu_mlme.tid_active_rx[tid]) return RX_DROP_MONITOR; tid_agg_rx = rx->sta->ampdu_mlme.tid_rx[tid]; diff --git a/net/mac80211/sta_info.c b/net/mac80211/sta_info.c index bd11753c152..5bf044b92dc 100644 --- a/net/mac80211/sta_info.c +++ b/net/mac80211/sta_info.c @@ -238,9 +238,6 @@ struct sta_info *sta_info_alloc(struct ieee80211_sub_if_data *sdata, * enable session_timer's data differentiation. refer to * sta_rx_agg_session_timer_expired for useage */ sta->timer_to_tid[i] = i; - /* rx */ - sta->ampdu_mlme.tid_state_rx[i] = HT_AGG_STATE_IDLE; - sta->ampdu_mlme.tid_rx[i] = NULL; /* tx */ sta->ampdu_mlme.tid_state_tx[i] = HT_AGG_STATE_IDLE; sta->ampdu_mlme.tid_tx[i] = NULL; @@ -606,7 +603,7 @@ static int __must_check __sta_info_destroy(struct sta_info *sta) struct ieee80211_sub_if_data *sdata; struct sk_buff *skb; unsigned long flags; - int ret, i; + int ret; might_sleep(); @@ -616,6 +613,15 @@ static int __must_check __sta_info_destroy(struct sta_info *sta) local = sta->local; sdata = sta->sdata; + /* + * Before removing the station from the driver and + * rate control, it might still start new aggregation + * sessions -- block that to make sure the tear-down + * will be sufficient. + */ + set_sta_flags(sta, WLAN_STA_BLOCK_BA); + ieee80211_sta_tear_down_BA_sessions(sta); + spin_lock_irqsave(&local->sta_lock, flags); ret = sta_info_hash_del(local, sta); /* this might still be the pending list ... which is fine */ @@ -700,50 +706,6 @@ static int __must_check __sta_info_destroy(struct sta_info *sta) while ((skb = skb_dequeue(&sta->tx_filtered)) != NULL) dev_kfree_skb_any(skb); - for (i = 0; i < STA_TID_NUM; i++) { - struct tid_ampdu_rx *tid_rx; - struct tid_ampdu_tx *tid_tx; - - spin_lock_bh(&sta->lock); - tid_rx = sta->ampdu_mlme.tid_rx[i]; - /* Make sure timer won't free the tid_rx struct, see below */ - if (tid_rx) - tid_rx->shutdown = true; - - spin_unlock_bh(&sta->lock); - - /* - * Outside spinlock - shutdown is true now so that the timer - * won't free tid_rx, we have to do that now. Can't let the - * timer do it because we have to sync the timer outside the - * lock that it takes itself. - */ - if (tid_rx) { - del_timer_sync(&tid_rx->session_timer); - kfree(tid_rx); - } - - /* - * No need to do such complications for TX agg sessions, the - * path leading to freeing the tid_tx struct goes via a call - * from the driver, and thus needs to look up the sta struct - * again, which cannot be found when we get here. Hence, we - * just need to delete the timer and free the aggregation - * info; we won't be telling the peer about it then but that - * doesn't matter if we're not talking to it again anyway. - */ - tid_tx = sta->ampdu_mlme.tid_tx[i]; - if (tid_tx) { - del_timer_sync(&tid_tx->addba_resp_timer); - /* - * STA removed while aggregation session being - * started? Bit odd, but purge frames anyway. - */ - skb_queue_purge(&tid_tx->pending); - kfree(tid_tx); - } - } - __sta_info_free(local, sta); return 0; diff --git a/net/mac80211/sta_info.h b/net/mac80211/sta_info.h index 57e81758d6f..48a5e80957f 100644 --- a/net/mac80211/sta_info.h +++ b/net/mac80211/sta_info.h @@ -36,7 +36,7 @@ * frame to this station is transmitted. * @WLAN_STA_MFP: Management frame protection is used with this STA. * @WLAN_STA_BLOCK_BA: Used to deny ADDBA requests (both TX and RX) - * during suspend/resume. + * during suspend/resume and station removal. * @WLAN_STA_PS_DRIVER: driver requires keeping this station in * power-save mode logically to flush frames that might still * be in the queues @@ -106,7 +106,6 @@ struct tid_ampdu_tx { * @buf_size: buffer size for incoming A-MPDUs * @timeout: reset timer value (in TUs). * @dialog_token: dialog token for aggregation session - * @shutdown: this session is being shut down due to STA removal */ struct tid_ampdu_rx { struct sk_buff **reorder_buf; @@ -118,7 +117,6 @@ struct tid_ampdu_rx { u16 buf_size; u16 timeout; u8 dialog_token; - bool shutdown; }; /** @@ -156,7 +154,7 @@ enum plink_state { */ struct sta_ampdu_mlme { /* rx */ - u8 tid_state_rx[STA_TID_NUM]; + bool tid_active_rx[STA_TID_NUM]; struct tid_ampdu_rx *tid_rx[STA_TID_NUM]; /* tx */ u8 tid_state_tx[STA_TID_NUM]; -- cgit v1.2.3 From 18e225f257663c59ff9d4482f07ffd06361fc2ec Mon Sep 17 00:00:00 2001 From: Pavel Roskin Date: Wed, 7 Apr 2010 16:40:09 -0700 Subject: net: fix definition of netdev_for_each_mc_addr() The first argument should be called ha, not mclist. All callers use the name "ha", but if they used a different name, there would be a compile error. Signed-off-by: Pavel Roskin Signed-off-by: David S. Miller --- include/linux/netdevice.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include/linux') diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index a343a21ba8b..d1a21b576a4 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -260,7 +260,7 @@ struct netdev_hw_addr_list { #define netdev_mc_count(dev) netdev_hw_addr_list_count(&(dev)->mc) #define netdev_mc_empty(dev) netdev_hw_addr_list_empty(&(dev)->mc) -#define netdev_for_each_mc_addr(mclist, dev) \ +#define netdev_for_each_mc_addr(ha, dev) \ netdev_hw_addr_list_for_each(ha, &(dev)->mc) struct hh_cache { -- cgit v1.2.3 From 97f8aefbbfb5aa5c9944e5fa8149f1fdaf71c7b6 Mon Sep 17 00:00:00 2001 From: chavey Date: Wed, 7 Apr 2010 21:54:42 -0700 Subject: net: fix ethtool coding style errors and warnings Fix coding style errors and warnings output while running checkpatch.pl on the files net/core/ethtool.c and include/linux/ethtool.h Signed-off-by: chavey Signed-off-by: David S. Miller --- include/linux/ethtool.h | 115 +++++++++++++++++++++------------------ net/core/ethtool.c | 141 +++++++++++++++++++++++++----------------------- 2 files changed, 136 insertions(+), 120 deletions(-) (limited to 'include/linux') diff --git a/include/linux/ethtool.h b/include/linux/ethtool.h index b391969a0dd..276b40a1683 100644 --- a/include/linux/ethtool.h +++ b/include/linux/ethtool.h @@ -491,12 +491,12 @@ void ethtool_ntuple_flush(struct net_device *dev); * get_ufo: Report whether UDP fragmentation offload is enabled * set_ufo: Turn UDP fragmentation offload on or off * self_test: Run specified self-tests - * get_strings: Return a set of strings that describe the requested objects + * get_strings: Return a set of strings that describe the requested objects * phys_id: Identify the device * get_stats: Return statistics about the device * get_flags: get 32-bit flags bitmap * set_flags: set 32-bit flags bitmap - * + * * Description: * * get_settings: @@ -532,14 +532,20 @@ struct ethtool_ops { int (*nway_reset)(struct net_device *); u32 (*get_link)(struct net_device *); int (*get_eeprom_len)(struct net_device *); - int (*get_eeprom)(struct net_device *, struct ethtool_eeprom *, u8 *); - int (*set_eeprom)(struct net_device *, struct ethtool_eeprom *, u8 *); + int (*get_eeprom)(struct net_device *, + struct ethtool_eeprom *, u8 *); + int (*set_eeprom)(struct net_device *, + struct ethtool_eeprom *, u8 *); int (*get_coalesce)(struct net_device *, struct ethtool_coalesce *); int (*set_coalesce)(struct net_device *, struct ethtool_coalesce *); - void (*get_ringparam)(struct net_device *, struct ethtool_ringparam *); - int (*set_ringparam)(struct net_device *, struct ethtool_ringparam *); - void (*get_pauseparam)(struct net_device *, struct ethtool_pauseparam*); - int (*set_pauseparam)(struct net_device *, struct ethtool_pauseparam*); + void (*get_ringparam)(struct net_device *, + struct ethtool_ringparam *); + int (*set_ringparam)(struct net_device *, + struct ethtool_ringparam *); + void (*get_pauseparam)(struct net_device *, + struct ethtool_pauseparam*); + int (*set_pauseparam)(struct net_device *, + struct ethtool_pauseparam*); u32 (*get_rx_csum)(struct net_device *); int (*set_rx_csum)(struct net_device *, u32); u32 (*get_tx_csum)(struct net_device *); @@ -551,21 +557,24 @@ struct ethtool_ops { void (*self_test)(struct net_device *, struct ethtool_test *, u64 *); void (*get_strings)(struct net_device *, u32 stringset, u8 *); int (*phys_id)(struct net_device *, u32); - void (*get_ethtool_stats)(struct net_device *, struct ethtool_stats *, u64 *); + void (*get_ethtool_stats)(struct net_device *, + struct ethtool_stats *, u64 *); int (*begin)(struct net_device *); void (*complete)(struct net_device *); - u32 (*get_ufo)(struct net_device *); - int (*set_ufo)(struct net_device *, u32); - u32 (*get_flags)(struct net_device *); - int (*set_flags)(struct net_device *, u32); - u32 (*get_priv_flags)(struct net_device *); - int (*set_priv_flags)(struct net_device *, u32); + u32 (*get_ufo)(struct net_device *); + int (*set_ufo)(struct net_device *, u32); + u32 (*get_flags)(struct net_device *); + int (*set_flags)(struct net_device *, u32); + u32 (*get_priv_flags)(struct net_device *); + int (*set_priv_flags)(struct net_device *, u32); int (*get_sset_count)(struct net_device *, int); - int (*get_rxnfc)(struct net_device *, struct ethtool_rxnfc *, void *); + int (*get_rxnfc)(struct net_device *, + struct ethtool_rxnfc *, void *); int (*set_rxnfc)(struct net_device *, struct ethtool_rxnfc *); - int (*flash_device)(struct net_device *, struct ethtool_flash *); + int (*flash_device)(struct net_device *, struct ethtool_flash *); int (*reset)(struct net_device *, u32 *); - int (*set_rx_ntuple)(struct net_device *, struct ethtool_rx_ntuple *); + int (*set_rx_ntuple)(struct net_device *, + struct ethtool_rx_ntuple *); int (*get_rx_ntuple)(struct net_device *, u32 stringset, void *); }; #endif /* __KERNEL__ */ @@ -577,29 +586,29 @@ struct ethtool_ops { #define ETHTOOL_GREGS 0x00000004 /* Get NIC registers. */ #define ETHTOOL_GWOL 0x00000005 /* Get wake-on-lan options. */ #define ETHTOOL_SWOL 0x00000006 /* Set wake-on-lan options. */ -#define ETHTOOL_GMSGLVL 0x00000007 /* Get driver message level */ -#define ETHTOOL_SMSGLVL 0x00000008 /* Set driver msg level. */ +#define ETHTOOL_GMSGLVL 0x00000007 /* Get driver message level */ +#define ETHTOOL_SMSGLVL 0x00000008 /* Set driver msg level. */ #define ETHTOOL_NWAY_RST 0x00000009 /* Restart autonegotiation. */ #define ETHTOOL_GLINK 0x0000000a /* Get link status (ethtool_value) */ -#define ETHTOOL_GEEPROM 0x0000000b /* Get EEPROM data */ -#define ETHTOOL_SEEPROM 0x0000000c /* Set EEPROM data. */ +#define ETHTOOL_GEEPROM 0x0000000b /* Get EEPROM data */ +#define ETHTOOL_SEEPROM 0x0000000c /* Set EEPROM data. */ #define ETHTOOL_GCOALESCE 0x0000000e /* Get coalesce config */ #define ETHTOOL_SCOALESCE 0x0000000f /* Set coalesce config. */ #define ETHTOOL_GRINGPARAM 0x00000010 /* Get ring parameters */ #define ETHTOOL_SRINGPARAM 0x00000011 /* Set ring parameters. */ #define ETHTOOL_GPAUSEPARAM 0x00000012 /* Get pause parameters */ #define ETHTOOL_SPAUSEPARAM 0x00000013 /* Set pause parameters. */ -#define ETHTOOL_GRXCSUM 0x00000014 /* Get RX hw csum enable (ethtool_value) */ -#define ETHTOOL_SRXCSUM 0x00000015 /* Set RX hw csum enable (ethtool_value) */ -#define ETHTOOL_GTXCSUM 0x00000016 /* Get TX hw csum enable (ethtool_value) */ -#define ETHTOOL_STXCSUM 0x00000017 /* Set TX hw csum enable (ethtool_value) */ +#define ETHTOOL_GRXCSUM 0x00000014 /* Get RX hw csum enable (ethtool_value) */ +#define ETHTOOL_SRXCSUM 0x00000015 /* Set RX hw csum enable (ethtool_value) */ +#define ETHTOOL_GTXCSUM 0x00000016 /* Get TX hw csum enable (ethtool_value) */ +#define ETHTOOL_STXCSUM 0x00000017 /* Set TX hw csum enable (ethtool_value) */ #define ETHTOOL_GSG 0x00000018 /* Get scatter-gather enable * (ethtool_value) */ #define ETHTOOL_SSG 0x00000019 /* Set scatter-gather enable * (ethtool_value). */ #define ETHTOOL_TEST 0x0000001a /* execute NIC self-test. */ #define ETHTOOL_GSTRINGS 0x0000001b /* get specified string set */ -#define ETHTOOL_PHYS_ID 0x0000001c /* identify the NIC */ +#define ETHTOOL_PHYS_ID 0x0000001c /* identify the NIC */ #define ETHTOOL_GSTATS 0x0000001d /* get NIC-specific statistics */ #define ETHTOOL_GTSO 0x0000001e /* Get TSO enable (ethtool_value) */ #define ETHTOOL_STSO 0x0000001f /* Set TSO enable (ethtool_value) */ @@ -610,24 +619,24 @@ struct ethtool_ops { #define ETHTOOL_SGSO 0x00000024 /* Set GSO enable (ethtool_value) */ #define ETHTOOL_GFLAGS 0x00000025 /* Get flags bitmap(ethtool_value) */ #define ETHTOOL_SFLAGS 0x00000026 /* Set flags bitmap(ethtool_value) */ -#define ETHTOOL_GPFLAGS 0x00000027 /* Get driver-private flags bitmap */ -#define ETHTOOL_SPFLAGS 0x00000028 /* Set driver-private flags bitmap */ +#define ETHTOOL_GPFLAGS 0x00000027 /* Get driver-private flags bitmap */ +#define ETHTOOL_SPFLAGS 0x00000028 /* Set driver-private flags bitmap */ -#define ETHTOOL_GRXFH 0x00000029 /* Get RX flow hash configuration */ -#define ETHTOOL_SRXFH 0x0000002a /* Set RX flow hash configuration */ +#define ETHTOOL_GRXFH 0x00000029 /* Get RX flow hash configuration */ +#define ETHTOOL_SRXFH 0x0000002a /* Set RX flow hash configuration */ #define ETHTOOL_GGRO 0x0000002b /* Get GRO enable (ethtool_value) */ #define ETHTOOL_SGRO 0x0000002c /* Set GRO enable (ethtool_value) */ -#define ETHTOOL_GRXRINGS 0x0000002d /* Get RX rings available for LB */ -#define ETHTOOL_GRXCLSRLCNT 0x0000002e /* Get RX class rule count */ -#define ETHTOOL_GRXCLSRULE 0x0000002f /* Get RX classification rule */ -#define ETHTOOL_GRXCLSRLALL 0x00000030 /* Get all RX classification rule */ -#define ETHTOOL_SRXCLSRLDEL 0x00000031 /* Delete RX classification rule */ -#define ETHTOOL_SRXCLSRLINS 0x00000032 /* Insert RX classification rule */ -#define ETHTOOL_FLASHDEV 0x00000033 /* Flash firmware to device */ -#define ETHTOOL_RESET 0x00000034 /* Reset hardware */ -#define ETHTOOL_SRXNTUPLE 0x00000035 /* Add an n-tuple filter to device */ -#define ETHTOOL_GRXNTUPLE 0x00000036 /* Get n-tuple filters from device */ -#define ETHTOOL_GSSET_INFO 0x00000037 /* Get string set info */ +#define ETHTOOL_GRXRINGS 0x0000002d /* Get RX rings available for LB */ +#define ETHTOOL_GRXCLSRLCNT 0x0000002e /* Get RX class rule count */ +#define ETHTOOL_GRXCLSRULE 0x0000002f /* Get RX classification rule */ +#define ETHTOOL_GRXCLSRLALL 0x00000030 /* Get all RX classification rule */ +#define ETHTOOL_SRXCLSRLDEL 0x00000031 /* Delete RX classification rule */ +#define ETHTOOL_SRXCLSRLINS 0x00000032 /* Insert RX classification rule */ +#define ETHTOOL_FLASHDEV 0x00000033 /* Flash firmware to device */ +#define ETHTOOL_RESET 0x00000034 /* Reset hardware */ +#define ETHTOOL_SRXNTUPLE 0x00000035 /* Add an n-tuple filter to device */ +#define ETHTOOL_GRXNTUPLE 0x00000036 /* Get n-tuple filters from device */ +#define ETHTOOL_GSSET_INFO 0x00000037 /* Get string set info */ /* compatibility with older code */ #define SPARC_ETH_GSET ETHTOOL_GSET @@ -636,18 +645,18 @@ struct ethtool_ops { /* Indicates what features are supported by the interface. */ #define SUPPORTED_10baseT_Half (1 << 0) #define SUPPORTED_10baseT_Full (1 << 1) -#define SUPPORTED_100baseT_Half (1 << 2) -#define SUPPORTED_100baseT_Full (1 << 3) +#define SUPPORTED_100baseT_Half (1 << 2) +#define SUPPORTED_100baseT_Full (1 << 3) #define SUPPORTED_1000baseT_Half (1 << 4) #define SUPPORTED_1000baseT_Full (1 << 5) #define SUPPORTED_Autoneg (1 << 6) #define SUPPORTED_TP (1 << 7) #define SUPPORTED_AUI (1 << 8) #define SUPPORTED_MII (1 << 9) -#define SUPPORTED_FIBRE (1 << 10) +#define SUPPORTED_FIBRE (1 << 10) #define SUPPORTED_BNC (1 << 11) #define SUPPORTED_10000baseT_Full (1 << 12) -#define SUPPORTED_Pause (1 << 13) +#define SUPPORTED_Pause (1 << 13) #define SUPPORTED_Asym_Pause (1 << 14) #define SUPPORTED_2500baseX_Full (1 << 15) #define SUPPORTED_Backplane (1 << 16) @@ -657,8 +666,8 @@ struct ethtool_ops { #define SUPPORTED_10000baseR_FEC (1 << 20) /* Indicates what features are advertised by the interface. */ -#define ADVERTISED_10baseT_Half (1 << 0) -#define ADVERTISED_10baseT_Full (1 << 1) +#define ADVERTISED_10baseT_Half (1 << 0) +#define ADVERTISED_10baseT_Full (1 << 1) #define ADVERTISED_100baseT_Half (1 << 2) #define ADVERTISED_100baseT_Full (1 << 3) #define ADVERTISED_1000baseT_Half (1 << 4) @@ -697,12 +706,12 @@ struct ethtool_ops { #define DUPLEX_FULL 0x01 /* Which connector port. */ -#define PORT_TP 0x00 +#define PORT_TP 0x00 #define PORT_AUI 0x01 #define PORT_MII 0x02 #define PORT_FIBRE 0x03 #define PORT_BNC 0x04 -#define PORT_DA 0x05 +#define PORT_DA 0x05 #define PORT_NONE 0xef #define PORT_OTHER 0xff @@ -716,7 +725,7 @@ struct ethtool_ops { /* Enable or disable autonegotiation. If this is set to enable, * the forced link modes above are completely ignored. */ -#define AUTONEG_DISABLE 0x00 +#define AUTONEG_DISABLE 0x00 #define AUTONEG_ENABLE 0x01 /* Mode MDI or MDI-X */ @@ -747,8 +756,8 @@ struct ethtool_ops { #define AH_V6_FLOW 0x0b #define ESP_V6_FLOW 0x0c #define IP_USER_FLOW 0x0d -#define IPV4_FLOW 0x10 -#define IPV6_FLOW 0x11 +#define IPV4_FLOW 0x10 +#define IPV6_FLOW 0x11 /* L3-L4 network traffic flow hash options */ #define RXH_L2DA (1 << 1) diff --git a/net/core/ethtool.c b/net/core/ethtool.c index 73c81edde8d..99e9f850ea0 100644 --- a/net/core/ethtool.c +++ b/net/core/ethtool.c @@ -18,7 +18,7 @@ #include #include #include -#include +#include /* * Some useful ethtool_ops methods that're device independent. @@ -30,6 +30,7 @@ u32 ethtool_op_get_link(struct net_device *dev) { return netif_carrier_ok(dev) ? 1 : 0; } +EXPORT_SYMBOL(ethtool_op_get_link); u32 ethtool_op_get_rx_csum(struct net_device *dev) { @@ -62,6 +63,7 @@ int ethtool_op_set_tx_hw_csum(struct net_device *dev, u32 data) return 0; } +EXPORT_SYMBOL(ethtool_op_set_tx_hw_csum); int ethtool_op_set_tx_ipv6_csum(struct net_device *dev, u32 data) { @@ -72,11 +74,13 @@ int ethtool_op_set_tx_ipv6_csum(struct net_device *dev, u32 data) return 0; } +EXPORT_SYMBOL(ethtool_op_set_tx_ipv6_csum); u32 ethtool_op_get_sg(struct net_device *dev) { return (dev->features & NETIF_F_SG) != 0; } +EXPORT_SYMBOL(ethtool_op_get_sg); int ethtool_op_set_sg(struct net_device *dev, u32 data) { @@ -87,11 +91,13 @@ int ethtool_op_set_sg(struct net_device *dev, u32 data) return 0; } +EXPORT_SYMBOL(ethtool_op_set_sg); u32 ethtool_op_get_tso(struct net_device *dev) { return (dev->features & NETIF_F_TSO) != 0; } +EXPORT_SYMBOL(ethtool_op_get_tso); int ethtool_op_set_tso(struct net_device *dev, u32 data) { @@ -102,11 +108,13 @@ int ethtool_op_set_tso(struct net_device *dev, u32 data) return 0; } +EXPORT_SYMBOL(ethtool_op_set_tso); u32 ethtool_op_get_ufo(struct net_device *dev) { return (dev->features & NETIF_F_UFO) != 0; } +EXPORT_SYMBOL(ethtool_op_get_ufo); int ethtool_op_set_ufo(struct net_device *dev, u32 data) { @@ -116,6 +124,7 @@ int ethtool_op_set_ufo(struct net_device *dev, u32 data) dev->features &= ~NETIF_F_UFO; return 0; } +EXPORT_SYMBOL(ethtool_op_set_ufo); /* the following list of flags are the same as their associated * NETIF_F_xxx values in include/linux/netdevice.h @@ -132,6 +141,7 @@ u32 ethtool_op_get_flags(struct net_device *dev) return dev->features & flags_dup_features; } +EXPORT_SYMBOL(ethtool_op_get_flags); int ethtool_op_set_flags(struct net_device *dev, u32 data) { @@ -160,6 +170,7 @@ int ethtool_op_set_flags(struct net_device *dev, u32 data) dev->features = features; return 0; } +EXPORT_SYMBOL(ethtool_op_set_flags); void ethtool_ntuple_flush(struct net_device *dev) { @@ -205,7 +216,8 @@ static int ethtool_set_settings(struct net_device *dev, void __user *useraddr) return dev->ethtool_ops->set_settings(dev, &cmd); } -static noinline_for_stack int ethtool_get_drvinfo(struct net_device *dev, void __user *useraddr) +static noinline_for_stack int ethtool_get_drvinfo(struct net_device *dev, + void __user *useraddr) { struct ethtool_drvinfo info; const struct ethtool_ops *ops = dev->ethtool_ops; @@ -245,7 +257,7 @@ static noinline_for_stack int ethtool_get_drvinfo(struct net_device *dev, void _ } static noinline_for_stack int ethtool_get_sset_info(struct net_device *dev, - void __user *useraddr) + void __user *useraddr) { struct ethtool_sset_info info; const struct ethtool_ops *ops = dev->ethtool_ops; @@ -304,7 +316,8 @@ out: return ret; } -static noinline_for_stack int ethtool_set_rxnfc(struct net_device *dev, void __user *useraddr) +static noinline_for_stack int ethtool_set_rxnfc(struct net_device *dev, + void __user *useraddr) { struct ethtool_rxnfc cmd; @@ -317,7 +330,8 @@ static noinline_for_stack int ethtool_set_rxnfc(struct net_device *dev, void __u return dev->ethtool_ops->set_rxnfc(dev, &cmd); } -static noinline_for_stack int ethtool_get_rxnfc(struct net_device *dev, void __user *useraddr) +static noinline_for_stack int ethtool_get_rxnfc(struct net_device *dev, + void __user *useraddr) { struct ethtool_rxnfc info; const struct ethtool_ops *ops = dev->ethtool_ops; @@ -362,8 +376,8 @@ err_out: } static void __rx_ntuple_filter_add(struct ethtool_rx_ntuple_list *list, - struct ethtool_rx_ntuple_flow_spec *spec, - struct ethtool_rx_ntuple_flow_spec_container *fsc) + struct ethtool_rx_ntuple_flow_spec *spec, + struct ethtool_rx_ntuple_flow_spec_container *fsc) { /* don't add filters forever */ @@ -389,7 +403,8 @@ static void __rx_ntuple_filter_add(struct ethtool_rx_ntuple_list *list, list->count++; } -static noinline_for_stack int ethtool_set_rx_ntuple(struct net_device *dev, void __user *useraddr) +static noinline_for_stack int ethtool_set_rx_ntuple(struct net_device *dev, + void __user *useraddr) { struct ethtool_rx_ntuple cmd; const struct ethtool_ops *ops = dev->ethtool_ops; @@ -514,125 +529,125 @@ static int ethtool_get_rx_ntuple(struct net_device *dev, void __user *useraddr) case UDP_V4_FLOW: case SCTP_V4_FLOW: sprintf(p, "\tSrc IP addr: 0x%x\n", - fsc->fs.h_u.tcp_ip4_spec.ip4src); + fsc->fs.h_u.tcp_ip4_spec.ip4src); p += ETH_GSTRING_LEN; num_strings++; sprintf(p, "\tSrc IP mask: 0x%x\n", - fsc->fs.m_u.tcp_ip4_spec.ip4src); + fsc->fs.m_u.tcp_ip4_spec.ip4src); p += ETH_GSTRING_LEN; num_strings++; sprintf(p, "\tDest IP addr: 0x%x\n", - fsc->fs.h_u.tcp_ip4_spec.ip4dst); + fsc->fs.h_u.tcp_ip4_spec.ip4dst); p += ETH_GSTRING_LEN; num_strings++; sprintf(p, "\tDest IP mask: 0x%x\n", - fsc->fs.m_u.tcp_ip4_spec.ip4dst); + fsc->fs.m_u.tcp_ip4_spec.ip4dst); p += ETH_GSTRING_LEN; num_strings++; sprintf(p, "\tSrc Port: %d, mask: 0x%x\n", - fsc->fs.h_u.tcp_ip4_spec.psrc, - fsc->fs.m_u.tcp_ip4_spec.psrc); + fsc->fs.h_u.tcp_ip4_spec.psrc, + fsc->fs.m_u.tcp_ip4_spec.psrc); p += ETH_GSTRING_LEN; num_strings++; sprintf(p, "\tDest Port: %d, mask: 0x%x\n", - fsc->fs.h_u.tcp_ip4_spec.pdst, - fsc->fs.m_u.tcp_ip4_spec.pdst); + fsc->fs.h_u.tcp_ip4_spec.pdst, + fsc->fs.m_u.tcp_ip4_spec.pdst); p += ETH_GSTRING_LEN; num_strings++; sprintf(p, "\tTOS: %d, mask: 0x%x\n", - fsc->fs.h_u.tcp_ip4_spec.tos, - fsc->fs.m_u.tcp_ip4_spec.tos); + fsc->fs.h_u.tcp_ip4_spec.tos, + fsc->fs.m_u.tcp_ip4_spec.tos); p += ETH_GSTRING_LEN; num_strings++; break; case AH_ESP_V4_FLOW: case ESP_V4_FLOW: sprintf(p, "\tSrc IP addr: 0x%x\n", - fsc->fs.h_u.ah_ip4_spec.ip4src); + fsc->fs.h_u.ah_ip4_spec.ip4src); p += ETH_GSTRING_LEN; num_strings++; sprintf(p, "\tSrc IP mask: 0x%x\n", - fsc->fs.m_u.ah_ip4_spec.ip4src); + fsc->fs.m_u.ah_ip4_spec.ip4src); p += ETH_GSTRING_LEN; num_strings++; sprintf(p, "\tDest IP addr: 0x%x\n", - fsc->fs.h_u.ah_ip4_spec.ip4dst); + fsc->fs.h_u.ah_ip4_spec.ip4dst); p += ETH_GSTRING_LEN; num_strings++; sprintf(p, "\tDest IP mask: 0x%x\n", - fsc->fs.m_u.ah_ip4_spec.ip4dst); + fsc->fs.m_u.ah_ip4_spec.ip4dst); p += ETH_GSTRING_LEN; num_strings++; sprintf(p, "\tSPI: %d, mask: 0x%x\n", - fsc->fs.h_u.ah_ip4_spec.spi, - fsc->fs.m_u.ah_ip4_spec.spi); + fsc->fs.h_u.ah_ip4_spec.spi, + fsc->fs.m_u.ah_ip4_spec.spi); p += ETH_GSTRING_LEN; num_strings++; sprintf(p, "\tTOS: %d, mask: 0x%x\n", - fsc->fs.h_u.ah_ip4_spec.tos, - fsc->fs.m_u.ah_ip4_spec.tos); + fsc->fs.h_u.ah_ip4_spec.tos, + fsc->fs.m_u.ah_ip4_spec.tos); p += ETH_GSTRING_LEN; num_strings++; break; case IP_USER_FLOW: sprintf(p, "\tSrc IP addr: 0x%x\n", - fsc->fs.h_u.raw_ip4_spec.ip4src); + fsc->fs.h_u.raw_ip4_spec.ip4src); p += ETH_GSTRING_LEN; num_strings++; sprintf(p, "\tSrc IP mask: 0x%x\n", - fsc->fs.m_u.raw_ip4_spec.ip4src); + fsc->fs.m_u.raw_ip4_spec.ip4src); p += ETH_GSTRING_LEN; num_strings++; sprintf(p, "\tDest IP addr: 0x%x\n", - fsc->fs.h_u.raw_ip4_spec.ip4dst); + fsc->fs.h_u.raw_ip4_spec.ip4dst); p += ETH_GSTRING_LEN; num_strings++; sprintf(p, "\tDest IP mask: 0x%x\n", - fsc->fs.m_u.raw_ip4_spec.ip4dst); + fsc->fs.m_u.raw_ip4_spec.ip4dst); p += ETH_GSTRING_LEN; num_strings++; break; case IPV4_FLOW: sprintf(p, "\tSrc IP addr: 0x%x\n", - fsc->fs.h_u.usr_ip4_spec.ip4src); + fsc->fs.h_u.usr_ip4_spec.ip4src); p += ETH_GSTRING_LEN; num_strings++; sprintf(p, "\tSrc IP mask: 0x%x\n", - fsc->fs.m_u.usr_ip4_spec.ip4src); + fsc->fs.m_u.usr_ip4_spec.ip4src); p += ETH_GSTRING_LEN; num_strings++; sprintf(p, "\tDest IP addr: 0x%x\n", - fsc->fs.h_u.usr_ip4_spec.ip4dst); + fsc->fs.h_u.usr_ip4_spec.ip4dst); p += ETH_GSTRING_LEN; num_strings++; sprintf(p, "\tDest IP mask: 0x%x\n", - fsc->fs.m_u.usr_ip4_spec.ip4dst); + fsc->fs.m_u.usr_ip4_spec.ip4dst); p += ETH_GSTRING_LEN; num_strings++; sprintf(p, "\tL4 bytes: 0x%x, mask: 0x%x\n", - fsc->fs.h_u.usr_ip4_spec.l4_4_bytes, - fsc->fs.m_u.usr_ip4_spec.l4_4_bytes); + fsc->fs.h_u.usr_ip4_spec.l4_4_bytes, + fsc->fs.m_u.usr_ip4_spec.l4_4_bytes); p += ETH_GSTRING_LEN; num_strings++; sprintf(p, "\tTOS: %d, mask: 0x%x\n", - fsc->fs.h_u.usr_ip4_spec.tos, - fsc->fs.m_u.usr_ip4_spec.tos); + fsc->fs.h_u.usr_ip4_spec.tos, + fsc->fs.m_u.usr_ip4_spec.tos); p += ETH_GSTRING_LEN; num_strings++; sprintf(p, "\tIP Version: %d, mask: 0x%x\n", - fsc->fs.h_u.usr_ip4_spec.ip_ver, - fsc->fs.m_u.usr_ip4_spec.ip_ver); + fsc->fs.h_u.usr_ip4_spec.ip_ver, + fsc->fs.m_u.usr_ip4_spec.ip_ver); p += ETH_GSTRING_LEN; num_strings++; sprintf(p, "\tProtocol: %d, mask: 0x%x\n", - fsc->fs.h_u.usr_ip4_spec.proto, - fsc->fs.m_u.usr_ip4_spec.proto); + fsc->fs.h_u.usr_ip4_spec.proto, + fsc->fs.m_u.usr_ip4_spec.proto); p += ETH_GSTRING_LEN; num_strings++; break; }; sprintf(p, "\tVLAN: %d, mask: 0x%x\n", - fsc->fs.vlan_tag, fsc->fs.vlan_tag_mask); + fsc->fs.vlan_tag, fsc->fs.vlan_tag_mask); p += ETH_GSTRING_LEN; num_strings++; sprintf(p, "\tUser-defined: 0x%Lx\n", fsc->fs.data); @@ -645,7 +660,7 @@ static int ethtool_get_rx_ntuple(struct net_device *dev, void __user *useraddr) sprintf(p, "\tAction: Drop\n"); else sprintf(p, "\tAction: Direct to queue %d\n", - fsc->fs.action); + fsc->fs.action); p += ETH_GSTRING_LEN; num_strings++; unknown_filter: @@ -857,7 +872,8 @@ static int ethtool_set_eeprom(struct net_device *dev, void __user *useraddr) return ret; } -static noinline_for_stack int ethtool_get_coalesce(struct net_device *dev, void __user *useraddr) +static noinline_for_stack int ethtool_get_coalesce(struct net_device *dev, + void __user *useraddr) { struct ethtool_coalesce coalesce = { .cmd = ETHTOOL_GCOALESCE }; @@ -871,7 +887,8 @@ static noinline_for_stack int ethtool_get_coalesce(struct net_device *dev, void return 0; } -static noinline_for_stack int ethtool_set_coalesce(struct net_device *dev, void __user *useraddr) +static noinline_for_stack int ethtool_set_coalesce(struct net_device *dev, + void __user *useraddr) { struct ethtool_coalesce coalesce; @@ -975,6 +992,7 @@ static int ethtool_set_tx_csum(struct net_device *dev, char __user *useraddr) return dev->ethtool_ops->set_tx_csum(dev, edata.data); } +EXPORT_SYMBOL(ethtool_op_set_tx_csum); static int ethtool_set_rx_csum(struct net_device *dev, char __user *useraddr) { @@ -1046,7 +1064,7 @@ static int ethtool_get_gso(struct net_device *dev, char __user *useraddr) edata.data = dev->features & NETIF_F_GSO; if (copy_to_user(useraddr, &edata, sizeof(edata))) - return -EFAULT; + return -EFAULT; return 0; } @@ -1069,7 +1087,7 @@ static int ethtool_get_gro(struct net_device *dev, char __user *useraddr) edata.data = dev->features & NETIF_F_GRO; if (copy_to_user(useraddr, &edata, sizeof(edata))) - return -EFAULT; + return -EFAULT; return 0; } @@ -1281,7 +1299,8 @@ static int ethtool_set_value(struct net_device *dev, char __user *useraddr, return actor(dev, edata.data); } -static noinline_for_stack int ethtool_flash_device(struct net_device *dev, char __user *useraddr) +static noinline_for_stack int ethtool_flash_device(struct net_device *dev, + char __user *useraddr) { struct ethtool_flash efl; @@ -1310,11 +1329,11 @@ int dev_ethtool(struct net *net, struct ifreq *ifr) if (!dev->ethtool_ops) return -EOPNOTSUPP; - if (copy_from_user(ðcmd, useraddr, sizeof (ethcmd))) + if (copy_from_user(ðcmd, useraddr, sizeof(ethcmd))) return -EFAULT; /* Allow some commands to be done by anyone */ - switch(ethcmd) { + switch (ethcmd) { case ETHTOOL_GDRVINFO: case ETHTOOL_GMSGLVL: case ETHTOOL_GCOALESCE: @@ -1342,10 +1361,11 @@ int dev_ethtool(struct net *net, struct ifreq *ifr) return -EPERM; } - if (dev->ethtool_ops->begin) - if ((rc = dev->ethtool_ops->begin(dev)) < 0) + if (dev->ethtool_ops->begin) { + rc = dev->ethtool_ops->begin(dev); + if (rc < 0) return rc; - + } old_features = dev->features; switch (ethcmd) { @@ -1535,16 +1555,3 @@ int dev_ethtool(struct net *net, struct ifreq *ifr) return rc; } - -EXPORT_SYMBOL(ethtool_op_get_link); -EXPORT_SYMBOL(ethtool_op_get_sg); -EXPORT_SYMBOL(ethtool_op_get_tso); -EXPORT_SYMBOL(ethtool_op_set_sg); -EXPORT_SYMBOL(ethtool_op_set_tso); -EXPORT_SYMBOL(ethtool_op_set_tx_csum); -EXPORT_SYMBOL(ethtool_op_set_tx_hw_csum); -EXPORT_SYMBOL(ethtool_op_set_tx_ipv6_csum); -EXPORT_SYMBOL(ethtool_op_set_ufo); -EXPORT_SYMBOL(ethtool_op_get_ufo); -EXPORT_SYMBOL(ethtool_op_set_flags); -EXPORT_SYMBOL(ethtool_op_get_flags); -- cgit v1.2.3 From 97ad9139fd68b5c71f44d28d3f9788d89cfd4916 Mon Sep 17 00:00:00 2001 From: Javier Cardona Date: Mon, 29 Mar 2010 11:00:21 -0700 Subject: mac80211: Moved mesh action codes to a more visible location Grouped mesh action codes together with the other action codes in ieee80211.h. Signed-off-by: Javier Cardona Reviewed-by: Johannes Berg Signed-off-by: John W. Linville --- include/linux/ieee80211.h | 2 ++ net/mac80211/mesh.c | 4 ++-- net/mac80211/mesh.h | 2 -- net/mac80211/mesh_hwmp.c | 4 ++-- net/mac80211/mesh_plink.c | 2 +- net/mac80211/rx.c | 6 +++--- 6 files changed, 10 insertions(+), 10 deletions(-) (limited to 'include/linux') diff --git a/include/linux/ieee80211.h b/include/linux/ieee80211.h index e9e03b02cb0..1252ba1fbff 100644 --- a/include/linux/ieee80211.h +++ b/include/linux/ieee80211.h @@ -1211,6 +1211,8 @@ enum ieee80211_category { WLAN_CATEGORY_SA_QUERY = 8, WLAN_CATEGORY_PROTECTED_DUAL_OF_ACTION = 9, WLAN_CATEGORY_WMM = 17, + WLAN_CATEGORY_MESH_PLINK = 30, /* Pending ANA approval */ + WLAN_CATEGORY_MESH_PATH_SEL = 32, /* Pending ANA approval */ WLAN_CATEGORY_VENDOR_SPECIFIC_PROTECTED = 126, WLAN_CATEGORY_VENDOR_SPECIFIC = 127, }; diff --git a/net/mac80211/mesh.c b/net/mac80211/mesh.c index 7a6bebce7f2..2669fbf8c81 100644 --- a/net/mac80211/mesh.c +++ b/net/mac80211/mesh.c @@ -600,10 +600,10 @@ static void ieee80211_mesh_rx_mgmt_action(struct ieee80211_sub_if_data *sdata, struct ieee80211_rx_status *rx_status) { switch (mgmt->u.action.category) { - case MESH_PLINK_CATEGORY: + case WLAN_CATEGORY_MESH_PLINK: mesh_rx_plink_frame(sdata, mgmt, len, rx_status); break; - case MESH_PATH_SEL_CATEGORY: + case WLAN_CATEGORY_MESH_PATH_SEL: mesh_rx_path_sel_frame(sdata, mgmt, len); break; } diff --git a/net/mac80211/mesh.h b/net/mac80211/mesh.h index 85562c59d7d..c88087f1cd0 100644 --- a/net/mac80211/mesh.h +++ b/net/mac80211/mesh.h @@ -209,8 +209,6 @@ struct mesh_rmc { #define MESH_MAX_MPATHS 1024 /* Pending ANA approval */ -#define MESH_PLINK_CATEGORY 30 -#define MESH_PATH_SEL_CATEGORY 32 #define MESH_PATH_SEL_ACTION 0 /* PERR reason codes */ diff --git a/net/mac80211/mesh_hwmp.c b/net/mac80211/mesh_hwmp.c index ccff6133e19..36141d6e701 100644 --- a/net/mac80211/mesh_hwmp.c +++ b/net/mac80211/mesh_hwmp.c @@ -131,7 +131,7 @@ static int mesh_path_sel_frame_tx(enum mpath_frame_type action, u8 flags, memcpy(mgmt->sa, sdata->vif.addr, ETH_ALEN); /* BSSID == SA */ memcpy(mgmt->bssid, sdata->vif.addr, ETH_ALEN); - mgmt->u.action.category = MESH_PATH_SEL_CATEGORY; + mgmt->u.action.category = WLAN_CATEGORY_MESH_PATH_SEL; mgmt->u.action.u.mesh_action.action_code = MESH_PATH_SEL_ACTION; switch (action) { @@ -224,7 +224,7 @@ int mesh_path_error_tx(u8 ttl, u8 *target, __le32 target_sn, memcpy(mgmt->da, ra, ETH_ALEN); memcpy(mgmt->sa, sdata->vif.addr, ETH_ALEN); /* BSSID is left zeroed, wildcard value */ - mgmt->u.action.category = MESH_PATH_SEL_CATEGORY; + mgmt->u.action.category = WLAN_CATEGORY_MESH_PATH_SEL; mgmt->u.action.u.mesh_action.action_code = MESH_PATH_SEL_ACTION; ie_len = 15; pos = skb_put(skb, 2 + ie_len); diff --git a/net/mac80211/mesh_plink.c b/net/mac80211/mesh_plink.c index bc4e20e57ff..c384154ac89 100644 --- a/net/mac80211/mesh_plink.c +++ b/net/mac80211/mesh_plink.c @@ -171,7 +171,7 @@ static int mesh_plink_frame_tx(struct ieee80211_sub_if_data *sdata, memcpy(mgmt->da, da, ETH_ALEN); memcpy(mgmt->sa, sdata->vif.addr, ETH_ALEN); /* BSSID is left zeroed, wildcard value */ - mgmt->u.action.category = MESH_PLINK_CATEGORY; + mgmt->u.action.category = WLAN_CATEGORY_MESH_PLINK; mgmt->u.action.u.plink_action.action_code = action; if (action == PLINK_CLOSE) diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c index c0ad7e879a6..d08ede44ac7 100644 --- a/net/mac80211/rx.c +++ b/net/mac80211/rx.c @@ -490,7 +490,7 @@ ieee80211_rx_mesh_check(struct ieee80211_rx_data *rx) if (ieee80211_is_action(hdr->frame_control)) { mgmt = (struct ieee80211_mgmt *)hdr; - if (mgmt->u.action.category != MESH_PLINK_CATEGORY) + if (mgmt->u.action.category != WLAN_CATEGORY_MESH_PLINK) return RX_DROP_MONITOR; return RX_CONTINUE; } @@ -1994,8 +1994,8 @@ ieee80211_rx_h_action(struct ieee80211_rx_data *rx) goto handled; } break; - case MESH_PLINK_CATEGORY: - case MESH_PATH_SEL_CATEGORY: + case WLAN_CATEGORY_MESH_PLINK: + case WLAN_CATEGORY_MESH_PATH_SEL: if (ieee80211_vif_is_mesh(&sdata->vif)) return ieee80211_mesh_rx_mgmt(sdata, rx->skb); break; -- cgit v1.2.3 From ed85b565b825566da34e55eee9ad150ed93fdda0 Mon Sep 17 00:00:00 2001 From: Richard Cochran Date: Wed, 7 Apr 2010 22:41:28 +0000 Subject: packet: support for TX time stamps on RAW sockets Enable the SO_TIMESTAMPING socket infrastructure for raw packet sockets. We introduce PACKET_TX_TIMESTAMP for the control message cmsg_type. Similar support for UDP and CAN sockets was added in commit 51f31cabe3ce5345b51e4a4f82138b38c4d5dc91 Signed-off-by: Richard Cochran Signed-off-by: David S. Miller --- include/linux/if_packet.h | 1 + net/packet/af_packet.c | 61 ++++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 61 insertions(+), 1 deletion(-) (limited to 'include/linux') diff --git a/include/linux/if_packet.h b/include/linux/if_packet.h index aa57a5f993f..6ac23ef1801 100644 --- a/include/linux/if_packet.h +++ b/include/linux/if_packet.h @@ -47,6 +47,7 @@ struct sockaddr_ll { #define PACKET_TX_RING 13 #define PACKET_LOSS 14 #define PACKET_VNET_HDR 15 +#define PACKET_TX_TIMESTAMP 16 struct tpacket_stats { unsigned int tp_packets; diff --git a/net/packet/af_packet.c b/net/packet/af_packet.c index d7d0310dca9..f162d59d816 100644 --- a/net/packet/af_packet.c +++ b/net/packet/af_packet.c @@ -82,6 +82,7 @@ #include #include #include +#include #ifdef CONFIG_INET #include @@ -315,6 +316,8 @@ static inline struct packet_sock *pkt_sk(struct sock *sk) static void packet_sock_destruct(struct sock *sk) { + skb_queue_purge(&sk->sk_error_queue); + WARN_ON(atomic_read(&sk->sk_rmem_alloc)); WARN_ON(atomic_read(&sk->sk_wmem_alloc)); @@ -483,6 +486,9 @@ retry: skb->dev = dev; skb->priority = sk->sk_priority; skb->mark = sk->sk_mark; + err = sock_tx_timestamp(msg, sk, skb_tx(skb)); + if (err < 0) + goto out_unlock; dev_queue_xmit(skb); rcu_read_unlock(); @@ -1188,6 +1194,9 @@ static int packet_snd(struct socket *sock, err = skb_copy_datagram_from_iovec(skb, offset, msg->msg_iov, 0, len); if (err) goto out_free; + err = sock_tx_timestamp(msg, sk, skb_tx(skb)); + if (err < 0) + goto out_free; skb->protocol = proto; skb->dev = dev; @@ -1487,6 +1496,51 @@ out: return err; } +static int packet_recv_error(struct sock *sk, struct msghdr *msg, int len) +{ + struct sock_exterr_skb *serr; + struct sk_buff *skb, *skb2; + int copied, err; + + err = -EAGAIN; + skb = skb_dequeue(&sk->sk_error_queue); + if (skb == NULL) + goto out; + + copied = skb->len; + if (copied > len) { + msg->msg_flags |= MSG_TRUNC; + copied = len; + } + err = skb_copy_datagram_iovec(skb, 0, msg->msg_iov, copied); + if (err) + goto out_free_skb; + + sock_recv_timestamp(msg, sk, skb); + + serr = SKB_EXT_ERR(skb); + put_cmsg(msg, SOL_PACKET, PACKET_TX_TIMESTAMP, + sizeof(serr->ee), &serr->ee); + + msg->msg_flags |= MSG_ERRQUEUE; + err = copied; + + /* Reset and regenerate socket error */ + spin_lock_bh(&sk->sk_error_queue.lock); + sk->sk_err = 0; + if ((skb2 = skb_peek(&sk->sk_error_queue)) != NULL) { + sk->sk_err = SKB_EXT_ERR(skb2)->ee.ee_errno; + spin_unlock_bh(&sk->sk_error_queue.lock); + sk->sk_error_report(sk); + } else + spin_unlock_bh(&sk->sk_error_queue.lock); + +out_free_skb: + kfree_skb(skb); +out: + return err; +} + /* * Pull a packet from our receive queue and hand it to the user. * If necessary we block. @@ -1502,7 +1556,7 @@ static int packet_recvmsg(struct kiocb *iocb, struct socket *sock, int vnet_hdr_len = 0; err = -EINVAL; - if (flags & ~(MSG_PEEK|MSG_DONTWAIT|MSG_TRUNC|MSG_CMSG_COMPAT)) + if (flags & ~(MSG_PEEK|MSG_DONTWAIT|MSG_TRUNC|MSG_CMSG_COMPAT|MSG_ERRQUEUE)) goto out; #if 0 @@ -1511,6 +1565,11 @@ static int packet_recvmsg(struct kiocb *iocb, struct socket *sock, return -ENODEV; #endif + if (flags & MSG_ERRQUEUE) { + err = packet_recv_error(sk, msg, len); + goto out; + } + /* * Call the generic datagram receiver. This handles all sorts * of horrible races and re-entrancy so we can forget about it -- cgit v1.2.3 From a79ff731a1b277d0e92d9453bdf374e04cec717a Mon Sep 17 00:00:00 2001 From: Alexey Dobriyan Date: Tue, 13 Apr 2010 11:21:46 +0200 Subject: netfilter: xtables: make XT_ALIGN() usable in exported headers by exporting __ALIGN_KERNEL() XT_ALIGN() was rewritten through ALIGN() by commit 42107f5009da223daa800d6da6904d77297ae829 "netfilter: xtables: symmetric COMPAT_XT_ALIGN definition". ALIGN() is not exported in userspace headers, which created compile problem for tc(8) and will create problem for iptables(8). We can't export generic looking name ALIGN() but we can export less generic __ALIGN_KERNEL() (suggested by Ben Hutchings). Google knows nothing about __ALIGN_KERNEL(). COMPAT_XT_ALIGN() changed for symmetry. Reported-by: Andreas Henriksson Signed-off-by: Alexey Dobriyan Signed-off-by: Patrick McHardy --- include/linux/kernel.h | 5 +++-- include/linux/netfilter/x_tables.h | 6 +++--- 2 files changed, 6 insertions(+), 5 deletions(-) (limited to 'include/linux') diff --git a/include/linux/kernel.h b/include/linux/kernel.h index 7f070746336..284ea995646 100644 --- a/include/linux/kernel.h +++ b/include/linux/kernel.h @@ -4,6 +4,8 @@ /* * 'kernel.h' contains some often-used function prototypes etc */ +#define __ALIGN_KERNEL(x, a) __ALIGN_KERNEL_MASK(x, (typeof(x))(a) - 1) +#define __ALIGN_KERNEL_MASK(x, mask) (((x) + (mask)) & ~(mask)) #ifdef __KERNEL__ @@ -37,8 +39,7 @@ extern const char linux_proc_banner[]; #define STACK_MAGIC 0xdeadbeef -#define ALIGN(x,a) __ALIGN_MASK(x,(typeof(x))(a)-1) -#define __ALIGN_MASK(x,mask) (((x)+(mask))&~(mask)) +#define ALIGN(x, a) __ALIGN_KERNEL((x), (a)) #define PTR_ALIGN(p, a) ((typeof(p))ALIGN((unsigned long)(p), (a))) #define IS_ALIGNED(x, a) (((x) & ((typeof(x))(a) - 1)) == 0) diff --git a/include/linux/netfilter/x_tables.h b/include/linux/netfilter/x_tables.h index 1a65d45ee4f..26ced0c323a 100644 --- a/include/linux/netfilter/x_tables.h +++ b/include/linux/netfilter/x_tables.h @@ -1,6 +1,6 @@ #ifndef _X_TABLES_H #define _X_TABLES_H - +#include #include #define XT_FUNCTION_MAXNAMELEN 30 @@ -93,7 +93,7 @@ struct _xt_align { __u64 u64; }; -#define XT_ALIGN(s) ALIGN((s), __alignof__(struct _xt_align)) +#define XT_ALIGN(s) __ALIGN_KERNEL((s), __alignof__(struct _xt_align)) /* Standard return verdict, or do jump. */ #define XT_STANDARD_TARGET "" @@ -603,7 +603,7 @@ struct _compat_xt_align { compat_u64 u64; }; -#define COMPAT_XT_ALIGN(s) ALIGN((s), __alignof__(struct _compat_xt_align)) +#define COMPAT_XT_ALIGN(s) __ALIGN_KERNEL((s), __alignof__(struct _compat_xt_align)) extern void xt_compat_lock(u_int8_t af); extern void xt_compat_unlock(u_int8_t af); -- cgit v1.2.3 From cd58950a5345f006a318f178705b9250aa54425c Mon Sep 17 00:00:00 2001 From: Alexander Duyck Date: Fri, 9 Apr 2010 10:01:37 +0000 Subject: skbuff: remove unused dev_consume_skb macro definition dev_consume_skb and kfree_skb_clean have no users and in the case of kfree_skb_clean could cause potential build issues since I cannot find where it is defined. Based on the patch in which it was introduced it appears to have been a bit of leftover code from an earlier version of the patch in which kfree_skb_clean was dropped in favor of consume_skb. Signed-off-by: Alexander Duyck Signed-off-by: Jeff Kirsher Signed-off-by: David S. Miller --- include/linux/skbuff.h | 1 - 1 file changed, 1 deletion(-) (limited to 'include/linux') diff --git a/include/linux/skbuff.h b/include/linux/skbuff.h index cf42f194616..38501d20650 100644 --- a/include/linux/skbuff.h +++ b/include/linux/skbuff.h @@ -470,7 +470,6 @@ extern int skb_cow_data(struct sk_buff *skb, int tailbits, struct sk_buff **trailer); extern int skb_pad(struct sk_buff *skb, int pad); #define dev_kfree_skb(a) consume_skb(a) -#define dev_consume_skb(a) kfree_skb_clean(a) extern void skb_over_panic(struct sk_buff *skb, int len, void *here); extern void skb_under_panic(struct sk_buff *skb, int len, -- cgit v1.2.3 From 829e0015431537176e38812f88fffe1d3250083e Mon Sep 17 00:00:00 2001 From: "Hans J. Koch" Date: Tue, 13 Apr 2010 00:03:25 +0000 Subject: Fix some #includes in CAN drivers (rebased for net-next-2.6) In the current implementation, CAN drivers need to #include _before_ they #include , which is both ugly and unnecessary. Fix this by including in and remove the #include lines from drivers. Signed-off-by: Hans J. Koch Signed-off-by: David S. Miller --- drivers/net/can/at91_can.c | 1 - drivers/net/can/bfin_can.c | 1 - drivers/net/can/mcp251x.c | 1 - drivers/net/can/mscan/mpc5xxx_can.c | 1 - drivers/net/can/mscan/mscan.c | 1 - drivers/net/can/sja1000/ems_pci.c | 1 - drivers/net/can/sja1000/kvaser_pci.c | 1 - drivers/net/can/sja1000/plx_pci.c | 1 - drivers/net/can/sja1000/sja1000.c | 1 - drivers/net/can/sja1000/sja1000_isa.c | 1 - drivers/net/can/sja1000/sja1000_of_platform.c | 1 - drivers/net/can/sja1000/sja1000_platform.c | 1 - drivers/net/can/ti_hecc.c | 1 - include/linux/can/dev.h | 1 + 14 files changed, 1 insertion(+), 13 deletions(-) (limited to 'include/linux') diff --git a/drivers/net/can/at91_can.c b/drivers/net/can/at91_can.c index a2f29a38798..3f7ceaebd68 100644 --- a/drivers/net/can/at91_can.c +++ b/drivers/net/can/at91_can.c @@ -35,7 +35,6 @@ #include #include -#include #include #include diff --git a/drivers/net/can/bfin_can.c b/drivers/net/can/bfin_can.c index 03489864376..d77264ad326 100644 --- a/drivers/net/can/bfin_can.c +++ b/drivers/net/can/bfin_can.c @@ -18,7 +18,6 @@ #include #include -#include #include #include diff --git a/drivers/net/can/mcp251x.c b/drivers/net/can/mcp251x.c index eb898515352..8431eb08075 100644 --- a/drivers/net/can/mcp251x.c +++ b/drivers/net/can/mcp251x.c @@ -58,7 +58,6 @@ * */ -#include #include #include #include diff --git a/drivers/net/can/mscan/mpc5xxx_can.c b/drivers/net/can/mscan/mpc5xxx_can.c index 03e7c48465a..225fd147774 100644 --- a/drivers/net/can/mscan/mpc5xxx_can.c +++ b/drivers/net/can/mscan/mpc5xxx_can.c @@ -25,7 +25,6 @@ #include #include #include -#include #include #include #include diff --git a/drivers/net/can/mscan/mscan.c b/drivers/net/can/mscan/mscan.c index 6b7dd578d41..64c378cd0c3 100644 --- a/drivers/net/can/mscan/mscan.c +++ b/drivers/net/can/mscan/mscan.c @@ -28,7 +28,6 @@ #include #include #include -#include #include #include #include diff --git a/drivers/net/can/sja1000/ems_pci.c b/drivers/net/can/sja1000/ems_pci.c index 5f53da0bc40..36f4f9780c3 100644 --- a/drivers/net/can/sja1000/ems_pci.c +++ b/drivers/net/can/sja1000/ems_pci.c @@ -24,7 +24,6 @@ #include #include #include -#include #include #include diff --git a/drivers/net/can/sja1000/kvaser_pci.c b/drivers/net/can/sja1000/kvaser_pci.c index 441e776a7f5..ed004cebd31 100644 --- a/drivers/net/can/sja1000/kvaser_pci.c +++ b/drivers/net/can/sja1000/kvaser_pci.c @@ -36,7 +36,6 @@ #include #include #include -#include #include #include diff --git a/drivers/net/can/sja1000/plx_pci.c b/drivers/net/can/sja1000/plx_pci.c index d5efd68085f..437b5c716a2 100644 --- a/drivers/net/can/sja1000/plx_pci.c +++ b/drivers/net/can/sja1000/plx_pci.c @@ -27,7 +27,6 @@ #include #include #include -#include #include #include diff --git a/drivers/net/can/sja1000/sja1000.c b/drivers/net/can/sja1000/sja1000.c index 145b1a731a5..618c11222ab 100644 --- a/drivers/net/can/sja1000/sja1000.c +++ b/drivers/net/can/sja1000/sja1000.c @@ -60,7 +60,6 @@ #include #include -#include #include #include diff --git a/drivers/net/can/sja1000/sja1000_isa.c b/drivers/net/can/sja1000/sja1000_isa.c index a6a51f15596..496223e9e2f 100644 --- a/drivers/net/can/sja1000/sja1000_isa.c +++ b/drivers/net/can/sja1000/sja1000_isa.c @@ -23,7 +23,6 @@ #include #include #include -#include #include #include diff --git a/drivers/net/can/sja1000/sja1000_of_platform.c b/drivers/net/can/sja1000/sja1000_of_platform.c index 9dd076a626a..34e79efbd2f 100644 --- a/drivers/net/can/sja1000/sja1000_of_platform.c +++ b/drivers/net/can/sja1000/sja1000_of_platform.c @@ -38,7 +38,6 @@ #include #include #include -#include #include #include diff --git a/drivers/net/can/sja1000/sja1000_platform.c b/drivers/net/can/sja1000/sja1000_platform.c index 1083b42ab6c..b65cabb361a 100644 --- a/drivers/net/can/sja1000/sja1000_platform.c +++ b/drivers/net/can/sja1000/sja1000_platform.c @@ -24,7 +24,6 @@ #include #include #include -#include #include #include #include diff --git a/drivers/net/can/ti_hecc.c b/drivers/net/can/ti_hecc.c index 0c3d2ba0d17..4d07f1ee716 100644 --- a/drivers/net/can/ti_hecc.c +++ b/drivers/net/can/ti_hecc.c @@ -47,7 +47,6 @@ #include #include -#include #include #include #include diff --git a/include/linux/can/dev.h b/include/linux/can/dev.h index 6e5a7f00223..cc0bb496166 100644 --- a/include/linux/can/dev.h +++ b/include/linux/can/dev.h @@ -14,6 +14,7 @@ #ifndef CAN_DEV_H #define CAN_DEV_H +#include #include #include -- cgit v1.2.3 From acbbc07145b919248c410e1852b953d385be5c97 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Sun, 11 Apr 2010 06:56:11 +0000 Subject: net: uninline skb_bond_should_drop() skb_bond_should_drop() is too big to be inlined. This patch reduces kernel text size, and its compilation time as well (shrinking include/linux/netdevice.h) Signed-off-by: Eric Dumazet Signed-off-by: David S. Miller --- include/linux/netdevice.h | 48 ++++------------------------------------------ net/core/dev.c | 49 +++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 53 insertions(+), 44 deletions(-) (limited to 'include/linux') diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index d1a21b576a4..470f7c951af 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -2089,54 +2089,14 @@ static inline void netif_set_gso_max_size(struct net_device *dev, dev->gso_max_size = size; } -static inline void skb_bond_set_mac_by_master(struct sk_buff *skb, - struct net_device *master) -{ - if (skb->pkt_type == PACKET_HOST) { - u16 *dest = (u16 *) eth_hdr(skb)->h_dest; - - memcpy(dest, master->dev_addr, ETH_ALEN); - } -} +extern int __skb_bond_should_drop(struct sk_buff *skb, + struct net_device *master); -/* On bonding slaves other than the currently active slave, suppress - * duplicates except for 802.3ad ETH_P_SLOW, alb non-mcast/bcast, and - * ARP on active-backup slaves with arp_validate enabled. - */ static inline int skb_bond_should_drop(struct sk_buff *skb, struct net_device *master) { - if (master) { - struct net_device *dev = skb->dev; - - if (master->priv_flags & IFF_MASTER_ARPMON) - dev->last_rx = jiffies; - - if ((master->priv_flags & IFF_MASTER_ALB) && master->br_port) { - /* Do address unmangle. The local destination address - * will be always the one master has. Provides the right - * functionality in a bridge. - */ - skb_bond_set_mac_by_master(skb, master); - } - - if (dev->priv_flags & IFF_SLAVE_INACTIVE) { - if ((dev->priv_flags & IFF_SLAVE_NEEDARP) && - skb->protocol == __cpu_to_be16(ETH_P_ARP)) - return 0; - - if (master->priv_flags & IFF_MASTER_ALB) { - if (skb->pkt_type != PACKET_BROADCAST && - skb->pkt_type != PACKET_MULTICAST) - return 0; - } - if (master->priv_flags & IFF_MASTER_8023AD && - skb->protocol == __cpu_to_be16(ETH_P_SLOW)) - return 0; - - return 1; - } - } + if (master) + return __skb_bond_should_drop(skb, master); return 0; } diff --git a/net/core/dev.c b/net/core/dev.c index ca4cdef74a1..876b1112d5b 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -2646,6 +2646,55 @@ void netif_nit_deliver(struct sk_buff *skb) rcu_read_unlock(); } +static inline void skb_bond_set_mac_by_master(struct sk_buff *skb, + struct net_device *master) +{ + if (skb->pkt_type == PACKET_HOST) { + u16 *dest = (u16 *) eth_hdr(skb)->h_dest; + + memcpy(dest, master->dev_addr, ETH_ALEN); + } +} + +/* On bonding slaves other than the currently active slave, suppress + * duplicates except for 802.3ad ETH_P_SLOW, alb non-mcast/bcast, and + * ARP on active-backup slaves with arp_validate enabled. + */ +int __skb_bond_should_drop(struct sk_buff *skb, struct net_device *master) +{ + struct net_device *dev = skb->dev; + + if (master->priv_flags & IFF_MASTER_ARPMON) + dev->last_rx = jiffies; + + if ((master->priv_flags & IFF_MASTER_ALB) && master->br_port) { + /* Do address unmangle. The local destination address + * will be always the one master has. Provides the right + * functionality in a bridge. + */ + skb_bond_set_mac_by_master(skb, master); + } + + if (dev->priv_flags & IFF_SLAVE_INACTIVE) { + if ((dev->priv_flags & IFF_SLAVE_NEEDARP) && + skb->protocol == __cpu_to_be16(ETH_P_ARP)) + return 0; + + if (master->priv_flags & IFF_MASTER_ALB) { + if (skb->pkt_type != PACKET_BROADCAST && + skb->pkt_type != PACKET_MULTICAST) + return 0; + } + if (master->priv_flags & IFF_MASTER_8023AD && + skb->protocol == __cpu_to_be16(ETH_P_SLOW)) + return 0; + + return 1; + } + return 0; +} +EXPORT_SYMBOL(__skb_bond_should_drop); + static int __netif_receive_skb(struct sk_buff *skb) { struct packet_type *ptype, *pt_prev; -- cgit v1.2.3 From 9f93ff5be54108066372d1c4100c515d9d9acc1b Mon Sep 17 00:00:00 2001 From: Alexey Dobriyan Date: Tue, 13 Apr 2010 14:09:15 +0200 Subject: Restore __ALIGN_MASK() Fix lib/bitmap.c compile failure due to __ALIGN_KERNEL changes. Signed-off-by: Alexey Dobriyan Signed-off-by: Patrick McHardy --- include/linux/kernel.h | 1 + 1 file changed, 1 insertion(+) (limited to 'include/linux') diff --git a/include/linux/kernel.h b/include/linux/kernel.h index 284ea995646..db6717d0fd6 100644 --- a/include/linux/kernel.h +++ b/include/linux/kernel.h @@ -40,6 +40,7 @@ extern const char linux_proc_banner[]; #define STACK_MAGIC 0xdeadbeef #define ALIGN(x, a) __ALIGN_KERNEL((x), (a)) +#define __ALIGN_MASK(x, mask) __ALIGN_KERNEL_MASK((x), (mask)) #define PTR_ALIGN(p, a) ((typeof(p))ALIGN((unsigned long)(p), (a))) #define IS_ALIGNED(x, a) (((x) & ((typeof(x))(a) - 1)) == 0) -- cgit v1.2.3 From 9c6eb28aca52d562f3ffbaebaa56385df9972a43 Mon Sep 17 00:00:00 2001 From: Jan Engelhardt Date: Tue, 13 Apr 2010 15:32:16 +0200 Subject: netfilter: ipv6: add IPSKB_REROUTED exclusion to NF_HOOK/POSTROUTING invocation Similar to how IPv4's ip_output.c works, have ip6_output also check the IPSKB_REROUTED flag. It will be set from xt_TEE for cloned packets since Xtables can currently only deal with a single packet in flight at a time. Signed-off-by: Jan Engelhardt Acked-by: David S. Miller [Patrick: changed to use an IP6SKB value instead of IPSKB] Signed-off-by: Patrick McHardy --- include/linux/ipv6.h | 1 + net/ipv6/ip6_output.c | 5 +++-- 2 files changed, 4 insertions(+), 2 deletions(-) (limited to 'include/linux') diff --git a/include/linux/ipv6.h b/include/linux/ipv6.h index e0cc9a7db2b..7bdf6ffe2b4 100644 --- a/include/linux/ipv6.h +++ b/include/linux/ipv6.h @@ -250,6 +250,7 @@ struct inet6_skb_parm { #define IP6SKB_XFRM_TRANSFORMED 1 #define IP6SKB_FORWARDED 2 +#define IP6SKB_REROUTED 4 }; #define IP6CB(skb) ((struct inet6_skb_parm*)((skb)->cb)) diff --git a/net/ipv6/ip6_output.c b/net/ipv6/ip6_output.c index 236ac781374..c10a38a71a5 100644 --- a/net/ipv6/ip6_output.c +++ b/net/ipv6/ip6_output.c @@ -172,8 +172,9 @@ int ip6_output(struct sk_buff *skb) return 0; } - return NF_HOOK(NFPROTO_IPV6, NF_INET_POST_ROUTING, skb, NULL, dev, - ip6_finish_output); + return NF_HOOK_COND(NFPROTO_IPV6, NF_INET_POST_ROUTING, skb, NULL, dev, + ip6_finish_output, + !(IP6CB(skb)->flags & IP6SKB_REROUTED)); } /* -- cgit v1.2.3 From 0f87b1dd01b51dc3c789f7a212656a4a87eee1bd Mon Sep 17 00:00:00 2001 From: Patrick McHardy Date: Tue, 13 Apr 2010 05:03:17 +0000 Subject: net: fib_rules: decouple address families from real address families Decouple the address family values used for fib_rules from the real address families in socket.h. This allows to use fib_rules for code that is not a real address family without increasing AF_MAX/NPROTO. Values up to 127 are reserved for real address families and map directly to the corresponding AF value, values starting from 128 are for other uses. rtnetlink is changed to invoke the AF_UNSPEC dumpit/doit handlers for these families. Signed-off-by: Patrick McHardy Signed-off-by: David S. Miller --- include/linux/fib_rules.h | 7 +++++++ net/core/rtnetlink.c | 15 ++++++++++----- net/decnet/dn_rules.c | 2 +- net/ipv4/fib_rules.c | 2 +- net/ipv6/fib6_rules.c | 2 +- 5 files changed, 20 insertions(+), 8 deletions(-) (limited to 'include/linux') diff --git a/include/linux/fib_rules.h b/include/linux/fib_rules.h index 51da65b68b8..405e41139a4 100644 --- a/include/linux/fib_rules.h +++ b/include/linux/fib_rules.h @@ -15,6 +15,13 @@ /* try to find source address in routing lookups */ #define FIB_RULE_FIND_SADDR 0x00010000 +/* fib_rules families. values up to 127 are reserved for real address + * families, values above 128 may be used arbitrarily. + */ +#define FIB_RULES_IPV4 AF_INET +#define FIB_RULES_IPV6 AF_INET6 +#define FIB_RULES_DECNET AF_DECnet + struct fib_rule_hdr { __u8 family; __u8 dst_len; diff --git a/net/core/rtnetlink.c b/net/core/rtnetlink.c index bf919b6acea..78c85985cb3 100644 --- a/net/core/rtnetlink.c +++ b/net/core/rtnetlink.c @@ -118,7 +118,11 @@ static rtnl_doit_func rtnl_get_doit(int protocol, int msgindex) { struct rtnl_link *tab; - tab = rtnl_msg_handlers[protocol]; + if (protocol < NPROTO) + tab = rtnl_msg_handlers[protocol]; + else + tab = NULL; + if (tab == NULL || tab[msgindex].doit == NULL) tab = rtnl_msg_handlers[PF_UNSPEC]; @@ -129,7 +133,11 @@ static rtnl_dumpit_func rtnl_get_dumpit(int protocol, int msgindex) { struct rtnl_link *tab; - tab = rtnl_msg_handlers[protocol]; + if (protocol < NPROTO) + tab = rtnl_msg_handlers[protocol]; + else + tab = NULL; + if (tab == NULL || tab[msgindex].dumpit == NULL) tab = rtnl_msg_handlers[PF_UNSPEC]; @@ -1444,9 +1452,6 @@ static int rtnetlink_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh) return 0; family = ((struct rtgenmsg *)NLMSG_DATA(nlh))->rtgen_family; - if (family >= NPROTO) - return -EAFNOSUPPORT; - sz_idx = type>>2; kind = type&3; diff --git a/net/decnet/dn_rules.c b/net/decnet/dn_rules.c index 1c8cc6d5b64..af28dcc2184 100644 --- a/net/decnet/dn_rules.c +++ b/net/decnet/dn_rules.c @@ -217,7 +217,7 @@ static void dn_fib_rule_flush_cache(struct fib_rules_ops *ops) } static struct fib_rules_ops dn_fib_rules_ops_template = { - .family = AF_DECnet, + .family = FIB_RULES_DECNET, .rule_size = sizeof(struct dn_fib_rule), .addr_size = sizeof(u16), .action = dn_fib_rule_action, diff --git a/net/ipv4/fib_rules.c b/net/ipv4/fib_rules.c index a18355e1511..3ec84fea5b7 100644 --- a/net/ipv4/fib_rules.c +++ b/net/ipv4/fib_rules.c @@ -246,7 +246,7 @@ static void fib4_rule_flush_cache(struct fib_rules_ops *ops) } static struct fib_rules_ops fib4_rules_ops_template = { - .family = AF_INET, + .family = FIB_RULES_IPV4, .rule_size = sizeof(struct fib4_rule), .addr_size = sizeof(u32), .action = fib4_rule_action, diff --git a/net/ipv6/fib6_rules.c b/net/ipv6/fib6_rules.c index 92b2b7fb6c3..8124f16f2ac 100644 --- a/net/ipv6/fib6_rules.c +++ b/net/ipv6/fib6_rules.c @@ -238,7 +238,7 @@ static size_t fib6_rule_nlmsg_payload(struct fib_rule *rule) } static struct fib_rules_ops fib6_rules_ops_template = { - .family = AF_INET6, + .family = FIB_RULES_IPV6, .rule_size = sizeof(struct fib6_rule), .addr_size = sizeof(struct in6_addr), .action = fib6_rule_action, -- cgit v1.2.3 From d658f8a0e63b6476148162aa7a3ffffc58dcad52 Mon Sep 17 00:00:00 2001 From: Patrick McHardy Date: Tue, 13 Apr 2010 05:03:20 +0000 Subject: ipv4: ipmr: remove net pointer from struct mfc_cache Now that cache entries in unres_queue don't need to be distinguished by their network namespace pointer anymore, we can remove it from struct mfc_cache add pass the namespace as function argument to the functions that need it. Signed-off-by: Patrick McHardy Signed-off-by: David S. Miller --- include/linux/mroute.h | 15 ------------ net/ipv4/ipmr.c | 65 +++++++++++++++++++++++++------------------------- 2 files changed, 32 insertions(+), 48 deletions(-) (limited to 'include/linux') diff --git a/include/linux/mroute.h b/include/linux/mroute.h index c5f3d53548e..de7780a6dd3 100644 --- a/include/linux/mroute.h +++ b/include/linux/mroute.h @@ -192,9 +192,6 @@ struct vif_device { struct mfc_cache { struct mfc_cache *next; /* Next entry on cache line */ -#ifdef CONFIG_NET_NS - struct net *mfc_net; -#endif __be32 mfc_mcastgrp; /* Group the entry belongs to */ __be32 mfc_origin; /* Source of packet */ vifi_t mfc_parent; /* Source interface */ @@ -217,18 +214,6 @@ struct mfc_cache { } mfc_un; }; -static inline -struct net *mfc_net(const struct mfc_cache *mfc) -{ - return read_pnet(&mfc->mfc_net); -} - -static inline -void mfc_net_set(struct mfc_cache *mfc, struct net *net) -{ - write_pnet(&mfc->mfc_net, hold_net(net)); -} - #define MFC_STATIC 1 #define MFC_NOTIFY 2 diff --git a/net/ipv4/ipmr.c b/net/ipv4/ipmr.c index d6aa65e2b08..f8e25c8ba07 100644 --- a/net/ipv4/ipmr.c +++ b/net/ipv4/ipmr.c @@ -93,10 +93,12 @@ static DEFINE_SPINLOCK(mfc_unres_lock); static struct kmem_cache *mrt_cachep __read_mostly; -static int ip_mr_forward(struct sk_buff *skb, struct mfc_cache *cache, int local); +static int ip_mr_forward(struct net *net, struct sk_buff *skb, + struct mfc_cache *cache, int local); static int ipmr_cache_report(struct net *net, struct sk_buff *pkt, vifi_t vifi, int assert); -static int ipmr_fill_mroute(struct sk_buff *skb, struct mfc_cache *c, struct rtmsg *rtm); +static int ipmr_fill_mroute(struct net *net, struct sk_buff *skb, + struct mfc_cache *c, struct rtmsg *rtm); /* Service routines creating virtual interfaces: DVMRP tunnels and PIMREG */ @@ -325,7 +327,6 @@ static int vif_delete(struct net *net, int vifi, int notify, static inline void ipmr_cache_free(struct mfc_cache *c) { - release_net(mfc_net(c)); kmem_cache_free(mrt_cachep, c); } @@ -333,11 +334,10 @@ static inline void ipmr_cache_free(struct mfc_cache *c) and reporting error to netlink readers. */ -static void ipmr_destroy_unres(struct mfc_cache *c) +static void ipmr_destroy_unres(struct net *net, struct mfc_cache *c) { struct sk_buff *skb; struct nlmsgerr *e; - struct net *net = mfc_net(c); atomic_dec(&net->ipv4.cache_resolve_queue_len); @@ -392,7 +392,7 @@ static void ipmr_expire_process(unsigned long arg) *cp = c->next; - ipmr_destroy_unres(c); + ipmr_destroy_unres(net, c); } if (net->ipv4.mfc_unres_queue != NULL) @@ -404,10 +404,10 @@ out: /* Fill oifs list. It is called under write locked mrt_lock. */ -static void ipmr_update_thresholds(struct mfc_cache *cache, unsigned char *ttls) +static void ipmr_update_thresholds(struct net *net, struct mfc_cache *cache, + unsigned char *ttls) { int vifi; - struct net *net = mfc_net(cache); cache->mfc_un.res.minvif = MAXVIFS; cache->mfc_un.res.maxvif = 0; @@ -547,24 +547,22 @@ static struct mfc_cache *ipmr_cache_find(struct net *net, /* * Allocate a multicast cache entry */ -static struct mfc_cache *ipmr_cache_alloc(struct net *net) +static struct mfc_cache *ipmr_cache_alloc(void) { struct mfc_cache *c = kmem_cache_zalloc(mrt_cachep, GFP_KERNEL); if (c == NULL) return NULL; c->mfc_un.res.minvif = MAXVIFS; - mfc_net_set(c, net); return c; } -static struct mfc_cache *ipmr_cache_alloc_unres(struct net *net) +static struct mfc_cache *ipmr_cache_alloc_unres(void) { struct mfc_cache *c = kmem_cache_zalloc(mrt_cachep, GFP_ATOMIC); if (c == NULL) return NULL; skb_queue_head_init(&c->mfc_un.unres.unresolved); c->mfc_un.unres.expires = jiffies + 10*HZ; - mfc_net_set(c, net); return c; } @@ -572,7 +570,8 @@ static struct mfc_cache *ipmr_cache_alloc_unres(struct net *net) * A cache entry has gone into a resolved state from queued */ -static void ipmr_cache_resolve(struct mfc_cache *uc, struct mfc_cache *c) +static void ipmr_cache_resolve(struct net *net, struct mfc_cache *uc, + struct mfc_cache *c) { struct sk_buff *skb; struct nlmsgerr *e; @@ -585,7 +584,7 @@ static void ipmr_cache_resolve(struct mfc_cache *uc, struct mfc_cache *c) if (ip_hdr(skb)->version == 0) { struct nlmsghdr *nlh = (struct nlmsghdr *)skb_pull(skb, sizeof(struct iphdr)); - if (ipmr_fill_mroute(skb, c, NLMSG_DATA(nlh)) > 0) { + if (ipmr_fill_mroute(net, skb, c, NLMSG_DATA(nlh)) > 0) { nlh->nlmsg_len = (skb_tail_pointer(skb) - (u8 *)nlh); } else { @@ -597,9 +596,9 @@ static void ipmr_cache_resolve(struct mfc_cache *uc, struct mfc_cache *c) memset(&e->msg, 0, sizeof(e->msg)); } - rtnl_unicast(skb, mfc_net(c), NETLINK_CB(skb).pid); + rtnl_unicast(skb, net, NETLINK_CB(skb).pid); } else - ip_mr_forward(skb, c, 0); + ip_mr_forward(net, skb, c, 0); } } @@ -717,7 +716,7 @@ ipmr_cache_unresolved(struct net *net, vifi_t vifi, struct sk_buff *skb) */ if (atomic_read(&net->ipv4.cache_resolve_queue_len) >= 10 || - (c = ipmr_cache_alloc_unres(net)) == NULL) { + (c = ipmr_cache_alloc_unres()) == NULL) { spin_unlock_bh(&mfc_unres_lock); kfree_skb(skb); @@ -814,7 +813,7 @@ static int ipmr_mfc_add(struct net *net, struct mfcctl *mfc, int mrtsock) if (c != NULL) { write_lock_bh(&mrt_lock); c->mfc_parent = mfc->mfcc_parent; - ipmr_update_thresholds(c, mfc->mfcc_ttls); + ipmr_update_thresholds(net, c, mfc->mfcc_ttls); if (!mrtsock) c->mfc_flags |= MFC_STATIC; write_unlock_bh(&mrt_lock); @@ -824,14 +823,14 @@ static int ipmr_mfc_add(struct net *net, struct mfcctl *mfc, int mrtsock) if (!ipv4_is_multicast(mfc->mfcc_mcastgrp.s_addr)) return -EINVAL; - c = ipmr_cache_alloc(net); + c = ipmr_cache_alloc(); if (c == NULL) return -ENOMEM; c->mfc_origin = mfc->mfcc_origin.s_addr; c->mfc_mcastgrp = mfc->mfcc_mcastgrp.s_addr; c->mfc_parent = mfc->mfcc_parent; - ipmr_update_thresholds(c, mfc->mfcc_ttls); + ipmr_update_thresholds(net, c, mfc->mfcc_ttls); if (!mrtsock) c->mfc_flags |= MFC_STATIC; @@ -859,7 +858,7 @@ static int ipmr_mfc_add(struct net *net, struct mfcctl *mfc, int mrtsock) spin_unlock_bh(&mfc_unres_lock); if (uc) { - ipmr_cache_resolve(uc, c); + ipmr_cache_resolve(net, uc, c); ipmr_cache_free(uc); } return 0; @@ -910,7 +909,7 @@ static void mroute_clean_tables(struct net *net) cp = &net->ipv4.mfc_unres_queue; while ((c = *cp) != NULL) { *cp = c->next; - ipmr_destroy_unres(c); + ipmr_destroy_unres(net, c); } spin_unlock_bh(&mfc_unres_lock); } @@ -1221,9 +1220,9 @@ static inline int ipmr_forward_finish(struct sk_buff *skb) * Processing handlers for ipmr_forward */ -static void ipmr_queue_xmit(struct sk_buff *skb, struct mfc_cache *c, int vifi) +static void ipmr_queue_xmit(struct net *net, struct sk_buff *skb, + struct mfc_cache *c, int vifi) { - struct net *net = mfc_net(c); const struct iphdr *iph = ip_hdr(skb); struct vif_device *vif = &net->ipv4.vif_table[vifi]; struct net_device *dev; @@ -1335,11 +1334,11 @@ static int ipmr_find_vif(struct net_device *dev) /* "local" means that we should preserve one skb (for local delivery) */ -static int ip_mr_forward(struct sk_buff *skb, struct mfc_cache *cache, int local) +static int ip_mr_forward(struct net *net, struct sk_buff *skb, + struct mfc_cache *cache, int local) { int psend = -1; int vif, ct; - struct net *net = mfc_net(cache); vif = cache->mfc_parent; cache->mfc_un.res.pkt++; @@ -1396,7 +1395,7 @@ static int ip_mr_forward(struct sk_buff *skb, struct mfc_cache *cache, int local if (psend != -1) { struct sk_buff *skb2 = skb_clone(skb, GFP_ATOMIC); if (skb2) - ipmr_queue_xmit(skb2, cache, psend); + ipmr_queue_xmit(net, skb2, cache, psend); } psend = ct; } @@ -1405,9 +1404,9 @@ static int ip_mr_forward(struct sk_buff *skb, struct mfc_cache *cache, int local if (local) { struct sk_buff *skb2 = skb_clone(skb, GFP_ATOMIC); if (skb2) - ipmr_queue_xmit(skb2, cache, psend); + ipmr_queue_xmit(net, skb2, cache, psend); } else { - ipmr_queue_xmit(skb, cache, psend); + ipmr_queue_xmit(net, skb, cache, psend); return 0; } } @@ -1488,7 +1487,7 @@ int ip_mr_input(struct sk_buff *skb) return -ENODEV; } - ip_mr_forward(skb, cache, local); + ip_mr_forward(net, skb, cache, local); read_unlock(&mrt_lock); @@ -1602,11 +1601,11 @@ drop: #endif static int -ipmr_fill_mroute(struct sk_buff *skb, struct mfc_cache *c, struct rtmsg *rtm) +ipmr_fill_mroute(struct net *net, struct sk_buff *skb, struct mfc_cache *c, + struct rtmsg *rtm) { int ct; struct rtnexthop *nhp; - struct net *net = mfc_net(c); u8 *b = skb_tail_pointer(skb); struct rtattr *mp_head; @@ -1686,7 +1685,7 @@ int ipmr_get_route(struct net *net, if (!nowait && (rtm->rtm_flags&RTM_F_NOTIFY)) cache->mfc_flags |= MFC_NOTIFY; - err = ipmr_fill_mroute(skb, cache, rtm); + err = ipmr_fill_mroute(net, skb, cache, rtm); read_unlock(&mrt_lock); return err; } -- cgit v1.2.3 From 862465f2e7e90975e7bf0ecfbb171dd3adedd950 Mon Sep 17 00:00:00 2001 From: Patrick McHardy Date: Tue, 13 Apr 2010 05:03:21 +0000 Subject: ipv4: ipmr: convert struct mfc_cache to struct list_head Signed-off-by: Patrick McHardy Signed-off-by: David S. Miller --- include/linux/mroute.h | 2 +- include/net/netns/ipv4.h | 4 +- net/ipv4/ipmr.c | 125 +++++++++++++++++++++++------------------------ 3 files changed, 64 insertions(+), 67 deletions(-) (limited to 'include/linux') diff --git a/include/linux/mroute.h b/include/linux/mroute.h index de7780a6dd3..7ff6c77d600 100644 --- a/include/linux/mroute.h +++ b/include/linux/mroute.h @@ -191,7 +191,7 @@ struct vif_device { #define VIFF_STATIC 0x8000 struct mfc_cache { - struct mfc_cache *next; /* Next entry on cache line */ + struct list_head list; __be32 mfc_mcastgrp; /* Group the entry belongs to */ __be32 mfc_origin; /* Source of packet */ vifi_t mfc_parent; /* Source interface */ diff --git a/include/net/netns/ipv4.h b/include/net/netns/ipv4.h index b15e518f952..5d06429968d 100644 --- a/include/net/netns/ipv4.h +++ b/include/net/netns/ipv4.h @@ -61,8 +61,8 @@ struct netns_ipv4 { #ifdef CONFIG_IP_MROUTE struct sock *mroute_sk; struct timer_list ipmr_expire_timer; - struct mfc_cache *mfc_unres_queue; - struct mfc_cache **mfc_cache_array; + struct list_head mfc_unres_queue; + struct list_head *mfc_cache_array; struct vif_device *vif_table; int maxvif; atomic_t cache_resolve_queue_len; diff --git a/net/ipv4/ipmr.c b/net/ipv4/ipmr.c index f8e25c8ba07..21b5edc2f34 100644 --- a/net/ipv4/ipmr.c +++ b/net/ipv4/ipmr.c @@ -367,35 +367,32 @@ static void ipmr_expire_process(unsigned long arg) struct net *net = (struct net *)arg; unsigned long now; unsigned long expires; - struct mfc_cache *c, **cp; + struct mfc_cache *c, *next; if (!spin_trylock(&mfc_unres_lock)) { mod_timer(&net->ipv4.ipmr_expire_timer, jiffies+HZ/10); return; } - if (net->ipv4.mfc_unres_queue == NULL) + if (list_empty(&net->ipv4.mfc_unres_queue)) goto out; now = jiffies; expires = 10*HZ; - cp = &net->ipv4.mfc_unres_queue; - while ((c=*cp) != NULL) { + list_for_each_entry_safe(c, next, &net->ipv4.mfc_unres_queue, list) { if (time_after(c->mfc_un.unres.expires, now)) { unsigned long interval = c->mfc_un.unres.expires - now; if (interval < expires) expires = interval; - cp = &c->next; continue; } - *cp = c->next; - + list_del(&c->list); ipmr_destroy_unres(net, c); } - if (net->ipv4.mfc_unres_queue != NULL) + if (!list_empty(&net->ipv4.mfc_unres_queue)) mod_timer(&net->ipv4.ipmr_expire_timer, jiffies + expires); out: @@ -537,11 +534,11 @@ static struct mfc_cache *ipmr_cache_find(struct net *net, int line = MFC_HASH(mcastgrp, origin); struct mfc_cache *c; - for (c = net->ipv4.mfc_cache_array[line]; c; c = c->next) { - if (c->mfc_origin==origin && c->mfc_mcastgrp==mcastgrp) - break; + list_for_each_entry(c, &net->ipv4.mfc_cache_array[line], list) { + if (c->mfc_origin == origin && c->mfc_mcastgrp == mcastgrp) + return c; } - return c; + return NULL; } /* @@ -699,18 +696,21 @@ static int ipmr_cache_report(struct net *net, static int ipmr_cache_unresolved(struct net *net, vifi_t vifi, struct sk_buff *skb) { + bool found = false; int err; struct mfc_cache *c; const struct iphdr *iph = ip_hdr(skb); spin_lock_bh(&mfc_unres_lock); - for (c=net->ipv4.mfc_unres_queue; c; c=c->next) { + list_for_each_entry(c, &net->ipv4.mfc_unres_queue, list) { if (c->mfc_mcastgrp == iph->daddr && - c->mfc_origin == iph->saddr) + c->mfc_origin == iph->saddr) { + found = true; break; + } } - if (c == NULL) { + if (!found) { /* * Create a new entry if allowable */ @@ -746,8 +746,7 @@ ipmr_cache_unresolved(struct net *net, vifi_t vifi, struct sk_buff *skb) } atomic_inc(&net->ipv4.cache_resolve_queue_len); - c->next = net->ipv4.mfc_unres_queue; - net->ipv4.mfc_unres_queue = c; + list_add(&c->list, &net->ipv4.mfc_unres_queue); mod_timer(&net->ipv4.ipmr_expire_timer, c->mfc_un.unres.expires); } @@ -774,16 +773,15 @@ ipmr_cache_unresolved(struct net *net, vifi_t vifi, struct sk_buff *skb) static int ipmr_mfc_delete(struct net *net, struct mfcctl *mfc) { int line; - struct mfc_cache *c, **cp; + struct mfc_cache *c, *next; line = MFC_HASH(mfc->mfcc_mcastgrp.s_addr, mfc->mfcc_origin.s_addr); - for (cp = &net->ipv4.mfc_cache_array[line]; - (c = *cp) != NULL; cp = &c->next) { + list_for_each_entry_safe(c, next, &net->ipv4.mfc_cache_array[line], list) { if (c->mfc_origin == mfc->mfcc_origin.s_addr && c->mfc_mcastgrp == mfc->mfcc_mcastgrp.s_addr) { write_lock_bh(&mrt_lock); - *cp = c->next; + list_del(&c->list); write_unlock_bh(&mrt_lock); ipmr_cache_free(c); @@ -795,22 +793,24 @@ static int ipmr_mfc_delete(struct net *net, struct mfcctl *mfc) static int ipmr_mfc_add(struct net *net, struct mfcctl *mfc, int mrtsock) { + bool found = false; int line; - struct mfc_cache *uc, *c, **cp; + struct mfc_cache *uc, *c; if (mfc->mfcc_parent >= MAXVIFS) return -ENFILE; line = MFC_HASH(mfc->mfcc_mcastgrp.s_addr, mfc->mfcc_origin.s_addr); - for (cp = &net->ipv4.mfc_cache_array[line]; - (c = *cp) != NULL; cp = &c->next) { + list_for_each_entry(c, &net->ipv4.mfc_cache_array[line], list) { if (c->mfc_origin == mfc->mfcc_origin.s_addr && - c->mfc_mcastgrp == mfc->mfcc_mcastgrp.s_addr) + c->mfc_mcastgrp == mfc->mfcc_mcastgrp.s_addr) { + found = true; break; + } } - if (c != NULL) { + if (found) { write_lock_bh(&mrt_lock); c->mfc_parent = mfc->mfcc_parent; ipmr_update_thresholds(net, c, mfc->mfcc_ttls); @@ -835,8 +835,7 @@ static int ipmr_mfc_add(struct net *net, struct mfcctl *mfc, int mrtsock) c->mfc_flags |= MFC_STATIC; write_lock_bh(&mrt_lock); - c->next = net->ipv4.mfc_cache_array[line]; - net->ipv4.mfc_cache_array[line] = c; + list_add(&c->list, &net->ipv4.mfc_cache_array[line]); write_unlock_bh(&mrt_lock); /* @@ -844,16 +843,15 @@ static int ipmr_mfc_add(struct net *net, struct mfcctl *mfc, int mrtsock) * need to send on the frames and tidy up. */ spin_lock_bh(&mfc_unres_lock); - for (cp = &net->ipv4.mfc_unres_queue; (uc=*cp) != NULL; - cp = &uc->next) { + list_for_each_entry(uc, &net->ipv4.mfc_unres_queue, list) { if (uc->mfc_origin == c->mfc_origin && uc->mfc_mcastgrp == c->mfc_mcastgrp) { - *cp = uc->next; + list_del(&uc->list); atomic_dec(&net->ipv4.cache_resolve_queue_len); break; } } - if (net->ipv4.mfc_unres_queue == NULL) + if (list_empty(&net->ipv4.mfc_unres_queue)) del_timer(&net->ipv4.ipmr_expire_timer); spin_unlock_bh(&mfc_unres_lock); @@ -872,6 +870,7 @@ static void mroute_clean_tables(struct net *net) { int i; LIST_HEAD(list); + struct mfc_cache *c, *next; /* * Shut down all active vif entries @@ -885,17 +884,12 @@ static void mroute_clean_tables(struct net *net) /* * Wipe the cache */ - for (i=0; iipv4.mfc_cache_array[i]; - while ((c = *cp) != NULL) { - if (c->mfc_flags&MFC_STATIC) { - cp = &c->next; + for (i = 0; i < MFC_LINES; i++) { + list_for_each_entry_safe(c, next, &net->ipv4.mfc_cache_array[i], list) { + if (c->mfc_flags&MFC_STATIC) continue; - } write_lock_bh(&mrt_lock); - *cp = c->next; + list_del(&c->list); write_unlock_bh(&mrt_lock); ipmr_cache_free(c); @@ -903,12 +897,9 @@ static void mroute_clean_tables(struct net *net) } if (atomic_read(&net->ipv4.cache_resolve_queue_len) != 0) { - struct mfc_cache *c, **cp; - spin_lock_bh(&mfc_unres_lock); - cp = &net->ipv4.mfc_unres_queue; - while ((c = *cp) != NULL) { - *cp = c->next; + list_for_each_entry_safe(c, next, &net->ipv4.mfc_unres_queue, list) { + list_del(&c->list); ipmr_destroy_unres(net, c); } spin_unlock_bh(&mfc_unres_lock); @@ -1789,7 +1780,7 @@ static const struct file_operations ipmr_vif_fops = { struct ipmr_mfc_iter { struct seq_net_private p; - struct mfc_cache **cache; + struct list_head *cache; int ct; }; @@ -1799,18 +1790,18 @@ static struct mfc_cache *ipmr_mfc_seq_idx(struct net *net, { struct mfc_cache *mfc; - it->cache = net->ipv4.mfc_cache_array; read_lock(&mrt_lock); - for (it->ct = 0; it->ct < MFC_LINES; it->ct++) - for (mfc = net->ipv4.mfc_cache_array[it->ct]; - mfc; mfc = mfc->next) + for (it->ct = 0; it->ct < MFC_LINES; it->ct++) { + it->cache = &net->ipv4.mfc_cache_array[it->ct]; + list_for_each_entry(mfc, it->cache, list) if (pos-- == 0) return mfc; + } read_unlock(&mrt_lock); - it->cache = &net->ipv4.mfc_unres_queue; spin_lock_bh(&mfc_unres_lock); - for (mfc = net->ipv4.mfc_unres_queue; mfc; mfc = mfc->next) + it->cache = &net->ipv4.mfc_unres_queue; + list_for_each_entry(mfc, it->cache, list) if (pos-- == 0) return mfc; spin_unlock_bh(&mfc_unres_lock); @@ -1842,18 +1833,19 @@ static void *ipmr_mfc_seq_next(struct seq_file *seq, void *v, loff_t *pos) if (v == SEQ_START_TOKEN) return ipmr_mfc_seq_idx(net, seq->private, 0); - if (mfc->next) - return mfc->next; + if (mfc->list.next != it->cache) + return list_entry(mfc->list.next, struct mfc_cache, list); if (it->cache == &net->ipv4.mfc_unres_queue) goto end_of_list; - BUG_ON(it->cache != net->ipv4.mfc_cache_array); + BUG_ON(it->cache != &net->ipv4.mfc_cache_array[it->ct]); while (++it->ct < MFC_LINES) { - mfc = net->ipv4.mfc_cache_array[it->ct]; - if (mfc) - return mfc; + it->cache = &net->ipv4.mfc_cache_array[it->ct]; + if (list_empty(it->cache)) + continue; + return list_first_entry(it->cache, struct mfc_cache, list); } /* exhausted cache_array, show unresolved */ @@ -1862,9 +1854,8 @@ static void *ipmr_mfc_seq_next(struct seq_file *seq, void *v, loff_t *pos) it->ct = 0; spin_lock_bh(&mfc_unres_lock); - mfc = net->ipv4.mfc_unres_queue; - if (mfc) - return mfc; + if (!list_empty(it->cache)) + return list_first_entry(it->cache, struct mfc_cache, list); end_of_list: spin_unlock_bh(&mfc_unres_lock); @@ -1880,7 +1871,7 @@ static void ipmr_mfc_seq_stop(struct seq_file *seq, void *v) if (it->cache == &net->ipv4.mfc_unres_queue) spin_unlock_bh(&mfc_unres_lock); - else if (it->cache == net->ipv4.mfc_cache_array) + else if (it->cache == &net->ipv4.mfc_cache_array[it->ct]) read_unlock(&mrt_lock); } @@ -1960,6 +1951,7 @@ static const struct net_protocol pim_protocol = { */ static int __net_init ipmr_net_init(struct net *net) { + unsigned int i; int err = 0; net->ipv4.vif_table = kcalloc(MAXVIFS, sizeof(struct vif_device), @@ -1971,13 +1963,18 @@ static int __net_init ipmr_net_init(struct net *net) /* Forwarding cache */ net->ipv4.mfc_cache_array = kcalloc(MFC_LINES, - sizeof(struct mfc_cache *), + sizeof(struct list_head), GFP_KERNEL); if (!net->ipv4.mfc_cache_array) { err = -ENOMEM; goto fail_mfc_cache; } + for (i = 0; i < MFC_LINES; i++) + INIT_LIST_HEAD(&net->ipv4.mfc_cache_array[i]); + + INIT_LIST_HEAD(&net->ipv4.mfc_unres_queue); + setup_timer(&net->ipv4.ipmr_expire_timer, ipmr_expire_process, (unsigned long)net); -- cgit v1.2.3 From f0ad0860d01e47a3ffd220564c5c653b3afbe962 Mon Sep 17 00:00:00 2001 From: Patrick McHardy Date: Tue, 13 Apr 2010 05:03:23 +0000 Subject: ipv4: ipmr: support multiple tables This patch adds support for multiple independant multicast routing instances, named "tables". Userspace multicast routing daemons can bind to a specific table instance by issuing a setsockopt call using a new option MRT_TABLE. The table number is stored in the raw socket data and affects all following ipmr setsockopt(), getsockopt() and ioctl() calls. By default, a single table (RT_TABLE_DEFAULT) is created with a default routing rule pointing to it. Newly created pimreg devices have the table number appended ("pimregX"), with the exception of devices created in the default table, which are named just "pimreg" for compatibility reasons. Packets are directed to a specific table instance using routing rules, similar to how regular routing rules work. Currently iif, oif and mark are supported as keys, source and destination addresses could be supported additionally. Example usage: - bind pimd/xorp/... to a specific table: uint32_t table = 123; setsockopt(fd, IPPROTO_IP, MRT_TABLE, &table, sizeof(table)); - create routing rules directing packets to the new table: # ip mrule add iif eth0 lookup 123 # ip mrule add oif eth0 lookup 123 Signed-off-by: Patrick McHardy Signed-off-by: David S. Miller --- include/linux/fib_rules.h | 1 + include/linux/mroute.h | 3 +- include/net/netns/ipv4.h | 5 + include/net/raw.h | 1 + net/ipv4/Kconfig | 14 ++ net/ipv4/ipmr.c | 399 +++++++++++++++++++++++++++++++++++++++------- 6 files changed, 361 insertions(+), 62 deletions(-) (limited to 'include/linux') diff --git a/include/linux/fib_rules.h b/include/linux/fib_rules.h index 405e41139a4..04a397619eb 100644 --- a/include/linux/fib_rules.h +++ b/include/linux/fib_rules.h @@ -21,6 +21,7 @@ #define FIB_RULES_IPV4 AF_INET #define FIB_RULES_IPV6 AF_INET6 #define FIB_RULES_DECNET AF_DECnet +#define FIB_RULES_IPMR 128 struct fib_rule_hdr { __u8 family; diff --git a/include/linux/mroute.h b/include/linux/mroute.h index 7ff6c77d600..fa04b246c9a 100644 --- a/include/linux/mroute.h +++ b/include/linux/mroute.h @@ -27,7 +27,8 @@ #define MRT_DEL_MFC (MRT_BASE+5) /* Delete a multicast forwarding entry */ #define MRT_VERSION (MRT_BASE+6) /* Get the kernel multicast version */ #define MRT_ASSERT (MRT_BASE+7) /* Activate PIM assert mode */ -#define MRT_PIM (MRT_BASE+8) /* enable PIM code */ +#define MRT_PIM (MRT_BASE+8) /* enable PIM code */ +#define MRT_TABLE (MRT_BASE+9) /* Specify mroute table ID */ #define SIOCGETVIFCNT SIOCPROTOPRIVATE /* IP protocol privates */ #define SIOCGETSGCNT (SIOCPROTOPRIVATE+1) diff --git a/include/net/netns/ipv4.h b/include/net/netns/ipv4.h index 72e762ab3e5..ae07feec644 100644 --- a/include/net/netns/ipv4.h +++ b/include/net/netns/ipv4.h @@ -59,7 +59,12 @@ struct netns_ipv4 { atomic_t rt_genid; #ifdef CONFIG_IP_MROUTE +#ifndef CONFIG_IP_MROUTE_MULTIPLE_TABLES struct mr_table *mrt; +#else + struct list_head mr_tables; + struct fib_rules_ops *mr_rules_ops; +#endif #endif }; #endif diff --git a/include/net/raw.h b/include/net/raw.h index 67cc6436943..43c57502659 100644 --- a/include/net/raw.h +++ b/include/net/raw.h @@ -61,6 +61,7 @@ struct raw_sock { /* inet_sock has to be the first member */ struct inet_sock inet; struct icmp_filter filter; + u32 ipmr_table; }; static inline struct raw_sock *raw_sk(const struct sock *sk) diff --git a/net/ipv4/Kconfig b/net/ipv4/Kconfig index c9a1c68767f..be597749c38 100644 --- a/net/ipv4/Kconfig +++ b/net/ipv4/Kconfig @@ -250,6 +250,20 @@ config IP_MROUTE . If you haven't heard about it, you don't need it. +config IP_MROUTE_MULTIPLE_TABLES + bool "IP: multicast policy routing" + depends on IP_ADVANCED_ROUTER + select FIB_RULES + help + Normally, a multicast router runs a userspace daemon and decides + what to do with a multicast packet based on the source and + destination addresses. If you say Y here, the multicast router + will also be able to take interfaces and packet marks into + account and run multiple instances of userspace daemons + simultaneously, each one handling a single table. + + If unsure, say N. + config IP_PIMSM_V1 bool "IP: PIM-SM version 1 support" depends on IP_MROUTE diff --git a/net/ipv4/ipmr.c b/net/ipv4/ipmr.c index 498f4e907d5..5df5fd74c6d 100644 --- a/net/ipv4/ipmr.c +++ b/net/ipv4/ipmr.c @@ -63,12 +63,15 @@ #include #include #include +#include #if defined(CONFIG_IP_PIMSM_V1) || defined(CONFIG_IP_PIMSM_V2) #define CONFIG_IP_PIMSM 1 #endif struct mr_table { + struct list_head list; + u32 id; struct sock *mroute_sk; struct timer_list ipmr_expire_timer; struct list_head mfc_unres_queue; @@ -83,6 +86,14 @@ struct mr_table { #endif }; +struct ipmr_rule { + struct fib_rule common; +}; + +struct ipmr_result { + struct mr_table *mrt; +}; + /* Big lock, protecting vif table, mrt cache and mroute socket state. Note that the changes are semaphored via rtnl_lock. */ @@ -108,6 +119,7 @@ static DEFINE_SPINLOCK(mfc_unres_lock); static struct kmem_cache *mrt_cachep __read_mostly; +static struct mr_table *ipmr_new_table(struct net *net, u32 id); static int ip_mr_forward(struct net *net, struct mr_table *mrt, struct sk_buff *skb, struct mfc_cache *cache, int local); @@ -115,6 +127,206 @@ static int ipmr_cache_report(struct mr_table *mrt, struct sk_buff *pkt, vifi_t vifi, int assert); static int ipmr_fill_mroute(struct mr_table *mrt, struct sk_buff *skb, struct mfc_cache *c, struct rtmsg *rtm); +static void ipmr_expire_process(unsigned long arg); + +#ifdef CONFIG_IP_MROUTE_MULTIPLE_TABLES +#define ipmr_for_each_table(mrt, net) \ + list_for_each_entry_rcu(mrt, &net->ipv4.mr_tables, list) + +static struct mr_table *ipmr_get_table(struct net *net, u32 id) +{ + struct mr_table *mrt; + + ipmr_for_each_table(mrt, net) { + if (mrt->id == id) + return mrt; + } + return NULL; +} + +static int ipmr_fib_lookup(struct net *net, struct flowi *flp, + struct mr_table **mrt) +{ + struct ipmr_result res; + struct fib_lookup_arg arg = { .result = &res, }; + int err; + + err = fib_rules_lookup(net->ipv4.mr_rules_ops, flp, 0, &arg); + if (err < 0) + return err; + *mrt = res.mrt; + return 0; +} + +static int ipmr_rule_action(struct fib_rule *rule, struct flowi *flp, + int flags, struct fib_lookup_arg *arg) +{ + struct ipmr_result *res = arg->result; + struct mr_table *mrt; + + switch (rule->action) { + case FR_ACT_TO_TBL: + break; + case FR_ACT_UNREACHABLE: + return -ENETUNREACH; + case FR_ACT_PROHIBIT: + return -EACCES; + case FR_ACT_BLACKHOLE: + default: + return -EINVAL; + } + + mrt = ipmr_get_table(rule->fr_net, rule->table); + if (mrt == NULL) + return -EAGAIN; + res->mrt = mrt; + return 0; +} + +static int ipmr_rule_match(struct fib_rule *rule, struct flowi *fl, int flags) +{ + return 1; +} + +static const struct nla_policy ipmr_rule_policy[FRA_MAX + 1] = { + FRA_GENERIC_POLICY, +}; + +static int ipmr_rule_configure(struct fib_rule *rule, struct sk_buff *skb, + struct fib_rule_hdr *frh, struct nlattr **tb) +{ + return 0; +} + +static int ipmr_rule_compare(struct fib_rule *rule, struct fib_rule_hdr *frh, + struct nlattr **tb) +{ + return 1; +} + +static int ipmr_rule_fill(struct fib_rule *rule, struct sk_buff *skb, + struct fib_rule_hdr *frh) +{ + frh->dst_len = 0; + frh->src_len = 0; + frh->tos = 0; + return 0; +} + +static struct fib_rules_ops ipmr_rules_ops_template = { + .family = FIB_RULES_IPMR, + .rule_size = sizeof(struct ipmr_rule), + .addr_size = sizeof(u32), + .action = ipmr_rule_action, + .match = ipmr_rule_match, + .configure = ipmr_rule_configure, + .compare = ipmr_rule_compare, + .default_pref = fib_default_rule_pref, + .fill = ipmr_rule_fill, + .nlgroup = RTNLGRP_IPV4_RULE, + .policy = ipmr_rule_policy, + .owner = THIS_MODULE, +}; + +static int __net_init ipmr_rules_init(struct net *net) +{ + struct fib_rules_ops *ops; + struct mr_table *mrt; + int err; + + ops = fib_rules_register(&ipmr_rules_ops_template, net); + if (IS_ERR(ops)) + return PTR_ERR(ops); + + INIT_LIST_HEAD(&net->ipv4.mr_tables); + + mrt = ipmr_new_table(net, RT_TABLE_DEFAULT); + if (mrt == NULL) { + err = -ENOMEM; + goto err1; + } + + err = fib_default_rule_add(ops, 0x7fff, RT_TABLE_DEFAULT, 0); + if (err < 0) + goto err2; + + net->ipv4.mr_rules_ops = ops; + return 0; + +err2: + kfree(mrt); +err1: + fib_rules_unregister(ops); + return err; +} + +static void __net_exit ipmr_rules_exit(struct net *net) +{ + struct mr_table *mrt, *next; + + list_for_each_entry_safe(mrt, next, &net->ipv4.mr_tables, list) + kfree(mrt); + fib_rules_unregister(net->ipv4.mr_rules_ops); +} +#else +#define ipmr_for_each_table(mrt, net) \ + for (mrt = net->ipv4.mrt; mrt; mrt = NULL) + +static struct mr_table *ipmr_get_table(struct net *net, u32 id) +{ + return net->ipv4.mrt; +} + +static int ipmr_fib_lookup(struct net *net, struct flowi *flp, + struct mr_table **mrt) +{ + *mrt = net->ipv4.mrt; + return 0; +} + +static int __net_init ipmr_rules_init(struct net *net) +{ + net->ipv4.mrt = ipmr_new_table(net, RT_TABLE_DEFAULT); + return net->ipv4.mrt ? 0 : -ENOMEM; +} + +static void __net_exit ipmr_rules_exit(struct net *net) +{ + kfree(net->ipv4.mrt); +} +#endif + +static struct mr_table *ipmr_new_table(struct net *net, u32 id) +{ + struct mr_table *mrt; + unsigned int i; + + mrt = ipmr_get_table(net, id); + if (mrt != NULL) + return mrt; + + mrt = kzalloc(sizeof(*mrt), GFP_KERNEL); + if (mrt == NULL) + return NULL; + mrt->id = id; + + /* Forwarding cache */ + for (i = 0; i < MFC_LINES; i++) + INIT_LIST_HEAD(&mrt->mfc_cache_array[i]); + + INIT_LIST_HEAD(&mrt->mfc_unres_queue); + + setup_timer(&mrt->ipmr_expire_timer, ipmr_expire_process, + (unsigned long)mrt); + +#ifdef CONFIG_IP_PIMSM + mrt->mroute_reg_vif_num = -1; +#endif +#ifdef CONFIG_IP_MROUTE_MULTIPLE_TABLES + list_add_tail_rcu(&mrt->list, &net->ipv4.mr_tables); +#endif + return mrt; +} /* Service routines creating virtual interfaces: DVMRP tunnels and PIMREG */ @@ -215,7 +427,17 @@ failure: static netdev_tx_t reg_vif_xmit(struct sk_buff *skb, struct net_device *dev) { struct net *net = dev_net(dev); - struct mr_table *mrt = net->ipv4.mrt; + struct mr_table *mrt; + struct flowi fl = { + .oif = dev->ifindex, + .iif = skb->skb_iif, + .mark = skb->mark, + }; + int err; + + err = ipmr_fib_lookup(net, &fl, &mrt); + if (err < 0) + return err; read_lock(&mrt_lock); dev->stats.tx_bytes += skb->len; @@ -240,12 +462,18 @@ static void reg_vif_setup(struct net_device *dev) dev->features |= NETIF_F_NETNS_LOCAL; } -static struct net_device *ipmr_reg_vif(struct net *net) +static struct net_device *ipmr_reg_vif(struct net *net, struct mr_table *mrt) { struct net_device *dev; struct in_device *in_dev; + char name[IFNAMSIZ]; + + if (mrt->id == RT_TABLE_DEFAULT) + sprintf(name, "pimreg"); + else + sprintf(name, "pimreg%u", mrt->id); - dev = alloc_netdev(0, "pimreg", reg_vif_setup); + dev = alloc_netdev(0, name, reg_vif_setup); if (dev == NULL) return NULL; @@ -461,7 +689,7 @@ static int vif_add(struct net *net, struct mr_table *mrt, */ if (mrt->mroute_reg_vif_num >= 0) return -EADDRINUSE; - dev = ipmr_reg_vif(net); + dev = ipmr_reg_vif(net, mrt); if (!dev) return -ENOBUFS; err = dev_set_allmulti(dev, 1); @@ -928,17 +1156,19 @@ static void mroute_clean_tables(struct mr_table *mrt) static void mrtsock_destruct(struct sock *sk) { struct net *net = sock_net(sk); - struct mr_table *mrt = net->ipv4.mrt; + struct mr_table *mrt; rtnl_lock(); - if (sk == mrt->mroute_sk) { - IPV4_DEVCONF_ALL(net, MC_FORWARDING)--; + ipmr_for_each_table(mrt, net) { + if (sk == mrt->mroute_sk) { + IPV4_DEVCONF_ALL(net, MC_FORWARDING)--; - write_lock_bh(&mrt_lock); - mrt->mroute_sk = NULL; - write_unlock_bh(&mrt_lock); + write_lock_bh(&mrt_lock); + mrt->mroute_sk = NULL; + write_unlock_bh(&mrt_lock); - mroute_clean_tables(mrt); + mroute_clean_tables(mrt); + } } rtnl_unlock(); } @@ -956,7 +1186,11 @@ int ip_mroute_setsockopt(struct sock *sk, int optname, char __user *optval, unsi struct vifctl vif; struct mfcctl mfc; struct net *net = sock_net(sk); - struct mr_table *mrt = net->ipv4.mrt; + struct mr_table *mrt; + + mrt = ipmr_get_table(net, raw_sk(sk)->ipmr_table ? : RT_TABLE_DEFAULT); + if (mrt == NULL) + return -ENOENT; if (optname != MRT_INIT) { if (sk != mrt->mroute_sk && !capable(CAP_NET_ADMIN)) @@ -1054,6 +1288,27 @@ int ip_mroute_setsockopt(struct sock *sk, int optname, char __user *optval, unsi rtnl_unlock(); return ret; } +#endif +#ifdef CONFIG_IP_MROUTE_MULTIPLE_TABLES + case MRT_TABLE: + { + u32 v; + + if (optlen != sizeof(u32)) + return -EINVAL; + if (get_user(v, (u32 __user *)optval)) + return -EFAULT; + if (sk == mrt->mroute_sk) + return -EBUSY; + + rtnl_lock(); + ret = 0; + if (!ipmr_new_table(net, v)) + ret = -ENOMEM; + raw_sk(sk)->ipmr_table = v; + rtnl_unlock(); + return ret; + } #endif /* * Spurious command, or MRT_VERSION which you cannot @@ -1073,7 +1328,11 @@ int ip_mroute_getsockopt(struct sock *sk, int optname, char __user *optval, int int olr; int val; struct net *net = sock_net(sk); - struct mr_table *mrt = net->ipv4.mrt; + struct mr_table *mrt; + + mrt = ipmr_get_table(net, raw_sk(sk)->ipmr_table ? : RT_TABLE_DEFAULT); + if (mrt == NULL) + return -ENOENT; if (optname != MRT_VERSION && #ifdef CONFIG_IP_PIMSM @@ -1115,7 +1374,11 @@ int ipmr_ioctl(struct sock *sk, int cmd, void __user *arg) struct vif_device *vif; struct mfc_cache *c; struct net *net = sock_net(sk); - struct mr_table *mrt = net->ipv4.mrt; + struct mr_table *mrt; + + mrt = ipmr_get_table(net, raw_sk(sk)->ipmr_table ? : RT_TABLE_DEFAULT); + if (mrt == NULL) + return -ENOENT; switch (cmd) { case SIOCGETVIFCNT: @@ -1166,17 +1429,20 @@ static int ipmr_device_event(struct notifier_block *this, unsigned long event, v { struct net_device *dev = ptr; struct net *net = dev_net(dev); - struct mr_table *mrt = net->ipv4.mrt; + struct mr_table *mrt; struct vif_device *v; int ct; LIST_HEAD(list); if (event != NETDEV_UNREGISTER) return NOTIFY_DONE; - v = &mrt->vif_table[0]; - for (ct = 0; ct < mrt->maxvif; ct++, v++) { - if (v->dev == dev) - vif_delete(mrt, ct, 1, &list); + + ipmr_for_each_table(mrt, net) { + v = &mrt->vif_table[0]; + for (ct = 0; ct < mrt->maxvif; ct++, v++) { + if (v->dev == dev) + vif_delete(mrt, ct, 1, &list); + } } unregister_netdevice_many(&list); return NOTIFY_DONE; @@ -1443,8 +1709,9 @@ int ip_mr_input(struct sk_buff *skb) { struct mfc_cache *cache; struct net *net = dev_net(skb->dev); - struct mr_table *mrt = net->ipv4.mrt; int local = skb_rtable(skb)->rt_flags & RTCF_LOCAL; + struct mr_table *mrt; + int err; /* Packet is looped back after forward, it should not be forwarded second time, but still can be delivered locally. @@ -1452,6 +1719,10 @@ int ip_mr_input(struct sk_buff *skb) if (IPCB(skb)->flags&IPSKB_FORWARDED) goto dont_forward; + err = ipmr_fib_lookup(net, &skb_rtable(skb)->fl, &mrt); + if (err < 0) + return err; + if (!local) { if (IPCB(skb)->opt.router_alert) { if (ip_call_ra_chain(skb)) @@ -1522,12 +1793,11 @@ dont_forward: } #ifdef CONFIG_IP_PIMSM -static int __pim_rcv(struct sk_buff *skb, unsigned int pimlen) +static int __pim_rcv(struct mr_table *mrt, struct sk_buff *skb, + unsigned int pimlen) { struct net_device *reg_dev = NULL; struct iphdr *encap; - struct net *net = dev_net(skb->dev); - struct mr_table *mrt = net->ipv4.mrt; encap = (struct iphdr *)(skb_transport_header(skb) + pimlen); /* @@ -1578,18 +1848,21 @@ int pim_rcv_v1(struct sk_buff * skb) { struct igmphdr *pim; struct net *net = dev_net(skb->dev); - struct mr_table *mrt = net->ipv4.mrt; + struct mr_table *mrt; if (!pskb_may_pull(skb, sizeof(*pim) + sizeof(struct iphdr))) goto drop; pim = igmp_hdr(skb); + if (ipmr_fib_lookup(net, &skb_rtable(skb)->fl, &mrt) < 0) + goto drop; + if (!mrt->mroute_do_pim || pim->group != PIM_V1_VERSION || pim->code != PIM_V1_REGISTER) goto drop; - if (__pim_rcv(skb, sizeof(*pim))) { + if (__pim_rcv(mrt, skb, sizeof(*pim))) { drop: kfree_skb(skb); } @@ -1601,6 +1874,8 @@ drop: static int pim_rcv(struct sk_buff * skb) { struct pimreghdr *pim; + struct net *net = dev_net(skb->dev); + struct mr_table *mrt; if (!pskb_may_pull(skb, sizeof(*pim) + sizeof(struct iphdr))) goto drop; @@ -1612,7 +1887,10 @@ static int pim_rcv(struct sk_buff * skb) csum_fold(skb_checksum(skb, 0, skb->len, 0)))) goto drop; - if (__pim_rcv(skb, sizeof(*pim))) { + if (ipmr_fib_lookup(net, &skb_rtable(skb)->fl, &mrt) < 0) + goto drop; + + if (__pim_rcv(mrt, skb, sizeof(*pim))) { drop: kfree_skb(skb); } @@ -1663,10 +1941,14 @@ int ipmr_get_route(struct net *net, struct sk_buff *skb, struct rtmsg *rtm, int nowait) { int err; - struct mr_table *mrt = net->ipv4.mrt; + struct mr_table *mrt; struct mfc_cache *cache; struct rtable *rt = skb_rtable(skb); + mrt = ipmr_get_table(net, RT_TABLE_DEFAULT); + if (mrt == NULL) + return -ENOENT; + read_lock(&mrt_lock); cache = ipmr_cache_find(mrt, rt->rt_src, rt->rt_dst); @@ -1717,6 +1999,7 @@ int ipmr_get_route(struct net *net, */ struct ipmr_vif_iter { struct seq_net_private p; + struct mr_table *mrt; int ct; }; @@ -1724,7 +2007,7 @@ static struct vif_device *ipmr_vif_seq_idx(struct net *net, struct ipmr_vif_iter *iter, loff_t pos) { - struct mr_table *mrt = net->ipv4.mrt; + struct mr_table *mrt = iter->mrt; for (iter->ct = 0; iter->ct < mrt->maxvif; ++iter->ct) { if (!VIF_EXISTS(mrt, iter->ct)) @@ -1738,7 +2021,15 @@ static struct vif_device *ipmr_vif_seq_idx(struct net *net, static void *ipmr_vif_seq_start(struct seq_file *seq, loff_t *pos) __acquires(mrt_lock) { + struct ipmr_vif_iter *iter = seq->private; struct net *net = seq_file_net(seq); + struct mr_table *mrt; + + mrt = ipmr_get_table(net, RT_TABLE_DEFAULT); + if (mrt == NULL) + return ERR_PTR(-ENOENT); + + iter->mrt = mrt; read_lock(&mrt_lock); return *pos ? ipmr_vif_seq_idx(net, seq->private, *pos - 1) @@ -1749,7 +2040,7 @@ static void *ipmr_vif_seq_next(struct seq_file *seq, void *v, loff_t *pos) { struct ipmr_vif_iter *iter = seq->private; struct net *net = seq_file_net(seq); - struct mr_table *mrt = net->ipv4.mrt; + struct mr_table *mrt = iter->mrt; ++*pos; if (v == SEQ_START_TOKEN) @@ -1771,8 +2062,8 @@ static void ipmr_vif_seq_stop(struct seq_file *seq, void *v) static int ipmr_vif_seq_show(struct seq_file *seq, void *v) { - struct net *net = seq_file_net(seq); - struct mr_table *mrt = net->ipv4.mrt; + struct ipmr_vif_iter *iter = seq->private; + struct mr_table *mrt = iter->mrt; if (v == SEQ_START_TOKEN) { seq_puts(seq, @@ -1814,6 +2105,7 @@ static const struct file_operations ipmr_vif_fops = { struct ipmr_mfc_iter { struct seq_net_private p; + struct mr_table *mrt; struct list_head *cache; int ct; }; @@ -1822,7 +2114,7 @@ struct ipmr_mfc_iter { static struct mfc_cache *ipmr_mfc_seq_idx(struct net *net, struct ipmr_mfc_iter *it, loff_t pos) { - struct mr_table *mrt = net->ipv4.mrt; + struct mr_table *mrt = it->mrt; struct mfc_cache *mfc; read_lock(&mrt_lock); @@ -1850,7 +2142,13 @@ static void *ipmr_mfc_seq_start(struct seq_file *seq, loff_t *pos) { struct ipmr_mfc_iter *it = seq->private; struct net *net = seq_file_net(seq); + struct mr_table *mrt; + + mrt = ipmr_get_table(net, RT_TABLE_DEFAULT); + if (mrt == NULL) + return ERR_PTR(-ENOENT); + it->mrt = mrt; it->cache = NULL; it->ct = 0; return *pos ? ipmr_mfc_seq_idx(net, seq->private, *pos - 1) @@ -1862,7 +2160,7 @@ static void *ipmr_mfc_seq_next(struct seq_file *seq, void *v, loff_t *pos) struct mfc_cache *mfc = v; struct ipmr_mfc_iter *it = seq->private; struct net *net = seq_file_net(seq); - struct mr_table *mrt = net->ipv4.mrt; + struct mr_table *mrt = it->mrt; ++*pos; @@ -1903,8 +2201,7 @@ static void *ipmr_mfc_seq_next(struct seq_file *seq, void *v, loff_t *pos) static void ipmr_mfc_seq_stop(struct seq_file *seq, void *v) { struct ipmr_mfc_iter *it = seq->private; - struct net *net = seq_file_net(seq); - struct mr_table *mrt = net->ipv4.mrt; + struct mr_table *mrt = it->mrt; if (it->cache == &mrt->mfc_unres_queue) spin_unlock_bh(&mfc_unres_lock); @@ -1915,8 +2212,6 @@ static void ipmr_mfc_seq_stop(struct seq_file *seq, void *v) static int ipmr_mfc_seq_show(struct seq_file *seq, void *v) { int n; - struct net *net = seq_file_net(seq); - struct mr_table *mrt = net->ipv4.mrt; if (v == SEQ_START_TOKEN) { seq_puts(seq, @@ -1924,6 +2219,7 @@ static int ipmr_mfc_seq_show(struct seq_file *seq, void *v) } else { const struct mfc_cache *mfc = v; const struct ipmr_mfc_iter *it = seq->private; + const struct mr_table *mrt = it->mrt; seq_printf(seq, "%08lX %08lX %-3hd", (unsigned long) mfc->mfc_mcastgrp, @@ -1989,28 +2285,11 @@ static const struct net_protocol pim_protocol = { */ static int __net_init ipmr_net_init(struct net *net) { - struct mr_table *mrt; - unsigned int i; - int err = 0; + int err; - mrt = kzalloc(sizeof(*mrt), GFP_KERNEL); - if (mrt == NULL) { - err = -ENOMEM; + err = ipmr_rules_init(net); + if (err < 0) goto fail; - } - - /* Forwarding cache */ - for (i = 0; i < MFC_LINES; i++) - INIT_LIST_HEAD(&mrt->mfc_cache_array[i]); - - INIT_LIST_HEAD(&mrt->mfc_unres_queue); - - setup_timer(&mrt->ipmr_expire_timer, ipmr_expire_process, - (unsigned long)net); - -#ifdef CONFIG_IP_PIMSM - mrt->mroute_reg_vif_num = -1; -#endif #ifdef CONFIG_PROC_FS err = -ENOMEM; @@ -2019,15 +2298,13 @@ static int __net_init ipmr_net_init(struct net *net) if (!proc_net_fops_create(net, "ip_mr_cache", 0, &ipmr_mfc_fops)) goto proc_cache_fail; #endif - - net->ipv4.mrt = mrt; return 0; #ifdef CONFIG_PROC_FS proc_cache_fail: proc_net_remove(net, "ip_mr_vif"); proc_vif_fail: - kfree(mrt); + ipmr_rules_exit(net); #endif fail: return err; @@ -2039,7 +2316,7 @@ static void __net_exit ipmr_net_exit(struct net *net) proc_net_remove(net, "ip_mr_cache"); proc_net_remove(net, "ip_mr_vif"); #endif - kfree(net->ipv4.mrt); + ipmr_rules_exit(net); } static struct pernet_operations ipmr_net_ops = { -- cgit v1.2.3 From e326e8503dfc73e4f79d488a78ee3d7cde10081f Mon Sep 17 00:00:00 2001 From: Giuseppe CAVALLARO Date: Tue, 13 Apr 2010 20:21:14 +0000 Subject: stmmac: new descriptor field for the driver's platform The new enh_desc is used for selecting the enhanced descriptors structure. There are several scenarios; some chips (mac10/100 or gmac) want to use the enhanced descriptors; others want the normal ones. For example, on ST platforms: MAC10/100 uses the normal desc structure and the GMAC uses the enhanced one. It can be useful to get this information from the platform. This could also be decided at run-time looking at the chip's ID number; but it could happen that chips with the same ID want to use different descriptor structure. Signed-off-by: Giuseppe Cavallaro Signed-off-by: David S. Miller --- include/linux/stmmac.h | 1 + 1 file changed, 1 insertion(+) (limited to 'include/linux') diff --git a/include/linux/stmmac.h b/include/linux/stmmac.h index 32bfd1a8a48..632ff7c0328 100644 --- a/include/linux/stmmac.h +++ b/include/linux/stmmac.h @@ -33,6 +33,7 @@ struct plat_stmmacenet_data { int bus_id; int pbl; int has_gmac; + int enh_desc; void (*fix_mac_speed)(void *priv, unsigned int speed); void (*bus_setup)(unsigned long ioaddr); #ifdef CONFIG_STM_DRIVERS -- cgit v1.2.3 From fd793d8905720595caede6bd26c5df6c0ecd37f8 Mon Sep 17 00:00:00 2001 From: Changli Gao Date: Thu, 15 Apr 2010 00:16:59 -0700 Subject: net: CONFIG_SMP should be CONFIG_RPS Signed-off-by: Changli Gao Signed-off-by: David S. Miller --- include/linux/netdevice.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include/linux') diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index 470f7c951af..55c2086e1f0 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -1331,7 +1331,7 @@ struct softnet_data { struct sk_buff *completion_queue; /* Elements below can be accessed between CPUs for RPS */ -#ifdef CONFIG_SMP +#ifdef CONFIG_RPS struct call_single_data csd ____cacheline_aligned_in_smp; #endif struct sk_buff_head input_pkt_queue; -- cgit v1.2.3 From ea2d9b41bd418894d1ee25de1642c3325d71c397 Mon Sep 17 00:00:00 2001 From: Bart De Schuymer Date: Thu, 15 Apr 2010 12:14:51 +0200 Subject: netfilter: bridge-netfilter: simplify IP DNAT Remove br_netfilter.c::br_nf_local_out(). The function br_nf_local_out() was needed because the PF_BRIDGE::LOCAL_OUT hook could be called when IP DNAT happens on to-be-bridged traffic. The new scheme eliminates this mess. Signed-off-by: Bart De Schuymer Signed-off-by: Patrick McHardy --- include/linux/netfilter_bridge.h | 17 ++++-- net/bridge/br_device.c | 9 +++- net/bridge/br_netfilter.c | 114 +++++++-------------------------------- 3 files changed, 40 insertions(+), 100 deletions(-) (limited to 'include/linux') diff --git a/include/linux/netfilter_bridge.h b/include/linux/netfilter_bridge.h index f8105e54716..ffab6c423a5 100644 --- a/include/linux/netfilter_bridge.h +++ b/include/linux/netfilter_bridge.h @@ -41,9 +41,8 @@ enum nf_br_hook_priorities { #define BRNF_PKT_TYPE 0x01 #define BRNF_BRIDGED_DNAT 0x02 -#define BRNF_DONT_TAKE_PARENT 0x04 -#define BRNF_BRIDGED 0x08 -#define BRNF_NF_BRIDGE_PREROUTING 0x10 +#define BRNF_BRIDGED 0x04 +#define BRNF_NF_BRIDGE_PREROUTING 0x08 /* Only used in br_forward.c */ @@ -68,6 +67,18 @@ static inline unsigned int nf_bridge_encap_header_len(const struct sk_buff *skb) } } +extern int br_handle_frame_finish(struct sk_buff *skb); +/* Only used in br_device.c */ +static inline int br_nf_pre_routing_finish_bridge_slow(struct sk_buff *skb) +{ + struct nf_bridge_info *nf_bridge = skb->nf_bridge; + + skb_pull(skb, ETH_HLEN); + nf_bridge->mask ^= BRNF_BRIDGED_DNAT; + skb->dev = nf_bridge->physindev; + return br_handle_frame_finish(skb); +} + /* This is called by the IP fragmenting code and it ensures there is * enough room for the encapsulating header (if there is one). */ static inline unsigned int nf_bridge_pad(const struct sk_buff *skb) diff --git a/net/bridge/br_device.c b/net/bridge/br_device.c index 5b8a6e73b02..007bde87415 100644 --- a/net/bridge/br_device.c +++ b/net/bridge/br_device.c @@ -15,7 +15,7 @@ #include #include #include - +#include #include #include "br_private.h" @@ -28,6 +28,13 @@ netdev_tx_t br_dev_xmit(struct sk_buff *skb, struct net_device *dev) struct net_bridge_mdb_entry *mdst; struct br_cpu_netstats *brstats = this_cpu_ptr(br->stats); +#ifdef CONFIG_BRIDGE_NETFILTER + if (skb->nf_bridge && (skb->nf_bridge->mask & BRNF_BRIDGED_DNAT)) { + br_nf_pre_routing_finish_bridge_slow(skb); + return NETDEV_TX_OK; + } +#endif + brstats->tx_packets++; brstats->tx_bytes += skb->len; diff --git a/net/bridge/br_netfilter.c b/net/bridge/br_netfilter.c index dd6f538ba0b..05dc6304992 100644 --- a/net/bridge/br_netfilter.c +++ b/net/bridge/br_netfilter.c @@ -246,8 +246,7 @@ static int br_nf_pre_routing_finish_ipv6(struct sk_buff *skb) } /* This requires some explaining. If DNAT has taken place, - * we will need to fix up the destination Ethernet address, - * and this is a tricky process. + * we will need to fix up the destination Ethernet address. * * There are two cases to consider: * 1. The packet was DNAT'ed to a device in the same bridge @@ -261,52 +260,38 @@ static int br_nf_pre_routing_finish_ipv6(struct sk_buff *skb) * call ip_route_input() and to look at skb->dst->dev, which is * changed to the destination device if ip_route_input() succeeds. * - * Let us first consider the case that ip_route_input() succeeds: - * - * If skb->dst->dev equals the logical bridge device the packet - * came in on, we can consider this bridging. The packet is passed - * through the neighbour output function to build a new destination - * MAC address, which will make the packet enter br_nf_local_out() - * not much later. In that function it is assured that the iptables - * FORWARD chain is traversed for the packet. + * Let's first consider the case that ip_route_input() succeeds: * + * If the output device equals the logical bridge device the packet + * came in on, we can consider this bridging. The corresponding MAC + * address will be obtained in br_nf_pre_routing_finish_bridge. * Otherwise, the packet is considered to be routed and we just * change the destination MAC address so that the packet will * later be passed up to the IP stack to be routed. For a redirected * packet, ip_route_input() will give back the localhost as output device, * which differs from the bridge device. * - * Let us now consider the case that ip_route_input() fails: + * Let's now consider the case that ip_route_input() fails: * * This can be because the destination address is martian, in which case * the packet will be dropped. - * After a "echo '0' > /proc/sys/net/ipv4/ip_forward" ip_route_input() - * will fail, while __ip_route_output_key() will return success. The source - * address for __ip_route_output_key() is set to zero, so __ip_route_output_key + * If IP forwarding is disabled, ip_route_input() will fail, while + * ip_route_output_key() can return success. The source + * address for ip_route_output_key() is set to zero, so ip_route_output_key() * thinks we're handling a locally generated packet and won't care - * if IP forwarding is allowed. We send a warning message to the users's - * log telling her to put IP forwarding on. - * - * ip_route_input() will also fail if there is no route available. - * In that case we just drop the packet. - * - * --Lennert, 20020411 - * --Bart, 20020416 (updated) - * --Bart, 20021007 (updated) - * --Bart, 20062711 (updated) */ + * if IP forwarding is enabled. If the output device equals the logical bridge + * device, we proceed as if ip_route_input() succeeded. If it differs from the + * logical bridge port or if ip_route_output_key() fails we drop the packet. + */ + static int br_nf_pre_routing_finish_bridge(struct sk_buff *skb) { - if (skb->pkt_type == PACKET_OTHERHOST) { - skb->pkt_type = PACKET_HOST; - skb->nf_bridge->mask |= BRNF_PKT_TYPE; - } - skb->nf_bridge->mask ^= BRNF_NF_BRIDGE_PREROUTING; - skb->dev = bridge_parent(skb->dev); if (skb->dev) { struct dst_entry *dst = skb_dst(skb); nf_bridge_pull_encap_header(skb); + skb->nf_bridge->mask |= BRNF_BRIDGED_DNAT; if (dst->hh) return neigh_hh_output(dst->hh, skb); @@ -368,9 +353,6 @@ free_skb: } else { if (skb_dst(skb)->dev == dev) { bridged_dnat: - /* Tell br_nf_local_out this is a - * bridged frame */ - nf_bridge->mask |= BRNF_BRIDGED_DNAT; skb->dev = nf_bridge->physindev; nf_bridge_push_encap_header(skb); NF_HOOK_THRESH(NFPROTO_BRIDGE, @@ -721,54 +703,6 @@ static unsigned int br_nf_forward_arp(unsigned int hook, struct sk_buff *skb, return NF_STOLEN; } -/* PF_BRIDGE/LOCAL_OUT *********************************************** - * - * This function sees both locally originated IP packets and forwarded - * IP packets (in both cases the destination device is a bridge - * device). It also sees bridged-and-DNAT'ed packets. - * - * If (nf_bridge->mask & BRNF_BRIDGED_DNAT) then the packet is bridged - * and we fake the PF_BRIDGE/FORWARD hook. The function br_nf_forward() - * will then fake the PF_INET/FORWARD hook. br_nf_local_out() has priority - * NF_BR_PRI_FIRST, so no relevant PF_BRIDGE/INPUT functions have been nor - * will be executed. - */ -static unsigned int br_nf_local_out(unsigned int hook, struct sk_buff *skb, - const struct net_device *in, - const struct net_device *out, - int (*okfn)(struct sk_buff *)) -{ - struct net_device *realindev; - struct nf_bridge_info *nf_bridge; - - if (!skb->nf_bridge) - return NF_ACCEPT; - - /* Need exclusive nf_bridge_info since we might have multiple - * different physoutdevs. */ - if (!nf_bridge_unshare(skb)) - return NF_DROP; - - nf_bridge = skb->nf_bridge; - if (!(nf_bridge->mask & BRNF_BRIDGED_DNAT)) - return NF_ACCEPT; - - /* Bridged, take PF_BRIDGE/FORWARD. - * (see big note in front of br_nf_pre_routing_finish) */ - nf_bridge->physoutdev = skb->dev; - realindev = nf_bridge->physindev; - - if (nf_bridge->mask & BRNF_PKT_TYPE) { - skb->pkt_type = PACKET_OTHERHOST; - nf_bridge->mask ^= BRNF_PKT_TYPE; - } - nf_bridge_push_encap_header(skb); - - NF_HOOK(NFPROTO_BRIDGE, NF_BR_FORWARD, skb, realindev, skb->dev, - br_forward_finish); - return NF_STOLEN; -} - #if defined(CONFIG_NF_CONNTRACK_IPV4) || defined(CONFIG_NF_CONNTRACK_IPV4_MODULE) static int br_nf_dev_queue_xmit(struct sk_buff *skb) { @@ -797,10 +731,7 @@ static unsigned int br_nf_post_routing(unsigned int hook, struct sk_buff *skb, struct net_device *realoutdev = bridge_parent(skb->dev); u_int8_t pf; - if (!nf_bridge) - return NF_ACCEPT; - - if (!(nf_bridge->mask & (BRNF_BRIDGED | BRNF_BRIDGED_DNAT))) + if (!nf_bridge || !(nf_bridge->mask & BRNF_BRIDGED)) return NF_ACCEPT; if (!realoutdev) @@ -847,10 +778,8 @@ static unsigned int ip_sabotage_in(unsigned int hook, struct sk_buff *skb, return NF_ACCEPT; } -/* For br_nf_local_out we need (prio = NF_BR_PRI_FIRST), to insure that innocent - * PF_BRIDGE/NF_BR_LOCAL_OUT functions don't get bridged traffic as input. - * For br_nf_post_routing, we need (prio = NF_BR_PRI_LAST), because - * ip_refrag() can return NF_STOLEN. */ +/* For br_nf_post_routing, we need (prio = NF_BR_PRI_LAST), because + * br_dev_queue_push_xmit is called afterwards */ static struct nf_hook_ops br_nf_ops[] __read_mostly = { { .hook = br_nf_pre_routing, @@ -880,13 +809,6 @@ static struct nf_hook_ops br_nf_ops[] __read_mostly = { .hooknum = NF_BR_FORWARD, .priority = NF_BR_PRI_BRNF, }, - { - .hook = br_nf_local_out, - .owner = THIS_MODULE, - .pf = PF_BRIDGE, - .hooknum = NF_BR_LOCAL_OUT, - .priority = NF_BR_PRI_FIRST, - }, { .hook = br_nf_post_routing, .owner = THIS_MODULE, -- cgit v1.2.3 From e179e6322ac334e21a3c6d669d95bc967e5d0a80 Mon Sep 17 00:00:00 2001 From: Bart De Schuymer Date: Thu, 15 Apr 2010 12:26:39 +0200 Subject: netfilter: bridge-netfilter: Fix MAC header handling with IP DNAT - fix IP DNAT on vlan- or pppoe-encapsulated traffic: The functions neigh_hh_output() or dst->neighbour->output() overwrite the complete Ethernet header, although we only need the destination MAC address. For encapsulated packets, they ended up overwriting the encapsulating header. The new code copies the Ethernet source MAC address and protocol number before calling dst->neighbour->output(). The Ethernet source MAC and protocol number are copied back in place in br_nf_pre_routing_finish_bridge_slow(). This also makes the IP DNAT more transparent because in the old scheme the source MAC of the bridge was copied into the source address in the Ethernet header. We also let skb->protocol equal ETH_P_IP resp. ETH_P_IPV6 during the execution of the PF_INET resp. PF_INET6 hooks. - Speed up IP DNAT by calling neigh_hh_bridge() instead of neigh_hh_output(): if dst->hh is available, we already know the MAC address so we can just copy it. Signed-off-by: Bart De Schuymer Signed-off-by: Patrick McHardy --- include/linux/netfilter_bridge.h | 5 ++- include/net/neighbour.h | 14 +++++++ net/bridge/br_netfilter.c | 90 +++++++++++++++++++++++++++++----------- 3 files changed, 83 insertions(+), 26 deletions(-) (limited to 'include/linux') diff --git a/include/linux/netfilter_bridge.h b/include/linux/netfilter_bridge.h index ffab6c423a5..ea0e44b9043 100644 --- a/include/linux/netfilter_bridge.h +++ b/include/linux/netfilter_bridge.h @@ -43,7 +43,8 @@ enum nf_br_hook_priorities { #define BRNF_BRIDGED_DNAT 0x02 #define BRNF_BRIDGED 0x04 #define BRNF_NF_BRIDGE_PREROUTING 0x08 - +#define BRNF_8021Q 0x10 +#define BRNF_PPPoE 0x20 /* Only used in br_forward.c */ extern int nf_bridge_copy_header(struct sk_buff *skb); @@ -75,6 +76,8 @@ static inline int br_nf_pre_routing_finish_bridge_slow(struct sk_buff *skb) skb_pull(skb, ETH_HLEN); nf_bridge->mask ^= BRNF_BRIDGED_DNAT; + skb_copy_to_linear_data_offset(skb, -(ETH_HLEN-ETH_ALEN), + skb->nf_bridge->data, ETH_HLEN-ETH_ALEN); skb->dev = nf_bridge->physindev; return br_handle_frame_finish(skb); } diff --git a/include/net/neighbour.h b/include/net/neighbour.h index da1d58be31b..eb21340a573 100644 --- a/include/net/neighbour.h +++ b/include/net/neighbour.h @@ -299,6 +299,20 @@ static inline int neigh_event_send(struct neighbour *neigh, struct sk_buff *skb) return 0; } +#ifdef CONFIG_BRIDGE_NETFILTER +static inline int neigh_hh_bridge(struct hh_cache *hh, struct sk_buff *skb) +{ + unsigned seq, hh_alen; + + do { + seq = read_seqbegin(&hh->hh_lock); + hh_alen = HH_DATA_ALIGN(ETH_HLEN); + memcpy(skb->data - hh_alen, hh->hh_data, ETH_ALEN + hh_alen - ETH_HLEN); + } while (read_seqretry(&hh->hh_lock, seq)); + return 0; +} +#endif + static inline int neigh_hh_output(struct hh_cache *hh, struct sk_buff *skb) { unsigned seq; diff --git a/net/bridge/br_netfilter.c b/net/bridge/br_netfilter.c index 05dc6304992..b7e405dc9d1 100644 --- a/net/bridge/br_netfilter.c +++ b/net/bridge/br_netfilter.c @@ -196,15 +196,24 @@ static inline void nf_bridge_save_header(struct sk_buff *skb) skb->nf_bridge->data, header_size); } -/* - * When forwarding bridge frames, we save a copy of the original - * header before processing. +static inline void nf_bridge_update_protocol(struct sk_buff *skb) +{ + if (skb->nf_bridge->mask & BRNF_8021Q) + skb->protocol = htons(ETH_P_8021Q); + else if (skb->nf_bridge->mask & BRNF_PPPoE) + skb->protocol = htons(ETH_P_PPP_SES); +} + +/* Fill in the header for fragmented IP packets handled by + * the IPv4 connection tracking code. */ int nf_bridge_copy_header(struct sk_buff *skb) { int err; - int header_size = ETH_HLEN + nf_bridge_encap_header_len(skb); + unsigned int header_size; + nf_bridge_update_protocol(skb); + header_size = ETH_HLEN + nf_bridge_encap_header_len(skb); err = skb_cow_head(skb, header_size); if (err) return err; @@ -238,6 +247,7 @@ static int br_nf_pre_routing_finish_ipv6(struct sk_buff *skb) skb_dst_set(skb, &rt->u.dst); skb->dev = nf_bridge->physindev; + nf_bridge_update_protocol(skb); nf_bridge_push_encap_header(skb); NF_HOOK_THRESH(NFPROTO_BRIDGE, NF_BR_PRE_ROUTING, skb, skb->dev, NULL, br_handle_frame_finish, 1); @@ -245,6 +255,38 @@ static int br_nf_pre_routing_finish_ipv6(struct sk_buff *skb) return 0; } +/* Obtain the correct destination MAC address, while preserving the original + * source MAC address. If we already know this address, we just copy it. If we + * don't, we use the neighbour framework to find out. In both cases, we make + * sure that br_handle_frame_finish() is called afterwards. + */ +static int br_nf_pre_routing_finish_bridge(struct sk_buff *skb) +{ + struct nf_bridge_info *nf_bridge = skb->nf_bridge; + struct dst_entry *dst; + + skb->dev = bridge_parent(skb->dev); + if (!skb->dev) + goto free_skb; + dst = skb_dst(skb); + if (dst->hh) { + neigh_hh_bridge(dst->hh, skb); + skb->dev = nf_bridge->physindev; + return br_handle_frame_finish(skb); + } else if (dst->neighbour) { + /* the neighbour function below overwrites the complete + * MAC header, so we save the Ethernet source address and + * protocol number. */ + skb_copy_from_linear_data_offset(skb, -(ETH_HLEN-ETH_ALEN), skb->nf_bridge->data, ETH_HLEN-ETH_ALEN); + /* tell br_dev_xmit to continue with forwarding */ + nf_bridge->mask |= BRNF_BRIDGED_DNAT; + return dst->neighbour->output(skb); + } +free_skb: + kfree_skb(skb); + return 0; +} + /* This requires some explaining. If DNAT has taken place, * we will need to fix up the destination Ethernet address. * @@ -283,25 +325,6 @@ static int br_nf_pre_routing_finish_ipv6(struct sk_buff *skb) * device, we proceed as if ip_route_input() succeeded. If it differs from the * logical bridge port or if ip_route_output_key() fails we drop the packet. */ - -static int br_nf_pre_routing_finish_bridge(struct sk_buff *skb) -{ - skb->dev = bridge_parent(skb->dev); - if (skb->dev) { - struct dst_entry *dst = skb_dst(skb); - - nf_bridge_pull_encap_header(skb); - skb->nf_bridge->mask |= BRNF_BRIDGED_DNAT; - - if (dst->hh) - return neigh_hh_output(dst->hh, skb); - else if (dst->neighbour) - return dst->neighbour->output(skb); - } - kfree_skb(skb); - return 0; -} - static int br_nf_pre_routing_finish(struct sk_buff *skb) { struct net_device *dev = skb->dev; @@ -354,6 +377,7 @@ free_skb: if (skb_dst(skb)->dev == dev) { bridged_dnat: skb->dev = nf_bridge->physindev; + nf_bridge_update_protocol(skb); nf_bridge_push_encap_header(skb); NF_HOOK_THRESH(NFPROTO_BRIDGE, NF_BR_PRE_ROUTING, @@ -376,6 +400,7 @@ bridged_dnat: } skb->dev = nf_bridge->physindev; + nf_bridge_update_protocol(skb); nf_bridge_push_encap_header(skb); NF_HOOK_THRESH(NFPROTO_BRIDGE, NF_BR_PRE_ROUTING, skb, skb->dev, NULL, br_handle_frame_finish, 1); @@ -396,6 +421,10 @@ static struct net_device *setup_pre_routing(struct sk_buff *skb) nf_bridge->mask |= BRNF_NF_BRIDGE_PREROUTING; nf_bridge->physindev = skb->dev; skb->dev = bridge_parent(skb->dev); + if (skb->protocol == htons(ETH_P_8021Q)) + nf_bridge->mask |= BRNF_8021Q; + else if (skb->protocol == htons(ETH_P_PPP_SES)) + nf_bridge->mask |= BRNF_PPPoE; return skb->dev; } @@ -494,6 +523,7 @@ static unsigned int br_nf_pre_routing_ipv6(unsigned int hook, if (!setup_pre_routing(skb)) return NF_DROP; + skb->protocol = htons(ETH_P_IPV6); NF_HOOK(NFPROTO_IPV6, NF_INET_PRE_ROUTING, skb, skb->dev, NULL, br_nf_pre_routing_finish_ipv6); @@ -566,6 +596,7 @@ static unsigned int br_nf_pre_routing(unsigned int hook, struct sk_buff *skb, if (!setup_pre_routing(skb)) return NF_DROP; store_orig_dstaddr(skb); + skb->protocol = htons(ETH_P_IP); NF_HOOK(NFPROTO_IPV4, NF_INET_PRE_ROUTING, skb, skb->dev, NULL, br_nf_pre_routing_finish); @@ -614,7 +645,9 @@ static int br_nf_forward_finish(struct sk_buff *skb) } else { in = *((struct net_device **)(skb->cb)); } + nf_bridge_update_protocol(skb); nf_bridge_push_encap_header(skb); + NF_HOOK_THRESH(NFPROTO_BRIDGE, NF_BR_FORWARD, skb, in, skb->dev, br_forward_finish, 1); return 0; @@ -666,6 +699,10 @@ static unsigned int br_nf_forward_ip(unsigned int hook, struct sk_buff *skb, /* The physdev module checks on this */ nf_bridge->mask |= BRNF_BRIDGED; nf_bridge->physoutdev = skb->dev; + if (pf == PF_INET) + skb->protocol = htons(ETH_P_IP); + else + skb->protocol = htons(ETH_P_IPV6); NF_HOOK(pf, NF_INET_FORWARD, skb, bridge_parent(in), parent, br_nf_forward_finish); @@ -706,8 +743,7 @@ static unsigned int br_nf_forward_arp(unsigned int hook, struct sk_buff *skb, #if defined(CONFIG_NF_CONNTRACK_IPV4) || defined(CONFIG_NF_CONNTRACK_IPV4_MODULE) static int br_nf_dev_queue_xmit(struct sk_buff *skb) { - if (skb->nfct != NULL && - (skb->protocol == htons(ETH_P_IP) || IS_VLAN_IP(skb)) && + if (skb->nfct != NULL && skb->protocol == htons(ETH_P_IP) && skb->len > skb->dev->mtu && !skb_is_gso(skb)) return ip_fragment(skb, br_dev_queue_push_xmit); @@ -755,6 +791,10 @@ static unsigned int br_nf_post_routing(unsigned int hook, struct sk_buff *skb, nf_bridge_pull_encap_header(skb); nf_bridge_save_header(skb); + if (pf == PF_INET) + skb->protocol = htons(ETH_P_IP); + else + skb->protocol = htons(ETH_P_IPV6); NF_HOOK(pf, NF_INET_POST_ROUTING, skb, NULL, realoutdev, br_nf_dev_queue_xmit); -- cgit v1.2.3 From a02a2956809d88b42b9a1f8003d60a343353b5ea Mon Sep 17 00:00:00 2001 From: Grazvydas Ignotas Date: Fri, 16 Apr 2010 13:22:12 +0300 Subject: wl1251: add support for dedicated IRQ line wl1251 has WLAN_IRQ pin for generating interrupts to host processor, which is mandatory in SPI mode and optional in SDIO mode (which can use SDIO interrupts instead). However TI recommends using deditated IRQ line for SDIO too. Add support for using dedicated interrupt line with SDIO, but also leave ability to switch to SDIO interrupts in case it's needed. Signed-off-by: Grazvydas Ignotas Reviewed-by: Bob Copeland Signed-off-by: John W. Linville --- drivers/net/wireless/wl12xx/wl1251_sdio.c | 56 ++++++++++++++++++++++++++++--- include/linux/spi/wl12xx.h | 2 ++ 2 files changed, 53 insertions(+), 5 deletions(-) (limited to 'include/linux') diff --git a/drivers/net/wireless/wl12xx/wl1251_sdio.c b/drivers/net/wireless/wl12xx/wl1251_sdio.c index 7409c3468e2..d234285c2c8 100644 --- a/drivers/net/wireless/wl12xx/wl1251_sdio.c +++ b/drivers/net/wireless/wl12xx/wl1251_sdio.c @@ -25,6 +25,7 @@ #include #include #include +#include #include "wl1251.h" @@ -134,18 +135,36 @@ static void wl1251_sdio_disable_irq(struct wl1251 *wl) sdio_release_host(func); } +/* Interrupts when using dedicated WLAN_IRQ pin */ +static irqreturn_t wl1251_line_irq(int irq, void *cookie) +{ + struct wl1251 *wl = cookie; + + ieee80211_queue_work(wl->hw, &wl->irq_work); + + return IRQ_HANDLED; +} + +static void wl1251_enable_line_irq(struct wl1251 *wl) +{ + return enable_irq(wl->irq); +} + +static void wl1251_disable_line_irq(struct wl1251 *wl) +{ + return disable_irq(wl->irq); +} + static void wl1251_sdio_set_power(bool enable) { } -static const struct wl1251_if_operations wl1251_sdio_ops = { +static struct wl1251_if_operations wl1251_sdio_ops = { .read = wl1251_sdio_read, .write = wl1251_sdio_write, .write_elp = wl1251_sdio_write_elp, .read_elp = wl1251_sdio_read_elp, .reset = wl1251_sdio_reset, - .enable_irq = wl1251_sdio_enable_irq, - .disable_irq = wl1251_sdio_disable_irq, }; static int wl1251_platform_probe(struct platform_device *pdev) @@ -191,6 +210,7 @@ static int wl1251_sdio_probe(struct sdio_func *func, goto release; sdio_set_block_size(func, 512); + sdio_release_host(func); SET_IEEE80211_DEV(hw, &func->dev); wl->if_priv = func; @@ -199,17 +219,41 @@ static int wl1251_sdio_probe(struct sdio_func *func, if (wl12xx_board_data != NULL) { wl->set_power = wl12xx_board_data->set_power; + wl->irq = wl12xx_board_data->irq; wl->use_eeprom = wl12xx_board_data->use_eeprom; } - sdio_release_host(func); + if (wl->irq) { + ret = request_irq(wl->irq, wl1251_line_irq, 0, "wl1251", wl); + if (ret < 0) { + wl1251_error("request_irq() failed: %d", ret); + goto disable; + } + + set_irq_type(wl->irq, IRQ_TYPE_EDGE_RISING); + disable_irq(wl->irq); + + wl1251_sdio_ops.enable_irq = wl1251_enable_line_irq; + wl1251_sdio_ops.disable_irq = wl1251_disable_line_irq; + + wl1251_info("using dedicated interrupt line"); + } else { + wl1251_sdio_ops.enable_irq = wl1251_sdio_enable_irq; + wl1251_sdio_ops.disable_irq = wl1251_sdio_disable_irq; + + wl1251_info("using SDIO interrupt"); + } + ret = wl1251_init_ieee80211(wl); if (ret) - goto disable; + goto out_free_irq; sdio_set_drvdata(func, wl); return ret; +out_free_irq: + if (wl->irq) + free_irq(wl->irq, wl); disable: sdio_claim_host(func); sdio_disable_func(func); @@ -222,6 +266,8 @@ static void __devexit wl1251_sdio_remove(struct sdio_func *func) { struct wl1251 *wl = sdio_get_drvdata(func); + if (wl->irq) + free_irq(wl->irq, wl); wl1251_free_hw(wl); sdio_claim_host(func); diff --git a/include/linux/spi/wl12xx.h b/include/linux/spi/wl12xx.h index aed64ed3dc8..a223ecbc71e 100644 --- a/include/linux/spi/wl12xx.h +++ b/include/linux/spi/wl12xx.h @@ -26,6 +26,8 @@ struct wl12xx_platform_data { void (*set_power)(bool enable); + /* SDIO only: IRQ number if WLAN_IRQ line is used, 0 for SDIO IRQs */ + int irq; bool use_eeprom; }; -- cgit v1.2.3 From fec5e652e58fa6017b2c9e06466cb2a6538de5b4 Mon Sep 17 00:00:00 2001 From: Tom Herbert Date: Fri, 16 Apr 2010 16:01:27 -0700 Subject: rfs: Receive Flow Steering This patch implements receive flow steering (RFS). RFS steers received packets for layer 3 and 4 processing to the CPU where the application for the corresponding flow is running. RFS is an extension of Receive Packet Steering (RPS). The basic idea of RFS is that when an application calls recvmsg (or sendmsg) the application's running CPU is stored in a hash table that is indexed by the connection's rxhash which is stored in the socket structure. The rxhash is passed in skb's received on the connection from netif_receive_skb. For each received packet, the associated rxhash is used to look up the CPU in the hash table, if a valid CPU is set then the packet is steered to that CPU using the RPS mechanisms. The convolution of the simple approach is that it would potentially allow OOO packets. If threads are thrashing around CPUs or multiple threads are trying to read from the same sockets, a quickly changing CPU value in the hash table could cause rampant OOO packets-- we consider this a non-starter. To avoid OOO packets, this solution implements two types of hash tables: rps_sock_flow_table and rps_dev_flow_table. rps_sock_table is a global hash table. Each entry is just a CPU number and it is populated in recvmsg and sendmsg as described above. This table contains the "desired" CPUs for flows. rps_dev_flow_table is specific to each device queue. Each entry contains a CPU and a tail queue counter. The CPU is the "current" CPU for a matching flow. The tail queue counter holds the value of a tail queue counter for the associated CPU's backlog queue at the time of last enqueue for a flow matching the entry. Each backlog queue has a queue head counter which is incremented on dequeue, and so a queue tail counter is computed as queue head count + queue length. When a packet is enqueued on a backlog queue, the current value of the queue tail counter is saved in the hash entry of the rps_dev_flow_table. And now the trick: when selecting the CPU for RPS (get_rps_cpu) the rps_sock_flow table and the rps_dev_flow table for the RX queue are consulted. When the desired CPU for the flow (found in the rps_sock_flow table) does not match the current CPU (found in the rps_dev_flow table), the current CPU is changed to the desired CPU if one of the following is true: - The current CPU is unset (equal to RPS_NO_CPU) - Current CPU is offline - The current CPU's queue head counter >= queue tail counter in the rps_dev_flow table. This checks if the queue tail has advanced beyond the last packet that was enqueued using this table entry. This guarantees that all packets queued using this entry have been dequeued, thus preserving in order delivery. Making each queue have its own rps_dev_flow table has two advantages: 1) the tail queue counters will be written on each receive, so keeping the table local to interrupting CPU s good for locality. 2) this allows lockless access to the table-- the CPU number and queue tail counter need to be accessed together under mutual exclusion from netif_receive_skb, we assume that this is only called from device napi_poll which is non-reentrant. This patch implements RFS for TCP and connected UDP sockets. It should be usable for other flow oriented protocols. There are two configuration parameters for RFS. The "rps_flow_entries" kernel init parameter sets the number of entries in the rps_sock_flow_table, the per rxqueue sysfs entry "rps_flow_cnt" contains the number of entries in the rps_dev_flow table for the rxqueue. Both are rounded to power of two. The obvious benefit of RFS (over just RPS) is that it achieves CPU locality between the receive processing for a flow and the applications processing; this can result in increased performance (higher pps, lower latency). The benefits of RFS are dependent on cache hierarchy, application load, and other factors. On simple benchmarks, we don't necessarily see improvement and sometimes see degradation. However, for more complex benchmarks and for applications where cache pressure is much higher this technique seems to perform very well. Below are some benchmark results which show the potential benfit of this patch. The netperf test has 500 instances of netperf TCP_RR test with 1 byte req. and resp. The RPC test is an request/response test similar in structure to netperf RR test ith 100 threads on each host, but does more work in userspace that netperf. e1000e on 8 core Intel No RFS or RPS 104K tps at 30% CPU No RFS (best RPS config): 290K tps at 63% CPU RFS 303K tps at 61% CPU RPC test tps CPU% 50/90/99% usec latency Latency StdDev No RFS/RPS 103K 48% 757/900/3185 4472.35 RPS only: 174K 73% 415/993/2468 491.66 RFS 223K 73% 379/651/1382 315.61 Signed-off-by: Tom Herbert Signed-off-by: Eric Dumazet Signed-off-by: David S. Miller --- include/linux/netdevice.h | 69 +++++++++++++++++++++++++++- include/net/inet_sock.h | 38 ++++++++++++++++ net/core/dev.c | 111 +++++++++++++++++++++++++++++++++++++-------- net/core/net-sysfs.c | 94 ++++++++++++++++++++++++++++++++++++-- net/core/sysctl_net_core.c | 68 +++++++++++++++++++++++++++ net/ipv4/af_inet.c | 29 ++++++++++-- net/ipv4/tcp_ipv4.c | 2 + net/ipv4/udp.c | 7 ++- 8 files changed, 389 insertions(+), 29 deletions(-) (limited to 'include/linux') diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index 55c2086e1f0..649a0252686 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -530,14 +530,73 @@ struct rps_map { }; #define RPS_MAP_SIZE(_num) (sizeof(struct rps_map) + (_num * sizeof(u16))) +/* + * The rps_dev_flow structure contains the mapping of a flow to a CPU and the + * tail pointer for that CPU's input queue at the time of last enqueue. + */ +struct rps_dev_flow { + u16 cpu; + u16 fill; + unsigned int last_qtail; +}; + +/* + * The rps_dev_flow_table structure contains a table of flow mappings. + */ +struct rps_dev_flow_table { + unsigned int mask; + struct rcu_head rcu; + struct work_struct free_work; + struct rps_dev_flow flows[0]; +}; +#define RPS_DEV_FLOW_TABLE_SIZE(_num) (sizeof(struct rps_dev_flow_table) + \ + (_num * sizeof(struct rps_dev_flow))) + +/* + * The rps_sock_flow_table contains mappings of flows to the last CPU + * on which they were processed by the application (set in recvmsg). + */ +struct rps_sock_flow_table { + unsigned int mask; + u16 ents[0]; +}; +#define RPS_SOCK_FLOW_TABLE_SIZE(_num) (sizeof(struct rps_sock_flow_table) + \ + (_num * sizeof(u16))) + +#define RPS_NO_CPU 0xffff + +static inline void rps_record_sock_flow(struct rps_sock_flow_table *table, + u32 hash) +{ + if (table && hash) { + unsigned int cpu, index = hash & table->mask; + + /* We only give a hint, preemption can change cpu under us */ + cpu = raw_smp_processor_id(); + + if (table->ents[index] != cpu) + table->ents[index] = cpu; + } +} + +static inline void rps_reset_sock_flow(struct rps_sock_flow_table *table, + u32 hash) +{ + if (table && hash) + table->ents[hash & table->mask] = RPS_NO_CPU; +} + +extern struct rps_sock_flow_table *rps_sock_flow_table; + /* This structure contains an instance of an RX queue. */ struct netdev_rx_queue { struct rps_map *rps_map; + struct rps_dev_flow_table *rps_flow_table; struct kobject kobj; struct netdev_rx_queue *first; atomic_t count; } ____cacheline_aligned_in_smp; -#endif +#endif /* CONFIG_RPS */ /* * This structure defines the management hooks for network devices. @@ -1333,11 +1392,19 @@ struct softnet_data { /* Elements below can be accessed between CPUs for RPS */ #ifdef CONFIG_RPS struct call_single_data csd ____cacheline_aligned_in_smp; + unsigned int input_queue_head; #endif struct sk_buff_head input_pkt_queue; struct napi_struct backlog; }; +static inline void incr_input_queue_head(struct softnet_data *queue) +{ +#ifdef CONFIG_RPS + queue->input_queue_head++; +#endif +} + DECLARE_PER_CPU_ALIGNED(struct softnet_data, softnet_data); #define HAVE_NETIF_QUEUE diff --git a/include/net/inet_sock.h b/include/net/inet_sock.h index 83fd34437cf..b487bc1b99a 100644 --- a/include/net/inet_sock.h +++ b/include/net/inet_sock.h @@ -21,6 +21,7 @@ #include #include #include +#include #include #include @@ -101,6 +102,7 @@ struct rtable; * @uc_ttl - Unicast TTL * @inet_sport - Source port * @inet_id - ID counter for DF pkts + * @rxhash - flow hash received from netif layer * @tos - TOS * @mc_ttl - Multicasting TTL * @is_icsk - is this an inet_connection_sock? @@ -124,6 +126,9 @@ struct inet_sock { __u16 cmsg_flags; __be16 inet_sport; __u16 inet_id; +#ifdef CONFIG_RPS + __u32 rxhash; +#endif struct ip_options *opt; __u8 tos; @@ -219,4 +224,37 @@ static inline __u8 inet_sk_flowi_flags(const struct sock *sk) return inet_sk(sk)->transparent ? FLOWI_FLAG_ANYSRC : 0; } +static inline void inet_rps_record_flow(const struct sock *sk) +{ +#ifdef CONFIG_RPS + struct rps_sock_flow_table *sock_flow_table; + + rcu_read_lock(); + sock_flow_table = rcu_dereference(rps_sock_flow_table); + rps_record_sock_flow(sock_flow_table, inet_sk(sk)->rxhash); + rcu_read_unlock(); +#endif +} + +static inline void inet_rps_reset_flow(const struct sock *sk) +{ +#ifdef CONFIG_RPS + struct rps_sock_flow_table *sock_flow_table; + + rcu_read_lock(); + sock_flow_table = rcu_dereference(rps_sock_flow_table); + rps_reset_sock_flow(sock_flow_table, inet_sk(sk)->rxhash); + rcu_read_unlock(); +#endif +} + +static inline void inet_rps_save_rxhash(const struct sock *sk, u32 rxhash) +{ +#ifdef CONFIG_RPS + if (unlikely(inet_sk(sk)->rxhash != rxhash)) { + inet_rps_reset_flow(sk); + inet_sk(sk)->rxhash = rxhash; + } +#endif +} #endif /* _INET_SOCK_H */ diff --git a/net/core/dev.c b/net/core/dev.c index e8041eb76ac..d7107ac835f 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -2203,19 +2203,28 @@ int weight_p __read_mostly = 64; /* old backlog weight */ DEFINE_PER_CPU(struct netif_rx_stats, netdev_rx_stat) = { 0, }; #ifdef CONFIG_RPS + +/* One global table that all flow-based protocols share. */ +struct rps_sock_flow_table *rps_sock_flow_table; +EXPORT_SYMBOL(rps_sock_flow_table); + /* * get_rps_cpu is called from netif_receive_skb and returns the target * CPU from the RPS map of the receiving queue for a given skb. * rcu_read_lock must be held on entry. */ -static int get_rps_cpu(struct net_device *dev, struct sk_buff *skb) +static int get_rps_cpu(struct net_device *dev, struct sk_buff *skb, + struct rps_dev_flow **rflowp) { struct ipv6hdr *ip6; struct iphdr *ip; struct netdev_rx_queue *rxqueue; struct rps_map *map; + struct rps_dev_flow_table *flow_table; + struct rps_sock_flow_table *sock_flow_table; int cpu = -1; u8 ip_proto; + u16 tcpu; u32 addr1, addr2, ports, ihl; if (skb_rx_queue_recorded(skb)) { @@ -2232,7 +2241,7 @@ static int get_rps_cpu(struct net_device *dev, struct sk_buff *skb) } else rxqueue = dev->_rx; - if (!rxqueue->rps_map) + if (!rxqueue->rps_map && !rxqueue->rps_flow_table) goto done; if (skb->rxhash) @@ -2284,9 +2293,48 @@ static int get_rps_cpu(struct net_device *dev, struct sk_buff *skb) skb->rxhash = 1; got_hash: + flow_table = rcu_dereference(rxqueue->rps_flow_table); + sock_flow_table = rcu_dereference(rps_sock_flow_table); + if (flow_table && sock_flow_table) { + u16 next_cpu; + struct rps_dev_flow *rflow; + + rflow = &flow_table->flows[skb->rxhash & flow_table->mask]; + tcpu = rflow->cpu; + + next_cpu = sock_flow_table->ents[skb->rxhash & + sock_flow_table->mask]; + + /* + * If the desired CPU (where last recvmsg was done) is + * different from current CPU (one in the rx-queue flow + * table entry), switch if one of the following holds: + * - Current CPU is unset (equal to RPS_NO_CPU). + * - Current CPU is offline. + * - The current CPU's queue tail has advanced beyond the + * last packet that was enqueued using this table entry. + * This guarantees that all previous packets for the flow + * have been dequeued, thus preserving in order delivery. + */ + if (unlikely(tcpu != next_cpu) && + (tcpu == RPS_NO_CPU || !cpu_online(tcpu) || + ((int)(per_cpu(softnet_data, tcpu).input_queue_head - + rflow->last_qtail)) >= 0)) { + tcpu = rflow->cpu = next_cpu; + if (tcpu != RPS_NO_CPU) + rflow->last_qtail = per_cpu(softnet_data, + tcpu).input_queue_head; + } + if (tcpu != RPS_NO_CPU && cpu_online(tcpu)) { + *rflowp = rflow; + cpu = tcpu; + goto done; + } + } + map = rcu_dereference(rxqueue->rps_map); if (map) { - u16 tcpu = map->cpus[((u64) skb->rxhash * map->len) >> 32]; + tcpu = map->cpus[((u64) skb->rxhash * map->len) >> 32]; if (cpu_online(tcpu)) { cpu = tcpu; @@ -2320,13 +2368,14 @@ static void trigger_softirq(void *data) __napi_schedule(&queue->backlog); __get_cpu_var(netdev_rx_stat).received_rps++; } -#endif /* CONFIG_SMP */ +#endif /* CONFIG_RPS */ /* * enqueue_to_backlog is called to queue an skb to a per CPU backlog * queue (may be a remote CPU queue). */ -static int enqueue_to_backlog(struct sk_buff *skb, int cpu) +static int enqueue_to_backlog(struct sk_buff *skb, int cpu, + unsigned int *qtail) { struct softnet_data *queue; unsigned long flags; @@ -2341,6 +2390,10 @@ static int enqueue_to_backlog(struct sk_buff *skb, int cpu) if (queue->input_pkt_queue.qlen) { enqueue: __skb_queue_tail(&queue->input_pkt_queue, skb); +#ifdef CONFIG_RPS + *qtail = queue->input_queue_head + + queue->input_pkt_queue.qlen; +#endif rps_unlock(queue); local_irq_restore(flags); return NET_RX_SUCCESS; @@ -2355,11 +2408,10 @@ enqueue: cpu_set(cpu, rcpus->mask[rcpus->select]); __raise_softirq_irqoff(NET_RX_SOFTIRQ); - } else - __napi_schedule(&queue->backlog); -#else - __napi_schedule(&queue->backlog); + goto enqueue; + } #endif + __napi_schedule(&queue->backlog); } goto enqueue; } @@ -2401,18 +2453,25 @@ int netif_rx(struct sk_buff *skb) #ifdef CONFIG_RPS { + struct rps_dev_flow voidflow, *rflow = &voidflow; int cpu; rcu_read_lock(); - cpu = get_rps_cpu(skb->dev, skb); + + cpu = get_rps_cpu(skb->dev, skb, &rflow); if (cpu < 0) cpu = smp_processor_id(); - ret = enqueue_to_backlog(skb, cpu); + + ret = enqueue_to_backlog(skb, cpu, &rflow->last_qtail); + rcu_read_unlock(); } #else - ret = enqueue_to_backlog(skb, get_cpu()); - put_cpu(); + { + unsigned int qtail; + ret = enqueue_to_backlog(skb, get_cpu(), &qtail); + put_cpu(); + } #endif return ret; } @@ -2830,14 +2889,22 @@ out: int netif_receive_skb(struct sk_buff *skb) { #ifdef CONFIG_RPS - int cpu; + struct rps_dev_flow voidflow, *rflow = &voidflow; + int cpu, ret; + + rcu_read_lock(); - cpu = get_rps_cpu(skb->dev, skb); + cpu = get_rps_cpu(skb->dev, skb, &rflow); - if (cpu < 0) - return __netif_receive_skb(skb); - else - return enqueue_to_backlog(skb, cpu); + if (cpu >= 0) { + ret = enqueue_to_backlog(skb, cpu, &rflow->last_qtail); + rcu_read_unlock(); + } else { + rcu_read_unlock(); + ret = __netif_receive_skb(skb); + } + + return ret; #else return __netif_receive_skb(skb); #endif @@ -2856,6 +2923,7 @@ static void flush_backlog(void *arg) if (skb->dev == dev) { __skb_unlink(skb, &queue->input_pkt_queue); kfree_skb(skb); + incr_input_queue_head(queue); } rps_unlock(queue); } @@ -3179,6 +3247,7 @@ static int process_backlog(struct napi_struct *napi, int quota) local_irq_enable(); break; } + incr_input_queue_head(queue); rps_unlock(queue); local_irq_enable(); @@ -5542,8 +5611,10 @@ static int dev_cpu_callback(struct notifier_block *nfb, local_irq_enable(); /* Process offline CPU's input_pkt_queue */ - while ((skb = __skb_dequeue(&oldsd->input_pkt_queue))) + while ((skb = __skb_dequeue(&oldsd->input_pkt_queue))) { netif_rx(skb); + incr_input_queue_head(oldsd); + } return NOTIFY_OK; } diff --git a/net/core/net-sysfs.c b/net/core/net-sysfs.c index 96ed6905b82..143052a22b9 100644 --- a/net/core/net-sysfs.c +++ b/net/core/net-sysfs.c @@ -17,6 +17,7 @@ #include #include #include +#include #include #include "net-sysfs.h" @@ -601,22 +602,109 @@ ssize_t store_rps_map(struct netdev_rx_queue *queue, return len; } +static ssize_t show_rps_dev_flow_table_cnt(struct netdev_rx_queue *queue, + struct rx_queue_attribute *attr, + char *buf) +{ + struct rps_dev_flow_table *flow_table; + unsigned int val = 0; + + rcu_read_lock(); + flow_table = rcu_dereference(queue->rps_flow_table); + if (flow_table) + val = flow_table->mask + 1; + rcu_read_unlock(); + + return sprintf(buf, "%u\n", val); +} + +static void rps_dev_flow_table_release_work(struct work_struct *work) +{ + struct rps_dev_flow_table *table = container_of(work, + struct rps_dev_flow_table, free_work); + + vfree(table); +} + +static void rps_dev_flow_table_release(struct rcu_head *rcu) +{ + struct rps_dev_flow_table *table = container_of(rcu, + struct rps_dev_flow_table, rcu); + + INIT_WORK(&table->free_work, rps_dev_flow_table_release_work); + schedule_work(&table->free_work); +} + +ssize_t store_rps_dev_flow_table_cnt(struct netdev_rx_queue *queue, + struct rx_queue_attribute *attr, + const char *buf, size_t len) +{ + unsigned int count; + char *endp; + struct rps_dev_flow_table *table, *old_table; + static DEFINE_SPINLOCK(rps_dev_flow_lock); + + if (!capable(CAP_NET_ADMIN)) + return -EPERM; + + count = simple_strtoul(buf, &endp, 0); + if (endp == buf) + return -EINVAL; + + if (count) { + int i; + + if (count > 1<<30) { + /* Enforce a limit to prevent overflow */ + return -EINVAL; + } + count = roundup_pow_of_two(count); + table = vmalloc(RPS_DEV_FLOW_TABLE_SIZE(count)); + if (!table) + return -ENOMEM; + + table->mask = count - 1; + for (i = 0; i < count; i++) + table->flows[i].cpu = RPS_NO_CPU; + } else + table = NULL; + + spin_lock(&rps_dev_flow_lock); + old_table = queue->rps_flow_table; + rcu_assign_pointer(queue->rps_flow_table, table); + spin_unlock(&rps_dev_flow_lock); + + if (old_table) + call_rcu(&old_table->rcu, rps_dev_flow_table_release); + + return len; +} + static struct rx_queue_attribute rps_cpus_attribute = __ATTR(rps_cpus, S_IRUGO | S_IWUSR, show_rps_map, store_rps_map); + +static struct rx_queue_attribute rps_dev_flow_table_cnt_attribute = + __ATTR(rps_flow_cnt, S_IRUGO | S_IWUSR, + show_rps_dev_flow_table_cnt, store_rps_dev_flow_table_cnt); + static struct attribute *rx_queue_default_attrs[] = { &rps_cpus_attribute.attr, + &rps_dev_flow_table_cnt_attribute.attr, NULL }; static void rx_queue_release(struct kobject *kobj) { struct netdev_rx_queue *queue = to_rx_queue(kobj); - struct rps_map *map = queue->rps_map; struct netdev_rx_queue *first = queue->first; - if (map) - call_rcu(&map->rcu, rps_map_release); + if (queue->rps_map) + call_rcu(&queue->rps_map->rcu, rps_map_release); + + if (queue->rps_flow_table) + call_rcu(&queue->rps_flow_table->rcu, + rps_dev_flow_table_release); if (atomic_dec_and_test(&first->count)) kfree(first); diff --git a/net/core/sysctl_net_core.c b/net/core/sysctl_net_core.c index b7b6b8208f7..dcc7d25996a 100644 --- a/net/core/sysctl_net_core.c +++ b/net/core/sysctl_net_core.c @@ -11,12 +11,72 @@ #include #include #include +#include #include #include #include #include +#ifdef CONFIG_RPS +static int rps_sock_flow_sysctl(ctl_table *table, int write, + void __user *buffer, size_t *lenp, loff_t *ppos) +{ + unsigned int orig_size, size; + int ret, i; + ctl_table tmp = { + .data = &size, + .maxlen = sizeof(size), + .mode = table->mode + }; + struct rps_sock_flow_table *orig_sock_table, *sock_table; + static DEFINE_MUTEX(sock_flow_mutex); + + mutex_lock(&sock_flow_mutex); + + orig_sock_table = rps_sock_flow_table; + size = orig_size = orig_sock_table ? orig_sock_table->mask + 1 : 0; + + ret = proc_dointvec(&tmp, write, buffer, lenp, ppos); + + if (write) { + if (size) { + if (size > 1<<30) { + /* Enforce limit to prevent overflow */ + mutex_unlock(&sock_flow_mutex); + return -EINVAL; + } + size = roundup_pow_of_two(size); + if (size != orig_size) { + sock_table = + vmalloc(RPS_SOCK_FLOW_TABLE_SIZE(size)); + if (!sock_table) { + mutex_unlock(&sock_flow_mutex); + return -ENOMEM; + } + + sock_table->mask = size - 1; + } else + sock_table = orig_sock_table; + + for (i = 0; i < size; i++) + sock_table->ents[i] = RPS_NO_CPU; + } else + sock_table = NULL; + + if (sock_table != orig_sock_table) { + rcu_assign_pointer(rps_sock_flow_table, sock_table); + synchronize_rcu(); + vfree(orig_sock_table); + } + } + + mutex_unlock(&sock_flow_mutex); + + return ret; +} +#endif /* CONFIG_RPS */ + static struct ctl_table net_core_table[] = { #ifdef CONFIG_NET { @@ -82,6 +142,14 @@ static struct ctl_table net_core_table[] = { .mode = 0644, .proc_handler = proc_dointvec }, +#ifdef CONFIG_RPS + { + .procname = "rps_sock_flow_entries", + .maxlen = sizeof(int), + .mode = 0644, + .proc_handler = rps_sock_flow_sysctl + }, +#endif #endif /* CONFIG_NET */ { .procname = "netdev_budget", diff --git a/net/ipv4/af_inet.c b/net/ipv4/af_inet.c index 193dcd6ed64..c5376c72550 100644 --- a/net/ipv4/af_inet.c +++ b/net/ipv4/af_inet.c @@ -419,6 +419,8 @@ int inet_release(struct socket *sock) if (sk) { long timeout; + inet_rps_reset_flow(sk); + /* Applications forget to leave groups before exiting */ ip_mc_drop_socket(sk); @@ -720,6 +722,8 @@ int inet_sendmsg(struct kiocb *iocb, struct socket *sock, struct msghdr *msg, { struct sock *sk = sock->sk; + inet_rps_record_flow(sk); + /* We may need to bind the socket. */ if (!inet_sk(sk)->inet_num && inet_autobind(sk)) return -EAGAIN; @@ -728,12 +732,13 @@ int inet_sendmsg(struct kiocb *iocb, struct socket *sock, struct msghdr *msg, } EXPORT_SYMBOL(inet_sendmsg); - static ssize_t inet_sendpage(struct socket *sock, struct page *page, int offset, size_t size, int flags) { struct sock *sk = sock->sk; + inet_rps_record_flow(sk); + /* We may need to bind the socket. */ if (!inet_sk(sk)->inet_num && inet_autobind(sk)) return -EAGAIN; @@ -743,6 +748,22 @@ static ssize_t inet_sendpage(struct socket *sock, struct page *page, int offset, return sock_no_sendpage(sock, page, offset, size, flags); } +int inet_recvmsg(struct kiocb *iocb, struct socket *sock, struct msghdr *msg, + size_t size, int flags) +{ + struct sock *sk = sock->sk; + int addr_len = 0; + int err; + + inet_rps_record_flow(sk); + + err = sk->sk_prot->recvmsg(iocb, sk, msg, size, flags & MSG_DONTWAIT, + flags & ~MSG_DONTWAIT, &addr_len); + if (err >= 0) + msg->msg_namelen = addr_len; + return err; +} +EXPORT_SYMBOL(inet_recvmsg); int inet_shutdown(struct socket *sock, int how) { @@ -872,7 +893,7 @@ const struct proto_ops inet_stream_ops = { .setsockopt = sock_common_setsockopt, .getsockopt = sock_common_getsockopt, .sendmsg = tcp_sendmsg, - .recvmsg = sock_common_recvmsg, + .recvmsg = inet_recvmsg, .mmap = sock_no_mmap, .sendpage = tcp_sendpage, .splice_read = tcp_splice_read, @@ -899,7 +920,7 @@ const struct proto_ops inet_dgram_ops = { .setsockopt = sock_common_setsockopt, .getsockopt = sock_common_getsockopt, .sendmsg = inet_sendmsg, - .recvmsg = sock_common_recvmsg, + .recvmsg = inet_recvmsg, .mmap = sock_no_mmap, .sendpage = inet_sendpage, #ifdef CONFIG_COMPAT @@ -929,7 +950,7 @@ static const struct proto_ops inet_sockraw_ops = { .setsockopt = sock_common_setsockopt, .getsockopt = sock_common_getsockopt, .sendmsg = inet_sendmsg, - .recvmsg = sock_common_recvmsg, + .recvmsg = inet_recvmsg, .mmap = sock_no_mmap, .sendpage = inet_sendpage, #ifdef CONFIG_COMPAT diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c index a24995cdc4b..ad08392a738 100644 --- a/net/ipv4/tcp_ipv4.c +++ b/net/ipv4/tcp_ipv4.c @@ -1672,6 +1672,8 @@ process: skb->dev = NULL; + inet_rps_save_rxhash(sk, skb->rxhash); + bh_lock_sock_nested(sk); ret = 0; if (!sock_owned_by_user(sk)) { diff --git a/net/ipv4/udp.c b/net/ipv4/udp.c index 8fef859db35..666b963496f 100644 --- a/net/ipv4/udp.c +++ b/net/ipv4/udp.c @@ -1217,6 +1217,7 @@ int udp_disconnect(struct sock *sk, int flags) sk->sk_state = TCP_CLOSE; inet->inet_daddr = 0; inet->inet_dport = 0; + inet_rps_save_rxhash(sk, 0); sk->sk_bound_dev_if = 0; if (!(sk->sk_userlocks & SOCK_BINDADDR_LOCK)) inet_reset_saddr(sk); @@ -1258,8 +1259,12 @@ EXPORT_SYMBOL(udp_lib_unhash); static int __udp_queue_rcv_skb(struct sock *sk, struct sk_buff *skb) { - int rc = sock_queue_rcv_skb(sk, skb); + int rc; + + if (inet_sk(sk)->inet_daddr) + inet_rps_save_rxhash(sk, skb->rxhash); + rc = sock_queue_rcv_skb(sk, skb); if (rc < 0) { int is_udplite = IS_UDPLITE(sk); -- cgit v1.2.3 From e281b19897dc21c1071802808d461627d747a877 Mon Sep 17 00:00:00 2001 From: Jan Engelhardt Date: Mon, 19 Apr 2010 14:17:47 +0200 Subject: netfilter: xtables: inclusion of xt_TEE xt_TEE can be used to clone and reroute a packet. This can for example be used to copy traffic at a router for logging purposes to another dedicated machine. References: http://www.gossamer-threads.com/lists/iptables/devel/68781 Signed-off-by: Jan Engelhardt Signed-off-by: Patrick McHardy --- include/linux/netfilter/Kbuild | 1 + include/linux/netfilter/xt_TEE.h | 9 ++ net/ipv4/ip_output.c | 1 + net/ipv6/ip6_output.c | 1 + net/netfilter/Kconfig | 7 ++ net/netfilter/Makefile | 1 + net/netfilter/xt_TEE.c | 256 +++++++++++++++++++++++++++++++++++++++ 7 files changed, 276 insertions(+) create mode 100644 include/linux/netfilter/xt_TEE.h create mode 100644 net/netfilter/xt_TEE.c (limited to 'include/linux') diff --git a/include/linux/netfilter/Kbuild b/include/linux/netfilter/Kbuild index a5a63e41b8a..48767cd1645 100644 --- a/include/linux/netfilter/Kbuild +++ b/include/linux/netfilter/Kbuild @@ -16,6 +16,7 @@ header-y += xt_RATEEST.h header-y += xt_SECMARK.h header-y += xt_TCPMSS.h header-y += xt_TCPOPTSTRIP.h +header-y += xt_TEE.h header-y += xt_TPROXY.h header-y += xt_comment.h header-y += xt_connbytes.h diff --git a/include/linux/netfilter/xt_TEE.h b/include/linux/netfilter/xt_TEE.h new file mode 100644 index 00000000000..55d4a501152 --- /dev/null +++ b/include/linux/netfilter/xt_TEE.h @@ -0,0 +1,9 @@ +#ifndef _XT_TEE_TARGET_H +#define _XT_TEE_TARGET_H + +struct xt_tee_tginfo { + union nf_inet_addr gw; + char oif[16]; +}; + +#endif /* _XT_TEE_TARGET_H */ diff --git a/net/ipv4/ip_output.c b/net/ipv4/ip_output.c index f09135e1e14..0abfddec1e2 100644 --- a/net/ipv4/ip_output.c +++ b/net/ipv4/ip_output.c @@ -309,6 +309,7 @@ int ip_output(struct sk_buff *skb) ip_finish_output, !(IPCB(skb)->flags & IPSKB_REROUTED)); } +EXPORT_SYMBOL_GPL(ip_output); int ip_queue_xmit(struct sk_buff *skb, int ipfragok) { diff --git a/net/ipv6/ip6_output.c b/net/ipv6/ip6_output.c index c10a38a71a5..d09be7ff873 100644 --- a/net/ipv6/ip6_output.c +++ b/net/ipv6/ip6_output.c @@ -176,6 +176,7 @@ int ip6_output(struct sk_buff *skb) ip6_finish_output, !(IP6CB(skb)->flags & IP6SKB_REROUTED)); } +EXPORT_SYMBOL_GPL(ip6_output); /* * xmit an sk_buff (used by TCP) diff --git a/net/netfilter/Kconfig b/net/netfilter/Kconfig index 8055786b770..673a6c8f0e9 100644 --- a/net/netfilter/Kconfig +++ b/net/netfilter/Kconfig @@ -502,6 +502,13 @@ config NETFILTER_XT_TARGET_RATEEST To compile it as a module, choose M here. If unsure, say N. +config NETFILTER_XT_TARGET_TEE + tristate '"TEE" - packet cloning to alternate destiantion' + depends on NETFILTER_ADVANCED + ---help--- + This option adds a "TEE" target with which a packet can be cloned and + this clone be rerouted to another nexthop. + config NETFILTER_XT_TARGET_TPROXY tristate '"TPROXY" target support (EXPERIMENTAL)' depends on EXPERIMENTAL diff --git a/net/netfilter/Makefile b/net/netfilter/Makefile index cd31afe0692..14e3a8fd818 100644 --- a/net/netfilter/Makefile +++ b/net/netfilter/Makefile @@ -59,6 +59,7 @@ obj-$(CONFIG_NETFILTER_XT_TARGET_SECMARK) += xt_SECMARK.o obj-$(CONFIG_NETFILTER_XT_TARGET_TPROXY) += xt_TPROXY.o obj-$(CONFIG_NETFILTER_XT_TARGET_TCPMSS) += xt_TCPMSS.o obj-$(CONFIG_NETFILTER_XT_TARGET_TCPOPTSTRIP) += xt_TCPOPTSTRIP.o +obj-$(CONFIG_NETFILTER_XT_TARGET_TEE) += xt_TEE.o obj-$(CONFIG_NETFILTER_XT_TARGET_TRACE) += xt_TRACE.o # matches diff --git a/net/netfilter/xt_TEE.c b/net/netfilter/xt_TEE.c new file mode 100644 index 00000000000..b3d730163f1 --- /dev/null +++ b/net/netfilter/xt_TEE.c @@ -0,0 +1,256 @@ +/* + * "TEE" target extension for Xtables + * Copyright © Sebastian Claßen, 2007 + * Jan Engelhardt, 2007-2010 + * + * based on ipt_ROUTE.c from Cédric de Launois + * + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 or later, as published by the Free Software Foundation. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#if defined(CONFIG_NF_CONNTRACK) || defined(CONFIG_NF_CONNTRACK_MODULE) +# define WITH_CONNTRACK 1 +# include +#endif +#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) +# define WITH_IPV6 1 +#endif + +static const union nf_inet_addr tee_zero_address; + +static struct net *pick_net(struct sk_buff *skb) +{ +#ifdef CONFIG_NET_NS + const struct dst_entry *dst; + + if (skb->dev != NULL) + return dev_net(skb->dev); + dst = skb_dst(skb); + if (dst != NULL && dst->dev != NULL) + return dev_net(dst->dev); +#endif + return &init_net; +} + +static bool tee_tg_route_oif(struct flowi *f, struct net *net, + const struct xt_tee_tginfo *info) +{ + const struct net_device *dev; + + if (*info->oif != '\0') + return true; + dev = dev_get_by_name(net, info->oif); + if (dev == NULL) + return false; + f->oif = dev->ifindex; + return true; +} + +static bool +tee_tg_route4(struct sk_buff *skb, const struct xt_tee_tginfo *info) +{ + const struct iphdr *iph = ip_hdr(skb); + struct net *net = pick_net(skb); + struct rtable *rt; + struct flowi fl; + + memset(&fl, 0, sizeof(fl)); + if (!tee_tg_route_oif(&fl, net, info)) + return false; + fl.nl_u.ip4_u.daddr = info->gw.ip; + fl.nl_u.ip4_u.tos = RT_TOS(iph->tos); + fl.nl_u.ip4_u.scope = RT_SCOPE_UNIVERSE; + if (ip_route_output_key(net, &rt, &fl) != 0) + return false; + + dst_release(skb_dst(skb)); + skb_dst_set(skb, &rt->u.dst); + skb->dev = rt->u.dst.dev; + skb->protocol = htons(ETH_P_IP); + return true; +} + +static unsigned int +tee_tg4(struct sk_buff *skb, const struct xt_target_param *par) +{ + const struct xt_tee_tginfo *info = par->targinfo; + struct iphdr *iph; + + /* + * Copy the skb, and route the copy. Will later return %XT_CONTINUE for + * the original skb, which should continue on its way as if nothing has + * happened. The copy should be independently delivered to the TEE + * --gateway. + */ + skb = pskb_copy(skb, GFP_ATOMIC); + if (skb == NULL) + return XT_CONTINUE; + +#ifdef WITH_CONNTRACK + /* Avoid counting cloned packets towards the original connection. */ + nf_conntrack_put(skb->nfct); + skb->nfct = &nf_conntrack_untracked.ct_general; + skb->nfctinfo = IP_CT_NEW; + nf_conntrack_get(skb->nfct); +#endif + /* + * If we are in PREROUTING/INPUT, the checksum must be recalculated + * since the length could have changed as a result of defragmentation. + * + * We also decrease the TTL to mitigate potential TEE loops + * between two hosts. + * + * Set %IP_DF so that the original source is notified of a potentially + * decreased MTU on the clone route. IPv6 does this too. + */ + iph = ip_hdr(skb); + iph->frag_off |= htons(IP_DF); + if (par->hooknum == NF_INET_PRE_ROUTING || + par->hooknum == NF_INET_LOCAL_IN) + --iph->ttl; + ip_send_check(iph); + + /* + * Xtables is not reentrant currently, so a choice has to be made: + * 1. return absolute verdict for the original and let the cloned + * packet travel through the chains + * 2. let the original continue travelling and not pass the clone + * to Xtables. + * #2 is chosen. Normally, we would use ip_local_out for the clone. + * Because iph->check is already correct and we don't pass it to + * Xtables anyway, a shortcut to dst_output [forwards to ip_output] can + * be taken. %IPSKB_REROUTED needs to be set so that ip_output does not + * invoke POSTROUTING on the cloned packet. + */ + IPCB(skb)->flags |= IPSKB_REROUTED; + if (tee_tg_route4(skb, info)) + ip_output(skb); + else + kfree_skb(skb); + + return XT_CONTINUE; +} + +#ifdef WITH_IPV6 +static bool +tee_tg_route6(struct sk_buff *skb, const struct xt_tee_tginfo *info) +{ + const struct ipv6hdr *iph = ipv6_hdr(skb); + struct net *net = pick_net(skb); + struct dst_entry *dst; + struct flowi fl; + + memset(&fl, 0, sizeof(fl)); + if (!tee_tg_route_oif(&fl, net, info)) + return false; + fl.nl_u.ip6_u.daddr = info->gw.in6; + fl.nl_u.ip6_u.flowlabel = ((iph->flow_lbl[0] & 0xF) << 16) | + (iph->flow_lbl[1] << 8) | iph->flow_lbl[2]; + dst = ip6_route_output(net, NULL, &fl); + if (dst == NULL) + return false; + + dst_release(skb_dst(skb)); + skb_dst_set(skb, dst); + skb->dev = dst->dev; + skb->protocol = htons(ETH_P_IPV6); + return true; +} + +static unsigned int +tee_tg6(struct sk_buff *skb, const struct xt_target_param *par) +{ + const struct xt_tee_tginfo *info = par->targinfo; + + skb = pskb_copy(skb, GFP_ATOMIC); + if (skb == NULL) + return XT_CONTINUE; + +#ifdef WITH_CONNTRACK + nf_conntrack_put(skb->nfct); + skb->nfct = &nf_conntrack_untracked.ct_general; + skb->nfctinfo = IP_CT_NEW; + nf_conntrack_get(skb->nfct); +#endif + if (par->hooknum == NF_INET_PRE_ROUTING || + par->hooknum == NF_INET_LOCAL_IN) { + struct ipv6hdr *iph = ipv6_hdr(skb); + --iph->hop_limit; + } + IP6CB(skb)->flags |= IP6SKB_REROUTED; + if (tee_tg_route6(skb, info)) + ip6_output(skb); + else + kfree_skb(skb); + + return XT_CONTINUE; +} +#endif /* WITH_IPV6 */ + +static int tee_tg_check(const struct xt_tgchk_param *par) +{ + const struct xt_tee_tginfo *info = par->targinfo; + + if (info->oif[sizeof(info->oif)-1] != '\0') + return -EINVAL; + /* 0.0.0.0 and :: not allowed */ + return (memcmp(&info->gw, &tee_zero_address, + sizeof(tee_zero_address)) == 0) ? -EINVAL : 0; +} + +static struct xt_target tee_tg_reg[] __read_mostly = { + { + .name = "TEE", + .revision = 1, + .family = NFPROTO_IPV4, + .target = tee_tg4, + .targetsize = sizeof(struct xt_tee_tginfo), + .checkentry = tee_tg_check, + .me = THIS_MODULE, + }, +#ifdef WITH_IPV6 + { + .name = "TEE", + .revision = 1, + .family = NFPROTO_IPV6, + .target = tee_tg6, + .targetsize = sizeof(struct xt_tee_tginfo), + .checkentry = tee_tg_check, + .me = THIS_MODULE, + }, +#endif +}; + +static int __init tee_tg_init(void) +{ + return xt_register_targets(tee_tg_reg, ARRAY_SIZE(tee_tg_reg)); +} + +static void __exit tee_tg_exit(void) +{ + xt_unregister_targets(tee_tg_reg, ARRAY_SIZE(tee_tg_reg)); +} + +module_init(tee_tg_init); +module_exit(tee_tg_exit); +MODULE_AUTHOR("Sebastian Claßen "); +MODULE_AUTHOR("Jan Engelhardt "); +MODULE_DESCRIPTION("Xtables: Reroute packet copy"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("ipt_TEE"); +MODULE_ALIAS("ip6t_TEE"); -- cgit v1.2.3 From f3c5c1bfd430858d3a05436f82c51e53104feb6b Mon Sep 17 00:00:00 2001 From: Jan Engelhardt Date: Mon, 19 Apr 2010 16:05:10 +0200 Subject: netfilter: xtables: make ip_tables reentrant Currently, the table traverser stores return addresses in the ruleset itself (struct ip6t_entry->comefrom). This has a well-known drawback: the jumpstack is overwritten on reentry, making it necessary for targets to return absolute verdicts. Also, the ruleset (which might be heavy memory-wise) needs to be replicated for each CPU that can possibly invoke ip6t_do_table. This patch decouples the jumpstack from struct ip6t_entry and instead puts it into xt_table_info. Not being restricted by 'comefrom' anymore, we can set up a stack as needed. By default, there is room allocated for two entries into the traverser. arp_tables is not touched though, because there is just one/two modules and further patches seek to collapse the table traverser anyhow. Signed-off-by: Jan Engelhardt Signed-off-by: Patrick McHardy --- include/linux/netfilter/x_tables.h | 7 ++++ net/ipv4/netfilter/arp_tables.c | 6 ++- net/ipv4/netfilter/ip_tables.c | 65 +++++++++++++++++--------------- net/ipv6/netfilter/ip6_tables.c | 56 +++++++++++---------------- net/netfilter/x_tables.c | 77 ++++++++++++++++++++++++++++++++++++++ 5 files changed, 145 insertions(+), 66 deletions(-) (limited to 'include/linux') diff --git a/include/linux/netfilter/x_tables.h b/include/linux/netfilter/x_tables.h index 26ced0c323a..50c867256ca 100644 --- a/include/linux/netfilter/x_tables.h +++ b/include/linux/netfilter/x_tables.h @@ -401,6 +401,13 @@ struct xt_table_info { unsigned int hook_entry[NF_INET_NUMHOOKS]; unsigned int underflow[NF_INET_NUMHOOKS]; + /* + * Number of user chains. Since tables cannot have loops, at most + * @stacksize jumps (number of user chains) can possibly be made. + */ + unsigned int stacksize; + unsigned int *stackptr; + void ***jumpstack; /* ipt_entry tables: one per CPU */ /* Note : this field MUST be the last one, see XT_TABLE_INFO_SZ */ void *entries[1]; diff --git a/net/ipv4/netfilter/arp_tables.c b/net/ipv4/netfilter/arp_tables.c index e8e363d9036..07a69905939 100644 --- a/net/ipv4/netfilter/arp_tables.c +++ b/net/ipv4/netfilter/arp_tables.c @@ -649,6 +649,9 @@ static int translate_table(struct xt_table_info *newinfo, void *entry0, if (ret != 0) break; ++i; + if (strcmp(arpt_get_target(iter)->u.user.name, + XT_ERROR_TARGET) == 0) + ++newinfo->stacksize; } duprintf("translate_table: ARPT_ENTRY_ITERATE gives %d\n", ret); if (ret != 0) @@ -1774,8 +1777,7 @@ struct xt_table *arpt_register_table(struct net *net, { int ret; struct xt_table_info *newinfo; - struct xt_table_info bootstrap - = { 0, 0, 0, { 0 }, { 0 }, { } }; + struct xt_table_info bootstrap = {0}; void *loc_cpu_entry; struct xt_table *new_table; diff --git a/net/ipv4/netfilter/ip_tables.c b/net/ipv4/netfilter/ip_tables.c index 18c5b1573f3..70900ecf88e 100644 --- a/net/ipv4/netfilter/ip_tables.c +++ b/net/ipv4/netfilter/ip_tables.c @@ -321,8 +321,6 @@ ipt_do_table(struct sk_buff *skb, const struct net_device *out, struct xt_table *table) { -#define tb_comefrom ((struct ipt_entry *)table_base)->comefrom - static const char nulldevname[IFNAMSIZ] __attribute__((aligned(sizeof(long)))); const struct iphdr *ip; bool hotdrop = false; @@ -330,7 +328,8 @@ ipt_do_table(struct sk_buff *skb, unsigned int verdict = NF_DROP; const char *indev, *outdev; const void *table_base; - struct ipt_entry *e, *back; + struct ipt_entry *e, **jumpstack; + unsigned int *stackptr, origptr, cpu; const struct xt_table_info *private; struct xt_match_param mtpar; struct xt_target_param tgpar; @@ -356,19 +355,23 @@ ipt_do_table(struct sk_buff *skb, IP_NF_ASSERT(table->valid_hooks & (1 << hook)); xt_info_rdlock_bh(); private = table->private; - table_base = private->entries[smp_processor_id()]; + cpu = smp_processor_id(); + table_base = private->entries[cpu]; + jumpstack = (struct ipt_entry **)private->jumpstack[cpu]; + stackptr = &private->stackptr[cpu]; + origptr = *stackptr; e = get_entry(table_base, private->hook_entry[hook]); - /* For return from builtin chain */ - back = get_entry(table_base, private->underflow[hook]); + pr_devel("Entering %s(hook %u); sp at %u (UF %p)\n", + table->name, hook, origptr, + get_entry(table_base, private->underflow[hook])); do { const struct ipt_entry_target *t; const struct xt_entry_match *ematch; IP_NF_ASSERT(e); - IP_NF_ASSERT(back); if (!ip_packet_match(ip, indev, outdev, &e->ip, mtpar.fragoff)) { no_match: @@ -403,17 +406,28 @@ ipt_do_table(struct sk_buff *skb, verdict = (unsigned)(-v) - 1; break; } - e = back; - back = get_entry(table_base, back->comefrom); + if (*stackptr == 0) { + e = get_entry(table_base, + private->underflow[hook]); + pr_devel("Underflow (this is normal) " + "to %p\n", e); + } else { + e = jumpstack[--*stackptr]; + pr_devel("Pulled %p out from pos %u\n", + e, *stackptr); + e = ipt_next_entry(e); + } continue; } if (table_base + v != ipt_next_entry(e) && !(e->ip.flags & IPT_F_GOTO)) { - /* Save old back ptr in next entry */ - struct ipt_entry *next = ipt_next_entry(e); - next->comefrom = (void *)back - table_base; - /* set back pointer to next entry */ - back = next; + if (*stackptr >= private->stacksize) { + verdict = NF_DROP; + break; + } + jumpstack[(*stackptr)++] = e; + pr_devel("Pushed %p into pos %u\n", + e, *stackptr - 1); } e = get_entry(table_base, v); @@ -426,18 +440,7 @@ ipt_do_table(struct sk_buff *skb, tgpar.targinfo = t->data; -#ifdef CONFIG_NETFILTER_DEBUG - tb_comefrom = 0xeeeeeeec; -#endif verdict = t->u.kernel.target->target(skb, &tgpar); -#ifdef CONFIG_NETFILTER_DEBUG - if (tb_comefrom != 0xeeeeeeec && verdict == IPT_CONTINUE) { - printk("Target %s reentered!\n", - t->u.kernel.target->name); - verdict = NF_DROP; - } - tb_comefrom = 0x57acc001; -#endif /* Target might have changed stuff. */ ip = ip_hdr(skb); if (verdict == IPT_CONTINUE) @@ -447,7 +450,9 @@ ipt_do_table(struct sk_buff *skb, break; } while (!hotdrop); xt_info_rdunlock_bh(); - + pr_devel("Exiting %s; resetting sp from %u to %u\n", + __func__, *stackptr, origptr); + *stackptr = origptr; #ifdef DEBUG_ALLOW_ALL return NF_ACCEPT; #else @@ -455,8 +460,6 @@ ipt_do_table(struct sk_buff *skb, return NF_DROP; else return verdict; #endif - -#undef tb_comefrom } /* Figures out from what hook each rule can be called: returns 0 if @@ -838,6 +841,9 @@ translate_table(struct net *net, struct xt_table_info *newinfo, void *entry0, if (ret != 0) return ret; ++i; + if (strcmp(ipt_get_target(iter)->u.user.name, + XT_ERROR_TARGET) == 0) + ++newinfo->stacksize; } if (i != repl->num_entries) { @@ -2086,8 +2092,7 @@ struct xt_table *ipt_register_table(struct net *net, { int ret; struct xt_table_info *newinfo; - struct xt_table_info bootstrap - = { 0, 0, 0, { 0 }, { 0 }, { } }; + struct xt_table_info bootstrap = {0}; void *loc_cpu_entry; struct xt_table *new_table; diff --git a/net/ipv6/netfilter/ip6_tables.c b/net/ipv6/netfilter/ip6_tables.c index f2b815e7232..2a2770bcd64 100644 --- a/net/ipv6/netfilter/ip6_tables.c +++ b/net/ipv6/netfilter/ip6_tables.c @@ -351,15 +351,14 @@ ip6t_do_table(struct sk_buff *skb, const struct net_device *out, struct xt_table *table) { -#define tb_comefrom ((struct ip6t_entry *)table_base)->comefrom - static const char nulldevname[IFNAMSIZ] __attribute__((aligned(sizeof(long)))); bool hotdrop = false; /* Initializing verdict to NF_DROP keeps gcc happy. */ unsigned int verdict = NF_DROP; const char *indev, *outdev; const void *table_base; - struct ip6t_entry *e, *back; + struct ip6t_entry *e, **jumpstack; + unsigned int *stackptr, origptr, cpu; const struct xt_table_info *private; struct xt_match_param mtpar; struct xt_target_param tgpar; @@ -383,19 +382,19 @@ ip6t_do_table(struct sk_buff *skb, xt_info_rdlock_bh(); private = table->private; - table_base = private->entries[smp_processor_id()]; + cpu = smp_processor_id(); + table_base = private->entries[cpu]; + jumpstack = (struct ip6t_entry **)private->jumpstack[cpu]; + stackptr = &private->stackptr[cpu]; + origptr = *stackptr; e = get_entry(table_base, private->hook_entry[hook]); - /* For return from builtin chain */ - back = get_entry(table_base, private->underflow[hook]); - do { const struct ip6t_entry_target *t; const struct xt_entry_match *ematch; IP_NF_ASSERT(e); - IP_NF_ASSERT(back); if (!ip6_packet_match(skb, indev, outdev, &e->ipv6, &mtpar.thoff, &mtpar.fragoff, &hotdrop)) { no_match: @@ -432,17 +431,20 @@ ip6t_do_table(struct sk_buff *skb, verdict = (unsigned)(-v) - 1; break; } - e = back; - back = get_entry(table_base, back->comefrom); + if (*stackptr == 0) + e = get_entry(table_base, + private->underflow[hook]); + else + e = ip6t_next_entry(jumpstack[--*stackptr]); continue; } if (table_base + v != ip6t_next_entry(e) && !(e->ipv6.flags & IP6T_F_GOTO)) { - /* Save old back ptr in next entry */ - struct ip6t_entry *next = ip6t_next_entry(e); - next->comefrom = (void *)back - table_base; - /* set back pointer to next entry */ - back = next; + if (*stackptr >= private->stacksize) { + verdict = NF_DROP; + break; + } + jumpstack[(*stackptr)++] = e; } e = get_entry(table_base, v); @@ -454,19 +456,7 @@ ip6t_do_table(struct sk_buff *skb, tgpar.target = t->u.kernel.target; tgpar.targinfo = t->data; -#ifdef CONFIG_NETFILTER_DEBUG - tb_comefrom = 0xeeeeeeec; -#endif verdict = t->u.kernel.target->target(skb, &tgpar); - -#ifdef CONFIG_NETFILTER_DEBUG - if (tb_comefrom != 0xeeeeeeec && verdict == IP6T_CONTINUE) { - printk("Target %s reentered!\n", - t->u.kernel.target->name); - verdict = NF_DROP; - } - tb_comefrom = 0x57acc001; -#endif if (verdict == IP6T_CONTINUE) e = ip6t_next_entry(e); else @@ -474,10 +464,8 @@ ip6t_do_table(struct sk_buff *skb, break; } while (!hotdrop); -#ifdef CONFIG_NETFILTER_DEBUG - tb_comefrom = NETFILTER_LINK_POISON; -#endif xt_info_rdunlock_bh(); + *stackptr = origptr; #ifdef DEBUG_ALLOW_ALL return NF_ACCEPT; @@ -486,8 +474,6 @@ ip6t_do_table(struct sk_buff *skb, return NF_DROP; else return verdict; #endif - -#undef tb_comefrom } /* Figures out from what hook each rule can be called: returns 0 if @@ -869,6 +855,9 @@ translate_table(struct net *net, struct xt_table_info *newinfo, void *entry0, if (ret != 0) return ret; ++i; + if (strcmp(ip6t_get_target(iter)->u.user.name, + XT_ERROR_TARGET) == 0) + ++newinfo->stacksize; } if (i != repl->num_entries) { @@ -2120,8 +2109,7 @@ struct xt_table *ip6t_register_table(struct net *net, { int ret; struct xt_table_info *newinfo; - struct xt_table_info bootstrap - = { 0, 0, 0, { 0 }, { 0 }, { } }; + struct xt_table_info bootstrap = {0}; void *loc_cpu_entry; struct xt_table *new_table; diff --git a/net/netfilter/x_tables.c b/net/netfilter/x_tables.c index 8e23d8f6845..edde5c60289 100644 --- a/net/netfilter/x_tables.c +++ b/net/netfilter/x_tables.c @@ -62,6 +62,9 @@ static const char *const xt_prefix[NFPROTO_NUMPROTO] = { [NFPROTO_IPV6] = "ip6", }; +/* Allow this many total (re)entries. */ +static const unsigned int xt_jumpstack_multiplier = 2; + /* Registration hooks for targets. */ int xt_register_target(struct xt_target *target) @@ -680,6 +683,26 @@ void xt_free_table_info(struct xt_table_info *info) else vfree(info->entries[cpu]); } + + if (info->jumpstack != NULL) { + if (sizeof(void *) * info->stacksize > PAGE_SIZE) { + for_each_possible_cpu(cpu) + vfree(info->jumpstack[cpu]); + } else { + for_each_possible_cpu(cpu) + kfree(info->jumpstack[cpu]); + } + } + + if (sizeof(void **) * nr_cpu_ids > PAGE_SIZE) + vfree(info->jumpstack); + else + kfree(info->jumpstack); + if (sizeof(unsigned int) * nr_cpu_ids > PAGE_SIZE) + vfree(info->stackptr); + else + kfree(info->stackptr); + kfree(info); } EXPORT_SYMBOL(xt_free_table_info); @@ -724,6 +747,49 @@ EXPORT_SYMBOL_GPL(xt_compat_unlock); DEFINE_PER_CPU(struct xt_info_lock, xt_info_locks); EXPORT_PER_CPU_SYMBOL_GPL(xt_info_locks); +static int xt_jumpstack_alloc(struct xt_table_info *i) +{ + unsigned int size; + int cpu; + + size = sizeof(unsigned int) * nr_cpu_ids; + if (size > PAGE_SIZE) + i->stackptr = vmalloc(size); + else + i->stackptr = kmalloc(size, GFP_KERNEL); + if (i->stackptr == NULL) + return -ENOMEM; + memset(i->stackptr, 0, size); + + size = sizeof(void **) * nr_cpu_ids; + if (size > PAGE_SIZE) + i->jumpstack = vmalloc(size); + else + i->jumpstack = kmalloc(size, GFP_KERNEL); + if (i->jumpstack == NULL) + return -ENOMEM; + memset(i->jumpstack, 0, size); + + i->stacksize *= xt_jumpstack_multiplier; + size = sizeof(void *) * i->stacksize; + for_each_possible_cpu(cpu) { + if (size > PAGE_SIZE) + i->jumpstack[cpu] = vmalloc_node(size, + cpu_to_node(cpu)); + else + i->jumpstack[cpu] = kmalloc_node(size, + GFP_KERNEL, cpu_to_node(cpu)); + if (i->jumpstack[cpu] == NULL) + /* + * Freeing will be done later on by the callers. The + * chain is: xt_replace_table -> __do_replace -> + * do_replace -> xt_free_table_info. + */ + return -ENOMEM; + } + + return 0; +} struct xt_table_info * xt_replace_table(struct xt_table *table, @@ -732,6 +798,7 @@ xt_replace_table(struct xt_table *table, int *error) { struct xt_table_info *private; + int ret; /* Do the substitution. */ local_bh_disable(); @@ -746,6 +813,12 @@ xt_replace_table(struct xt_table *table, return NULL; } + ret = xt_jumpstack_alloc(newinfo); + if (ret < 0) { + *error = ret; + return NULL; + } + table->private = newinfo; newinfo->initial_entries = private->initial_entries; @@ -770,6 +843,10 @@ struct xt_table *xt_register_table(struct net *net, struct xt_table_info *private; struct xt_table *t, *table; + ret = xt_jumpstack_alloc(newinfo); + if (ret < 0) + return ERR_PTR(ret); + /* Don't add one object to multiple lists. */ table = kmemdup(input_table, sizeof(struct xt_table), GFP_KERNEL); if (!table) { -- cgit v1.2.3 From 88751275b8e867d756e4f86ae92afe0232de129f Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Mon, 19 Apr 2010 05:07:33 +0000 Subject: rps: shortcut net_rps_action() net_rps_action() is a bit expensive on NR_CPUS=64..4096 kernels, even if RPS is not active. Tom Herbert used two bitmasks to hold information needed to send IPI, but a single LIFO list seems more appropriate. Move all RPS logic into net_rps_action() to cleanup net_rx_action() code (remove two ifdefs) Move rps_remote_softirq_cpus into softnet_data to share its first cache line, filling an existing hole. In a future patch, we could call net_rps_action() from process_backlog() to make sure we send IPI before handling this cpu backlog. Signed-off-by: Eric Dumazet Signed-off-by: David S. Miller --- include/linux/netdevice.h | 9 ++++-- net/core/dev.c | 79 +++++++++++++++++++---------------------------- 2 files changed, 38 insertions(+), 50 deletions(-) (limited to 'include/linux') diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index 649a0252686..83ab3da149a 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -1381,17 +1381,20 @@ static inline int unregister_gifconf(unsigned int family) } /* - * Incoming packets are placed on per-cpu queues so that - * no locking is needed. + * Incoming packets are placed on per-cpu queues */ struct softnet_data { struct Qdisc *output_queue; struct list_head poll_list; struct sk_buff *completion_queue; - /* Elements below can be accessed between CPUs for RPS */ #ifdef CONFIG_RPS + struct softnet_data *rps_ipi_list; + + /* Elements below can be accessed between CPUs for RPS */ struct call_single_data csd ____cacheline_aligned_in_smp; + struct softnet_data *rps_ipi_next; + unsigned int cpu; unsigned int input_queue_head; #endif struct sk_buff_head input_pkt_queue; diff --git a/net/core/dev.c b/net/core/dev.c index 8eb50e2292f..05a2b294906 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -2345,21 +2345,6 @@ done: return cpu; } -/* - * This structure holds the per-CPU mask of CPUs for which IPIs are scheduled - * to be sent to kick remote softirq processing. There are two masks since - * the sending of IPIs must be done with interrupts enabled. The select field - * indicates the current mask that enqueue_backlog uses to schedule IPIs. - * select is flipped before net_rps_action is called while still under lock, - * net_rps_action then uses the non-selected mask to send the IPIs and clears - * it without conflicting with enqueue_backlog operation. - */ -struct rps_remote_softirq_cpus { - cpumask_t mask[2]; - int select; -}; -static DEFINE_PER_CPU(struct rps_remote_softirq_cpus, rps_remote_softirq_cpus); - /* Called from hardirq (IPI) context */ static void trigger_softirq(void *data) { @@ -2402,10 +2387,12 @@ enqueue: if (napi_schedule_prep(&queue->backlog)) { #ifdef CONFIG_RPS if (cpu != smp_processor_id()) { - struct rps_remote_softirq_cpus *rcpus = - &__get_cpu_var(rps_remote_softirq_cpus); + struct softnet_data *myqueue; + + myqueue = &__get_cpu_var(softnet_data); + queue->rps_ipi_next = myqueue->rps_ipi_list; + myqueue->rps_ipi_list = queue; - cpu_set(cpu, rcpus->mask[rcpus->select]); __raise_softirq_irqoff(NET_RX_SOFTIRQ); goto enqueue; } @@ -2910,7 +2897,9 @@ int netif_receive_skb(struct sk_buff *skb) } EXPORT_SYMBOL(netif_receive_skb); -/* Network device is going away, flush any packets still pending */ +/* Network device is going away, flush any packets still pending + * Called with irqs disabled. + */ static void flush_backlog(void *arg) { struct net_device *dev = arg; @@ -3338,24 +3327,33 @@ void netif_napi_del(struct napi_struct *napi) } EXPORT_SYMBOL(netif_napi_del); -#ifdef CONFIG_RPS /* - * net_rps_action sends any pending IPI's for rps. This is only called from - * softirq and interrupts must be enabled. + * net_rps_action sends any pending IPI's for rps. + * Note: called with local irq disabled, but exits with local irq enabled. */ -static void net_rps_action(cpumask_t *mask) +static void net_rps_action(void) { - int cpu; +#ifdef CONFIG_RPS + struct softnet_data *locqueue = &__get_cpu_var(softnet_data); + struct softnet_data *remqueue = locqueue->rps_ipi_list; - /* Send pending IPI's to kick RPS processing on remote cpus. */ - for_each_cpu_mask_nr(cpu, *mask) { - struct softnet_data *queue = &per_cpu(softnet_data, cpu); - if (cpu_online(cpu)) - __smp_call_function_single(cpu, &queue->csd, 0); - } - cpus_clear(*mask); -} + if (remqueue) { + locqueue->rps_ipi_list = NULL; + + local_irq_enable(); + + /* Send pending IPI's to kick RPS processing on remote cpus. */ + while (remqueue) { + struct softnet_data *next = remqueue->rps_ipi_next; + if (cpu_online(remqueue->cpu)) + __smp_call_function_single(remqueue->cpu, + &remqueue->csd, 0); + remqueue = next; + } + } else #endif + local_irq_enable(); +} static void net_rx_action(struct softirq_action *h) { @@ -3363,10 +3361,6 @@ static void net_rx_action(struct softirq_action *h) unsigned long time_limit = jiffies + 2; int budget = netdev_budget; void *have; -#ifdef CONFIG_RPS - int select; - struct rps_remote_softirq_cpus *rcpus; -#endif local_irq_disable(); @@ -3429,17 +3423,7 @@ static void net_rx_action(struct softirq_action *h) netpoll_poll_unlock(have); } out: -#ifdef CONFIG_RPS - rcpus = &__get_cpu_var(rps_remote_softirq_cpus); - select = rcpus->select; - rcpus->select ^= 1; - - local_irq_enable(); - - net_rps_action(&rcpus->mask[select]); -#else - local_irq_enable(); -#endif + net_rps_action(); #ifdef CONFIG_NET_DMA /* @@ -5839,6 +5823,7 @@ static int __init net_dev_init(void) queue->csd.func = trigger_softirq; queue->csd.info = queue; queue->csd.flags = 0; + queue->cpu = i; #endif queue->backlog.poll = process_backlog; -- cgit v1.2.3 From e36fa2f7e92f25aab2e3d787dcfe3590817f19d3 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Mon, 19 Apr 2010 21:17:14 +0000 Subject: rps: cleanups struct softnet_data holds many queues, so consistent use "sd" name instead of "queue" is better. Adds a rps_ipi_queued() helper to cleanup enqueue_to_backlog() Adds a _and_irq_disable suffix to net_rps_action() name, as David suggested. incr_input_queue_head() becomes input_queue_head_incr() Signed-off-by: Eric Dumazet Signed-off-by: David S. Miller --- include/linux/netdevice.h | 4 +- net/core/dev.c | 149 +++++++++++++++++++++++++--------------------- 2 files changed, 82 insertions(+), 71 deletions(-) (limited to 'include/linux') diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index 83ab3da149a..3c5ed5f5274 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -1401,10 +1401,10 @@ struct softnet_data { struct napi_struct backlog; }; -static inline void incr_input_queue_head(struct softnet_data *queue) +static inline void input_queue_head_incr(struct softnet_data *sd) { #ifdef CONFIG_RPS - queue->input_queue_head++; + sd->input_queue_head++; #endif } diff --git a/net/core/dev.c b/net/core/dev.c index 05a2b294906..7f5755b0a57 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -208,17 +208,17 @@ static inline struct hlist_head *dev_index_hash(struct net *net, int ifindex) return &net->dev_index_head[ifindex & (NETDEV_HASHENTRIES - 1)]; } -static inline void rps_lock(struct softnet_data *queue) +static inline void rps_lock(struct softnet_data *sd) { #ifdef CONFIG_RPS - spin_lock(&queue->input_pkt_queue.lock); + spin_lock(&sd->input_pkt_queue.lock); #endif } -static inline void rps_unlock(struct softnet_data *queue) +static inline void rps_unlock(struct softnet_data *sd) { #ifdef CONFIG_RPS - spin_unlock(&queue->input_pkt_queue.lock); + spin_unlock(&sd->input_pkt_queue.lock); #endif } @@ -2346,14 +2346,37 @@ done: } /* Called from hardirq (IPI) context */ -static void trigger_softirq(void *data) +static void rps_trigger_softirq(void *data) { - struct softnet_data *queue = data; - __napi_schedule(&queue->backlog); + struct softnet_data *sd = data; + + __napi_schedule(&sd->backlog); __get_cpu_var(netdev_rx_stat).received_rps++; } + #endif /* CONFIG_RPS */ +/* + * Check if this softnet_data structure is another cpu one + * If yes, queue it to our IPI list and return 1 + * If no, return 0 + */ +static int rps_ipi_queued(struct softnet_data *sd) +{ +#ifdef CONFIG_RPS + struct softnet_data *mysd = &__get_cpu_var(softnet_data); + + if (sd != mysd) { + sd->rps_ipi_next = mysd->rps_ipi_list; + mysd->rps_ipi_list = sd; + + __raise_softirq_irqoff(NET_RX_SOFTIRQ); + return 1; + } +#endif /* CONFIG_RPS */ + return 0; +} + /* * enqueue_to_backlog is called to queue an skb to a per CPU backlog * queue (may be a remote CPU queue). @@ -2361,48 +2384,36 @@ static void trigger_softirq(void *data) static int enqueue_to_backlog(struct sk_buff *skb, int cpu, unsigned int *qtail) { - struct softnet_data *queue; + struct softnet_data *sd; unsigned long flags; - queue = &per_cpu(softnet_data, cpu); + sd = &per_cpu(softnet_data, cpu); local_irq_save(flags); __get_cpu_var(netdev_rx_stat).total++; - rps_lock(queue); - if (queue->input_pkt_queue.qlen <= netdev_max_backlog) { - if (queue->input_pkt_queue.qlen) { + rps_lock(sd); + if (sd->input_pkt_queue.qlen <= netdev_max_backlog) { + if (sd->input_pkt_queue.qlen) { enqueue: - __skb_queue_tail(&queue->input_pkt_queue, skb); + __skb_queue_tail(&sd->input_pkt_queue, skb); #ifdef CONFIG_RPS - *qtail = queue->input_queue_head + - queue->input_pkt_queue.qlen; + *qtail = sd->input_queue_head + sd->input_pkt_queue.qlen; #endif - rps_unlock(queue); + rps_unlock(sd); local_irq_restore(flags); return NET_RX_SUCCESS; } /* Schedule NAPI for backlog device */ - if (napi_schedule_prep(&queue->backlog)) { -#ifdef CONFIG_RPS - if (cpu != smp_processor_id()) { - struct softnet_data *myqueue; - - myqueue = &__get_cpu_var(softnet_data); - queue->rps_ipi_next = myqueue->rps_ipi_list; - myqueue->rps_ipi_list = queue; - - __raise_softirq_irqoff(NET_RX_SOFTIRQ); - goto enqueue; - } -#endif - __napi_schedule(&queue->backlog); + if (napi_schedule_prep(&sd->backlog)) { + if (!rps_ipi_queued(sd)) + __napi_schedule(&sd->backlog); } goto enqueue; } - rps_unlock(queue); + rps_unlock(sd); __get_cpu_var(netdev_rx_stat).dropped++; local_irq_restore(flags); @@ -2903,17 +2914,17 @@ EXPORT_SYMBOL(netif_receive_skb); static void flush_backlog(void *arg) { struct net_device *dev = arg; - struct softnet_data *queue = &__get_cpu_var(softnet_data); + struct softnet_data *sd = &__get_cpu_var(softnet_data); struct sk_buff *skb, *tmp; - rps_lock(queue); - skb_queue_walk_safe(&queue->input_pkt_queue, skb, tmp) + rps_lock(sd); + skb_queue_walk_safe(&sd->input_pkt_queue, skb, tmp) if (skb->dev == dev) { - __skb_unlink(skb, &queue->input_pkt_queue); + __skb_unlink(skb, &sd->input_pkt_queue); kfree_skb(skb); - incr_input_queue_head(queue); + input_queue_head_incr(sd); } - rps_unlock(queue); + rps_unlock(sd); } static int napi_gro_complete(struct sk_buff *skb) @@ -3219,23 +3230,23 @@ EXPORT_SYMBOL(napi_gro_frags); static int process_backlog(struct napi_struct *napi, int quota) { int work = 0; - struct softnet_data *queue = &__get_cpu_var(softnet_data); + struct softnet_data *sd = &__get_cpu_var(softnet_data); napi->weight = weight_p; do { struct sk_buff *skb; local_irq_disable(); - rps_lock(queue); - skb = __skb_dequeue(&queue->input_pkt_queue); + rps_lock(sd); + skb = __skb_dequeue(&sd->input_pkt_queue); if (!skb) { __napi_complete(napi); - rps_unlock(queue); + rps_unlock(sd); local_irq_enable(); break; } - incr_input_queue_head(queue); - rps_unlock(queue); + input_queue_head_incr(sd); + rps_unlock(sd); local_irq_enable(); __netif_receive_skb(skb); @@ -3331,24 +3342,25 @@ EXPORT_SYMBOL(netif_napi_del); * net_rps_action sends any pending IPI's for rps. * Note: called with local irq disabled, but exits with local irq enabled. */ -static void net_rps_action(void) +static void net_rps_action_and_irq_disable(void) { #ifdef CONFIG_RPS - struct softnet_data *locqueue = &__get_cpu_var(softnet_data); - struct softnet_data *remqueue = locqueue->rps_ipi_list; + struct softnet_data *sd = &__get_cpu_var(softnet_data); + struct softnet_data *remsd = sd->rps_ipi_list; - if (remqueue) { - locqueue->rps_ipi_list = NULL; + if (remsd) { + sd->rps_ipi_list = NULL; local_irq_enable(); /* Send pending IPI's to kick RPS processing on remote cpus. */ - while (remqueue) { - struct softnet_data *next = remqueue->rps_ipi_next; - if (cpu_online(remqueue->cpu)) - __smp_call_function_single(remqueue->cpu, - &remqueue->csd, 0); - remqueue = next; + while (remsd) { + struct softnet_data *next = remsd->rps_ipi_next; + + if (cpu_online(remsd->cpu)) + __smp_call_function_single(remsd->cpu, + &remsd->csd, 0); + remsd = next; } } else #endif @@ -3423,7 +3435,7 @@ static void net_rx_action(struct softirq_action *h) netpoll_poll_unlock(have); } out: - net_rps_action(); + net_rps_action_and_irq_disable(); #ifdef CONFIG_NET_DMA /* @@ -5595,7 +5607,7 @@ static int dev_cpu_callback(struct notifier_block *nfb, /* Process offline CPU's input_pkt_queue */ while ((skb = __skb_dequeue(&oldsd->input_pkt_queue))) { netif_rx(skb); - incr_input_queue_head(oldsd); + input_queue_head_incr(oldsd); } return NOTIFY_OK; @@ -5812,24 +5824,23 @@ static int __init net_dev_init(void) */ for_each_possible_cpu(i) { - struct softnet_data *queue; + struct softnet_data *sd = &per_cpu(softnet_data, i); - queue = &per_cpu(softnet_data, i); - skb_queue_head_init(&queue->input_pkt_queue); - queue->completion_queue = NULL; - INIT_LIST_HEAD(&queue->poll_list); + skb_queue_head_init(&sd->input_pkt_queue); + sd->completion_queue = NULL; + INIT_LIST_HEAD(&sd->poll_list); #ifdef CONFIG_RPS - queue->csd.func = trigger_softirq; - queue->csd.info = queue; - queue->csd.flags = 0; - queue->cpu = i; + sd->csd.func = rps_trigger_softirq; + sd->csd.info = sd; + sd->csd.flags = 0; + sd->cpu = i; #endif - queue->backlog.poll = process_backlog; - queue->backlog.weight = weight_p; - queue->backlog.gro_list = NULL; - queue->backlog.gro_count = 0; + sd->backlog.poll = process_backlog; + sd->backlog.weight = weight_p; + sd->backlog.gro_list = NULL; + sd->backlog.gro_count = 0; } dev_boot_phase = 0; -- cgit v1.2.3 From 22265a5c3c103cf8c50be62e6c90d045eb649e6d Mon Sep 17 00:00:00 2001 From: Patrick McHardy Date: Tue, 20 Apr 2010 15:07:32 +0200 Subject: netfilter: xt_TEE: resolve oif using netdevice notifiers Replace the runtime oif name resolving by netdevice notifier based resolving. When an oif is given, a netdevice notifier is registered to resolve the name on NETDEV_REGISTER or NETDEV_CHANGE and unresolve it again on NETDEV_UNREGISTER or NETDEV_CHANGE to a different name. Signed-off-by: Patrick McHardy --- include/linux/netfilter/xt_TEE.h | 3 ++ net/netfilter/xt_TEE.c | 103 ++++++++++++++++++++++++++++++--------- 2 files changed, 83 insertions(+), 23 deletions(-) (limited to 'include/linux') diff --git a/include/linux/netfilter/xt_TEE.h b/include/linux/netfilter/xt_TEE.h index 55d4a501152..5c21d5c829a 100644 --- a/include/linux/netfilter/xt_TEE.h +++ b/include/linux/netfilter/xt_TEE.h @@ -4,6 +4,9 @@ struct xt_tee_tginfo { union nf_inet_addr gw; char oif[16]; + + /* used internally by the kernel */ + struct xt_tee_priv *priv __attribute__((aligned(8))); }; #endif /* _XT_TEE_TARGET_H */ diff --git a/net/netfilter/xt_TEE.c b/net/netfilter/xt_TEE.c index 842e7012eca..49da6c05f4e 100644 --- a/net/netfilter/xt_TEE.c +++ b/net/netfilter/xt_TEE.c @@ -15,6 +15,7 @@ #include #include #include +#include #include #include #include @@ -32,6 +33,12 @@ # define WITH_IPV6 1 #endif +struct xt_tee_priv { + struct notifier_block notifier; + struct xt_tee_tginfo *tginfo; + int oif; +}; + static const union nf_inet_addr tee_zero_address; static DEFINE_PER_CPU(bool, tee_active); @@ -49,20 +56,6 @@ static struct net *pick_net(struct sk_buff *skb) return &init_net; } -static bool tee_tg_route_oif(struct flowi *f, struct net *net, - const struct xt_tee_tginfo *info) -{ - const struct net_device *dev; - - if (*info->oif != '\0') - return true; - dev = dev_get_by_name(net, info->oif); - if (dev == NULL) - return false; - f->oif = dev->ifindex; - return true; -} - static bool tee_tg_route4(struct sk_buff *skb, const struct xt_tee_tginfo *info) { @@ -72,8 +65,11 @@ tee_tg_route4(struct sk_buff *skb, const struct xt_tee_tginfo *info) struct flowi fl; memset(&fl, 0, sizeof(fl)); - if (!tee_tg_route_oif(&fl, net, info)) - return false; + if (info->priv) { + if (info->priv->oif == -1) + return false; + fl.oif = info->priv->oif; + } fl.nl_u.ip4_u.daddr = info->gw.ip; fl.nl_u.ip4_u.tos = RT_TOS(iph->tos); fl.nl_u.ip4_u.scope = RT_SCOPE_UNIVERSE; @@ -149,8 +145,11 @@ tee_tg_route6(struct sk_buff *skb, const struct xt_tee_tginfo *info) struct flowi fl; memset(&fl, 0, sizeof(fl)); - if (!tee_tg_route_oif(&fl, net, info)) - return false; + if (info->priv) { + if (info->priv->oif == -1) + return false; + fl.oif = info->priv->oif; + } fl.nl_u.ip6_u.daddr = info->gw.in6; fl.nl_u.ip6_u.flowlabel = ((iph->flow_lbl[0] & 0xF) << 16) | (iph->flow_lbl[1] << 8) | iph->flow_lbl[2]; @@ -198,15 +197,71 @@ tee_tg6(struct sk_buff *skb, const struct xt_target_param *par) } #endif /* WITH_IPV6 */ +static int tee_netdev_event(struct notifier_block *this, unsigned long event, + void *ptr) +{ + struct net_device *dev = ptr; + struct xt_tee_priv *priv; + + priv = container_of(this, struct xt_tee_priv, notifier); + switch (event) { + case NETDEV_REGISTER: + if (!strcmp(dev->name, priv->tginfo->oif)) + priv->oif = dev->ifindex; + break; + case NETDEV_UNREGISTER: + if (dev->ifindex == priv->oif) + priv->oif = -1; + break; + case NETDEV_CHANGENAME: + if (!strcmp(dev->name, priv->tginfo->oif)) + priv->oif = dev->ifindex; + else if (dev->ifindex == priv->oif) + priv->oif = -1; + break; + } + + return NOTIFY_DONE; +} + static int tee_tg_check(const struct xt_tgchk_param *par) { - const struct xt_tee_tginfo *info = par->targinfo; + struct xt_tee_tginfo *info = par->targinfo; + struct xt_tee_priv *priv; - if (info->oif[sizeof(info->oif)-1] != '\0') - return -EINVAL; /* 0.0.0.0 and :: not allowed */ - return (memcmp(&info->gw, &tee_zero_address, - sizeof(tee_zero_address)) == 0) ? -EINVAL : 0; + if (memcmp(&info->gw, &tee_zero_address, + sizeof(tee_zero_address)) == 0) + return -EINVAL; + + if (info->oif[0]) { + if (info->oif[sizeof(info->oif)-1] != '\0') + return -EINVAL; + + priv = kzalloc(sizeof(*priv), GFP_KERNEL); + if (priv == NULL) + return -ENOMEM; + + priv->tginfo = info; + priv->oif = -1; + priv->notifier.notifier_call = tee_netdev_event; + info->priv = priv; + + register_netdevice_notifier(&priv->notifier); + } else + info->priv = NULL; + + return 0; +} + +static void tee_tg_destroy(const struct xt_tgdtor_param *par) +{ + struct xt_tee_tginfo *info = par->targinfo; + + if (info->priv) { + unregister_netdevice_notifier(&info->priv->notifier); + kfree(info->priv); + } } static struct xt_target tee_tg_reg[] __read_mostly = { @@ -217,6 +272,7 @@ static struct xt_target tee_tg_reg[] __read_mostly = { .target = tee_tg4, .targetsize = sizeof(struct xt_tee_tginfo), .checkentry = tee_tg_check, + .destroy = tee_tg_destroy, .me = THIS_MODULE, }, #ifdef WITH_IPV6 @@ -227,6 +283,7 @@ static struct xt_target tee_tg_reg[] __read_mostly = { .target = tee_tg6, .targetsize = sizeof(struct xt_tee_tginfo), .checkentry = tee_tg_check, + .destroy = tee_tg_destroy, .me = THIS_MODULE, }, #endif -- cgit v1.2.3 From 6c79bf0f2440fd250c8fce8d9b82fcf03d4e8350 Mon Sep 17 00:00:00 2001 From: Bart De Schuymer Date: Tue, 20 Apr 2010 16:22:01 +0200 Subject: netfilter: bridge-netfilter: fix refragmenting IP traffic encapsulated in PPPoE traffic The MTU for IP traffic encapsulated inside PPPoE traffic is smaller than the MTU of the Ethernet device (1500). Connection tracking gathers all IP packets and sometimes will refragment them in ip_fragment(). We then need to subtract the length of the encapsulating header from the mtu used in ip_fragment(). The check in br_nf_dev_queue_xmit() which determines if ip_fragment() has to be called is also updated for the PPPoE-encapsulated packets. nf_bridge_copy_header() is also updated to make sure the PPPoE data length field has the correct value. Signed-off-by: Bart De Schuymer Signed-off-by: Patrick McHardy --- include/linux/netfilter_bridge.h | 7 +++++++ net/bridge/br_netfilter.c | 2 +- net/ipv4/ip_output.c | 4 ++++ 3 files changed, 12 insertions(+), 1 deletion(-) (limited to 'include/linux') diff --git a/include/linux/netfilter_bridge.h b/include/linux/netfilter_bridge.h index ea0e44b9043..0ddd161f3b0 100644 --- a/include/linux/netfilter_bridge.h +++ b/include/linux/netfilter_bridge.h @@ -68,6 +68,13 @@ static inline unsigned int nf_bridge_encap_header_len(const struct sk_buff *skb) } } +static inline unsigned int nf_bridge_mtu_reduction(const struct sk_buff *skb) +{ + if (unlikely(skb->nf_bridge->mask & BRNF_PPPoE)) + return PPPOE_SES_HLEN; + return 0; +} + extern int br_handle_frame_finish(struct sk_buff *skb); /* Only used in br_device.c */ static inline int br_nf_pre_routing_finish_bridge_slow(struct sk_buff *skb) diff --git a/net/bridge/br_netfilter.c b/net/bridge/br_netfilter.c index 6b80ebc3766..93f80fefa49 100644 --- a/net/bridge/br_netfilter.c +++ b/net/bridge/br_netfilter.c @@ -745,7 +745,7 @@ static unsigned int br_nf_forward_arp(unsigned int hook, struct sk_buff *skb, static int br_nf_dev_queue_xmit(struct sk_buff *skb) { if (skb->nfct != NULL && skb->protocol == htons(ETH_P_IP) && - skb->len > skb->dev->mtu && + skb->len + nf_bridge_mtu_reduction(skb) > skb->dev->mtu && !skb_is_gso(skb)) return ip_fragment(skb, br_dev_queue_push_xmit); else diff --git a/net/ipv4/ip_output.c b/net/ipv4/ip_output.c index b0b2e3059f1..d979710684b 100644 --- a/net/ipv4/ip_output.c +++ b/net/ipv4/ip_output.c @@ -469,6 +469,10 @@ int ip_fragment(struct sk_buff *skb, int (*output)(struct sk_buff *)) hlen = iph->ihl * 4; mtu = dst_mtu(&rt->u.dst) - hlen; /* Size of data space */ +#ifdef CONFIG_BRIDGE_NETFILTER + if (skb->nf_bridge) + mtu -= nf_bridge_mtu_reduction(skb); +#endif IPCB(skb)->flags |= IPSKB_FRAG_COMPLETE; /* When frag_list is given, use it. First, check its validity: -- cgit v1.2.3 From f79d9bad37cb1e7ef23d60b1dd0b7859957ced9e Mon Sep 17 00:00:00 2001 From: Felix Fietkau Date: Mon, 19 Apr 2010 19:57:35 +0200 Subject: mac80211: add flags for STBC (Space-Time Block Coding) Signed-off-by: Felix Fietkau Signed-off-by: John W. Linville --- include/linux/ieee80211.h | 1 + include/net/mac80211.h | 4 ++++ 2 files changed, 5 insertions(+) (limited to 'include/linux') diff --git a/include/linux/ieee80211.h b/include/linux/ieee80211.h index 1252ba1fbff..97b2eae6a22 100644 --- a/include/linux/ieee80211.h +++ b/include/linux/ieee80211.h @@ -876,6 +876,7 @@ struct ieee80211_ht_cap { #define IEEE80211_HT_CAP_SGI_40 0x0040 #define IEEE80211_HT_CAP_TX_STBC 0x0080 #define IEEE80211_HT_CAP_RX_STBC 0x0300 +#define IEEE80211_HT_CAP_RX_STBC_SHIFT 8 #define IEEE80211_HT_CAP_DELAY_BA 0x0400 #define IEEE80211_HT_CAP_MAX_AMSDU 0x0800 #define IEEE80211_HT_CAP_DSSSCCK40 0x1000 diff --git a/include/net/mac80211.h b/include/net/mac80211.h index 0e2a1a9b2b6..7dc5a679039 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h @@ -275,6 +275,8 @@ struct ieee80211_bss_conf { * MLME command (internal to mac80211 to figure out whether to send TX * status to user space) * @IEEE80211_TX_CTL_LDPC: tells the driver to use LDPC for this frame + * @IEEE80211_TX_CTL_STBC: Enables Space-Time Block Coding (STBC) for this + * frame and selects the maximum number of streams that it can use. */ enum mac80211_tx_control_flags { IEEE80211_TX_CTL_REQ_TX_STATUS = BIT(0), @@ -299,6 +301,8 @@ enum mac80211_tx_control_flags { IEEE80211_TX_INTFL_HAS_RADIOTAP = BIT(20), IEEE80211_TX_INTFL_NL80211_FRAME_TX = BIT(21), IEEE80211_TX_CTL_LDPC = BIT(22), + IEEE80211_TX_CTL_STBC = BIT(23) | BIT(24), +#define IEEE80211_TX_CTL_STBC_SHIFT 23 }; /** -- cgit v1.2.3 From ccb7c7732e2ceb4e81a7806faf1670be9681ccd2 Mon Sep 17 00:00:00 2001 From: Rami Rosen Date: Tue, 20 Apr 2010 22:39:53 -0700 Subject: net: Remove two unnecessary exports (skbuff). There is no need to export skb_under_panic() and skb_over_panic() in skbuff.c, since these methods are used only in skbuff.c ; this patch removes these two exports. It also marks these functions as 'static' and removeS the extern declarations of them from include/linux/skbuff.h Signed-off-by: Rami Rosen Signed-off-by: David S. Miller --- include/linux/skbuff.h | 4 ---- net/core/skbuff.c | 6 ++---- 2 files changed, 2 insertions(+), 8 deletions(-) (limited to 'include/linux') diff --git a/include/linux/skbuff.h b/include/linux/skbuff.h index 38501d20650..82f5116a89e 100644 --- a/include/linux/skbuff.h +++ b/include/linux/skbuff.h @@ -470,10 +470,6 @@ extern int skb_cow_data(struct sk_buff *skb, int tailbits, struct sk_buff **trailer); extern int skb_pad(struct sk_buff *skb, int pad); #define dev_kfree_skb(a) consume_skb(a) -extern void skb_over_panic(struct sk_buff *skb, int len, - void *here); -extern void skb_under_panic(struct sk_buff *skb, int len, - void *here); extern int skb_append_datato_frags(struct sock *sk, struct sk_buff *skb, int getfrag(void *from, char *to, int offset, diff --git a/net/core/skbuff.c b/net/core/skbuff.c index bdea0efdf8c..4218ff49bf1 100644 --- a/net/core/skbuff.c +++ b/net/core/skbuff.c @@ -117,7 +117,7 @@ static const struct pipe_buf_operations sock_pipe_buf_ops = { * * Out of line support code for skb_put(). Not user callable. */ -void skb_over_panic(struct sk_buff *skb, int sz, void *here) +static void skb_over_panic(struct sk_buff *skb, int sz, void *here) { printk(KERN_EMERG "skb_over_panic: text:%p len:%d put:%d head:%p " "data:%p tail:%#lx end:%#lx dev:%s\n", @@ -126,7 +126,6 @@ void skb_over_panic(struct sk_buff *skb, int sz, void *here) skb->dev ? skb->dev->name : ""); BUG(); } -EXPORT_SYMBOL(skb_over_panic); /** * skb_under_panic - private function @@ -137,7 +136,7 @@ EXPORT_SYMBOL(skb_over_panic); * Out of line support code for skb_push(). Not user callable. */ -void skb_under_panic(struct sk_buff *skb, int sz, void *here) +static void skb_under_panic(struct sk_buff *skb, int sz, void *here) { printk(KERN_EMERG "skb_under_panic: text:%p len:%d put:%d head:%p " "data:%p tail:%#lx end:%#lx dev:%s\n", @@ -146,7 +145,6 @@ void skb_under_panic(struct sk_buff *skb, int sz, void *here) skb->dev ? skb->dev->name : ""); BUG(); } -EXPORT_SYMBOL(skb_under_panic); /* Allocate a new skbuff. We do this ourselves so we can fill in a few * 'private' fields and also do memory statistics to find all the -- cgit v1.2.3 From 989a2979205dd34269382b357e6d4b4b6956b889 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Wed, 14 Apr 2010 09:55:35 +0000 Subject: fasync: RCU and fine grained locking kill_fasync() uses a central rwlock, candidate for RCU conversion, to avoid cache line ping pongs on SMP. fasync_remove_entry() and fasync_add_entry() can disable IRQS on a short section instead during whole list scan. Use a spinlock per fasync_struct to synchronize kill_fasync_rcu() and fasync_{remove|add}_entry(). This spinlock is IRQ safe, so sock_fasync() doesnt need its own implementation and can use fasync_helper(), to reduce code size and complexity. We can remove __kill_fasync() direct use in net/socket.c, and rename it to kill_fasync_rcu(). Signed-off-by: Eric Dumazet Cc: Paul E. McKenney Cc: Lai Jiangshan Signed-off-by: David S. Miller --- fs/fcntl.c | 66 ++++++++++++++++++++++++++++++------------------ include/linux/fs.h | 12 ++++----- net/socket.c | 73 ++++++++---------------------------------------------- 3 files changed, 59 insertions(+), 92 deletions(-) (limited to 'include/linux') diff --git a/fs/fcntl.c b/fs/fcntl.c index 452d02f9075..0a140741b39 100644 --- a/fs/fcntl.c +++ b/fs/fcntl.c @@ -614,9 +614,15 @@ int send_sigurg(struct fown_struct *fown) return ret; } -static DEFINE_RWLOCK(fasync_lock); +static DEFINE_SPINLOCK(fasync_lock); static struct kmem_cache *fasync_cache __read_mostly; +static void fasync_free_rcu(struct rcu_head *head) +{ + kmem_cache_free(fasync_cache, + container_of(head, struct fasync_struct, fa_rcu)); +} + /* * Remove a fasync entry. If successfully removed, return * positive and clear the FASYNC flag. If no entry exists, @@ -625,8 +631,6 @@ static struct kmem_cache *fasync_cache __read_mostly; * NOTE! It is very important that the FASYNC flag always * match the state "is the filp on a fasync list". * - * We always take the 'filp->f_lock', in since fasync_lock - * needs to be irq-safe. */ static int fasync_remove_entry(struct file *filp, struct fasync_struct **fapp) { @@ -634,17 +638,22 @@ static int fasync_remove_entry(struct file *filp, struct fasync_struct **fapp) int result = 0; spin_lock(&filp->f_lock); - write_lock_irq(&fasync_lock); + spin_lock(&fasync_lock); for (fp = fapp; (fa = *fp) != NULL; fp = &fa->fa_next) { if (fa->fa_file != filp) continue; + + spin_lock_irq(&fa->fa_lock); + fa->fa_file = NULL; + spin_unlock_irq(&fa->fa_lock); + *fp = fa->fa_next; - kmem_cache_free(fasync_cache, fa); + call_rcu(&fa->fa_rcu, fasync_free_rcu); filp->f_flags &= ~FASYNC; result = 1; break; } - write_unlock_irq(&fasync_lock); + spin_unlock(&fasync_lock); spin_unlock(&filp->f_lock); return result; } @@ -666,25 +675,30 @@ static int fasync_add_entry(int fd, struct file *filp, struct fasync_struct **fa return -ENOMEM; spin_lock(&filp->f_lock); - write_lock_irq(&fasync_lock); + spin_lock(&fasync_lock); for (fp = fapp; (fa = *fp) != NULL; fp = &fa->fa_next) { if (fa->fa_file != filp) continue; + + spin_lock_irq(&fa->fa_lock); fa->fa_fd = fd; + spin_unlock_irq(&fa->fa_lock); + kmem_cache_free(fasync_cache, new); goto out; } + spin_lock_init(&new->fa_lock); new->magic = FASYNC_MAGIC; new->fa_file = filp; new->fa_fd = fd; new->fa_next = *fapp; - *fapp = new; + rcu_assign_pointer(*fapp, new); result = 1; filp->f_flags |= FASYNC; out: - write_unlock_irq(&fasync_lock); + spin_unlock(&fasync_lock); spin_unlock(&filp->f_lock); return result; } @@ -704,37 +718,41 @@ int fasync_helper(int fd, struct file * filp, int on, struct fasync_struct **fap EXPORT_SYMBOL(fasync_helper); -void __kill_fasync(struct fasync_struct *fa, int sig, int band) +/* + * rcu_read_lock() is held + */ +static void kill_fasync_rcu(struct fasync_struct *fa, int sig, int band) { while (fa) { - struct fown_struct * fown; + struct fown_struct *fown; if (fa->magic != FASYNC_MAGIC) { printk(KERN_ERR "kill_fasync: bad magic number in " "fasync_struct!\n"); return; } - fown = &fa->fa_file->f_owner; - /* Don't send SIGURG to processes which have not set a - queued signum: SIGURG has its own default signalling - mechanism. */ - if (!(sig == SIGURG && fown->signum == 0)) - send_sigio(fown, fa->fa_fd, band); - fa = fa->fa_next; + spin_lock(&fa->fa_lock); + if (fa->fa_file) { + fown = &fa->fa_file->f_owner; + /* Don't send SIGURG to processes which have not set a + queued signum: SIGURG has its own default signalling + mechanism. */ + if (!(sig == SIGURG && fown->signum == 0)) + send_sigio(fown, fa->fa_fd, band); + } + spin_unlock(&fa->fa_lock); + fa = rcu_dereference(fa->fa_next); } } -EXPORT_SYMBOL(__kill_fasync); - void kill_fasync(struct fasync_struct **fp, int sig, int band) { /* First a quick test without locking: usually * the list is empty. */ if (*fp) { - read_lock(&fasync_lock); - /* reread *fp after obtaining the lock */ - __kill_fasync(*fp, sig, band); - read_unlock(&fasync_lock); + rcu_read_lock(); + kill_fasync_rcu(rcu_dereference(*fp), sig, band); + rcu_read_unlock(); } } EXPORT_SYMBOL(kill_fasync); diff --git a/include/linux/fs.h b/include/linux/fs.h index 39d57bc6cc7..018d382f6f9 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -1280,10 +1280,12 @@ static inline int lock_may_write(struct inode *inode, loff_t start, struct fasync_struct { - int magic; - int fa_fd; - struct fasync_struct *fa_next; /* singly linked list */ - struct file *fa_file; + spinlock_t fa_lock; + int magic; + int fa_fd; + struct fasync_struct *fa_next; /* singly linked list */ + struct file *fa_file; + struct rcu_head fa_rcu; }; #define FASYNC_MAGIC 0x4601 @@ -1292,8 +1294,6 @@ struct fasync_struct { extern int fasync_helper(int, struct file *, int, struct fasync_struct **); /* can be called from interrupts */ extern void kill_fasync(struct fasync_struct **, int, int); -/* only for net: no internal synchronization */ -extern void __kill_fasync(struct fasync_struct *, int, int); extern int __f_setown(struct file *filp, struct pid *, enum pid_type, int force); extern int f_setown(struct file *filp, unsigned long arg, int force); diff --git a/net/socket.c b/net/socket.c index 35bc198bbf6..9822081eab3 100644 --- a/net/socket.c +++ b/net/socket.c @@ -1067,78 +1067,27 @@ static int sock_close(struct inode *inode, struct file *filp) * 1. fasync_list is modified only under process context socket lock * i.e. under semaphore. * 2. fasync_list is used under read_lock(&sk->sk_callback_lock) - * or under socket lock. - * 3. fasync_list can be used from softirq context, so that - * modification under socket lock have to be enhanced with - * write_lock_bh(&sk->sk_callback_lock). - * --ANK (990710) + * or under socket lock */ static int sock_fasync(int fd, struct file *filp, int on) { - struct fasync_struct *fa, *fna = NULL, **prev; - struct socket *sock; - struct sock *sk; - - if (on) { - fna = kmalloc(sizeof(struct fasync_struct), GFP_KERNEL); - if (fna == NULL) - return -ENOMEM; - } - - sock = filp->private_data; + struct socket *sock = filp->private_data; + struct sock *sk = sock->sk; - sk = sock->sk; - if (sk == NULL) { - kfree(fna); + if (sk == NULL) return -EINVAL; - } lock_sock(sk); - spin_lock(&filp->f_lock); - if (on) - filp->f_flags |= FASYNC; - else - filp->f_flags &= ~FASYNC; - spin_unlock(&filp->f_lock); - - prev = &(sock->fasync_list); + fasync_helper(fd, filp, on, &sock->fasync_list); - for (fa = *prev; fa != NULL; prev = &fa->fa_next, fa = *prev) - if (fa->fa_file == filp) - break; - - if (on) { - if (fa != NULL) { - write_lock_bh(&sk->sk_callback_lock); - fa->fa_fd = fd; - write_unlock_bh(&sk->sk_callback_lock); - - kfree(fna); - goto out; - } - fna->fa_file = filp; - fna->fa_fd = fd; - fna->magic = FASYNC_MAGIC; - fna->fa_next = sock->fasync_list; - write_lock_bh(&sk->sk_callback_lock); - sock->fasync_list = fna; + if (!sock->fasync_list) + sock_reset_flag(sk, SOCK_FASYNC); + else sock_set_flag(sk, SOCK_FASYNC); - write_unlock_bh(&sk->sk_callback_lock); - } else { - if (fa != NULL) { - write_lock_bh(&sk->sk_callback_lock); - *prev = fa->fa_next; - if (!sock->fasync_list) - sock_reset_flag(sk, SOCK_FASYNC); - write_unlock_bh(&sk->sk_callback_lock); - kfree(fa); - } - } -out: - release_sock(sock->sk); + release_sock(sk); return 0; } @@ -1159,10 +1108,10 @@ int sock_wake_async(struct socket *sock, int how, int band) /* fall through */ case SOCK_WAKE_IO: call_kill: - __kill_fasync(sock->fasync_list, SIGIO, band); + kill_fasync(&sock->fasync_list, SIGIO, band); break; case SOCK_WAKE_URG: - __kill_fasync(sock->fasync_list, SIGURG, band); + kill_fasync(&sock->fasync_list, SIGURG, band); } return 0; } -- cgit v1.2.3 From a1aa8822d577c8714f8d343eea028befbab3da9d Mon Sep 17 00:00:00 2001 From: Richard Röjfors Date: Wed, 21 Apr 2010 16:33:29 -0700 Subject: ks8842: Add platform data for setting mac address MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This patch adds platform data to the ks8842 driver. Via the platform data a MAC address, to be used by the controller, can be passed. To ensure this MAC address is used, the MAC address is written after each hardware reset. Signed-off-by: Richard Röjfors Signed-off-by: David S. Miller --- drivers/net/ks8842.c | 53 +++++++++++++++++++++++++++++++++++++++----------- include/linux/ks8842.h | 34 ++++++++++++++++++++++++++++++++ 2 files changed, 76 insertions(+), 11 deletions(-) create mode 100644 include/linux/ks8842.h (limited to 'include/linux') diff --git a/drivers/net/ks8842.c b/drivers/net/ks8842.c index b91492f4e48..f852ab3ae9c 100644 --- a/drivers/net/ks8842.c +++ b/drivers/net/ks8842.c @@ -1,5 +1,5 @@ /* - * ks8842_main.c timberdale KS8842 ethernet driver + * ks8842.c timberdale KS8842 ethernet driver * Copyright (c) 2009 Intel Corporation * * This program is free software; you can redistribute it and/or modify @@ -28,6 +28,7 @@ #include #include #include +#include #define DRV_NAME "ks8842" @@ -304,6 +305,20 @@ static void ks8842_read_mac_addr(struct ks8842_adapter *adapter, u8 *dest) ks8842_write16(adapter, 39, mac, REG_MACAR3); } +static void ks8842_write_mac_addr(struct ks8842_adapter *adapter, u8 *mac) +{ + unsigned long flags; + unsigned i; + + spin_lock_irqsave(&adapter->lock, flags); + for (i = 0; i < ETH_ALEN; i++) { + ks8842_write8(adapter, 2, mac[ETH_ALEN - i - 1], REG_MARL + i); + ks8842_write8(adapter, 39, mac[ETH_ALEN - i - 1], + REG_MACAR1 + i); + } + spin_unlock_irqrestore(&adapter->lock, flags); +} + static inline u16 ks8842_tx_fifo_space(struct ks8842_adapter *adapter) { return ks8842_read16(adapter, 16, REG_TXMIR) & 0x1fff; @@ -522,6 +537,8 @@ static int ks8842_open(struct net_device *netdev) /* reset the HW */ ks8842_reset_hw(adapter); + ks8842_write_mac_addr(adapter, netdev->dev_addr); + ks8842_update_link_status(netdev, adapter); err = request_irq(adapter->irq, ks8842_irq, IRQF_SHARED, DRV_NAME, @@ -568,10 +585,8 @@ static netdev_tx_t ks8842_xmit_frame(struct sk_buff *skb, static int ks8842_set_mac(struct net_device *netdev, void *p) { struct ks8842_adapter *adapter = netdev_priv(netdev); - unsigned long flags; struct sockaddr *addr = p; char *mac = (u8 *)addr->sa_data; - int i; dev_dbg(&adapter->pdev->dev, "%s: entry\n", __func__); @@ -580,13 +595,7 @@ static int ks8842_set_mac(struct net_device *netdev, void *p) memcpy(netdev->dev_addr, mac, netdev->addr_len); - spin_lock_irqsave(&adapter->lock, flags); - for (i = 0; i < ETH_ALEN; i++) { - ks8842_write8(adapter, 2, mac[ETH_ALEN - i - 1], REG_MARL + i); - ks8842_write8(adapter, 39, mac[ETH_ALEN - i - 1], - REG_MACAR1 + i); - } - spin_unlock_irqrestore(&adapter->lock, flags); + ks8842_write_mac_addr(adapter, mac); return 0; } @@ -605,6 +614,8 @@ static void ks8842_tx_timeout(struct net_device *netdev) ks8842_reset_hw(adapter); + ks8842_write_mac_addr(adapter, netdev->dev_addr); + ks8842_update_link_status(netdev, adapter); } @@ -627,7 +638,9 @@ static int __devinit ks8842_probe(struct platform_device *pdev) struct resource *iomem; struct net_device *netdev; struct ks8842_adapter *adapter; + struct ks8842_platform_data *pdata = pdev->dev.platform_data; u16 id; + unsigned i; iomem = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (!request_mem_region(iomem->start, resource_size(iomem), DRV_NAME)) @@ -658,7 +671,25 @@ static int __devinit ks8842_probe(struct platform_device *pdev) netdev->netdev_ops = &ks8842_netdev_ops; netdev->ethtool_ops = &ks8842_ethtool_ops; - ks8842_read_mac_addr(adapter, netdev->dev_addr); + /* Check if a mac address was given */ + i = netdev->addr_len; + if (pdata) { + for (i = 0; i < netdev->addr_len; i++) + if (pdata->macaddr[i] != 0) + break; + + if (i < netdev->addr_len) + /* an address was passed, use it */ + memcpy(netdev->dev_addr, pdata->macaddr, + netdev->addr_len); + } + + if (i == netdev->addr_len) { + ks8842_read_mac_addr(adapter, netdev->dev_addr); + + if (!is_valid_ether_addr(netdev->dev_addr)) + random_ether_addr(netdev->dev_addr); + } id = ks8842_read16(adapter, 32, REG_SW_ID_AND_ENABLE); diff --git a/include/linux/ks8842.h b/include/linux/ks8842.h new file mode 100644 index 00000000000..da0341b8ca0 --- /dev/null +++ b/include/linux/ks8842.h @@ -0,0 +1,34 @@ +/* + * ks8842.h KS8842 platform data struct definition + * Copyright (c) 2010 Intel Corporation + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#ifndef _LINUX_KS8842_H +#define _LINUX_KS8842_H + +#include + +/** + * struct ks8842_platform_data - Platform data of the KS8842 network driver + * @macaddr: The MAC address of the device, set to all 0:s to use the on in + * the chip. + * + */ +struct ks8842_platform_data { + u8 macaddr[ETH_ALEN]; +}; + +#endif -- cgit v1.2.3 From e802af9cabb011f09b9c19a82faef3dd315f27eb Mon Sep 17 00:00:00 2001 From: Stephen Hemminger Date: Thu, 22 Apr 2010 15:24:53 -0700 Subject: IPv6: Generic TTL Security Mechanism (final version) This patch adds IPv6 support for RFC5082 Generalized TTL Security Mechanism. Not to users of mapped address; the IPV6 and IPV4 socket options are seperate. The server does have to deal with both IPv4 and IPv6 socket options and the client has to handle the different for each family. On client: int ttl = 255; getaddrinfo(argv[1], argv[2], &hint, &result); for (rp = result; rp != NULL; rp = rp->ai_next) { s = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol); if (s < 0) continue; if (rp->ai_family == AF_INET) { setsockopt(s, IPPROTO_IP, IP_TTL, &ttl, sizeof(ttl)); } else if (rp->ai_family == AF_INET6) { setsockopt(s, IPPROTO_IPV6, IPV6_UNICAST_HOPS, &ttl, sizeof(ttl))) } if (connect(s, rp->ai_addr, rp->ai_addrlen) == 0) { ... On server: int minttl = 255 - maxhops; getaddrinfo(NULL, port, &hints, &result); for (rp = result; rp != NULL; rp = rp->ai_next) { s = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol); if (s < 0) continue; if (rp->ai_family == AF_INET6) setsockopt(s, IPPROTO_IPV6, IPV6_MINHOPCOUNT, &minttl, sizeof(minttl)); setsockopt(s, IPPROTO_IP, IP_MINTTL, &minttl, sizeof(minttl)); if (bind(s, rp->ai_addr, rp->ai_addrlen) == 0) break ... Signed-off-by: Stephen Hemminger Signed-off-by: David S. Miller --- include/linux/in6.h | 3 +++ include/linux/ipv6.h | 1 + net/ipv6/ipv6_sockglue.c | 12 ++++++++++++ net/ipv6/tcp_ipv6.c | 14 +++++++++++++- 4 files changed, 29 insertions(+), 1 deletion(-) (limited to 'include/linux') diff --git a/include/linux/in6.h b/include/linux/in6.h index bd55c6e46b2..9b90cb296eb 100644 --- a/include/linux/in6.h +++ b/include/linux/in6.h @@ -265,6 +265,9 @@ struct in6_flowlabel_req { #define IPV6_PREFER_SRC_CGA 0x0008 #define IPV6_PREFER_SRC_NONCGA 0x0800 +/* RFC5082: Generalized Ttl Security Mechanism */ +#define IPV6_MINHOPCOUNT 73 + /* * Multicast Routing: * see include/linux/mroute6.h. diff --git a/include/linux/ipv6.h b/include/linux/ipv6.h index e0cc9a7db2b..1bdbebf08d1 100644 --- a/include/linux/ipv6.h +++ b/include/linux/ipv6.h @@ -348,6 +348,7 @@ struct ipv6_pinfo { * 010: prefer public address * 100: prefer care-of address */ + __u8 min_hopcount; __u8 tclass; __u32 dst_cookie; diff --git a/net/ipv6/ipv6_sockglue.c b/net/ipv6/ipv6_sockglue.c index 1160400e9db..92295ad3487 100644 --- a/net/ipv6/ipv6_sockglue.c +++ b/net/ipv6/ipv6_sockglue.c @@ -767,6 +767,14 @@ pref_skip_coa: break; } + case IPV6_MINHOPCOUNT: + if (optlen < sizeof(int)) + goto e_inval; + if (val < 0 || val > 255) + goto e_inval; + np->min_hopcount = val; + retv = 0; + break; } release_sock(sk); @@ -1116,6 +1124,10 @@ static int do_ipv6_getsockopt(struct sock *sk, int level, int optname, val |= IPV6_PREFER_SRC_HOME; break; + case IPV6_MINHOPCOUNT: + val = np->min_hopcount; + break; + default: return -ENOPROTOOPT; } diff --git a/net/ipv6/tcp_ipv6.c b/net/ipv6/tcp_ipv6.c index 1ababbb4113..6603511e367 100644 --- a/net/ipv6/tcp_ipv6.c +++ b/net/ipv6/tcp_ipv6.c @@ -353,6 +353,11 @@ static void tcp_v6_err(struct sk_buff *skb, struct inet6_skb_parm *opt, if (sk->sk_state == TCP_CLOSE) goto out; + if (ipv6_hdr(skb)->hop_limit < inet6_sk(sk)->min_hopcount) { + NET_INC_STATS_BH(net, LINUX_MIB_TCPMINTTLDROP); + goto out; + } + tp = tcp_sk(sk); seq = ntohl(th->seq); if (sk->sk_state != TCP_LISTEN && @@ -1678,6 +1683,7 @@ ipv6_pktoptions: static int tcp_v6_rcv(struct sk_buff *skb) { struct tcphdr *th; + struct ipv6hdr *hdr; struct sock *sk; int ret; struct net *net = dev_net(skb->dev); @@ -1704,12 +1710,13 @@ static int tcp_v6_rcv(struct sk_buff *skb) goto bad_packet; th = tcp_hdr(skb); + hdr = ipv6_hdr(skb); TCP_SKB_CB(skb)->seq = ntohl(th->seq); TCP_SKB_CB(skb)->end_seq = (TCP_SKB_CB(skb)->seq + th->syn + th->fin + skb->len - th->doff*4); TCP_SKB_CB(skb)->ack_seq = ntohl(th->ack_seq); TCP_SKB_CB(skb)->when = 0; - TCP_SKB_CB(skb)->flags = ipv6_get_dsfield(ipv6_hdr(skb)); + TCP_SKB_CB(skb)->flags = ipv6_get_dsfield(hdr); TCP_SKB_CB(skb)->sacked = 0; sk = __inet6_lookup_skb(&tcp_hashinfo, skb, th->source, th->dest); @@ -1720,6 +1727,11 @@ process: if (sk->sk_state == TCP_TIME_WAIT) goto do_time_wait; + if (hdr->hop_limit < inet6_sk(sk)->min_hopcount) { + NET_INC_STATS_BH(net, LINUX_MIB_TCPMINTTLDROP); + goto discard_and_relse; + } + if (!xfrm6_policy_check(sk, XFRM_POLICY_IN, skb)) goto discard_and_relse; -- cgit v1.2.3 From 40eaf96271526a9f71030dd1a199ce46c045752e Mon Sep 17 00:00:00 2001 From: Paul LeoNerd Evans Date: Thu, 22 Apr 2010 03:32:22 +0000 Subject: net: Socket filter ancilliary data access for skb->dev->type Add an SKF_AD_HATYPE field to the packet ancilliary data area, giving access to skb->dev->type, as reported in the sll_hatype field. When capturing packets on a PF_PACKET/SOCK_RAW socket bound to all interfaces, there doesn't appear to be a way for the filter program to actually find out the underlying hardware type the packet was captured on. This patch adds such ability. This patch also handles the case where skb->dev can be NULL, such as on netlink sockets. Signed-off-by: Paul Evans Signed-off-by: David S. Miller --- include/linux/filter.h | 3 ++- net/core/filter.c | 7 +++++++ 2 files changed, 9 insertions(+), 1 deletion(-) (limited to 'include/linux') diff --git a/include/linux/filter.h b/include/linux/filter.h index 29a0e3db9f4..151f5d703b7 100644 --- a/include/linux/filter.h +++ b/include/linux/filter.h @@ -123,7 +123,8 @@ struct sock_fprog { /* Required for SO_ATTACH_FILTER. */ #define SKF_AD_NLATTR_NEST 16 #define SKF_AD_MARK 20 #define SKF_AD_QUEUE 24 -#define SKF_AD_MAX 28 +#define SKF_AD_HATYPE 28 +#define SKF_AD_MAX 32 #define SKF_NET_OFF (-0x100000) #define SKF_LL_OFF (-0x200000) diff --git a/net/core/filter.c b/net/core/filter.c index ff943bed21a..da69fb728d3 100644 --- a/net/core/filter.c +++ b/net/core/filter.c @@ -302,6 +302,8 @@ load_b: A = skb->pkt_type; continue; case SKF_AD_IFINDEX: + if (!skb->dev) + return 0; A = skb->dev->ifindex; continue; case SKF_AD_MARK: @@ -310,6 +312,11 @@ load_b: case SKF_AD_QUEUE: A = skb->queue_mapping; continue; + case SKF_AD_HATYPE: + if (!skb->dev) + return 0; + A = skb->dev->type; + continue; case SKF_AD_NLATTR: { struct nlattr *nla; -- cgit v1.2.3 From 5ebfbc06aae941484326c9e7e9c4d85330f63591 Mon Sep 17 00:00:00 2001 From: Andrew Hendry Date: Thu, 22 Apr 2010 16:12:36 -0700 Subject: X25: Add if_x25.h and x25 to device identifiers V2 Feedback from John Hughes. - Add header for userspace implementations such as xot/xoe to use - Use explicit values for interface stability - No changes to driver patches V1 - Use identifiers instead of magic numbers for X25 layer 3 to device interface. - Also fixed checkpatch notes on updated code. [ Add new user header to include/linux/Kbuild -DaveM ] Signed-off-by: Andrew Hendry Signed-off-by: David S. Miller --- include/linux/Kbuild | 1 + include/linux/if_x25.h | 26 ++++++++++++++++++++++++++ include/net/x25device.h | 1 + net/x25/x25_dev.c | 36 ++++++++++++++++++++---------------- 4 files changed, 48 insertions(+), 16 deletions(-) create mode 100644 include/linux/if_x25.h (limited to 'include/linux') diff --git a/include/linux/Kbuild b/include/linux/Kbuild index e2ea0b2159c..2fc8e14cc24 100644 --- a/include/linux/Kbuild +++ b/include/linux/Kbuild @@ -94,6 +94,7 @@ header-y += if_ppp.h header-y += if_slip.h header-y += if_strip.h header-y += if_tun.h +header-y += if_x25.h header-y += in_route.h header-y += ioctl.h header-y += ip6_tunnel.h diff --git a/include/linux/if_x25.h b/include/linux/if_x25.h new file mode 100644 index 00000000000..897765f5feb --- /dev/null +++ b/include/linux/if_x25.h @@ -0,0 +1,26 @@ +/* + * Linux X.25 packet to device interface + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef _IF_X25_H +#define _IF_X25_H + +#include + +/* Documentation/networking/x25-iface.txt */ +#define X25_IFACE_DATA 0x00 +#define X25_IFACE_CONNECT 0x01 +#define X25_IFACE_DISCONNECT 0x02 +#define X25_IFACE_PARAMS 0x03 + +#endif /* _IF_X25_H */ diff --git a/include/net/x25device.h b/include/net/x25device.h index 1415bcf9398..1fa08b49f1c 100644 --- a/include/net/x25device.h +++ b/include/net/x25device.h @@ -3,6 +3,7 @@ #include #include +#include #include static inline __be16 x25_type_trans(struct sk_buff *skb, struct net_device *dev) diff --git a/net/x25/x25_dev.c b/net/x25/x25_dev.c index b9ef682230a..9005f6daeab 100644 --- a/net/x25/x25_dev.c +++ b/net/x25/x25_dev.c @@ -24,6 +24,7 @@ #include #include #include +#include static int x25_receive_data(struct sk_buff *skb, struct x25_neigh *nb) { @@ -115,19 +116,22 @@ int x25_lapb_receive_frame(struct sk_buff *skb, struct net_device *dev, } switch (skb->data[0]) { - case 0x00: - skb_pull(skb, 1); - if (x25_receive_data(skb, nb)) { - x25_neigh_put(nb); - goto out; - } - break; - case 0x01: - x25_link_established(nb); - break; - case 0x02: - x25_link_terminated(nb); - break; + + case X25_IFACE_DATA: + skb_pull(skb, 1); + if (x25_receive_data(skb, nb)) { + x25_neigh_put(nb); + goto out; + } + break; + + case X25_IFACE_CONNECT: + x25_link_established(nb); + break; + + case X25_IFACE_DISCONNECT: + x25_link_terminated(nb); + break; } x25_neigh_put(nb); drop: @@ -148,7 +152,7 @@ void x25_establish_link(struct x25_neigh *nb) return; } ptr = skb_put(skb, 1); - *ptr = 0x01; + *ptr = X25_IFACE_CONNECT; break; #if defined(CONFIG_LLC) || defined(CONFIG_LLC_MODULE) @@ -184,7 +188,7 @@ void x25_terminate_link(struct x25_neigh *nb) } ptr = skb_put(skb, 1); - *ptr = 0x02; + *ptr = X25_IFACE_DISCONNECT; skb->protocol = htons(ETH_P_X25); skb->dev = nb->dev; @@ -200,7 +204,7 @@ void x25_send_frame(struct sk_buff *skb, struct x25_neigh *nb) switch (nb->dev->type) { case ARPHRD_X25: dptr = skb_push(skb, 1); - *dptr = 0x00; + *dptr = X25_IFACE_DATA; break; #if defined(CONFIG_LLC) || defined(CONFIG_LLC_MODULE) -- cgit v1.2.3 From 286d1e7f73320be063a5f6af25d3d61c741065c2 Mon Sep 17 00:00:00 2001 From: Scott Feldman Date: Thu, 22 Apr 2010 14:38:03 +0000 Subject: remove DCB_PROTO_VERSION as we don't do netlink versioning remove DCB_PROTO_VERSION as we don't do netlink versioning Signed-off-by: Scott Feldman Signed-off-by: David S. Miller --- include/linux/dcbnl.h | 2 -- 1 file changed, 2 deletions(-) (limited to 'include/linux') diff --git a/include/linux/dcbnl.h b/include/linux/dcbnl.h index b7cdbb4373d..8723491f7df 100644 --- a/include/linux/dcbnl.h +++ b/include/linux/dcbnl.h @@ -22,8 +22,6 @@ #include -#define DCB_PROTO_VERSION 1 - struct dcbmsg { __u8 dcb_family; __u8 cmd; -- cgit v1.2.3 From af740b2c8f4521e2c45698ee6040941a82d6349d Mon Sep 17 00:00:00 2001 From: Jesper Dangaard Brouer Date: Fri, 23 Apr 2010 12:34:56 +0200 Subject: netfilter: nf_conntrack: extend with extra stat counter I suspect an unfortunatly series of events occuring under a DDoS attack, in function __nf_conntrack_find() nf_contrack_core.c. Adding a stats counter to see if the search is restarted too often. Signed-off-by: Jesper Dangaard Brouer Signed-off-by: Patrick McHardy --- include/linux/netfilter/nf_conntrack_common.h | 1 + net/ipv4/netfilter/nf_conntrack_l3proto_ipv4_compat.c | 7 ++++--- net/netfilter/nf_conntrack_core.c | 4 +++- net/netfilter/nf_conntrack_standalone.c | 7 ++++--- 4 files changed, 12 insertions(+), 7 deletions(-) (limited to 'include/linux') diff --git a/include/linux/netfilter/nf_conntrack_common.h b/include/linux/netfilter/nf_conntrack_common.h index c608677dda6..14e6d32002c 100644 --- a/include/linux/netfilter/nf_conntrack_common.h +++ b/include/linux/netfilter/nf_conntrack_common.h @@ -113,6 +113,7 @@ struct ip_conntrack_stat { unsigned int expect_new; unsigned int expect_create; unsigned int expect_delete; + unsigned int search_restart; }; /* call to create an explicit dependency on nf_conntrack. */ diff --git a/net/ipv4/netfilter/nf_conntrack_l3proto_ipv4_compat.c b/net/ipv4/netfilter/nf_conntrack_l3proto_ipv4_compat.c index 2fb7b76da94..244f7cb08d6 100644 --- a/net/ipv4/netfilter/nf_conntrack_l3proto_ipv4_compat.c +++ b/net/ipv4/netfilter/nf_conntrack_l3proto_ipv4_compat.c @@ -336,12 +336,12 @@ static int ct_cpu_seq_show(struct seq_file *seq, void *v) const struct ip_conntrack_stat *st = v; if (v == SEQ_START_TOKEN) { - seq_printf(seq, "entries searched found new invalid ignore delete delete_list insert insert_failed drop early_drop icmp_error expect_new expect_create expect_delete\n"); + seq_printf(seq, "entries searched found new invalid ignore delete delete_list insert insert_failed drop early_drop icmp_error expect_new expect_create expect_delete search_restart\n"); return 0; } seq_printf(seq, "%08x %08x %08x %08x %08x %08x %08x %08x " - "%08x %08x %08x %08x %08x %08x %08x %08x \n", + "%08x %08x %08x %08x %08x %08x %08x %08x %08x\n", nr_conntracks, st->searched, st->found, @@ -358,7 +358,8 @@ static int ct_cpu_seq_show(struct seq_file *seq, void *v) st->expect_new, st->expect_create, - st->expect_delete + st->expect_delete, + st->search_restart ); return 0; } diff --git a/net/netfilter/nf_conntrack_core.c b/net/netfilter/nf_conntrack_core.c index 0c9bbe93cc1..3907efb97a7 100644 --- a/net/netfilter/nf_conntrack_core.c +++ b/net/netfilter/nf_conntrack_core.c @@ -319,8 +319,10 @@ begin: * not the expected one, we must restart lookup. * We probably met an item that was moved to another chain. */ - if (get_nulls_value(n) != hash) + if (get_nulls_value(n) != hash) { + NF_CT_STAT_INC(net, search_restart); goto begin; + } local_bh_enable(); return NULL; diff --git a/net/netfilter/nf_conntrack_standalone.c b/net/netfilter/nf_conntrack_standalone.c index faa8eb3722b..ea4a8d38423 100644 --- a/net/netfilter/nf_conntrack_standalone.c +++ b/net/netfilter/nf_conntrack_standalone.c @@ -252,12 +252,12 @@ static int ct_cpu_seq_show(struct seq_file *seq, void *v) const struct ip_conntrack_stat *st = v; if (v == SEQ_START_TOKEN) { - seq_printf(seq, "entries searched found new invalid ignore delete delete_list insert insert_failed drop early_drop icmp_error expect_new expect_create expect_delete\n"); + seq_printf(seq, "entries searched found new invalid ignore delete delete_list insert insert_failed drop early_drop icmp_error expect_new expect_create expect_delete search_restart\n"); return 0; } seq_printf(seq, "%08x %08x %08x %08x %08x %08x %08x %08x " - "%08x %08x %08x %08x %08x %08x %08x %08x \n", + "%08x %08x %08x %08x %08x %08x %08x %08x %08x\n", nr_conntracks, st->searched, st->found, @@ -274,7 +274,8 @@ static int ct_cpu_seq_show(struct seq_file *seq, void *v) st->expect_new, st->expect_create, - st->expect_delete + st->expect_delete, + st->search_restart ); return 0; } -- cgit v1.2.3 From 793b14731686595a741d9f47726ad8b9a235385a Mon Sep 17 00:00:00 2001 From: Brian Haley Date: Fri, 23 Apr 2010 11:26:07 +0000 Subject: IPv6: data structure changes for new socket options Add underlying data structure changes and basic setsockopt() and getsockopt() support for IPV6_RECVPATHMTU, IPV6_PATHMTU, and IPV6_DONTFRAG. IPV6_PATHMTU is actually fully functional at this point. Signed-off-by: Brian Haley Signed-off-by: David S. Miller --- include/linux/in6.h | 2 +- include/linux/ipv6.h | 13 ++++++++++--- net/ipv6/ipv6_sockglue.c | 46 ++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 57 insertions(+), 4 deletions(-) (limited to 'include/linux') diff --git a/include/linux/in6.h b/include/linux/in6.h index 9b90cb296eb..c4bf46f764b 100644 --- a/include/linux/in6.h +++ b/include/linux/in6.h @@ -221,10 +221,10 @@ struct in6_flowlabel_req { #define IPV6_RTHDR 57 #define IPV6_RECVDSTOPTS 58 #define IPV6_DSTOPTS 59 -#if 0 /* not yet */ #define IPV6_RECVPATHMTU 60 #define IPV6_PATHMTU 61 #define IPV6_DONTFRAG 62 +#if 0 /* not yet */ #define IPV6_USE_MIN_MTU 63 #endif diff --git a/include/linux/ipv6.h b/include/linux/ipv6.h index 1bdbebf08d1..1976942cf6f 100644 --- a/include/linux/ipv6.h +++ b/include/linux/ipv6.h @@ -21,6 +21,10 @@ struct in6_pktinfo { int ipi6_ifindex; }; +struct ip6_mtuinfo { + struct sockaddr_in6 ip6m_addr; + __u32 ip6m_mtu; +}; struct in6_ifreq { struct in6_addr ifr6_addr; @@ -334,22 +338,25 @@ struct ipv6_pinfo { dstopts:1, odstopts:1, rxflow:1, - rxtclass:1; + rxtclass:1, + rxpmtu:1; } bits; __u16 all; } rxopt; /* sockopt flags */ - __u8 recverr:1, + __u16 recverr:1, sndflow:1, pmtudisc:2, ipv6only:1, - srcprefs:3; /* 001: prefer temporary address + srcprefs:3, /* 001: prefer temporary address * 010: prefer public address * 100: prefer care-of address */ + dontfrag:1; __u8 min_hopcount; __u8 tclass; + __u8 padding; __u32 dst_cookie; diff --git a/net/ipv6/ipv6_sockglue.c b/net/ipv6/ipv6_sockglue.c index 92295ad3487..2bf9eda7278 100644 --- a/net/ipv6/ipv6_sockglue.c +++ b/net/ipv6/ipv6_sockglue.c @@ -337,6 +337,13 @@ static int do_ipv6_setsockopt(struct sock *sk, int level, int optname, retv = 0; break; + case IPV6_RECVPATHMTU: + if (optlen < sizeof(int)) + goto e_inval; + np->rxopt.bits.rxpmtu = valbool; + retv = 0; + break; + case IPV6_HOPOPTS: case IPV6_RTHDRDSTOPTS: case IPV6_RTHDR: @@ -773,6 +780,9 @@ pref_skip_coa: if (val < 0 || val > 255) goto e_inval; np->min_hopcount = val; + break; + case IPV6_DONTFRAG: + np->dontfrag = valbool; retv = 0; break; } @@ -1063,6 +1073,38 @@ static int do_ipv6_getsockopt(struct sock *sk, int level, int optname, val = np->rxopt.bits.rxflow; break; + case IPV6_RECVPATHMTU: + val = np->rxopt.bits.rxpmtu; + break; + + case IPV6_PATHMTU: + { + struct dst_entry *dst; + struct ip6_mtuinfo mtuinfo; + + if (len < sizeof(mtuinfo)) + return -EINVAL; + + len = sizeof(mtuinfo); + memset(&mtuinfo, 0, sizeof(mtuinfo)); + + rcu_read_lock(); + dst = __sk_dst_get(sk); + if (dst) + mtuinfo.ip6m_mtu = dst_mtu(dst); + rcu_read_unlock(); + if (!mtuinfo.ip6m_mtu) + return -ENOTCONN; + + if (put_user(len, optlen)) + return -EFAULT; + if (copy_to_user(optval, &mtuinfo, len)) + return -EFAULT; + + return 0; + break; + } + case IPV6_UNICAST_HOPS: case IPV6_MULTICAST_HOPS: { @@ -1128,6 +1170,10 @@ static int do_ipv6_getsockopt(struct sock *sk, int level, int optname, val = np->min_hopcount; break; + case IPV6_DONTFRAG: + val = np->dontfrag; + break; + default: return -ENOPROTOOPT; } -- cgit v1.2.3 From 4b340ae20d0e2366792abe70f46629e576adaf5e Mon Sep 17 00:00:00 2001 From: Brian Haley Date: Fri, 23 Apr 2010 11:26:09 +0000 Subject: IPv6: Complete IPV6_DONTFRAG support Finally add support to detect a local IPV6_DONTFRAG event and return the relevant data to the user if they've enabled IPV6_RECVPATHMTU on the socket. The next recvmsg() will return no data, but have an IPV6_PATHMTU as ancillary data. Signed-off-by: Brian Haley Signed-off-by: David S. Miller --- include/linux/ipv6.h | 2 ++ include/net/ipv6.h | 2 ++ net/ipv6/af_inet6.c | 3 ++ net/ipv6/datagram.c | 87 +++++++++++++++++++++++++++++++++++++++++++++++++++ net/ipv6/ip6_output.c | 24 +++++++++----- net/ipv6/raw.c | 3 ++ net/ipv6/udp.c | 3 ++ 7 files changed, 116 insertions(+), 8 deletions(-) (limited to 'include/linux') diff --git a/include/linux/ipv6.h b/include/linux/ipv6.h index 1976942cf6f..2ab5509f6d4 100644 --- a/include/linux/ipv6.h +++ b/include/linux/ipv6.h @@ -257,6 +257,7 @@ struct inet6_skb_parm { }; #define IP6CB(skb) ((struct inet6_skb_parm*)((skb)->cb)) +#define IP6CBMTU(skb) ((struct ip6_mtuinfo *)((skb)->cb)) static inline int inet6_iif(const struct sk_buff *skb) { @@ -366,6 +367,7 @@ struct ipv6_pinfo { struct ipv6_txoptions *opt; struct sk_buff *pktoptions; + struct sk_buff *rxpmtu; struct { struct ipv6_txoptions *opt; u8 hop_limit; diff --git a/include/net/ipv6.h b/include/net/ipv6.h index 7ab6323e631..eba5cc00325 100644 --- a/include/net/ipv6.h +++ b/include/net/ipv6.h @@ -578,9 +578,11 @@ extern int ip6_datagram_connect(struct sock *sk, struct sockaddr *addr, int addr_len); extern int ipv6_recv_error(struct sock *sk, struct msghdr *msg, int len); +extern int ipv6_recv_rxpmtu(struct sock *sk, struct msghdr *msg, int len); extern void ipv6_icmp_error(struct sock *sk, struct sk_buff *skb, int err, __be16 port, u32 info, u8 *payload); extern void ipv6_local_error(struct sock *sk, int err, struct flowi *fl, u32 info); +extern void ipv6_local_rxpmtu(struct sock *sk, struct flowi *fl, u32 mtu); extern int inet6_release(struct socket *sock); extern int inet6_bind(struct socket *sock, struct sockaddr *uaddr, diff --git a/net/ipv6/af_inet6.c b/net/ipv6/af_inet6.c index 3192aa02ba5..d2df3144429 100644 --- a/net/ipv6/af_inet6.c +++ b/net/ipv6/af_inet6.c @@ -417,6 +417,9 @@ void inet6_destroy_sock(struct sock *sk) if ((skb = xchg(&np->pktoptions, NULL)) != NULL) kfree_skb(skb); + if ((skb = xchg(&np->rxpmtu, NULL)) != NULL) + kfree_skb(skb); + /* Free flowlabels */ fl6_free_socklist(sk); diff --git a/net/ipv6/datagram.c b/net/ipv6/datagram.c index f5076d349b1..5959230bc6c 100644 --- a/net/ipv6/datagram.c +++ b/net/ipv6/datagram.c @@ -278,6 +278,45 @@ void ipv6_local_error(struct sock *sk, int err, struct flowi *fl, u32 info) kfree_skb(skb); } +void ipv6_local_rxpmtu(struct sock *sk, struct flowi *fl, u32 mtu) +{ + struct ipv6_pinfo *np = inet6_sk(sk); + struct ipv6hdr *iph; + struct sk_buff *skb; + struct ip6_mtuinfo *mtu_info; + + if (!np->rxopt.bits.rxpmtu) + return; + + skb = alloc_skb(sizeof(struct ipv6hdr), GFP_ATOMIC); + if (!skb) + return; + + skb_put(skb, sizeof(struct ipv6hdr)); + skb_reset_network_header(skb); + iph = ipv6_hdr(skb); + ipv6_addr_copy(&iph->daddr, &fl->fl6_dst); + + mtu_info = IP6CBMTU(skb); + if (!mtu_info) { + kfree_skb(skb); + return; + } + + mtu_info->ip6m_mtu = mtu; + mtu_info->ip6m_addr.sin6_family = AF_INET6; + mtu_info->ip6m_addr.sin6_port = 0; + mtu_info->ip6m_addr.sin6_flowinfo = 0; + mtu_info->ip6m_addr.sin6_scope_id = fl->oif; + ipv6_addr_copy(&mtu_info->ip6m_addr.sin6_addr, &ipv6_hdr(skb)->daddr); + + __skb_pull(skb, skb_tail_pointer(skb) - skb->data); + skb_reset_transport_header(skb); + + skb = xchg(&np->rxpmtu, skb); + kfree_skb(skb); +} + /* * Handle MSG_ERRQUEUE */ @@ -381,6 +420,54 @@ out: return err; } +/* + * Handle IPV6_RECVPATHMTU + */ +int ipv6_recv_rxpmtu(struct sock *sk, struct msghdr *msg, int len) +{ + struct ipv6_pinfo *np = inet6_sk(sk); + struct sk_buff *skb; + struct sockaddr_in6 *sin; + struct ip6_mtuinfo mtu_info; + int err; + int copied; + + err = -EAGAIN; + skb = xchg(&np->rxpmtu, NULL); + if (skb == NULL) + goto out; + + copied = skb->len; + if (copied > len) { + msg->msg_flags |= MSG_TRUNC; + copied = len; + } + err = skb_copy_datagram_iovec(skb, 0, msg->msg_iov, copied); + if (err) + goto out_free_skb; + + sock_recv_timestamp(msg, sk, skb); + + memcpy(&mtu_info, IP6CBMTU(skb), sizeof(mtu_info)); + + sin = (struct sockaddr_in6 *)msg->msg_name; + if (sin) { + sin->sin6_family = AF_INET6; + sin->sin6_flowinfo = 0; + sin->sin6_port = 0; + sin->sin6_scope_id = mtu_info.ip6m_addr.sin6_scope_id; + ipv6_addr_copy(&sin->sin6_addr, &mtu_info.ip6m_addr.sin6_addr); + } + + put_cmsg(msg, SOL_IPV6, IPV6_PATHMTU, sizeof(mtu_info), &mtu_info); + + err = copied; + +out_free_skb: + kfree_skb(skb); +out: + return err; +} int datagram_recv_ctl(struct sock *sk, struct msghdr *msg, struct sk_buff *skb) diff --git a/net/ipv6/ip6_output.c b/net/ipv6/ip6_output.c index 54d43dd1f08..61e2bef5609 100644 --- a/net/ipv6/ip6_output.c +++ b/net/ipv6/ip6_output.c @@ -1219,15 +1219,23 @@ int ip6_append_data(struct sock *sk, int getfrag(void *from, char *to, */ inet->cork.length += length; - if (((length > mtu) && (sk->sk_protocol == IPPROTO_UDP)) && - (rt->u.dst.dev->features & NETIF_F_UFO)) { + if (length > mtu) { + int proto = sk->sk_protocol; + if (dontfrag && (proto == IPPROTO_UDP || proto == IPPROTO_RAW)){ + ipv6_local_rxpmtu(sk, fl, mtu-exthdrlen); + return -EMSGSIZE; + } - err = ip6_ufo_append_data(sk, getfrag, from, length, hh_len, - fragheaderlen, transhdrlen, mtu, - flags); - if (err) - goto error; - return 0; + if (proto == IPPROTO_UDP && + (rt->u.dst.dev->features & NETIF_F_UFO)) { + + err = ip6_ufo_append_data(sk, getfrag, from, length, + hh_len, fragheaderlen, + transhdrlen, mtu, flags); + if (err) + goto error; + return 0; + } } if ((skb = skb_peek_tail(&sk->sk_write_queue)) == NULL) diff --git a/net/ipv6/raw.c b/net/ipv6/raw.c index 44a84ea9b3e..85627386cb0 100644 --- a/net/ipv6/raw.c +++ b/net/ipv6/raw.c @@ -461,6 +461,9 @@ static int rawv6_recvmsg(struct kiocb *iocb, struct sock *sk, if (flags & MSG_ERRQUEUE) return ipv6_recv_error(sk, msg, len); + if (np->rxpmtu && np->rxopt.bits.rxpmtu) + return ipv6_recv_rxpmtu(sk, msg, len); + skb = skb_recv_datagram(sk, flags, noblock, &err); if (!skb) goto out; diff --git a/net/ipv6/udp.c b/net/ipv6/udp.c index 39e3665d946..2850e35cee3 100644 --- a/net/ipv6/udp.c +++ b/net/ipv6/udp.c @@ -335,6 +335,9 @@ int udpv6_recvmsg(struct kiocb *iocb, struct sock *sk, if (flags & MSG_ERRQUEUE) return ipv6_recv_error(sk, msg, len); + if (np->rxpmtu && np->rxopt.bits.rxpmtu) + return ipv6_recv_rxpmtu(sk, msg, len); + try_again: skb = __skb_recv_datagram(sk, flags | (noblock ? MSG_DONTWAIT : 0), &peeked, &err); -- cgit v1.2.3 From 25239cee7e8732dbdc9f5d324f1c22a3bdec1d1f Mon Sep 17 00:00:00 2001 From: Patrick McHardy Date: Mon, 26 Apr 2010 16:02:05 +0200 Subject: net: rtnetlink: decouple rtnetlink address families from real address families Decouple rtnetlink address families from real address families in socket.h to be able to add rtnetlink interfaces to code that is not a real address family without increasing AF_MAX/NPROTO. This will be used to add support for multicast route dumping from all tables as the proc interface can't be extended to support anything but the main table without breaking compatibility. This partialy undoes the patch to introduce independant families for routing rules and converts ipmr routing rules to a new rtnetlink family. Similar to that patch, values up to 127 are reserved for real address families, values above that may be used arbitrarily. Signed-off-by: Patrick McHardy --- include/linux/fib_rules.h | 8 -------- include/linux/rtnetlink.h | 6 ++++++ net/core/rtnetlink.c | 14 +++++++------- net/decnet/dn_rules.c | 2 +- net/ipv4/fib_rules.c | 2 +- net/ipv4/ipmr.c | 2 +- net/ipv6/fib6_rules.c | 2 +- 7 files changed, 17 insertions(+), 19 deletions(-) (limited to 'include/linux') diff --git a/include/linux/fib_rules.h b/include/linux/fib_rules.h index 04a397619eb..51da65b68b8 100644 --- a/include/linux/fib_rules.h +++ b/include/linux/fib_rules.h @@ -15,14 +15,6 @@ /* try to find source address in routing lookups */ #define FIB_RULE_FIND_SADDR 0x00010000 -/* fib_rules families. values up to 127 are reserved for real address - * families, values above 128 may be used arbitrarily. - */ -#define FIB_RULES_IPV4 AF_INET -#define FIB_RULES_IPV6 AF_INET6 -#define FIB_RULES_DECNET AF_DECnet -#define FIB_RULES_IPMR 128 - struct fib_rule_hdr { __u8 family; __u8 dst_len; diff --git a/include/linux/rtnetlink.h b/include/linux/rtnetlink.h index d1c7c90e9cd..5a42c36cb6a 100644 --- a/include/linux/rtnetlink.h +++ b/include/linux/rtnetlink.h @@ -7,6 +7,12 @@ #include #include +/* rtnetlink families. Values up to 127 are reserved for real address + * families, values above 128 may be used arbitrarily. + */ +#define RTNL_FAMILY_IPMR 128 +#define RTNL_FAMILY_MAX 128 + /**** * Routing/neighbour discovery messages. ****/ diff --git a/net/core/rtnetlink.c b/net/core/rtnetlink.c index 78c85985cb3..fd781b62fa7 100644 --- a/net/core/rtnetlink.c +++ b/net/core/rtnetlink.c @@ -98,7 +98,7 @@ int lockdep_rtnl_is_held(void) EXPORT_SYMBOL(lockdep_rtnl_is_held); #endif /* #ifdef CONFIG_PROVE_LOCKING */ -static struct rtnl_link *rtnl_msg_handlers[NPROTO]; +static struct rtnl_link *rtnl_msg_handlers[RTNL_FAMILY_MAX + 1]; static inline int rtm_msgindex(int msgtype) { @@ -118,7 +118,7 @@ static rtnl_doit_func rtnl_get_doit(int protocol, int msgindex) { struct rtnl_link *tab; - if (protocol < NPROTO) + if (protocol <= RTNL_FAMILY_MAX) tab = rtnl_msg_handlers[protocol]; else tab = NULL; @@ -133,7 +133,7 @@ static rtnl_dumpit_func rtnl_get_dumpit(int protocol, int msgindex) { struct rtnl_link *tab; - if (protocol < NPROTO) + if (protocol <= RTNL_FAMILY_MAX) tab = rtnl_msg_handlers[protocol]; else tab = NULL; @@ -167,7 +167,7 @@ int __rtnl_register(int protocol, int msgtype, struct rtnl_link *tab; int msgindex; - BUG_ON(protocol < 0 || protocol >= NPROTO); + BUG_ON(protocol < 0 || protocol > RTNL_FAMILY_MAX); msgindex = rtm_msgindex(msgtype); tab = rtnl_msg_handlers[protocol]; @@ -219,7 +219,7 @@ int rtnl_unregister(int protocol, int msgtype) { int msgindex; - BUG_ON(protocol < 0 || protocol >= NPROTO); + BUG_ON(protocol < 0 || protocol > RTNL_FAMILY_MAX); msgindex = rtm_msgindex(msgtype); if (rtnl_msg_handlers[protocol] == NULL) @@ -241,7 +241,7 @@ EXPORT_SYMBOL_GPL(rtnl_unregister); */ void rtnl_unregister_all(int protocol) { - BUG_ON(protocol < 0 || protocol >= NPROTO); + BUG_ON(protocol < 0 || protocol > RTNL_FAMILY_MAX); kfree(rtnl_msg_handlers[protocol]); rtnl_msg_handlers[protocol] = NULL; @@ -1384,7 +1384,7 @@ static int rtnl_dump_all(struct sk_buff *skb, struct netlink_callback *cb) if (s_idx == 0) s_idx = 1; - for (idx = 1; idx < NPROTO; idx++) { + for (idx = 1; idx <= RTNL_FAMILY_MAX; idx++) { int type = cb->nlh->nlmsg_type-RTM_BASE; if (idx < s_idx || idx == PF_PACKET) continue; diff --git a/net/decnet/dn_rules.c b/net/decnet/dn_rules.c index 1226bcad776..48fdf10be7a 100644 --- a/net/decnet/dn_rules.c +++ b/net/decnet/dn_rules.c @@ -217,7 +217,7 @@ static void dn_fib_rule_flush_cache(struct fib_rules_ops *ops) } static const struct fib_rules_ops __net_initdata dn_fib_rules_ops_template = { - .family = FIB_RULES_DECNET, + .family = AF_DECnet, .rule_size = sizeof(struct dn_fib_rule), .addr_size = sizeof(u16), .action = dn_fib_rule_action, diff --git a/net/ipv4/fib_rules.c b/net/ipv4/fib_rules.c index 8ab62a56701..76daeb5ff56 100644 --- a/net/ipv4/fib_rules.c +++ b/net/ipv4/fib_rules.c @@ -246,7 +246,7 @@ static void fib4_rule_flush_cache(struct fib_rules_ops *ops) } static const struct fib_rules_ops __net_initdata fib4_rules_ops_template = { - .family = FIB_RULES_IPV4, + .family = AF_INET, .rule_size = sizeof(struct fib4_rule), .addr_size = sizeof(u32), .action = fib4_rule_action, diff --git a/net/ipv4/ipmr.c b/net/ipv4/ipmr.c index 7d3e382aed6..41e8fc0ce8b 100644 --- a/net/ipv4/ipmr.c +++ b/net/ipv4/ipmr.c @@ -217,7 +217,7 @@ static int ipmr_rule_fill(struct fib_rule *rule, struct sk_buff *skb, } static const struct fib_rules_ops __net_initdata ipmr_rules_ops_template = { - .family = FIB_RULES_IPMR, + .family = RTNL_FAMILY_IPMR, .rule_size = sizeof(struct ipmr_rule), .addr_size = sizeof(u32), .action = ipmr_rule_action, diff --git a/net/ipv6/fib6_rules.c b/net/ipv6/fib6_rules.c index 35f6949446f..8e44f8f9c18 100644 --- a/net/ipv6/fib6_rules.c +++ b/net/ipv6/fib6_rules.c @@ -238,7 +238,7 @@ static size_t fib6_rule_nlmsg_payload(struct fib_rule *rule) } static const struct fib_rules_ops __net_initdata fib6_rules_ops_template = { - .family = FIB_RULES_IPV6, + .family = AF_INET6, .rule_size = sizeof(struct fib6_rule), .addr_size = sizeof(struct in6_addr), .action = fib6_rule_action, -- cgit v1.2.3 From d53cdbb94a52a920d5420ed64d986c3523a56743 Mon Sep 17 00:00:00 2001 From: "John W. Linville" Date: Wed, 31 Mar 2010 21:39:35 +0200 Subject: ssb: do not read SPROM if it does not exist MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Attempting to read registers that don't exist on the SSB bus can cause hangs on some boxes. At least some b43 devices are 'in the wild' that don't have SPROMs at all. When the SSB bus support loads, it attempts to read these (non-existant) SPROMs and causes hard hangs on the box -- no console output, etc. This patch adds some intelligence to determine whether or not the SPROM is present before attempting to read it. This avoids those hard hangs on those devices with no SPROM attached to their SSB bus. The SSB-attached devices (e.g. b43, et al.) won't work, but at least the box will survive to test further patches. :-) Signed-off-by: John W. Linville Signed-off-by: Rafał Miłecki Cc: Larry Finger Cc: Michael Buesch --- drivers/ssb/driver_chipcommon.c | 2 ++ drivers/ssb/pci.c | 5 +++++ drivers/ssb/sprom.c | 14 ++++++++++++++ include/linux/ssb/ssb.h | 3 +++ include/linux/ssb/ssb_driver_chipcommon.h | 15 +++++++++++++++ 5 files changed, 39 insertions(+) (limited to 'include/linux') diff --git a/drivers/ssb/driver_chipcommon.c b/drivers/ssb/driver_chipcommon.c index 59c3c0fdbec..59ae76bace1 100644 --- a/drivers/ssb/driver_chipcommon.c +++ b/drivers/ssb/driver_chipcommon.c @@ -233,6 +233,8 @@ void ssb_chipcommon_init(struct ssb_chipcommon *cc) { if (!cc->dev) return; /* We don't have a ChipCommon */ + if (cc->dev->id.revision >= 11) + cc->status = chipco_read32(cc, SSB_CHIPCO_CHIPSTAT); ssb_pmu_init(cc); chipco_powercontrol_init(cc); ssb_chipco_set_clockmode(cc, SSB_CLKMODE_FAST); diff --git a/drivers/ssb/pci.c b/drivers/ssb/pci.c index 9e50896233a..a4b2b99f2c8 100644 --- a/drivers/ssb/pci.c +++ b/drivers/ssb/pci.c @@ -620,6 +620,11 @@ static int ssb_pci_sprom_get(struct ssb_bus *bus, int err = -ENOMEM; u16 *buf; + if (!ssb_is_sprom_available(bus)) { + ssb_printk(KERN_ERR PFX "No SPROM available!\n"); + return -ENODEV; + } + buf = kcalloc(SSB_SPROMSIZE_WORDS_R123, sizeof(u16), GFP_KERNEL); if (!buf) goto out; diff --git a/drivers/ssb/sprom.c b/drivers/ssb/sprom.c index d0e6762fec5..83bc088b941 100644 --- a/drivers/ssb/sprom.c +++ b/drivers/ssb/sprom.c @@ -175,3 +175,17 @@ const struct ssb_sprom *ssb_get_fallback_sprom(void) { return fallback_sprom; } + +/* http://bcm-v4.sipsolutions.net/802.11/IsSpromAvailable */ +bool ssb_is_sprom_available(struct ssb_bus *bus) +{ + /* status register only exists on chipcomon rev >= 11 and we need check + for >= 31 only */ + /* this routine differs from specs as we do not access SPROM directly + on PCMCIA */ + if (bus->bustype == SSB_BUSTYPE_PCI && + bus->chipco.dev->id.revision >= 31) + return bus->chipco.capabilities & SSB_CHIPCO_CAP_SPROM; + + return true; +} diff --git a/include/linux/ssb/ssb.h b/include/linux/ssb/ssb.h index 24f98854736..3b4da233e31 100644 --- a/include/linux/ssb/ssb.h +++ b/include/linux/ssb/ssb.h @@ -394,6 +394,9 @@ extern int ssb_bus_sdiobus_register(struct ssb_bus *bus, extern void ssb_bus_unregister(struct ssb_bus *bus); +/* Does the device have an SPROM? */ +extern bool ssb_is_sprom_available(struct ssb_bus *bus); + /* Set a fallback SPROM. * See kdoc at the function definition for complete documentation. */ extern int ssb_arch_set_fallback_sprom(const struct ssb_sprom *sprom); diff --git a/include/linux/ssb/ssb_driver_chipcommon.h b/include/linux/ssb/ssb_driver_chipcommon.h index 4e27acf0a92..2cdf249b4e5 100644 --- a/include/linux/ssb/ssb_driver_chipcommon.h +++ b/include/linux/ssb/ssb_driver_chipcommon.h @@ -53,6 +53,7 @@ #define SSB_CHIPCO_CAP_64BIT 0x08000000 /* 64-bit Backplane */ #define SSB_CHIPCO_CAP_PMU 0x10000000 /* PMU available (rev >= 20) */ #define SSB_CHIPCO_CAP_ECI 0x20000000 /* ECI available (rev >= 20) */ +#define SSB_CHIPCO_CAP_SPROM 0x40000000 /* SPROM present */ #define SSB_CHIPCO_CORECTL 0x0008 #define SSB_CHIPCO_CORECTL_UARTCLK0 0x00000001 /* Drive UART with internal clock */ #define SSB_CHIPCO_CORECTL_SE 0x00000002 /* sync clk out enable (corerev >= 3) */ @@ -385,6 +386,7 @@ /** Chip specific Chip-Status register contents. */ +#define SSB_CHIPCO_CHST_4322_SPROM_EXISTS 0x00000040 /* SPROM present */ #define SSB_CHIPCO_CHST_4325_SPROM_OTP_SEL 0x00000003 #define SSB_CHIPCO_CHST_4325_DEFCIS_SEL 0 /* OTP is powered up, use def. CIS, no SPROM */ #define SSB_CHIPCO_CHST_4325_SPROM_SEL 1 /* OTP is powered up, SPROM is present */ @@ -398,6 +400,18 @@ #define SSB_CHIPCO_CHST_4325_RCAL_VALUE_SHIFT 4 #define SSB_CHIPCO_CHST_4325_PMUTOP_2B 0x00000200 /* 1 for 2b, 0 for to 2a */ +/** Macros to determine SPROM presence based on Chip-Status register. */ +#define SSB_CHIPCO_CHST_4312_SPROM_PRESENT(status) \ + ((status & SSB_CHIPCO_CHST_4325_SPROM_OTP_SEL) != \ + SSB_CHIPCO_CHST_4325_OTP_SEL) +#define SSB_CHIPCO_CHST_4322_SPROM_PRESENT(status) \ + (status & SSB_CHIPCO_CHST_4322_SPROM_EXISTS) +#define SSB_CHIPCO_CHST_4325_SPROM_PRESENT(status) \ + (((status & SSB_CHIPCO_CHST_4325_SPROM_OTP_SEL) != \ + SSB_CHIPCO_CHST_4325_DEFCIS_SEL) && \ + ((status & SSB_CHIPCO_CHST_4325_SPROM_OTP_SEL) != \ + SSB_CHIPCO_CHST_4325_OTP_SEL)) + /** Clockcontrol masks and values **/ @@ -564,6 +578,7 @@ struct ssb_chipcommon_pmu { struct ssb_chipcommon { struct ssb_device *dev; u32 capabilities; + u32 status; /* Fast Powerup Delay constant */ u16 fast_pwrup_delay; struct ssb_chipcommon_pmu pmu; -- cgit v1.2.3 From ea2db495f92ad2cf3301623e60cb95b4062bc484 Mon Sep 17 00:00:00 2001 From: Rafał Miłecki Date: Wed, 31 Mar 2010 21:59:21 +0200 Subject: ssb: Look for SPROM at different offset on higher rev CC MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Our offset handling becomes even a little more hackish now. For some reason I do not understand all offsets as inrelative. It assumes base offset is 0x1000 but it will work for now as we make offsets relative anyway by removing base 0x1000. Should be cleaner however. Signed-off-by: Rafał Miłecki Signed-off-by: John W. Linville --- drivers/ssb/pci.c | 9 ++++++--- include/linux/ssb/ssb.h | 1 + include/linux/ssb/ssb_regs.h | 3 ++- 3 files changed, 9 insertions(+), 4 deletions(-) (limited to 'include/linux') diff --git a/drivers/ssb/pci.c b/drivers/ssb/pci.c index a4b2b99f2c8..5bb1278759c 100644 --- a/drivers/ssb/pci.c +++ b/drivers/ssb/pci.c @@ -167,7 +167,7 @@ err_pci: } /* Get the word-offset for a SSB_SPROM_XXX define. */ -#define SPOFF(offset) (((offset) - SSB_SPROM_BASE) / sizeof(u16)) +#define SPOFF(offset) (((offset) - SSB_SPROM_BASE1) / sizeof(u16)) /* Helper to extract some _offset, which is one of the SSB_SPROM_XXX defines. */ #define SPEX16(_outvar, _offset, _mask, _shift) \ out->_outvar = ((in[SPOFF(_offset)] & (_mask)) >> (_shift)) @@ -253,7 +253,7 @@ static int sprom_do_read(struct ssb_bus *bus, u16 *sprom) int i; for (i = 0; i < bus->sprom_size; i++) - sprom[i] = ioread16(bus->mmio + SSB_SPROM_BASE + (i * 2)); + sprom[i] = ioread16(bus->mmio + bus->sprom_offset + (i * 2)); return 0; } @@ -284,7 +284,7 @@ static int sprom_do_write(struct ssb_bus *bus, const u16 *sprom) ssb_printk("75%%"); else if (i % 2) ssb_printk("."); - writew(sprom[i], bus->mmio + SSB_SPROM_BASE + (i * 2)); + writew(sprom[i], bus->mmio + bus->sprom_offset + (i * 2)); mmiowb(); msleep(20); } @@ -625,6 +625,9 @@ static int ssb_pci_sprom_get(struct ssb_bus *bus, return -ENODEV; } + bus->sprom_offset = (bus->chipco.dev->id.revision < 31) ? + SSB_SPROM_BASE1 : SSB_SPROM_BASE31; + buf = kcalloc(SSB_SPROMSIZE_WORDS_R123, sizeof(u16), GFP_KERNEL); if (!buf) goto out; diff --git a/include/linux/ssb/ssb.h b/include/linux/ssb/ssb.h index 3b4da233e31..a2608bff9c7 100644 --- a/include/linux/ssb/ssb.h +++ b/include/linux/ssb/ssb.h @@ -305,6 +305,7 @@ struct ssb_bus { /* ID information about the Chip. */ u16 chip_id; u16 chip_rev; + u16 sprom_offset; u16 sprom_size; /* number of words in sprom */ u8 chip_package; diff --git a/include/linux/ssb/ssb_regs.h b/include/linux/ssb/ssb_regs.h index 9ae9082eaeb..b8be23ce191 100644 --- a/include/linux/ssb/ssb_regs.h +++ b/include/linux/ssb/ssb_regs.h @@ -170,7 +170,8 @@ #define SSB_SPROMSIZE_WORDS_R4 220 #define SSB_SPROMSIZE_BYTES_R123 (SSB_SPROMSIZE_WORDS_R123 * sizeof(u16)) #define SSB_SPROMSIZE_BYTES_R4 (SSB_SPROMSIZE_WORDS_R4 * sizeof(u16)) -#define SSB_SPROM_BASE 0x1000 +#define SSB_SPROM_BASE1 0x1000 +#define SSB_SPROM_BASE31 0x0800 #define SSB_SPROM_REVISION 0x107E #define SSB_SPROM_REVISION_REV 0x00FF /* SPROM Revision number */ #define SSB_SPROM_REVISION_CRC 0xFF00 /* SPROM CRC8 value */ -- cgit v1.2.3 From 0a182fd88f8180b342f753f04c7d5507b5891c96 Mon Sep 17 00:00:00 2001 From: Rafał Miłecki Date: Wed, 31 Mar 2010 22:54:18 +0200 Subject: ssb: Use relative offsets for SPROM MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Rafał Miłecki Signed-off-by: John W. Linville --- drivers/ssb/pci.c | 2 +- include/linux/ssb/ssb_regs.h | 196 +++++++++++++++++++++---------------------- 2 files changed, 99 insertions(+), 99 deletions(-) (limited to 'include/linux') diff --git a/drivers/ssb/pci.c b/drivers/ssb/pci.c index 5bb1278759c..3f556d6ec4c 100644 --- a/drivers/ssb/pci.c +++ b/drivers/ssb/pci.c @@ -167,7 +167,7 @@ err_pci: } /* Get the word-offset for a SSB_SPROM_XXX define. */ -#define SPOFF(offset) (((offset) - SSB_SPROM_BASE1) / sizeof(u16)) +#define SPOFF(offset) ((offset) / sizeof(u16)) /* Helper to extract some _offset, which is one of the SSB_SPROM_XXX defines. */ #define SPEX16(_outvar, _offset, _mask, _shift) \ out->_outvar = ((in[SPOFF(_offset)] & (_mask)) >> (_shift)) diff --git a/include/linux/ssb/ssb_regs.h b/include/linux/ssb/ssb_regs.h index b8be23ce191..8990e30c657 100644 --- a/include/linux/ssb/ssb_regs.h +++ b/include/linux/ssb/ssb_regs.h @@ -172,25 +172,25 @@ #define SSB_SPROMSIZE_BYTES_R4 (SSB_SPROMSIZE_WORDS_R4 * sizeof(u16)) #define SSB_SPROM_BASE1 0x1000 #define SSB_SPROM_BASE31 0x0800 -#define SSB_SPROM_REVISION 0x107E +#define SSB_SPROM_REVISION 0x007E #define SSB_SPROM_REVISION_REV 0x00FF /* SPROM Revision number */ #define SSB_SPROM_REVISION_CRC 0xFF00 /* SPROM CRC8 value */ #define SSB_SPROM_REVISION_CRC_SHIFT 8 /* SPROM Revision 1 */ -#define SSB_SPROM1_SPID 0x1004 /* Subsystem Product ID for PCI */ -#define SSB_SPROM1_SVID 0x1006 /* Subsystem Vendor ID for PCI */ -#define SSB_SPROM1_PID 0x1008 /* Product ID for PCI */ -#define SSB_SPROM1_IL0MAC 0x1048 /* 6 bytes MAC address for 802.11b/g */ -#define SSB_SPROM1_ET0MAC 0x104E /* 6 bytes MAC address for Ethernet */ -#define SSB_SPROM1_ET1MAC 0x1054 /* 6 bytes MAC address for 802.11a */ -#define SSB_SPROM1_ETHPHY 0x105A /* Ethernet PHY settings */ +#define SSB_SPROM1_SPID 0x0004 /* Subsystem Product ID for PCI */ +#define SSB_SPROM1_SVID 0x0006 /* Subsystem Vendor ID for PCI */ +#define SSB_SPROM1_PID 0x0008 /* Product ID for PCI */ +#define SSB_SPROM1_IL0MAC 0x0048 /* 6 bytes MAC address for 802.11b/g */ +#define SSB_SPROM1_ET0MAC 0x004E /* 6 bytes MAC address for Ethernet */ +#define SSB_SPROM1_ET1MAC 0x0054 /* 6 bytes MAC address for 802.11a */ +#define SSB_SPROM1_ETHPHY 0x005A /* Ethernet PHY settings */ #define SSB_SPROM1_ETHPHY_ET0A 0x001F /* MII Address for enet0 */ #define SSB_SPROM1_ETHPHY_ET1A 0x03E0 /* MII Address for enet1 */ #define SSB_SPROM1_ETHPHY_ET1A_SHIFT 5 #define SSB_SPROM1_ETHPHY_ET0M (1<<14) /* MDIO for enet0 */ #define SSB_SPROM1_ETHPHY_ET1M (1<<15) /* MDIO for enet1 */ -#define SSB_SPROM1_BINF 0x105C /* Board info */ +#define SSB_SPROM1_BINF 0x005C /* Board info */ #define SSB_SPROM1_BINF_BREV 0x00FF /* Board Revision */ #define SSB_SPROM1_BINF_CCODE 0x0F00 /* Country Code */ #define SSB_SPROM1_BINF_CCODE_SHIFT 8 @@ -198,63 +198,63 @@ #define SSB_SPROM1_BINF_ANTBG_SHIFT 12 #define SSB_SPROM1_BINF_ANTA 0xC000 /* Available A-PHY antennas */ #define SSB_SPROM1_BINF_ANTA_SHIFT 14 -#define SSB_SPROM1_PA0B0 0x105E -#define SSB_SPROM1_PA0B1 0x1060 -#define SSB_SPROM1_PA0B2 0x1062 -#define SSB_SPROM1_GPIOA 0x1064 /* General Purpose IO pins 0 and 1 */ +#define SSB_SPROM1_PA0B0 0x005E +#define SSB_SPROM1_PA0B1 0x0060 +#define SSB_SPROM1_PA0B2 0x0062 +#define SSB_SPROM1_GPIOA 0x0064 /* General Purpose IO pins 0 and 1 */ #define SSB_SPROM1_GPIOA_P0 0x00FF /* Pin 0 */ #define SSB_SPROM1_GPIOA_P1 0xFF00 /* Pin 1 */ #define SSB_SPROM1_GPIOA_P1_SHIFT 8 -#define SSB_SPROM1_GPIOB 0x1066 /* General Purpuse IO pins 2 and 3 */ +#define SSB_SPROM1_GPIOB 0x0066 /* General Purpuse IO pins 2 and 3 */ #define SSB_SPROM1_GPIOB_P2 0x00FF /* Pin 2 */ #define SSB_SPROM1_GPIOB_P3 0xFF00 /* Pin 3 */ #define SSB_SPROM1_GPIOB_P3_SHIFT 8 -#define SSB_SPROM1_MAXPWR 0x1068 /* Power Amplifier Max Power */ +#define SSB_SPROM1_MAXPWR 0x0068 /* Power Amplifier Max Power */ #define SSB_SPROM1_MAXPWR_BG 0x00FF /* B-PHY and G-PHY (in dBm Q5.2) */ #define SSB_SPROM1_MAXPWR_A 0xFF00 /* A-PHY (in dBm Q5.2) */ #define SSB_SPROM1_MAXPWR_A_SHIFT 8 -#define SSB_SPROM1_PA1B0 0x106A -#define SSB_SPROM1_PA1B1 0x106C -#define SSB_SPROM1_PA1B2 0x106E -#define SSB_SPROM1_ITSSI 0x1070 /* Idle TSSI Target */ +#define SSB_SPROM1_PA1B0 0x006A +#define SSB_SPROM1_PA1B1 0x006C +#define SSB_SPROM1_PA1B2 0x006E +#define SSB_SPROM1_ITSSI 0x0070 /* Idle TSSI Target */ #define SSB_SPROM1_ITSSI_BG 0x00FF /* B-PHY and G-PHY*/ #define SSB_SPROM1_ITSSI_A 0xFF00 /* A-PHY */ #define SSB_SPROM1_ITSSI_A_SHIFT 8 -#define SSB_SPROM1_BFLLO 0x1072 /* Boardflags (low 16 bits) */ -#define SSB_SPROM1_AGAIN 0x1074 /* Antenna Gain (in dBm Q5.2) */ +#define SSB_SPROM1_BFLLO 0x0072 /* Boardflags (low 16 bits) */ +#define SSB_SPROM1_AGAIN 0x0074 /* Antenna Gain (in dBm Q5.2) */ #define SSB_SPROM1_AGAIN_BG 0x00FF /* B-PHY and G-PHY */ #define SSB_SPROM1_AGAIN_BG_SHIFT 0 #define SSB_SPROM1_AGAIN_A 0xFF00 /* A-PHY */ #define SSB_SPROM1_AGAIN_A_SHIFT 8 /* SPROM Revision 2 (inherits from rev 1) */ -#define SSB_SPROM2_BFLHI 0x1038 /* Boardflags (high 16 bits) */ -#define SSB_SPROM2_MAXP_A 0x103A /* A-PHY Max Power */ +#define SSB_SPROM2_BFLHI 0x0038 /* Boardflags (high 16 bits) */ +#define SSB_SPROM2_MAXP_A 0x003A /* A-PHY Max Power */ #define SSB_SPROM2_MAXP_A_HI 0x00FF /* Max Power High */ #define SSB_SPROM2_MAXP_A_LO 0xFF00 /* Max Power Low */ #define SSB_SPROM2_MAXP_A_LO_SHIFT 8 -#define SSB_SPROM2_PA1LOB0 0x103C /* A-PHY PowerAmplifier Low Settings */ -#define SSB_SPROM2_PA1LOB1 0x103E /* A-PHY PowerAmplifier Low Settings */ -#define SSB_SPROM2_PA1LOB2 0x1040 /* A-PHY PowerAmplifier Low Settings */ -#define SSB_SPROM2_PA1HIB0 0x1042 /* A-PHY PowerAmplifier High Settings */ -#define SSB_SPROM2_PA1HIB1 0x1044 /* A-PHY PowerAmplifier High Settings */ -#define SSB_SPROM2_PA1HIB2 0x1046 /* A-PHY PowerAmplifier High Settings */ -#define SSB_SPROM2_OPO 0x1078 /* OFDM Power Offset from CCK Level */ +#define SSB_SPROM2_PA1LOB0 0x003C /* A-PHY PowerAmplifier Low Settings */ +#define SSB_SPROM2_PA1LOB1 0x003E /* A-PHY PowerAmplifier Low Settings */ +#define SSB_SPROM2_PA1LOB2 0x0040 /* A-PHY PowerAmplifier Low Settings */ +#define SSB_SPROM2_PA1HIB0 0x0042 /* A-PHY PowerAmplifier High Settings */ +#define SSB_SPROM2_PA1HIB1 0x0044 /* A-PHY PowerAmplifier High Settings */ +#define SSB_SPROM2_PA1HIB2 0x0046 /* A-PHY PowerAmplifier High Settings */ +#define SSB_SPROM2_OPO 0x0078 /* OFDM Power Offset from CCK Level */ #define SSB_SPROM2_OPO_VALUE 0x00FF #define SSB_SPROM2_OPO_UNUSED 0xFF00 -#define SSB_SPROM2_CCODE 0x107C /* Two char Country Code */ +#define SSB_SPROM2_CCODE 0x007C /* Two char Country Code */ /* SPROM Revision 3 (inherits most data from rev 2) */ -#define SSB_SPROM3_IL0MAC 0x104A /* 6 bytes MAC address for 802.11b/g */ -#define SSB_SPROM3_OFDMAPO 0x102C /* A-PHY OFDM Mid Power Offset (4 bytes, BigEndian) */ -#define SSB_SPROM3_OFDMALPO 0x1030 /* A-PHY OFDM Low Power Offset (4 bytes, BigEndian) */ -#define SSB_SPROM3_OFDMAHPO 0x1034 /* A-PHY OFDM High Power Offset (4 bytes, BigEndian) */ -#define SSB_SPROM3_GPIOLDC 0x1042 /* GPIO LED Powersave Duty Cycle (4 bytes, BigEndian) */ +#define SSB_SPROM3_IL0MAC 0x004A /* 6 bytes MAC address for 802.11b/g */ +#define SSB_SPROM3_OFDMAPO 0x002C /* A-PHY OFDM Mid Power Offset (4 bytes, BigEndian) */ +#define SSB_SPROM3_OFDMALPO 0x0030 /* A-PHY OFDM Low Power Offset (4 bytes, BigEndian) */ +#define SSB_SPROM3_OFDMAHPO 0x0034 /* A-PHY OFDM High Power Offset (4 bytes, BigEndian) */ +#define SSB_SPROM3_GPIOLDC 0x0042 /* GPIO LED Powersave Duty Cycle (4 bytes, BigEndian) */ #define SSB_SPROM3_GPIOLDC_OFF 0x0000FF00 /* Off Count */ #define SSB_SPROM3_GPIOLDC_OFF_SHIFT 8 #define SSB_SPROM3_GPIOLDC_ON 0x00FF0000 /* On Count */ #define SSB_SPROM3_GPIOLDC_ON_SHIFT 16 -#define SSB_SPROM3_CCKPO 0x1078 /* CCK Power Offset */ +#define SSB_SPROM3_CCKPO 0x0078 /* CCK Power Offset */ #define SSB_SPROM3_CCKPO_1M 0x000F /* 1M Rate PO */ #define SSB_SPROM3_CCKPO_2M 0x00F0 /* 2M Rate PO */ #define SSB_SPROM3_CCKPO_2M_SHIFT 4 @@ -265,100 +265,100 @@ #define SSB_SPROM3_OFDMGPO 0x107A /* G-PHY OFDM Power Offset (4 bytes, BigEndian) */ /* SPROM Revision 4 */ -#define SSB_SPROM4_IL0MAC 0x104C /* 6 byte MAC address for a/b/g/n */ -#define SSB_SPROM4_ETHPHY 0x105A /* Ethernet PHY settings ?? */ +#define SSB_SPROM4_IL0MAC 0x004C /* 6 byte MAC address for a/b/g/n */ +#define SSB_SPROM4_ETHPHY 0x005A /* Ethernet PHY settings ?? */ #define SSB_SPROM4_ETHPHY_ET0A 0x001F /* MII Address for enet0 */ #define SSB_SPROM4_ETHPHY_ET1A 0x03E0 /* MII Address for enet1 */ #define SSB_SPROM4_ETHPHY_ET1A_SHIFT 5 #define SSB_SPROM4_ETHPHY_ET0M (1<<14) /* MDIO for enet0 */ #define SSB_SPROM4_ETHPHY_ET1M (1<<15) /* MDIO for enet1 */ -#define SSB_SPROM4_CCODE 0x1052 /* Country Code (2 bytes) */ -#define SSB_SPROM4_ANTAVAIL 0x105D /* Antenna available bitfields */ +#define SSB_SPROM4_CCODE 0x0052 /* Country Code (2 bytes) */ +#define SSB_SPROM4_ANTAVAIL 0x005D /* Antenna available bitfields */ #define SSB_SPROM4_ANTAVAIL_A 0x00FF /* A-PHY bitfield */ #define SSB_SPROM4_ANTAVAIL_A_SHIFT 0 #define SSB_SPROM4_ANTAVAIL_BG 0xFF00 /* B-PHY and G-PHY bitfield */ #define SSB_SPROM4_ANTAVAIL_BG_SHIFT 8 -#define SSB_SPROM4_BFLLO 0x1044 /* Boardflags (low 16 bits) */ -#define SSB_SPROM4_AGAIN01 0x105E /* Antenna Gain (in dBm Q5.2) */ +#define SSB_SPROM4_BFLLO 0x0044 /* Boardflags (low 16 bits) */ +#define SSB_SPROM4_AGAIN01 0x005E /* Antenna Gain (in dBm Q5.2) */ #define SSB_SPROM4_AGAIN0 0x00FF /* Antenna 0 */ #define SSB_SPROM4_AGAIN0_SHIFT 0 #define SSB_SPROM4_AGAIN1 0xFF00 /* Antenna 1 */ #define SSB_SPROM4_AGAIN1_SHIFT 8 -#define SSB_SPROM4_AGAIN23 0x1060 +#define SSB_SPROM4_AGAIN23 0x0060 #define SSB_SPROM4_AGAIN2 0x00FF /* Antenna 2 */ #define SSB_SPROM4_AGAIN2_SHIFT 0 #define SSB_SPROM4_AGAIN3 0xFF00 /* Antenna 3 */ #define SSB_SPROM4_AGAIN3_SHIFT 8 -#define SSB_SPROM4_BFLHI 0x1046 /* Board Flags Hi */ -#define SSB_SPROM4_MAXP_BG 0x1080 /* Max Power BG in path 1 */ +#define SSB_SPROM4_BFLHI 0x0046 /* Board Flags Hi */ +#define SSB_SPROM4_MAXP_BG 0x0080 /* Max Power BG in path 1 */ #define SSB_SPROM4_MAXP_BG_MASK 0x00FF /* Mask for Max Power BG */ #define SSB_SPROM4_ITSSI_BG 0xFF00 /* Mask for path 1 itssi_bg */ #define SSB_SPROM4_ITSSI_BG_SHIFT 8 -#define SSB_SPROM4_MAXP_A 0x108A /* Max Power A in path 1 */ +#define SSB_SPROM4_MAXP_A 0x008A /* Max Power A in path 1 */ #define SSB_SPROM4_MAXP_A_MASK 0x00FF /* Mask for Max Power A */ #define SSB_SPROM4_ITSSI_A 0xFF00 /* Mask for path 1 itssi_a */ #define SSB_SPROM4_ITSSI_A_SHIFT 8 -#define SSB_SPROM4_GPIOA 0x1056 /* Gen. Purpose IO # 0 and 1 */ +#define SSB_SPROM4_GPIOA 0x0056 /* Gen. Purpose IO # 0 and 1 */ #define SSB_SPROM4_GPIOA_P0 0x00FF /* Pin 0 */ #define SSB_SPROM4_GPIOA_P1 0xFF00 /* Pin 1 */ #define SSB_SPROM4_GPIOA_P1_SHIFT 8 -#define SSB_SPROM4_GPIOB 0x1058 /* Gen. Purpose IO # 2 and 3 */ +#define SSB_SPROM4_GPIOB 0x0058 /* Gen. Purpose IO # 2 and 3 */ #define SSB_SPROM4_GPIOB_P2 0x00FF /* Pin 2 */ #define SSB_SPROM4_GPIOB_P3 0xFF00 /* Pin 3 */ #define SSB_SPROM4_GPIOB_P3_SHIFT 8 -#define SSB_SPROM4_PA0B0 0x1082 /* The paXbY locations are */ -#define SSB_SPROM4_PA0B1 0x1084 /* only guesses */ -#define SSB_SPROM4_PA0B2 0x1086 -#define SSB_SPROM4_PA1B0 0x108E -#define SSB_SPROM4_PA1B1 0x1090 -#define SSB_SPROM4_PA1B2 0x1092 +#define SSB_SPROM4_PA0B0 0x0082 /* The paXbY locations are */ +#define SSB_SPROM4_PA0B1 0x0084 /* only guesses */ +#define SSB_SPROM4_PA0B2 0x0086 +#define SSB_SPROM4_PA1B0 0x008E +#define SSB_SPROM4_PA1B1 0x0090 +#define SSB_SPROM4_PA1B2 0x0092 /* SPROM Revision 5 (inherits most data from rev 4) */ -#define SSB_SPROM5_BFLLO 0x104A /* Boardflags (low 16 bits) */ -#define SSB_SPROM5_BFLHI 0x104C /* Board Flags Hi */ -#define SSB_SPROM5_IL0MAC 0x1052 /* 6 byte MAC address for a/b/g/n */ -#define SSB_SPROM5_CCODE 0x1044 /* Country Code (2 bytes) */ -#define SSB_SPROM5_GPIOA 0x1076 /* Gen. Purpose IO # 0 and 1 */ +#define SSB_SPROM5_BFLLO 0x004A /* Boardflags (low 16 bits) */ +#define SSB_SPROM5_BFLHI 0x004C /* Board Flags Hi */ +#define SSB_SPROM5_IL0MAC 0x0052 /* 6 byte MAC address for a/b/g/n */ +#define SSB_SPROM5_CCODE 0x0044 /* Country Code (2 bytes) */ +#define SSB_SPROM5_GPIOA 0x0076 /* Gen. Purpose IO # 0 and 1 */ #define SSB_SPROM5_GPIOA_P0 0x00FF /* Pin 0 */ #define SSB_SPROM5_GPIOA_P1 0xFF00 /* Pin 1 */ #define SSB_SPROM5_GPIOA_P1_SHIFT 8 -#define SSB_SPROM5_GPIOB 0x1078 /* Gen. Purpose IO # 2 and 3 */ +#define SSB_SPROM5_GPIOB 0x0078 /* Gen. Purpose IO # 2 and 3 */ #define SSB_SPROM5_GPIOB_P2 0x00FF /* Pin 2 */ #define SSB_SPROM5_GPIOB_P3 0xFF00 /* Pin 3 */ #define SSB_SPROM5_GPIOB_P3_SHIFT 8 /* SPROM Revision 8 */ -#define SSB_SPROM8_BOARDREV 0x1082 /* Board revision */ -#define SSB_SPROM8_BFLLO 0x1084 /* Board flags (bits 0-15) */ -#define SSB_SPROM8_BFLHI 0x1086 /* Board flags (bits 16-31) */ -#define SSB_SPROM8_BFL2LO 0x1088 /* Board flags (bits 32-47) */ -#define SSB_SPROM8_BFL2HI 0x108A /* Board flags (bits 48-63) */ -#define SSB_SPROM8_IL0MAC 0x108C /* 6 byte MAC address */ -#define SSB_SPROM8_CCODE 0x1092 /* 2 byte country code */ -#define SSB_SPROM8_ANTAVAIL 0x109C /* Antenna available bitfields*/ +#define SSB_SPROM8_BOARDREV 0x0082 /* Board revision */ +#define SSB_SPROM8_BFLLO 0x0084 /* Board flags (bits 0-15) */ +#define SSB_SPROM8_BFLHI 0x0086 /* Board flags (bits 16-31) */ +#define SSB_SPROM8_BFL2LO 0x0088 /* Board flags (bits 32-47) */ +#define SSB_SPROM8_BFL2HI 0x008A /* Board flags (bits 48-63) */ +#define SSB_SPROM8_IL0MAC 0x008C /* 6 byte MAC address */ +#define SSB_SPROM8_CCODE 0x0092 /* 2 byte country code */ +#define SSB_SPROM8_ANTAVAIL 0x009C /* Antenna available bitfields*/ #define SSB_SPROM8_ANTAVAIL_A 0xFF00 /* A-PHY bitfield */ #define SSB_SPROM8_ANTAVAIL_A_SHIFT 8 #define SSB_SPROM8_ANTAVAIL_BG 0x00FF /* B-PHY and G-PHY bitfield */ #define SSB_SPROM8_ANTAVAIL_BG_SHIFT 0 -#define SSB_SPROM8_AGAIN01 0x109E /* Antenna Gain (in dBm Q5.2) */ +#define SSB_SPROM8_AGAIN01 0x009E /* Antenna Gain (in dBm Q5.2) */ #define SSB_SPROM8_AGAIN0 0x00FF /* Antenna 0 */ #define SSB_SPROM8_AGAIN0_SHIFT 0 #define SSB_SPROM8_AGAIN1 0xFF00 /* Antenna 1 */ #define SSB_SPROM8_AGAIN1_SHIFT 8 -#define SSB_SPROM8_AGAIN23 0x10A0 +#define SSB_SPROM8_AGAIN23 0x00A0 #define SSB_SPROM8_AGAIN2 0x00FF /* Antenna 2 */ #define SSB_SPROM8_AGAIN2_SHIFT 0 #define SSB_SPROM8_AGAIN3 0xFF00 /* Antenna 3 */ #define SSB_SPROM8_AGAIN3_SHIFT 8 -#define SSB_SPROM8_GPIOA 0x1096 /*Gen. Purpose IO # 0 and 1 */ +#define SSB_SPROM8_GPIOA 0x0096 /*Gen. Purpose IO # 0 and 1 */ #define SSB_SPROM8_GPIOA_P0 0x00FF /* Pin 0 */ #define SSB_SPROM8_GPIOA_P1 0xFF00 /* Pin 1 */ #define SSB_SPROM8_GPIOA_P1_SHIFT 8 -#define SSB_SPROM8_GPIOB 0x1098 /* Gen. Purpose IO # 2 and 3 */ +#define SSB_SPROM8_GPIOB 0x0098 /* Gen. Purpose IO # 2 and 3 */ #define SSB_SPROM8_GPIOB_P2 0x00FF /* Pin 2 */ #define SSB_SPROM8_GPIOB_P3 0xFF00 /* Pin 3 */ #define SSB_SPROM8_GPIOB_P3_SHIFT 8 -#define SSB_SPROM8_RSSIPARM2G 0x10A4 /* RSSI params for 2GHz */ +#define SSB_SPROM8_RSSIPARM2G 0x00A4 /* RSSI params for 2GHz */ #define SSB_SPROM8_RSSISMF2G 0x000F #define SSB_SPROM8_RSSISMC2G 0x00F0 #define SSB_SPROM8_RSSISMC2G_SHIFT 4 @@ -366,7 +366,7 @@ #define SSB_SPROM8_RSSISAV2G_SHIFT 8 #define SSB_SPROM8_BXA2G 0x1800 #define SSB_SPROM8_BXA2G_SHIFT 11 -#define SSB_SPROM8_RSSIPARM5G 0x10A6 /* RSSI params for 5GHz */ +#define SSB_SPROM8_RSSIPARM5G 0x00A6 /* RSSI params for 5GHz */ #define SSB_SPROM8_RSSISMF5G 0x000F #define SSB_SPROM8_RSSISMC5G 0x00F0 #define SSB_SPROM8_RSSISMC5G_SHIFT 4 @@ -374,47 +374,47 @@ #define SSB_SPROM8_RSSISAV5G_SHIFT 8 #define SSB_SPROM8_BXA5G 0x1800 #define SSB_SPROM8_BXA5G_SHIFT 11 -#define SSB_SPROM8_TRI25G 0x10A8 /* TX isolation 2.4&5.3GHz */ +#define SSB_SPROM8_TRI25G 0x00A8 /* TX isolation 2.4&5.3GHz */ #define SSB_SPROM8_TRI2G 0x00FF /* TX isolation 2.4GHz */ #define SSB_SPROM8_TRI5G 0xFF00 /* TX isolation 5.3GHz */ #define SSB_SPROM8_TRI5G_SHIFT 8 -#define SSB_SPROM8_TRI5GHL 0x10AA /* TX isolation 5.2/5.8GHz */ +#define SSB_SPROM8_TRI5GHL 0x00AA /* TX isolation 5.2/5.8GHz */ #define SSB_SPROM8_TRI5GL 0x00FF /* TX isolation 5.2GHz */ #define SSB_SPROM8_TRI5GH 0xFF00 /* TX isolation 5.8GHz */ #define SSB_SPROM8_TRI5GH_SHIFT 8 -#define SSB_SPROM8_RXPO 0x10AC /* RX power offsets */ +#define SSB_SPROM8_RXPO 0x00AC /* RX power offsets */ #define SSB_SPROM8_RXPO2G 0x00FF /* 2GHz RX power offset */ #define SSB_SPROM8_RXPO5G 0xFF00 /* 5GHz RX power offset */ #define SSB_SPROM8_RXPO5G_SHIFT 8 -#define SSB_SPROM8_MAXP_BG 0x10C0 /* Max Power 2GHz in path 1 */ +#define SSB_SPROM8_MAXP_BG 0x00C0 /* Max Power 2GHz in path 1 */ #define SSB_SPROM8_MAXP_BG_MASK 0x00FF /* Mask for Max Power 2GHz */ #define SSB_SPROM8_ITSSI_BG 0xFF00 /* Mask for path 1 itssi_bg */ #define SSB_SPROM8_ITSSI_BG_SHIFT 8 -#define SSB_SPROM8_PA0B0 0x10C2 /* 2GHz power amp settings */ -#define SSB_SPROM8_PA0B1 0x10C4 -#define SSB_SPROM8_PA0B2 0x10C6 -#define SSB_SPROM8_MAXP_A 0x10C8 /* Max Power 5.3GHz */ +#define SSB_SPROM8_PA0B0 0x00C2 /* 2GHz power amp settings */ +#define SSB_SPROM8_PA0B1 0x00C4 +#define SSB_SPROM8_PA0B2 0x00C6 +#define SSB_SPROM8_MAXP_A 0x00C8 /* Max Power 5.3GHz */ #define SSB_SPROM8_MAXP_A_MASK 0x00FF /* Mask for Max Power 5.3GHz */ #define SSB_SPROM8_ITSSI_A 0xFF00 /* Mask for path 1 itssi_a */ #define SSB_SPROM8_ITSSI_A_SHIFT 8 -#define SSB_SPROM8_MAXP_AHL 0x10CA /* Max Power 5.2/5.8GHz */ +#define SSB_SPROM8_MAXP_AHL 0x00CA /* Max Power 5.2/5.8GHz */ #define SSB_SPROM8_MAXP_AH_MASK 0x00FF /* Mask for Max Power 5.8GHz */ #define SSB_SPROM8_MAXP_AL_MASK 0xFF00 /* Mask for Max Power 5.2GHz */ #define SSB_SPROM8_MAXP_AL_SHIFT 8 -#define SSB_SPROM8_PA1B0 0x10CC /* 5.3GHz power amp settings */ -#define SSB_SPROM8_PA1B1 0x10CE -#define SSB_SPROM8_PA1B2 0x10D0 -#define SSB_SPROM8_PA1LOB0 0x10D2 /* 5.2GHz power amp settings */ -#define SSB_SPROM8_PA1LOB1 0x10D4 -#define SSB_SPROM8_PA1LOB2 0x10D6 -#define SSB_SPROM8_PA1HIB0 0x10D8 /* 5.8GHz power amp settings */ -#define SSB_SPROM8_PA1HIB1 0x10DA -#define SSB_SPROM8_PA1HIB2 0x10DC -#define SSB_SPROM8_CCK2GPO 0x1140 /* CCK power offset */ -#define SSB_SPROM8_OFDM2GPO 0x1142 /* 2.4GHz OFDM power offset */ -#define SSB_SPROM8_OFDM5GPO 0x1146 /* 5.3GHz OFDM power offset */ -#define SSB_SPROM8_OFDM5GLPO 0x114A /* 5.2GHz OFDM power offset */ -#define SSB_SPROM8_OFDM5GHPO 0x114E /* 5.8GHz OFDM power offset */ +#define SSB_SPROM8_PA1B0 0x00CC /* 5.3GHz power amp settings */ +#define SSB_SPROM8_PA1B1 0x00CE +#define SSB_SPROM8_PA1B2 0x00D0 +#define SSB_SPROM8_PA1LOB0 0x00D2 /* 5.2GHz power amp settings */ +#define SSB_SPROM8_PA1LOB1 0x00D4 +#define SSB_SPROM8_PA1LOB2 0x00D6 +#define SSB_SPROM8_PA1HIB0 0x00D8 /* 5.8GHz power amp settings */ +#define SSB_SPROM8_PA1HIB1 0x00DA +#define SSB_SPROM8_PA1HIB2 0x00DC +#define SSB_SPROM8_CCK2GPO 0x0140 /* CCK power offset */ +#define SSB_SPROM8_OFDM2GPO 0x0142 /* 2.4GHz OFDM power offset */ +#define SSB_SPROM8_OFDM5GPO 0x0146 /* 5.3GHz OFDM power offset */ +#define SSB_SPROM8_OFDM5GLPO 0x014A /* 5.2GHz OFDM power offset */ +#define SSB_SPROM8_OFDM5GHPO 0x014E /* 5.8GHz OFDM power offset */ /* Values for SSB_SPROM1_BINF_CCODE */ enum { -- cgit v1.2.3 From 5af55428858a45d94893fd6124d60988e89c0d59 Mon Sep 17 00:00:00 2001 From: Rafał Miłecki Date: Wed, 31 Mar 2010 22:54:19 +0200 Subject: ssb: Fix order of definitions and some text space indents MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Rafał Miłecki Signed-off-by: John W. Linville --- include/linux/ssb/ssb_regs.h | 58 ++++++++++++++++++++++---------------------- 1 file changed, 29 insertions(+), 29 deletions(-) (limited to 'include/linux') diff --git a/include/linux/ssb/ssb_regs.h b/include/linux/ssb/ssb_regs.h index 8990e30c657..a6d5225b927 100644 --- a/include/linux/ssb/ssb_regs.h +++ b/include/linux/ssb/ssb_regs.h @@ -245,7 +245,6 @@ #define SSB_SPROM2_CCODE 0x007C /* Two char Country Code */ /* SPROM Revision 3 (inherits most data from rev 2) */ -#define SSB_SPROM3_IL0MAC 0x004A /* 6 bytes MAC address for 802.11b/g */ #define SSB_SPROM3_OFDMAPO 0x002C /* A-PHY OFDM Mid Power Offset (4 bytes, BigEndian) */ #define SSB_SPROM3_OFDMALPO 0x0030 /* A-PHY OFDM Low Power Offset (4 bytes, BigEndian) */ #define SSB_SPROM3_OFDMAHPO 0x0034 /* A-PHY OFDM High Power Offset (4 bytes, BigEndian) */ @@ -254,6 +253,7 @@ #define SSB_SPROM3_GPIOLDC_OFF_SHIFT 8 #define SSB_SPROM3_GPIOLDC_ON 0x00FF0000 /* On Count */ #define SSB_SPROM3_GPIOLDC_ON_SHIFT 16 +#define SSB_SPROM3_IL0MAC 0x004A /* 6 bytes MAC address for 802.11b/g */ #define SSB_SPROM3_CCKPO 0x0078 /* CCK Power Offset */ #define SSB_SPROM3_CCKPO_1M 0x000F /* 1M Rate PO */ #define SSB_SPROM3_CCKPO_2M 0x00F0 /* 2M Rate PO */ @@ -265,20 +265,29 @@ #define SSB_SPROM3_OFDMGPO 0x107A /* G-PHY OFDM Power Offset (4 bytes, BigEndian) */ /* SPROM Revision 4 */ +#define SSB_SPROM4_BFLLO 0x0044 /* Boardflags (low 16 bits) */ +#define SSB_SPROM4_BFLHI 0x0046 /* Board Flags Hi */ #define SSB_SPROM4_IL0MAC 0x004C /* 6 byte MAC address for a/b/g/n */ +#define SSB_SPROM4_CCODE 0x0052 /* Country Code (2 bytes) */ +#define SSB_SPROM4_GPIOA 0x0056 /* Gen. Purpose IO # 0 and 1 */ +#define SSB_SPROM4_GPIOA_P0 0x00FF /* Pin 0 */ +#define SSB_SPROM4_GPIOA_P1 0xFF00 /* Pin 1 */ +#define SSB_SPROM4_GPIOA_P1_SHIFT 8 +#define SSB_SPROM4_GPIOB 0x0058 /* Gen. Purpose IO # 2 and 3 */ +#define SSB_SPROM4_GPIOB_P2 0x00FF /* Pin 2 */ +#define SSB_SPROM4_GPIOB_P3 0xFF00 /* Pin 3 */ +#define SSB_SPROM4_GPIOB_P3_SHIFT 8 #define SSB_SPROM4_ETHPHY 0x005A /* Ethernet PHY settings ?? */ #define SSB_SPROM4_ETHPHY_ET0A 0x001F /* MII Address for enet0 */ #define SSB_SPROM4_ETHPHY_ET1A 0x03E0 /* MII Address for enet1 */ #define SSB_SPROM4_ETHPHY_ET1A_SHIFT 5 #define SSB_SPROM4_ETHPHY_ET0M (1<<14) /* MDIO for enet0 */ #define SSB_SPROM4_ETHPHY_ET1M (1<<15) /* MDIO for enet1 */ -#define SSB_SPROM4_CCODE 0x0052 /* Country Code (2 bytes) */ #define SSB_SPROM4_ANTAVAIL 0x005D /* Antenna available bitfields */ -#define SSB_SPROM4_ANTAVAIL_A 0x00FF /* A-PHY bitfield */ -#define SSB_SPROM4_ANTAVAIL_A_SHIFT 0 -#define SSB_SPROM4_ANTAVAIL_BG 0xFF00 /* B-PHY and G-PHY bitfield */ -#define SSB_SPROM4_ANTAVAIL_BG_SHIFT 8 -#define SSB_SPROM4_BFLLO 0x0044 /* Boardflags (low 16 bits) */ +#define SSB_SPROM4_ANTAVAIL_A 0x00FF /* A-PHY bitfield */ +#define SSB_SPROM4_ANTAVAIL_A_SHIFT 0 +#define SSB_SPROM4_ANTAVAIL_BG 0xFF00 /* B-PHY and G-PHY bitfield */ +#define SSB_SPROM4_ANTAVAIL_BG_SHIFT 8 #define SSB_SPROM4_AGAIN01 0x005E /* Antenna Gain (in dBm Q5.2) */ #define SSB_SPROM4_AGAIN0 0x00FF /* Antenna 0 */ #define SSB_SPROM4_AGAIN0_SHIFT 0 @@ -289,7 +298,6 @@ #define SSB_SPROM4_AGAIN2_SHIFT 0 #define SSB_SPROM4_AGAIN3 0xFF00 /* Antenna 3 */ #define SSB_SPROM4_AGAIN3_SHIFT 8 -#define SSB_SPROM4_BFLHI 0x0046 /* Board Flags Hi */ #define SSB_SPROM4_MAXP_BG 0x0080 /* Max Power BG in path 1 */ #define SSB_SPROM4_MAXP_BG_MASK 0x00FF /* Mask for Max Power BG */ #define SSB_SPROM4_ITSSI_BG 0xFF00 /* Mask for path 1 itssi_bg */ @@ -298,14 +306,6 @@ #define SSB_SPROM4_MAXP_A_MASK 0x00FF /* Mask for Max Power A */ #define SSB_SPROM4_ITSSI_A 0xFF00 /* Mask for path 1 itssi_a */ #define SSB_SPROM4_ITSSI_A_SHIFT 8 -#define SSB_SPROM4_GPIOA 0x0056 /* Gen. Purpose IO # 0 and 1 */ -#define SSB_SPROM4_GPIOA_P0 0x00FF /* Pin 0 */ -#define SSB_SPROM4_GPIOA_P1 0xFF00 /* Pin 1 */ -#define SSB_SPROM4_GPIOA_P1_SHIFT 8 -#define SSB_SPROM4_GPIOB 0x0058 /* Gen. Purpose IO # 2 and 3 */ -#define SSB_SPROM4_GPIOB_P2 0x00FF /* Pin 2 */ -#define SSB_SPROM4_GPIOB_P3 0xFF00 /* Pin 3 */ -#define SSB_SPROM4_GPIOB_P3_SHIFT 8 #define SSB_SPROM4_PA0B0 0x0082 /* The paXbY locations are */ #define SSB_SPROM4_PA0B1 0x0084 /* only guesses */ #define SSB_SPROM4_PA0B2 0x0086 @@ -314,10 +314,10 @@ #define SSB_SPROM4_PA1B2 0x0092 /* SPROM Revision 5 (inherits most data from rev 4) */ +#define SSB_SPROM5_CCODE 0x0044 /* Country Code (2 bytes) */ #define SSB_SPROM5_BFLLO 0x004A /* Boardflags (low 16 bits) */ #define SSB_SPROM5_BFLHI 0x004C /* Board Flags Hi */ #define SSB_SPROM5_IL0MAC 0x0052 /* 6 byte MAC address for a/b/g/n */ -#define SSB_SPROM5_CCODE 0x0044 /* Country Code (2 bytes) */ #define SSB_SPROM5_GPIOA 0x0076 /* Gen. Purpose IO # 0 and 1 */ #define SSB_SPROM5_GPIOA_P0 0x00FF /* Pin 0 */ #define SSB_SPROM5_GPIOA_P1 0xFF00 /* Pin 1 */ @@ -335,11 +335,19 @@ #define SSB_SPROM8_BFL2HI 0x008A /* Board flags (bits 48-63) */ #define SSB_SPROM8_IL0MAC 0x008C /* 6 byte MAC address */ #define SSB_SPROM8_CCODE 0x0092 /* 2 byte country code */ +#define SSB_SPROM8_GPIOA 0x0096 /*Gen. Purpose IO # 0 and 1 */ +#define SSB_SPROM8_GPIOA_P0 0x00FF /* Pin 0 */ +#define SSB_SPROM8_GPIOA_P1 0xFF00 /* Pin 1 */ +#define SSB_SPROM8_GPIOA_P1_SHIFT 8 +#define SSB_SPROM8_GPIOB 0x0098 /* Gen. Purpose IO # 2 and 3 */ +#define SSB_SPROM8_GPIOB_P2 0x00FF /* Pin 2 */ +#define SSB_SPROM8_GPIOB_P3 0xFF00 /* Pin 3 */ +#define SSB_SPROM8_GPIOB_P3_SHIFT 8 #define SSB_SPROM8_ANTAVAIL 0x009C /* Antenna available bitfields*/ -#define SSB_SPROM8_ANTAVAIL_A 0xFF00 /* A-PHY bitfield */ -#define SSB_SPROM8_ANTAVAIL_A_SHIFT 8 -#define SSB_SPROM8_ANTAVAIL_BG 0x00FF /* B-PHY and G-PHY bitfield */ -#define SSB_SPROM8_ANTAVAIL_BG_SHIFT 0 +#define SSB_SPROM8_ANTAVAIL_A 0xFF00 /* A-PHY bitfield */ +#define SSB_SPROM8_ANTAVAIL_A_SHIFT 8 +#define SSB_SPROM8_ANTAVAIL_BG 0x00FF /* B-PHY and G-PHY bitfield */ +#define SSB_SPROM8_ANTAVAIL_BG_SHIFT 0 #define SSB_SPROM8_AGAIN01 0x009E /* Antenna Gain (in dBm Q5.2) */ #define SSB_SPROM8_AGAIN0 0x00FF /* Antenna 0 */ #define SSB_SPROM8_AGAIN0_SHIFT 0 @@ -350,14 +358,6 @@ #define SSB_SPROM8_AGAIN2_SHIFT 0 #define SSB_SPROM8_AGAIN3 0xFF00 /* Antenna 3 */ #define SSB_SPROM8_AGAIN3_SHIFT 8 -#define SSB_SPROM8_GPIOA 0x0096 /*Gen. Purpose IO # 0 and 1 */ -#define SSB_SPROM8_GPIOA_P0 0x00FF /* Pin 0 */ -#define SSB_SPROM8_GPIOA_P1 0xFF00 /* Pin 1 */ -#define SSB_SPROM8_GPIOA_P1_SHIFT 8 -#define SSB_SPROM8_GPIOB 0x0098 /* Gen. Purpose IO # 2 and 3 */ -#define SSB_SPROM8_GPIOB_P2 0x00FF /* Pin 2 */ -#define SSB_SPROM8_GPIOB_P3 0xFF00 /* Pin 3 */ -#define SSB_SPROM8_GPIOB_P3_SHIFT 8 #define SSB_SPROM8_RSSIPARM2G 0x00A4 /* RSSI params for 2GHz */ #define SSB_SPROM8_RSSISMF2G 0x000F #define SSB_SPROM8_RSSISMC2G 0x00F0 -- cgit v1.2.3 From 4b2cbd42bef5a22bb681acd607a7c3fbca1eeb3c Mon Sep 17 00:00:00 2001 From: Jan Engelhardt Date: Tue, 27 Apr 2010 15:34:34 +0200 Subject: netfilter: x_tables: rectify XT_FUNCTION_MAXNAMELEN usage There has been quite a confusion in userspace about XT_FUNCTION_MAXNAMELEN; because struct xt_entry_match used MAX-1, userspace would have to do an awkward MAX-2 for maximum length checking (due to '\0'). This patch adds a new define that matches the definition of XT_TABLE_MAXNAMELEN - being the size of the actual struct member, not one off. Signed-off-by: Jan Engelhardt Signed-off-by: Patrick McHardy --- include/linux/netfilter/x_tables.h | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) (limited to 'include/linux') diff --git a/include/linux/netfilter/x_tables.h b/include/linux/netfilter/x_tables.h index 50c867256ca..eeb4884c30b 100644 --- a/include/linux/netfilter/x_tables.h +++ b/include/linux/netfilter/x_tables.h @@ -4,6 +4,7 @@ #include #define XT_FUNCTION_MAXNAMELEN 30 +#define XT_EXTENSION_MAXNAMELEN 29 #define XT_TABLE_MAXNAMELEN 32 struct xt_entry_match { @@ -12,8 +13,7 @@ struct xt_entry_match { __u16 match_size; /* Used by userspace */ - char name[XT_FUNCTION_MAXNAMELEN-1]; - + char name[XT_EXTENSION_MAXNAMELEN]; __u8 revision; } user; struct { @@ -36,8 +36,7 @@ struct xt_entry_target { __u16 target_size; /* Used by userspace */ - char name[XT_FUNCTION_MAXNAMELEN-1]; - + char name[XT_EXTENSION_MAXNAMELEN]; __u8 revision; } user; struct { @@ -70,8 +69,7 @@ struct xt_standard_target { /* The argument to IPT_SO_GET_REVISION_*. Returns highest revision * kernel supports, if >= revision. */ struct xt_get_revision { - char name[XT_FUNCTION_MAXNAMELEN-1]; - + char name[XT_EXTENSION_MAXNAMELEN]; __u8 revision; }; @@ -291,7 +289,7 @@ struct xt_tgdtor_param { struct xt_match { struct list_head list; - const char name[XT_FUNCTION_MAXNAMELEN-1]; + const char name[XT_EXTENSION_MAXNAMELEN]; u_int8_t revision; /* Return true or false: return FALSE and set *hotdrop = 1 to @@ -330,7 +328,7 @@ struct xt_match { struct xt_target { struct list_head list; - const char name[XT_FUNCTION_MAXNAMELEN-1]; + const char name[XT_EXTENSION_MAXNAMELEN]; u_int8_t revision; /* Returns verdict. Argument order changed since 2.6.9, as this -- cgit v1.2.3 From ff846f52935e6c8dfb0c97df7c2c1bf777454684 Mon Sep 17 00:00:00 2001 From: Alexander Duyck Date: Tue, 27 Apr 2010 01:02:40 +0000 Subject: igb: add support for reporting 5GT/s during probe on PCIe Gen2 This change corrects the fact that we were not reporting Gen2 link speeds when we were in fact connected at Gen2 rates. Signed-off-by: Alexander Duyck Signed-off-by: Jeff Kirsher Signed-off-by: David S. Miller --- drivers/net/igb/e1000_defines.h | 4 ---- drivers/net/igb/e1000_mac.c | 27 ++++++++++++++++++++------- drivers/net/igb/igb_main.c | 1 + include/linux/pci_regs.h | 3 +++ 4 files changed, 24 insertions(+), 11 deletions(-) (limited to 'include/linux') diff --git a/drivers/net/igb/e1000_defines.h b/drivers/net/igb/e1000_defines.h index 31d24e0e76d..24d9be64342 100644 --- a/drivers/net/igb/e1000_defines.h +++ b/drivers/net/igb/e1000_defines.h @@ -610,11 +610,7 @@ #define IGP_LED3_MODE 0x07000000 /* PCI/PCI-X/PCI-EX Config space */ -#define PCIE_LINK_STATUS 0x12 #define PCIE_DEVICE_CONTROL2 0x28 - -#define PCIE_LINK_WIDTH_MASK 0x3F0 -#define PCIE_LINK_WIDTH_SHIFT 4 #define PCIE_DEVICE_CONTROL2_16ms 0x0005 #define PHY_REVISION_MASK 0xFFFFFFF0 diff --git a/drivers/net/igb/e1000_mac.c b/drivers/net/igb/e1000_mac.c index be8d010e402..90c5e01e923 100644 --- a/drivers/net/igb/e1000_mac.c +++ b/drivers/net/igb/e1000_mac.c @@ -53,17 +53,30 @@ s32 igb_get_bus_info_pcie(struct e1000_hw *hw) u16 pcie_link_status; bus->type = e1000_bus_type_pci_express; - bus->speed = e1000_bus_speed_2500; ret_val = igb_read_pcie_cap_reg(hw, - PCIE_LINK_STATUS, - &pcie_link_status); - if (ret_val) + PCI_EXP_LNKSTA, + &pcie_link_status); + if (ret_val) { bus->width = e1000_bus_width_unknown; - else + bus->speed = e1000_bus_speed_unknown; + } else { + switch (pcie_link_status & PCI_EXP_LNKSTA_CLS) { + case PCI_EXP_LNKSTA_CLS_2_5GB: + bus->speed = e1000_bus_speed_2500; + break; + case PCI_EXP_LNKSTA_CLS_5_0GB: + bus->speed = e1000_bus_speed_5000; + break; + default: + bus->speed = e1000_bus_speed_unknown; + break; + } + bus->width = (enum e1000_bus_width)((pcie_link_status & - PCIE_LINK_WIDTH_MASK) >> - PCIE_LINK_WIDTH_SHIFT); + PCI_EXP_LNKSTA_NLW) >> + PCI_EXP_LNKSTA_NLW_SHIFT); + } reg = rd32(E1000_STATUS); bus->func = (reg & E1000_STATUS_FUNC_MASK) >> E1000_STATUS_FUNC_SHIFT; diff --git a/drivers/net/igb/igb_main.c b/drivers/net/igb/igb_main.c index a14303a0ad7..919e3638667 100644 --- a/drivers/net/igb/igb_main.c +++ b/drivers/net/igb/igb_main.c @@ -1638,6 +1638,7 @@ static int __devinit igb_probe(struct pci_dev *pdev, dev_info(&pdev->dev, "%s: (PCIe:%s:%s) %pM\n", netdev->name, ((hw->bus.speed == e1000_bus_speed_2500) ? "2.5Gb/s" : + (hw->bus.speed == e1000_bus_speed_5000) ? "5.0Gb/s" : "unknown"), ((hw->bus.width == e1000_bus_width_pcie_x4) ? "Width x4" : (hw->bus.width == e1000_bus_width_pcie_x2) ? "Width x2" : diff --git a/include/linux/pci_regs.h b/include/linux/pci_regs.h index c8f302991b6..c4c3d68be19 100644 --- a/include/linux/pci_regs.h +++ b/include/linux/pci_regs.h @@ -442,7 +442,10 @@ #define PCI_EXP_LNKCTL_LABIE 0x0800 /* Lnk Autonomous Bandwidth Interrupt Enable */ #define PCI_EXP_LNKSTA 18 /* Link Status */ #define PCI_EXP_LNKSTA_CLS 0x000f /* Current Link Speed */ +#define PCI_EXP_LNKSTA_CLS_2_5GB 0x01 /* Current Link Speed 2.5GT/s */ +#define PCI_EXP_LNKSTA_CLS_5_0GB 0x02 /* Current Link Speed 5.0GT/s */ #define PCI_EXP_LNKSTA_NLW 0x03f0 /* Nogotiated Link Width */ +#define PCI_EXP_LNKSTA_NLW_SHIFT 4 /* start of NLW mask in link status */ #define PCI_EXP_LNKSTA_LT 0x0800 /* Link Training */ #define PCI_EXP_LNKSTA_SLC 0x1000 /* Slot Clock Configuration */ #define PCI_EXP_LNKSTA_DLLLA 0x2000 /* Data Link Layer Link Active */ -- cgit v1.2.3 From fd8aaaf3519f3fd3c82594e90bc6808072b94d54 Mon Sep 17 00:00:00 2001 From: Felix Fietkau Date: Tue, 27 Apr 2010 01:23:35 +0200 Subject: cfg80211: add ap isolation support This is used to configure APs to not bridge traffic between connected stations. Signed-off-by: Felix Fietkau Signed-off-by: John W. Linville --- include/linux/nl80211.h | 5 +++++ include/net/cfg80211.h | 2 ++ net/wireless/nl80211.c | 4 ++++ 3 files changed, 11 insertions(+) (limited to 'include/linux') diff --git a/include/linux/nl80211.h b/include/linux/nl80211.h index 2ea3edeee7a..f8750f9a65b 100644 --- a/include/linux/nl80211.h +++ b/include/linux/nl80211.h @@ -709,6 +709,9 @@ enum nl80211_commands { * NL80211_CMD_AUTHENTICATE, NL80211_CMD_DEAUTHENTICATE, * NL80211_CMD_DISASSOCIATE. * + * @NL80211_ATTR_AP_ISOLATE: (AP mode) Do not forward traffic between stations + * connected to this BSS. + * * @NL80211_ATTR_MAX: highest attribute number currently defined * @__NL80211_ATTR_AFTER_LAST: internal use */ @@ -864,6 +867,8 @@ enum nl80211_attrs { NL80211_ATTR_LOCAL_STATE_CHANGE, + NL80211_ATTR_AP_ISOLATE, + /* add attributes here, update the policy in nl80211.c */ __NL80211_ATTR_AFTER_LAST, diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index 5a4efe54cff..f6b29bf925d 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -511,6 +511,7 @@ struct mpath_info { * @basic_rates: basic rates in IEEE 802.11 format * (or NULL for no change) * @basic_rates_len: number of basic rates + * @ap_isolate: do not forward packets between connected stations */ struct bss_parameters { int use_cts_prot; @@ -518,6 +519,7 @@ struct bss_parameters { int use_short_slot_time; u8 *basic_rates; u8 basic_rates_len; + int ap_isolate; }; struct mesh_config { diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index df5505b3930..c27bef8e0c1 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -151,6 +151,7 @@ static const struct nla_policy nl80211_policy[NL80211_ATTR_MAX+1] = { [NL80211_ATTR_PS_STATE] = { .type = NLA_U32 }, [NL80211_ATTR_CQM] = { .type = NLA_NESTED, }, [NL80211_ATTR_LOCAL_STATE_CHANGE] = { .type = NLA_FLAG }, + [NL80211_ATTR_AP_ISOLATE] = { .type = NLA_U8 }, }; /* policy for the attributes */ @@ -2441,6 +2442,7 @@ static int nl80211_set_bss(struct sk_buff *skb, struct genl_info *info) params.use_cts_prot = -1; params.use_short_preamble = -1; params.use_short_slot_time = -1; + params.ap_isolate = -1; if (info->attrs[NL80211_ATTR_BSS_CTS_PROT]) params.use_cts_prot = @@ -2457,6 +2459,8 @@ static int nl80211_set_bss(struct sk_buff *skb, struct genl_info *info) params.basic_rates_len = nla_len(info->attrs[NL80211_ATTR_BSS_BASIC_RATES]); } + if (info->attrs[NL80211_ATTR_AP_ISOLATE]) + params.ap_isolate = !!nla_get_u8(info->attrs[NL80211_ATTR_AP_ISOLATE]); rtnl_lock(); -- cgit v1.2.3 From a9cbd588fdb71ea415754c885e2f9f03e6bf1ba0 Mon Sep 17 00:00:00 2001 From: Changli Gao Date: Mon, 26 Apr 2010 23:06:24 +0000 Subject: net: reimplement softnet_data.output_queue as a FIFO queue reimplement softnet_data.output_queue as a FIFO queue to keep the fairness among the qdiscs rescheduled. Signed-off-by: Changli Gao Acked-by: Eric Dumazet ---- include/linux/netdevice.h | 1 + net/core/dev.c | 22 ++++++++++++---------- 2 files changed, 13 insertions(+), 10 deletions(-) Signed-off-by: David S. Miller --- include/linux/netdevice.h | 1 + net/core/dev.c | 22 ++++++++++++---------- 2 files changed, 13 insertions(+), 10 deletions(-) (limited to 'include/linux') diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index 3c5ed5f5274..c04ca246395 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -1385,6 +1385,7 @@ static inline int unregister_gifconf(unsigned int family) */ struct softnet_data { struct Qdisc *output_queue; + struct Qdisc **output_queue_tailp; struct list_head poll_list; struct sk_buff *completion_queue; diff --git a/net/core/dev.c b/net/core/dev.c index 4d43f1a80f7..3d314919a2c 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -1557,8 +1557,9 @@ static inline void __netif_reschedule(struct Qdisc *q) local_irq_save(flags); sd = &__get_cpu_var(softnet_data); - q->next_sched = sd->output_queue; - sd->output_queue = q; + q->next_sched = NULL; + *sd->output_queue_tailp = q; + sd->output_queue_tailp = &q->next_sched; raise_softirq_irqoff(NET_TX_SOFTIRQ); local_irq_restore(flags); } @@ -2529,6 +2530,7 @@ static void net_tx_action(struct softirq_action *h) local_irq_disable(); head = sd->output_queue; sd->output_queue = NULL; + sd->output_queue_tailp = &sd->output_queue; local_irq_enable(); while (head) { @@ -5594,7 +5596,6 @@ static int dev_cpu_callback(struct notifier_block *nfb, void *ocpu) { struct sk_buff **list_skb; - struct Qdisc **list_net; struct sk_buff *skb; unsigned int cpu, oldcpu = (unsigned long)ocpu; struct softnet_data *sd, *oldsd; @@ -5615,13 +5616,13 @@ static int dev_cpu_callback(struct notifier_block *nfb, *list_skb = oldsd->completion_queue; oldsd->completion_queue = NULL; - /* Find end of our output_queue. */ - list_net = &sd->output_queue; - while (*list_net) - list_net = &(*list_net)->next_sched; /* Append output queue from offline CPU. */ - *list_net = oldsd->output_queue; - oldsd->output_queue = NULL; + if (oldsd->output_queue) { + *sd->output_queue_tailp = oldsd->output_queue; + sd->output_queue_tailp = oldsd->output_queue_tailp; + oldsd->output_queue = NULL; + oldsd->output_queue_tailp = &oldsd->output_queue; + } raise_softirq_irqoff(NET_TX_SOFTIRQ); local_irq_enable(); @@ -5851,7 +5852,8 @@ static int __init net_dev_init(void) skb_queue_head_init(&sd->input_pkt_queue); sd->completion_queue = NULL; INIT_LIST_HEAD(&sd->poll_list); - + sd->output_queue = NULL; + sd->output_queue_tailp = &sd->output_queue; #ifdef CONFIG_RPS sd->csd.func = rps_trigger_softirq; sd->csd.info = sd; -- cgit v1.2.3 From 6e7676c1a76aed6e957611d8d7a9e5592e23aeba Mon Sep 17 00:00:00 2001 From: Changli Gao Date: Tue, 27 Apr 2010 15:07:33 -0700 Subject: net: batch skb dequeueing from softnet input_pkt_queue batch skb dequeueing from softnet input_pkt_queue to reduce potential lock contention when RPS is enabled. Note: in the worst case, the number of packets in a softnet_data may be double of netdev_max_backlog. Signed-off-by: Changli Gao Signed-off-by: Eric Dumazet Signed-off-by: David S. Miller --- include/linux/netdevice.h | 6 +++-- net/core/dev.c | 57 ++++++++++++++++++++++++++++++++--------------- 2 files changed, 43 insertions(+), 20 deletions(-) (limited to 'include/linux') diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index c04ca246395..40d4c20d034 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -1388,6 +1388,7 @@ struct softnet_data { struct Qdisc **output_queue_tailp; struct list_head poll_list; struct sk_buff *completion_queue; + struct sk_buff_head process_queue; #ifdef CONFIG_RPS struct softnet_data *rps_ipi_list; @@ -1402,10 +1403,11 @@ struct softnet_data { struct napi_struct backlog; }; -static inline void input_queue_head_incr(struct softnet_data *sd) +static inline void input_queue_head_add(struct softnet_data *sd, + unsigned int len) { #ifdef CONFIG_RPS - sd->input_queue_head++; + sd->input_queue_head += len; #endif } diff --git a/net/core/dev.c b/net/core/dev.c index 3d314919a2c..100dcbd2973 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -2408,12 +2408,13 @@ static int enqueue_to_backlog(struct sk_buff *skb, int cpu, __get_cpu_var(netdev_rx_stat).total++; rps_lock(sd); - if (sd->input_pkt_queue.qlen <= netdev_max_backlog) { - if (sd->input_pkt_queue.qlen) { + if (skb_queue_len(&sd->input_pkt_queue) <= netdev_max_backlog) { + if (skb_queue_len(&sd->input_pkt_queue)) { enqueue: __skb_queue_tail(&sd->input_pkt_queue, skb); #ifdef CONFIG_RPS - *qtail = sd->input_queue_head + sd->input_pkt_queue.qlen; + *qtail = sd->input_queue_head + + skb_queue_len(&sd->input_pkt_queue); #endif rps_unlock(sd); local_irq_restore(flags); @@ -2934,13 +2935,21 @@ static void flush_backlog(void *arg) struct sk_buff *skb, *tmp; rps_lock(sd); - skb_queue_walk_safe(&sd->input_pkt_queue, skb, tmp) + skb_queue_walk_safe(&sd->input_pkt_queue, skb, tmp) { if (skb->dev == dev) { __skb_unlink(skb, &sd->input_pkt_queue); kfree_skb(skb); - input_queue_head_incr(sd); + input_queue_head_add(sd, 1); } + } rps_unlock(sd); + + skb_queue_walk_safe(&sd->process_queue, skb, tmp) { + if (skb->dev == dev) { + __skb_unlink(skb, &sd->process_queue); + kfree_skb(skb); + } + } } static int napi_gro_complete(struct sk_buff *skb) @@ -3286,24 +3295,33 @@ static int process_backlog(struct napi_struct *napi, int quota) } #endif napi->weight = weight_p; - do { + local_irq_disable(); + while (work < quota) { struct sk_buff *skb; + unsigned int qlen; + + while ((skb = __skb_dequeue(&sd->process_queue))) { + local_irq_enable(); + __netif_receive_skb(skb); + if (++work >= quota) + return work; + local_irq_disable(); + } - local_irq_disable(); rps_lock(sd); - skb = __skb_dequeue(&sd->input_pkt_queue); - if (!skb) { + qlen = skb_queue_len(&sd->input_pkt_queue); + if (qlen) { + input_queue_head_add(sd, qlen); + skb_queue_splice_tail_init(&sd->input_pkt_queue, + &sd->process_queue); + } + if (qlen < quota - work) { __napi_complete(napi); - rps_unlock(sd); - local_irq_enable(); - break; + quota = work + qlen; } - input_queue_head_incr(sd); rps_unlock(sd); - local_irq_enable(); - - __netif_receive_skb(skb); - } while (++work < quota); + } + local_irq_enable(); return work; } @@ -5630,8 +5648,10 @@ static int dev_cpu_callback(struct notifier_block *nfb, /* Process offline CPU's input_pkt_queue */ while ((skb = __skb_dequeue(&oldsd->input_pkt_queue))) { netif_rx(skb); - input_queue_head_incr(oldsd); + input_queue_head_add(oldsd, 1); } + while ((skb = __skb_dequeue(&oldsd->process_queue))) + netif_rx(skb); return NOTIFY_OK; } @@ -5850,6 +5870,7 @@ static int __init net_dev_init(void) struct softnet_data *sd = &per_cpu(softnet_data, i); skb_queue_head_init(&sd->input_pkt_queue); + skb_queue_head_init(&sd->process_queue); sd->completion_queue = NULL; INIT_LIST_HEAD(&sd->poll_list); sd->output_queue = NULL; -- cgit v1.2.3 From bece7b2398d073d11b2e352405a3ecd3a1e39c60 Mon Sep 17 00:00:00 2001 From: Sjur Braendeland Date: Wed, 28 Apr 2010 08:54:38 +0000 Subject: caif: Rewritten socket implementation Changes: This is a complete re-write of the socket layer. Making the socket implementation more aligned with the other socket layers and using more of the support functions available in sock.c. Lots of code is copied from af_unix (and some from af_irda). Non-blocking mode should be working as well. Signed-off-by: Sjur Braendeland Signed-off-by: David S. Miller --- include/linux/caif/caif_socket.h | 5 +- net/caif/caif_socket.c | 1795 ++++++++++++++++++-------------------- 2 files changed, 831 insertions(+), 969 deletions(-) (limited to 'include/linux') diff --git a/include/linux/caif/caif_socket.h b/include/linux/caif/caif_socket.h index 8e5c8444a3f..2a61eb1beb8 100644 --- a/include/linux/caif/caif_socket.h +++ b/include/linux/caif/caif_socket.h @@ -16,7 +16,6 @@ #include #endif - /** * enum caif_link_selector - Physical Link Selection. * @CAIF_LINK_HIGH_BANDW: Physical interface for high-bandwidth @@ -59,7 +58,7 @@ enum caif_channel_priority { /** * enum caif_protocol_type - CAIF Channel type. * @CAIFPROTO_AT: Classic AT channel. - * @CAIFPROTO_DATAGRAM: Datagram channel. + * @CAIFPROTO_DATAGRAM: Datagram channel. * @CAIFPROTO_DATAGRAM_LOOP: Datagram loopback channel, used for testing. * @CAIFPROTO_UTIL: Utility (Psock) channel. * @CAIFPROTO_RFM: Remote File Manager @@ -87,6 +86,7 @@ enum caif_at_type { /** * struct sockaddr_caif - the sockaddr structure for CAIF sockets. + * @family: Address family number, must be AF_CAIF. * @u: Union of address data 'switched' by family. * : * @u.at: Applies when family = CAIFPROTO_AT. @@ -153,6 +153,7 @@ struct sockaddr_caif { * * * This enum defines the CAIF Socket options to be used on a socket + * of type PF_CAIF. * */ enum caif_socket_opts { diff --git a/net/caif/caif_socket.c b/net/caif/caif_socket.c index d455375789f..c3a70c5c893 100644 --- a/net/caif/caif_socket.c +++ b/net/caif/caif_socket.c @@ -1,7 +1,6 @@ /* * Copyright (C) ST-Ericsson AB 2010 * Author: Sjur Brendeland sjur.brandeland@stericsson.com - * Per Sigmond per.sigmond@stericsson.com * License terms: GNU General Public License (GPL) version 2 */ @@ -16,91 +15,52 @@ #include #include #include -#include - +#include +#include #include +#include +#include +#include #include #include #include MODULE_LICENSE("GPL"); +MODULE_ALIAS_NETPROTO(AF_CAIF); + +#define CAIF_DEF_SNDBUF (CAIF_MAX_PAYLOAD_SIZE*10) +#define CAIF_DEF_RCVBUF (CAIF_MAX_PAYLOAD_SIZE*100) + +/* + * CAIF state is re-using the TCP socket states. + * caif_states stored in sk_state reflect the state as reported by + * the CAIF stack, while sk_socket->state is the state of the socket. + */ +enum caif_states { + CAIF_CONNECTED = TCP_ESTABLISHED, + CAIF_CONNECTING = TCP_SYN_SENT, + CAIF_DISCONNECTED = TCP_CLOSE +}; + +#define TX_FLOW_ON_BIT 1 +#define RX_FLOW_ON_BIT 2 -#define CHNL_SKT_READ_QUEUE_HIGH 200 -#define CHNL_SKT_READ_QUEUE_LOW 100 - -static int caif_sockbuf_size = 40000; -static atomic_t caif_nr_socks = ATOMIC_INIT(0); - -#define CONN_STATE_OPEN_BIT 1 -#define CONN_STATE_PENDING_BIT 2 -#define CONN_STATE_PEND_DESTROY_BIT 3 -#define CONN_REMOTE_SHUTDOWN_BIT 4 - -#define TX_FLOW_ON_BIT 1 -#define RX_FLOW_ON_BIT 2 - -#define STATE_IS_OPEN(cf_sk) test_bit(CONN_STATE_OPEN_BIT,\ - (void *) &(cf_sk)->conn_state) -#define STATE_IS_REMOTE_SHUTDOWN(cf_sk) test_bit(CONN_REMOTE_SHUTDOWN_BIT,\ - (void *) &(cf_sk)->conn_state) -#define STATE_IS_PENDING(cf_sk) test_bit(CONN_STATE_PENDING_BIT,\ - (void *) &(cf_sk)->conn_state) -#define STATE_IS_PENDING_DESTROY(cf_sk) test_bit(CONN_STATE_PEND_DESTROY_BIT,\ - (void *) &(cf_sk)->conn_state) - -#define SET_STATE_PENDING_DESTROY(cf_sk) set_bit(CONN_STATE_PEND_DESTROY_BIT,\ - (void *) &(cf_sk)->conn_state) -#define SET_STATE_OPEN(cf_sk) set_bit(CONN_STATE_OPEN_BIT,\ - (void *) &(cf_sk)->conn_state) -#define SET_STATE_CLOSED(cf_sk) clear_bit(CONN_STATE_OPEN_BIT,\ - (void *) &(cf_sk)->conn_state) -#define SET_PENDING_ON(cf_sk) set_bit(CONN_STATE_PENDING_BIT,\ - (void *) &(cf_sk)->conn_state) -#define SET_PENDING_OFF(cf_sk) clear_bit(CONN_STATE_PENDING_BIT,\ - (void *) &(cf_sk)->conn_state) -#define SET_REMOTE_SHUTDOWN(cf_sk) set_bit(CONN_REMOTE_SHUTDOWN_BIT,\ - (void *) &(cf_sk)->conn_state) - -#define SET_REMOTE_SHUTDOWN_OFF(dev) clear_bit(CONN_REMOTE_SHUTDOWN_BIT,\ - (void *) &(dev)->conn_state) -#define RX_FLOW_IS_ON(cf_sk) test_bit(RX_FLOW_ON_BIT,\ - (void *) &(cf_sk)->flow_state) -#define TX_FLOW_IS_ON(cf_sk) test_bit(TX_FLOW_ON_BIT,\ - (void *) &(cf_sk)->flow_state) - -#define SET_RX_FLOW_OFF(cf_sk) clear_bit(RX_FLOW_ON_BIT,\ - (void *) &(cf_sk)->flow_state) -#define SET_RX_FLOW_ON(cf_sk) set_bit(RX_FLOW_ON_BIT,\ - (void *) &(cf_sk)->flow_state) -#define SET_TX_FLOW_OFF(cf_sk) clear_bit(TX_FLOW_ON_BIT,\ - (void *) &(cf_sk)->flow_state) -#define SET_TX_FLOW_ON(cf_sk) set_bit(TX_FLOW_ON_BIT,\ - (void *) &(cf_sk)->flow_state) - -#define SKT_READ_FLAG 0x01 -#define SKT_WRITE_FLAG 0x02 static struct dentry *debugfsdir; -#include #ifdef CONFIG_DEBUG_FS struct debug_fs_counter { - atomic_t num_open; - atomic_t num_close; - atomic_t num_init; - atomic_t num_init_resp; - atomic_t num_init_fail_resp; - atomic_t num_deinit; - atomic_t num_deinit_resp; + atomic_t caif_nr_socks; + atomic_t num_connect_req; + atomic_t num_connect_resp; + atomic_t num_connect_fail_resp; + atomic_t num_disconnect; atomic_t num_remote_shutdown_ind; atomic_t num_tx_flow_off_ind; atomic_t num_tx_flow_on_ind; atomic_t num_rx_flow_off; atomic_t num_rx_flow_on; - atomic_t skb_in_use; - atomic_t skb_alloc; - atomic_t skb_free; }; -static struct debug_fs_counter cnt; +struct debug_fs_counter cnt; #define dbfs_atomic_inc(v) atomic_inc(v) #define dbfs_atomic_dec(v) atomic_dec(v) #else @@ -108,624 +68,666 @@ static struct debug_fs_counter cnt; #define dbfs_atomic_dec(v) #endif -/* The AF_CAIF socket */ struct caifsock { - /* NOTE: sk has to be the first member */ - struct sock sk; + struct sock sk; /* must be first member */ struct cflayer layer; - char name[CAIF_LAYER_NAME_SZ]; - u32 conn_state; + char name[CAIF_LAYER_NAME_SZ]; /* Used for debugging */ u32 flow_state; - struct cfpktq *pktq; - int file_mode; struct caif_connect_request conn_req; - int read_queue_len; - /* protect updates of read_queue_len */ - spinlock_t read_queue_len_lock; + struct mutex readlock; struct dentry *debugfs_socket_dir; }; -static void drain_queue(struct caifsock *cf_sk); +static int rx_flow_is_on(struct caifsock *cf_sk) +{ + return test_bit(RX_FLOW_ON_BIT, + (void *) &cf_sk->flow_state); +} + +static int tx_flow_is_on(struct caifsock *cf_sk) +{ + return test_bit(TX_FLOW_ON_BIT, + (void *) &cf_sk->flow_state); +} -/* Packet Receive Callback function called from CAIF Stack */ -static int caif_sktrecv_cb(struct cflayer *layr, struct cfpkt *pkt) +static void set_rx_flow_off(struct caifsock *cf_sk) { - struct caifsock *cf_sk; - int read_queue_high; - cf_sk = container_of(layr, struct caifsock, layer); + clear_bit(RX_FLOW_ON_BIT, + (void *) &cf_sk->flow_state); +} - if (!STATE_IS_OPEN(cf_sk)) { - /*FIXME: This should be allowed finally!*/ - pr_debug("CAIF: %s(): called after close request\n", __func__); - cfpkt_destroy(pkt); - return 0; - } - /* NOTE: This function may be called in Tasklet context! */ +static void set_rx_flow_on(struct caifsock *cf_sk) +{ + set_bit(RX_FLOW_ON_BIT, + (void *) &cf_sk->flow_state); +} - /* The queue has its own lock */ - cfpkt_queue(cf_sk->pktq, pkt, 0); +static void set_tx_flow_off(struct caifsock *cf_sk) +{ + clear_bit(TX_FLOW_ON_BIT, + (void *) &cf_sk->flow_state); +} - spin_lock(&cf_sk->read_queue_len_lock); - cf_sk->read_queue_len++; +static void set_tx_flow_on(struct caifsock *cf_sk) +{ + set_bit(TX_FLOW_ON_BIT, + (void *) &cf_sk->flow_state); +} - read_queue_high = (cf_sk->read_queue_len > CHNL_SKT_READ_QUEUE_HIGH); - spin_unlock(&cf_sk->read_queue_len_lock); +static void caif_read_lock(struct sock *sk) +{ + struct caifsock *cf_sk; + cf_sk = container_of(sk, struct caifsock, sk); + mutex_lock(&cf_sk->readlock); +} - if (RX_FLOW_IS_ON(cf_sk) && read_queue_high) { - dbfs_atomic_inc(&cnt.num_rx_flow_off); - SET_RX_FLOW_OFF(cf_sk); +static void caif_read_unlock(struct sock *sk) +{ + struct caifsock *cf_sk; + cf_sk = container_of(sk, struct caifsock, sk); + mutex_unlock(&cf_sk->readlock); +} - /* Send flow off (NOTE: must not sleep) */ - pr_debug("CAIF: %s():" - " sending flow OFF (queue len = %d)\n", - __func__, - cf_sk->read_queue_len); - caif_assert(cf_sk->layer.dn); - caif_assert(cf_sk->layer.dn->ctrlcmd); +int sk_rcvbuf_lowwater(struct caifsock *cf_sk) +{ + /* A quarter of full buffer is used a low water mark */ + return cf_sk->sk.sk_rcvbuf / 4; +} - (void) cf_sk->layer.dn->modemcmd(cf_sk->layer.dn, - CAIF_MODEMCMD_FLOW_OFF_REQ); - } +void caif_flow_ctrl(struct sock *sk, int mode) +{ + struct caifsock *cf_sk; + cf_sk = container_of(sk, struct caifsock, sk); + if (cf_sk->layer.dn) + cf_sk->layer.dn->modemcmd(cf_sk->layer.dn, mode); +} - /* Signal reader that data is available. */ +/* + * Copied from sock.c:sock_queue_rcv_skb(), but changed so packets are + * not dropped, but CAIF is sending flow off instead. + */ +int caif_queue_rcv_skb(struct sock *sk, struct sk_buff *skb) +{ + int err; + int skb_len; + unsigned long flags; + struct sk_buff_head *list = &sk->sk_receive_queue; + struct caifsock *cf_sk = container_of(sk, struct caifsock, sk); - wake_up_interruptible(sk_sleep(&cf_sk->sk)); + if (atomic_read(&sk->sk_rmem_alloc) + skb->truesize >= + (unsigned)sk->sk_rcvbuf && rx_flow_is_on(cf_sk)) { + trace_printk("CAIF: %s():" + " sending flow OFF (queue len = %d %d)\n", + __func__, + atomic_read(&cf_sk->sk.sk_rmem_alloc), + sk_rcvbuf_lowwater(cf_sk)); + set_rx_flow_off(cf_sk); + if (cf_sk->layer.dn) + cf_sk->layer.dn->modemcmd(cf_sk->layer.dn, + CAIF_MODEMCMD_FLOW_OFF_REQ); + } + err = sk_filter(sk, skb); + if (err) + return err; + if (!sk_rmem_schedule(sk, skb->truesize) && rx_flow_is_on(cf_sk)) { + set_rx_flow_off(cf_sk); + trace_printk("CAIF: %s():" + " sending flow OFF due to rmem_schedule\n", + __func__); + if (cf_sk->layer.dn) + cf_sk->layer.dn->modemcmd(cf_sk->layer.dn, + CAIF_MODEMCMD_FLOW_OFF_REQ); + } + skb->dev = NULL; + skb_set_owner_r(skb, sk); + /* Cache the SKB length before we tack it onto the receive + * queue. Once it is added it no longer belongs to us and + * may be freed by other threads of control pulling packets + * from the queue. + */ + skb_len = skb->len; + spin_lock_irqsave(&list->lock, flags); + if (!sock_flag(sk, SOCK_DEAD)) + __skb_queue_tail(list, skb); + spin_unlock_irqrestore(&list->lock, flags); + + if (!sock_flag(sk, SOCK_DEAD)) + sk->sk_data_ready(sk, skb_len); + else + kfree_skb(skb); return 0; } -/* Packet Flow Control Callback function called from CAIF */ -static void caif_sktflowctrl_cb(struct cflayer *layr, - enum caif_ctrlcmd flow, - int phyid) +/* Packet Receive Callback function called from CAIF Stack */ +static int caif_sktrecv_cb(struct cflayer *layr, struct cfpkt *pkt) { struct caifsock *cf_sk; - - /* NOTE: This function may be called in Tasklet context! */ - pr_debug("CAIF: %s(): flowctrl func called: %s.\n", - __func__, - flow == CAIF_CTRLCMD_FLOW_ON_IND ? "ON" : - flow == CAIF_CTRLCMD_FLOW_OFF_IND ? "OFF" : - flow == CAIF_CTRLCMD_INIT_RSP ? "INIT_RSP" : - flow == CAIF_CTRLCMD_DEINIT_RSP ? "DEINIT_RSP" : - flow == CAIF_CTRLCMD_INIT_FAIL_RSP ? "INIT_FAIL_RSP" : - flow == - CAIF_CTRLCMD_REMOTE_SHUTDOWN_IND ? "REMOTE_SHUTDOWN" : - "UKNOWN CTRL COMMAND"); - - if (layr == NULL) - return; + struct sk_buff *skb; cf_sk = container_of(layr, struct caifsock, layer); + skb = cfpkt_tonative(pkt); + + if (unlikely(cf_sk->sk.sk_state != CAIF_CONNECTED)) { + cfpkt_destroy(pkt); + return 0; + } + caif_queue_rcv_skb(&cf_sk->sk, skb); + return 0; +} +/* Packet Control Callback function called from CAIF */ +static void caif_ctrl_cb(struct cflayer *layr, + enum caif_ctrlcmd flow, + int phyid) +{ + struct caifsock *cf_sk = container_of(layr, struct caifsock, layer); switch (flow) { case CAIF_CTRLCMD_FLOW_ON_IND: + /* OK from modem to start sending again */ dbfs_atomic_inc(&cnt.num_tx_flow_on_ind); - /* Signal reader that data is available. */ - SET_TX_FLOW_ON(cf_sk); - wake_up_interruptible(sk_sleep(&cf_sk->sk)); + set_tx_flow_on(cf_sk); + cf_sk->sk.sk_state_change(&cf_sk->sk); break; case CAIF_CTRLCMD_FLOW_OFF_IND: + /* Modem asks us to shut up */ dbfs_atomic_inc(&cnt.num_tx_flow_off_ind); - SET_TX_FLOW_OFF(cf_sk); + set_tx_flow_off(cf_sk); + cf_sk->sk.sk_state_change(&cf_sk->sk); break; case CAIF_CTRLCMD_INIT_RSP: - dbfs_atomic_inc(&cnt.num_init_resp); - /* Signal reader that data is available. */ - caif_assert(STATE_IS_OPEN(cf_sk)); - SET_PENDING_OFF(cf_sk); - SET_TX_FLOW_ON(cf_sk); - wake_up_interruptible(sk_sleep(&cf_sk->sk)); + /* We're now connected */ + dbfs_atomic_inc(&cnt.num_connect_resp); + cf_sk->sk.sk_state = CAIF_CONNECTED; + set_tx_flow_on(cf_sk); + cf_sk->sk.sk_state_change(&cf_sk->sk); break; case CAIF_CTRLCMD_DEINIT_RSP: - dbfs_atomic_inc(&cnt.num_deinit_resp); - caif_assert(!STATE_IS_OPEN(cf_sk)); - SET_PENDING_OFF(cf_sk); - if (!STATE_IS_PENDING_DESTROY(cf_sk)) { - if (sk_sleep(&cf_sk->sk) != NULL) - wake_up_interruptible(sk_sleep(&cf_sk->sk)); - } - dbfs_atomic_inc(&cnt.num_deinit); - sock_put(&cf_sk->sk); + /* We're now disconnected */ + cf_sk->sk.sk_state = CAIF_DISCONNECTED; + cf_sk->sk.sk_state_change(&cf_sk->sk); + cfcnfg_release_adap_layer(&cf_sk->layer); break; case CAIF_CTRLCMD_INIT_FAIL_RSP: - dbfs_atomic_inc(&cnt.num_init_fail_resp); - caif_assert(STATE_IS_OPEN(cf_sk)); - SET_STATE_CLOSED(cf_sk); - SET_PENDING_OFF(cf_sk); - SET_TX_FLOW_OFF(cf_sk); - wake_up_interruptible(sk_sleep(&cf_sk->sk)); + /* Connect request failed */ + dbfs_atomic_inc(&cnt.num_connect_fail_resp); + cf_sk->sk.sk_err = ECONNREFUSED; + cf_sk->sk.sk_state = CAIF_DISCONNECTED; + cf_sk->sk.sk_shutdown = SHUTDOWN_MASK; + /* + * Socket "standards" seems to require POLLOUT to + * be set at connect failure. + */ + set_tx_flow_on(cf_sk); + cf_sk->sk.sk_state_change(&cf_sk->sk); break; case CAIF_CTRLCMD_REMOTE_SHUTDOWN_IND: + /* Modem has closed this connection, or device is down. */ dbfs_atomic_inc(&cnt.num_remote_shutdown_ind); - SET_REMOTE_SHUTDOWN(cf_sk); - /* Use sk_shutdown to indicate remote shutdown indication */ - cf_sk->sk.sk_shutdown |= RCV_SHUTDOWN; - cf_sk->file_mode = 0; - wake_up_interruptible(sk_sleep(&cf_sk->sk)); + cf_sk->sk.sk_shutdown = SHUTDOWN_MASK; + cf_sk->sk.sk_err = ECONNRESET; + set_rx_flow_on(cf_sk); + cf_sk->sk.sk_error_report(&cf_sk->sk); break; default: pr_debug("CAIF: %s(): Unexpected flow command %d\n", - __func__, flow); + __func__, flow); } } -static void skb_destructor(struct sk_buff *skb) +static void caif_check_flow_release(struct sock *sk) { - dbfs_atomic_inc(&cnt.skb_free); - dbfs_atomic_dec(&cnt.skb_in_use); -} + struct caifsock *cf_sk = container_of(sk, struct caifsock, sk); + if (cf_sk->layer.dn == NULL || cf_sk->layer.dn->modemcmd == NULL) + return; + if (rx_flow_is_on(cf_sk)) + return; -static int caif_recvmsg(struct kiocb *iocb, struct socket *sock, + if (atomic_read(&sk->sk_rmem_alloc) <= sk_rcvbuf_lowwater(cf_sk)) { + dbfs_atomic_inc(&cnt.num_rx_flow_on); + set_rx_flow_on(cf_sk); + cf_sk->layer.dn->modemcmd(cf_sk->layer.dn, + CAIF_MODEMCMD_FLOW_ON_REQ); + } +} +/* + * Copied from sock.c:sock_queue_rcv_skb(), and added check that user buffer + * has sufficient size. + */ + +static int caif_seqpkt_recvmsg(struct kiocb *iocb, struct socket *sock, struct msghdr *m, size_t buf_len, int flags) { struct sock *sk = sock->sk; - struct caifsock *cf_sk = container_of(sk, struct caifsock, sk); - struct cfpkt *pkt = NULL; - size_t len; - int result; struct sk_buff *skb; - ssize_t ret = -EIO; - int read_queue_low; - - if (cf_sk == NULL) { - pr_debug("CAIF: %s(): private_data not set!\n", - __func__); - ret = -EBADFD; - goto read_error; - } - - /* Don't do multiple iovec entries yet */ - if (m->msg_iovlen != 1) - return -EOPNOTSUPP; + int ret = 0; + int len; if (unlikely(!buf_len)) return -EINVAL; - lock_sock(&(cf_sk->sk)); - - caif_assert(cf_sk->pktq); - - if (!STATE_IS_OPEN(cf_sk)) { - /* Socket is closed or closing. */ - if (!STATE_IS_PENDING(cf_sk)) { - pr_debug("CAIF: %s(): socket is closed (by remote)\n", - __func__); - ret = -EPIPE; - } else { - pr_debug("CAIF: %s(): socket is closing..\n", __func__); - ret = -EBADF; - } + skb = skb_recv_datagram(sk, flags, 0 , &ret); + if (!skb) goto read_error; - } - /* Socket is open or opening. */ - if (STATE_IS_PENDING(cf_sk)) { - pr_debug("CAIF: %s(): socket is opening...\n", __func__); - - if (flags & MSG_DONTWAIT) { - /* We can't block. */ - pr_debug("CAIF: %s():state pending and MSG_DONTWAIT\n", - __func__); - ret = -EAGAIN; - goto read_error; - } + len = skb->len; + + if (skb && skb->len > buf_len && !(flags & MSG_PEEK)) { + len = buf_len; /* - * Blocking mode; state is pending and we need to wait - * for its conclusion. + * Push skb back on receive queue if buffer too small. + * This has a built-in race where multi-threaded receive + * may get packet in wrong order, but multiple read does + * not really guarantee ordered delivery anyway. + * Let's optimize for speed without taking locks. */ - release_sock(&cf_sk->sk); - - result = - wait_event_interruptible(*sk_sleep(&cf_sk->sk), - !STATE_IS_PENDING(cf_sk)); - lock_sock(&(cf_sk->sk)); - - if (result == -ERESTARTSYS) { - pr_debug("CAIF: %s(): wait_event_interruptible" - " woken by a signal (1)", __func__); - ret = -ERESTARTSYS; - goto read_error; - } + skb_queue_head(&sk->sk_receive_queue, skb); + ret = -EMSGSIZE; + goto read_error; } - if (STATE_IS_REMOTE_SHUTDOWN(cf_sk) || - !STATE_IS_OPEN(cf_sk) || - STATE_IS_PENDING(cf_sk)) { - - pr_debug("CAIF: %s(): socket closed\n", - __func__); - ret = -ESHUTDOWN; + ret = skb_copy_datagram_iovec(skb, 0, m->msg_iov, len); + if (ret) goto read_error; - } - /* - * Block if we don't have any received buffers. - * The queue has its own lock. - */ - while ((pkt = cfpkt_qpeek(cf_sk->pktq)) == NULL) { + skb_free_datagram(sk, skb); - if (flags & MSG_DONTWAIT) { - pr_debug("CAIF: %s(): MSG_DONTWAIT\n", __func__); - ret = -EAGAIN; - goto read_error; - } - trace_printk("CAIF: %s() wait_event\n", __func__); + caif_check_flow_release(sk); - /* Let writers in. */ - release_sock(&cf_sk->sk); + return len; - /* Block reader until data arrives or socket is closed. */ - if (wait_event_interruptible(*sk_sleep(&cf_sk->sk), - cfpkt_qpeek(cf_sk->pktq) - || STATE_IS_REMOTE_SHUTDOWN(cf_sk) - || !STATE_IS_OPEN(cf_sk)) == - -ERESTARTSYS) { - pr_debug("CAIF: %s():" - " wait_event_interruptible woken by " - "a signal, signal_pending(current) = %d\n", - __func__, - signal_pending(current)); - return -ERESTARTSYS; - } +read_error: + return ret; +} - trace_printk("CAIF: %s() awake\n", __func__); - if (STATE_IS_REMOTE_SHUTDOWN(cf_sk)) { - pr_debug("CAIF: %s(): " - "received remote_shutdown indication\n", - __func__); - ret = -ESHUTDOWN; - goto read_error_no_unlock; - } - /* I want to be alone on cf_sk (except status and queue). */ - lock_sock(&(cf_sk->sk)); +/* Copied from unix_stream_wait_data, identical except for lock call. */ +static long caif_stream_data_wait(struct sock *sk, long timeo) +{ + DEFINE_WAIT(wait); + lock_sock(sk); + + for (;;) { + prepare_to_wait(sk_sleep(sk), &wait, TASK_INTERRUPTIBLE); + + if (!skb_queue_empty(&sk->sk_receive_queue) || + sk->sk_err || + sk->sk_state != CAIF_CONNECTED || + sock_flag(sk, SOCK_DEAD) || + (sk->sk_shutdown & RCV_SHUTDOWN) || + signal_pending(current) || + !timeo) + break; - if (!STATE_IS_OPEN(cf_sk)) { - /* Someone closed the link, report error. */ - pr_debug("CAIF: %s(): remote end shutdown!\n", - __func__); - ret = -EPIPE; - goto read_error; - } + set_bit(SOCK_ASYNC_WAITDATA, &sk->sk_socket->flags); + release_sock(sk); + timeo = schedule_timeout(timeo); + lock_sock(sk); + clear_bit(SOCK_ASYNC_WAITDATA, &sk->sk_socket->flags); } - /* The queue has its own lock. */ - len = cfpkt_getlen(pkt); - - /* Check max length that can be copied. */ - if (len <= buf_len) - pkt = cfpkt_dequeue(cf_sk->pktq); - else { - pr_debug("CAIF: %s(): user buffer too small (%ld,%ld)\n", - __func__, (long) len, (long) buf_len); - if (sock->type == SOCK_SEQPACKET) { - ret = -EMSGSIZE; - goto read_error; - } - len = buf_len; - } + finish_wait(sk_sleep(sk), &wait); + release_sock(sk); + return timeo; +} - spin_lock(&cf_sk->read_queue_len_lock); - cf_sk->read_queue_len--; - read_queue_low = (cf_sk->read_queue_len < CHNL_SKT_READ_QUEUE_LOW); - spin_unlock(&cf_sk->read_queue_len_lock); +/* + * Copied from unix_stream_recvmsg, but removed credit checks, + * changed locking calls, changed address handling. + */ +static int caif_stream_recvmsg(struct kiocb *iocb, struct socket *sock, + struct msghdr *msg, size_t size, + int flags) +{ + struct sock *sk = sock->sk; + int copied = 0; + int target; + int err = 0; + long timeo; - if (!RX_FLOW_IS_ON(cf_sk) && read_queue_low) { - dbfs_atomic_inc(&cnt.num_rx_flow_on); - SET_RX_FLOW_ON(cf_sk); + err = -EOPNOTSUPP; + if (flags&MSG_OOB) + goto out; - /* Send flow on. */ - pr_debug("CAIF: %s(): sending flow ON (queue len = %d)\n", - __func__, cf_sk->read_queue_len); - caif_assert(cf_sk->layer.dn); - caif_assert(cf_sk->layer.dn->ctrlcmd); - (void) cf_sk->layer.dn->modemcmd(cf_sk->layer.dn, - CAIF_MODEMCMD_FLOW_ON_REQ); + msg->msg_namelen = 0; - caif_assert(cf_sk->read_queue_len >= 0); - } + /* + * Lock the socket to prevent queue disordering + * while sleeps in memcpy_tomsg + */ + err = -EAGAIN; + if (sk->sk_state == CAIF_CONNECTING) + goto out; - skb = cfpkt_tonative(pkt); - result = skb_copy_datagram_iovec(skb, 0, m->msg_iov, len); - skb_pull(skb, len); + caif_read_lock(sk); + target = sock_rcvlowat(sk, flags&MSG_WAITALL, size); + timeo = sock_rcvtimeo(sk, flags&MSG_DONTWAIT); - if (result) { - pr_debug("CAIF: %s(): copy to_iovec failed\n", __func__); - cfpkt_destroy(pkt); - ret = -EFAULT; - goto read_error; - } + do { + int chunk; + struct sk_buff *skb; - /* Free packet and remove from queue */ - if (skb->len == 0) - skb_free_datagram(sk, skb); + lock_sock(sk); + skb = skb_dequeue(&sk->sk_receive_queue); + caif_check_flow_release(sk); - /* Let the others in. */ - release_sock(&cf_sk->sk); - return len; + if (skb == NULL) { + if (copied >= target) + goto unlock; + /* + * POSIX 1003.1g mandates this order. + */ + err = sock_error(sk); + if (err) + goto unlock; + err = -ECONNRESET; + if (sk->sk_shutdown & RCV_SHUTDOWN) + goto unlock; -read_error: - release_sock(&cf_sk->sk); -read_error_no_unlock: - return ret; -} + err = -EPIPE; + if (sk->sk_state != CAIF_CONNECTED) + goto unlock; + if (sock_flag(sk, SOCK_DEAD)) + goto unlock; -/* Send a signal as a consequence of sendmsg, sendto or caif_sendmsg. */ -static int caif_sendmsg(struct kiocb *kiocb, struct socket *sock, - struct msghdr *msg, size_t len) -{ + release_sock(sk); - struct sock *sk = sock->sk; - struct caifsock *cf_sk = container_of(sk, struct caifsock, sk); - size_t payload_size = msg->msg_iov->iov_len; - struct cfpkt *pkt = NULL; - struct caif_payload_info info; - unsigned char *txbuf; - ssize_t ret = -EIO; - int result; - struct sk_buff *skb; - caif_assert(msg->msg_iovlen == 1); + err = -EAGAIN; + if (!timeo) + break; - if (cf_sk == NULL) { - pr_debug("CAIF: %s(): private_data not set!\n", - __func__); - ret = -EBADFD; - goto write_error_no_unlock; - } + caif_read_unlock(sk); - if (unlikely(msg->msg_iov->iov_base == NULL)) { - pr_warning("CAIF: %s(): Buffer is NULL.\n", __func__); - ret = -EINVAL; - goto write_error_no_unlock; - } + timeo = caif_stream_data_wait(sk, timeo); - if (payload_size > CAIF_MAX_PAYLOAD_SIZE) { - pr_debug("CAIF: %s(): buffer too long\n", __func__); - if (sock->type == SOCK_SEQPACKET) { - ret = -EINVAL; - goto write_error_no_unlock; + if (signal_pending(current)) { + err = sock_intr_errno(timeo); + goto out; + } + caif_read_lock(sk); + continue; +unlock: + release_sock(sk); + break; } - payload_size = CAIF_MAX_PAYLOAD_SIZE; - } + release_sock(sk); + chunk = min_t(unsigned int, skb->len, size); + if (memcpy_toiovec(msg->msg_iov, skb->data, chunk)) { + skb_queue_head(&sk->sk_receive_queue, skb); + if (copied == 0) + copied = -EFAULT; + break; + } + copied += chunk; + size -= chunk; - /* I want to be alone on cf_sk (except status and queue) */ - lock_sock(&(cf_sk->sk)); + /* Mark read part of skb as used */ + if (!(flags & MSG_PEEK)) { + skb_pull(skb, chunk); - caif_assert(cf_sk->pktq); + /* put the skb back if we didn't use it up. */ + if (skb->len) { + skb_queue_head(&sk->sk_receive_queue, skb); + break; + } + kfree_skb(skb); - if (!STATE_IS_OPEN(cf_sk)) { - /* Socket is closed or closing */ - if (!STATE_IS_PENDING(cf_sk)) { - pr_debug("CAIF: %s(): socket is closed (by remote)\n", - __func__); - ret = -EPIPE; } else { - pr_debug("CAIF: %s(): socket is closing...\n", - __func__); - ret = -EBADF; - } - goto write_error; - } - - /* Socket is open or opening */ - if (STATE_IS_PENDING(cf_sk)) { - pr_debug("CAIF: %s(): socket is opening...\n", __func__); - - if (msg->msg_flags & MSG_DONTWAIT) { - /* We can't block */ - trace_printk("CAIF: %s():state pending:" - "state=MSG_DONTWAIT\n", __func__); - ret = -EAGAIN; - goto write_error; + /* + * It is questionable, see note in unix_dgram_recvmsg. + */ + /* put message back and return */ + skb_queue_head(&sk->sk_receive_queue, skb); + break; } - /* Let readers in */ - release_sock(&cf_sk->sk); - - /* - * Blocking mode; state is pending and we need to wait - * for its conclusion. - */ - result = - wait_event_interruptible(*sk_sleep(&cf_sk->sk), - !STATE_IS_PENDING(cf_sk)); - /* I want to be alone on cf_sk (except status and queue) */ - lock_sock(&(cf_sk->sk)); + } while (size); + caif_read_unlock(sk); - if (result == -ERESTARTSYS) { - pr_debug("CAIF: %s(): wait_event_interruptible" - " woken by a signal (1)", __func__); - ret = -ERESTARTSYS; - goto write_error; - } - } - if (STATE_IS_REMOTE_SHUTDOWN(cf_sk) || - !STATE_IS_OPEN(cf_sk) || - STATE_IS_PENDING(cf_sk)) { +out: + return copied ? : err; +} - pr_debug("CAIF: %s(): socket closed\n", - __func__); - ret = -ESHUTDOWN; - goto write_error; +/* + * Copied from sock.c:sock_wait_for_wmem, but change to wait for + * CAIF flow-on and sock_writable. + */ +static long caif_wait_for_flow_on(struct caifsock *cf_sk, + int wait_writeable, long timeo, int *err) +{ + struct sock *sk = &cf_sk->sk; + DEFINE_WAIT(wait); + for (;;) { + *err = 0; + if (tx_flow_is_on(cf_sk) && + (!wait_writeable || sock_writeable(&cf_sk->sk))) + break; + *err = -ETIMEDOUT; + if (!timeo) + break; + *err = -ERESTARTSYS; + if (signal_pending(current)) + break; + prepare_to_wait(sk_sleep(sk), &wait, TASK_INTERRUPTIBLE); + *err = -ECONNRESET; + if (sk->sk_shutdown & SHUTDOWN_MASK) + break; + *err = -sk->sk_err; + if (sk->sk_err) + break; + *err = -EPIPE; + if (cf_sk->sk.sk_state != CAIF_CONNECTED) + break; + timeo = schedule_timeout(timeo); } + finish_wait(sk_sleep(sk), &wait); + return timeo; +} - if (!TX_FLOW_IS_ON(cf_sk)) { +/* + * Transmit a SKB. The device may temporarily request re-transmission + * by returning EAGAIN. + */ +static int transmit_skb(struct sk_buff *skb, struct caifsock *cf_sk, + int noblock, long timeo) +{ + struct cfpkt *pkt; + int ret, loopcnt = 0; - /* Flow is off. Check non-block flag */ - if (msg->msg_flags & MSG_DONTWAIT) { - trace_printk("CAIF: %s(): MSG_DONTWAIT and tx flow off", - __func__); - ret = -EAGAIN; - goto write_error; - } + pkt = cfpkt_fromnative(CAIF_DIR_OUT, skb); + memset(cfpkt_info(pkt), 0, sizeof(struct caif_payload_info)); + do { - /* release lock before waiting */ - release_sock(&cf_sk->sk); + ret = -ETIMEDOUT; - /* Wait until flow is on or socket is closed */ - if (wait_event_interruptible(*sk_sleep(&cf_sk->sk), - TX_FLOW_IS_ON(cf_sk) - || !STATE_IS_OPEN(cf_sk) - || STATE_IS_REMOTE_SHUTDOWN(cf_sk) - ) == -ERESTARTSYS) { - pr_debug("CAIF: %s():" - " wait_event_interruptible woken by a signal", - __func__); - ret = -ERESTARTSYS; - goto write_error_no_unlock; + /* Slight paranoia, probably not needed. */ + if (unlikely(loopcnt++ > 1000)) { + pr_warning("CAIF: %s(): transmit retries failed," + " error = %d\n", __func__, ret); + break; } - /* I want to be alone on cf_sk (except status and queue) */ - lock_sock(&(cf_sk->sk)); - - if (!STATE_IS_OPEN(cf_sk)) { - /* someone closed the link, report error */ - pr_debug("CAIF: %s(): remote end shutdown!\n", - __func__); - ret = -EPIPE; - goto write_error; + if (cf_sk->layer.dn != NULL) + ret = cf_sk->layer.dn->transmit(cf_sk->layer.dn, pkt); + if (likely(ret >= 0)) + break; + /* if transmit return -EAGAIN, then retry */ + if (noblock && ret == -EAGAIN) + break; + timeo = caif_wait_for_flow_on(cf_sk, 0, timeo, &ret); + if (signal_pending(current)) { + ret = sock_intr_errno(timeo); + break; } - - if (STATE_IS_REMOTE_SHUTDOWN(cf_sk)) { - pr_debug("CAIF: %s(): " - "received remote_shutdown indication\n", - __func__); - ret = -ESHUTDOWN; - goto write_error; + if (ret) + break; + if (cf_sk->sk.sk_state != CAIF_CONNECTED || + sock_flag(&cf_sk->sk, SOCK_DEAD) || + (cf_sk->sk.sk_shutdown & RCV_SHUTDOWN)) { + ret = -EPIPE; + cf_sk->sk.sk_err = EPIPE; + break; } - } + } while (ret == -EAGAIN); + return ret; +} - pkt = cfpkt_create(payload_size); - skb = (struct sk_buff *)pkt; - skb->destructor = skb_destructor; - skb->sk = sk; - dbfs_atomic_inc(&cnt.skb_alloc); - dbfs_atomic_inc(&cnt.skb_in_use); - if (cfpkt_raw_append(pkt, (void **) &txbuf, payload_size) < 0) { - pr_debug("CAIF: %s(): cfpkt_raw_append failed\n", __func__); - cfpkt_destroy(pkt); - ret = -EINVAL; - goto write_error; - } +/* Copied from af_unix:unix_dgram_sendmsg, and adapted to CAIF */ +static int caif_seqpkt_sendmsg(struct kiocb *kiocb, struct socket *sock, + struct msghdr *msg, size_t len) +{ + struct sock *sk = sock->sk; + struct caifsock *cf_sk = container_of(sk, struct caifsock, sk); + int buffer_size; + int ret = 0; + struct sk_buff *skb = NULL; + int noblock; + long timeo; + caif_assert(cf_sk); + ret = sock_error(sk); + if (ret) + goto err; + + ret = -EOPNOTSUPP; + if (msg->msg_flags&MSG_OOB) + goto err; + + ret = -EOPNOTSUPP; + if (msg->msg_namelen) + goto err; + + ret = -EINVAL; + if (unlikely(msg->msg_iov->iov_base == NULL)) + goto err; + noblock = msg->msg_flags & MSG_DONTWAIT; + + buffer_size = len + CAIF_NEEDED_HEADROOM + CAIF_NEEDED_TAILROOM; + + ret = -EMSGSIZE; + if (buffer_size > CAIF_MAX_PAYLOAD_SIZE) + goto err; + + timeo = sock_sndtimeo(sk, noblock); + timeo = caif_wait_for_flow_on(container_of(sk, struct caifsock, sk), + 1, timeo, &ret); + + ret = -EPIPE; + if (cf_sk->sk.sk_state != CAIF_CONNECTED || + sock_flag(sk, SOCK_DEAD) || + (sk->sk_shutdown & RCV_SHUTDOWN)) + goto err; + + ret = -ENOMEM; + skb = sock_alloc_send_skb(sk, buffer_size, noblock, &ret); + if (!skb) + goto err; + skb_reserve(skb, CAIF_NEEDED_HEADROOM); + + ret = memcpy_fromiovec(skb_put(skb, len), msg->msg_iov, len); + + if (ret) + goto err; + ret = transmit_skb(skb, cf_sk, noblock, timeo); + if (ret < 0) + goto err; + return len; +err: + kfree_skb(skb); + return ret; +} - /* Copy data into buffer. */ - if (copy_from_user(txbuf, msg->msg_iov->iov_base, payload_size)) { - pr_debug("CAIF: %s(): copy_from_user returned non zero.\n", - __func__); - cfpkt_destroy(pkt); - ret = -EINVAL; - goto write_error; - } - memset(&info, 0, sizeof(info)); +/* + * Copied from unix_stream_sendmsg and adapted to CAIF: + * Changed removed permission handling and added waiting for flow on + * and other minor adaptations. + */ +static int caif_stream_sendmsg(struct kiocb *kiocb, struct socket *sock, + struct msghdr *msg, size_t len) +{ + struct sock *sk = sock->sk; + struct caifsock *cf_sk = container_of(sk, struct caifsock, sk); + int err, size; + struct sk_buff *skb; + int sent = 0; + long timeo; - /* Send the packet down the stack. */ - caif_assert(cf_sk->layer.dn); - caif_assert(cf_sk->layer.dn->transmit); + err = -EOPNOTSUPP; - do { - ret = cf_sk->layer.dn->transmit(cf_sk->layer.dn, pkt); + if (unlikely(msg->msg_flags&MSG_OOB)) + goto out_err; - if (likely((ret >= 0) || (ret != -EAGAIN))) - break; + if (unlikely(msg->msg_namelen)) + goto out_err; - /* EAGAIN - retry */ - if (msg->msg_flags & MSG_DONTWAIT) { - pr_debug("CAIF: %s(): NONBLOCK and transmit failed," - " error = %ld\n", __func__, (long) ret); - ret = -EAGAIN; - goto write_error; - } + timeo = sock_sndtimeo(sk, msg->msg_flags & MSG_DONTWAIT); + timeo = caif_wait_for_flow_on(cf_sk, 1, timeo, &err); - /* Let readers in */ - release_sock(&cf_sk->sk); + if (unlikely(sk->sk_shutdown & SEND_SHUTDOWN)) + goto pipe_err; - /* Wait until flow is on or socket is closed */ - if (wait_event_interruptible(*sk_sleep(&cf_sk->sk), - TX_FLOW_IS_ON(cf_sk) - || !STATE_IS_OPEN(cf_sk) - || STATE_IS_REMOTE_SHUTDOWN(cf_sk) - ) == -ERESTARTSYS) { - pr_debug("CAIF: %s(): wait_event_interruptible" - " woken by a signal", __func__); - ret = -ERESTARTSYS; - goto write_error_no_unlock; - } + while (sent < len) { - /* I want to be alone on cf_sk (except status and queue) */ - lock_sock(&(cf_sk->sk)); + size = len-sent; - } while (ret == -EAGAIN); + if (size > CAIF_MAX_PAYLOAD_SIZE) + size = CAIF_MAX_PAYLOAD_SIZE; - if (ret < 0) { - cfpkt_destroy(pkt); - pr_debug("CAIF: %s(): transmit failed, error = %ld\n", - __func__, (long) ret); + /* If size is more than half of sndbuf, chop up message */ + if (size > ((sk->sk_sndbuf >> 1) - 64)) + size = (sk->sk_sndbuf >> 1) - 64; - goto write_error; - } + if (size > SKB_MAX_ALLOC) + size = SKB_MAX_ALLOC; - release_sock(&cf_sk->sk); - return payload_size; + skb = sock_alloc_send_skb(sk, + size + CAIF_NEEDED_HEADROOM + + CAIF_NEEDED_TAILROOM, + msg->msg_flags&MSG_DONTWAIT, + &err); + if (skb == NULL) + goto out_err; -write_error: - release_sock(&cf_sk->sk); -write_error_no_unlock: - return ret; -} + skb_reserve(skb, CAIF_NEEDED_HEADROOM); + /* + * If you pass two values to the sock_alloc_send_skb + * it tries to grab the large buffer with GFP_NOFS + * (which can fail easily), and if it fails grab the + * fallback size buffer which is under a page and will + * succeed. [Alan] + */ + size = min_t(int, size, skb_tailroom(skb)); -static unsigned int caif_poll(struct file *file, struct socket *sock, - poll_table *wait) -{ - struct sock *sk = sock->sk; - struct caifsock *cf_sk = container_of(sk, struct caifsock, sk); - u32 mask = 0; - poll_wait(file, sk_sleep(sk), wait); - lock_sock(&(cf_sk->sk)); - if (!STATE_IS_OPEN(cf_sk)) { - if (!STATE_IS_PENDING(cf_sk)) - mask |= POLLHUP; - } else { - if (cfpkt_qpeek(cf_sk->pktq) != NULL) - mask |= (POLLIN | POLLRDNORM); - if (TX_FLOW_IS_ON(cf_sk)) - mask |= (POLLOUT | POLLWRNORM); + err = memcpy_fromiovec(skb_put(skb, size), msg->msg_iov, size); + if (err) { + kfree_skb(skb); + goto out_err; + } + err = transmit_skb(skb, cf_sk, + msg->msg_flags&MSG_DONTWAIT, timeo); + if (err < 0) { + kfree_skb(skb); + goto pipe_err; + } + sent += size; } - release_sock(&cf_sk->sk); - trace_printk("CAIF: %s(): poll mask=0x%04x\n", - __func__, mask); - return mask; -} - -static void drain_queue(struct caifsock *cf_sk) -{ - struct cfpkt *pkt = NULL; - - /* Empty the queue */ - do { - /* The queue has its own lock */ - if (!cf_sk->pktq) - break; - - pkt = cfpkt_dequeue(cf_sk->pktq); - if (!pkt) - break; - pr_debug("CAIF: %s(): freeing packet from read queue\n", - __func__); - cfpkt_destroy(pkt); - } while (1); + return sent; - cf_sk->read_queue_len = 0; +pipe_err: + if (sent == 0 && !(msg->msg_flags&MSG_NOSIGNAL)) + send_sig(SIGPIPE, current, 0); + err = -EPIPE; +out_err: + return sent ? : err; } static int setsockopt(struct socket *sock, @@ -736,19 +738,13 @@ static int setsockopt(struct socket *sock, int prio, linksel; struct ifreq ifreq; - if (STATE_IS_OPEN(cf_sk)) { - pr_debug("CAIF: %s(): setsockopt " - "cannot be done on a connected socket\n", - __func__); + if (cf_sk->sk.sk_socket->state != SS_UNCONNECTED) return -ENOPROTOOPT; - } + switch (opt) { case CAIFSO_LINK_SELECT: - if (ol < sizeof(int)) { - pr_debug("CAIF: %s(): setsockopt" - " CAIFSO_CHANNEL_CONFIG bad size\n", __func__); + if (ol < sizeof(int)) return -EINVAL; - } if (lvl != SOL_CAIF) goto bad_sol; if (copy_from_user(&linksel, ov, sizeof(int))) @@ -761,28 +757,20 @@ static int setsockopt(struct socket *sock, case SO_PRIORITY: if (lvl != SOL_SOCKET) goto bad_sol; - if (ol < sizeof(int)) { - pr_debug("CAIF: %s(): setsockopt" - " SO_PRIORITY bad size\n", __func__); + if (ol < sizeof(int)) return -EINVAL; - } if (copy_from_user(&prio, ov, sizeof(int))) return -EINVAL; lock_sock(&(cf_sk->sk)); cf_sk->conn_req.priority = prio; - pr_debug("CAIF: %s(): Setting sockopt priority=%d\n", __func__, - cf_sk->conn_req.priority); release_sock(&cf_sk->sk); return 0; case SO_BINDTODEVICE: if (lvl != SOL_SOCKET) goto bad_sol; - if (ol < sizeof(struct ifreq)) { - pr_debug("CAIF: %s(): setsockopt" - " SO_PRIORITY bad size\n", __func__); + if (ol < sizeof(struct ifreq)) return -EINVAL; - } if (copy_from_user(&ifreq, ov, sizeof(ifreq))) return -EFAULT; lock_sock(&(cf_sk->sk)); @@ -798,359 +786,275 @@ static int setsockopt(struct socket *sock, goto bad_sol; if (cf_sk->sk.sk_protocol != CAIFPROTO_UTIL) return -ENOPROTOOPT; - if (ol > sizeof(cf_sk->conn_req.param.data)) - goto req_param_bad_size; - lock_sock(&(cf_sk->sk)); cf_sk->conn_req.param.size = ol; - if (copy_from_user(&cf_sk->conn_req.param.data, ov, ol)) { + if (ol > sizeof(cf_sk->conn_req.param.data) || + copy_from_user(&cf_sk->conn_req.param.data, ov, ol)) { release_sock(&cf_sk->sk); -req_param_bad_size: - pr_debug("CAIF: %s(): setsockopt" - " CAIFSO_CHANNEL_CONFIG bad size\n", __func__); return -EINVAL; } - release_sock(&cf_sk->sk); return 0; default: - pr_debug("CAIF: %s(): unhandled option %d\n", __func__, opt); - return -EINVAL; + return -ENOPROTOOPT; } return 0; bad_sol: - pr_debug("CAIF: %s(): setsockopt bad level\n", __func__); return -ENOPROTOOPT; } -static int caif_connect(struct socket *sock, struct sockaddr *uservaddr, - int sockaddr_len, int flags) +/* + * caif_connect() - Connect a CAIF Socket + * Copied and modified af_irda.c:irda_connect(). + * + * Note : by consulting "errno", the user space caller may learn the cause + * of the failure. Most of them are visible in the function, others may come + * from subroutines called and are listed here : + * o -EAFNOSUPPORT: bad socket family or type. + * o -ESOCKTNOSUPPORT: bad socket type or protocol + * o -EINVAL: bad socket address, or CAIF link type + * o -ECONNREFUSED: remote end refused the connection. + * o -EINPROGRESS: connect request sent but timed out (or non-blocking) + * o -EISCONN: already connected. + * o -ETIMEDOUT: Connection timed out (send timeout) + * o -ENODEV: No link layer to send request + * o -ECONNRESET: Received Shutdown indication or lost link layer + * o -ENOMEM: Out of memory + * + * State Strategy: + * o sk_state: holds the CAIF_* protocol state, it's updated by + * caif_ctrl_cb. + * o sock->state: holds the SS_* socket state and is updated by connect and + * disconnect. + */ +static int caif_connect(struct socket *sock, struct sockaddr *uaddr, + int addr_len, int flags) { - struct caifsock *cf_sk = NULL; - int result = -1; - int mode = 0; - int ret = -EIO; struct sock *sk = sock->sk; - BUG_ON(sk == NULL); - - cf_sk = container_of(sk, struct caifsock, sk); - - trace_printk("CAIF: %s(): cf_sk=%p OPEN=%d, TX_FLOW=%d, RX_FLOW=%d\n", - __func__, cf_sk, - STATE_IS_OPEN(cf_sk), - TX_FLOW_IS_ON(cf_sk), RX_FLOW_IS_ON(cf_sk)); - + struct caifsock *cf_sk = container_of(sk, struct caifsock, sk); + long timeo; + int err; + lock_sock(sk); - if (sock->type == SOCK_SEQPACKET || sock->type == SOCK_STREAM) - sock->state = SS_CONNECTING; - else + err = -EAFNOSUPPORT; + if (uaddr->sa_family != AF_CAIF) goto out; - /* I want to be alone on cf_sk (except status and queue) */ - lock_sock(&(cf_sk->sk)); - - if (sockaddr_len != sizeof(struct sockaddr_caif)) { - pr_debug("CAIF: %s(): Bad address len (%ld,%lu)\n", - __func__, (long) sockaddr_len, - (long unsigned) sizeof(struct sockaddr_caif)); - ret = -EINVAL; - goto open_error; + err = -ESOCKTNOSUPPORT; + if (unlikely(!(sk->sk_type == SOCK_STREAM && + cf_sk->sk.sk_protocol == CAIFPROTO_AT) && + sk->sk_type != SOCK_SEQPACKET)) + goto out; + switch (sock->state) { + case SS_UNCONNECTED: + /* Normal case, a fresh connect */ + caif_assert(sk->sk_state == CAIF_DISCONNECTED); + break; + case SS_CONNECTING: + switch (sk->sk_state) { + case CAIF_CONNECTED: + sock->state = SS_CONNECTED; + err = -EISCONN; + goto out; + case CAIF_DISCONNECTED: + /* Reconnect allowed */ + break; + case CAIF_CONNECTING: + err = -EALREADY; + if (flags & O_NONBLOCK) + goto out; + goto wait_connect; + } + break; + case SS_CONNECTED: + caif_assert(sk->sk_state == CAIF_CONNECTED || + sk->sk_state == CAIF_DISCONNECTED); + if (sk->sk_shutdown & SHUTDOWN_MASK) { + /* Allow re-connect after SHUTDOWN_IND */ + caif_disconnect_client(&cf_sk->layer); + break; + } + /* No reconnect on a seqpacket socket */ + err = -EISCONN; + goto out; + case SS_DISCONNECTING: + case SS_FREE: + caif_assert(1); /*Should never happen */ + break; } + sk->sk_state = CAIF_DISCONNECTED; + sock->state = SS_UNCONNECTED; + sk_stream_kill_queues(&cf_sk->sk); - if (uservaddr->sa_family != AF_CAIF) { - pr_debug("CAIF: %s(): Bad address family (%d)\n", - __func__, uservaddr->sa_family); - ret = -EAFNOSUPPORT; - goto open_error; - } + err = -EINVAL; + if (addr_len != sizeof(struct sockaddr_caif) || + !uaddr) + goto out; - memcpy(&cf_sk->conn_req.sockaddr, uservaddr, + memcpy(&cf_sk->conn_req.sockaddr, uaddr, sizeof(struct sockaddr_caif)); - dbfs_atomic_inc(&cnt.num_open); - mode = SKT_READ_FLAG | SKT_WRITE_FLAG; - - /* If socket is not open, make sure socket is in fully closed state */ - if (!STATE_IS_OPEN(cf_sk)) { - /* Has link close response been received (if we ever sent it)?*/ - if (STATE_IS_PENDING(cf_sk)) { - /* - * Still waiting for close response from remote. - * If opened non-blocking, report "would block" - */ - if (flags & O_NONBLOCK) { - pr_debug("CAIF: %s(): O_NONBLOCK" - " && close pending\n", __func__); - ret = -EAGAIN; - goto open_error; - } - - pr_debug("CAIF: %s(): Wait for close response" - " from remote...\n", __func__); - - release_sock(&cf_sk->sk); - - /* - * Blocking mode; close is pending and we need to wait - * for its conclusion. - */ - result = - wait_event_interruptible(*sk_sleep(&cf_sk->sk), - !STATE_IS_PENDING(cf_sk)); - - lock_sock(&(cf_sk->sk)); - if (result == -ERESTARTSYS) { - pr_debug("CAIF: %s(): wait_event_interruptible" - "woken by a signal (1)", __func__); - ret = -ERESTARTSYS; - goto open_error; - } - } + /* Move to connecting socket, start sending Connect Requests */ + sock->state = SS_CONNECTING; + sk->sk_state = CAIF_CONNECTING; + + dbfs_atomic_inc(&cnt.num_connect_req); + cf_sk->layer.receive = caif_sktrecv_cb; + err = caif_connect_client(&cf_sk->conn_req, + &cf_sk->layer); + if (err < 0) { + cf_sk->sk.sk_socket->state = SS_UNCONNECTED; + cf_sk->sk.sk_state = CAIF_DISCONNECTED; + goto out; } - /* socket is now either closed, pending open or open */ - if (STATE_IS_OPEN(cf_sk) && !STATE_IS_PENDING(cf_sk)) { - /* Open */ - pr_debug("CAIF: %s(): Socket is already opened (cf_sk=%p)" - " check access f_flags = 0x%x file_mode = 0x%x\n", - __func__, cf_sk, mode, cf_sk->file_mode); - - } else { - /* We are closed or pending open. - * If closed: send link setup - * If pending open: link setup already sent (we could have been - * interrupted by a signal last time) - */ - if (!STATE_IS_OPEN(cf_sk)) { - /* First opening of file; connect lower layers: */ - /* Drain queue (very unlikely) */ - drain_queue(cf_sk); - - cf_sk->layer.receive = caif_sktrecv_cb; - SET_STATE_OPEN(cf_sk); - SET_PENDING_ON(cf_sk); - - /* Register this channel. */ - result = - caif_connect_client(&cf_sk->conn_req, - &cf_sk->layer); - if (result < 0) { - pr_debug("CAIF: %s(): can't register channel\n", - __func__); - ret = -EIO; - SET_STATE_CLOSED(cf_sk); - SET_PENDING_OFF(cf_sk); - goto open_error; - } - dbfs_atomic_inc(&cnt.num_init); - } - - /* If opened non-blocking, report "success". - */ - if (flags & O_NONBLOCK) { - pr_debug("CAIF: %s(): O_NONBLOCK success\n", - __func__); - ret = -EINPROGRESS; - cf_sk->sk.sk_err = -EINPROGRESS; - goto open_error; - } - - trace_printk("CAIF: %s(): Wait for connect response\n", - __func__); + err = -EINPROGRESS; +wait_connect: - /* release lock before waiting */ - release_sock(&cf_sk->sk); - - result = - wait_event_interruptible(*sk_sleep(&cf_sk->sk), - !STATE_IS_PENDING(cf_sk)); - - lock_sock(&(cf_sk->sk)); - - if (result == -ERESTARTSYS) { - pr_debug("CAIF: %s(): wait_event_interruptible" - "woken by a signal (2)", __func__); - ret = -ERESTARTSYS; - goto open_error; - } - - if (!STATE_IS_OPEN(cf_sk)) { - /* Lower layers said "no" */ - pr_debug("CAIF: %s(): Closed received\n", __func__); - ret = -EPIPE; - goto open_error; - } + if (sk->sk_state != CAIF_CONNECTED && (flags & O_NONBLOCK)) + goto out; - trace_printk("CAIF: %s(): Connect received\n", __func__); + timeo = sock_sndtimeo(sk, flags & O_NONBLOCK); + + release_sock(sk); + err = wait_event_interruptible_timeout(*sk_sleep(sk), + sk->sk_state != CAIF_CONNECTING, + timeo); + lock_sock(sk); + if (err < 0) + goto out; /* -ERESTARTSYS */ + if (err == 0 && sk->sk_state != CAIF_CONNECTED) { + err = -ETIMEDOUT; + goto out; } - /* Open is ok */ - cf_sk->file_mode |= mode; - trace_printk("CAIF: %s(): Connected - file mode = %x\n", - __func__, cf_sk->file_mode); - - release_sock(&cf_sk->sk); - return 0; -open_error: - sock->state = SS_UNCONNECTED; - release_sock(&cf_sk->sk); + if (sk->sk_state != CAIF_CONNECTED) { + sock->state = SS_UNCONNECTED; + err = sock_error(sk); + if (!err) + err = -ECONNREFUSED; + goto out; + } + sock->state = SS_CONNECTED; + err = 0; out: - return ret; + release_sock(sk); + return err; } -static int caif_shutdown(struct socket *sock, int how) + +/* + * caif_release() - Disconnect a CAIF Socket + * Copied and modified af_irda.c:irda_release(). + */ +static int caif_release(struct socket *sock) { - struct caifsock *cf_sk = NULL; - int result = 0; - int tx_flow_state_was_on; struct sock *sk = sock->sk; + struct caifsock *cf_sk = container_of(sk, struct caifsock, sk); + int res = 0; - trace_printk("CAIF: %s(): enter\n", __func__); - pr_debug("f_flags=%x\n", sock->file->f_flags); - - if (how != SHUT_RDWR) - return -EOPNOTSUPP; - - cf_sk = container_of(sk, struct caifsock, sk); - if (cf_sk == NULL) { - pr_debug("CAIF: %s(): COULD NOT FIND SOCKET\n", __func__); - return -EBADF; - } - - /* I want to be alone on cf_sk (except status queue) */ - lock_sock(&(cf_sk->sk)); - sock_hold(&cf_sk->sk); - - /* IS_CLOSED have double meaning: - * 1) Spontanous Remote Shutdown Request. - * 2) Ack on a channel teardown(disconnect) - * Must clear bit in case we previously received - * remote shudown request. - */ - if (STATE_IS_OPEN(cf_sk) && !STATE_IS_PENDING(cf_sk)) { - SET_STATE_CLOSED(cf_sk); - SET_PENDING_ON(cf_sk); - tx_flow_state_was_on = TX_FLOW_IS_ON(cf_sk); - SET_TX_FLOW_OFF(cf_sk); - - /* Hold the socket until DEINIT_RSP is received */ - sock_hold(&cf_sk->sk); - result = caif_disconnect_client(&cf_sk->layer); - - if (result < 0) { - pr_debug("CAIF: %s(): " - "caif_disconnect_client() failed\n", - __func__); - SET_STATE_CLOSED(cf_sk); - SET_PENDING_OFF(cf_sk); - SET_TX_FLOW_OFF(cf_sk); - release_sock(&cf_sk->sk); - sock_put(&cf_sk->sk); - return -EIO; - } + if (!sk) + return 0; - } - if (STATE_IS_REMOTE_SHUTDOWN(cf_sk)) { - SET_PENDING_OFF(cf_sk); - SET_REMOTE_SHUTDOWN_OFF(cf_sk); - } + set_tx_flow_off(cf_sk); /* - * Socket is no longer in state pending close, - * and we can release the reference. + * Ensure that packets are not queued after this point in time. + * caif_queue_rcv_skb checks SOCK_DEAD holding the queue lock, + * this ensures no packets when sock is dead. */ + spin_lock(&sk->sk_receive_queue.lock); + sock_set_flag(sk, SOCK_DEAD); + spin_unlock(&sk->sk_receive_queue.lock); + sock->sk = NULL; - dbfs_atomic_inc(&cnt.num_close); - drain_queue(cf_sk); - SET_RX_FLOW_ON(cf_sk); - cf_sk->file_mode = 0; - sock_put(&cf_sk->sk); - release_sock(&cf_sk->sk); - if (!result && (sock->file->f_flags & O_NONBLOCK)) { - pr_debug("nonblocking shutdown returing -EAGAIN\n"); - return -EAGAIN; - } else - return result; -} - -static ssize_t caif_sock_no_sendpage(struct socket *sock, - struct page *page, - int offset, size_t size, int flags) -{ - return -EOPNOTSUPP; -} - -/* This function is called as part of close. */ -static int caif_release(struct socket *sock) -{ - struct sock *sk = sock->sk; - struct caifsock *cf_sk = NULL; - int res; - caif_assert(sk != NULL); - cf_sk = container_of(sk, struct caifsock, sk); + dbfs_atomic_inc(&cnt.num_disconnect); if (cf_sk->debugfs_socket_dir != NULL) debugfs_remove_recursive(cf_sk->debugfs_socket_dir); - res = caif_shutdown(sock, SHUT_RDWR); - if (res && res != -EINPROGRESS) - return res; - - /* - * FIXME: Shutdown should probably be possible to do async - * without flushing queues, allowing reception of frames while - * waiting for DEINIT_IND. - * Release should always block, to allow secure decoupling of - * CAIF stack. - */ - if (!(sock->file->f_flags & O_NONBLOCK)) { - res = wait_event_interruptible(*sk_sleep(&cf_sk->sk), - !STATE_IS_PENDING(cf_sk)); - - if (res == -ERESTARTSYS) { - pr_debug("CAIF: %s(): wait_event_interruptible" - "woken by a signal (1)", __func__); - } - } lock_sock(&(cf_sk->sk)); + sk->sk_state = CAIF_DISCONNECTED; + sk->sk_shutdown = SHUTDOWN_MASK; - sock->sk = NULL; + if (cf_sk->sk.sk_socket->state == SS_CONNECTED || + cf_sk->sk.sk_socket->state == SS_CONNECTING) + res = caif_disconnect_client(&cf_sk->layer); - /* Detach the socket from its process context by making it orphan. */ - sock_orphan(sk); + cf_sk->sk.sk_socket->state = SS_DISCONNECTING; + wake_up_interruptible_poll(sk_sleep(sk), POLLERR|POLLHUP); - /* - * Setting SHUTDOWN_MASK means that both send and receive are shutdown - * for the socket. - */ - sk->sk_shutdown = SHUTDOWN_MASK; + sock_orphan(sk); + cf_sk->layer.dn = NULL; + sk_stream_kill_queues(&cf_sk->sk); + release_sock(sk); + sock_put(sk); + return res; +} - /* - * Set the socket state to closed, the TCP_CLOSE macro is used when - * closing any socket. - */ +/* Copied from af_unix.c:unix_poll(), added CAIF tx_flow handling */ +static unsigned int caif_poll(struct file *file, + struct socket *sock, poll_table *wait) +{ + struct sock *sk = sock->sk; + unsigned int mask; + struct caifsock *cf_sk = container_of(sk, struct caifsock, sk); - /* Flush out this sockets receive queue. */ - drain_queue(cf_sk); + sock_poll_wait(file, sk_sleep(sk), wait); + mask = 0; - /* Finally release the socket. */ - SET_STATE_PENDING_DESTROY(cf_sk); + /* exceptional events? */ + if (sk->sk_err) + mask |= POLLERR; + if (sk->sk_shutdown == SHUTDOWN_MASK) + mask |= POLLHUP; + if (sk->sk_shutdown & RCV_SHUTDOWN) + mask |= POLLRDHUP; - release_sock(&cf_sk->sk); + /* readable? */ + if (!skb_queue_empty(&sk->sk_receive_queue) || + (sk->sk_shutdown & RCV_SHUTDOWN)) + mask |= POLLIN | POLLRDNORM; - sock_put(sk); + /* Connection-based need to check for termination and startup */ + if (sk->sk_state == CAIF_DISCONNECTED) + mask |= POLLHUP; /* - * The rest of the cleanup will be handled from the - * caif_sock_destructor + * we set writable also when the other side has shut down the + * connection. This prevents stuck sockets. */ - return res; + if (sock_writeable(sk) && tx_flow_is_on(cf_sk)) + mask |= POLLOUT | POLLWRNORM | POLLWRBAND; + + return mask; } -static const struct proto_ops caif_ops = { +static const struct proto_ops caif_seqpacket_ops = { + .family = PF_CAIF, + .owner = THIS_MODULE, + .release = caif_release, + .bind = sock_no_bind, + .connect = caif_connect, + .socketpair = sock_no_socketpair, + .accept = sock_no_accept, + .getname = sock_no_getname, + .poll = caif_poll, + .ioctl = sock_no_ioctl, + .listen = sock_no_listen, + .shutdown = sock_no_shutdown, + .setsockopt = setsockopt, + .getsockopt = sock_no_getsockopt, + .sendmsg = caif_seqpkt_sendmsg, + .recvmsg = caif_seqpkt_recvmsg, + .mmap = sock_no_mmap, + .sendpage = sock_no_sendpage, +}; + +static const struct proto_ops caif_stream_ops = { .family = PF_CAIF, .owner = THIS_MODULE, .release = caif_release, @@ -1162,73 +1066,62 @@ static const struct proto_ops caif_ops = { .poll = caif_poll, .ioctl = sock_no_ioctl, .listen = sock_no_listen, - .shutdown = caif_shutdown, + .shutdown = sock_no_shutdown, .setsockopt = setsockopt, .getsockopt = sock_no_getsockopt, - .sendmsg = caif_sendmsg, - .recvmsg = caif_recvmsg, + .sendmsg = caif_stream_sendmsg, + .recvmsg = caif_stream_recvmsg, .mmap = sock_no_mmap, - .sendpage = caif_sock_no_sendpage, + .sendpage = sock_no_sendpage, }; /* This function is called when a socket is finally destroyed. */ static void caif_sock_destructor(struct sock *sk) { - struct caifsock *cf_sk = NULL; - cf_sk = container_of(sk, struct caifsock, sk); - /* Error checks. */ + struct caifsock *cf_sk = container_of(sk, struct caifsock, sk); caif_assert(!atomic_read(&sk->sk_wmem_alloc)); caif_assert(sk_unhashed(sk)); caif_assert(!sk->sk_socket); if (!sock_flag(sk, SOCK_DEAD)) { - pr_debug("CAIF: %s(): 0x%p", __func__, sk); + pr_info("Attempt to release alive CAIF socket: %p\n", sk); return; } - - if (STATE_IS_OPEN(cf_sk)) { - pr_debug("CAIF: %s(): socket is opened (cf_sk=%p)" - " file_mode = 0x%x\n", __func__, - cf_sk, cf_sk->file_mode); - return; - } - drain_queue(cf_sk); - kfree(cf_sk->pktq); - - trace_printk("CAIF: %s(): caif_sock_destructor: Removing socket %s\n", - __func__, cf_sk->name); - atomic_dec(&caif_nr_socks); + sk_stream_kill_queues(&cf_sk->sk); + dbfs_atomic_dec(&cnt.caif_nr_socks); } static int caif_create(struct net *net, struct socket *sock, int protocol, - int kern) + int kern) { struct sock *sk = NULL; struct caifsock *cf_sk = NULL; - int result = 0; static struct proto prot = {.name = "PF_CAIF", .owner = THIS_MODULE, .obj_size = sizeof(struct caifsock), }; + if (!capable(CAP_SYS_ADMIN) && !capable(CAP_NET_ADMIN)) + return -EPERM; /* * The sock->type specifies the socket type to use. - * in SEQPACKET mode packet boundaries are enforced. + * The CAIF socket is a packet stream in the sense + * that it is packet based. CAIF trusts the reliability + * of the link, no resending is implemented. */ - if (sock->type != SOCK_SEQPACKET && sock->type != SOCK_STREAM) + if (sock->type == SOCK_SEQPACKET) + sock->ops = &caif_seqpacket_ops; + else if (sock->type == SOCK_STREAM) + sock->ops = &caif_stream_ops; + else return -ESOCKTNOSUPPORT; - if (net != &init_net) - return -EAFNOSUPPORT; - if (protocol < 0 || protocol >= CAIFPROTO_MAX) return -EPROTONOSUPPORT; /* - * Set the socket state to unconnected. The socket state is really - * not used at all in the net/core or socket.c but the + * Set the socket state to unconnected. The socket state + * is really not used at all in the net/core or socket.c but the * initialization makes sure that sock->state is not uninitialized. */ - sock->state = SS_UNCONNECTED; - sk = sk_alloc(net, PF_CAIF, GFP_KERNEL, &prot); if (!sk) return -ENOMEM; @@ -1238,11 +1131,9 @@ static int caif_create(struct net *net, struct socket *sock, int protocol, /* Store the protocol */ sk->sk_protocol = (unsigned char) protocol; - spin_lock_init(&cf_sk->read_queue_len_lock); - - /* Fill in some information concerning the misc socket. */ - snprintf(cf_sk->name, sizeof(cf_sk->name), "cf_sk%d", - atomic_read(&caif_nr_socks)); + /* Sendbuf dictates the amount of outbound packets not yet sent */ + sk->sk_sndbuf = CAIF_DEF_SNDBUF; + sk->sk_rcvbuf = CAIF_DEF_RCVBUF; /* * Lock in order to try to stop someone from opening the socket @@ -1252,108 +1143,85 @@ static int caif_create(struct net *net, struct socket *sock, int protocol, /* Initialize the nozero default sock structure data. */ sock_init_data(sock, sk); - sock->ops = &caif_ops; sk->sk_destruct = caif_sock_destructor; - sk->sk_sndbuf = caif_sockbuf_size; - sk->sk_rcvbuf = caif_sockbuf_size; - cf_sk->pktq = cfpktq_create(); + mutex_init(&cf_sk->readlock); /* single task reading lock */ + cf_sk->layer.ctrlcmd = caif_ctrl_cb; + cf_sk->sk.sk_socket->state = SS_UNCONNECTED; + cf_sk->sk.sk_state = CAIF_DISCONNECTED; - if (!cf_sk->pktq) { - pr_err("CAIF: %s(): queue create failed.\n", __func__); - result = -ENOMEM; - release_sock(&cf_sk->sk); - goto err_failed; - } - cf_sk->layer.ctrlcmd = caif_sktflowctrl_cb; - SET_STATE_CLOSED(cf_sk); - SET_PENDING_OFF(cf_sk); - SET_TX_FLOW_OFF(cf_sk); - SET_RX_FLOW_ON(cf_sk); + set_tx_flow_off(cf_sk); + set_rx_flow_on(cf_sk); /* Set default options on configuration */ cf_sk->conn_req.priority = CAIF_PRIO_NORMAL; - cf_sk->conn_req.link_selector = CAIF_LINK_HIGH_BANDW; + cf_sk->conn_req.link_selector = CAIF_LINK_LOW_LATENCY; cf_sk->conn_req.protocol = protocol; /* Increase the number of sockets created. */ - atomic_inc(&caif_nr_socks); + dbfs_atomic_inc(&cnt.caif_nr_socks); +#ifdef CONFIG_DEBUG_FS if (!IS_ERR(debugfsdir)) { + /* Fill in some information concerning the misc socket. */ + snprintf(cf_sk->name, sizeof(cf_sk->name), "cfsk%d", + atomic_read(&cnt.caif_nr_socks)); + cf_sk->debugfs_socket_dir = debugfs_create_dir(cf_sk->name, debugfsdir); - debugfs_create_u32("conn_state", S_IRUSR | S_IWUSR, - cf_sk->debugfs_socket_dir, &cf_sk->conn_state); + debugfs_create_u32("sk_state", S_IRUSR | S_IWUSR, + cf_sk->debugfs_socket_dir, + (u32 *) &cf_sk->sk.sk_state); debugfs_create_u32("flow_state", S_IRUSR | S_IWUSR, cf_sk->debugfs_socket_dir, &cf_sk->flow_state); - debugfs_create_u32("read_queue_len", S_IRUSR | S_IWUSR, + debugfs_create_u32("sk_rmem_alloc", S_IRUSR | S_IWUSR, + cf_sk->debugfs_socket_dir, + (u32 *) &cf_sk->sk.sk_rmem_alloc); + debugfs_create_u32("sk_wmem_alloc", S_IRUSR | S_IWUSR, cf_sk->debugfs_socket_dir, - (u32 *) &cf_sk->read_queue_len); + (u32 *) &cf_sk->sk.sk_wmem_alloc); debugfs_create_u32("identity", S_IRUSR | S_IWUSR, cf_sk->debugfs_socket_dir, (u32 *) &cf_sk->layer.id); } +#endif release_sock(&cf_sk->sk); return 0; -err_failed: - sk_free(sk); - return result; } + static struct net_proto_family caif_family_ops = { .family = PF_CAIF, .create = caif_create, .owner = THIS_MODULE, }; -static int af_caif_init(void) +int af_caif_init(void) { - int err; - err = sock_register(&caif_family_ops); - + int err = sock_register(&caif_family_ops); if (!err) return err; - return 0; } static int __init caif_sktinit_module(void) { - int stat; #ifdef CONFIG_DEBUG_FS - debugfsdir = debugfs_create_dir("chnl_skt", NULL); + debugfsdir = debugfs_create_dir("caif_sk", NULL); if (!IS_ERR(debugfsdir)) { - debugfs_create_u32("skb_inuse", S_IRUSR | S_IWUSR, - debugfsdir, - (u32 *) &cnt.skb_in_use); - debugfs_create_u32("skb_alloc", S_IRUSR | S_IWUSR, - debugfsdir, - (u32 *) &cnt.skb_alloc); - debugfs_create_u32("skb_free", S_IRUSR | S_IWUSR, - debugfsdir, - (u32 *) &cnt.skb_free); debugfs_create_u32("num_sockets", S_IRUSR | S_IWUSR, debugfsdir, - (u32 *) &caif_nr_socks); - debugfs_create_u32("num_open", S_IRUSR | S_IWUSR, + (u32 *) &cnt.caif_nr_socks); + debugfs_create_u32("num_connect_req", S_IRUSR | S_IWUSR, debugfsdir, - (u32 *) &cnt.num_open); - debugfs_create_u32("num_close", S_IRUSR | S_IWUSR, + (u32 *) &cnt.num_connect_req); + debugfs_create_u32("num_connect_resp", S_IRUSR | S_IWUSR, debugfsdir, - (u32 *) &cnt.num_close); - debugfs_create_u32("num_init", S_IRUSR | S_IWUSR, + (u32 *) &cnt.num_connect_resp); + debugfs_create_u32("num_connect_fail_resp", S_IRUSR | S_IWUSR, debugfsdir, - (u32 *) &cnt.num_init); - debugfs_create_u32("num_init_resp", S_IRUSR | S_IWUSR, + (u32 *) &cnt.num_connect_fail_resp); + debugfs_create_u32("num_disconnect", S_IRUSR | S_IWUSR, debugfsdir, - (u32 *) &cnt.num_init_resp); - debugfs_create_u32("num_init_fail_resp", S_IRUSR | S_IWUSR, - debugfsdir, - (u32 *) &cnt.num_init_fail_resp); - debugfs_create_u32("num_deinit", S_IRUSR | S_IWUSR, - debugfsdir, - (u32 *) &cnt.num_deinit); - debugfs_create_u32("num_deinit_resp", S_IRUSR | S_IWUSR, - debugfsdir, - (u32 *) &cnt.num_deinit_resp); + (u32 *) &cnt.num_disconnect); debugfs_create_u32("num_remote_shutdown_ind", S_IRUSR | S_IWUSR, debugfsdir, (u32 *) &cnt.num_remote_shutdown_ind); @@ -1371,13 +1239,7 @@ static int __init caif_sktinit_module(void) (u32 *) &cnt.num_rx_flow_on); } #endif - stat = af_caif_init(); - if (stat) { - pr_err("CAIF: %s(): Failed to initialize CAIF socket layer.", - __func__); - return stat; - } - return 0; + return af_caif_init(); } static void __exit caif_sktexit_module(void) @@ -1386,6 +1248,5 @@ static void __exit caif_sktexit_module(void) if (debugfsdir != NULL) debugfs_remove_recursive(debugfsdir); } - module_init(caif_sktinit_module); module_exit(caif_sktexit_module); -- cgit v1.2.3 From 43815482370c510c569fd18edb57afcb0fa8cab6 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Thu, 29 Apr 2010 11:01:49 +0000 Subject: net: sock_def_readable() and friends RCU conversion sk_callback_lock rwlock actually protects sk->sk_sleep pointer, so we need two atomic operations (and associated dirtying) per incoming packet. RCU conversion is pretty much needed : 1) Add a new structure, called "struct socket_wq" to hold all fields that will need rcu_read_lock() protection (currently: a wait_queue_head_t and a struct fasync_struct pointer). [Future patch will add a list anchor for wakeup coalescing] 2) Attach one of such structure to each "struct socket" created in sock_alloc_inode(). 3) Respect RCU grace period when freeing a "struct socket_wq" 4) Change sk_sleep pointer in "struct sock" by sk_wq, pointer to "struct socket_wq" 5) Change sk_sleep() function to use new sk->sk_wq instead of sk->sk_sleep 6) Change sk_has_sleeper() to wq_has_sleeper() that must be used inside a rcu_read_lock() section. 7) Change all sk_has_sleeper() callers to : - Use rcu_read_lock() instead of read_lock(&sk->sk_callback_lock) - Use wq_has_sleeper() to eventually wakeup tasks. - Use rcu_read_unlock() instead of read_unlock(&sk->sk_callback_lock) 8) sock_wake_async() is modified to use rcu protection as well. 9) Exceptions : macvtap, drivers/net/tun.c, af_unix use integrated "struct socket_wq" instead of dynamically allocated ones. They dont need rcu freeing. Some cleanups or followups are probably needed, (possible sk_callback_lock conversion to a spinlock for example...). Signed-off-by: Eric Dumazet Signed-off-by: David S. Miller --- drivers/net/macvtap.c | 13 +++++++++---- drivers/net/tun.c | 21 ++++++++++++--------- include/linux/net.h | 14 +++++++++----- include/net/af_unix.h | 20 +++++++++++--------- include/net/sock.h | 38 +++++++++++++++++++------------------- net/atm/common.c | 22 ++++++++++++++-------- net/core/sock.c | 50 +++++++++++++++++++++++++++++++------------------- net/core/stream.c | 10 +++++++--- net/dccp/output.c | 10 ++++++---- net/iucv/af_iucv.c | 11 +++++++---- net/phonet/pep.c | 8 ++++---- net/phonet/socket.c | 2 +- net/rxrpc/af_rxrpc.c | 10 ++++++---- net/sctp/socket.c | 2 +- net/socket.c | 47 ++++++++++++++++++++++++++++++++++++----------- net/unix/af_unix.c | 17 ++++++++--------- 16 files changed, 181 insertions(+), 114 deletions(-) (limited to 'include/linux') diff --git a/drivers/net/macvtap.c b/drivers/net/macvtap.c index d97e1fd234b..1c4110df343 100644 --- a/drivers/net/macvtap.c +++ b/drivers/net/macvtap.c @@ -37,6 +37,7 @@ struct macvtap_queue { struct sock sk; struct socket sock; + struct socket_wq wq; struct macvlan_dev *vlan; struct file *file; unsigned int flags; @@ -242,12 +243,15 @@ static struct rtnl_link_ops macvtap_link_ops __read_mostly = { static void macvtap_sock_write_space(struct sock *sk) { + wait_queue_head_t *wqueue; + if (!sock_writeable(sk) || !test_and_clear_bit(SOCK_ASYNC_NOSPACE, &sk->sk_socket->flags)) return; - if (sk_sleep(sk) && waitqueue_active(sk_sleep(sk))) - wake_up_interruptible_poll(sk_sleep(sk), POLLOUT | POLLWRNORM | POLLWRBAND); + wqueue = sk_sleep(sk); + if (wqueue && waitqueue_active(wqueue)) + wake_up_interruptible_poll(wqueue, POLLOUT | POLLWRNORM | POLLWRBAND); } static int macvtap_open(struct inode *inode, struct file *file) @@ -272,7 +276,8 @@ static int macvtap_open(struct inode *inode, struct file *file) if (!q) goto out; - init_waitqueue_head(&q->sock.wait); + q->sock.wq = &q->wq; + init_waitqueue_head(&q->wq.wait); q->sock.type = SOCK_RAW; q->sock.state = SS_CONNECTED; q->sock.file = file; @@ -308,7 +313,7 @@ static unsigned int macvtap_poll(struct file *file, poll_table * wait) goto out; mask = 0; - poll_wait(file, &q->sock.wait, wait); + poll_wait(file, &q->wq.wait, wait); if (!skb_queue_empty(&q->sk.sk_receive_queue)) mask |= POLLIN | POLLRDNORM; diff --git a/drivers/net/tun.c b/drivers/net/tun.c index 20a17938c62..e525a6cf558 100644 --- a/drivers/net/tun.c +++ b/drivers/net/tun.c @@ -109,7 +109,7 @@ struct tun_struct { struct tap_filter txflt; struct socket socket; - + struct socket_wq wq; #ifdef TUN_DEBUG int debug; #endif @@ -323,7 +323,7 @@ static void tun_net_uninit(struct net_device *dev) /* Inform the methods they need to stop using the dev. */ if (tfile) { - wake_up_all(&tun->socket.wait); + wake_up_all(&tun->wq.wait); if (atomic_dec_and_test(&tfile->count)) __tun_detach(tun); } @@ -398,7 +398,7 @@ static netdev_tx_t tun_net_xmit(struct sk_buff *skb, struct net_device *dev) /* Notify and wake up reader process */ if (tun->flags & TUN_FASYNC) kill_fasync(&tun->fasync, SIGIO, POLL_IN); - wake_up_interruptible_poll(&tun->socket.wait, POLLIN | + wake_up_interruptible_poll(&tun->wq.wait, POLLIN | POLLRDNORM | POLLRDBAND); return NETDEV_TX_OK; @@ -498,7 +498,7 @@ static unsigned int tun_chr_poll(struct file *file, poll_table * wait) DBG(KERN_INFO "%s: tun_chr_poll\n", tun->dev->name); - poll_wait(file, &tun->socket.wait, wait); + poll_wait(file, &tun->wq.wait, wait); if (!skb_queue_empty(&sk->sk_receive_queue)) mask |= POLLIN | POLLRDNORM; @@ -773,7 +773,7 @@ static ssize_t tun_do_read(struct tun_struct *tun, DBG(KERN_INFO "%s: tun_chr_read\n", tun->dev->name); - add_wait_queue(&tun->socket.wait, &wait); + add_wait_queue(&tun->wq.wait, &wait); while (len) { current->state = TASK_INTERRUPTIBLE; @@ -804,7 +804,7 @@ static ssize_t tun_do_read(struct tun_struct *tun, } current->state = TASK_RUNNING; - remove_wait_queue(&tun->socket.wait, &wait); + remove_wait_queue(&tun->wq.wait, &wait); return ret; } @@ -861,6 +861,7 @@ static struct rtnl_link_ops tun_link_ops __read_mostly = { static void tun_sock_write_space(struct sock *sk) { struct tun_struct *tun; + wait_queue_head_t *wqueue; if (!sock_writeable(sk)) return; @@ -868,8 +869,9 @@ static void tun_sock_write_space(struct sock *sk) if (!test_and_clear_bit(SOCK_ASYNC_NOSPACE, &sk->sk_socket->flags)) return; - if (sk_sleep(sk) && waitqueue_active(sk_sleep(sk))) - wake_up_interruptible_sync_poll(sk_sleep(sk), POLLOUT | + wqueue = sk_sleep(sk); + if (wqueue && waitqueue_active(wqueue)) + wake_up_interruptible_sync_poll(wqueue, POLLOUT | POLLWRNORM | POLLWRBAND); tun = tun_sk(sk)->tun; @@ -1039,7 +1041,8 @@ static int tun_set_iff(struct net *net, struct file *file, struct ifreq *ifr) if (!sk) goto err_free_dev; - init_waitqueue_head(&tun->socket.wait); + tun->socket.wq = &tun->wq; + init_waitqueue_head(&tun->wq.wait); tun->socket.ops = &tun_socket_ops; sock_init_data(&tun->socket, sk); sk->sk_write_space = tun_sock_write_space; diff --git a/include/linux/net.h b/include/linux/net.h index 4157b5d42bd..2b4deeeb864 100644 --- a/include/linux/net.h +++ b/include/linux/net.h @@ -59,6 +59,7 @@ typedef enum { #include #include /* For O_CLOEXEC and O_NONBLOCK */ #include +#include struct poll_table_struct; struct pipe_inode_info; @@ -116,6 +117,12 @@ enum sock_shutdown_cmd { SHUT_RDWR = 2, }; +struct socket_wq { + wait_queue_head_t wait; + struct fasync_struct *fasync_list; + struct rcu_head rcu; +} ____cacheline_aligned_in_smp; + /** * struct socket - general BSD socket * @state: socket state (%SS_CONNECTED, etc) @@ -135,11 +142,8 @@ struct socket { kmemcheck_bitfield_end(type); unsigned long flags; - /* - * Please keep fasync_list & wait fields in the same cache line - */ - struct fasync_struct *fasync_list; - wait_queue_head_t wait; + + struct socket_wq *wq; struct file *file; struct sock *sk; diff --git a/include/net/af_unix.h b/include/net/af_unix.h index 1614d78c60e..20725e213ae 100644 --- a/include/net/af_unix.h +++ b/include/net/af_unix.h @@ -30,7 +30,7 @@ struct unix_skb_parms { #endif }; -#define UNIXCB(skb) (*(struct unix_skb_parms*)&((skb)->cb)) +#define UNIXCB(skb) (*(struct unix_skb_parms *)&((skb)->cb)) #define UNIXCREDS(skb) (&UNIXCB((skb)).creds) #define UNIXSID(skb) (&UNIXCB((skb)).secid) @@ -45,21 +45,23 @@ struct unix_skb_parms { struct unix_sock { /* WARNING: sk has to be the first member */ struct sock sk; - struct unix_address *addr; - struct dentry *dentry; - struct vfsmount *mnt; + struct unix_address *addr; + struct dentry *dentry; + struct vfsmount *mnt; struct mutex readlock; - struct sock *peer; - struct sock *other; + struct sock *peer; + struct sock *other; struct list_head link; - atomic_long_t inflight; - spinlock_t lock; + atomic_long_t inflight; + spinlock_t lock; unsigned int gc_candidate : 1; unsigned int gc_maybe_cycle : 1; - wait_queue_head_t peer_wait; + struct socket_wq peer_wq; }; #define unix_sk(__sk) ((struct unix_sock *)__sk) +#define peer_wait peer_wq.wait + #ifdef CONFIG_SYSCTL extern int unix_sysctl_register(struct net *net); extern void unix_sysctl_unregister(struct net *net); diff --git a/include/net/sock.h b/include/net/sock.h index e1777db5b9a..cc7f91ec972 100644 --- a/include/net/sock.h +++ b/include/net/sock.h @@ -159,7 +159,7 @@ struct sock_common { * @sk_userlocks: %SO_SNDBUF and %SO_RCVBUF settings * @sk_lock: synchronizer * @sk_rcvbuf: size of receive buffer in bytes - * @sk_sleep: sock wait queue + * @sk_wq: sock wait queue and async head * @sk_dst_cache: destination cache * @sk_dst_lock: destination cache lock * @sk_policy: flow policy @@ -257,7 +257,7 @@ struct sock { struct sk_buff *tail; int len; } sk_backlog; - wait_queue_head_t *sk_sleep; + struct socket_wq *sk_wq; struct dst_entry *sk_dst_cache; #ifdef CONFIG_XFRM struct xfrm_policy *sk_policy[2]; @@ -1219,7 +1219,7 @@ static inline void sk_set_socket(struct sock *sk, struct socket *sock) static inline wait_queue_head_t *sk_sleep(struct sock *sk) { - return sk->sk_sleep; + return &sk->sk_wq->wait; } /* Detach socket from process context. * Announce socket dead, detach it from wait queue and inode. @@ -1233,14 +1233,14 @@ static inline void sock_orphan(struct sock *sk) write_lock_bh(&sk->sk_callback_lock); sock_set_flag(sk, SOCK_DEAD); sk_set_socket(sk, NULL); - sk->sk_sleep = NULL; + sk->sk_wq = NULL; write_unlock_bh(&sk->sk_callback_lock); } static inline void sock_graft(struct sock *sk, struct socket *parent) { write_lock_bh(&sk->sk_callback_lock); - sk->sk_sleep = &parent->wait; + rcu_assign_pointer(sk->sk_wq, parent->wq); parent->sk = sk; sk_set_socket(sk, parent); security_sock_graft(sk, parent); @@ -1392,12 +1392,12 @@ static inline int sk_has_allocations(const struct sock *sk) } /** - * sk_has_sleeper - check if there are any waiting processes - * @sk: socket + * wq_has_sleeper - check if there are any waiting processes + * @sk: struct socket_wq * - * Returns true if socket has waiting processes + * Returns true if socket_wq has waiting processes * - * The purpose of the sk_has_sleeper and sock_poll_wait is to wrap the memory + * The purpose of the wq_has_sleeper and sock_poll_wait is to wrap the memory * barrier call. They were added due to the race found within the tcp code. * * Consider following tcp code paths: @@ -1410,9 +1410,10 @@ static inline int sk_has_allocations(const struct sock *sk) * ... ... * tp->rcv_nxt check sock_def_readable * ... { - * schedule ... - * if (sk_sleep(sk) && waitqueue_active(sk_sleep(sk))) - * wake_up_interruptible(sk_sleep(sk)) + * schedule rcu_read_lock(); + * wq = rcu_dereference(sk->sk_wq); + * if (wq && waitqueue_active(&wq->wait)) + * wake_up_interruptible(&wq->wait) * ... * } * @@ -1421,19 +1422,18 @@ static inline int sk_has_allocations(const struct sock *sk) * could then endup calling schedule and sleep forever if there are no more * data on the socket. * - * The sk_has_sleeper is always called right after a call to read_lock, so we - * can use smp_mb__after_lock barrier. */ -static inline int sk_has_sleeper(struct sock *sk) +static inline bool wq_has_sleeper(struct socket_wq *wq) { + /* * We need to be sure we are in sync with the * add_wait_queue modifications to the wait queue. * * This memory barrier is paired in the sock_poll_wait. */ - smp_mb__after_lock(); - return sk_sleep(sk) && waitqueue_active(sk_sleep(sk)); + smp_mb(); + return wq && waitqueue_active(&wq->wait); } /** @@ -1442,7 +1442,7 @@ static inline int sk_has_sleeper(struct sock *sk) * @wait_address: socket wait queue * @p: poll_table * - * See the comments in the sk_has_sleeper function. + * See the comments in the wq_has_sleeper function. */ static inline void sock_poll_wait(struct file *filp, wait_queue_head_t *wait_address, poll_table *p) @@ -1453,7 +1453,7 @@ static inline void sock_poll_wait(struct file *filp, * We need to be sure we are in sync with the * socket flags modification. * - * This memory barrier is paired in the sk_has_sleeper. + * This memory barrier is paired in the wq_has_sleeper. */ smp_mb(); } diff --git a/net/atm/common.c b/net/atm/common.c index e3e10e6f862..b43feb1a399 100644 --- a/net/atm/common.c +++ b/net/atm/common.c @@ -90,10 +90,13 @@ static void vcc_sock_destruct(struct sock *sk) static void vcc_def_wakeup(struct sock *sk) { - read_lock(&sk->sk_callback_lock); - if (sk_has_sleeper(sk)) - wake_up(sk_sleep(sk)); - read_unlock(&sk->sk_callback_lock); + struct socket_wq *wq; + + rcu_read_lock(); + wq = rcu_dereference(sk->sk_wq); + if (wq_has_sleeper(wq)) + wake_up(&wq->wait); + rcu_read_unlock(); } static inline int vcc_writable(struct sock *sk) @@ -106,16 +109,19 @@ static inline int vcc_writable(struct sock *sk) static void vcc_write_space(struct sock *sk) { - read_lock(&sk->sk_callback_lock); + struct socket_wq *wq; + + rcu_read_lock(); if (vcc_writable(sk)) { - if (sk_has_sleeper(sk)) - wake_up_interruptible(sk_sleep(sk)); + wq = rcu_dereference(sk->sk_wq); + if (wq_has_sleeper(wq)) + wake_up_interruptible(&wq->wait); sk_wake_async(sk, SOCK_WAKE_SPACE, POLL_OUT); } - read_unlock(&sk->sk_callback_lock); + rcu_read_unlock(); } static struct proto vcc_proto = { diff --git a/net/core/sock.c b/net/core/sock.c index 51041759517..94c4affdda9 100644 --- a/net/core/sock.c +++ b/net/core/sock.c @@ -1211,7 +1211,7 @@ struct sock *sk_clone(const struct sock *sk, const gfp_t priority) */ sk_refcnt_debug_inc(newsk); sk_set_socket(newsk, NULL); - newsk->sk_sleep = NULL; + newsk->sk_wq = NULL; if (newsk->sk_prot->sockets_allocated) percpu_counter_inc(newsk->sk_prot->sockets_allocated); @@ -1800,41 +1800,53 @@ EXPORT_SYMBOL(sock_no_sendpage); static void sock_def_wakeup(struct sock *sk) { - read_lock(&sk->sk_callback_lock); - if (sk_has_sleeper(sk)) - wake_up_interruptible_all(sk_sleep(sk)); - read_unlock(&sk->sk_callback_lock); + struct socket_wq *wq; + + rcu_read_lock(); + wq = rcu_dereference(sk->sk_wq); + if (wq_has_sleeper(wq)) + wake_up_interruptible_all(&wq->wait); + rcu_read_unlock(); } static void sock_def_error_report(struct sock *sk) { - read_lock(&sk->sk_callback_lock); - if (sk_has_sleeper(sk)) - wake_up_interruptible_poll(sk_sleep(sk), POLLERR); + struct socket_wq *wq; + + rcu_read_lock(); + wq = rcu_dereference(sk->sk_wq); + if (wq_has_sleeper(wq)) + wake_up_interruptible_poll(&wq->wait, POLLERR); sk_wake_async(sk, SOCK_WAKE_IO, POLL_ERR); - read_unlock(&sk->sk_callback_lock); + rcu_read_unlock(); } static void sock_def_readable(struct sock *sk, int len) { - read_lock(&sk->sk_callback_lock); - if (sk_has_sleeper(sk)) - wake_up_interruptible_sync_poll(sk_sleep(sk), POLLIN | + struct socket_wq *wq; + + rcu_read_lock(); + wq = rcu_dereference(sk->sk_wq); + if (wq_has_sleeper(wq)) + wake_up_interruptible_sync_poll(&wq->wait, POLLIN | POLLRDNORM | POLLRDBAND); sk_wake_async(sk, SOCK_WAKE_WAITD, POLL_IN); - read_unlock(&sk->sk_callback_lock); + rcu_read_unlock(); } static void sock_def_write_space(struct sock *sk) { - read_lock(&sk->sk_callback_lock); + struct socket_wq *wq; + + rcu_read_lock(); /* Do not wake up a writer until he can make "significant" * progress. --DaveM */ if ((atomic_read(&sk->sk_wmem_alloc) << 1) <= sk->sk_sndbuf) { - if (sk_has_sleeper(sk)) - wake_up_interruptible_sync_poll(sk_sleep(sk), POLLOUT | + wq = rcu_dereference(sk->sk_wq); + if (wq_has_sleeper(wq)) + wake_up_interruptible_sync_poll(&wq->wait, POLLOUT | POLLWRNORM | POLLWRBAND); /* Should agree with poll, otherwise some programs break */ @@ -1842,7 +1854,7 @@ static void sock_def_write_space(struct sock *sk) sk_wake_async(sk, SOCK_WAKE_SPACE, POLL_OUT); } - read_unlock(&sk->sk_callback_lock); + rcu_read_unlock(); } static void sock_def_destruct(struct sock *sk) @@ -1896,10 +1908,10 @@ void sock_init_data(struct socket *sock, struct sock *sk) if (sock) { sk->sk_type = sock->type; - sk->sk_sleep = &sock->wait; + sk->sk_wq = sock->wq; sock->sk = sk; } else - sk->sk_sleep = NULL; + sk->sk_wq = NULL; spin_lock_init(&sk->sk_dst_lock); rwlock_init(&sk->sk_callback_lock); diff --git a/net/core/stream.c b/net/core/stream.c index 7b3c3f30b10..cc196f42b8d 100644 --- a/net/core/stream.c +++ b/net/core/stream.c @@ -28,15 +28,19 @@ void sk_stream_write_space(struct sock *sk) { struct socket *sock = sk->sk_socket; + struct socket_wq *wq; if (sk_stream_wspace(sk) >= sk_stream_min_wspace(sk) && sock) { clear_bit(SOCK_NOSPACE, &sock->flags); - if (sk_sleep(sk) && waitqueue_active(sk_sleep(sk))) - wake_up_interruptible_poll(sk_sleep(sk), POLLOUT | + rcu_read_lock(); + wq = rcu_dereference(sk->sk_wq); + if (wq_has_sleeper(wq)) + wake_up_interruptible_poll(&wq->wait, POLLOUT | POLLWRNORM | POLLWRBAND); - if (sock->fasync_list && !(sk->sk_shutdown & SEND_SHUTDOWN)) + if (wq && wq->fasync_list && !(sk->sk_shutdown & SEND_SHUTDOWN)) sock_wake_async(sock, SOCK_WAKE_SPACE, POLL_OUT); + rcu_read_unlock(); } } diff --git a/net/dccp/output.c b/net/dccp/output.c index 2d3dcb39851..aadbdb58758 100644 --- a/net/dccp/output.c +++ b/net/dccp/output.c @@ -195,15 +195,17 @@ EXPORT_SYMBOL_GPL(dccp_sync_mss); void dccp_write_space(struct sock *sk) { - read_lock(&sk->sk_callback_lock); + struct socket_wq *wq; - if (sk_has_sleeper(sk)) - wake_up_interruptible(sk_sleep(sk)); + rcu_read_lock(); + wq = rcu_dereference(sk->sk_wq); + if (wq_has_sleeper(wq)) + wake_up_interruptible(&wq->wait); /* Should agree with poll, otherwise some programs break */ if (sock_writeable(sk)) sk_wake_async(sk, SOCK_WAKE_SPACE, POLL_OUT); - read_unlock(&sk->sk_callback_lock); + rcu_read_unlock(); } /** diff --git a/net/iucv/af_iucv.c b/net/iucv/af_iucv.c index 9636b7d27b4..8be324fe08b 100644 --- a/net/iucv/af_iucv.c +++ b/net/iucv/af_iucv.c @@ -305,11 +305,14 @@ static inline int iucv_below_msglim(struct sock *sk) */ static void iucv_sock_wake_msglim(struct sock *sk) { - read_lock(&sk->sk_callback_lock); - if (sk_has_sleeper(sk)) - wake_up_interruptible_all(sk_sleep(sk)); + struct socket_wq *wq; + + rcu_read_lock(); + wq = rcu_dereference(sk->sk_wq); + if (wq_has_sleeper(wq)) + wake_up_interruptible_all(&wq->wait); sk_wake_async(sk, SOCK_WAKE_SPACE, POLL_OUT); - read_unlock(&sk->sk_callback_lock); + rcu_read_unlock(); } /* Timers */ diff --git a/net/phonet/pep.c b/net/phonet/pep.c index e2a95762abd..af4d38bc3b2 100644 --- a/net/phonet/pep.c +++ b/net/phonet/pep.c @@ -664,12 +664,12 @@ static int pep_wait_connreq(struct sock *sk, int noblock) if (signal_pending(tsk)) return sock_intr_errno(timeo); - prepare_to_wait_exclusive(&sk->sk_socket->wait, &wait, + prepare_to_wait_exclusive(sk_sleep(sk), &wait, TASK_INTERRUPTIBLE); release_sock(sk); timeo = schedule_timeout(timeo); lock_sock(sk); - finish_wait(&sk->sk_socket->wait, &wait); + finish_wait(sk_sleep(sk), &wait); } return 0; @@ -910,10 +910,10 @@ disabled: goto out; } - prepare_to_wait(&sk->sk_socket->wait, &wait, + prepare_to_wait(sk_sleep(sk), &wait, TASK_INTERRUPTIBLE); done = sk_wait_event(sk, &timeo, atomic_read(&pn->tx_credits)); - finish_wait(&sk->sk_socket->wait, &wait); + finish_wait(sk_sleep(sk), &wait); if (sk->sk_state != TCP_ESTABLISHED) goto disabled; diff --git a/net/phonet/socket.c b/net/phonet/socket.c index c785bfd0744..6e9848bf037 100644 --- a/net/phonet/socket.c +++ b/net/phonet/socket.c @@ -265,7 +265,7 @@ static unsigned int pn_socket_poll(struct file *file, struct socket *sock, struct pep_sock *pn = pep_sk(sk); unsigned int mask = 0; - poll_wait(file, &sock->wait, wait); + poll_wait(file, sk_sleep(sk), wait); switch (sk->sk_state) { case TCP_LISTEN: diff --git a/net/rxrpc/af_rxrpc.c b/net/rxrpc/af_rxrpc.c index c432d76f415..0b9bb2085ce 100644 --- a/net/rxrpc/af_rxrpc.c +++ b/net/rxrpc/af_rxrpc.c @@ -62,13 +62,15 @@ static inline int rxrpc_writable(struct sock *sk) static void rxrpc_write_space(struct sock *sk) { _enter("%p", sk); - read_lock(&sk->sk_callback_lock); + rcu_read_lock(); if (rxrpc_writable(sk)) { - if (sk_has_sleeper(sk)) - wake_up_interruptible(sk_sleep(sk)); + struct socket_wq *wq = rcu_dereference(sk->sk_wq); + + if (wq_has_sleeper(wq)) + wake_up_interruptible(&wq->wait); sk_wake_async(sk, SOCK_WAKE_SPACE, POLL_OUT); } - read_unlock(&sk->sk_callback_lock); + rcu_read_unlock(); } /* diff --git a/net/sctp/socket.c b/net/sctp/socket.c index 13d8229f3a9..d54700af927 100644 --- a/net/sctp/socket.c +++ b/net/sctp/socket.c @@ -6065,7 +6065,7 @@ static void __sctp_write_space(struct sctp_association *asoc) * here by modeling from the current TCP/UDP code. * We have not tested with it yet. */ - if (sock->fasync_list && + if (sock->wq->fasync_list && !(sk->sk_shutdown & SEND_SHUTDOWN)) sock_wake_async(sock, SOCK_WAKE_SPACE, POLL_OUT); diff --git a/net/socket.c b/net/socket.c index cb7c1f6c0d6..dae8c6b84a0 100644 --- a/net/socket.c +++ b/net/socket.c @@ -252,9 +252,14 @@ static struct inode *sock_alloc_inode(struct super_block *sb) ei = kmem_cache_alloc(sock_inode_cachep, GFP_KERNEL); if (!ei) return NULL; - init_waitqueue_head(&ei->socket.wait); + ei->socket.wq = kmalloc(sizeof(struct socket_wq), GFP_KERNEL); + if (!ei->socket.wq) { + kmem_cache_free(sock_inode_cachep, ei); + return NULL; + } + init_waitqueue_head(&ei->socket.wq->wait); + ei->socket.wq->fasync_list = NULL; - ei->socket.fasync_list = NULL; ei->socket.state = SS_UNCONNECTED; ei->socket.flags = 0; ei->socket.ops = NULL; @@ -264,10 +269,21 @@ static struct inode *sock_alloc_inode(struct super_block *sb) return &ei->vfs_inode; } + +static void wq_free_rcu(struct rcu_head *head) +{ + struct socket_wq *wq = container_of(head, struct socket_wq, rcu); + + kfree(wq); +} + static void sock_destroy_inode(struct inode *inode) { - kmem_cache_free(sock_inode_cachep, - container_of(inode, struct socket_alloc, vfs_inode)); + struct socket_alloc *ei; + + ei = container_of(inode, struct socket_alloc, vfs_inode); + call_rcu(&ei->socket.wq->rcu, wq_free_rcu); + kmem_cache_free(sock_inode_cachep, ei); } static void init_once(void *foo) @@ -513,7 +529,7 @@ void sock_release(struct socket *sock) module_put(owner); } - if (sock->fasync_list) + if (sock->wq->fasync_list) printk(KERN_ERR "sock_release: fasync list not empty!\n"); percpu_sub(sockets_in_use, 1); @@ -1080,9 +1096,9 @@ static int sock_fasync(int fd, struct file *filp, int on) lock_sock(sk); - fasync_helper(fd, filp, on, &sock->fasync_list); + fasync_helper(fd, filp, on, &sock->wq->fasync_list); - if (!sock->fasync_list) + if (!sock->wq->fasync_list) sock_reset_flag(sk, SOCK_FASYNC); else sock_set_flag(sk, SOCK_FASYNC); @@ -1091,12 +1107,20 @@ static int sock_fasync(int fd, struct file *filp, int on) return 0; } -/* This function may be called only under socket lock or callback_lock */ +/* This function may be called only under socket lock or callback_lock or rcu_lock */ int sock_wake_async(struct socket *sock, int how, int band) { - if (!sock || !sock->fasync_list) + struct socket_wq *wq; + + if (!sock) return -1; + rcu_read_lock(); + wq = rcu_dereference(sock->wq); + if (!wq || !wq->fasync_list) { + rcu_read_unlock(); + return -1; + } switch (how) { case SOCK_WAKE_WAITD: if (test_bit(SOCK_ASYNC_WAITDATA, &sock->flags)) @@ -1108,11 +1132,12 @@ int sock_wake_async(struct socket *sock, int how, int band) /* fall through */ case SOCK_WAKE_IO: call_kill: - kill_fasync(&sock->fasync_list, SIGIO, band); + kill_fasync(&wq->fasync_list, SIGIO, band); break; case SOCK_WAKE_URG: - kill_fasync(&sock->fasync_list, SIGURG, band); + kill_fasync(&wq->fasync_list, SIGURG, band); } + rcu_read_unlock(); return 0; } diff --git a/net/unix/af_unix.c b/net/unix/af_unix.c index 87c0360eaa2..fef2cc5e9d2 100644 --- a/net/unix/af_unix.c +++ b/net/unix/af_unix.c @@ -313,13 +313,16 @@ static inline int unix_writable(struct sock *sk) static void unix_write_space(struct sock *sk) { - read_lock(&sk->sk_callback_lock); + struct socket_wq *wq; + + rcu_read_lock(); if (unix_writable(sk)) { - if (sk_has_sleeper(sk)) - wake_up_interruptible_sync(sk_sleep(sk)); + wq = rcu_dereference(sk->sk_wq); + if (wq_has_sleeper(wq)) + wake_up_interruptible_sync(&wq->wait); sk_wake_async(sk, SOCK_WAKE_SPACE, POLL_OUT); } - read_unlock(&sk->sk_callback_lock); + rcu_read_unlock(); } /* When dgram socket disconnects (or changes its peer), we clear its receive @@ -406,9 +409,7 @@ static int unix_release_sock(struct sock *sk, int embrion) skpair->sk_err = ECONNRESET; unix_state_unlock(skpair); skpair->sk_state_change(skpair); - read_lock(&skpair->sk_callback_lock); sk_wake_async(skpair, SOCK_WAKE_WAITD, POLL_HUP); - read_unlock(&skpair->sk_callback_lock); } sock_put(skpair); /* It may now die */ unix_peer(sk) = NULL; @@ -1142,7 +1143,7 @@ restart: newsk->sk_peercred.pid = task_tgid_vnr(current); current_euid_egid(&newsk->sk_peercred.uid, &newsk->sk_peercred.gid); newu = unix_sk(newsk); - newsk->sk_sleep = &newu->peer_wait; + newsk->sk_wq = &newu->peer_wq; otheru = unix_sk(other); /* copy address information from listening to new sock*/ @@ -1931,12 +1932,10 @@ static int unix_shutdown(struct socket *sock, int mode) other->sk_shutdown |= peer_mode; unix_state_unlock(other); other->sk_state_change(other); - read_lock(&other->sk_callback_lock); if (peer_mode == SHUTDOWN_MASK) sk_wake_async(other, SOCK_WAKE_WAITD, POLL_HUP); else if (peer_mode & RCV_SHUTDOWN) sk_wake_async(other, SOCK_WAKE_WAITD, POLL_IN); - read_unlock(&other->sk_callback_lock); } if (other) sock_put(other); -- cgit v1.2.3 From 47d29646a2c1c147d8a7598aeac2c87dd71ed638 Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Sun, 2 May 2010 02:21:44 -0700 Subject: net: Inline skb_pull() in eth_type_trans(). In commit 6be8ac2f ("[NET]: uninline skb_pull, de-bloats a lot") we uninlined skb_pull. But in some critical paths it makes sense to inline this thing and it helps performance significantly. Create an skb_pull_inline() so that we can do this in a way that serves also as annotation. Based upon a patch by Eric Dumazet. Signed-off-by: David S. Miller --- include/linux/skbuff.h | 5 +++++ net/core/skbuff.c | 2 +- net/ethernet/eth.c | 2 +- 3 files changed, 7 insertions(+), 2 deletions(-) (limited to 'include/linux') diff --git a/include/linux/skbuff.h b/include/linux/skbuff.h index 82f5116a89e..746a652b9f6 100644 --- a/include/linux/skbuff.h +++ b/include/linux/skbuff.h @@ -1128,6 +1128,11 @@ static inline unsigned char *__skb_pull(struct sk_buff *skb, unsigned int len) return skb->data += len; } +static inline unsigned char *skb_pull_inline(struct sk_buff *skb, unsigned int len) +{ + return unlikely(len > skb->len) ? NULL : __skb_pull(skb, len); +} + extern unsigned char *__pskb_pull_tail(struct sk_buff *skb, int delta); static inline unsigned char *__pskb_pull(struct sk_buff *skb, unsigned int len) diff --git a/net/core/skbuff.c b/net/core/skbuff.c index 4218ff49bf1..8b9c109166a 100644 --- a/net/core/skbuff.c +++ b/net/core/skbuff.c @@ -1051,7 +1051,7 @@ EXPORT_SYMBOL(skb_push); */ unsigned char *skb_pull(struct sk_buff *skb, unsigned int len) { - return unlikely(len > skb->len) ? NULL : __skb_pull(skb, len); + return skb_pull_inline(skb, len); } EXPORT_SYMBOL(skb_pull); diff --git a/net/ethernet/eth.c b/net/ethernet/eth.c index 0c0d272a988..61ec0329316 100644 --- a/net/ethernet/eth.c +++ b/net/ethernet/eth.c @@ -162,7 +162,7 @@ __be16 eth_type_trans(struct sk_buff *skb, struct net_device *dev) skb->dev = dev; skb_reset_mac_header(skb); - skb_pull(skb, ETH_HLEN); + skb_pull_inline(skb, ETH_HLEN); eth = eth_hdr(skb); if (unlikely(is_multicast_ether_addr(eth->h_dest))) { -- cgit v1.2.3 From dee42870a423ad485129f43cddfe7275479f11d8 Mon Sep 17 00:00:00 2001 From: Changli Gao Date: Sun, 2 May 2010 05:42:16 +0000 Subject: net: fix softnet_stat Per cpu variable softnet_data.total was shared between IRQ and SoftIRQ context without any protection. And enqueue_to_backlog should update the netdev_rx_stat of the target CPU. This patch renames softnet_data.total to softnet_data.processed: the number of packets processed in uppper levels(IP stacks). softnet_stat data is moved into softnet_data. Signed-off-by: Changli Gao ---- include/linux/netdevice.h | 17 +++++++---------- net/core/dev.c | 26 ++++++++++++-------------- net/sched/sch_generic.c | 2 +- 3 files changed, 20 insertions(+), 25 deletions(-) Signed-off-by: Eric Dumazet Signed-off-by: David S. Miller --- include/linux/netdevice.h | 17 +++++++---------- net/core/dev.c | 26 ++++++++++++-------------- net/sched/sch_generic.c | 2 +- 3 files changed, 20 insertions(+), 25 deletions(-) (limited to 'include/linux') diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index 40d4c20d034..c39938f8a8d 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -218,16 +218,6 @@ struct neighbour; struct neigh_parms; struct sk_buff; -struct netif_rx_stats { - unsigned total; - unsigned dropped; - unsigned time_squeeze; - unsigned cpu_collision; - unsigned received_rps; -}; - -DECLARE_PER_CPU(struct netif_rx_stats, netdev_rx_stat); - struct netdev_hw_addr { struct list_head list; unsigned char addr[MAX_ADDR_LEN]; @@ -1390,6 +1380,12 @@ struct softnet_data { struct sk_buff *completion_queue; struct sk_buff_head process_queue; + /* stats */ + unsigned processed; + unsigned time_squeeze; + unsigned cpu_collision; + unsigned received_rps; + #ifdef CONFIG_RPS struct softnet_data *rps_ipi_list; @@ -1399,6 +1395,7 @@ struct softnet_data { unsigned int cpu; unsigned int input_queue_head; #endif + unsigned dropped; struct sk_buff_head input_pkt_queue; struct napi_struct backlog; }; diff --git a/net/core/dev.c b/net/core/dev.c index 100dcbd2973..36d53be4fca 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -2205,8 +2205,6 @@ int netdev_max_backlog __read_mostly = 1000; int netdev_budget __read_mostly = 300; int weight_p __read_mostly = 64; /* old backlog weight */ -DEFINE_PER_CPU(struct netif_rx_stats, netdev_rx_stat) = { 0, }; - #ifdef CONFIG_RPS /* One global table that all flow-based protocols share. */ @@ -2366,7 +2364,7 @@ static void rps_trigger_softirq(void *data) struct softnet_data *sd = data; __napi_schedule(&sd->backlog); - __get_cpu_var(netdev_rx_stat).received_rps++; + sd->received_rps++; } #endif /* CONFIG_RPS */ @@ -2405,7 +2403,6 @@ static int enqueue_to_backlog(struct sk_buff *skb, int cpu, sd = &per_cpu(softnet_data, cpu); local_irq_save(flags); - __get_cpu_var(netdev_rx_stat).total++; rps_lock(sd); if (skb_queue_len(&sd->input_pkt_queue) <= netdev_max_backlog) { @@ -2429,9 +2426,9 @@ enqueue: goto enqueue; } + sd->dropped++; rps_unlock(sd); - __get_cpu_var(netdev_rx_stat).dropped++; local_irq_restore(flags); kfree_skb(skb); @@ -2806,7 +2803,7 @@ static int __netif_receive_skb(struct sk_buff *skb) skb->dev = master; } - __get_cpu_var(netdev_rx_stat).total++; + __get_cpu_var(softnet_data).processed++; skb_reset_network_header(skb); skb_reset_transport_header(skb); @@ -3490,7 +3487,7 @@ out: return; softnet_break: - __get_cpu_var(netdev_rx_stat).time_squeeze++; + sd->time_squeeze++; __raise_softirq_irqoff(NET_RX_SOFTIRQ); goto out; } @@ -3691,17 +3688,17 @@ static int dev_seq_show(struct seq_file *seq, void *v) return 0; } -static struct netif_rx_stats *softnet_get_online(loff_t *pos) +static struct softnet_data *softnet_get_online(loff_t *pos) { - struct netif_rx_stats *rc = NULL; + struct softnet_data *sd = NULL; while (*pos < nr_cpu_ids) if (cpu_online(*pos)) { - rc = &per_cpu(netdev_rx_stat, *pos); + sd = &per_cpu(softnet_data, *pos); break; } else ++*pos; - return rc; + return sd; } static void *softnet_seq_start(struct seq_file *seq, loff_t *pos) @@ -3721,12 +3718,12 @@ static void softnet_seq_stop(struct seq_file *seq, void *v) static int softnet_seq_show(struct seq_file *seq, void *v) { - struct netif_rx_stats *s = v; + struct softnet_data *sd = v; seq_printf(seq, "%08x %08x %08x %08x %08x %08x %08x %08x %08x %08x\n", - s->total, s->dropped, s->time_squeeze, 0, + sd->processed, sd->dropped, sd->time_squeeze, 0, 0, 0, 0, 0, /* was fastroute */ - s->cpu_collision, s->received_rps); + sd->cpu_collision, sd->received_rps); return 0; } @@ -5869,6 +5866,7 @@ static int __init net_dev_init(void) for_each_possible_cpu(i) { struct softnet_data *sd = &per_cpu(softnet_data, i); + memset(sd, 0, sizeof(*sd)); skb_queue_head_init(&sd->input_pkt_queue); skb_queue_head_init(&sd->process_queue); sd->completion_queue = NULL; diff --git a/net/sched/sch_generic.c b/net/sched/sch_generic.c index aeddabfb8e4..a969b111bd7 100644 --- a/net/sched/sch_generic.c +++ b/net/sched/sch_generic.c @@ -94,7 +94,7 @@ static inline int handle_dev_cpu_collision(struct sk_buff *skb, * Another cpu is holding lock, requeue & delay xmits for * some time. */ - __get_cpu_var(netdev_rx_stat).cpu_collision++; + __get_cpu_var(softnet_data).cpu_collision++; ret = dev_requeue_skb(skb, q); } -- cgit v1.2.3 From cd7b5396e7e4d10c51116f59f414ff90312af8d4 Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Sun, 2 May 2010 22:27:59 -0700 Subject: net: Use explicit "unsigned int" instead of plain "unsigned" in netdevice.h Signed-off-by: David S. Miller --- include/linux/netdevice.h | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'include/linux') diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index c39938f8a8d..98112fbddef 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -878,7 +878,7 @@ struct net_device { unsigned char operstate; /* RFC2863 operstate */ unsigned char link_mode; /* mapping policy to operstate */ - unsigned mtu; /* interface MTU value */ + unsigned int mtu; /* interface MTU value */ unsigned short type; /* interface hardware type */ unsigned short hard_header_len; /* hardware hdr length */ @@ -1381,10 +1381,10 @@ struct softnet_data { struct sk_buff_head process_queue; /* stats */ - unsigned processed; - unsigned time_squeeze; - unsigned cpu_collision; - unsigned received_rps; + unsigned int processed; + unsigned int time_squeeze; + unsigned int cpu_collision; + unsigned int received_rps; #ifdef CONFIG_RPS struct softnet_data *rps_ipi_list; -- cgit v1.2.3 From d9d52b5178af586d679c1052fb161ee05ea2e83f Mon Sep 17 00:00:00 2001 From: "Michael S. Tsirkin" Date: Wed, 17 Mar 2010 17:45:01 +0200 Subject: tun: add ioctl to modify vnet header size virtio added mergeable buffers mode where 2 bytes of extra info is put after vnet header but before actual data (tun does not need this data). In hindsight, it would have been better to add the new info *before* the packet: as it is, users need a lot of tricky code to skip the extra 2 bytes in the middle of the iovec, and in fact applications seem to get it wrong, and only work with specific iovec layout. The fact we might need to split iovec also means we might in theory overflow iovec max size. This patch adds a simpler way for applications to handle this, and future proofs the interface against further extensions, by making the size of the virtio net header configurable from userspace. As a result, tun driver will simply skip the extra 2 bytes on both input and output. Signed-off-by: Michael S. Tsirkin Acked-by: David S. Miller --- drivers/net/tun.c | 32 ++++++++++++++++++++++++++++---- include/linux/if_tun.h | 2 ++ 2 files changed, 30 insertions(+), 4 deletions(-) (limited to 'include/linux') diff --git a/drivers/net/tun.c b/drivers/net/tun.c index e525a6cf558..6b150c072a4 100644 --- a/drivers/net/tun.c +++ b/drivers/net/tun.c @@ -110,6 +110,9 @@ struct tun_struct { struct tap_filter txflt; struct socket socket; struct socket_wq wq; + + int vnet_hdr_sz; + #ifdef TUN_DEBUG int debug; #endif @@ -563,7 +566,7 @@ static __inline__ ssize_t tun_get_user(struct tun_struct *tun, } if (tun->flags & TUN_VNET_HDR) { - if ((len -= sizeof(gso)) > count) + if ((len -= tun->vnet_hdr_sz) > count) return -EINVAL; if (memcpy_fromiovecend((void *)&gso, iv, offset, sizeof(gso))) @@ -575,7 +578,7 @@ static __inline__ ssize_t tun_get_user(struct tun_struct *tun, if (gso.hdr_len > len) return -EINVAL; - offset += sizeof(gso); + offset += tun->vnet_hdr_sz; } if ((tun->flags & TUN_TYPE_MASK) == TUN_TAP_DEV) { @@ -718,7 +721,7 @@ static __inline__ ssize_t tun_put_user(struct tun_struct *tun, if (tun->flags & TUN_VNET_HDR) { struct virtio_net_hdr gso = { 0 }; /* no info leak */ - if ((len -= sizeof(gso)) < 0) + if ((len -= tun->vnet_hdr_sz) < 0) return -EINVAL; if (skb_is_gso(skb)) { @@ -749,7 +752,7 @@ static __inline__ ssize_t tun_put_user(struct tun_struct *tun, if (unlikely(memcpy_toiovecend(iv, (void *)&gso, total, sizeof(gso)))) return -EFAULT; - total += sizeof(gso); + total += tun->vnet_hdr_sz; } len = min_t(int, skb->len, len); @@ -1035,6 +1038,7 @@ static int tun_set_iff(struct net *net, struct file *file, struct ifreq *ifr) tun->dev = dev; tun->flags = flags; tun->txflt.count = 0; + tun->vnet_hdr_sz = sizeof(struct virtio_net_hdr); err = -ENOMEM; sk = sk_alloc(net, AF_UNSPEC, GFP_KERNEL, &tun_proto); @@ -1177,6 +1181,7 @@ static long __tun_chr_ioctl(struct file *file, unsigned int cmd, struct sock_fprog fprog; struct ifreq ifr; int sndbuf; + int vnet_hdr_sz; int ret; if (cmd == TUNSETIFF || _IOC_TYPE(cmd) == 0x89) @@ -1322,6 +1327,25 @@ static long __tun_chr_ioctl(struct file *file, unsigned int cmd, tun->socket.sk->sk_sndbuf = sndbuf; break; + case TUNGETVNETHDRSZ: + vnet_hdr_sz = tun->vnet_hdr_sz; + if (copy_to_user(argp, &vnet_hdr_sz, sizeof(vnet_hdr_sz))) + ret = -EFAULT; + break; + + case TUNSETVNETHDRSZ: + if (copy_from_user(&vnet_hdr_sz, argp, sizeof(vnet_hdr_sz))) { + ret = -EFAULT; + break; + } + if (vnet_hdr_sz < (int)sizeof(struct virtio_net_hdr)) { + ret = -EINVAL; + break; + } + + tun->vnet_hdr_sz = vnet_hdr_sz; + break; + case TUNATTACHFILTER: /* Can be set only for TAPs */ ret = -EINVAL; diff --git a/include/linux/if_tun.h b/include/linux/if_tun.h index 1350a246893..06b1829731f 100644 --- a/include/linux/if_tun.h +++ b/include/linux/if_tun.h @@ -51,6 +51,8 @@ #define TUNSETSNDBUF _IOW('T', 212, int) #define TUNATTACHFILTER _IOW('T', 213, struct sock_fprog) #define TUNDETACHFILTER _IOW('T', 214, struct sock_fprog) +#define TUNGETVNETHDRSZ _IOR('T', 215, int) +#define TUNSETVNETHDRSZ _IOW('T', 216, int) /* TUNSETIFF ifr flags */ #define IFF_TUN 0x0001 -- cgit v1.2.3 From 4f70ecca9c57731b4acbe5043eb22e4416bd2368 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Mon, 3 May 2010 10:50:14 +0000 Subject: net: rcu fixes Add hlist_for_each_entry_rcu_bh() and hlist_for_each_entry_continue_rcu_bh() macros, and use them in ipv6_get_ifaddr(), if6_get_first() and if6_get_next() to fix lockdeps warnings. Signed-off-by: Eric Dumazet Reviewed-by: "Paul E. McKenney" Signed-off-by: David S. Miller --- include/linux/rculist.h | 29 +++++++++++++++++++++++++++++ net/ipv6/addrconf.c | 16 ++++++++-------- 2 files changed, 37 insertions(+), 8 deletions(-) (limited to 'include/linux') diff --git a/include/linux/rculist.h b/include/linux/rculist.h index 004908b104d..4ec3b38ce9c 100644 --- a/include/linux/rculist.h +++ b/include/linux/rculist.h @@ -428,6 +428,23 @@ static inline void hlist_add_after_rcu(struct hlist_node *prev, ({ tpos = hlist_entry(pos, typeof(*tpos), member); 1; }); \ pos = rcu_dereference_raw(pos->next)) +/** + * hlist_for_each_entry_rcu_bh - iterate over rcu list of given type + * @tpos: the type * to use as a loop cursor. + * @pos: the &struct hlist_node to use as a loop cursor. + * @head: the head for your list. + * @member: the name of the hlist_node within the struct. + * + * This list-traversal primitive may safely run concurrently with + * the _rcu list-mutation primitives such as hlist_add_head_rcu() + * as long as the traversal is guarded by rcu_read_lock(). + */ +#define hlist_for_each_entry_rcu_bh(tpos, pos, head, member) \ + for (pos = rcu_dereference_bh((head)->first); \ + pos && ({ prefetch(pos->next); 1; }) && \ + ({ tpos = hlist_entry(pos, typeof(*tpos), member); 1; }); \ + pos = rcu_dereference_bh(pos->next)) + /** * hlist_for_each_entry_continue_rcu - iterate over a hlist continuing after current point * @tpos: the type * to use as a loop cursor. @@ -440,6 +457,18 @@ static inline void hlist_add_after_rcu(struct hlist_node *prev, ({ tpos = hlist_entry(pos, typeof(*tpos), member); 1; }); \ pos = rcu_dereference(pos->next)) +/** + * hlist_for_each_entry_continue_rcu_bh - iterate over a hlist continuing after current point + * @tpos: the type * to use as a loop cursor. + * @pos: the &struct hlist_node to use as a loop cursor. + * @member: the name of the hlist_node within the struct. + */ +#define hlist_for_each_entry_continue_rcu_bh(tpos, pos, member) \ + for (pos = rcu_dereference_bh((pos)->next); \ + pos && ({ prefetch(pos->next); 1; }) && \ + ({ tpos = hlist_entry(pos, typeof(*tpos), member); 1; }); \ + pos = rcu_dereference_bh(pos->next)) + #endif /* __KERNEL__ */ #endif diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c index 34d2d649e39..3984f52181f 100644 --- a/net/ipv6/addrconf.c +++ b/net/ipv6/addrconf.c @@ -1346,7 +1346,7 @@ struct inet6_ifaddr *ipv6_get_ifaddr(struct net *net, const struct in6_addr *add struct hlist_node *node; rcu_read_lock_bh(); - hlist_for_each_entry_rcu(ifp, node, &inet6_addr_lst[hash], addr_lst) { + hlist_for_each_entry_rcu_bh(ifp, node, &inet6_addr_lst[hash], addr_lst) { if (!net_eq(dev_net(ifp->idev->dev), net)) continue; if (ipv6_addr_equal(&ifp->addr, addr)) { @@ -2959,7 +2959,7 @@ static struct inet6_ifaddr *if6_get_first(struct seq_file *seq) for (state->bucket = 0; state->bucket < IN6_ADDR_HSIZE; ++state->bucket) { struct hlist_node *n; - hlist_for_each_entry_rcu(ifa, n, &inet6_addr_lst[state->bucket], + hlist_for_each_entry_rcu_bh(ifa, n, &inet6_addr_lst[state->bucket], addr_lst) if (net_eq(dev_net(ifa->idev->dev), net)) return ifa; @@ -2974,12 +2974,12 @@ static struct inet6_ifaddr *if6_get_next(struct seq_file *seq, struct net *net = seq_file_net(seq); struct hlist_node *n = &ifa->addr_lst; - hlist_for_each_entry_continue_rcu(ifa, n, addr_lst) + hlist_for_each_entry_continue_rcu_bh(ifa, n, addr_lst) if (net_eq(dev_net(ifa->idev->dev), net)) return ifa; while (++state->bucket < IN6_ADDR_HSIZE) { - hlist_for_each_entry(ifa, n, + hlist_for_each_entry_rcu_bh(ifa, n, &inet6_addr_lst[state->bucket], addr_lst) { if (net_eq(dev_net(ifa->idev->dev), net)) return ifa; @@ -3000,7 +3000,7 @@ static struct inet6_ifaddr *if6_get_idx(struct seq_file *seq, loff_t pos) } static void *if6_seq_start(struct seq_file *seq, loff_t *pos) - __acquires(rcu) + __acquires(rcu_bh) { rcu_read_lock_bh(); return if6_get_idx(seq, *pos); @@ -3016,7 +3016,7 @@ static void *if6_seq_next(struct seq_file *seq, void *v, loff_t *pos) } static void if6_seq_stop(struct seq_file *seq, void *v) - __releases(rcu) + __releases(rcu_bh) { rcu_read_unlock_bh(); } @@ -3093,7 +3093,7 @@ int ipv6_chk_home_addr(struct net *net, struct in6_addr *addr) unsigned int hash = ipv6_addr_hash(addr); rcu_read_lock_bh(); - hlist_for_each_entry_rcu(ifp, n, &inet6_addr_lst[hash], addr_lst) { + hlist_for_each_entry_rcu_bh(ifp, n, &inet6_addr_lst[hash], addr_lst) { if (!net_eq(dev_net(ifp->idev->dev), net)) continue; if (ipv6_addr_equal(&ifp->addr, addr) && @@ -3127,7 +3127,7 @@ static void addrconf_verify(unsigned long foo) for (i = 0; i < IN6_ADDR_HSIZE; i++) { restart: - hlist_for_each_entry_rcu(ifp, node, + hlist_for_each_entry_rcu_bh(ifp, node, &inet6_addr_lst[i], addr_lst) { unsigned long age; -- cgit v1.2.3 From ec7d2f2cf3a1b76202986519ec4f8ec75b2de232 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Wed, 5 May 2010 01:07:37 -0700 Subject: net: __alloc_skb() speedup With following patch I can reach maximum rate of my pktgen+udpsink simulator : - 'old' machine : dual quad core E5450 @3.00GHz - 64 UDP rx flows (only differ by destination port) - RPS enabled, NIC interrupts serviced on cpu0 - rps dispatched on 7 other cores. (~130.000 IPI per second) - SLAB allocator (faster than SLUB in this workload) - tg3 NIC - 1.080.000 pps without a single drop at NIC level. Idea is to add two prefetchw() calls in __alloc_skb(), one to prefetch first sk_buff cache line, the second to prefetch the shinfo part. Also using one memset() to initialize all skb_shared_info fields instead of one by one to reduce number of instructions, using long word moves. All skb_shared_info fields before 'dataref' are cleared in __alloc_skb(). Signed-off-by: Eric Dumazet Signed-off-by: David S. Miller --- include/linux/skbuff.h | 7 ++++++- net/core/skbuff.c | 21 +++++---------------- 2 files changed, 11 insertions(+), 17 deletions(-) (limited to 'include/linux') diff --git a/include/linux/skbuff.h b/include/linux/skbuff.h index 746a652b9f6..88d55395a27 100644 --- a/include/linux/skbuff.h +++ b/include/linux/skbuff.h @@ -187,7 +187,6 @@ union skb_shared_tx { * the end of the header data, ie. at skb->end. */ struct skb_shared_info { - atomic_t dataref; unsigned short nr_frags; unsigned short gso_size; /* Warning: this field is not always filled in (UFO)! */ @@ -197,6 +196,12 @@ struct skb_shared_info { union skb_shared_tx tx_flags; struct sk_buff *frag_list; struct skb_shared_hwtstamps hwtstamps; + + /* + * Warning : all fields before dataref are cleared in __alloc_skb() + */ + atomic_t dataref; + skb_frag_t frags[MAX_SKB_FRAGS]; /* Intermediate layers must ensure that destructor_arg * remains valid until skb destructor */ diff --git a/net/core/skbuff.c b/net/core/skbuff.c index 8b9c109166a..a9b0e1f7780 100644 --- a/net/core/skbuff.c +++ b/net/core/skbuff.c @@ -181,12 +181,14 @@ struct sk_buff *__alloc_skb(unsigned int size, gfp_t gfp_mask, skb = kmem_cache_alloc_node(cache, gfp_mask & ~__GFP_DMA, node); if (!skb) goto out; + prefetchw(skb); size = SKB_DATA_ALIGN(size); data = kmalloc_node_track_caller(size + sizeof(struct skb_shared_info), gfp_mask, node); if (!data) goto nodata; + prefetchw(data + size); /* * Only clear those fields we need to clear, not those that we will @@ -208,15 +210,8 @@ struct sk_buff *__alloc_skb(unsigned int size, gfp_t gfp_mask, /* make sure we initialize shinfo sequentially */ shinfo = skb_shinfo(skb); + memset(shinfo, 0, offsetof(struct skb_shared_info, dataref)); atomic_set(&shinfo->dataref, 1); - shinfo->nr_frags = 0; - shinfo->gso_size = 0; - shinfo->gso_segs = 0; - shinfo->gso_type = 0; - shinfo->ip6_frag_id = 0; - shinfo->tx_flags.flags = 0; - skb_frag_list_init(skb); - memset(&shinfo->hwtstamps, 0, sizeof(shinfo->hwtstamps)); if (fclone) { struct sk_buff *child = skb + 1; @@ -505,16 +500,10 @@ int skb_recycle_check(struct sk_buff *skb, int skb_size) return 0; skb_release_head_state(skb); + shinfo = skb_shinfo(skb); + memset(shinfo, 0, offsetof(struct skb_shared_info, dataref)); atomic_set(&shinfo->dataref, 1); - shinfo->nr_frags = 0; - shinfo->gso_size = 0; - shinfo->gso_segs = 0; - shinfo->gso_type = 0; - shinfo->ip6_frag_id = 0; - shinfo->tx_flags.flags = 0; - skb_frag_list_init(skb); - memset(&shinfo->hwtstamps, 0, sizeof(shinfo->hwtstamps)); memset(skb, 0, offsetof(struct sk_buff, tail)); skb->data = skb->head + NET_SKB_PAD; -- cgit v1.2.3 From 0e34e93177fb1f642cab080e0bde664c06c7183a Mon Sep 17 00:00:00 2001 From: WANG Cong Date: Thu, 6 May 2010 00:47:21 -0700 Subject: netpoll: add generic support for bridge and bonding devices This whole patchset is for adding netpoll support to bridge and bonding devices. I already tested it for bridge, bonding, bridge over bonding, and bonding over bridge. It looks fine now. To make bridge and bonding support netpoll, we need to adjust some netpoll generic code. This patch does the following things: 1) introduce two new priv_flags for struct net_device: IFF_IN_NETPOLL which identifies we are processing a netpoll; IFF_DISABLE_NETPOLL is used to disable netpoll support for a device at run-time; 2) introduce one new method for netdev_ops: ->ndo_netpoll_cleanup() is used to clean up netpoll when a device is removed. 3) introduce netpoll_poll_dev() which takes a struct net_device * parameter; export netpoll_send_skb() and netpoll_poll_dev() which will be used later; 4) hide a pointer to struct netpoll in struct netpoll_info, ditto. 5) introduce ->real_dev for struct netpoll. 6) introduce a new status NETDEV_BONDING_DESLAE, which is used to disable netconsole before releasing a slave, to avoid deadlocks. Cc: David Miller Cc: Neil Horman Signed-off-by: WANG Cong Signed-off-by: David S. Miller --- drivers/net/netconsole.c | 15 +++++++++------ include/linux/if.h | 2 ++ include/linux/netdevice.h | 1 + include/linux/netpoll.h | 5 +++++ include/linux/notifier.h | 1 + net/core/netpoll.c | 26 +++++++++++++++++++++----- 6 files changed, 39 insertions(+), 11 deletions(-) (limited to 'include/linux') diff --git a/drivers/net/netconsole.c b/drivers/net/netconsole.c index a361dea3557..ca142c47b2e 100644 --- a/drivers/net/netconsole.c +++ b/drivers/net/netconsole.c @@ -665,7 +665,8 @@ static int netconsole_netdev_event(struct notifier_block *this, struct netconsole_target *nt; struct net_device *dev = ptr; - if (!(event == NETDEV_CHANGENAME || event == NETDEV_UNREGISTER)) + if (!(event == NETDEV_CHANGENAME || event == NETDEV_UNREGISTER || + event == NETDEV_BONDING_DESLAVE || event == NETDEV_GOING_DOWN)) goto done; spin_lock_irqsave(&target_list_lock, flags); @@ -677,19 +678,21 @@ static int netconsole_netdev_event(struct notifier_block *this, strlcpy(nt->np.dev_name, dev->name, IFNAMSIZ); break; case NETDEV_UNREGISTER: - if (!nt->enabled) - break; netpoll_cleanup(&nt->np); + /* Fall through */ + case NETDEV_GOING_DOWN: + case NETDEV_BONDING_DESLAVE: nt->enabled = 0; - printk(KERN_INFO "netconsole: network logging stopped" - ", interface %s unregistered\n", - dev->name); break; } } netconsole_target_put(nt); } spin_unlock_irqrestore(&target_list_lock, flags); + if (event == NETDEV_UNREGISTER || event == NETDEV_BONDING_DESLAVE) + printk(KERN_INFO "netconsole: network logging stopped, " + "interface %s %s\n", dev->name, + event == NETDEV_UNREGISTER ? "unregistered" : "released slaves"); done: return NOTIFY_DONE; diff --git a/include/linux/if.h b/include/linux/if.h index 3a9f410a296..be350e62a90 100644 --- a/include/linux/if.h +++ b/include/linux/if.h @@ -71,6 +71,8 @@ * release skb->dst */ #define IFF_DONT_BRIDGE 0x800 /* disallow bridging this ether dev */ +#define IFF_IN_NETPOLL 0x1000 /* whether we are processing netpoll */ +#define IFF_DISABLE_NETPOLL 0x2000 /* disable netpoll at run-time */ #define IF_GET_IFACE 0x0001 /* for querying only */ #define IF_GET_PROTO 0x0002 diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index 98112fbddef..69022d47d6f 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -724,6 +724,7 @@ struct net_device_ops { unsigned short vid); #ifdef CONFIG_NET_POLL_CONTROLLER void (*ndo_poll_controller)(struct net_device *dev); + void (*ndo_netpoll_cleanup)(struct net_device *dev); #endif int (*ndo_set_vf_mac)(struct net_device *dev, int queue, u8 *mac); diff --git a/include/linux/netpoll.h b/include/linux/netpoll.h index a765ea89854..017e604d05f 100644 --- a/include/linux/netpoll.h +++ b/include/linux/netpoll.h @@ -14,6 +14,7 @@ struct netpoll { struct net_device *dev; + struct net_device *real_dev; char dev_name[IFNAMSIZ]; const char *name; void (*rx_hook)(struct netpoll *, int, char *, int); @@ -36,8 +37,11 @@ struct netpoll_info { struct sk_buff_head txq; struct delayed_work tx_work; + + struct netpoll *netpoll; }; +void netpoll_poll_dev(struct net_device *dev); void netpoll_poll(struct netpoll *np); void netpoll_send_udp(struct netpoll *np, const char *msg, int len); void netpoll_print_options(struct netpoll *np); @@ -47,6 +51,7 @@ int netpoll_trap(void); void netpoll_set_trap(int trap); void netpoll_cleanup(struct netpoll *np); int __netpoll_rx(struct sk_buff *skb); +void netpoll_send_skb(struct netpoll *np, struct sk_buff *skb); #ifdef CONFIG_NETPOLL diff --git a/include/linux/notifier.h b/include/linux/notifier.h index 9c5d3fad01f..7c360962233 100644 --- a/include/linux/notifier.h +++ b/include/linux/notifier.h @@ -206,6 +206,7 @@ static inline int notifier_to_errno(int ret) #define NETDEV_POST_TYPE_CHANGE 0x000F #define NETDEV_POST_INIT 0x0010 #define NETDEV_UNREGISTER_BATCH 0x0011 +#define NETDEV_BONDING_DESLAVE 0x0012 #define SYS_DOWN 0x0001 /* Notify of system down */ #define SYS_RESTART SYS_DOWN diff --git a/net/core/netpoll.c b/net/core/netpoll.c index a58f59b9759..94825b10955 100644 --- a/net/core/netpoll.c +++ b/net/core/netpoll.c @@ -179,9 +179,8 @@ static void service_arp_queue(struct netpoll_info *npi) } } -void netpoll_poll(struct netpoll *np) +void netpoll_poll_dev(struct net_device *dev) { - struct net_device *dev = np->dev; const struct net_device_ops *ops; if (!dev || !netif_running(dev)) @@ -201,6 +200,11 @@ void netpoll_poll(struct netpoll *np) zap_completion_queue(); } +void netpoll_poll(struct netpoll *np) +{ + netpoll_poll_dev(np->dev); +} + static void refill_skbs(void) { struct sk_buff *skb; @@ -282,7 +286,7 @@ static int netpoll_owner_active(struct net_device *dev) return 0; } -static void netpoll_send_skb(struct netpoll *np, struct sk_buff *skb) +void netpoll_send_skb(struct netpoll *np, struct sk_buff *skb) { int status = NETDEV_TX_BUSY; unsigned long tries; @@ -308,7 +312,9 @@ static void netpoll_send_skb(struct netpoll *np, struct sk_buff *skb) tries > 0; --tries) { if (__netif_tx_trylock(txq)) { if (!netif_tx_queue_stopped(txq)) { + dev->priv_flags |= IFF_IN_NETPOLL; status = ops->ndo_start_xmit(skb, dev); + dev->priv_flags &= ~IFF_IN_NETPOLL; if (status == NETDEV_TX_OK) txq_trans_update(txq); } @@ -756,7 +762,10 @@ int netpoll_setup(struct netpoll *np) atomic_inc(&npinfo->refcnt); } - if (!ndev->netdev_ops->ndo_poll_controller) { + npinfo->netpoll = np; + + if ((ndev->priv_flags & IFF_DISABLE_NETPOLL) || + !ndev->netdev_ops->ndo_poll_controller) { printk(KERN_ERR "%s: %s doesn't support polling, aborting.\n", np->name, np->dev_name); err = -ENOTSUPP; @@ -878,6 +887,7 @@ void netpoll_cleanup(struct netpoll *np) } if (atomic_dec_and_test(&npinfo->refcnt)) { + const struct net_device_ops *ops; skb_queue_purge(&npinfo->arp_tx); skb_queue_purge(&npinfo->txq); cancel_rearming_delayed_work(&npinfo->tx_work); @@ -885,7 +895,11 @@ void netpoll_cleanup(struct netpoll *np) /* clean after last, unfinished work */ __skb_queue_purge(&npinfo->txq); kfree(npinfo); - np->dev->npinfo = NULL; + ops = np->dev->netdev_ops; + if (ops->ndo_netpoll_cleanup) + ops->ndo_netpoll_cleanup(np->dev); + else + np->dev->npinfo = NULL; } } @@ -908,6 +922,7 @@ void netpoll_set_trap(int trap) atomic_dec(&trapped); } +EXPORT_SYMBOL(netpoll_send_skb); EXPORT_SYMBOL(netpoll_set_trap); EXPORT_SYMBOL(netpoll_trap); EXPORT_SYMBOL(netpoll_print_options); @@ -915,4 +930,5 @@ EXPORT_SYMBOL(netpoll_parse_options); EXPORT_SYMBOL(netpoll_setup); EXPORT_SYMBOL(netpoll_cleanup); EXPORT_SYMBOL(netpoll_send_udp); +EXPORT_SYMBOL(netpoll_poll_dev); EXPORT_SYMBOL(netpoll_poll); -- cgit v1.2.3 From ffb273623bc506d854902a415ef241b79232f93a Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Thu, 6 May 2010 01:20:10 -0700 Subject: netpoll: Use 'bool' for netpoll_rx() return type. Signed-off-by: David S. Miller --- include/linux/netpoll.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'include/linux') diff --git a/include/linux/netpoll.h b/include/linux/netpoll.h index 017e604d05f..e9e23121586 100644 --- a/include/linux/netpoll.h +++ b/include/linux/netpoll.h @@ -55,19 +55,19 @@ void netpoll_send_skb(struct netpoll *np, struct sk_buff *skb); #ifdef CONFIG_NETPOLL -static inline int netpoll_rx(struct sk_buff *skb) +static inline bool netpoll_rx(struct sk_buff *skb) { struct netpoll_info *npinfo = skb->dev->npinfo; unsigned long flags; - int ret = 0; + bool ret = false; if (!npinfo || (list_empty(&npinfo->rx_np) && !npinfo->rx_flags)) - return 0; + return false; spin_lock_irqsave(&npinfo->rx_lock, flags); /* check rx_flags again with the lock held */ if (npinfo->rx_flags && __netpoll_rx(skb)) - ret = 1; + ret = true; spin_unlock_irqrestore(&npinfo->rx_lock, flags); return ret; -- cgit v1.2.3 From 18e8c134f4e984e6639e62846345192816f06d5c Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Thu, 6 May 2010 21:58:51 -0700 Subject: net: Increase NET_SKB_PAD to 64 bytes eth_type_trans() & get_rps_cpus() currently need two 64bytes cache lines in packet to compute rxhash. Increasing NET_SKB_PAD from 32 to 64 reduces the need to one cache line only, and makes RPS faster. NET_IP_ALIGN(2) + ethernet_header(14) + IP_header(20/40) + ports(8) Signed-off-by: Eric Dumazet Signed-off-by: David S. Miller --- include/linux/skbuff.h | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'include/linux') diff --git a/include/linux/skbuff.h b/include/linux/skbuff.h index 88d55395a27..c9525bce80f 100644 --- a/include/linux/skbuff.h +++ b/include/linux/skbuff.h @@ -1361,9 +1361,12 @@ static inline int skb_network_offset(const struct sk_buff *skb) * * Various parts of the networking layer expect at least 32 bytes of * headroom, you should not reduce this. + * With RPS, we raised NET_SKB_PAD to 64 so that get_rps_cpus() fetches span + * a 64 bytes aligned block to fit modern (>= 64 bytes) cache line sizes + * NET_IP_ALIGN(2) + ethernet_header(14) + IP_header(20/40) + ports(8) */ #ifndef NET_SKB_PAD -#define NET_SKB_PAD 32 +#define NET_SKB_PAD 64 #endif extern int ___pskb_trim(struct sk_buff *skb, unsigned int len); -- cgit v1.2.3 From f444de05d20e27cdd960c13fcbcfca3099f03143 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Wed, 5 May 2010 15:25:02 +0200 Subject: cfg80211/mac80211: better channel handling Currently (all tested with hwsim) you can do stupid things like setting up an AP on a certain channel, then adding another virtual interface and making that associate on another channel -- this will make the beaconing to move channel but obviously without the necessary IEs data update. In order to improve this situation, first make the configuration APIs (cfg80211 and nl80211) aware of multi-channel operation -- we'll eventually need that in the future anyway. There's one userland API change and one API addition. The API change is that now SET_WIPHY must be called with virtual interface index rather than only wiphy index in order to take effect for that interface -- luckily all current users (hostapd) do that. For monitor interfaces, the old setting is preserved, but monitors are always slaved to other devices anyway so no guarantees. The second userland API change is the introduction of a per virtual interface SET_CHANNEL command, that hostapd should use going forward to make it easier to understand what's going on (it can automatically detect a kernel with this command). Other than mac80211, no existing cfg80211 drivers are affected by this change because they only allow a single virtual interface. mac80211, however, now needs to be aware that the channel settings are per interface now, and needs to disallow (for now) real multi-channel operation, which is another important part of this patch. One of the immediate benefits is that you can now start hostapd to operate on a hardware that already has a connection on another virtual interface, as long as you specify the same channel. Note that two things are left unhandled (this is an improvement -- not a complete fix): * different HT/no-HT modes currently you could start an HT AP and then connect to a non-HT network on the same channel which would configure the hardware for no HT; that can be fixed fairly easily * CSA An AP we're connected to on a virtual interface might indicate switching channels, and in that case we would follow it, regardless of how many other interfaces are operating; this requires more effort to fix but is pretty rare after all Signed-off-by: Johannes Berg Signed-off-by: John W. Linville --- drivers/net/wireless/libertas/cfg.c | 1 + drivers/net/wireless/orinoco/cfg.c | 1 + drivers/net/wireless/rndis_wlan.c | 4 +- include/linux/nl80211.h | 13 +++ include/net/cfg80211.h | 11 ++- net/mac80211/Makefile | 3 +- net/mac80211/cfg.c | 41 +++++++++ net/mac80211/chan.c | 57 ++++++++++++ net/mac80211/ieee80211_i.h | 11 +++ net/wireless/chan.c | 56 ++++-------- net/wireless/core.h | 12 +-- net/wireless/ibss.c | 5 -- net/wireless/nl80211.c | 171 ++++++++++++++++++++++++++++-------- net/wireless/sme.c | 5 -- net/wireless/wext-compat.c | 15 ++-- net/wireless/wext-sme.c | 2 +- 16 files changed, 300 insertions(+), 108 deletions(-) create mode 100644 net/mac80211/chan.c (limited to 'include/linux') diff --git a/drivers/net/wireless/libertas/cfg.c b/drivers/net/wireless/libertas/cfg.c index 82ebe1461a7..ea9d0b2ea0d 100644 --- a/drivers/net/wireless/libertas/cfg.c +++ b/drivers/net/wireless/libertas/cfg.c @@ -78,6 +78,7 @@ static const u32 cipher_suites[] = { static int lbs_cfg_set_channel(struct wiphy *wiphy, + struct net_device *netdev, struct ieee80211_channel *chan, enum nl80211_channel_type channel_type) { diff --git a/drivers/net/wireless/orinoco/cfg.c b/drivers/net/wireless/orinoco/cfg.c index 81d228de9e5..8c4169c227a 100644 --- a/drivers/net/wireless/orinoco/cfg.c +++ b/drivers/net/wireless/orinoco/cfg.c @@ -159,6 +159,7 @@ static int orinoco_scan(struct wiphy *wiphy, struct net_device *dev, } static int orinoco_set_channel(struct wiphy *wiphy, + struct net_device *netdev, struct ieee80211_channel *chan, enum nl80211_channel_type channel_type) { diff --git a/drivers/net/wireless/rndis_wlan.c b/drivers/net/wireless/rndis_wlan.c index aceb95ef727..99d4f0de77c 100644 --- a/drivers/net/wireless/rndis_wlan.c +++ b/drivers/net/wireless/rndis_wlan.c @@ -534,7 +534,7 @@ static int rndis_join_ibss(struct wiphy *wiphy, struct net_device *dev, static int rndis_leave_ibss(struct wiphy *wiphy, struct net_device *dev); -static int rndis_set_channel(struct wiphy *wiphy, +static int rndis_set_channel(struct wiphy *wiphy, struct net_device *dev, struct ieee80211_channel *chan, enum nl80211_channel_type channel_type); static int rndis_add_key(struct wiphy *wiphy, struct net_device *netdev, @@ -2290,7 +2290,7 @@ static int rndis_leave_ibss(struct wiphy *wiphy, struct net_device *dev) return deauthenticate(usbdev); } -static int rndis_set_channel(struct wiphy *wiphy, +static int rndis_set_channel(struct wiphy *wiphy, struct net_device *netdev, struct ieee80211_channel *chan, enum nl80211_channel_type channel_type) { struct rndis_wlan_private *priv = wiphy_priv(wiphy); diff --git a/include/linux/nl80211.h b/include/linux/nl80211.h index f8750f9a65b..b7c77f9712f 100644 --- a/include/linux/nl80211.h +++ b/include/linux/nl80211.h @@ -52,6 +52,8 @@ * %NL80211_ATTR_WIPHY_CHANNEL_TYPE, %NL80211_ATTR_WIPHY_RETRY_SHORT, * %NL80211_ATTR_WIPHY_RETRY_LONG, %NL80211_ATTR_WIPHY_FRAG_THRESHOLD, * and/or %NL80211_ATTR_WIPHY_RTS_THRESHOLD. + * However, for setting the channel, see %NL80211_CMD_SET_CHANNEL + * instead, the support here is for backward compatibility only. * @NL80211_CMD_NEW_WIPHY: Newly created wiphy, response to get request * or rename notification. Has attributes %NL80211_ATTR_WIPHY and * %NL80211_ATTR_WIPHY_NAME. @@ -329,6 +331,15 @@ * @NL80211_CMD_NOTIFY_CQM: Connection quality monitor notification. This * command is used as an event to indicate the that a trigger level was * reached. + * @NL80211_CMD_SET_CHANNEL: Set the channel (using %NL80211_ATTR_WIPHY_FREQ + * and %NL80211_ATTR_WIPHY_CHANNEL_TYPE) the given interface (identifed + * by %NL80211_ATTR_IFINDEX) shall operate on. + * In case multiple channels are supported by the device, the mechanism + * with which it switches channels is implementation-defined. + * When a monitor interface is given, it can only switch channel while + * no other interfaces are operating to avoid disturbing the operation + * of any other interfaces, and other interfaces will again take + * precedence when they are used. * * @NL80211_CMD_MAX: highest used command number * @__NL80211_CMD_AFTER_LAST: internal use @@ -428,6 +439,8 @@ enum nl80211_commands { NL80211_CMD_SET_CQM, NL80211_CMD_NOTIFY_CQM, + NL80211_CMD_SET_CHANNEL, + /* add new commands above here */ /* used to define NL80211_CMD_MAX below */ diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index 7d10c0182f5..b44a2e5321a 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -966,7 +966,11 @@ struct cfg80211_pmksa { * * @set_txq_params: Set TX queue parameters * - * @set_channel: Set channel + * @set_channel: Set channel for a given wireless interface. Some devices + * may support multi-channel operation (by channel hopping) so cfg80211 + * doesn't verify much. Note, however, that the passed netdev may be + * %NULL as well if the user requested changing the channel for the + * device itself, or for a monitor interface. * * @scan: Request to do a scan. If returning zero, the scan request is given * the driver, and will be valid until passed to cfg80211_scan_done(). @@ -1095,7 +1099,7 @@ struct cfg80211_ops { int (*set_txq_params)(struct wiphy *wiphy, struct ieee80211_txq_params *params); - int (*set_channel)(struct wiphy *wiphy, + int (*set_channel)(struct wiphy *wiphy, struct net_device *dev, struct ieee80211_channel *chan, enum nl80211_channel_type channel_type); @@ -1461,6 +1465,8 @@ struct cfg80211_cached_keys; * @list: (private) Used to collect the interfaces * @netdev: (private) Used to reference back to the netdev * @current_bss: (private) Used by the internal configuration code + * @channel: (private) Used by the internal configuration code to track + * user-set AP, monitor and WDS channels for wireless extensions * @bssid: (private) Used by the internal configuration code * @ssid: (private) Used by the internal configuration code * @ssid_len: (private) Used by the internal configuration code @@ -1507,6 +1513,7 @@ struct wireless_dev { struct cfg80211_internal_bss *authtry_bsses[MAX_AUTH_BSSES]; struct cfg80211_internal_bss *auth_bsses[MAX_AUTH_BSSES]; struct cfg80211_internal_bss *current_bss; /* associated / joined */ + struct ieee80211_channel *channel; bool ps; int ps_timeout; diff --git a/net/mac80211/Makefile b/net/mac80211/Makefile index 04420291e7a..84b48ba8a77 100644 --- a/net/mac80211/Makefile +++ b/net/mac80211/Makefile @@ -23,7 +23,8 @@ mac80211-y := \ key.o \ util.o \ wme.o \ - event.o + event.o \ + chan.o mac80211-$(CONFIG_MAC80211_LEDS) += led.o mac80211-$(CONFIG_MAC80211_DEBUGFS) += \ diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c index b575a506621..414b7dd7d7f 100644 --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c @@ -1161,11 +1161,24 @@ static int ieee80211_set_txq_params(struct wiphy *wiphy, } static int ieee80211_set_channel(struct wiphy *wiphy, + struct net_device *netdev, struct ieee80211_channel *chan, enum nl80211_channel_type channel_type) { struct ieee80211_local *local = wiphy_priv(wiphy); + switch (ieee80211_get_channel_mode(local, NULL)) { + case CHAN_MODE_HOPPING: + return -EBUSY; + case CHAN_MODE_FIXED: + if (local->oper_channel == chan && + local->oper_channel_type == channel_type) + return 0; + return -EBUSY; + case CHAN_MODE_UNDEFINED: + break; + } + local->oper_channel = chan; local->oper_channel_type = channel_type; @@ -1213,6 +1226,20 @@ static int ieee80211_auth(struct wiphy *wiphy, struct net_device *dev, static int ieee80211_assoc(struct wiphy *wiphy, struct net_device *dev, struct cfg80211_assoc_request *req) { + struct ieee80211_local *local = wiphy_priv(wiphy); + struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); + + switch (ieee80211_get_channel_mode(local, sdata)) { + case CHAN_MODE_HOPPING: + return -EBUSY; + case CHAN_MODE_FIXED: + if (local->oper_channel == req->bss->channel) + break; + return -EBUSY; + case CHAN_MODE_UNDEFINED: + break; + } + return ieee80211_mgd_assoc(IEEE80211_DEV_TO_SUB_IF(dev), req); } @@ -1235,8 +1262,22 @@ static int ieee80211_disassoc(struct wiphy *wiphy, struct net_device *dev, static int ieee80211_join_ibss(struct wiphy *wiphy, struct net_device *dev, struct cfg80211_ibss_params *params) { + struct ieee80211_local *local = wiphy_priv(wiphy); struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); + switch (ieee80211_get_channel_mode(local, sdata)) { + case CHAN_MODE_HOPPING: + return -EBUSY; + case CHAN_MODE_FIXED: + if (!params->channel_fixed) + return -EBUSY; + if (local->oper_channel == params->channel) + break; + return -EBUSY; + case CHAN_MODE_UNDEFINED: + break; + } + return ieee80211_ibss_join(sdata, params); } diff --git a/net/mac80211/chan.c b/net/mac80211/chan.c new file mode 100644 index 00000000000..08f3832661a --- /dev/null +++ b/net/mac80211/chan.c @@ -0,0 +1,57 @@ +/* + * mac80211 - channel management + */ + +#include "ieee80211_i.h" + +enum ieee80211_chan_mode +__ieee80211_get_channel_mode(struct ieee80211_local *local, + struct ieee80211_sub_if_data *ignore) +{ + struct ieee80211_sub_if_data *sdata; + + WARN_ON(!mutex_is_locked(&local->iflist_mtx)); + + list_for_each_entry(sdata, &local->interfaces, list) { + if (sdata == ignore) + continue; + + if (!ieee80211_sdata_running(sdata)) + continue; + + if (sdata->vif.type == NL80211_IFTYPE_MONITOR) + continue; + + if (sdata->vif.type == NL80211_IFTYPE_STATION && + !sdata->u.mgd.associated) + continue; + + if (sdata->vif.type == NL80211_IFTYPE_ADHOC) { + if (!sdata->u.ibss.ssid_len) + continue; + if (!sdata->u.ibss.fixed_channel) + return CHAN_MODE_HOPPING; + } + + if (sdata->vif.type == NL80211_IFTYPE_AP && + !sdata->u.ap.beacon) + continue; + + return CHAN_MODE_FIXED; + } + + return CHAN_MODE_UNDEFINED; +} + +enum ieee80211_chan_mode +ieee80211_get_channel_mode(struct ieee80211_local *local, + struct ieee80211_sub_if_data *ignore) +{ + enum ieee80211_chan_mode mode; + + mutex_lock(&local->iflist_mtx); + mode = __ieee80211_get_channel_mode(local, ignore); + mutex_unlock(&local->iflist_mtx); + + return mode; +} diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index c8077a3647c..359edff3147 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -1229,6 +1229,17 @@ int ieee80211_wk_remain_on_channel(struct ieee80211_sub_if_data *sdata, int ieee80211_wk_cancel_remain_on_channel( struct ieee80211_sub_if_data *sdata, u64 cookie); +/* channel management */ +enum ieee80211_chan_mode { + CHAN_MODE_UNDEFINED, + CHAN_MODE_HOPPING, + CHAN_MODE_FIXED, +}; + +enum ieee80211_chan_mode +ieee80211_get_channel_mode(struct ieee80211_local *local, + struct ieee80211_sub_if_data *ignore); + #ifdef CONFIG_MAC80211_NOINLINE #define debug_noinline noinline #else diff --git a/net/wireless/chan.c b/net/wireless/chan.c index bf1737fc9a7..d92d088026b 100644 --- a/net/wireless/chan.c +++ b/net/wireless/chan.c @@ -9,38 +9,6 @@ #include #include "core.h" -struct ieee80211_channel * -rdev_fixed_channel(struct cfg80211_registered_device *rdev, - struct wireless_dev *for_wdev) -{ - struct wireless_dev *wdev; - struct ieee80211_channel *result = NULL; - - WARN_ON(!mutex_is_locked(&rdev->devlist_mtx)); - - list_for_each_entry(wdev, &rdev->netdev_list, list) { - if (wdev == for_wdev) - continue; - - /* - * Lock manually to tell lockdep about allowed - * nesting here if for_wdev->mtx is held already. - * This is ok as it's all under the rdev devlist - * mutex and as such can only be done once at any - * given time. - */ - mutex_lock_nested(&wdev->mtx, SINGLE_DEPTH_NESTING); - if (wdev->current_bss) - result = wdev->current_bss->pub.channel; - wdev_unlock(wdev); - - if (result) - break; - } - - return result; -} - struct ieee80211_channel * rdev_freq_to_chan(struct cfg80211_registered_device *rdev, int freq, enum nl80211_channel_type channel_type) @@ -75,15 +43,22 @@ rdev_freq_to_chan(struct cfg80211_registered_device *rdev, return chan; } -int rdev_set_freq(struct cfg80211_registered_device *rdev, - struct wireless_dev *for_wdev, - int freq, enum nl80211_channel_type channel_type) +int cfg80211_set_freq(struct cfg80211_registered_device *rdev, + struct wireless_dev *wdev, int freq, + enum nl80211_channel_type channel_type) { struct ieee80211_channel *chan; int result; - if (rdev_fixed_channel(rdev, for_wdev)) - return -EBUSY; + if (wdev->iftype == NL80211_IFTYPE_MONITOR) + wdev = NULL; + + if (wdev) { + ASSERT_WDEV_LOCK(wdev); + + if (!netif_running(wdev->netdev)) + return -ENETDOWN; + } if (!rdev->ops->set_channel) return -EOPNOTSUPP; @@ -92,11 +67,14 @@ int rdev_set_freq(struct cfg80211_registered_device *rdev, if (!chan) return -EINVAL; - result = rdev->ops->set_channel(&rdev->wiphy, chan, channel_type); + result = rdev->ops->set_channel(&rdev->wiphy, + wdev ? wdev->netdev : NULL, + chan, channel_type); if (result) return result; - rdev->channel = chan; + if (wdev) + wdev->channel = chan; return 0; } diff --git a/net/wireless/core.h b/net/wireless/core.h index b2234b436ea..ae930acf75e 100644 --- a/net/wireless/core.h +++ b/net/wireless/core.h @@ -70,9 +70,6 @@ struct cfg80211_registered_device { struct work_struct conn_work; struct work_struct event_work; - /* current channel */ - struct ieee80211_channel *channel; - /* must be last because of the way we do wiphy_priv(), * and it should at least be aligned to NETDEV_ALIGN */ struct wiphy wiphy __attribute__((__aligned__(NETDEV_ALIGN))); @@ -387,15 +384,12 @@ int cfg80211_change_iface(struct cfg80211_registered_device *rdev, u32 *flags, struct vif_params *params); void cfg80211_process_rdev_events(struct cfg80211_registered_device *rdev); -struct ieee80211_channel * -rdev_fixed_channel(struct cfg80211_registered_device *rdev, - struct wireless_dev *for_wdev); struct ieee80211_channel * rdev_freq_to_chan(struct cfg80211_registered_device *rdev, int freq, enum nl80211_channel_type channel_type); -int rdev_set_freq(struct cfg80211_registered_device *rdev, - struct wireless_dev *for_wdev, - int freq, enum nl80211_channel_type channel_type); +int cfg80211_set_freq(struct cfg80211_registered_device *rdev, + struct wireless_dev *wdev, int freq, + enum nl80211_channel_type channel_type); u16 cfg80211_calculate_bitrate(struct rate_info *rate); diff --git a/net/wireless/ibss.c b/net/wireless/ibss.c index 6ef5a491fb4..9825317e653 100644 --- a/net/wireless/ibss.c +++ b/net/wireless/ibss.c @@ -80,15 +80,10 @@ int __cfg80211_join_ibss(struct cfg80211_registered_device *rdev, struct cfg80211_cached_keys *connkeys) { struct wireless_dev *wdev = dev->ieee80211_ptr; - struct ieee80211_channel *chan; int err; ASSERT_WDEV_LOCK(wdev); - chan = rdev_fixed_channel(rdev, wdev); - if (chan && chan != params->channel) - return -EBUSY; - if (wdev->ssid_len) return -EALREADY; diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index c27bef8e0c1..ec1b4a896c6 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -588,6 +588,7 @@ static int nl80211_send_wiphy(struct sk_buff *msg, u32 pid, u32 seq, int flags, i++; NLA_PUT_U32(msg, i, NL80211_CMD_SET_WIPHY_NETNS); } + CMD(set_channel, SET_CHANNEL); #undef CMD @@ -688,10 +689,90 @@ static int parse_txq_params(struct nlattr *tb[], return 0; } +static bool nl80211_can_set_dev_channel(struct wireless_dev *wdev) +{ + /* + * You can only set the channel explicitly for AP, mesh + * and WDS type interfaces; all others have their channel + * managed via their respective "establish a connection" + * command (connect, join, ...) + * + * Monitors are special as they are normally slaved to + * whatever else is going on, so they behave as though + * you tried setting the wiphy channel itself. + */ + return !wdev || + wdev->iftype == NL80211_IFTYPE_AP || + wdev->iftype == NL80211_IFTYPE_WDS || + wdev->iftype == NL80211_IFTYPE_MESH_POINT || + wdev->iftype == NL80211_IFTYPE_MONITOR; +} + +static int __nl80211_set_channel(struct cfg80211_registered_device *rdev, + struct wireless_dev *wdev, + struct genl_info *info) +{ + enum nl80211_channel_type channel_type = NL80211_CHAN_NO_HT; + u32 freq; + int result; + + if (!info->attrs[NL80211_ATTR_WIPHY_FREQ]) + return -EINVAL; + + if (!nl80211_can_set_dev_channel(wdev)) + return -EOPNOTSUPP; + + if (info->attrs[NL80211_ATTR_WIPHY_CHANNEL_TYPE]) { + channel_type = nla_get_u32(info->attrs[ + NL80211_ATTR_WIPHY_CHANNEL_TYPE]); + if (channel_type != NL80211_CHAN_NO_HT && + channel_type != NL80211_CHAN_HT20 && + channel_type != NL80211_CHAN_HT40PLUS && + channel_type != NL80211_CHAN_HT40MINUS) + return -EINVAL; + } + + freq = nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_FREQ]); + + mutex_lock(&rdev->devlist_mtx); + if (wdev) { + wdev_lock(wdev); + result = cfg80211_set_freq(rdev, wdev, freq, channel_type); + wdev_unlock(wdev); + } else { + result = cfg80211_set_freq(rdev, NULL, freq, channel_type); + } + mutex_unlock(&rdev->devlist_mtx); + + return result; +} + +static int nl80211_set_channel(struct sk_buff *skb, struct genl_info *info) +{ + struct cfg80211_registered_device *rdev; + struct net_device *netdev; + int result; + + rtnl_lock(); + + result = get_rdev_dev_by_info_ifindex(info, &rdev, &netdev); + if (result) + goto unlock; + + result = __nl80211_set_channel(rdev, netdev->ieee80211_ptr, info); + + unlock: + rtnl_unlock(); + + return result; +} + static int nl80211_set_wiphy(struct sk_buff *skb, struct genl_info *info) { struct cfg80211_registered_device *rdev; - int result = 0, rem_txq_params = 0; + struct net_device *netdev = NULL; + struct wireless_dev *wdev; + int result, rem_txq_params = 0; struct nlattr *nl_txq_params; u32 changed; u8 retry_short = 0, retry_long = 0; @@ -700,16 +781,50 @@ static int nl80211_set_wiphy(struct sk_buff *skb, struct genl_info *info) rtnl_lock(); + /* + * Try to find the wiphy and netdev. Normally this + * function shouldn't need the netdev, but this is + * done for backward compatibility -- previously + * setting the channel was done per wiphy, but now + * it is per netdev. Previous userland like hostapd + * also passed a netdev to set_wiphy, so that it is + * possible to let that go to the right netdev! + */ mutex_lock(&cfg80211_mutex); - rdev = __cfg80211_rdev_from_info(info); - if (IS_ERR(rdev)) { - mutex_unlock(&cfg80211_mutex); - result = PTR_ERR(rdev); - goto unlock; + if (info->attrs[NL80211_ATTR_IFINDEX]) { + int ifindex = nla_get_u32(info->attrs[NL80211_ATTR_IFINDEX]); + + netdev = dev_get_by_index(genl_info_net(info), ifindex); + if (netdev && netdev->ieee80211_ptr) { + rdev = wiphy_to_dev(netdev->ieee80211_ptr->wiphy); + mutex_lock(&rdev->mtx); + } else + netdev = NULL; } - mutex_lock(&rdev->mtx); + if (!netdev) { + rdev = __cfg80211_rdev_from_info(info); + if (IS_ERR(rdev)) { + mutex_unlock(&cfg80211_mutex); + result = PTR_ERR(rdev); + goto unlock; + } + wdev = NULL; + netdev = NULL; + result = 0; + + mutex_lock(&rdev->mtx); + } else if (netif_running(netdev) && + nl80211_can_set_dev_channel(netdev->ieee80211_ptr)) + wdev = netdev->ieee80211_ptr; + else + wdev = NULL; + + /* + * end workaround code, by now the rdev is available + * and locked, and wdev may or may not be NULL. + */ if (info->attrs[NL80211_ATTR_WIPHY_NAME]) result = cfg80211_dev_rename( @@ -748,26 +863,7 @@ static int nl80211_set_wiphy(struct sk_buff *skb, struct genl_info *info) } if (info->attrs[NL80211_ATTR_WIPHY_FREQ]) { - enum nl80211_channel_type channel_type = NL80211_CHAN_NO_HT; - u32 freq; - - result = -EINVAL; - - if (info->attrs[NL80211_ATTR_WIPHY_CHANNEL_TYPE]) { - channel_type = nla_get_u32(info->attrs[ - NL80211_ATTR_WIPHY_CHANNEL_TYPE]); - if (channel_type != NL80211_CHAN_NO_HT && - channel_type != NL80211_CHAN_HT20 && - channel_type != NL80211_CHAN_HT40PLUS && - channel_type != NL80211_CHAN_HT40MINUS) - goto bad_res; - } - - freq = nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_FREQ]); - - mutex_lock(&rdev->devlist_mtx); - result = rdev_set_freq(rdev, NULL, freq, channel_type); - mutex_unlock(&rdev->devlist_mtx); + result = __nl80211_set_channel(rdev, wdev, info); if (result) goto bad_res; } @@ -864,6 +960,8 @@ static int nl80211_set_wiphy(struct sk_buff *skb, struct genl_info *info) bad_res: mutex_unlock(&rdev->mtx); + if (netdev) + dev_put(netdev); unlock: rtnl_unlock(); return result; @@ -3561,9 +3659,8 @@ static int nl80211_associate(struct sk_buff *skb, struct genl_info *info) { struct cfg80211_registered_device *rdev; struct net_device *dev; - struct wireless_dev *wdev; struct cfg80211_crypto_settings crypto; - struct ieee80211_channel *chan, *fixedchan; + struct ieee80211_channel *chan; const u8 *bssid, *ssid, *ie = NULL, *prev_bssid = NULL; int err, ssid_len, ie_len = 0; bool use_mfp = false; @@ -3606,16 +3703,6 @@ static int nl80211_associate(struct sk_buff *skb, struct genl_info *info) goto out; } - mutex_lock(&rdev->devlist_mtx); - wdev = dev->ieee80211_ptr; - fixedchan = rdev_fixed_channel(rdev, wdev); - if (fixedchan && chan != fixedchan) { - err = -EBUSY; - mutex_unlock(&rdev->devlist_mtx); - goto out; - } - mutex_unlock(&rdev->devlist_mtx); - ssid = nla_data(info->attrs[NL80211_ATTR_SSID]); ssid_len = nla_len(info->attrs[NL80211_ATTR_SSID]); @@ -5185,6 +5272,12 @@ static struct genl_ops nl80211_ops[] = { .policy = nl80211_policy, .flags = GENL_ADMIN_PERM, }, + { + .cmd = NL80211_CMD_SET_CHANNEL, + .doit = nl80211_set_channel, + .policy = nl80211_policy, + .flags = GENL_ADMIN_PERM, + }, }; static struct genl_multicast_group nl80211_mlme_mcgrp = { diff --git a/net/wireless/sme.c b/net/wireless/sme.c index dcd7685242f..14cf8163912 100644 --- a/net/wireless/sme.c +++ b/net/wireless/sme.c @@ -740,7 +740,6 @@ int __cfg80211_connect(struct cfg80211_registered_device *rdev, const u8 *prev_bssid) { struct wireless_dev *wdev = dev->ieee80211_ptr; - struct ieee80211_channel *chan; struct cfg80211_bss *bss = NULL; int err; @@ -749,10 +748,6 @@ int __cfg80211_connect(struct cfg80211_registered_device *rdev, if (wdev->sme_state != CFG80211_SME_IDLE) return -EALREADY; - chan = rdev_fixed_channel(rdev, wdev); - if (chan && chan != connect->channel) - return -EBUSY; - if (WARN_ON(wdev->connect_keys)) { kfree(wdev->connect_keys); wdev->connect_keys = NULL; diff --git a/net/wireless/wext-compat.c b/net/wireless/wext-compat.c index 9ab51838849..75848c6cb22 100644 --- a/net/wireless/wext-compat.c +++ b/net/wireless/wext-compat.c @@ -781,16 +781,22 @@ int cfg80211_wext_siwfreq(struct net_device *dev, return cfg80211_mgd_wext_siwfreq(dev, info, wextfreq, extra); case NL80211_IFTYPE_ADHOC: return cfg80211_ibss_wext_siwfreq(dev, info, wextfreq, extra); - default: + case NL80211_IFTYPE_MONITOR: + case NL80211_IFTYPE_WDS: + case NL80211_IFTYPE_MESH_POINT: freq = cfg80211_wext_freq(wdev->wiphy, wextfreq); if (freq < 0) return freq; if (freq == 0) return -EINVAL; + wdev_lock(wdev); mutex_lock(&rdev->devlist_mtx); - err = rdev_set_freq(rdev, NULL, freq, NL80211_CHAN_NO_HT); + err = cfg80211_set_freq(rdev, wdev, freq, NL80211_CHAN_NO_HT); mutex_unlock(&rdev->devlist_mtx); + wdev_unlock(wdev); return err; + default: + return -EOPNOTSUPP; } } EXPORT_SYMBOL_GPL(cfg80211_wext_siwfreq); @@ -800,7 +806,6 @@ int cfg80211_wext_giwfreq(struct net_device *dev, struct iw_freq *freq, char *extra) { struct wireless_dev *wdev = dev->ieee80211_ptr; - struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy); switch (wdev->iftype) { case NL80211_IFTYPE_STATION: @@ -808,9 +813,9 @@ int cfg80211_wext_giwfreq(struct net_device *dev, case NL80211_IFTYPE_ADHOC: return cfg80211_ibss_wext_giwfreq(dev, info, freq, extra); default: - if (!rdev->channel) + if (!wdev->channel) return -EINVAL; - freq->m = rdev->channel->center_freq; + freq->m = wdev->channel->center_freq; freq->e = 6; return 0; } diff --git a/net/wireless/wext-sme.c b/net/wireless/wext-sme.c index 5615a880253..8e5ab4f4e9c 100644 --- a/net/wireless/wext-sme.c +++ b/net/wireless/wext-sme.c @@ -107,7 +107,7 @@ int cfg80211_mgd_wext_siwfreq(struct net_device *dev, /* SSID is not set, we just want to switch channel */ if (chan && !wdev->wext.connect.ssid_len) { - err = rdev_set_freq(rdev, wdev, freq, NL80211_CHAN_NO_HT); + err = cfg80211_set_freq(rdev, wdev, freq, NL80211_CHAN_NO_HT); goto out; } -- cgit v1.2.3 From b5aa30b19121de49021fba57aa1f6e4c787fcf67 Mon Sep 17 00:00:00 2001 From: Patrick McHardy Date: Tue, 11 May 2010 14:40:50 +0200 Subject: ipv6: ip6mr: remove net pointer from struct mfc6_cache Now that cache entries in unres_queue don't need to be distinguished by their network namespace pointer anymore, we can remove it from struct mfc6_cache add pass the namespace as function argument to the functions that need it. Signed-off-by: Patrick McHardy --- include/linux/mroute6.h | 15 ------------ net/ipv6/ip6mr.c | 63 ++++++++++++++++++++++++------------------------- 2 files changed, 31 insertions(+), 47 deletions(-) (limited to 'include/linux') diff --git a/include/linux/mroute6.h b/include/linux/mroute6.h index 2caa1a8e525..04e2e54d040 100644 --- a/include/linux/mroute6.h +++ b/include/linux/mroute6.h @@ -183,9 +183,6 @@ struct mif_device { struct mfc6_cache { struct mfc6_cache *next; /* Next entry on cache line */ -#ifdef CONFIG_NET_NS - struct net *mfc6_net; -#endif struct in6_addr mf6c_mcastgrp; /* Group the entry belongs to */ struct in6_addr mf6c_origin; /* Source of packet */ mifi_t mf6c_parent; /* Source interface */ @@ -208,18 +205,6 @@ struct mfc6_cache { } mfc_un; }; -static inline -struct net *mfc6_net(const struct mfc6_cache *mfc) -{ - return read_pnet(&mfc->mfc6_net); -} - -static inline -void mfc6_net_set(struct mfc6_cache *mfc, struct net *net) -{ - write_pnet(&mfc->mfc6_net, hold_net(net)); -} - #define MFC_STATIC 1 #define MFC_NOTIFY 2 diff --git a/net/ipv6/ip6mr.c b/net/ipv6/ip6mr.c index 7236030e403..b3783a436bb 100644 --- a/net/ipv6/ip6mr.c +++ b/net/ipv6/ip6mr.c @@ -76,10 +76,12 @@ static DEFINE_SPINLOCK(mfc_unres_lock); static struct kmem_cache *mrt_cachep __read_mostly; -static int ip6_mr_forward(struct sk_buff *skb, struct mfc6_cache *cache); +static int ip6_mr_forward(struct net *net, struct sk_buff *skb, + struct mfc6_cache *cache); static int ip6mr_cache_report(struct net *net, struct sk_buff *pkt, mifi_t mifi, int assert); -static int ip6mr_fill_mroute(struct sk_buff *skb, struct mfc6_cache *c, struct rtmsg *rtm); +static int ip6mr_fill_mroute(struct net *net, struct sk_buff *skb, + struct mfc6_cache *c, struct rtmsg *rtm); static void mroute_clean_tables(struct net *net); @@ -523,7 +525,6 @@ static int mif6_delete(struct net *net, int vifi, struct list_head *head) static inline void ip6mr_cache_free(struct mfc6_cache *c) { - release_net(mfc6_net(c)); kmem_cache_free(mrt_cachep, c); } @@ -531,10 +532,9 @@ static inline void ip6mr_cache_free(struct mfc6_cache *c) and reporting error to netlink readers. */ -static void ip6mr_destroy_unres(struct mfc6_cache *c) +static void ip6mr_destroy_unres(struct net *net, struct mfc6_cache *c) { struct sk_buff *skb; - struct net *net = mfc6_net(c); atomic_dec(&net->ipv6.cache_resolve_queue_len); @@ -575,7 +575,7 @@ static void ipmr_do_expire_process(struct net *net) } *cp = c->next; - ip6mr_destroy_unres(c); + ip6mr_destroy_unres(net, c); } if (net->ipv6.mfc6_unres_queue != NULL) @@ -599,10 +599,10 @@ static void ipmr_expire_process(unsigned long arg) /* Fill oifs list. It is called under write locked mrt_lock. */ -static void ip6mr_update_thresholds(struct mfc6_cache *cache, unsigned char *ttls) +static void ip6mr_update_thresholds(struct net *net, struct mfc6_cache *cache, + unsigned char *ttls) { int vifi; - struct net *net = mfc6_net(cache); cache->mfc_un.res.minvif = MAXMIFS; cache->mfc_un.res.maxvif = 0; @@ -717,24 +717,22 @@ static struct mfc6_cache *ip6mr_cache_find(struct net *net, /* * Allocate a multicast cache entry */ -static struct mfc6_cache *ip6mr_cache_alloc(struct net *net) +static struct mfc6_cache *ip6mr_cache_alloc(void) { struct mfc6_cache *c = kmem_cache_zalloc(mrt_cachep, GFP_KERNEL); if (c == NULL) return NULL; c->mfc_un.res.minvif = MAXMIFS; - mfc6_net_set(c, net); return c; } -static struct mfc6_cache *ip6mr_cache_alloc_unres(struct net *net) +static struct mfc6_cache *ip6mr_cache_alloc_unres(void) { struct mfc6_cache *c = kmem_cache_zalloc(mrt_cachep, GFP_ATOMIC); if (c == NULL) return NULL; skb_queue_head_init(&c->mfc_un.unres.unresolved); c->mfc_un.unres.expires = jiffies + 10 * HZ; - mfc6_net_set(c, net); return c; } @@ -742,7 +740,8 @@ static struct mfc6_cache *ip6mr_cache_alloc_unres(struct net *net) * A cache entry has gone into a resolved state from queued */ -static void ip6mr_cache_resolve(struct mfc6_cache *uc, struct mfc6_cache *c) +static void ip6mr_cache_resolve(struct net *net, struct mfc6_cache *uc, + struct mfc6_cache *c) { struct sk_buff *skb; @@ -755,7 +754,7 @@ static void ip6mr_cache_resolve(struct mfc6_cache *uc, struct mfc6_cache *c) int err; struct nlmsghdr *nlh = (struct nlmsghdr *)skb_pull(skb, sizeof(struct ipv6hdr)); - if (ip6mr_fill_mroute(skb, c, NLMSG_DATA(nlh)) > 0) { + if (ip6mr_fill_mroute(net, skb, c, NLMSG_DATA(nlh)) > 0) { nlh->nlmsg_len = skb_tail_pointer(skb) - (u8 *)nlh; } else { nlh->nlmsg_type = NLMSG_ERROR; @@ -763,9 +762,9 @@ static void ip6mr_cache_resolve(struct mfc6_cache *uc, struct mfc6_cache *c) skb_trim(skb, nlh->nlmsg_len); ((struct nlmsgerr *)NLMSG_DATA(nlh))->error = -EMSGSIZE; } - err = rtnl_unicast(skb, mfc6_net(uc), NETLINK_CB(skb).pid); + err = rtnl_unicast(skb, net, NETLINK_CB(skb).pid); } else - ip6_mr_forward(skb, c); + ip6_mr_forward(net, skb, c); } } @@ -889,7 +888,7 @@ ip6mr_cache_unresolved(struct net *net, mifi_t mifi, struct sk_buff *skb) */ if (atomic_read(&net->ipv6.cache_resolve_queue_len) >= 10 || - (c = ip6mr_cache_alloc_unres(net)) == NULL) { + (c = ip6mr_cache_alloc_unres()) == NULL) { spin_unlock_bh(&mfc_unres_lock); kfree_skb(skb); @@ -1133,7 +1132,7 @@ static int ip6mr_mfc_add(struct net *net, struct mf6cctl *mfc, int mrtsock) if (c != NULL) { write_lock_bh(&mrt_lock); c->mf6c_parent = mfc->mf6cc_parent; - ip6mr_update_thresholds(c, ttls); + ip6mr_update_thresholds(net, c, ttls); if (!mrtsock) c->mfc_flags |= MFC_STATIC; write_unlock_bh(&mrt_lock); @@ -1143,14 +1142,14 @@ static int ip6mr_mfc_add(struct net *net, struct mf6cctl *mfc, int mrtsock) if (!ipv6_addr_is_multicast(&mfc->mf6cc_mcastgrp.sin6_addr)) return -EINVAL; - c = ip6mr_cache_alloc(net); + c = ip6mr_cache_alloc(); if (c == NULL) return -ENOMEM; c->mf6c_origin = mfc->mf6cc_origin.sin6_addr; c->mf6c_mcastgrp = mfc->mf6cc_mcastgrp.sin6_addr; c->mf6c_parent = mfc->mf6cc_parent; - ip6mr_update_thresholds(c, ttls); + ip6mr_update_thresholds(net, c, ttls); if (!mrtsock) c->mfc_flags |= MFC_STATIC; @@ -1178,7 +1177,7 @@ static int ip6mr_mfc_add(struct net *net, struct mf6cctl *mfc, int mrtsock) spin_unlock_bh(&mfc_unres_lock); if (uc) { - ip6mr_cache_resolve(uc, c); + ip6mr_cache_resolve(net, uc, c); ip6mr_cache_free(uc); } return 0; @@ -1229,7 +1228,7 @@ static void mroute_clean_tables(struct net *net) cp = &net->ipv6.mfc6_unres_queue; while ((c = *cp) != NULL) { *cp = c->next; - ip6mr_destroy_unres(c); + ip6mr_destroy_unres(net, c); } spin_unlock_bh(&mfc_unres_lock); } @@ -1497,10 +1496,10 @@ static inline int ip6mr_forward2_finish(struct sk_buff *skb) * Processing handlers for ip6mr_forward */ -static int ip6mr_forward2(struct sk_buff *skb, struct mfc6_cache *c, int vifi) +static int ip6mr_forward2(struct net *net, struct sk_buff *skb, + struct mfc6_cache *c, int vifi) { struct ipv6hdr *ipv6h; - struct net *net = mfc6_net(c); struct mif_device *vif = &net->ipv6.vif6_table[vifi]; struct net_device *dev; struct dst_entry *dst; @@ -1581,11 +1580,11 @@ static int ip6mr_find_vif(struct net_device *dev) return ct; } -static int ip6_mr_forward(struct sk_buff *skb, struct mfc6_cache *cache) +static int ip6_mr_forward(struct net *net, struct sk_buff *skb, + struct mfc6_cache *cache) { int psend = -1; int vif, ct; - struct net *net = mfc6_net(cache); vif = cache->mf6c_parent; cache->mfc_un.res.pkt++; @@ -1627,13 +1626,13 @@ static int ip6_mr_forward(struct sk_buff *skb, struct mfc6_cache *cache) if (psend != -1) { struct sk_buff *skb2 = skb_clone(skb, GFP_ATOMIC); if (skb2) - ip6mr_forward2(skb2, cache, psend); + ip6mr_forward2(net, skb2, cache, psend); } psend = ct; } } if (psend != -1) { - ip6mr_forward2(skb, cache, psend); + ip6mr_forward2(net, skb, cache, psend); return 0; } @@ -1674,7 +1673,7 @@ int ip6_mr_input(struct sk_buff *skb) return -ENODEV; } - ip6_mr_forward(skb, cache); + ip6_mr_forward(net, skb, cache); read_unlock(&mrt_lock); @@ -1683,11 +1682,11 @@ int ip6_mr_input(struct sk_buff *skb) static int -ip6mr_fill_mroute(struct sk_buff *skb, struct mfc6_cache *c, struct rtmsg *rtm) +ip6mr_fill_mroute(struct net *net, struct sk_buff *skb, struct mfc6_cache *c, + struct rtmsg *rtm) { int ct; struct rtnexthop *nhp; - struct net *net = mfc6_net(c); u8 *b = skb_tail_pointer(skb); struct rtattr *mp_head; @@ -1781,7 +1780,7 @@ int ip6mr_get_route(struct net *net, if (!nowait && (rtm->rtm_flags&RTM_F_NOTIFY)) cache->mfc_flags |= MFC_NOTIFY; - err = ip6mr_fill_mroute(skb, cache, rtm); + err = ip6mr_fill_mroute(net, skb, cache, rtm); read_unlock(&mrt_lock); return err; } -- cgit v1.2.3 From f30a77842129b5656360cc1f5db48a3fcfb64528 Mon Sep 17 00:00:00 2001 From: Patrick McHardy Date: Tue, 11 May 2010 14:40:51 +0200 Subject: ipv6: ip6mr: convert struct mfc_cache to struct list_head Signed-off-by: Patrick McHardy --- include/linux/mroute6.h | 2 +- include/net/netns/ipv6.h | 4 +- net/ipv6/ip6mr.c | 127 +++++++++++++++++++++++------------------------ 3 files changed, 65 insertions(+), 68 deletions(-) (limited to 'include/linux') diff --git a/include/linux/mroute6.h b/include/linux/mroute6.h index 04e2e54d040..94a0cb521ca 100644 --- a/include/linux/mroute6.h +++ b/include/linux/mroute6.h @@ -182,7 +182,7 @@ struct mif_device { #define VIFF_STATIC 0x8000 struct mfc6_cache { - struct mfc6_cache *next; /* Next entry on cache line */ + struct list_head list; struct in6_addr mf6c_mcastgrp; /* Group the entry belongs to */ struct in6_addr mf6c_origin; /* Source of packet */ mifi_t mf6c_parent; /* Source interface */ diff --git a/include/net/netns/ipv6.h b/include/net/netns/ipv6.h index 43d842ab004..9cb3b5f2cd7 100644 --- a/include/net/netns/ipv6.h +++ b/include/net/netns/ipv6.h @@ -61,8 +61,8 @@ struct netns_ipv6 { #ifdef CONFIG_IPV6_MROUTE struct sock *mroute6_sk; struct timer_list ipmr_expire_timer; - struct mfc6_cache *mfc6_unres_queue; - struct mfc6_cache **mfc6_cache_array; + struct list_head mfc6_unres_queue; + struct list_head *mfc6_cache_array; struct mif_device *vif6_table; int maxvif; atomic_t cache_resolve_queue_len; diff --git a/net/ipv6/ip6mr.c b/net/ipv6/ip6mr.c index b3783a436bb..08e09042ad1 100644 --- a/net/ipv6/ip6mr.c +++ b/net/ipv6/ip6mr.c @@ -89,7 +89,7 @@ static void mroute_clean_tables(struct net *net); struct ipmr_mfc_iter { struct seq_net_private p; - struct mfc6_cache **cache; + struct list_head *cache; int ct; }; @@ -99,18 +99,18 @@ static struct mfc6_cache *ipmr_mfc_seq_idx(struct net *net, { struct mfc6_cache *mfc; - it->cache = net->ipv6.mfc6_cache_array; read_lock(&mrt_lock); - for (it->ct = 0; it->ct < MFC6_LINES; it->ct++) - for (mfc = net->ipv6.mfc6_cache_array[it->ct]; - mfc; mfc = mfc->next) + for (it->ct = 0; it->ct < MFC6_LINES; it->ct++) { + it->cache = &net->ipv6.mfc6_cache_array[it->ct]; + list_for_each_entry(mfc, it->cache, list) if (pos-- == 0) return mfc; + } read_unlock(&mrt_lock); - it->cache = &net->ipv6.mfc6_unres_queue; spin_lock_bh(&mfc_unres_lock); - for (mfc = net->ipv6.mfc6_unres_queue; mfc; mfc = mfc->next) + it->cache = &net->ipv6.mfc6_unres_queue; + list_for_each_entry(mfc, it->cache, list) if (pos-- == 0) return mfc; spin_unlock_bh(&mfc_unres_lock); @@ -119,9 +119,6 @@ static struct mfc6_cache *ipmr_mfc_seq_idx(struct net *net, return NULL; } - - - /* * The /proc interfaces to multicast routing /proc/ip6_mr_cache /proc/ip6_mr_vif */ @@ -238,18 +235,19 @@ static void *ipmr_mfc_seq_next(struct seq_file *seq, void *v, loff_t *pos) if (v == SEQ_START_TOKEN) return ipmr_mfc_seq_idx(net, seq->private, 0); - if (mfc->next) - return mfc->next; + if (mfc->list.next != it->cache) + return list_entry(mfc->list.next, struct mfc6_cache, list); if (it->cache == &net->ipv6.mfc6_unres_queue) goto end_of_list; - BUG_ON(it->cache != net->ipv6.mfc6_cache_array); + BUG_ON(it->cache != &net->ipv6.mfc6_cache_array[it->ct]); while (++it->ct < MFC6_LINES) { - mfc = net->ipv6.mfc6_cache_array[it->ct]; - if (mfc) - return mfc; + it->cache = &net->ipv6.mfc6_cache_array[it->ct]; + if (list_empty(it->cache)) + continue; + return list_first_entry(it->cache, struct mfc6_cache, list); } /* exhausted cache_array, show unresolved */ @@ -258,9 +256,8 @@ static void *ipmr_mfc_seq_next(struct seq_file *seq, void *v, loff_t *pos) it->ct = 0; spin_lock_bh(&mfc_unres_lock); - mfc = net->ipv6.mfc6_unres_queue; - if (mfc) - return mfc; + if (!list_empty(it->cache)) + return list_first_entry(it->cache, struct mfc6_cache, list); end_of_list: spin_unlock_bh(&mfc_unres_lock); @@ -560,25 +557,22 @@ static void ipmr_do_expire_process(struct net *net) { unsigned long now = jiffies; unsigned long expires = 10 * HZ; - struct mfc6_cache *c, **cp; - - cp = &net->ipv6.mfc6_unres_queue; + struct mfc6_cache *c, *next; - while ((c = *cp) != NULL) { + list_for_each_entry_safe(c, next, &net->ipv6.mfc6_unres_queue, list) { if (time_after(c->mfc_un.unres.expires, now)) { /* not yet... */ unsigned long interval = c->mfc_un.unres.expires - now; if (interval < expires) expires = interval; - cp = &c->next; continue; } - *cp = c->next; + list_del(&c->list); ip6mr_destroy_unres(net, c); } - if (net->ipv6.mfc6_unres_queue != NULL) + if (!list_empty(&net->ipv6.mfc6_unres_queue)) mod_timer(&net->ipv6.ipmr_expire_timer, jiffies + expires); } @@ -591,7 +585,7 @@ static void ipmr_expire_process(unsigned long arg) return; } - if (net->ipv6.mfc6_unres_queue != NULL) + if (!list_empty(&net->ipv6.mfc6_unres_queue)) ipmr_do_expire_process(net); spin_unlock(&mfc_unres_lock); @@ -706,12 +700,12 @@ static struct mfc6_cache *ip6mr_cache_find(struct net *net, int line = MFC6_HASH(mcastgrp, origin); struct mfc6_cache *c; - for (c = net->ipv6.mfc6_cache_array[line]; c; c = c->next) { + list_for_each_entry(c, &net->ipv6.mfc6_cache_array[line], list) { if (ipv6_addr_equal(&c->mf6c_origin, origin) && ipv6_addr_equal(&c->mf6c_mcastgrp, mcastgrp)) - break; + return c; } - return c; + return NULL; } /* @@ -872,17 +866,20 @@ static int ip6mr_cache_report(struct net *net, struct sk_buff *pkt, mifi_t mifi, static int ip6mr_cache_unresolved(struct net *net, mifi_t mifi, struct sk_buff *skb) { + bool found = false; int err; struct mfc6_cache *c; spin_lock_bh(&mfc_unres_lock); - for (c = net->ipv6.mfc6_unres_queue; c; c = c->next) { + list_for_each_entry(c, &net->ipv6.mfc6_unres_queue, list) { if (ipv6_addr_equal(&c->mf6c_mcastgrp, &ipv6_hdr(skb)->daddr) && - ipv6_addr_equal(&c->mf6c_origin, &ipv6_hdr(skb)->saddr)) + ipv6_addr_equal(&c->mf6c_origin, &ipv6_hdr(skb)->saddr)) { + found = true; break; + } } - if (c == NULL) { + if (!found) { /* * Create a new entry if allowable */ @@ -918,8 +915,7 @@ ip6mr_cache_unresolved(struct net *net, mifi_t mifi, struct sk_buff *skb) } atomic_inc(&net->ipv6.cache_resolve_queue_len); - c->next = net->ipv6.mfc6_unres_queue; - net->ipv6.mfc6_unres_queue = c; + list_add(&c->list, &net->ipv6.mfc6_unres_queue); ipmr_do_expire_process(net); } @@ -946,16 +942,15 @@ ip6mr_cache_unresolved(struct net *net, mifi_t mifi, struct sk_buff *skb) static int ip6mr_mfc_delete(struct net *net, struct mf6cctl *mfc) { int line; - struct mfc6_cache *c, **cp; + struct mfc6_cache *c, *next; line = MFC6_HASH(&mfc->mf6cc_mcastgrp.sin6_addr, &mfc->mf6cc_origin.sin6_addr); - for (cp = &net->ipv6.mfc6_cache_array[line]; - (c = *cp) != NULL; cp = &c->next) { + list_for_each_entry_safe(c, next, &net->ipv6.mfc6_cache_array[line], list) { if (ipv6_addr_equal(&c->mf6c_origin, &mfc->mf6cc_origin.sin6_addr) && ipv6_addr_equal(&c->mf6c_mcastgrp, &mfc->mf6cc_mcastgrp.sin6_addr)) { write_lock_bh(&mrt_lock); - *cp = c->next; + list_del(&c->list); write_unlock_bh(&mrt_lock); ip6mr_cache_free(c); @@ -997,7 +992,9 @@ static struct notifier_block ip6_mr_notifier = { static int __net_init ip6mr_net_init(struct net *net) { + unsigned int i; int err = 0; + net->ipv6.vif6_table = kcalloc(MAXMIFS, sizeof(struct mif_device), GFP_KERNEL); if (!net->ipv6.vif6_table) { @@ -1007,13 +1004,18 @@ static int __net_init ip6mr_net_init(struct net *net) /* Forwarding cache */ net->ipv6.mfc6_cache_array = kcalloc(MFC6_LINES, - sizeof(struct mfc6_cache *), + sizeof(struct list_head), GFP_KERNEL); if (!net->ipv6.mfc6_cache_array) { err = -ENOMEM; goto fail_mfc6_cache; } + for (i = 0; i < MFC6_LINES; i++) + INIT_LIST_HEAD(&net->ipv6.mfc6_cache_array[i]); + + INIT_LIST_HEAD(&net->ipv6.mfc6_unres_queue); + setup_timer(&net->ipv6.ipmr_expire_timer, ipmr_expire_process, (unsigned long)net); @@ -1105,8 +1107,9 @@ void ip6_mr_cleanup(void) static int ip6mr_mfc_add(struct net *net, struct mf6cctl *mfc, int mrtsock) { + bool found = false; int line; - struct mfc6_cache *uc, *c, **cp; + struct mfc6_cache *uc, *c; unsigned char ttls[MAXMIFS]; int i; @@ -1122,14 +1125,15 @@ static int ip6mr_mfc_add(struct net *net, struct mf6cctl *mfc, int mrtsock) line = MFC6_HASH(&mfc->mf6cc_mcastgrp.sin6_addr, &mfc->mf6cc_origin.sin6_addr); - for (cp = &net->ipv6.mfc6_cache_array[line]; - (c = *cp) != NULL; cp = &c->next) { + list_for_each_entry(c, &net->ipv6.mfc6_cache_array[line], list) { if (ipv6_addr_equal(&c->mf6c_origin, &mfc->mf6cc_origin.sin6_addr) && - ipv6_addr_equal(&c->mf6c_mcastgrp, &mfc->mf6cc_mcastgrp.sin6_addr)) + ipv6_addr_equal(&c->mf6c_mcastgrp, &mfc->mf6cc_mcastgrp.sin6_addr)) { + found = true; break; + } } - if (c != NULL) { + if (found) { write_lock_bh(&mrt_lock); c->mf6c_parent = mfc->mf6cc_parent; ip6mr_update_thresholds(net, c, ttls); @@ -1154,29 +1158,29 @@ static int ip6mr_mfc_add(struct net *net, struct mf6cctl *mfc, int mrtsock) c->mfc_flags |= MFC_STATIC; write_lock_bh(&mrt_lock); - c->next = net->ipv6.mfc6_cache_array[line]; - net->ipv6.mfc6_cache_array[line] = c; + list_add(&c->list, &net->ipv6.mfc6_cache_array[line]); write_unlock_bh(&mrt_lock); /* * Check to see if we resolved a queued list. If so we * need to send on the frames and tidy up. */ + found = false; spin_lock_bh(&mfc_unres_lock); - for (cp = &net->ipv6.mfc6_unres_queue; (uc = *cp) != NULL; - cp = &uc->next) { + list_for_each_entry(uc, &net->ipv6.mfc6_unres_queue, list) { if (ipv6_addr_equal(&uc->mf6c_origin, &c->mf6c_origin) && ipv6_addr_equal(&uc->mf6c_mcastgrp, &c->mf6c_mcastgrp)) { - *cp = uc->next; + list_del(&uc->list); atomic_dec(&net->ipv6.cache_resolve_queue_len); + found = true; break; } } - if (net->ipv6.mfc6_unres_queue == NULL) + if (list_empty(&net->ipv6.mfc6_unres_queue)) del_timer(&net->ipv6.ipmr_expire_timer); spin_unlock_bh(&mfc_unres_lock); - if (uc) { + if (found) { ip6mr_cache_resolve(net, uc, c); ip6mr_cache_free(uc); } @@ -1191,6 +1195,7 @@ static void mroute_clean_tables(struct net *net) { int i; LIST_HEAD(list); + struct mfc6_cache *c, *next; /* * Shut down all active vif entries @@ -1205,16 +1210,11 @@ static void mroute_clean_tables(struct net *net) * Wipe the cache */ for (i = 0; i < MFC6_LINES; i++) { - struct mfc6_cache *c, **cp; - - cp = &net->ipv6.mfc6_cache_array[i]; - while ((c = *cp) != NULL) { - if (c->mfc_flags & MFC_STATIC) { - cp = &c->next; + list_for_each_entry_safe(c, next, &net->ipv6.mfc6_cache_array[i], list) { + if (c->mfc_flags & MFC_STATIC) continue; - } write_lock_bh(&mrt_lock); - *cp = c->next; + list_del(&c->list); write_unlock_bh(&mrt_lock); ip6mr_cache_free(c); @@ -1222,12 +1222,9 @@ static void mroute_clean_tables(struct net *net) } if (atomic_read(&net->ipv6.cache_resolve_queue_len) != 0) { - struct mfc6_cache *c, **cp; - spin_lock_bh(&mfc_unres_lock); - cp = &net->ipv6.mfc6_unres_queue; - while ((c = *cp) != NULL) { - *cp = c->next; + list_for_each_entry_safe(c, next, &net->ipv6.mfc6_unres_queue, list) { + list_del(&c->list); ip6mr_destroy_unres(net, c); } spin_unlock_bh(&mfc_unres_lock); -- cgit v1.2.3 From 6bd521433942d85e80f7a731a88cc91a327f38e0 Mon Sep 17 00:00:00 2001 From: Patrick McHardy Date: Tue, 11 May 2010 14:40:53 +0200 Subject: ipv6: ip6mr: move mroute data into seperate structure Signed-off-by: Patrick McHardy --- include/linux/mroute6.h | 5 +- include/net/netns/ipv6.h | 13 +- net/ipv6/ip6mr.c | 390 ++++++++++++++++++++++++++--------------------- 3 files changed, 216 insertions(+), 192 deletions(-) (limited to 'include/linux') diff --git a/include/linux/mroute6.h b/include/linux/mroute6.h index 94a0cb521ca..0370dd4f238 100644 --- a/include/linux/mroute6.h +++ b/include/linux/mroute6.h @@ -229,10 +229,7 @@ extern int ip6mr_get_route(struct net *net, struct sk_buff *skb, struct rtmsg *rtm, int nowait); #ifdef CONFIG_IPV6_MROUTE -static inline struct sock *mroute6_socket(struct net *net) -{ - return net->ipv6.mroute6_sk; -} +extern struct sock *mroute6_socket(struct net *net); extern int ip6mr_sk_done(struct sock *sk); #else static inline struct sock *mroute6_socket(struct net *net) { return NULL; } diff --git a/include/net/netns/ipv6.h b/include/net/netns/ipv6.h index 9cb3b5f2cd7..4e2780e6d8b 100644 --- a/include/net/netns/ipv6.h +++ b/include/net/netns/ipv6.h @@ -59,18 +59,7 @@ struct netns_ipv6 { struct sock *tcp_sk; struct sock *igmp_sk; #ifdef CONFIG_IPV6_MROUTE - struct sock *mroute6_sk; - struct timer_list ipmr_expire_timer; - struct list_head mfc6_unres_queue; - struct list_head *mfc6_cache_array; - struct mif_device *vif6_table; - int maxvif; - atomic_t cache_resolve_queue_len; - int mroute_do_assert; - int mroute_do_pim; -#ifdef CONFIG_IPV6_PIMSM_V2 - int mroute_reg_vif_num; -#endif + struct mr6_table *mrt6; #endif }; #endif diff --git a/net/ipv6/ip6mr.c b/net/ipv6/ip6mr.c index 08e09042ad1..9419fceeed4 100644 --- a/net/ipv6/ip6mr.c +++ b/net/ipv6/ip6mr.c @@ -51,6 +51,24 @@ #include #include +struct mr6_table { +#ifdef CONFIG_NET_NS + struct net *net; +#endif + struct sock *mroute6_sk; + struct timer_list ipmr_expire_timer; + struct list_head mfc6_unres_queue; + struct list_head mfc6_cache_array[MFC6_LINES]; + struct mif_device vif6_table[MAXMIFS]; + int maxvif; + atomic_t cache_resolve_queue_len; + int mroute_do_assert; + int mroute_do_pim; +#ifdef CONFIG_IPV6_PIMSM_V2 + int mroute_reg_vif_num; +#endif +}; + /* Big lock, protecting vif table, mrt cache and mroute socket state. Note that the changes are semaphored via rtnl_lock. */ @@ -61,7 +79,7 @@ static DEFINE_RWLOCK(mrt_lock); * Multicast router control variables */ -#define MIF_EXISTS(_net, _idx) ((_net)->ipv6.vif6_table[_idx].dev != NULL) +#define MIF_EXISTS(_mrt, _idx) ((_mrt)->vif6_table[_idx].dev != NULL) /* Special spinlock for queue of unresolved entries */ static DEFINE_SPINLOCK(mfc_unres_lock); @@ -76,13 +94,13 @@ static DEFINE_SPINLOCK(mfc_unres_lock); static struct kmem_cache *mrt_cachep __read_mostly; -static int ip6_mr_forward(struct net *net, struct sk_buff *skb, - struct mfc6_cache *cache); -static int ip6mr_cache_report(struct net *net, struct sk_buff *pkt, +static int ip6_mr_forward(struct net *net, struct mr6_table *mrt, + struct sk_buff *skb, struct mfc6_cache *cache); +static int ip6mr_cache_report(struct mr6_table *mrt, struct sk_buff *pkt, mifi_t mifi, int assert); -static int ip6mr_fill_mroute(struct net *net, struct sk_buff *skb, +static int ip6mr_fill_mroute(struct mr6_table *mrt, struct sk_buff *skb, struct mfc6_cache *c, struct rtmsg *rtm); -static void mroute_clean_tables(struct net *net); +static void mroute_clean_tables(struct mr6_table *mrt); #ifdef CONFIG_PROC_FS @@ -97,11 +115,12 @@ struct ipmr_mfc_iter { static struct mfc6_cache *ipmr_mfc_seq_idx(struct net *net, struct ipmr_mfc_iter *it, loff_t pos) { + struct mr6_table *mrt = net->ipv6.mrt6; struct mfc6_cache *mfc; read_lock(&mrt_lock); for (it->ct = 0; it->ct < MFC6_LINES; it->ct++) { - it->cache = &net->ipv6.mfc6_cache_array[it->ct]; + it->cache = &mrt->mfc6_cache_array[it->ct]; list_for_each_entry(mfc, it->cache, list) if (pos-- == 0) return mfc; @@ -109,7 +128,7 @@ static struct mfc6_cache *ipmr_mfc_seq_idx(struct net *net, read_unlock(&mrt_lock); spin_lock_bh(&mfc_unres_lock); - it->cache = &net->ipv6.mfc6_unres_queue; + it->cache = &mrt->mfc6_unres_queue; list_for_each_entry(mfc, it->cache, list) if (pos-- == 0) return mfc; @@ -132,11 +151,13 @@ static struct mif_device *ip6mr_vif_seq_idx(struct net *net, struct ipmr_vif_iter *iter, loff_t pos) { - for (iter->ct = 0; iter->ct < net->ipv6.maxvif; ++iter->ct) { - if (!MIF_EXISTS(net, iter->ct)) + struct mr6_table *mrt = net->ipv6.mrt6; + + for (iter->ct = 0; iter->ct < mrt->maxvif; ++iter->ct) { + if (!MIF_EXISTS(mrt, iter->ct)) continue; if (pos-- == 0) - return &net->ipv6.vif6_table[iter->ct]; + return &mrt->vif6_table[iter->ct]; } return NULL; } @@ -155,15 +176,16 @@ static void *ip6mr_vif_seq_next(struct seq_file *seq, void *v, loff_t *pos) { struct ipmr_vif_iter *iter = seq->private; struct net *net = seq_file_net(seq); + struct mr6_table *mrt = net->ipv6.mrt6; ++*pos; if (v == SEQ_START_TOKEN) return ip6mr_vif_seq_idx(net, iter, 0); - while (++iter->ct < net->ipv6.maxvif) { - if (!MIF_EXISTS(net, iter->ct)) + while (++iter->ct < mrt->maxvif) { + if (!MIF_EXISTS(mrt, iter->ct)) continue; - return &net->ipv6.vif6_table[iter->ct]; + return &mrt->vif6_table[iter->ct]; } return NULL; } @@ -177,6 +199,7 @@ static void ip6mr_vif_seq_stop(struct seq_file *seq, void *v) static int ip6mr_vif_seq_show(struct seq_file *seq, void *v) { struct net *net = seq_file_net(seq); + struct mr6_table *mrt = net->ipv6.mrt6; if (v == SEQ_START_TOKEN) { seq_puts(seq, @@ -187,7 +210,7 @@ static int ip6mr_vif_seq_show(struct seq_file *seq, void *v) seq_printf(seq, "%2td %-10s %8ld %7ld %8ld %7ld %05X\n", - vif - net->ipv6.vif6_table, + vif - mrt->vif6_table, name, vif->bytes_in, vif->pkt_in, vif->bytes_out, vif->pkt_out, vif->flags); @@ -229,6 +252,7 @@ static void *ipmr_mfc_seq_next(struct seq_file *seq, void *v, loff_t *pos) struct mfc6_cache *mfc = v; struct ipmr_mfc_iter *it = seq->private; struct net *net = seq_file_net(seq); + struct mr6_table *mrt = net->ipv6.mrt6; ++*pos; @@ -238,13 +262,13 @@ static void *ipmr_mfc_seq_next(struct seq_file *seq, void *v, loff_t *pos) if (mfc->list.next != it->cache) return list_entry(mfc->list.next, struct mfc6_cache, list); - if (it->cache == &net->ipv6.mfc6_unres_queue) + if (it->cache == &mrt->mfc6_unres_queue) goto end_of_list; - BUG_ON(it->cache != &net->ipv6.mfc6_cache_array[it->ct]); + BUG_ON(it->cache != &mrt->mfc6_cache_array[it->ct]); while (++it->ct < MFC6_LINES) { - it->cache = &net->ipv6.mfc6_cache_array[it->ct]; + it->cache = &mrt->mfc6_cache_array[it->ct]; if (list_empty(it->cache)) continue; return list_first_entry(it->cache, struct mfc6_cache, list); @@ -252,7 +276,7 @@ static void *ipmr_mfc_seq_next(struct seq_file *seq, void *v, loff_t *pos) /* exhausted cache_array, show unresolved */ read_unlock(&mrt_lock); - it->cache = &net->ipv6.mfc6_unres_queue; + it->cache = &mrt->mfc6_unres_queue; it->ct = 0; spin_lock_bh(&mfc_unres_lock); @@ -270,10 +294,11 @@ static void ipmr_mfc_seq_stop(struct seq_file *seq, void *v) { struct ipmr_mfc_iter *it = seq->private; struct net *net = seq_file_net(seq); + struct mr6_table *mrt = net->ipv6.mrt6; - if (it->cache == &net->ipv6.mfc6_unres_queue) + if (it->cache == &mrt->mfc6_unres_queue) spin_unlock_bh(&mfc_unres_lock); - else if (it->cache == net->ipv6.mfc6_cache_array) + else if (it->cache == mrt->mfc6_cache_array) read_unlock(&mrt_lock); } @@ -281,6 +306,7 @@ static int ipmr_mfc_seq_show(struct seq_file *seq, void *v) { int n; struct net *net = seq_file_net(seq); + struct mr6_table *mrt = net->ipv6.mrt6; if (v == SEQ_START_TOKEN) { seq_puts(seq, @@ -295,14 +321,14 @@ static int ipmr_mfc_seq_show(struct seq_file *seq, void *v) &mfc->mf6c_mcastgrp, &mfc->mf6c_origin, mfc->mf6c_parent); - if (it->cache != &net->ipv6.mfc6_unres_queue) { + if (it->cache != &mrt->mfc6_unres_queue) { seq_printf(seq, " %8lu %8lu %8lu", mfc->mfc_un.res.pkt, mfc->mfc_un.res.bytes, mfc->mfc_un.res.wrong_if); for (n = mfc->mfc_un.res.minvif; n < mfc->mfc_un.res.maxvif; n++) { - if (MIF_EXISTS(net, n) && + if (MIF_EXISTS(mrt, n) && mfc->mfc_un.res.ttls[n] < 255) seq_printf(seq, " %2d:%-3d", @@ -349,7 +375,8 @@ static int pim6_rcv(struct sk_buff *skb) struct ipv6hdr *encap; struct net_device *reg_dev = NULL; struct net *net = dev_net(skb->dev); - int reg_vif_num = net->ipv6.mroute_reg_vif_num; + struct mr6_table *mrt = net->ipv6.mrt6; + int reg_vif_num = mrt->mroute_reg_vif_num; if (!pskb_may_pull(skb, sizeof(*pim) + sizeof(*encap))) goto drop; @@ -374,7 +401,7 @@ static int pim6_rcv(struct sk_buff *skb) read_lock(&mrt_lock); if (reg_vif_num >= 0) - reg_dev = net->ipv6.vif6_table[reg_vif_num].dev; + reg_dev = mrt->vif6_table[reg_vif_num].dev; if (reg_dev) dev_hold(reg_dev); read_unlock(&mrt_lock); @@ -411,12 +438,12 @@ static netdev_tx_t reg_vif_xmit(struct sk_buff *skb, struct net_device *dev) { struct net *net = dev_net(dev); + struct mr6_table *mrt = net->ipv6.mrt6; read_lock(&mrt_lock); dev->stats.tx_bytes += skb->len; dev->stats.tx_packets++; - ip6mr_cache_report(net, skb, net->ipv6.mroute_reg_vif_num, - MRT6MSG_WHOLEPKT); + ip6mr_cache_report(mrt, skb, mrt->mroute_reg_vif_num, MRT6MSG_WHOLEPKT); read_unlock(&mrt_lock); kfree_skb(skb); return NETDEV_TX_OK; @@ -472,15 +499,16 @@ failure: * Delete a VIF entry */ -static int mif6_delete(struct net *net, int vifi, struct list_head *head) +static int mif6_delete(struct mr6_table *mrt, int vifi, struct list_head *head) { struct mif_device *v; struct net_device *dev; struct inet6_dev *in6_dev; - if (vifi < 0 || vifi >= net->ipv6.maxvif) + + if (vifi < 0 || vifi >= mrt->maxvif) return -EADDRNOTAVAIL; - v = &net->ipv6.vif6_table[vifi]; + v = &mrt->vif6_table[vifi]; write_lock_bh(&mrt_lock); dev = v->dev; @@ -492,17 +520,17 @@ static int mif6_delete(struct net *net, int vifi, struct list_head *head) } #ifdef CONFIG_IPV6_PIMSM_V2 - if (vifi == net->ipv6.mroute_reg_vif_num) - net->ipv6.mroute_reg_vif_num = -1; + if (vifi == mrt->mroute_reg_vif_num) + mrt->mroute_reg_vif_num = -1; #endif - if (vifi + 1 == net->ipv6.maxvif) { + if (vifi + 1 == mrt->maxvif) { int tmp; for (tmp = vifi - 1; tmp >= 0; tmp--) { - if (MIF_EXISTS(net, tmp)) + if (MIF_EXISTS(mrt, tmp)) break; } - net->ipv6.maxvif = tmp + 1; + mrt->maxvif = tmp + 1; } write_unlock_bh(&mrt_lock); @@ -529,11 +557,12 @@ static inline void ip6mr_cache_free(struct mfc6_cache *c) and reporting error to netlink readers. */ -static void ip6mr_destroy_unres(struct net *net, struct mfc6_cache *c) +static void ip6mr_destroy_unres(struct mr6_table *mrt, struct mfc6_cache *c) { + struct net *net = read_pnet(&mrt->net); struct sk_buff *skb; - atomic_dec(&net->ipv6.cache_resolve_queue_len); + atomic_dec(&mrt->cache_resolve_queue_len); while((skb = skb_dequeue(&c->mfc_un.unres.unresolved)) != NULL) { if (ipv6_hdr(skb)->version == 0) { @@ -553,13 +582,13 @@ static void ip6mr_destroy_unres(struct net *net, struct mfc6_cache *c) /* Timer process for all the unresolved queue. */ -static void ipmr_do_expire_process(struct net *net) +static void ipmr_do_expire_process(struct mr6_table *mrt) { unsigned long now = jiffies; unsigned long expires = 10 * HZ; struct mfc6_cache *c, *next; - list_for_each_entry_safe(c, next, &net->ipv6.mfc6_unres_queue, list) { + list_for_each_entry_safe(c, next, &mrt->mfc6_unres_queue, list) { if (time_after(c->mfc_un.unres.expires, now)) { /* not yet... */ unsigned long interval = c->mfc_un.unres.expires - now; @@ -569,31 +598,31 @@ static void ipmr_do_expire_process(struct net *net) } list_del(&c->list); - ip6mr_destroy_unres(net, c); + ip6mr_destroy_unres(mrt, c); } - if (!list_empty(&net->ipv6.mfc6_unres_queue)) - mod_timer(&net->ipv6.ipmr_expire_timer, jiffies + expires); + if (!list_empty(&mrt->mfc6_unres_queue)) + mod_timer(&mrt->ipmr_expire_timer, jiffies + expires); } static void ipmr_expire_process(unsigned long arg) { - struct net *net = (struct net *)arg; + struct mr6_table *mrt = (struct mr6_table *)arg; if (!spin_trylock(&mfc_unres_lock)) { - mod_timer(&net->ipv6.ipmr_expire_timer, jiffies + 1); + mod_timer(&mrt->ipmr_expire_timer, jiffies + 1); return; } - if (!list_empty(&net->ipv6.mfc6_unres_queue)) - ipmr_do_expire_process(net); + if (!list_empty(&mrt->mfc6_unres_queue)) + ipmr_do_expire_process(mrt); spin_unlock(&mfc_unres_lock); } /* Fill oifs list. It is called under write locked mrt_lock. */ -static void ip6mr_update_thresholds(struct net *net, struct mfc6_cache *cache, +static void ip6mr_update_thresholds(struct mr6_table *mrt, struct mfc6_cache *cache, unsigned char *ttls) { int vifi; @@ -602,8 +631,8 @@ static void ip6mr_update_thresholds(struct net *net, struct mfc6_cache *cache, cache->mfc_un.res.maxvif = 0; memset(cache->mfc_un.res.ttls, 255, MAXMIFS); - for (vifi = 0; vifi < net->ipv6.maxvif; vifi++) { - if (MIF_EXISTS(net, vifi) && + for (vifi = 0; vifi < mrt->maxvif; vifi++) { + if (MIF_EXISTS(mrt, vifi) && ttls[vifi] && ttls[vifi] < 255) { cache->mfc_un.res.ttls[vifi] = ttls[vifi]; if (cache->mfc_un.res.minvif > vifi) @@ -614,16 +643,17 @@ static void ip6mr_update_thresholds(struct net *net, struct mfc6_cache *cache, } } -static int mif6_add(struct net *net, struct mif6ctl *vifc, int mrtsock) +static int mif6_add(struct net *net, struct mr6_table *mrt, + struct mif6ctl *vifc, int mrtsock) { int vifi = vifc->mif6c_mifi; - struct mif_device *v = &net->ipv6.vif6_table[vifi]; + struct mif_device *v = &mrt->vif6_table[vifi]; struct net_device *dev; struct inet6_dev *in6_dev; int err; /* Is vif busy ? */ - if (MIF_EXISTS(net, vifi)) + if (MIF_EXISTS(mrt, vifi)) return -EADDRINUSE; switch (vifc->mif6c_flags) { @@ -633,7 +663,7 @@ static int mif6_add(struct net *net, struct mif6ctl *vifc, int mrtsock) * Special Purpose VIF in PIM * All the packets will be sent to the daemon */ - if (net->ipv6.mroute_reg_vif_num >= 0) + if (mrt->mroute_reg_vif_num >= 0) return -EADDRINUSE; dev = ip6mr_reg_vif(net); if (!dev) @@ -685,22 +715,22 @@ static int mif6_add(struct net *net, struct mif6ctl *vifc, int mrtsock) v->dev = dev; #ifdef CONFIG_IPV6_PIMSM_V2 if (v->flags & MIFF_REGISTER) - net->ipv6.mroute_reg_vif_num = vifi; + mrt->mroute_reg_vif_num = vifi; #endif - if (vifi + 1 > net->ipv6.maxvif) - net->ipv6.maxvif = vifi + 1; + if (vifi + 1 > mrt->maxvif) + mrt->maxvif = vifi + 1; write_unlock_bh(&mrt_lock); return 0; } -static struct mfc6_cache *ip6mr_cache_find(struct net *net, +static struct mfc6_cache *ip6mr_cache_find(struct mr6_table *mrt, struct in6_addr *origin, struct in6_addr *mcastgrp) { int line = MFC6_HASH(mcastgrp, origin); struct mfc6_cache *c; - list_for_each_entry(c, &net->ipv6.mfc6_cache_array[line], list) { + list_for_each_entry(c, &mrt->mfc6_cache_array[line], list) { if (ipv6_addr_equal(&c->mf6c_origin, origin) && ipv6_addr_equal(&c->mf6c_mcastgrp, mcastgrp)) return c; @@ -734,8 +764,8 @@ static struct mfc6_cache *ip6mr_cache_alloc_unres(void) * A cache entry has gone into a resolved state from queued */ -static void ip6mr_cache_resolve(struct net *net, struct mfc6_cache *uc, - struct mfc6_cache *c) +static void ip6mr_cache_resolve(struct net *net, struct mr6_table *mrt, + struct mfc6_cache *uc, struct mfc6_cache *c) { struct sk_buff *skb; @@ -748,7 +778,7 @@ static void ip6mr_cache_resolve(struct net *net, struct mfc6_cache *uc, int err; struct nlmsghdr *nlh = (struct nlmsghdr *)skb_pull(skb, sizeof(struct ipv6hdr)); - if (ip6mr_fill_mroute(net, skb, c, NLMSG_DATA(nlh)) > 0) { + if (ip6mr_fill_mroute(mrt, skb, c, NLMSG_DATA(nlh)) > 0) { nlh->nlmsg_len = skb_tail_pointer(skb) - (u8 *)nlh; } else { nlh->nlmsg_type = NLMSG_ERROR; @@ -758,7 +788,7 @@ static void ip6mr_cache_resolve(struct net *net, struct mfc6_cache *uc, } err = rtnl_unicast(skb, net, NETLINK_CB(skb).pid); } else - ip6_mr_forward(net, skb, c); + ip6_mr_forward(net, mrt, skb, c); } } @@ -769,8 +799,8 @@ static void ip6mr_cache_resolve(struct net *net, struct mfc6_cache *uc, * Called under mrt_lock. */ -static int ip6mr_cache_report(struct net *net, struct sk_buff *pkt, mifi_t mifi, - int assert) +static int ip6mr_cache_report(struct mr6_table *mrt, struct sk_buff *pkt, + mifi_t mifi, int assert) { struct sk_buff *skb; struct mrt6msg *msg; @@ -806,7 +836,7 @@ static int ip6mr_cache_report(struct net *net, struct sk_buff *pkt, mifi_t mifi, msg = (struct mrt6msg *)skb_transport_header(skb); msg->im6_mbz = 0; msg->im6_msgtype = MRT6MSG_WHOLEPKT; - msg->im6_mif = net->ipv6.mroute_reg_vif_num; + msg->im6_mif = mrt->mroute_reg_vif_num; msg->im6_pad = 0; ipv6_addr_copy(&msg->im6_src, &ipv6_hdr(pkt)->saddr); ipv6_addr_copy(&msg->im6_dst, &ipv6_hdr(pkt)->daddr); @@ -841,7 +871,7 @@ static int ip6mr_cache_report(struct net *net, struct sk_buff *pkt, mifi_t mifi, skb->ip_summed = CHECKSUM_UNNECESSARY; } - if (net->ipv6.mroute6_sk == NULL) { + if (mrt->mroute6_sk == NULL) { kfree_skb(skb); return -EINVAL; } @@ -849,7 +879,7 @@ static int ip6mr_cache_report(struct net *net, struct sk_buff *pkt, mifi_t mifi, /* * Deliver to user space multicast routing algorithms */ - ret = sock_queue_rcv_skb(net->ipv6.mroute6_sk, skb); + ret = sock_queue_rcv_skb(mrt->mroute6_sk, skb); if (ret < 0) { if (net_ratelimit()) printk(KERN_WARNING "mroute6: pending queue full, dropping entries.\n"); @@ -864,14 +894,14 @@ static int ip6mr_cache_report(struct net *net, struct sk_buff *pkt, mifi_t mifi, */ static int -ip6mr_cache_unresolved(struct net *net, mifi_t mifi, struct sk_buff *skb) +ip6mr_cache_unresolved(struct mr6_table *mrt, mifi_t mifi, struct sk_buff *skb) { bool found = false; int err; struct mfc6_cache *c; spin_lock_bh(&mfc_unres_lock); - list_for_each_entry(c, &net->ipv6.mfc6_unres_queue, list) { + list_for_each_entry(c, &mrt->mfc6_unres_queue, list) { if (ipv6_addr_equal(&c->mf6c_mcastgrp, &ipv6_hdr(skb)->daddr) && ipv6_addr_equal(&c->mf6c_origin, &ipv6_hdr(skb)->saddr)) { found = true; @@ -884,7 +914,7 @@ ip6mr_cache_unresolved(struct net *net, mifi_t mifi, struct sk_buff *skb) * Create a new entry if allowable */ - if (atomic_read(&net->ipv6.cache_resolve_queue_len) >= 10 || + if (atomic_read(&mrt->cache_resolve_queue_len) >= 10 || (c = ip6mr_cache_alloc_unres()) == NULL) { spin_unlock_bh(&mfc_unres_lock); @@ -902,7 +932,7 @@ ip6mr_cache_unresolved(struct net *net, mifi_t mifi, struct sk_buff *skb) /* * Reflect first query at pim6sd */ - err = ip6mr_cache_report(net, skb, mifi, MRT6MSG_NOCACHE); + err = ip6mr_cache_report(mrt, skb, mifi, MRT6MSG_NOCACHE); if (err < 0) { /* If the report failed throw the cache entry out - Brad Parker @@ -914,10 +944,10 @@ ip6mr_cache_unresolved(struct net *net, mifi_t mifi, struct sk_buff *skb) return err; } - atomic_inc(&net->ipv6.cache_resolve_queue_len); - list_add(&c->list, &net->ipv6.mfc6_unres_queue); + atomic_inc(&mrt->cache_resolve_queue_len); + list_add(&c->list, &mrt->mfc6_unres_queue); - ipmr_do_expire_process(net); + ipmr_do_expire_process(mrt); } /* @@ -939,14 +969,14 @@ ip6mr_cache_unresolved(struct net *net, mifi_t mifi, struct sk_buff *skb) * MFC6 cache manipulation by user space */ -static int ip6mr_mfc_delete(struct net *net, struct mf6cctl *mfc) +static int ip6mr_mfc_delete(struct mr6_table *mrt, struct mf6cctl *mfc) { int line; struct mfc6_cache *c, *next; line = MFC6_HASH(&mfc->mf6cc_mcastgrp.sin6_addr, &mfc->mf6cc_origin.sin6_addr); - list_for_each_entry_safe(c, next, &net->ipv6.mfc6_cache_array[line], list) { + list_for_each_entry_safe(c, next, &mrt->mfc6_cache_array[line], list) { if (ipv6_addr_equal(&c->mf6c_origin, &mfc->mf6cc_origin.sin6_addr) && ipv6_addr_equal(&c->mf6c_mcastgrp, &mfc->mf6cc_mcastgrp.sin6_addr)) { write_lock_bh(&mrt_lock); @@ -965,6 +995,7 @@ static int ip6mr_device_event(struct notifier_block *this, { struct net_device *dev = ptr; struct net *net = dev_net(dev); + struct mr6_table *mrt = net->ipv6.mrt6; struct mif_device *v; int ct; LIST_HEAD(list); @@ -972,10 +1003,10 @@ static int ip6mr_device_event(struct notifier_block *this, if (event != NETDEV_UNREGISTER) return NOTIFY_DONE; - v = &net->ipv6.vif6_table[0]; - for (ct = 0; ct < net->ipv6.maxvif; ct++, v++) { + v = &mrt->vif6_table[0]; + for (ct = 0; ct < mrt->maxvif; ct++, v++) { if (v->dev == dev) - mif6_delete(net, ct, &list); + mif6_delete(mrt, ct, &list); } unregister_netdevice_many(&list); @@ -992,35 +1023,28 @@ static struct notifier_block ip6_mr_notifier = { static int __net_init ip6mr_net_init(struct net *net) { + struct mr6_table *mrt; unsigned int i; int err = 0; - net->ipv6.vif6_table = kcalloc(MAXMIFS, sizeof(struct mif_device), - GFP_KERNEL); - if (!net->ipv6.vif6_table) { + mrt = kzalloc(sizeof(*mrt), GFP_KERNEL); + if (mrt == NULL) { err = -ENOMEM; goto fail; } - /* Forwarding cache */ - net->ipv6.mfc6_cache_array = kcalloc(MFC6_LINES, - sizeof(struct list_head), - GFP_KERNEL); - if (!net->ipv6.mfc6_cache_array) { - err = -ENOMEM; - goto fail_mfc6_cache; - } + write_pnet(&mrt->net, net); for (i = 0; i < MFC6_LINES; i++) - INIT_LIST_HEAD(&net->ipv6.mfc6_cache_array[i]); + INIT_LIST_HEAD(&mrt->mfc6_cache_array[i]); - INIT_LIST_HEAD(&net->ipv6.mfc6_unres_queue); + INIT_LIST_HEAD(&mrt->mfc6_unres_queue); - setup_timer(&net->ipv6.ipmr_expire_timer, ipmr_expire_process, - (unsigned long)net); + setup_timer(&mrt->ipmr_expire_timer, ipmr_expire_process, + (unsigned long)mrt); #ifdef CONFIG_IPV6_PIMSM_V2 - net->ipv6.mroute_reg_vif_num = -1; + mrt->mroute_reg_vif_num = -1; #endif #ifdef CONFIG_PROC_FS @@ -1030,30 +1054,31 @@ static int __net_init ip6mr_net_init(struct net *net) if (!proc_net_fops_create(net, "ip6_mr_cache", 0, &ip6mr_mfc_fops)) goto proc_cache_fail; #endif + + net->ipv6.mrt6 = mrt; return 0; #ifdef CONFIG_PROC_FS proc_cache_fail: proc_net_remove(net, "ip6_mr_vif"); proc_vif_fail: - kfree(net->ipv6.mfc6_cache_array); + kfree(mrt); #endif -fail_mfc6_cache: - kfree(net->ipv6.vif6_table); fail: return err; } static void __net_exit ip6mr_net_exit(struct net *net) { + struct mr6_table *mrt = net->ipv6.mrt6; + #ifdef CONFIG_PROC_FS proc_net_remove(net, "ip6_mr_cache"); proc_net_remove(net, "ip6_mr_vif"); #endif - del_timer(&net->ipv6.ipmr_expire_timer); - mroute_clean_tables(net); - kfree(net->ipv6.mfc6_cache_array); - kfree(net->ipv6.vif6_table); + del_timer(&mrt->ipmr_expire_timer); + mroute_clean_tables(mrt); + kfree(mrt); } static struct pernet_operations ip6mr_net_ops = { @@ -1105,7 +1130,8 @@ void ip6_mr_cleanup(void) kmem_cache_destroy(mrt_cachep); } -static int ip6mr_mfc_add(struct net *net, struct mf6cctl *mfc, int mrtsock) +static int ip6mr_mfc_add(struct net *net, struct mr6_table *mrt, + struct mf6cctl *mfc, int mrtsock) { bool found = false; int line; @@ -1125,7 +1151,7 @@ static int ip6mr_mfc_add(struct net *net, struct mf6cctl *mfc, int mrtsock) line = MFC6_HASH(&mfc->mf6cc_mcastgrp.sin6_addr, &mfc->mf6cc_origin.sin6_addr); - list_for_each_entry(c, &net->ipv6.mfc6_cache_array[line], list) { + list_for_each_entry(c, &mrt->mfc6_cache_array[line], list) { if (ipv6_addr_equal(&c->mf6c_origin, &mfc->mf6cc_origin.sin6_addr) && ipv6_addr_equal(&c->mf6c_mcastgrp, &mfc->mf6cc_mcastgrp.sin6_addr)) { found = true; @@ -1136,7 +1162,7 @@ static int ip6mr_mfc_add(struct net *net, struct mf6cctl *mfc, int mrtsock) if (found) { write_lock_bh(&mrt_lock); c->mf6c_parent = mfc->mf6cc_parent; - ip6mr_update_thresholds(net, c, ttls); + ip6mr_update_thresholds(mrt, c, ttls); if (!mrtsock) c->mfc_flags |= MFC_STATIC; write_unlock_bh(&mrt_lock); @@ -1153,12 +1179,12 @@ static int ip6mr_mfc_add(struct net *net, struct mf6cctl *mfc, int mrtsock) c->mf6c_origin = mfc->mf6cc_origin.sin6_addr; c->mf6c_mcastgrp = mfc->mf6cc_mcastgrp.sin6_addr; c->mf6c_parent = mfc->mf6cc_parent; - ip6mr_update_thresholds(net, c, ttls); + ip6mr_update_thresholds(mrt, c, ttls); if (!mrtsock) c->mfc_flags |= MFC_STATIC; write_lock_bh(&mrt_lock); - list_add(&c->list, &net->ipv6.mfc6_cache_array[line]); + list_add(&c->list, &mrt->mfc6_cache_array[line]); write_unlock_bh(&mrt_lock); /* @@ -1167,21 +1193,21 @@ static int ip6mr_mfc_add(struct net *net, struct mf6cctl *mfc, int mrtsock) */ found = false; spin_lock_bh(&mfc_unres_lock); - list_for_each_entry(uc, &net->ipv6.mfc6_unres_queue, list) { + list_for_each_entry(uc, &mrt->mfc6_unres_queue, list) { if (ipv6_addr_equal(&uc->mf6c_origin, &c->mf6c_origin) && ipv6_addr_equal(&uc->mf6c_mcastgrp, &c->mf6c_mcastgrp)) { list_del(&uc->list); - atomic_dec(&net->ipv6.cache_resolve_queue_len); + atomic_dec(&mrt->cache_resolve_queue_len); found = true; break; } } - if (list_empty(&net->ipv6.mfc6_unres_queue)) - del_timer(&net->ipv6.ipmr_expire_timer); + if (list_empty(&mrt->mfc6_unres_queue)) + del_timer(&mrt->ipmr_expire_timer); spin_unlock_bh(&mfc_unres_lock); if (found) { - ip6mr_cache_resolve(net, uc, c); + ip6mr_cache_resolve(net, mrt, uc, c); ip6mr_cache_free(uc); } return 0; @@ -1191,7 +1217,7 @@ static int ip6mr_mfc_add(struct net *net, struct mf6cctl *mfc, int mrtsock) * Close the multicast socket, and clear the vif tables etc */ -static void mroute_clean_tables(struct net *net) +static void mroute_clean_tables(struct mr6_table *mrt) { int i; LIST_HEAD(list); @@ -1200,9 +1226,9 @@ static void mroute_clean_tables(struct net *net) /* * Shut down all active vif entries */ - for (i = 0; i < net->ipv6.maxvif; i++) { - if (!(net->ipv6.vif6_table[i].flags & VIFF_STATIC)) - mif6_delete(net, i, &list); + for (i = 0; i < mrt->maxvif; i++) { + if (!(mrt->vif6_table[i].flags & VIFF_STATIC)) + mif6_delete(mrt, i, &list); } unregister_netdevice_many(&list); @@ -1210,7 +1236,7 @@ static void mroute_clean_tables(struct net *net) * Wipe the cache */ for (i = 0; i < MFC6_LINES; i++) { - list_for_each_entry_safe(c, next, &net->ipv6.mfc6_cache_array[i], list) { + list_for_each_entry_safe(c, next, &mrt->mfc6_cache_array[i], list) { if (c->mfc_flags & MFC_STATIC) continue; write_lock_bh(&mrt_lock); @@ -1221,25 +1247,25 @@ static void mroute_clean_tables(struct net *net) } } - if (atomic_read(&net->ipv6.cache_resolve_queue_len) != 0) { + if (atomic_read(&mrt->cache_resolve_queue_len) != 0) { spin_lock_bh(&mfc_unres_lock); - list_for_each_entry_safe(c, next, &net->ipv6.mfc6_unres_queue, list) { + list_for_each_entry_safe(c, next, &mrt->mfc6_unres_queue, list) { list_del(&c->list); - ip6mr_destroy_unres(net, c); + ip6mr_destroy_unres(mrt, c); } spin_unlock_bh(&mfc_unres_lock); } } -static int ip6mr_sk_init(struct sock *sk) +static int ip6mr_sk_init(struct mr6_table *mrt, struct sock *sk) { int err = 0; struct net *net = sock_net(sk); rtnl_lock(); write_lock_bh(&mrt_lock); - if (likely(net->ipv6.mroute6_sk == NULL)) { - net->ipv6.mroute6_sk = sk; + if (likely(mrt->mroute6_sk == NULL)) { + mrt->mroute6_sk = sk; net->ipv6.devconf_all->mc_forwarding++; } else @@ -1255,15 +1281,16 @@ int ip6mr_sk_done(struct sock *sk) { int err = 0; struct net *net = sock_net(sk); + struct mr6_table *mrt = net->ipv6.mrt6; rtnl_lock(); - if (sk == net->ipv6.mroute6_sk) { + if (sk == mrt->mroute6_sk) { write_lock_bh(&mrt_lock); - net->ipv6.mroute6_sk = NULL; + mrt->mroute6_sk = NULL; net->ipv6.devconf_all->mc_forwarding--; write_unlock_bh(&mrt_lock); - mroute_clean_tables(net); + mroute_clean_tables(mrt); } else err = -EACCES; rtnl_unlock(); @@ -1271,6 +1298,13 @@ int ip6mr_sk_done(struct sock *sk) return err; } +struct sock *mroute6_socket(struct net *net) +{ + struct mr6_table *mrt = net->ipv6.mrt6; + + return mrt->mroute6_sk; +} + /* * Socket options and virtual interface manipulation. The whole * virtual interface system is a complete heap, but unfortunately @@ -1285,9 +1319,10 @@ int ip6_mroute_setsockopt(struct sock *sk, int optname, char __user *optval, uns struct mf6cctl mfc; mifi_t mifi; struct net *net = sock_net(sk); + struct mr6_table *mrt = net->ipv6.mrt6; if (optname != MRT6_INIT) { - if (sk != net->ipv6.mroute6_sk && !capable(CAP_NET_ADMIN)) + if (sk != mrt->mroute6_sk && !capable(CAP_NET_ADMIN)) return -EACCES; } @@ -1299,7 +1334,7 @@ int ip6_mroute_setsockopt(struct sock *sk, int optname, char __user *optval, uns if (optlen < sizeof(int)) return -EINVAL; - return ip6mr_sk_init(sk); + return ip6mr_sk_init(mrt, sk); case MRT6_DONE: return ip6mr_sk_done(sk); @@ -1312,7 +1347,7 @@ int ip6_mroute_setsockopt(struct sock *sk, int optname, char __user *optval, uns if (vif.mif6c_mifi >= MAXMIFS) return -ENFILE; rtnl_lock(); - ret = mif6_add(net, &vif, sk == net->ipv6.mroute6_sk); + ret = mif6_add(net, mrt, &vif, sk == mrt->mroute6_sk); rtnl_unlock(); return ret; @@ -1322,7 +1357,7 @@ int ip6_mroute_setsockopt(struct sock *sk, int optname, char __user *optval, uns if (copy_from_user(&mifi, optval, sizeof(mifi_t))) return -EFAULT; rtnl_lock(); - ret = mif6_delete(net, mifi, NULL); + ret = mif6_delete(mrt, mifi, NULL); rtnl_unlock(); return ret; @@ -1338,10 +1373,9 @@ int ip6_mroute_setsockopt(struct sock *sk, int optname, char __user *optval, uns return -EFAULT; rtnl_lock(); if (optname == MRT6_DEL_MFC) - ret = ip6mr_mfc_delete(net, &mfc); + ret = ip6mr_mfc_delete(mrt, &mfc); else - ret = ip6mr_mfc_add(net, &mfc, - sk == net->ipv6.mroute6_sk); + ret = ip6mr_mfc_add(net, mrt, &mfc, sk == mrt->mroute6_sk); rtnl_unlock(); return ret; @@ -1353,7 +1387,7 @@ int ip6_mroute_setsockopt(struct sock *sk, int optname, char __user *optval, uns int v; if (get_user(v, (int __user *)optval)) return -EFAULT; - net->ipv6.mroute_do_assert = !!v; + mrt->mroute_do_assert = !!v; return 0; } @@ -1366,9 +1400,9 @@ int ip6_mroute_setsockopt(struct sock *sk, int optname, char __user *optval, uns v = !!v; rtnl_lock(); ret = 0; - if (v != net->ipv6.mroute_do_pim) { - net->ipv6.mroute_do_pim = v; - net->ipv6.mroute_do_assert = v; + if (v != mrt->mroute_do_pim) { + mrt->mroute_do_pim = v; + mrt->mroute_do_assert = v; } rtnl_unlock(); return ret; @@ -1394,6 +1428,7 @@ int ip6_mroute_getsockopt(struct sock *sk, int optname, char __user *optval, int olr; int val; struct net *net = sock_net(sk); + struct mr6_table *mrt = net->ipv6.mrt6; switch (optname) { case MRT6_VERSION: @@ -1401,11 +1436,11 @@ int ip6_mroute_getsockopt(struct sock *sk, int optname, char __user *optval, break; #ifdef CONFIG_IPV6_PIMSM_V2 case MRT6_PIM: - val = net->ipv6.mroute_do_pim; + val = mrt->mroute_do_pim; break; #endif case MRT6_ASSERT: - val = net->ipv6.mroute_do_assert; + val = mrt->mroute_do_assert; break; default: return -ENOPROTOOPT; @@ -1436,16 +1471,17 @@ int ip6mr_ioctl(struct sock *sk, int cmd, void __user *arg) struct mif_device *vif; struct mfc6_cache *c; struct net *net = sock_net(sk); + struct mr6_table *mrt = net->ipv6.mrt6; switch (cmd) { case SIOCGETMIFCNT_IN6: if (copy_from_user(&vr, arg, sizeof(vr))) return -EFAULT; - if (vr.mifi >= net->ipv6.maxvif) + if (vr.mifi >= mrt->maxvif) return -EINVAL; read_lock(&mrt_lock); - vif = &net->ipv6.vif6_table[vr.mifi]; - if (MIF_EXISTS(net, vr.mifi)) { + vif = &mrt->vif6_table[vr.mifi]; + if (MIF_EXISTS(mrt, vr.mifi)) { vr.icount = vif->pkt_in; vr.ocount = vif->pkt_out; vr.ibytes = vif->bytes_in; @@ -1463,7 +1499,7 @@ int ip6mr_ioctl(struct sock *sk, int cmd, void __user *arg) return -EFAULT; read_lock(&mrt_lock); - c = ip6mr_cache_find(net, &sr.src.sin6_addr, &sr.grp.sin6_addr); + c = ip6mr_cache_find(mrt, &sr.src.sin6_addr, &sr.grp.sin6_addr); if (c) { sr.pktcnt = c->mfc_un.res.pkt; sr.bytecnt = c->mfc_un.res.bytes; @@ -1493,11 +1529,11 @@ static inline int ip6mr_forward2_finish(struct sk_buff *skb) * Processing handlers for ip6mr_forward */ -static int ip6mr_forward2(struct net *net, struct sk_buff *skb, - struct mfc6_cache *c, int vifi) +static int ip6mr_forward2(struct net *net, struct mr6_table *mrt, + struct sk_buff *skb, struct mfc6_cache *c, int vifi) { struct ipv6hdr *ipv6h; - struct mif_device *vif = &net->ipv6.vif6_table[vifi]; + struct mif_device *vif = &mrt->vif6_table[vifi]; struct net_device *dev; struct dst_entry *dst; struct flowi fl; @@ -1511,7 +1547,7 @@ static int ip6mr_forward2(struct net *net, struct sk_buff *skb, vif->bytes_out += skb->len; vif->dev->stats.tx_bytes += skb->len; vif->dev->stats.tx_packets++; - ip6mr_cache_report(net, skb, vifi, MRT6MSG_WHOLEPKT); + ip6mr_cache_report(mrt, skb, vifi, MRT6MSG_WHOLEPKT); goto out_free; } #endif @@ -1566,19 +1602,19 @@ out_free: return 0; } -static int ip6mr_find_vif(struct net_device *dev) +static int ip6mr_find_vif(struct mr6_table *mrt, struct net_device *dev) { - struct net *net = dev_net(dev); int ct; - for (ct = net->ipv6.maxvif - 1; ct >= 0; ct--) { - if (net->ipv6.vif6_table[ct].dev == dev) + + for (ct = mrt->maxvif - 1; ct >= 0; ct--) { + if (mrt->vif6_table[ct].dev == dev) break; } return ct; } -static int ip6_mr_forward(struct net *net, struct sk_buff *skb, - struct mfc6_cache *cache) +static int ip6_mr_forward(struct net *net, struct mr6_table *mrt, + struct sk_buff *skb, struct mfc6_cache *cache) { int psend = -1; int vif, ct; @@ -1590,30 +1626,30 @@ static int ip6_mr_forward(struct net *net, struct sk_buff *skb, /* * Wrong interface: drop packet and (maybe) send PIM assert. */ - if (net->ipv6.vif6_table[vif].dev != skb->dev) { + if (mrt->vif6_table[vif].dev != skb->dev) { int true_vifi; cache->mfc_un.res.wrong_if++; - true_vifi = ip6mr_find_vif(skb->dev); + true_vifi = ip6mr_find_vif(mrt, skb->dev); - if (true_vifi >= 0 && net->ipv6.mroute_do_assert && + if (true_vifi >= 0 && mrt->mroute_do_assert && /* pimsm uses asserts, when switching from RPT to SPT, so that we cannot check that packet arrived on an oif. It is bad, but otherwise we would need to move pretty large chunk of pimd to kernel. Ough... --ANK */ - (net->ipv6.mroute_do_pim || + (mrt->mroute_do_pim || cache->mfc_un.res.ttls[true_vifi] < 255) && time_after(jiffies, cache->mfc_un.res.last_assert + MFC_ASSERT_THRESH)) { cache->mfc_un.res.last_assert = jiffies; - ip6mr_cache_report(net, skb, true_vifi, MRT6MSG_WRONGMIF); + ip6mr_cache_report(mrt, skb, true_vifi, MRT6MSG_WRONGMIF); } goto dont_forward; } - net->ipv6.vif6_table[vif].pkt_in++; - net->ipv6.vif6_table[vif].bytes_in += skb->len; + mrt->vif6_table[vif].pkt_in++; + mrt->vif6_table[vif].bytes_in += skb->len; /* * Forward the frame @@ -1623,13 +1659,13 @@ static int ip6_mr_forward(struct net *net, struct sk_buff *skb, if (psend != -1) { struct sk_buff *skb2 = skb_clone(skb, GFP_ATOMIC); if (skb2) - ip6mr_forward2(net, skb2, cache, psend); + ip6mr_forward2(net, mrt, skb2, cache, psend); } psend = ct; } } if (psend != -1) { - ip6mr_forward2(net, skb, cache, psend); + ip6mr_forward2(net, mrt, skb, cache, psend); return 0; } @@ -1647,9 +1683,10 @@ int ip6_mr_input(struct sk_buff *skb) { struct mfc6_cache *cache; struct net *net = dev_net(skb->dev); + struct mr6_table *mrt = net->ipv6.mrt6; read_lock(&mrt_lock); - cache = ip6mr_cache_find(net, + cache = ip6mr_cache_find(mrt, &ipv6_hdr(skb)->saddr, &ipv6_hdr(skb)->daddr); /* @@ -1658,9 +1695,9 @@ int ip6_mr_input(struct sk_buff *skb) if (cache == NULL) { int vif; - vif = ip6mr_find_vif(skb->dev); + vif = ip6mr_find_vif(mrt, skb->dev); if (vif >= 0) { - int err = ip6mr_cache_unresolved(net, vif, skb); + int err = ip6mr_cache_unresolved(mrt, vif, skb); read_unlock(&mrt_lock); return err; @@ -1670,7 +1707,7 @@ int ip6_mr_input(struct sk_buff *skb) return -ENODEV; } - ip6_mr_forward(net, skb, cache); + ip6_mr_forward(net, mrt, skb, cache); read_unlock(&mrt_lock); @@ -1679,8 +1716,8 @@ int ip6_mr_input(struct sk_buff *skb) static int -ip6mr_fill_mroute(struct net *net, struct sk_buff *skb, struct mfc6_cache *c, - struct rtmsg *rtm) +ip6mr_fill_mroute(struct mr6_table *mrt, struct sk_buff *skb, + struct mfc6_cache *c, struct rtmsg *rtm) { int ct; struct rtnexthop *nhp; @@ -1691,19 +1728,19 @@ ip6mr_fill_mroute(struct net *net, struct sk_buff *skb, struct mfc6_cache *c, if (c->mf6c_parent > MAXMIFS) return -ENOENT; - if (MIF_EXISTS(net, c->mf6c_parent)) - RTA_PUT(skb, RTA_IIF, 4, &net->ipv6.vif6_table[c->mf6c_parent].dev->ifindex); + if (MIF_EXISTS(mrt, c->mf6c_parent)) + RTA_PUT(skb, RTA_IIF, 4, &mrt->vif6_table[c->mf6c_parent].dev->ifindex); mp_head = (struct rtattr *)skb_put(skb, RTA_LENGTH(0)); for (ct = c->mfc_un.res.minvif; ct < c->mfc_un.res.maxvif; ct++) { - if (MIF_EXISTS(net, ct) && c->mfc_un.res.ttls[ct] < 255) { + if (MIF_EXISTS(mrt, ct) && c->mfc_un.res.ttls[ct] < 255) { if (skb_tailroom(skb) < RTA_ALIGN(RTA_ALIGN(sizeof(*nhp)) + 4)) goto rtattr_failure; nhp = (struct rtnexthop *)skb_put(skb, RTA_ALIGN(sizeof(*nhp))); nhp->rtnh_flags = 0; nhp->rtnh_hops = c->mfc_un.res.ttls[ct]; - nhp->rtnh_ifindex = net->ipv6.vif6_table[ct].dev->ifindex; + nhp->rtnh_ifindex = mrt->vif6_table[ct].dev->ifindex; nhp->rtnh_len = sizeof(*nhp); } } @@ -1721,11 +1758,12 @@ int ip6mr_get_route(struct net *net, struct sk_buff *skb, struct rtmsg *rtm, int nowait) { int err; + struct mr6_table *mrt = net->ipv6.mrt6; struct mfc6_cache *cache; struct rt6_info *rt = (struct rt6_info *)skb_dst(skb); read_lock(&mrt_lock); - cache = ip6mr_cache_find(net, &rt->rt6i_src.addr, &rt->rt6i_dst.addr); + cache = ip6mr_cache_find(mrt, &rt->rt6i_src.addr, &rt->rt6i_dst.addr); if (!cache) { struct sk_buff *skb2; @@ -1739,7 +1777,7 @@ int ip6mr_get_route(struct net *net, } dev = skb->dev; - if (dev == NULL || (vif = ip6mr_find_vif(dev)) < 0) { + if (dev == NULL || (vif = ip6mr_find_vif(mrt, dev)) < 0) { read_unlock(&mrt_lock); return -ENODEV; } @@ -1768,7 +1806,7 @@ int ip6mr_get_route(struct net *net, ipv6_addr_copy(&iph->saddr, &rt->rt6i_src.addr); ipv6_addr_copy(&iph->daddr, &rt->rt6i_dst.addr); - err = ip6mr_cache_unresolved(net, vif, skb2); + err = ip6mr_cache_unresolved(mrt, vif, skb2); read_unlock(&mrt_lock); return err; @@ -1777,7 +1815,7 @@ int ip6mr_get_route(struct net *net, if (!nowait && (rtm->rtm_flags&RTM_F_NOTIFY)) cache->mfc_flags |= MFC_NOTIFY; - err = ip6mr_fill_mroute(net, skb, cache, rtm); + err = ip6mr_fill_mroute(mrt, skb, cache, rtm); read_unlock(&mrt_lock); return err; } -- cgit v1.2.3 From d1db275dd3f6e4182c4c4b4a1ac6287925d60569 Mon Sep 17 00:00:00 2001 From: Patrick McHardy Date: Tue, 11 May 2010 14:40:55 +0200 Subject: ipv6: ip6mr: support multiple tables This patch adds support for multiple independant multicast routing instances, named "tables". Userspace multicast routing daemons can bind to a specific table instance by issuing a setsockopt call using a new option MRT6_TABLE. The table number is stored in the raw socket data and affects all following ip6mr setsockopt(), getsockopt() and ioctl() calls. By default, a single table (RT6_TABLE_DFLT) is created with a default routing rule pointing to it. Newly created pim6reg devices have the table number appended ("pim6regX"), with the exception of devices created in the default table, which are named just "pim6reg" for compatibility reasons. Packets are directed to a specific table instance using routing rules, similar to how regular routing rules work. Currently iif, oif and mark are supported as keys, source and destination addresses could be supported additionally. Example usage: - bind pimd/xorp/... to a specific table: uint32_t table = 123; setsockopt(fd, SOL_IPV6, MRT6_TABLE, &table, sizeof(table)); - create routing rules directing packets to the new table: # ip -6 mrule add iif eth0 lookup 123 # ip -6 mrule add oif eth0 lookup 123 Signed-off-by: Patrick McHardy --- include/linux/ipv6.h | 1 + include/linux/mroute6.h | 15 +- include/linux/rtnetlink.h | 3 +- include/net/netns/ipv6.h | 5 + net/ipv6/Kconfig | 14 ++ net/ipv6/ip6_output.c | 2 +- net/ipv6/ip6mr.c | 428 +++++++++++++++++++++++++++++++++++++++------- 7 files changed, 396 insertions(+), 72 deletions(-) (limited to 'include/linux') diff --git a/include/linux/ipv6.h b/include/linux/ipv6.h index 0e269038bb3..99e1ab7e3ee 100644 --- a/include/linux/ipv6.h +++ b/include/linux/ipv6.h @@ -383,6 +383,7 @@ struct raw6_sock { __u32 checksum; /* perform checksum */ __u32 offset; /* checksum offset */ struct icmp6_filter filter; + __u32 ip6mr_table; /* ipv6_pinfo has to be the last member of raw6_sock, see inet6_sk_generic */ struct ipv6_pinfo inet6; }; diff --git a/include/linux/mroute6.h b/include/linux/mroute6.h index 0370dd4f238..6091ab77f38 100644 --- a/include/linux/mroute6.h +++ b/include/linux/mroute6.h @@ -24,7 +24,8 @@ #define MRT6_DEL_MFC (MRT6_BASE+5) /* Delete a multicast forwarding entry */ #define MRT6_VERSION (MRT6_BASE+6) /* Get the kernel multicast version */ #define MRT6_ASSERT (MRT6_BASE+7) /* Activate PIM assert mode */ -#define MRT6_PIM (MRT6_BASE+8) /* enable PIM code */ +#define MRT6_PIM (MRT6_BASE+8) /* enable PIM code */ +#define MRT6_TABLE (MRT6_BASE+9) /* Specify mroute table ID */ #define SIOCGETMIFCNT_IN6 SIOCPROTOPRIVATE /* IP protocol privates */ #define SIOCGETSGCNT_IN6 (SIOCPROTOPRIVATE+1) @@ -229,11 +230,17 @@ extern int ip6mr_get_route(struct net *net, struct sk_buff *skb, struct rtmsg *rtm, int nowait); #ifdef CONFIG_IPV6_MROUTE -extern struct sock *mroute6_socket(struct net *net); +extern struct sock *mroute6_socket(struct net *net, struct sk_buff *skb); extern int ip6mr_sk_done(struct sock *sk); #else -static inline struct sock *mroute6_socket(struct net *net) { return NULL; } -static inline int ip6mr_sk_done(struct sock *sk) { return 0; } +static inline struct sock *mroute6_socket(struct net *net, struct sk_buff *skb) +{ + return NULL; +} +static inline int ip6mr_sk_done(struct sock *sk) +{ + return 0; +} #endif #endif diff --git a/include/linux/rtnetlink.h b/include/linux/rtnetlink.h index 5a42c36cb6a..fbc8cb0d48c 100644 --- a/include/linux/rtnetlink.h +++ b/include/linux/rtnetlink.h @@ -11,7 +11,8 @@ * families, values above 128 may be used arbitrarily. */ #define RTNL_FAMILY_IPMR 128 -#define RTNL_FAMILY_MAX 128 +#define RTNL_FAMILY_IP6MR 129 +#define RTNL_FAMILY_MAX 129 /**** * Routing/neighbour discovery messages. diff --git a/include/net/netns/ipv6.h b/include/net/netns/ipv6.h index 4e2780e6d8b..81abfcb2eb4 100644 --- a/include/net/netns/ipv6.h +++ b/include/net/netns/ipv6.h @@ -59,7 +59,12 @@ struct netns_ipv6 { struct sock *tcp_sk; struct sock *igmp_sk; #ifdef CONFIG_IPV6_MROUTE +#ifndef CONFIG_IPV6_MROUTE_MULTIPLE_TABLES struct mr6_table *mrt6; +#else + struct list_head mr6_tables; + struct fib_rules_ops *mr6_rules_ops; +#endif #endif }; #endif diff --git a/net/ipv6/Kconfig b/net/ipv6/Kconfig index a578096152a..36d7437ac05 100644 --- a/net/ipv6/Kconfig +++ b/net/ipv6/Kconfig @@ -229,6 +229,20 @@ config IPV6_MROUTE Experimental support for IPv6 multicast forwarding. If unsure, say N. +config IPV6_MROUTE_MULTIPLE_TABLES + bool "IPv6: multicast policy routing" + depends on IPV6_MROUTE + select FIB_RULES + help + Normally, a multicast router runs a userspace daemon and decides + what to do with a multicast packet based on the source and + destination addresses. If you say Y here, the multicast router + will also be able to take interfaces and packet marks into + account and run multiple instances of userspace daemons + simultaneously, each one handling a single table. + + If unsure, say N. + config IPV6_PIMSM_V2 bool "IPv6: PIM-SM version 2 support (EXPERIMENTAL)" depends on IPV6_MROUTE diff --git a/net/ipv6/ip6_output.c b/net/ipv6/ip6_output.c index 5173acaeb50..cd963f64e27 100644 --- a/net/ipv6/ip6_output.c +++ b/net/ipv6/ip6_output.c @@ -108,7 +108,7 @@ static int ip6_finish_output2(struct sk_buff *skb) struct inet6_dev *idev = ip6_dst_idev(skb_dst(skb)); if (!(dev->flags & IFF_LOOPBACK) && sk_mc_loop(skb->sk) && - ((mroute6_socket(dev_net(dev)) && + ((mroute6_socket(dev_net(dev), skb) && !(IP6CB(skb)->flags & IP6SKB_FORWARDED)) || ipv6_chk_mcast_addr(dev, &ipv6_hdr(skb)->daddr, &ipv6_hdr(skb)->saddr))) { diff --git a/net/ipv6/ip6mr.c b/net/ipv6/ip6mr.c index 9419fceeed4..c2920a1a6db 100644 --- a/net/ipv6/ip6mr.c +++ b/net/ipv6/ip6mr.c @@ -42,6 +42,7 @@ #include #include #include +#include #include #include @@ -52,9 +53,11 @@ #include struct mr6_table { + struct list_head list; #ifdef CONFIG_NET_NS struct net *net; #endif + u32 id; struct sock *mroute6_sk; struct timer_list ipmr_expire_timer; struct list_head mfc6_unres_queue; @@ -69,6 +72,14 @@ struct mr6_table { #endif }; +struct ip6mr_rule { + struct fib_rule common; +}; + +struct ip6mr_result { + struct mr6_table *mrt; +}; + /* Big lock, protecting vif table, mrt cache and mroute socket state. Note that the changes are semaphored via rtnl_lock. */ @@ -94,6 +105,9 @@ static DEFINE_SPINLOCK(mfc_unres_lock); static struct kmem_cache *mrt_cachep __read_mostly; +static struct mr6_table *ip6mr_new_table(struct net *net, u32 id); +static void ip6mr_free_table(struct mr6_table *mrt); + static int ip6_mr_forward(struct net *net, struct mr6_table *mrt, struct sk_buff *skb, struct mfc6_cache *cache); static int ip6mr_cache_report(struct mr6_table *mrt, struct sk_buff *pkt, @@ -101,12 +115,220 @@ static int ip6mr_cache_report(struct mr6_table *mrt, struct sk_buff *pkt, static int ip6mr_fill_mroute(struct mr6_table *mrt, struct sk_buff *skb, struct mfc6_cache *c, struct rtmsg *rtm); static void mroute_clean_tables(struct mr6_table *mrt); +static void ipmr_expire_process(unsigned long arg); + +#ifdef CONFIG_IPV6_MROUTE_MULTIPLE_TABLES +#define ip6mr_for_each_table(mrt, met) \ + list_for_each_entry_rcu(mrt, &net->ipv6.mr6_tables, list) + +static struct mr6_table *ip6mr_get_table(struct net *net, u32 id) +{ + struct mr6_table *mrt; + ip6mr_for_each_table(mrt, net) { + if (mrt->id == id) + return mrt; + } + return NULL; +} + +static int ip6mr_fib_lookup(struct net *net, struct flowi *flp, + struct mr6_table **mrt) +{ + struct ip6mr_result res; + struct fib_lookup_arg arg = { .result = &res, }; + int err; + + err = fib_rules_lookup(net->ipv6.mr6_rules_ops, flp, 0, &arg); + if (err < 0) + return err; + *mrt = res.mrt; + return 0; +} + +static int ip6mr_rule_action(struct fib_rule *rule, struct flowi *flp, + int flags, struct fib_lookup_arg *arg) +{ + struct ip6mr_result *res = arg->result; + struct mr6_table *mrt; + + switch (rule->action) { + case FR_ACT_TO_TBL: + break; + case FR_ACT_UNREACHABLE: + return -ENETUNREACH; + case FR_ACT_PROHIBIT: + return -EACCES; + case FR_ACT_BLACKHOLE: + default: + return -EINVAL; + } + + mrt = ip6mr_get_table(rule->fr_net, rule->table); + if (mrt == NULL) + return -EAGAIN; + res->mrt = mrt; + return 0; +} + +static int ip6mr_rule_match(struct fib_rule *rule, struct flowi *flp, int flags) +{ + return 1; +} + +static const struct nla_policy ip6mr_rule_policy[FRA_MAX + 1] = { + FRA_GENERIC_POLICY, +}; + +static int ip6mr_rule_configure(struct fib_rule *rule, struct sk_buff *skb, + struct fib_rule_hdr *frh, struct nlattr **tb) +{ + return 0; +} + +static int ip6mr_rule_compare(struct fib_rule *rule, struct fib_rule_hdr *frh, + struct nlattr **tb) +{ + return 1; +} + +static int ip6mr_rule_fill(struct fib_rule *rule, struct sk_buff *skb, + struct fib_rule_hdr *frh) +{ + frh->dst_len = 0; + frh->src_len = 0; + frh->tos = 0; + return 0; +} + +static const struct fib_rules_ops __net_initdata ip6mr_rules_ops_template = { + .family = RTNL_FAMILY_IP6MR, + .rule_size = sizeof(struct ip6mr_rule), + .addr_size = sizeof(struct in6_addr), + .action = ip6mr_rule_action, + .match = ip6mr_rule_match, + .configure = ip6mr_rule_configure, + .compare = ip6mr_rule_compare, + .default_pref = fib_default_rule_pref, + .fill = ip6mr_rule_fill, + .nlgroup = RTNLGRP_IPV6_RULE, + .policy = ip6mr_rule_policy, + .owner = THIS_MODULE, +}; + +static int __net_init ip6mr_rules_init(struct net *net) +{ + struct fib_rules_ops *ops; + struct mr6_table *mrt; + int err; + + ops = fib_rules_register(&ip6mr_rules_ops_template, net); + if (IS_ERR(ops)) + return PTR_ERR(ops); + + INIT_LIST_HEAD(&net->ipv6.mr6_tables); + + mrt = ip6mr_new_table(net, RT6_TABLE_DFLT); + if (mrt == NULL) { + err = -ENOMEM; + goto err1; + } + + err = fib_default_rule_add(ops, 0x7fff, RT6_TABLE_DFLT, 0); + if (err < 0) + goto err2; + + net->ipv6.mr6_rules_ops = ops; + return 0; + +err2: + kfree(mrt); +err1: + fib_rules_unregister(ops); + return err; +} + +static void __net_exit ip6mr_rules_exit(struct net *net) +{ + struct mr6_table *mrt, *next; + + list_for_each_entry_safe(mrt, next, &net->ipv6.mr6_tables, list) + ip6mr_free_table(mrt); + fib_rules_unregister(net->ipv6.mr6_rules_ops); +} +#else +#define ip6mr_for_each_table(mrt, net) \ + for (mrt = net->ipv6.mrt6; mrt; mrt = NULL) + +static struct mr6_table *ip6mr_get_table(struct net *net, u32 id) +{ + return net->ipv6.mrt6; +} + +static int ip6mr_fib_lookup(struct net *net, struct flowi *flp, + struct mr6_table **mrt) +{ + *mrt = net->ipv6.mrt6; + return 0; +} + +static int __net_init ip6mr_rules_init(struct net *net) +{ + net->ipv6.mrt6 = ip6mr_new_table(net, RT6_TABLE_DFLT); + return net->ipv6.mrt6 ? 0 : -ENOMEM; +} + +static void __net_exit ip6mr_rules_exit(struct net *net) +{ + ip6mr_free_table(net->ipv6.mrt6); +} +#endif + +static struct mr6_table *ip6mr_new_table(struct net *net, u32 id) +{ + struct mr6_table *mrt; + unsigned int i; + + mrt = ip6mr_get_table(net, id); + if (mrt != NULL) + return mrt; + + mrt = kzalloc(sizeof(*mrt), GFP_KERNEL); + if (mrt == NULL) + return NULL; + mrt->id = id; + write_pnet(&mrt->net, net); + + /* Forwarding cache */ + for (i = 0; i < MFC6_LINES; i++) + INIT_LIST_HEAD(&mrt->mfc6_cache_array[i]); + + INIT_LIST_HEAD(&mrt->mfc6_unres_queue); + + setup_timer(&mrt->ipmr_expire_timer, ipmr_expire_process, + (unsigned long)mrt); + +#ifdef CONFIG_IPV6_PIMSM_V2 + mrt->mroute_reg_vif_num = -1; +#endif +#ifdef CONFIG_IPV6_MROUTE_MULTIPLE_TABLES + list_add_tail_rcu(&mrt->list, &net->ipv6.mr6_tables); +#endif + return mrt; +} + +static void ip6mr_free_table(struct mr6_table *mrt) +{ + del_timer(&mrt->ipmr_expire_timer); + mroute_clean_tables(mrt); + kfree(mrt); +} #ifdef CONFIG_PROC_FS struct ipmr_mfc_iter { struct seq_net_private p; + struct mr6_table *mrt; struct list_head *cache; int ct; }; @@ -115,7 +337,7 @@ struct ipmr_mfc_iter { static struct mfc6_cache *ipmr_mfc_seq_idx(struct net *net, struct ipmr_mfc_iter *it, loff_t pos) { - struct mr6_table *mrt = net->ipv6.mrt6; + struct mr6_table *mrt = it->mrt; struct mfc6_cache *mfc; read_lock(&mrt_lock); @@ -144,6 +366,7 @@ static struct mfc6_cache *ipmr_mfc_seq_idx(struct net *net, struct ipmr_vif_iter { struct seq_net_private p; + struct mr6_table *mrt; int ct; }; @@ -151,7 +374,7 @@ static struct mif_device *ip6mr_vif_seq_idx(struct net *net, struct ipmr_vif_iter *iter, loff_t pos) { - struct mr6_table *mrt = net->ipv6.mrt6; + struct mr6_table *mrt = iter->mrt; for (iter->ct = 0; iter->ct < mrt->maxvif; ++iter->ct) { if (!MIF_EXISTS(mrt, iter->ct)) @@ -165,7 +388,15 @@ static struct mif_device *ip6mr_vif_seq_idx(struct net *net, static void *ip6mr_vif_seq_start(struct seq_file *seq, loff_t *pos) __acquires(mrt_lock) { + struct ipmr_vif_iter *iter = seq->private; struct net *net = seq_file_net(seq); + struct mr6_table *mrt; + + mrt = ip6mr_get_table(net, RT6_TABLE_DFLT); + if (mrt == NULL) + return ERR_PTR(-ENOENT); + + iter->mrt = mrt; read_lock(&mrt_lock); return *pos ? ip6mr_vif_seq_idx(net, seq->private, *pos - 1) @@ -176,7 +407,7 @@ static void *ip6mr_vif_seq_next(struct seq_file *seq, void *v, loff_t *pos) { struct ipmr_vif_iter *iter = seq->private; struct net *net = seq_file_net(seq); - struct mr6_table *mrt = net->ipv6.mrt6; + struct mr6_table *mrt = iter->mrt; ++*pos; if (v == SEQ_START_TOKEN) @@ -198,8 +429,8 @@ static void ip6mr_vif_seq_stop(struct seq_file *seq, void *v) static int ip6mr_vif_seq_show(struct seq_file *seq, void *v) { - struct net *net = seq_file_net(seq); - struct mr6_table *mrt = net->ipv6.mrt6; + struct ipmr_vif_iter *iter = seq->private; + struct mr6_table *mrt = iter->mrt; if (v == SEQ_START_TOKEN) { seq_puts(seq, @@ -241,8 +472,15 @@ static const struct file_operations ip6mr_vif_fops = { static void *ipmr_mfc_seq_start(struct seq_file *seq, loff_t *pos) { + struct ipmr_mfc_iter *it = seq->private; struct net *net = seq_file_net(seq); + struct mr6_table *mrt; + + mrt = ip6mr_get_table(net, RT6_TABLE_DFLT); + if (mrt == NULL) + return ERR_PTR(-ENOENT); + it->mrt = mrt; return *pos ? ipmr_mfc_seq_idx(net, seq->private, *pos - 1) : SEQ_START_TOKEN; } @@ -252,7 +490,7 @@ static void *ipmr_mfc_seq_next(struct seq_file *seq, void *v, loff_t *pos) struct mfc6_cache *mfc = v; struct ipmr_mfc_iter *it = seq->private; struct net *net = seq_file_net(seq); - struct mr6_table *mrt = net->ipv6.mrt6; + struct mr6_table *mrt = it->mrt; ++*pos; @@ -293,8 +531,7 @@ static void *ipmr_mfc_seq_next(struct seq_file *seq, void *v, loff_t *pos) static void ipmr_mfc_seq_stop(struct seq_file *seq, void *v) { struct ipmr_mfc_iter *it = seq->private; - struct net *net = seq_file_net(seq); - struct mr6_table *mrt = net->ipv6.mrt6; + struct mr6_table *mrt = it->mrt; if (it->cache == &mrt->mfc6_unres_queue) spin_unlock_bh(&mfc_unres_lock); @@ -305,8 +542,6 @@ static void ipmr_mfc_seq_stop(struct seq_file *seq, void *v) static int ipmr_mfc_seq_show(struct seq_file *seq, void *v) { int n; - struct net *net = seq_file_net(seq); - struct mr6_table *mrt = net->ipv6.mrt6; if (v == SEQ_START_TOKEN) { seq_puts(seq, @@ -316,6 +551,7 @@ static int ipmr_mfc_seq_show(struct seq_file *seq, void *v) } else { const struct mfc6_cache *mfc = v; const struct ipmr_mfc_iter *it = seq->private; + struct mr6_table *mrt = it->mrt; seq_printf(seq, "%pI6 %pI6 %-3hd", &mfc->mf6c_mcastgrp, &mfc->mf6c_origin, @@ -375,8 +611,12 @@ static int pim6_rcv(struct sk_buff *skb) struct ipv6hdr *encap; struct net_device *reg_dev = NULL; struct net *net = dev_net(skb->dev); - struct mr6_table *mrt = net->ipv6.mrt6; - int reg_vif_num = mrt->mroute_reg_vif_num; + struct mr6_table *mrt; + struct flowi fl = { + .iif = skb->dev->ifindex, + .mark = skb->mark, + }; + int reg_vif_num; if (!pskb_may_pull(skb, sizeof(*pim) + sizeof(*encap))) goto drop; @@ -399,6 +639,10 @@ static int pim6_rcv(struct sk_buff *skb) ntohs(encap->payload_len) + sizeof(*pim) > skb->len) goto drop; + if (ip6mr_fib_lookup(net, &fl, &mrt) < 0) + goto drop; + reg_vif_num = mrt->mroute_reg_vif_num; + read_lock(&mrt_lock); if (reg_vif_num >= 0) reg_dev = mrt->vif6_table[reg_vif_num].dev; @@ -438,7 +682,17 @@ static netdev_tx_t reg_vif_xmit(struct sk_buff *skb, struct net_device *dev) { struct net *net = dev_net(dev); - struct mr6_table *mrt = net->ipv6.mrt6; + struct mr6_table *mrt; + struct flowi fl = { + .oif = dev->ifindex, + .iif = skb->skb_iif, + .mark = skb->mark, + }; + int err; + + err = ip6mr_fib_lookup(net, &fl, &mrt); + if (err < 0) + return err; read_lock(&mrt_lock); dev->stats.tx_bytes += skb->len; @@ -463,11 +717,17 @@ static void reg_vif_setup(struct net_device *dev) dev->features |= NETIF_F_NETNS_LOCAL; } -static struct net_device *ip6mr_reg_vif(struct net *net) +static struct net_device *ip6mr_reg_vif(struct net *net, struct mr6_table *mrt) { struct net_device *dev; + char name[IFNAMSIZ]; + + if (mrt->id == RT6_TABLE_DFLT) + sprintf(name, "pim6reg"); + else + sprintf(name, "pim6reg%u", mrt->id); - dev = alloc_netdev(0, "pim6reg", reg_vif_setup); + dev = alloc_netdev(0, name, reg_vif_setup); if (dev == NULL) return NULL; @@ -665,7 +925,7 @@ static int mif6_add(struct net *net, struct mr6_table *mrt, */ if (mrt->mroute_reg_vif_num >= 0) return -EADDRINUSE; - dev = ip6mr_reg_vif(net); + dev = ip6mr_reg_vif(net, mrt); if (!dev) return -ENOBUFS; err = dev_set_allmulti(dev, 1); @@ -995,7 +1255,7 @@ static int ip6mr_device_event(struct notifier_block *this, { struct net_device *dev = ptr; struct net *net = dev_net(dev); - struct mr6_table *mrt = net->ipv6.mrt6; + struct mr6_table *mrt; struct mif_device *v; int ct; LIST_HEAD(list); @@ -1003,10 +1263,12 @@ static int ip6mr_device_event(struct notifier_block *this, if (event != NETDEV_UNREGISTER) return NOTIFY_DONE; - v = &mrt->vif6_table[0]; - for (ct = 0; ct < mrt->maxvif; ct++, v++) { - if (v->dev == dev) - mif6_delete(mrt, ct, &list); + ip6mr_for_each_table(mrt, net) { + v = &mrt->vif6_table[0]; + for (ct = 0; ct < mrt->maxvif; ct++, v++) { + if (v->dev == dev) + mif6_delete(mrt, ct, &list); + } } unregister_netdevice_many(&list); @@ -1023,29 +1285,11 @@ static struct notifier_block ip6_mr_notifier = { static int __net_init ip6mr_net_init(struct net *net) { - struct mr6_table *mrt; - unsigned int i; - int err = 0; + int err; - mrt = kzalloc(sizeof(*mrt), GFP_KERNEL); - if (mrt == NULL) { - err = -ENOMEM; + err = ip6mr_rules_init(net); + if (err < 0) goto fail; - } - - write_pnet(&mrt->net, net); - - for (i = 0; i < MFC6_LINES; i++) - INIT_LIST_HEAD(&mrt->mfc6_cache_array[i]); - - INIT_LIST_HEAD(&mrt->mfc6_unres_queue); - - setup_timer(&mrt->ipmr_expire_timer, ipmr_expire_process, - (unsigned long)mrt); - -#ifdef CONFIG_IPV6_PIMSM_V2 - mrt->mroute_reg_vif_num = -1; -#endif #ifdef CONFIG_PROC_FS err = -ENOMEM; @@ -1055,14 +1299,13 @@ static int __net_init ip6mr_net_init(struct net *net) goto proc_cache_fail; #endif - net->ipv6.mrt6 = mrt; return 0; #ifdef CONFIG_PROC_FS proc_cache_fail: proc_net_remove(net, "ip6_mr_vif"); proc_vif_fail: - kfree(mrt); + ip6mr_rules_exit(net); #endif fail: return err; @@ -1070,15 +1313,11 @@ fail: static void __net_exit ip6mr_net_exit(struct net *net) { - struct mr6_table *mrt = net->ipv6.mrt6; - #ifdef CONFIG_PROC_FS proc_net_remove(net, "ip6_mr_cache"); proc_net_remove(net, "ip6_mr_vif"); #endif - del_timer(&mrt->ipmr_expire_timer); - mroute_clean_tables(mrt); - kfree(mrt); + ip6mr_rules_exit(net); } static struct pernet_operations ip6mr_net_ops = { @@ -1279,28 +1518,39 @@ static int ip6mr_sk_init(struct mr6_table *mrt, struct sock *sk) int ip6mr_sk_done(struct sock *sk) { - int err = 0; + int err = -EACCES; struct net *net = sock_net(sk); - struct mr6_table *mrt = net->ipv6.mrt6; + struct mr6_table *mrt; rtnl_lock(); - if (sk == mrt->mroute6_sk) { - write_lock_bh(&mrt_lock); - mrt->mroute6_sk = NULL; - net->ipv6.devconf_all->mc_forwarding--; - write_unlock_bh(&mrt_lock); + ip6mr_for_each_table(mrt, net) { + if (sk == mrt->mroute6_sk) { + write_lock_bh(&mrt_lock); + mrt->mroute6_sk = NULL; + net->ipv6.devconf_all->mc_forwarding--; + write_unlock_bh(&mrt_lock); - mroute_clean_tables(mrt); - } else - err = -EACCES; + mroute_clean_tables(mrt); + err = 0; + break; + } + } rtnl_unlock(); return err; } -struct sock *mroute6_socket(struct net *net) +struct sock *mroute6_socket(struct net *net, struct sk_buff *skb) { - struct mr6_table *mrt = net->ipv6.mrt6; + struct mr6_table *mrt; + struct flowi fl = { + .iif = skb->skb_iif, + .oif = skb->dev->ifindex, + .mark = skb->mark, + }; + + if (ip6mr_fib_lookup(net, &fl, &mrt) < 0) + return NULL; return mrt->mroute6_sk; } @@ -1319,7 +1569,11 @@ int ip6_mroute_setsockopt(struct sock *sk, int optname, char __user *optval, uns struct mf6cctl mfc; mifi_t mifi; struct net *net = sock_net(sk); - struct mr6_table *mrt = net->ipv6.mrt6; + struct mr6_table *mrt; + + mrt = ip6mr_get_table(net, raw6_sk(sk)->ip6mr_table ? : RT6_TABLE_DFLT); + if (mrt == NULL) + return -ENOENT; if (optname != MRT6_INIT) { if (sk != mrt->mroute6_sk && !capable(CAP_NET_ADMIN)) @@ -1408,6 +1662,27 @@ int ip6_mroute_setsockopt(struct sock *sk, int optname, char __user *optval, uns return ret; } +#endif +#ifdef CONFIG_IPV6_MROUTE_MULTIPLE_TABLES + case MRT6_TABLE: + { + u32 v; + + if (optlen != sizeof(u32)) + return -EINVAL; + if (get_user(v, (u32 __user *)optval)) + return -EFAULT; + if (sk == mrt->mroute6_sk) + return -EBUSY; + + rtnl_lock(); + ret = 0; + if (!ip6mr_new_table(net, v)) + ret = -ENOMEM; + raw6_sk(sk)->ip6mr_table = v; + rtnl_unlock(); + return ret; + } #endif /* * Spurious command, or MRT6_VERSION which you cannot @@ -1428,7 +1703,11 @@ int ip6_mroute_getsockopt(struct sock *sk, int optname, char __user *optval, int olr; int val; struct net *net = sock_net(sk); - struct mr6_table *mrt = net->ipv6.mrt6; + struct mr6_table *mrt; + + mrt = ip6mr_get_table(net, raw6_sk(sk)->ip6mr_table ? : RT6_TABLE_DFLT); + if (mrt == NULL) + return -ENOENT; switch (optname) { case MRT6_VERSION: @@ -1471,7 +1750,11 @@ int ip6mr_ioctl(struct sock *sk, int cmd, void __user *arg) struct mif_device *vif; struct mfc6_cache *c; struct net *net = sock_net(sk); - struct mr6_table *mrt = net->ipv6.mrt6; + struct mr6_table *mrt; + + mrt = ip6mr_get_table(net, raw6_sk(sk)->ip6mr_table ? : RT6_TABLE_DFLT); + if (mrt == NULL) + return -ENOENT; switch (cmd) { case SIOCGETMIFCNT_IN6: @@ -1683,7 +1966,16 @@ int ip6_mr_input(struct sk_buff *skb) { struct mfc6_cache *cache; struct net *net = dev_net(skb->dev); - struct mr6_table *mrt = net->ipv6.mrt6; + struct mr6_table *mrt; + struct flowi fl = { + .iif = skb->dev->ifindex, + .mark = skb->mark, + }; + int err; + + err = ip6mr_fib_lookup(net, &fl, &mrt); + if (err < 0) + return err; read_lock(&mrt_lock); cache = ip6mr_cache_find(mrt, @@ -1758,10 +2050,14 @@ int ip6mr_get_route(struct net *net, struct sk_buff *skb, struct rtmsg *rtm, int nowait) { int err; - struct mr6_table *mrt = net->ipv6.mrt6; + struct mr6_table *mrt; struct mfc6_cache *cache; struct rt6_info *rt = (struct rt6_info *)skb_dst(skb); + mrt = ip6mr_get_table(net, RT6_TABLE_DFLT); + if (mrt == NULL) + return -ENOENT; + read_lock(&mrt_lock); cache = ip6mr_cache_find(mrt, &rt->rt6i_src.addr, &rt->rt6i_dst.addr); -- cgit v1.2.3 From de74c16996287250f0d947663127f80c6beebd3c Mon Sep 17 00:00:00 2001 From: Jan Engelhardt Date: Sun, 5 Jul 2009 18:26:37 +0200 Subject: netfilter: xtables: combine struct xt_match_param and xt_target_param The structures carried - besides match/target - almost the same data. It is possible to combine them, as extensions are evaluated serially, and so, the callers end up a little smaller. text data bss filename -15318 740 104 net/ipv4/netfilter/ip_tables.o +15286 740 104 net/ipv4/netfilter/ip_tables.o -15333 540 152 net/ipv6/netfilter/ip6_tables.o +15269 540 152 net/ipv6/netfilter/ip6_tables.o Signed-off-by: Jan Engelhardt --- include/linux/netfilter/x_tables.h | 42 +++++++++++++++----------------------- net/bridge/netfilter/ebtables.c | 30 +++++++++++++-------------- net/ipv4/netfilter/arp_tables.c | 16 +++++++-------- net/ipv4/netfilter/ip_tables.c | 32 ++++++++++++++--------------- net/ipv6/netfilter/ip6_tables.c | 27 ++++++++++++------------ 5 files changed, 68 insertions(+), 79 deletions(-) (limited to 'include/linux') diff --git a/include/linux/netfilter/x_tables.h b/include/linux/netfilter/x_tables.h index eeb4884c30b..5efa3757d08 100644 --- a/include/linux/netfilter/x_tables.h +++ b/include/linux/netfilter/x_tables.h @@ -182,13 +182,17 @@ struct xt_counters_info { #include +#define xt_match_param xt_action_param +#define xt_target_param xt_action_param /** - * struct xt_match_param - parameters for match extensions' match functions + * struct xt_action_param - parameters for matches/targets * + * @match: the match extension + * @target: the target extension + * @matchinfo: per-match data + * @targetinfo: per-target data * @in: input netdevice * @out: output netdevice - * @match: struct xt_match through which this function was invoked - * @matchinfo: per-match data * @fragoff: packet is a fragment, this is the data offset * @thoff: position of transport header relative to skb->data * @hook: hook number given packet came from @@ -197,10 +201,15 @@ struct xt_counters_info { * @hotdrop: drop packet if we had inspection problems * Network namespace obtainable using dev_net(in/out) */ -struct xt_match_param { +struct xt_action_param { + union { + const struct xt_match *match; + const struct xt_target *target; + }; + union { + const void *matchinfo, *targinfo; + }; const struct net_device *in, *out; - const struct xt_match *match; - const void *matchinfo; int fragoff; unsigned int thoff; unsigned int hooknum; @@ -242,23 +251,6 @@ struct xt_mtdtor_param { u_int8_t family; }; -/** - * struct xt_target_param - parameters for target extensions' target functions - * - * @hooknum: hook through which this target was invoked - * @target: struct xt_target through which this function was invoked - * @targinfo: per-target data - * - * Other fields see above. - */ -struct xt_target_param { - const struct net_device *in, *out; - const struct xt_target *target; - const void *targinfo; - unsigned int hooknum; - u_int8_t family; -}; - /** * struct xt_tgchk_param - parameters for target extensions' * checkentry functions @@ -298,7 +290,7 @@ struct xt_match { non-linear skb, using skb_header_pointer and skb_ip_make_writable. */ bool (*match)(const struct sk_buff *skb, - const struct xt_match_param *); + const struct xt_action_param *); /* Called when user tries to insert an entry of this type. */ int (*checkentry)(const struct xt_mtchk_param *); @@ -335,7 +327,7 @@ struct xt_target { must now handle non-linear skbs, using skb_copy_bits and skb_ip_make_writable. */ unsigned int (*target)(struct sk_buff *skb, - const struct xt_target_param *); + const struct xt_action_param *); /* Called when user tries to insert an entry of this type: hook_mask is a bitmask of hooks from which it can be diff --git a/net/bridge/netfilter/ebtables.c b/net/bridge/netfilter/ebtables.c index 1d8c2c0a747..290d43541d4 100644 --- a/net/bridge/netfilter/ebtables.c +++ b/net/bridge/netfilter/ebtables.c @@ -86,7 +86,7 @@ static struct xt_target ebt_standard_target = { static inline int ebt_do_watcher(const struct ebt_entry_watcher *w, struct sk_buff *skb, - struct xt_target_param *par) + struct xt_action_param *par) { par->target = w->u.watcher; par->targinfo = w->data; @@ -95,8 +95,9 @@ ebt_do_watcher(const struct ebt_entry_watcher *w, struct sk_buff *skb, return 0; } -static inline int ebt_do_match (struct ebt_entry_match *m, - const struct sk_buff *skb, struct xt_match_param *par) +static inline int +ebt_do_match(struct ebt_entry_match *m, const struct sk_buff *skb, + struct xt_action_param *par) { par->match = m->u.match; par->matchinfo = m->data; @@ -186,14 +187,13 @@ unsigned int ebt_do_table (unsigned int hook, struct sk_buff *skb, const char *base; const struct ebt_table_info *private; bool hotdrop = false; - struct xt_match_param mtpar; - struct xt_target_param tgpar; + struct xt_action_param acpar; - mtpar.family = tgpar.family = NFPROTO_BRIDGE; - mtpar.in = tgpar.in = in; - mtpar.out = tgpar.out = out; - mtpar.hotdrop = &hotdrop; - mtpar.hooknum = tgpar.hooknum = hook; + acpar.family = NFPROTO_BRIDGE; + acpar.in = in; + acpar.out = out; + acpar.hotdrop = &hotdrop; + acpar.hooknum = hook; read_lock_bh(&table->lock); private = table->private; @@ -214,7 +214,7 @@ unsigned int ebt_do_table (unsigned int hook, struct sk_buff *skb, if (ebt_basic_match(point, eth_hdr(skb), in, out)) goto letscontinue; - if (EBT_MATCH_ITERATE(point, ebt_do_match, skb, &mtpar) != 0) + if (EBT_MATCH_ITERATE(point, ebt_do_match, skb, &acpar) != 0) goto letscontinue; if (hotdrop) { read_unlock_bh(&table->lock); @@ -227,7 +227,7 @@ unsigned int ebt_do_table (unsigned int hook, struct sk_buff *skb, /* these should only watch: not modify, nor tell us what to do with the packet */ - EBT_WATCHER_ITERATE(point, ebt_do_watcher, skb, &tgpar); + EBT_WATCHER_ITERATE(point, ebt_do_watcher, skb, &acpar); t = (struct ebt_entry_target *) (((char *)point) + point->target_offset); @@ -235,9 +235,9 @@ unsigned int ebt_do_table (unsigned int hook, struct sk_buff *skb, if (!t->u.target->target) verdict = ((struct ebt_standard_target *)t)->verdict; else { - tgpar.target = t->u.target; - tgpar.targinfo = t->data; - verdict = t->u.target->target(skb, &tgpar); + acpar.target = t->u.target; + acpar.targinfo = t->data; + verdict = t->u.target->target(skb, &acpar); } if (verdict == EBT_ACCEPT) { read_unlock_bh(&table->lock); diff --git a/net/ipv4/netfilter/arp_tables.c b/net/ipv4/netfilter/arp_tables.c index 07a69905939..73d924b88f8 100644 --- a/net/ipv4/netfilter/arp_tables.c +++ b/net/ipv4/netfilter/arp_tables.c @@ -265,7 +265,7 @@ unsigned int arpt_do_table(struct sk_buff *skb, const char *indev, *outdev; void *table_base; const struct xt_table_info *private; - struct xt_target_param tgpar; + struct xt_action_param acpar; if (!pskb_may_pull(skb, arp_hdr_len(skb->dev))) return NF_DROP; @@ -280,10 +280,10 @@ unsigned int arpt_do_table(struct sk_buff *skb, e = get_entry(table_base, private->hook_entry[hook]); back = get_entry(table_base, private->underflow[hook]); - tgpar.in = in; - tgpar.out = out; - tgpar.hooknum = hook; - tgpar.family = NFPROTO_ARP; + acpar.in = in; + acpar.out = out; + acpar.hooknum = hook; + acpar.family = NFPROTO_ARP; arp = arp_hdr(skb); do { @@ -333,9 +333,9 @@ unsigned int arpt_do_table(struct sk_buff *skb, /* Targets which reenter must return * abs. verdicts */ - tgpar.target = t->u.kernel.target; - tgpar.targinfo = t->data; - verdict = t->u.kernel.target->target(skb, &tgpar); + acpar.target = t->u.kernel.target; + acpar.targinfo = t->data; + verdict = t->u.kernel.target->target(skb, &acpar); /* Target might have changed stuff. */ arp = arp_hdr(skb); diff --git a/net/ipv4/netfilter/ip_tables.c b/net/ipv4/netfilter/ip_tables.c index 265cedf8866..e1a53c2da03 100644 --- a/net/ipv4/netfilter/ip_tables.c +++ b/net/ipv4/netfilter/ip_tables.c @@ -316,8 +316,7 @@ ipt_do_table(struct sk_buff *skb, struct ipt_entry *e, **jumpstack; unsigned int *stackptr, origptr, cpu; const struct xt_table_info *private; - struct xt_match_param mtpar; - struct xt_target_param tgpar; + struct xt_action_param acpar; /* Initialization */ ip = ip_hdr(skb); @@ -329,13 +328,13 @@ ipt_do_table(struct sk_buff *skb, * things we don't know, ie. tcp syn flag or ports). If the * rule is also a fragment-specific rule, non-fragments won't * match it. */ - mtpar.fragoff = ntohs(ip->frag_off) & IP_OFFSET; - mtpar.thoff = ip_hdrlen(skb); - mtpar.hotdrop = &hotdrop; - mtpar.in = tgpar.in = in; - mtpar.out = tgpar.out = out; - mtpar.family = tgpar.family = NFPROTO_IPV4; - mtpar.hooknum = tgpar.hooknum = hook; + acpar.fragoff = ntohs(ip->frag_off) & IP_OFFSET; + acpar.thoff = ip_hdrlen(skb); + acpar.hotdrop = &hotdrop; + acpar.in = in; + acpar.out = out; + acpar.family = NFPROTO_IPV4; + acpar.hooknum = hook; IP_NF_ASSERT(table->valid_hooks & (1 << hook)); xt_info_rdlock_bh(); @@ -358,16 +357,16 @@ ipt_do_table(struct sk_buff *skb, IP_NF_ASSERT(e); if (!ip_packet_match(ip, indev, outdev, - &e->ip, mtpar.fragoff)) { + &e->ip, acpar.fragoff)) { no_match: e = ipt_next_entry(e); continue; } xt_ematch_foreach(ematch, e) { - mtpar.match = ematch->u.kernel.match; - mtpar.matchinfo = ematch->data; - if (!mtpar.match->match(skb, &mtpar)) + acpar.match = ematch->u.kernel.match; + acpar.matchinfo = ematch->data; + if (!acpar.match->match(skb, &acpar)) goto no_match; } @@ -422,11 +421,10 @@ ipt_do_table(struct sk_buff *skb, continue; } - tgpar.target = t->u.kernel.target; - tgpar.targinfo = t->data; + acpar.target = t->u.kernel.target; + acpar.targinfo = t->data; - - verdict = t->u.kernel.target->target(skb, &tgpar); + verdict = t->u.kernel.target->target(skb, &acpar); /* Target might have changed stuff. */ ip = ip_hdr(skb); if (verdict == IPT_CONTINUE) diff --git a/net/ipv6/netfilter/ip6_tables.c b/net/ipv6/netfilter/ip6_tables.c index f8ac4a0b589..076308c1acd 100644 --- a/net/ipv6/netfilter/ip6_tables.c +++ b/net/ipv6/netfilter/ip6_tables.c @@ -345,8 +345,7 @@ ip6t_do_table(struct sk_buff *skb, struct ip6t_entry *e, **jumpstack; unsigned int *stackptr, origptr, cpu; const struct xt_table_info *private; - struct xt_match_param mtpar; - struct xt_target_param tgpar; + struct xt_action_param acpar; /* Initialization */ indev = in ? in->name : nulldevname; @@ -357,11 +356,11 @@ ip6t_do_table(struct sk_buff *skb, * things we don't know, ie. tcp syn flag or ports). If the * rule is also a fragment-specific rule, non-fragments won't * match it. */ - mtpar.hotdrop = &hotdrop; - mtpar.in = tgpar.in = in; - mtpar.out = tgpar.out = out; - mtpar.family = tgpar.family = NFPROTO_IPV6; - mtpar.hooknum = tgpar.hooknum = hook; + acpar.hotdrop = &hotdrop; + acpar.in = in; + acpar.out = out; + acpar.family = NFPROTO_IPV6; + acpar.hooknum = hook; IP_NF_ASSERT(table->valid_hooks & (1 << hook)); @@ -381,16 +380,16 @@ ip6t_do_table(struct sk_buff *skb, IP_NF_ASSERT(e); if (!ip6_packet_match(skb, indev, outdev, &e->ipv6, - &mtpar.thoff, &mtpar.fragoff, &hotdrop)) { + &acpar.thoff, &acpar.fragoff, &hotdrop)) { no_match: e = ip6t_next_entry(e); continue; } xt_ematch_foreach(ematch, e) { - mtpar.match = ematch->u.kernel.match; - mtpar.matchinfo = ematch->data; - if (!mtpar.match->match(skb, &mtpar)) + acpar.match = ematch->u.kernel.match; + acpar.matchinfo = ematch->data; + if (!acpar.match->match(skb, &acpar)) goto no_match; } @@ -439,10 +438,10 @@ ip6t_do_table(struct sk_buff *skb, continue; } - tgpar.target = t->u.kernel.target; - tgpar.targinfo = t->data; + acpar.target = t->u.kernel.target; + acpar.targinfo = t->data; - verdict = t->u.kernel.target->target(skb, &tgpar); + verdict = t->u.kernel.target->target(skb, &acpar); if (verdict == IP6T_CONTINUE) e = ip6t_next_entry(e); else -- cgit v1.2.3 From 4b560b447df83368df44bd3712c0c39b1d79ba04 Mon Sep 17 00:00:00 2001 From: Jan Engelhardt Date: Sun, 5 Jul 2009 19:43:26 +0200 Subject: netfilter: xtables: substitute temporary defines by final name Signed-off-by: Jan Engelhardt --- include/linux/netfilter/x_tables.h | 2 -- net/bridge/netfilter/ebt_802_3.c | 2 +- net/bridge/netfilter/ebt_among.c | 2 +- net/bridge/netfilter/ebt_arp.c | 2 +- net/bridge/netfilter/ebt_arpreply.c | 2 +- net/bridge/netfilter/ebt_dnat.c | 2 +- net/bridge/netfilter/ebt_ip.c | 2 +- net/bridge/netfilter/ebt_ip6.c | 2 +- net/bridge/netfilter/ebt_limit.c | 2 +- net/bridge/netfilter/ebt_log.c | 2 +- net/bridge/netfilter/ebt_mark.c | 2 +- net/bridge/netfilter/ebt_mark_m.c | 2 +- net/bridge/netfilter/ebt_nflog.c | 2 +- net/bridge/netfilter/ebt_pkttype.c | 2 +- net/bridge/netfilter/ebt_redirect.c | 2 +- net/bridge/netfilter/ebt_snat.c | 2 +- net/bridge/netfilter/ebt_stp.c | 2 +- net/bridge/netfilter/ebt_ulog.c | 2 +- net/bridge/netfilter/ebt_vlan.c | 2 +- net/ipv4/netfilter/arp_tables.c | 2 +- net/ipv4/netfilter/arpt_mangle.c | 2 +- net/ipv4/netfilter/ip_tables.c | 4 ++-- net/ipv4/netfilter/ipt_CLUSTERIP.c | 2 +- net/ipv4/netfilter/ipt_ECN.c | 2 +- net/ipv4/netfilter/ipt_LOG.c | 2 +- net/ipv4/netfilter/ipt_MASQUERADE.c | 2 +- net/ipv4/netfilter/ipt_NETMAP.c | 2 +- net/ipv4/netfilter/ipt_REDIRECT.c | 2 +- net/ipv4/netfilter/ipt_REJECT.c | 2 +- net/ipv4/netfilter/ipt_ULOG.c | 2 +- net/ipv4/netfilter/ipt_addrtype.c | 4 ++-- net/ipv4/netfilter/ipt_ah.c | 2 +- net/ipv4/netfilter/ipt_ecn.c | 3 ++- net/ipv4/netfilter/nf_nat_rule.c | 4 ++-- net/ipv6/netfilter/ip6_tables.c | 4 ++-- net/ipv6/netfilter/ip6t_LOG.c | 2 +- net/ipv6/netfilter/ip6t_REJECT.c | 2 +- net/ipv6/netfilter/ip6t_ah.c | 3 ++- net/ipv6/netfilter/ip6t_eui64.c | 2 +- net/ipv6/netfilter/ip6t_frag.c | 2 +- net/ipv6/netfilter/ip6t_hbh.c | 2 +- net/ipv6/netfilter/ip6t_ipv6header.c | 2 +- net/ipv6/netfilter/ip6t_mh.c | 3 ++- net/ipv6/netfilter/ip6t_rt.c | 3 ++- net/netfilter/xt_CLASSIFY.c | 2 +- net/netfilter/xt_CONNSECMARK.c | 2 +- net/netfilter/xt_CT.c | 2 +- net/netfilter/xt_DSCP.c | 8 ++++---- net/netfilter/xt_HL.c | 4 ++-- net/netfilter/xt_LED.c | 2 +- net/netfilter/xt_NFLOG.c | 2 +- net/netfilter/xt_NFQUEUE.c | 4 ++-- net/netfilter/xt_NOTRACK.c | 2 +- net/netfilter/xt_RATEEST.c | 2 +- net/netfilter/xt_SECMARK.c | 2 +- net/netfilter/xt_TCPMSS.c | 4 ++-- net/netfilter/xt_TCPOPTSTRIP.c | 4 ++-- net/netfilter/xt_TEE.c | 4 ++-- net/netfilter/xt_TPROXY.c | 2 +- net/netfilter/xt_TRACE.c | 2 +- net/netfilter/xt_cluster.c | 2 +- net/netfilter/xt_comment.c | 2 +- net/netfilter/xt_connbytes.c | 2 +- net/netfilter/xt_connlimit.c | 2 +- net/netfilter/xt_connmark.c | 4 ++-- net/netfilter/xt_conntrack.c | 6 +++--- net/netfilter/xt_dccp.c | 2 +- net/netfilter/xt_dscp.c | 7 ++++--- net/netfilter/xt_esp.c | 3 ++- net/netfilter/xt_hashlimit.c | 2 +- net/netfilter/xt_helper.c | 2 +- net/netfilter/xt_hl.c | 6 ++++-- net/netfilter/xt_iprange.c | 4 ++-- net/netfilter/xt_length.c | 4 ++-- net/netfilter/xt_limit.c | 2 +- net/netfilter/xt_mac.c | 3 ++- net/netfilter/xt_mark.c | 4 ++-- net/netfilter/xt_multiport.c | 2 +- net/netfilter/xt_osf.c | 4 ++-- net/netfilter/xt_owner.c | 2 +- net/netfilter/xt_physdev.c | 2 +- net/netfilter/xt_pkttype.c | 2 +- net/netfilter/xt_policy.c | 2 +- net/netfilter/xt_quota.c | 2 +- net/netfilter/xt_rateest.c | 2 +- net/netfilter/xt_realm.c | 2 +- net/netfilter/xt_recent.c | 2 +- net/netfilter/xt_sctp.c | 2 +- net/netfilter/xt_socket.c | 6 +++--- net/netfilter/xt_state.c | 2 +- net/netfilter/xt_statistic.c | 2 +- net/netfilter/xt_string.c | 2 +- net/netfilter/xt_tcpmss.c | 2 +- net/netfilter/xt_tcpudp.c | 6 ++++-- net/netfilter/xt_time.c | 2 +- net/netfilter/xt_u32.c | 3 ++- net/sched/act_ipt.c | 2 +- 97 files changed, 133 insertions(+), 123 deletions(-) (limited to 'include/linux') diff --git a/include/linux/netfilter/x_tables.h b/include/linux/netfilter/x_tables.h index 5efa3757d08..6d1f9a63805 100644 --- a/include/linux/netfilter/x_tables.h +++ b/include/linux/netfilter/x_tables.h @@ -182,8 +182,6 @@ struct xt_counters_info { #include -#define xt_match_param xt_action_param -#define xt_target_param xt_action_param /** * struct xt_action_param - parameters for matches/targets * diff --git a/net/bridge/netfilter/ebt_802_3.c b/net/bridge/netfilter/ebt_802_3.c index f7de8dbc342..4b0df00c82e 100644 --- a/net/bridge/netfilter/ebt_802_3.c +++ b/net/bridge/netfilter/ebt_802_3.c @@ -13,7 +13,7 @@ #include static bool -ebt_802_3_mt(const struct sk_buff *skb, const struct xt_match_param *par) +ebt_802_3_mt(const struct sk_buff *skb, const struct xt_action_param *par) { const struct ebt_802_3_info *info = par->matchinfo; const struct ebt_802_3_hdr *hdr = ebt_802_3_hdr(skb); diff --git a/net/bridge/netfilter/ebt_among.c b/net/bridge/netfilter/ebt_among.c index 20068e03fa8..c04f9461f73 100644 --- a/net/bridge/netfilter/ebt_among.c +++ b/net/bridge/netfilter/ebt_among.c @@ -129,7 +129,7 @@ static int get_ip_src(const struct sk_buff *skb, __be32 *addr) } static bool -ebt_among_mt(const struct sk_buff *skb, const struct xt_match_param *par) +ebt_among_mt(const struct sk_buff *skb, const struct xt_action_param *par) { const struct ebt_among_info *info = par->matchinfo; const char *dmac, *smac; diff --git a/net/bridge/netfilter/ebt_arp.c b/net/bridge/netfilter/ebt_arp.c index 952150cd5e7..6203f4dea2e 100644 --- a/net/bridge/netfilter/ebt_arp.c +++ b/net/bridge/netfilter/ebt_arp.c @@ -16,7 +16,7 @@ #include static bool -ebt_arp_mt(const struct sk_buff *skb, const struct xt_match_param *par) +ebt_arp_mt(const struct sk_buff *skb, const struct xt_action_param *par) { const struct ebt_arp_info *info = par->matchinfo; const struct arphdr *ah; diff --git a/net/bridge/netfilter/ebt_arpreply.c b/net/bridge/netfilter/ebt_arpreply.c index 4581adb2758..070cf134a22 100644 --- a/net/bridge/netfilter/ebt_arpreply.c +++ b/net/bridge/netfilter/ebt_arpreply.c @@ -16,7 +16,7 @@ #include static unsigned int -ebt_arpreply_tg(struct sk_buff *skb, const struct xt_target_param *par) +ebt_arpreply_tg(struct sk_buff *skb, const struct xt_action_param *par) { const struct ebt_arpreply_info *info = par->targinfo; const __be32 *siptr, *diptr; diff --git a/net/bridge/netfilter/ebt_dnat.c b/net/bridge/netfilter/ebt_dnat.c index 59d5b7c8a55..c59f7bfae6e 100644 --- a/net/bridge/netfilter/ebt_dnat.c +++ b/net/bridge/netfilter/ebt_dnat.c @@ -15,7 +15,7 @@ #include static unsigned int -ebt_dnat_tg(struct sk_buff *skb, const struct xt_target_param *par) +ebt_dnat_tg(struct sk_buff *skb, const struct xt_action_param *par) { const struct ebt_nat_info *info = par->targinfo; diff --git a/net/bridge/netfilter/ebt_ip.c b/net/bridge/netfilter/ebt_ip.c index a1c76c7e521..a0cde7442b5 100644 --- a/net/bridge/netfilter/ebt_ip.c +++ b/net/bridge/netfilter/ebt_ip.c @@ -25,7 +25,7 @@ struct tcpudphdr { }; static bool -ebt_ip_mt(const struct sk_buff *skb, const struct xt_match_param *par) +ebt_ip_mt(const struct sk_buff *skb, const struct xt_action_param *par) { const struct ebt_ip_info *info = par->matchinfo; const struct iphdr *ih; diff --git a/net/bridge/netfilter/ebt_ip6.c b/net/bridge/netfilter/ebt_ip6.c index 33f8413f05a..c451dc2ff82 100644 --- a/net/bridge/netfilter/ebt_ip6.c +++ b/net/bridge/netfilter/ebt_ip6.c @@ -28,7 +28,7 @@ struct tcpudphdr { }; static bool -ebt_ip6_mt(const struct sk_buff *skb, const struct xt_match_param *par) +ebt_ip6_mt(const struct sk_buff *skb, const struct xt_action_param *par) { const struct ebt_ip6_info *info = par->matchinfo; const struct ipv6hdr *ih6; diff --git a/net/bridge/netfilter/ebt_limit.c b/net/bridge/netfilter/ebt_limit.c index 4b0e2e53fa5..760923f0806 100644 --- a/net/bridge/netfilter/ebt_limit.c +++ b/net/bridge/netfilter/ebt_limit.c @@ -32,7 +32,7 @@ static DEFINE_SPINLOCK(limit_lock); #define CREDITS_PER_JIFFY POW2_BELOW32(MAX_CPJ) static bool -ebt_limit_mt(const struct sk_buff *skb, const struct xt_match_param *par) +ebt_limit_mt(const struct sk_buff *skb, const struct xt_action_param *par) { struct ebt_limit_info *info = (void *)par->matchinfo; unsigned long now = jiffies; diff --git a/net/bridge/netfilter/ebt_log.c b/net/bridge/netfilter/ebt_log.c index c4602415653..6e5a8bb9b94 100644 --- a/net/bridge/netfilter/ebt_log.c +++ b/net/bridge/netfilter/ebt_log.c @@ -171,7 +171,7 @@ out: } static unsigned int -ebt_log_tg(struct sk_buff *skb, const struct xt_target_param *par) +ebt_log_tg(struct sk_buff *skb, const struct xt_action_param *par) { const struct ebt_log_info *info = par->targinfo; struct nf_loginfo li; diff --git a/net/bridge/netfilter/ebt_mark.c b/net/bridge/netfilter/ebt_mark.c index 126e536ff8f..66697cbd0a8 100644 --- a/net/bridge/netfilter/ebt_mark.c +++ b/net/bridge/netfilter/ebt_mark.c @@ -19,7 +19,7 @@ #include static unsigned int -ebt_mark_tg(struct sk_buff *skb, const struct xt_target_param *par) +ebt_mark_tg(struct sk_buff *skb, const struct xt_action_param *par) { const struct ebt_mark_t_info *info = par->targinfo; int action = info->target & -16; diff --git a/net/bridge/netfilter/ebt_mark_m.c b/net/bridge/netfilter/ebt_mark_m.c index e4366c0a1a4..de901634fa3 100644 --- a/net/bridge/netfilter/ebt_mark_m.c +++ b/net/bridge/netfilter/ebt_mark_m.c @@ -13,7 +13,7 @@ #include static bool -ebt_mark_mt(const struct sk_buff *skb, const struct xt_match_param *par) +ebt_mark_mt(const struct sk_buff *skb, const struct xt_action_param *par) { const struct ebt_mark_m_info *info = par->matchinfo; diff --git a/net/bridge/netfilter/ebt_nflog.c b/net/bridge/netfilter/ebt_nflog.c index 22e2ad5f23e..5be68bbcc34 100644 --- a/net/bridge/netfilter/ebt_nflog.c +++ b/net/bridge/netfilter/ebt_nflog.c @@ -20,7 +20,7 @@ #include static unsigned int -ebt_nflog_tg(struct sk_buff *skb, const struct xt_target_param *par) +ebt_nflog_tg(struct sk_buff *skb, const struct xt_action_param *par) { const struct ebt_nflog_info *info = par->targinfo; struct nf_loginfo li; diff --git a/net/bridge/netfilter/ebt_pkttype.c b/net/bridge/netfilter/ebt_pkttype.c index f34bcc3197b..7ba67c4b677 100644 --- a/net/bridge/netfilter/ebt_pkttype.c +++ b/net/bridge/netfilter/ebt_pkttype.c @@ -13,7 +13,7 @@ #include static bool -ebt_pkttype_mt(const struct sk_buff *skb, const struct xt_match_param *par) +ebt_pkttype_mt(const struct sk_buff *skb, const struct xt_action_param *par) { const struct ebt_pkttype_info *info = par->matchinfo; diff --git a/net/bridge/netfilter/ebt_redirect.c b/net/bridge/netfilter/ebt_redirect.c index a6044a6f238..9e19166ba45 100644 --- a/net/bridge/netfilter/ebt_redirect.c +++ b/net/bridge/netfilter/ebt_redirect.c @@ -16,7 +16,7 @@ #include static unsigned int -ebt_redirect_tg(struct sk_buff *skb, const struct xt_target_param *par) +ebt_redirect_tg(struct sk_buff *skb, const struct xt_action_param *par) { const struct ebt_redirect_info *info = par->targinfo; diff --git a/net/bridge/netfilter/ebt_snat.c b/net/bridge/netfilter/ebt_snat.c index 79caca34ae2..f8f0bd1a1d5 100644 --- a/net/bridge/netfilter/ebt_snat.c +++ b/net/bridge/netfilter/ebt_snat.c @@ -17,7 +17,7 @@ #include static unsigned int -ebt_snat_tg(struct sk_buff *skb, const struct xt_target_param *par) +ebt_snat_tg(struct sk_buff *skb, const struct xt_action_param *par) { const struct ebt_nat_info *info = par->targinfo; diff --git a/net/bridge/netfilter/ebt_stp.c b/net/bridge/netfilter/ebt_stp.c index 02f28fdda39..3cd6070a113 100644 --- a/net/bridge/netfilter/ebt_stp.c +++ b/net/bridge/netfilter/ebt_stp.c @@ -120,7 +120,7 @@ static bool ebt_filter_config(const struct ebt_stp_info *info, } static bool -ebt_stp_mt(const struct sk_buff *skb, const struct xt_match_param *par) +ebt_stp_mt(const struct sk_buff *skb, const struct xt_action_param *par) { const struct ebt_stp_info *info = par->matchinfo; const struct stp_header *sp; diff --git a/net/bridge/netfilter/ebt_ulog.c b/net/bridge/netfilter/ebt_ulog.c index 852f37c2765..ae3c7cef148 100644 --- a/net/bridge/netfilter/ebt_ulog.c +++ b/net/bridge/netfilter/ebt_ulog.c @@ -243,7 +243,7 @@ static void ebt_log_packet(u_int8_t pf, unsigned int hooknum, } static unsigned int -ebt_ulog_tg(struct sk_buff *skb, const struct xt_target_param *par) +ebt_ulog_tg(struct sk_buff *skb, const struct xt_action_param *par) { ebt_ulog_packet(par->hooknum, skb, par->in, par->out, par->targinfo, NULL); diff --git a/net/bridge/netfilter/ebt_vlan.c b/net/bridge/netfilter/ebt_vlan.c index bf8ae5c7a0c..e4ab62533c7 100644 --- a/net/bridge/netfilter/ebt_vlan.c +++ b/net/bridge/netfilter/ebt_vlan.c @@ -36,7 +36,7 @@ MODULE_LICENSE("GPL"); #define EXIT_ON_MISMATCH(_MATCH_,_MASK_) {if (!((info->_MATCH_ == _MATCH_)^!!(info->invflags & _MASK_))) return false; } static bool -ebt_vlan_mt(const struct sk_buff *skb, const struct xt_match_param *par) +ebt_vlan_mt(const struct sk_buff *skb, const struct xt_action_param *par) { const struct ebt_vlan_info *info = par->matchinfo; const struct vlan_hdr *fp; diff --git a/net/ipv4/netfilter/arp_tables.c b/net/ipv4/netfilter/arp_tables.c index 73d924b88f8..9e7d089f168 100644 --- a/net/ipv4/netfilter/arp_tables.c +++ b/net/ipv4/netfilter/arp_tables.c @@ -224,7 +224,7 @@ static inline int arp_checkentry(const struct arpt_arp *arp) } static unsigned int -arpt_error(struct sk_buff *skb, const struct xt_target_param *par) +arpt_error(struct sk_buff *skb, const struct xt_action_param *par) { if (net_ratelimit()) printk("arp_tables: error: '%s'\n", diff --git a/net/ipv4/netfilter/arpt_mangle.c b/net/ipv4/netfilter/arpt_mangle.c index 4b51a027f30..e1be7dd1171 100644 --- a/net/ipv4/netfilter/arpt_mangle.c +++ b/net/ipv4/netfilter/arpt_mangle.c @@ -9,7 +9,7 @@ MODULE_AUTHOR("Bart De Schuymer "); MODULE_DESCRIPTION("arptables arp payload mangle target"); static unsigned int -target(struct sk_buff *skb, const struct xt_target_param *par) +target(struct sk_buff *skb, const struct xt_action_param *par) { const struct arpt_mangle *mangle = par->targinfo; const struct arphdr *arp; diff --git a/net/ipv4/netfilter/ip_tables.c b/net/ipv4/netfilter/ip_tables.c index e1a53c2da03..3ab1b81e799 100644 --- a/net/ipv4/netfilter/ip_tables.c +++ b/net/ipv4/netfilter/ip_tables.c @@ -165,7 +165,7 @@ ip_checkentry(const struct ipt_ip *ip) } static unsigned int -ipt_error(struct sk_buff *skb, const struct xt_target_param *par) +ipt_error(struct sk_buff *skb, const struct xt_action_param *par) { if (net_ratelimit()) pr_info("error: `%s'\n", (const char *)par->targinfo); @@ -2138,7 +2138,7 @@ icmp_type_code_match(u_int8_t test_type, u_int8_t min_code, u_int8_t max_code, } static bool -icmp_match(const struct sk_buff *skb, const struct xt_match_param *par) +icmp_match(const struct sk_buff *skb, const struct xt_action_param *par) { const struct icmphdr *ic; struct icmphdr _icmph; diff --git a/net/ipv4/netfilter/ipt_CLUSTERIP.c b/net/ipv4/netfilter/ipt_CLUSTERIP.c index 8815d458de4..f91c94b9a79 100644 --- a/net/ipv4/netfilter/ipt_CLUSTERIP.c +++ b/net/ipv4/netfilter/ipt_CLUSTERIP.c @@ -282,7 +282,7 @@ clusterip_responsible(const struct clusterip_config *config, u_int32_t hash) ***********************************************************************/ static unsigned int -clusterip_tg(struct sk_buff *skb, const struct xt_target_param *par) +clusterip_tg(struct sk_buff *skb, const struct xt_action_param *par) { const struct ipt_clusterip_tgt_info *cipinfo = par->targinfo; struct nf_conn *ct; diff --git a/net/ipv4/netfilter/ipt_ECN.c b/net/ipv4/netfilter/ipt_ECN.c index 563049f31ae..4bf3dc49ad1 100644 --- a/net/ipv4/netfilter/ipt_ECN.c +++ b/net/ipv4/netfilter/ipt_ECN.c @@ -77,7 +77,7 @@ set_ect_tcp(struct sk_buff *skb, const struct ipt_ECN_info *einfo) } static unsigned int -ecn_tg(struct sk_buff *skb, const struct xt_target_param *par) +ecn_tg(struct sk_buff *skb, const struct xt_action_param *par) { const struct ipt_ECN_info *einfo = par->targinfo; diff --git a/net/ipv4/netfilter/ipt_LOG.c b/net/ipv4/netfilter/ipt_LOG.c index 3bd35f37081..5234f4f3499 100644 --- a/net/ipv4/netfilter/ipt_LOG.c +++ b/net/ipv4/netfilter/ipt_LOG.c @@ -425,7 +425,7 @@ ipt_log_packet(u_int8_t pf, } static unsigned int -log_tg(struct sk_buff *skb, const struct xt_target_param *par) +log_tg(struct sk_buff *skb, const struct xt_action_param *par) { const struct ipt_log_info *loginfo = par->targinfo; struct nf_loginfo li; diff --git a/net/ipv4/netfilter/ipt_MASQUERADE.c b/net/ipv4/netfilter/ipt_MASQUERADE.c index 02b1bc47799..d2ed9dc74eb 100644 --- a/net/ipv4/netfilter/ipt_MASQUERADE.c +++ b/net/ipv4/netfilter/ipt_MASQUERADE.c @@ -44,7 +44,7 @@ static int masquerade_tg_check(const struct xt_tgchk_param *par) } static unsigned int -masquerade_tg(struct sk_buff *skb, const struct xt_target_param *par) +masquerade_tg(struct sk_buff *skb, const struct xt_action_param *par) { struct nf_conn *ct; struct nf_conn_nat *nat; diff --git a/net/ipv4/netfilter/ipt_NETMAP.c b/net/ipv4/netfilter/ipt_NETMAP.c index 708c7f8f7ee..f43867d1697 100644 --- a/net/ipv4/netfilter/ipt_NETMAP.c +++ b/net/ipv4/netfilter/ipt_NETMAP.c @@ -38,7 +38,7 @@ static int netmap_tg_check(const struct xt_tgchk_param *par) } static unsigned int -netmap_tg(struct sk_buff *skb, const struct xt_target_param *par) +netmap_tg(struct sk_buff *skb, const struct xt_action_param *par) { struct nf_conn *ct; enum ip_conntrack_info ctinfo; diff --git a/net/ipv4/netfilter/ipt_REDIRECT.c b/net/ipv4/netfilter/ipt_REDIRECT.c index 3cf10191652..18a0656505a 100644 --- a/net/ipv4/netfilter/ipt_REDIRECT.c +++ b/net/ipv4/netfilter/ipt_REDIRECT.c @@ -42,7 +42,7 @@ static int redirect_tg_check(const struct xt_tgchk_param *par) } static unsigned int -redirect_tg(struct sk_buff *skb, const struct xt_target_param *par) +redirect_tg(struct sk_buff *skb, const struct xt_action_param *par) { struct nf_conn *ct; enum ip_conntrack_info ctinfo; diff --git a/net/ipv4/netfilter/ipt_REJECT.c b/net/ipv4/netfilter/ipt_REJECT.c index a86135a2805..f5f4a888e4e 100644 --- a/net/ipv4/netfilter/ipt_REJECT.c +++ b/net/ipv4/netfilter/ipt_REJECT.c @@ -136,7 +136,7 @@ static inline void send_unreach(struct sk_buff *skb_in, int code) } static unsigned int -reject_tg(struct sk_buff *skb, const struct xt_target_param *par) +reject_tg(struct sk_buff *skb, const struct xt_action_param *par) { const struct ipt_reject_info *reject = par->targinfo; diff --git a/net/ipv4/netfilter/ipt_ULOG.c b/net/ipv4/netfilter/ipt_ULOG.c index 8f60749e87a..446e0f467a1 100644 --- a/net/ipv4/netfilter/ipt_ULOG.c +++ b/net/ipv4/netfilter/ipt_ULOG.c @@ -276,7 +276,7 @@ alloc_failure: } static unsigned int -ulog_tg(struct sk_buff *skb, const struct xt_target_param *par) +ulog_tg(struct sk_buff *skb, const struct xt_action_param *par) { ipt_ulog_packet(par->hooknum, skb, par->in, par->out, par->targinfo, NULL); diff --git a/net/ipv4/netfilter/ipt_addrtype.c b/net/ipv4/netfilter/ipt_addrtype.c index e4b8f2bf8aa..24ec548515e 100644 --- a/net/ipv4/netfilter/ipt_addrtype.c +++ b/net/ipv4/netfilter/ipt_addrtype.c @@ -30,7 +30,7 @@ static inline bool match_type(struct net *net, const struct net_device *dev, } static bool -addrtype_mt_v0(const struct sk_buff *skb, const struct xt_match_param *par) +addrtype_mt_v0(const struct sk_buff *skb, const struct xt_action_param *par) { struct net *net = dev_net(par->in ? par->in : par->out); const struct ipt_addrtype_info *info = par->matchinfo; @@ -48,7 +48,7 @@ addrtype_mt_v0(const struct sk_buff *skb, const struct xt_match_param *par) } static bool -addrtype_mt_v1(const struct sk_buff *skb, const struct xt_match_param *par) +addrtype_mt_v1(const struct sk_buff *skb, const struct xt_action_param *par) { struct net *net = dev_net(par->in ? par->in : par->out); const struct ipt_addrtype_info_v1 *info = par->matchinfo; diff --git a/net/ipv4/netfilter/ipt_ah.c b/net/ipv4/netfilter/ipt_ah.c index 9f981020489..48a8293bc1d 100644 --- a/net/ipv4/netfilter/ipt_ah.c +++ b/net/ipv4/netfilter/ipt_ah.c @@ -30,7 +30,7 @@ spi_match(u_int32_t min, u_int32_t max, u_int32_t spi, bool invert) return r; } -static bool ah_mt(const struct sk_buff *skb, const struct xt_match_param *par) +static bool ah_mt(const struct sk_buff *skb, const struct xt_action_param *par) { struct ip_auth_hdr _ahdr; const struct ip_auth_hdr *ah; diff --git a/net/ipv4/netfilter/ipt_ecn.c b/net/ipv4/netfilter/ipt_ecn.c index 32e24100d8d..744d13ee296 100644 --- a/net/ipv4/netfilter/ipt_ecn.c +++ b/net/ipv4/netfilter/ipt_ecn.c @@ -67,7 +67,8 @@ static inline bool match_tcp(const struct sk_buff *skb, return true; } -static bool ecn_mt(const struct sk_buff *skb, const struct xt_match_param *par) +static bool ecn_mt(const struct sk_buff *skb, + const struct xt_action_param *par) { const struct ipt_ecn_info *info = par->matchinfo; diff --git a/net/ipv4/netfilter/nf_nat_rule.c b/net/ipv4/netfilter/nf_nat_rule.c index b48a0fc3d9e..98ed78281ae 100644 --- a/net/ipv4/netfilter/nf_nat_rule.c +++ b/net/ipv4/netfilter/nf_nat_rule.c @@ -39,7 +39,7 @@ static const struct xt_table nat_table = { /* Source NAT */ static unsigned int -ipt_snat_target(struct sk_buff *skb, const struct xt_target_param *par) +ipt_snat_target(struct sk_buff *skb, const struct xt_action_param *par) { struct nf_conn *ct; enum ip_conntrack_info ctinfo; @@ -58,7 +58,7 @@ ipt_snat_target(struct sk_buff *skb, const struct xt_target_param *par) } static unsigned int -ipt_dnat_target(struct sk_buff *skb, const struct xt_target_param *par) +ipt_dnat_target(struct sk_buff *skb, const struct xt_action_param *par) { struct nf_conn *ct; enum ip_conntrack_info ctinfo; diff --git a/net/ipv6/netfilter/ip6_tables.c b/net/ipv6/netfilter/ip6_tables.c index 076308c1acd..c3bc999a8bb 100644 --- a/net/ipv6/netfilter/ip6_tables.c +++ b/net/ipv6/netfilter/ip6_tables.c @@ -197,7 +197,7 @@ ip6_checkentry(const struct ip6t_ip6 *ipv6) } static unsigned int -ip6t_error(struct sk_buff *skb, const struct xt_target_param *par) +ip6t_error(struct sk_buff *skb, const struct xt_action_param *par) { if (net_ratelimit()) pr_info("error: `%s'\n", (const char *)par->targinfo); @@ -2154,7 +2154,7 @@ icmp6_type_code_match(u_int8_t test_type, u_int8_t min_code, u_int8_t max_code, } static bool -icmp6_match(const struct sk_buff *skb, const struct xt_match_param *par) +icmp6_match(const struct sk_buff *skb, const struct xt_action_param *par) { const struct icmp6hdr *ic; struct icmp6hdr _icmph; diff --git a/net/ipv6/netfilter/ip6t_LOG.c b/net/ipv6/netfilter/ip6t_LOG.c index 1f47a525f48..af4ee11f206 100644 --- a/net/ipv6/netfilter/ip6t_LOG.c +++ b/net/ipv6/netfilter/ip6t_LOG.c @@ -436,7 +436,7 @@ ip6t_log_packet(u_int8_t pf, } static unsigned int -log_tg6(struct sk_buff *skb, const struct xt_target_param *par) +log_tg6(struct sk_buff *skb, const struct xt_action_param *par) { const struct ip6t_log_info *loginfo = par->targinfo; struct nf_loginfo li; diff --git a/net/ipv6/netfilter/ip6t_REJECT.c b/net/ipv6/netfilter/ip6t_REJECT.c index af1d6494ac3..47d22771375 100644 --- a/net/ipv6/netfilter/ip6t_REJECT.c +++ b/net/ipv6/netfilter/ip6t_REJECT.c @@ -175,7 +175,7 @@ send_unreach(struct net *net, struct sk_buff *skb_in, unsigned char code, } static unsigned int -reject_tg6(struct sk_buff *skb, const struct xt_target_param *par) +reject_tg6(struct sk_buff *skb, const struct xt_action_param *par) { const struct ip6t_reject_info *reject = par->targinfo; struct net *net = dev_net((par->in != NULL) ? par->in : par->out); diff --git a/net/ipv6/netfilter/ip6t_ah.c b/net/ipv6/netfilter/ip6t_ah.c index 1580693c86c..4fe71898381 100644 --- a/net/ipv6/netfilter/ip6t_ah.c +++ b/net/ipv6/netfilter/ip6t_ah.c @@ -36,7 +36,8 @@ spi_match(u_int32_t min, u_int32_t max, u_int32_t spi, bool invert) return r; } -static bool ah_mt6(const struct sk_buff *skb, const struct xt_match_param *par) +static bool ah_mt6(const struct sk_buff *skb, + const struct xt_action_param *par) { struct ip_auth_hdr _ah; const struct ip_auth_hdr *ah; diff --git a/net/ipv6/netfilter/ip6t_eui64.c b/net/ipv6/netfilter/ip6t_eui64.c index ca287f6d2bc..2fd2be1795e 100644 --- a/net/ipv6/netfilter/ip6t_eui64.c +++ b/net/ipv6/netfilter/ip6t_eui64.c @@ -20,7 +20,7 @@ MODULE_LICENSE("GPL"); MODULE_AUTHOR("Andras Kis-Szabo "); static bool -eui64_mt6(const struct sk_buff *skb, const struct xt_match_param *par) +eui64_mt6(const struct sk_buff *skb, const struct xt_action_param *par) { unsigned char eui64[8]; diff --git a/net/ipv6/netfilter/ip6t_frag.c b/net/ipv6/netfilter/ip6t_frag.c index a5daf0ffb4e..8401aa82ea0 100644 --- a/net/ipv6/netfilter/ip6t_frag.c +++ b/net/ipv6/netfilter/ip6t_frag.c @@ -35,7 +35,7 @@ id_match(u_int32_t min, u_int32_t max, u_int32_t id, bool invert) } static bool -frag_mt6(const struct sk_buff *skb, const struct xt_match_param *par) +frag_mt6(const struct sk_buff *skb, const struct xt_action_param *par) { struct frag_hdr _frag; const struct frag_hdr *fh; diff --git a/net/ipv6/netfilter/ip6t_hbh.c b/net/ipv6/netfilter/ip6t_hbh.c index e424e7c8f82..d19d5cf47a3 100644 --- a/net/ipv6/netfilter/ip6t_hbh.c +++ b/net/ipv6/netfilter/ip6t_hbh.c @@ -44,7 +44,7 @@ MODULE_ALIAS("ip6t_dst"); static struct xt_match hbh_mt6_reg[] __read_mostly; static bool -hbh_mt6(const struct sk_buff *skb, const struct xt_match_param *par) +hbh_mt6(const struct sk_buff *skb, const struct xt_action_param *par) { struct ipv6_opt_hdr _optsh; const struct ipv6_opt_hdr *oh; diff --git a/net/ipv6/netfilter/ip6t_ipv6header.c b/net/ipv6/netfilter/ip6t_ipv6header.c index 46fbabb493f..8e88bb8311d 100644 --- a/net/ipv6/netfilter/ip6t_ipv6header.c +++ b/net/ipv6/netfilter/ip6t_ipv6header.c @@ -27,7 +27,7 @@ MODULE_DESCRIPTION("Xtables: IPv6 header types match"); MODULE_AUTHOR("Andras Kis-Szabo "); static bool -ipv6header_mt6(const struct sk_buff *skb, const struct xt_match_param *par) +ipv6header_mt6(const struct sk_buff *skb, const struct xt_action_param *par) { const struct ip6t_ipv6header_info *info = par->matchinfo; unsigned int temp; diff --git a/net/ipv6/netfilter/ip6t_mh.c b/net/ipv6/netfilter/ip6t_mh.c index c9f443e0138..4a60788873f 100644 --- a/net/ipv6/netfilter/ip6t_mh.c +++ b/net/ipv6/netfilter/ip6t_mh.c @@ -32,7 +32,8 @@ type_match(u_int8_t min, u_int8_t max, u_int8_t type, bool invert) return (type >= min && type <= max) ^ invert; } -static bool mh_mt6(const struct sk_buff *skb, const struct xt_match_param *par) +static bool mh_mt6(const struct sk_buff *skb, + const struct xt_action_param *par) { struct ip6_mh _mh; const struct ip6_mh *mh; diff --git a/net/ipv6/netfilter/ip6t_rt.c b/net/ipv6/netfilter/ip6t_rt.c index 09322720d2a..793c2720088 100644 --- a/net/ipv6/netfilter/ip6t_rt.c +++ b/net/ipv6/netfilter/ip6t_rt.c @@ -36,7 +36,8 @@ segsleft_match(u_int32_t min, u_int32_t max, u_int32_t id, bool invert) return r; } -static bool rt_mt6(const struct sk_buff *skb, const struct xt_match_param *par) +static bool rt_mt6(const struct sk_buff *skb, + const struct xt_action_param *par) { struct ipv6_rt_hdr _route; const struct ipv6_rt_hdr *rh; diff --git a/net/netfilter/xt_CLASSIFY.c b/net/netfilter/xt_CLASSIFY.c index 011bc80dd2a..c2c0e4abeb9 100644 --- a/net/netfilter/xt_CLASSIFY.c +++ b/net/netfilter/xt_CLASSIFY.c @@ -27,7 +27,7 @@ MODULE_ALIAS("ipt_CLASSIFY"); MODULE_ALIAS("ip6t_CLASSIFY"); static unsigned int -classify_tg(struct sk_buff *skb, const struct xt_target_param *par) +classify_tg(struct sk_buff *skb, const struct xt_action_param *par) { const struct xt_classify_target_info *clinfo = par->targinfo; diff --git a/net/netfilter/xt_CONNSECMARK.c b/net/netfilter/xt_CONNSECMARK.c index e953e302141..e04dc282e3b 100644 --- a/net/netfilter/xt_CONNSECMARK.c +++ b/net/netfilter/xt_CONNSECMARK.c @@ -64,7 +64,7 @@ static void secmark_restore(struct sk_buff *skb) } static unsigned int -connsecmark_tg(struct sk_buff *skb, const struct xt_target_param *par) +connsecmark_tg(struct sk_buff *skb, const struct xt_action_param *par) { const struct xt_connsecmark_target_info *info = par->targinfo; diff --git a/net/netfilter/xt_CT.c b/net/netfilter/xt_CT.c index c8f547829ba..562bf3266e0 100644 --- a/net/netfilter/xt_CT.c +++ b/net/netfilter/xt_CT.c @@ -20,7 +20,7 @@ #include static unsigned int xt_ct_target(struct sk_buff *skb, - const struct xt_target_param *par) + const struct xt_action_param *par) { const struct xt_ct_target_info *info = par->targinfo; struct nf_conn *ct = info->ct; diff --git a/net/netfilter/xt_DSCP.c b/net/netfilter/xt_DSCP.c index 969634f293e..0a229191e55 100644 --- a/net/netfilter/xt_DSCP.c +++ b/net/netfilter/xt_DSCP.c @@ -28,7 +28,7 @@ MODULE_ALIAS("ipt_TOS"); MODULE_ALIAS("ip6t_TOS"); static unsigned int -dscp_tg(struct sk_buff *skb, const struct xt_target_param *par) +dscp_tg(struct sk_buff *skb, const struct xt_action_param *par) { const struct xt_DSCP_info *dinfo = par->targinfo; u_int8_t dscp = ipv4_get_dsfield(ip_hdr(skb)) >> XT_DSCP_SHIFT; @@ -45,7 +45,7 @@ dscp_tg(struct sk_buff *skb, const struct xt_target_param *par) } static unsigned int -dscp_tg6(struct sk_buff *skb, const struct xt_target_param *par) +dscp_tg6(struct sk_buff *skb, const struct xt_action_param *par) { const struct xt_DSCP_info *dinfo = par->targinfo; u_int8_t dscp = ipv6_get_dsfield(ipv6_hdr(skb)) >> XT_DSCP_SHIFT; @@ -72,7 +72,7 @@ static int dscp_tg_check(const struct xt_tgchk_param *par) } static unsigned int -tos_tg(struct sk_buff *skb, const struct xt_target_param *par) +tos_tg(struct sk_buff *skb, const struct xt_action_param *par) { const struct xt_tos_target_info *info = par->targinfo; struct iphdr *iph = ip_hdr(skb); @@ -92,7 +92,7 @@ tos_tg(struct sk_buff *skb, const struct xt_target_param *par) } static unsigned int -tos_tg6(struct sk_buff *skb, const struct xt_target_param *par) +tos_tg6(struct sk_buff *skb, const struct xt_action_param *par) { const struct xt_tos_target_info *info = par->targinfo; struct ipv6hdr *iph = ipv6_hdr(skb); diff --git a/net/netfilter/xt_HL.c b/net/netfilter/xt_HL.c index 77b99f73271..95b084800fc 100644 --- a/net/netfilter/xt_HL.c +++ b/net/netfilter/xt_HL.c @@ -26,7 +26,7 @@ MODULE_DESCRIPTION("Xtables: Hoplimit/TTL Limit field modification target"); MODULE_LICENSE("GPL"); static unsigned int -ttl_tg(struct sk_buff *skb, const struct xt_target_param *par) +ttl_tg(struct sk_buff *skb, const struct xt_action_param *par) { struct iphdr *iph; const struct ipt_TTL_info *info = par->targinfo; @@ -66,7 +66,7 @@ ttl_tg(struct sk_buff *skb, const struct xt_target_param *par) } static unsigned int -hl_tg6(struct sk_buff *skb, const struct xt_target_param *par) +hl_tg6(struct sk_buff *skb, const struct xt_action_param *par) { struct ipv6hdr *ip6h; const struct ip6t_HL_info *info = par->targinfo; diff --git a/net/netfilter/xt_LED.c b/net/netfilter/xt_LED.c index ab6f8ff9c9a..a4140509eea 100644 --- a/net/netfilter/xt_LED.c +++ b/net/netfilter/xt_LED.c @@ -49,7 +49,7 @@ struct xt_led_info_internal { }; static unsigned int -led_tg(struct sk_buff *skb, const struct xt_target_param *par) +led_tg(struct sk_buff *skb, const struct xt_action_param *par) { const struct xt_led_info *ledinfo = par->targinfo; struct xt_led_info_internal *ledinternal = ledinfo->internal_data; diff --git a/net/netfilter/xt_NFLOG.c b/net/netfilter/xt_NFLOG.c index 42dd8747b42..a17dd0f589b 100644 --- a/net/netfilter/xt_NFLOG.c +++ b/net/netfilter/xt_NFLOG.c @@ -22,7 +22,7 @@ MODULE_ALIAS("ipt_NFLOG"); MODULE_ALIAS("ip6t_NFLOG"); static unsigned int -nflog_tg(struct sk_buff *skb, const struct xt_target_param *par) +nflog_tg(struct sk_buff *skb, const struct xt_action_param *par) { const struct xt_nflog_info *info = par->targinfo; struct nf_loginfo li; diff --git a/net/netfilter/xt_NFQUEUE.c b/net/netfilter/xt_NFQUEUE.c index f9217cb56fe..039cce1bde3 100644 --- a/net/netfilter/xt_NFQUEUE.c +++ b/net/netfilter/xt_NFQUEUE.c @@ -31,7 +31,7 @@ static u32 jhash_initval __read_mostly; static bool rnd_inited __read_mostly; static unsigned int -nfqueue_tg(struct sk_buff *skb, const struct xt_target_param *par) +nfqueue_tg(struct sk_buff *skb, const struct xt_action_param *par) { const struct xt_NFQ_info *tinfo = par->targinfo; @@ -65,7 +65,7 @@ static u32 hash_v6(const struct sk_buff *skb) #endif static unsigned int -nfqueue_tg_v1(struct sk_buff *skb, const struct xt_target_param *par) +nfqueue_tg_v1(struct sk_buff *skb, const struct xt_action_param *par) { const struct xt_NFQ_info_v1 *info = par->targinfo; u32 queue = info->queuenum; diff --git a/net/netfilter/xt_NOTRACK.c b/net/netfilter/xt_NOTRACK.c index e7a0a54fd4e..512b9123252 100644 --- a/net/netfilter/xt_NOTRACK.c +++ b/net/netfilter/xt_NOTRACK.c @@ -13,7 +13,7 @@ MODULE_ALIAS("ipt_NOTRACK"); MODULE_ALIAS("ip6t_NOTRACK"); static unsigned int -notrack_tg(struct sk_buff *skb, const struct xt_target_param *par) +notrack_tg(struct sk_buff *skb, const struct xt_action_param *par) { /* Previously seen (loopback)? Ignore. */ if (skb->nfct != NULL) diff --git a/net/netfilter/xt_RATEEST.c b/net/netfilter/xt_RATEEST.c index a02193f06e3..69c01e10f8a 100644 --- a/net/netfilter/xt_RATEEST.c +++ b/net/netfilter/xt_RATEEST.c @@ -73,7 +73,7 @@ void xt_rateest_put(struct xt_rateest *est) EXPORT_SYMBOL_GPL(xt_rateest_put); static unsigned int -xt_rateest_tg(struct sk_buff *skb, const struct xt_target_param *par) +xt_rateest_tg(struct sk_buff *skb, const struct xt_action_param *par) { const struct xt_rateest_target_info *info = par->targinfo; struct gnet_stats_basic_packed *stats = &info->est->bstats; diff --git a/net/netfilter/xt_SECMARK.c b/net/netfilter/xt_SECMARK.c index a91d4a7d5a2..23b2d6c486b 100644 --- a/net/netfilter/xt_SECMARK.c +++ b/net/netfilter/xt_SECMARK.c @@ -30,7 +30,7 @@ MODULE_ALIAS("ip6t_SECMARK"); static u8 mode; static unsigned int -secmark_tg(struct sk_buff *skb, const struct xt_target_param *par) +secmark_tg(struct sk_buff *skb, const struct xt_action_param *par) { u32 secmark = 0; const struct xt_secmark_target_info *info = par->targinfo; diff --git a/net/netfilter/xt_TCPMSS.c b/net/netfilter/xt_TCPMSS.c index d04606459c9..62ec021fbd5 100644 --- a/net/netfilter/xt_TCPMSS.c +++ b/net/netfilter/xt_TCPMSS.c @@ -172,7 +172,7 @@ static u_int32_t tcpmss_reverse_mtu(const struct sk_buff *skb, } static unsigned int -tcpmss_tg4(struct sk_buff *skb, const struct xt_target_param *par) +tcpmss_tg4(struct sk_buff *skb, const struct xt_action_param *par) { struct iphdr *iph = ip_hdr(skb); __be16 newlen; @@ -195,7 +195,7 @@ tcpmss_tg4(struct sk_buff *skb, const struct xt_target_param *par) #if defined(CONFIG_IP6_NF_IPTABLES) || defined(CONFIG_IP6_NF_IPTABLES_MODULE) static unsigned int -tcpmss_tg6(struct sk_buff *skb, const struct xt_target_param *par) +tcpmss_tg6(struct sk_buff *skb, const struct xt_action_param *par) { struct ipv6hdr *ipv6h = ipv6_hdr(skb); u8 nexthdr; diff --git a/net/netfilter/xt_TCPOPTSTRIP.c b/net/netfilter/xt_TCPOPTSTRIP.c index e8b57609ddc..9dc9ecfdd54 100644 --- a/net/netfilter/xt_TCPOPTSTRIP.c +++ b/net/netfilter/xt_TCPOPTSTRIP.c @@ -74,7 +74,7 @@ tcpoptstrip_mangle_packet(struct sk_buff *skb, } static unsigned int -tcpoptstrip_tg4(struct sk_buff *skb, const struct xt_target_param *par) +tcpoptstrip_tg4(struct sk_buff *skb, const struct xt_action_param *par) { return tcpoptstrip_mangle_packet(skb, par->targinfo, ip_hdrlen(skb), sizeof(struct iphdr) + sizeof(struct tcphdr)); @@ -82,7 +82,7 @@ tcpoptstrip_tg4(struct sk_buff *skb, const struct xt_target_param *par) #if defined(CONFIG_IP6_NF_MANGLE) || defined(CONFIG_IP6_NF_MANGLE_MODULE) static unsigned int -tcpoptstrip_tg6(struct sk_buff *skb, const struct xt_target_param *par) +tcpoptstrip_tg6(struct sk_buff *skb, const struct xt_action_param *par) { struct ipv6hdr *ipv6h = ipv6_hdr(skb); int tcphoff; diff --git a/net/netfilter/xt_TEE.c b/net/netfilter/xt_TEE.c index 49da6c05f4e..d7920d9f49e 100644 --- a/net/netfilter/xt_TEE.c +++ b/net/netfilter/xt_TEE.c @@ -84,7 +84,7 @@ tee_tg_route4(struct sk_buff *skb, const struct xt_tee_tginfo *info) } static unsigned int -tee_tg4(struct sk_buff *skb, const struct xt_target_param *par) +tee_tg4(struct sk_buff *skb, const struct xt_action_param *par) { const struct xt_tee_tginfo *info = par->targinfo; struct iphdr *iph; @@ -165,7 +165,7 @@ tee_tg_route6(struct sk_buff *skb, const struct xt_tee_tginfo *info) } static unsigned int -tee_tg6(struct sk_buff *skb, const struct xt_target_param *par) +tee_tg6(struct sk_buff *skb, const struct xt_action_param *par) { const struct xt_tee_tginfo *info = par->targinfo; diff --git a/net/netfilter/xt_TPROXY.c b/net/netfilter/xt_TPROXY.c index 4f246ddc5c4..e1a0dedac25 100644 --- a/net/netfilter/xt_TPROXY.c +++ b/net/netfilter/xt_TPROXY.c @@ -25,7 +25,7 @@ #include static unsigned int -tproxy_tg(struct sk_buff *skb, const struct xt_target_param *par) +tproxy_tg(struct sk_buff *skb, const struct xt_action_param *par) { const struct iphdr *iph = ip_hdr(skb); const struct xt_tproxy_target_info *tgi = par->targinfo; diff --git a/net/netfilter/xt_TRACE.c b/net/netfilter/xt_TRACE.c index fbb04b86c46..df48967af38 100644 --- a/net/netfilter/xt_TRACE.c +++ b/net/netfilter/xt_TRACE.c @@ -11,7 +11,7 @@ MODULE_ALIAS("ipt_TRACE"); MODULE_ALIAS("ip6t_TRACE"); static unsigned int -trace_tg(struct sk_buff *skb, const struct xt_target_param *par) +trace_tg(struct sk_buff *skb, const struct xt_action_param *par) { skb->nf_trace = 1; return XT_CONTINUE; diff --git a/net/netfilter/xt_cluster.c b/net/netfilter/xt_cluster.c index 6c941e1c6b9..67fc317118c 100644 --- a/net/netfilter/xt_cluster.c +++ b/net/netfilter/xt_cluster.c @@ -86,7 +86,7 @@ xt_cluster_is_multicast_addr(const struct sk_buff *skb, u_int8_t family) } static bool -xt_cluster_mt(const struct sk_buff *skb, const struct xt_match_param *par) +xt_cluster_mt(const struct sk_buff *skb, const struct xt_action_param *par) { struct sk_buff *pskb = (struct sk_buff *)skb; const struct xt_cluster_match_info *info = par->matchinfo; diff --git a/net/netfilter/xt_comment.c b/net/netfilter/xt_comment.c index e82179832ac..1dbb3e13c05 100644 --- a/net/netfilter/xt_comment.c +++ b/net/netfilter/xt_comment.c @@ -16,7 +16,7 @@ MODULE_ALIAS("ipt_comment"); MODULE_ALIAS("ip6t_comment"); static bool -comment_mt(const struct sk_buff *skb, const struct xt_match_param *par) +comment_mt(const struct sk_buff *skb, const struct xt_action_param *par) { /* We always match */ return true; diff --git a/net/netfilter/xt_connbytes.c b/net/netfilter/xt_connbytes.c index ff738a5f963..5e5cf15f011 100644 --- a/net/netfilter/xt_connbytes.c +++ b/net/netfilter/xt_connbytes.c @@ -18,7 +18,7 @@ MODULE_ALIAS("ipt_connbytes"); MODULE_ALIAS("ip6t_connbytes"); static bool -connbytes_mt(const struct sk_buff *skb, const struct xt_match_param *par) +connbytes_mt(const struct sk_buff *skb, const struct xt_action_param *par) { const struct xt_connbytes_info *sinfo = par->matchinfo; const struct nf_conn *ct; diff --git a/net/netfilter/xt_connlimit.c b/net/netfilter/xt_connlimit.c index 326bc1b8168..6eec1913298 100644 --- a/net/netfilter/xt_connlimit.c +++ b/net/netfilter/xt_connlimit.c @@ -173,7 +173,7 @@ static int count_them(struct net *net, } static bool -connlimit_mt(const struct sk_buff *skb, const struct xt_match_param *par) +connlimit_mt(const struct sk_buff *skb, const struct xt_action_param *par) { struct net *net = dev_net(par->in ? par->in : par->out); const struct xt_connlimit_info *info = par->matchinfo; diff --git a/net/netfilter/xt_connmark.c b/net/netfilter/xt_connmark.c index ae1015484ae..b43cfc7f1bb 100644 --- a/net/netfilter/xt_connmark.c +++ b/net/netfilter/xt_connmark.c @@ -37,7 +37,7 @@ MODULE_ALIAS("ipt_connmark"); MODULE_ALIAS("ip6t_connmark"); static unsigned int -connmark_tg(struct sk_buff *skb, const struct xt_target_param *par) +connmark_tg(struct sk_buff *skb, const struct xt_action_param *par) { const struct xt_connmark_tginfo1 *info = par->targinfo; enum ip_conntrack_info ctinfo; @@ -91,7 +91,7 @@ static void connmark_tg_destroy(const struct xt_tgdtor_param *par) } static bool -connmark_mt(const struct sk_buff *skb, const struct xt_match_param *par) +connmark_mt(const struct sk_buff *skb, const struct xt_action_param *par) { const struct xt_connmark_mtinfo1 *info = par->matchinfo; enum ip_conntrack_info ctinfo; diff --git a/net/netfilter/xt_conntrack.c b/net/netfilter/xt_conntrack.c index 3348706ce56..14a96f48001 100644 --- a/net/netfilter/xt_conntrack.c +++ b/net/netfilter/xt_conntrack.c @@ -113,7 +113,7 @@ ct_proto_port_check(const struct xt_conntrack_mtinfo2 *info, } static bool -conntrack_mt(const struct sk_buff *skb, const struct xt_match_param *par, +conntrack_mt(const struct sk_buff *skb, const struct xt_action_param *par, u16 state_mask, u16 status_mask) { const struct xt_conntrack_mtinfo2 *info = par->matchinfo; @@ -191,7 +191,7 @@ conntrack_mt(const struct sk_buff *skb, const struct xt_match_param *par, } static bool -conntrack_mt_v1(const struct sk_buff *skb, const struct xt_match_param *par) +conntrack_mt_v1(const struct sk_buff *skb, const struct xt_action_param *par) { const struct xt_conntrack_mtinfo1 *info = par->matchinfo; @@ -199,7 +199,7 @@ conntrack_mt_v1(const struct sk_buff *skb, const struct xt_match_param *par) } static bool -conntrack_mt_v2(const struct sk_buff *skb, const struct xt_match_param *par) +conntrack_mt_v2(const struct sk_buff *skb, const struct xt_action_param *par) { const struct xt_conntrack_mtinfo2 *info = par->matchinfo; diff --git a/net/netfilter/xt_dccp.c b/net/netfilter/xt_dccp.c index 0d260aec487..7a4d4e8edc1 100644 --- a/net/netfilter/xt_dccp.c +++ b/net/netfilter/xt_dccp.c @@ -96,7 +96,7 @@ match_option(u_int8_t option, const struct sk_buff *skb, unsigned int protoff, } static bool -dccp_mt(const struct sk_buff *skb, const struct xt_match_param *par) +dccp_mt(const struct sk_buff *skb, const struct xt_action_param *par) { const struct xt_dccp_info *info = par->matchinfo; const struct dccp_hdr *dh; diff --git a/net/netfilter/xt_dscp.c b/net/netfilter/xt_dscp.c index 9db51fddbdb..2133b509d15 100644 --- a/net/netfilter/xt_dscp.c +++ b/net/netfilter/xt_dscp.c @@ -25,7 +25,7 @@ MODULE_ALIAS("ipt_tos"); MODULE_ALIAS("ip6t_tos"); static bool -dscp_mt(const struct sk_buff *skb, const struct xt_match_param *par) +dscp_mt(const struct sk_buff *skb, const struct xt_action_param *par) { const struct xt_dscp_info *info = par->matchinfo; u_int8_t dscp = ipv4_get_dsfield(ip_hdr(skb)) >> XT_DSCP_SHIFT; @@ -34,7 +34,7 @@ dscp_mt(const struct sk_buff *skb, const struct xt_match_param *par) } static bool -dscp_mt6(const struct sk_buff *skb, const struct xt_match_param *par) +dscp_mt6(const struct sk_buff *skb, const struct xt_action_param *par) { const struct xt_dscp_info *info = par->matchinfo; u_int8_t dscp = ipv6_get_dsfield(ipv6_hdr(skb)) >> XT_DSCP_SHIFT; @@ -54,7 +54,8 @@ static int dscp_mt_check(const struct xt_mtchk_param *par) return 0; } -static bool tos_mt(const struct sk_buff *skb, const struct xt_match_param *par) +static bool tos_mt(const struct sk_buff *skb, + const struct xt_action_param *par) { const struct xt_tos_match_info *info = par->matchinfo; diff --git a/net/netfilter/xt_esp.c b/net/netfilter/xt_esp.c index 143bfdc8e38..39caafff948 100644 --- a/net/netfilter/xt_esp.c +++ b/net/netfilter/xt_esp.c @@ -36,7 +36,8 @@ spi_match(u_int32_t min, u_int32_t max, u_int32_t spi, bool invert) return r; } -static bool esp_mt(const struct sk_buff *skb, const struct xt_match_param *par) +static bool esp_mt(const struct sk_buff *skb, + const struct xt_action_param *par) { const struct ip_esp_hdr *eh; struct ip_esp_hdr _esp; diff --git a/net/netfilter/xt_hashlimit.c b/net/netfilter/xt_hashlimit.c index 0c366d387c8..700c21e0804 100644 --- a/net/netfilter/xt_hashlimit.c +++ b/net/netfilter/xt_hashlimit.c @@ -516,7 +516,7 @@ hashlimit_init_dst(const struct xt_hashlimit_htable *hinfo, } static bool -hashlimit_mt(const struct sk_buff *skb, const struct xt_match_param *par) +hashlimit_mt(const struct sk_buff *skb, const struct xt_action_param *par) { const struct xt_hashlimit_mtinfo1 *info = par->matchinfo; struct xt_hashlimit_htable *hinfo = info->hinfo; diff --git a/net/netfilter/xt_helper.c b/net/netfilter/xt_helper.c index b8b3e13dc71..e941bd26a51 100644 --- a/net/netfilter/xt_helper.c +++ b/net/netfilter/xt_helper.c @@ -24,7 +24,7 @@ MODULE_ALIAS("ip6t_helper"); static bool -helper_mt(const struct sk_buff *skb, const struct xt_match_param *par) +helper_mt(const struct sk_buff *skb, const struct xt_action_param *par) { const struct xt_helper_info *info = par->matchinfo; const struct nf_conn *ct; diff --git a/net/netfilter/xt_hl.c b/net/netfilter/xt_hl.c index be53f729962..335c34a4fd1 100644 --- a/net/netfilter/xt_hl.c +++ b/net/netfilter/xt_hl.c @@ -25,7 +25,8 @@ MODULE_LICENSE("GPL"); MODULE_ALIAS("ipt_ttl"); MODULE_ALIAS("ip6t_hl"); -static bool ttl_mt(const struct sk_buff *skb, const struct xt_match_param *par) +static bool ttl_mt(const struct sk_buff *skb, + const struct xt_action_param *par) { const struct ipt_ttl_info *info = par->matchinfo; const u8 ttl = ip_hdr(skb)->ttl; @@ -44,7 +45,8 @@ static bool ttl_mt(const struct sk_buff *skb, const struct xt_match_param *par) return false; } -static bool hl_mt6(const struct sk_buff *skb, const struct xt_match_param *par) +static bool hl_mt6(const struct sk_buff *skb, + const struct xt_action_param *par) { const struct ip6t_hl_info *info = par->matchinfo; const struct ipv6hdr *ip6h = ipv6_hdr(skb); diff --git a/net/netfilter/xt_iprange.c b/net/netfilter/xt_iprange.c index 8471d9715bd..9578860a921 100644 --- a/net/netfilter/xt_iprange.c +++ b/net/netfilter/xt_iprange.c @@ -17,7 +17,7 @@ #include static bool -iprange_mt4(const struct sk_buff *skb, const struct xt_match_param *par) +iprange_mt4(const struct sk_buff *skb, const struct xt_action_param *par) { const struct xt_iprange_mtinfo *info = par->matchinfo; const struct iphdr *iph = ip_hdr(skb); @@ -68,7 +68,7 @@ iprange_ipv6_sub(const struct in6_addr *a, const struct in6_addr *b) } static bool -iprange_mt6(const struct sk_buff *skb, const struct xt_match_param *par) +iprange_mt6(const struct sk_buff *skb, const struct xt_action_param *par) { const struct xt_iprange_mtinfo *info = par->matchinfo; const struct ipv6hdr *iph = ipv6_hdr(skb); diff --git a/net/netfilter/xt_length.c b/net/netfilter/xt_length.c index c4871ca6c86..842149b4122 100644 --- a/net/netfilter/xt_length.c +++ b/net/netfilter/xt_length.c @@ -21,7 +21,7 @@ MODULE_ALIAS("ipt_length"); MODULE_ALIAS("ip6t_length"); static bool -length_mt(const struct sk_buff *skb, const struct xt_match_param *par) +length_mt(const struct sk_buff *skb, const struct xt_action_param *par) { const struct xt_length_info *info = par->matchinfo; u_int16_t pktlen = ntohs(ip_hdr(skb)->tot_len); @@ -30,7 +30,7 @@ length_mt(const struct sk_buff *skb, const struct xt_match_param *par) } static bool -length_mt6(const struct sk_buff *skb, const struct xt_match_param *par) +length_mt6(const struct sk_buff *skb, const struct xt_action_param *par) { const struct xt_length_info *info = par->matchinfo; const u_int16_t pktlen = ntohs(ipv6_hdr(skb)->payload_len) + diff --git a/net/netfilter/xt_limit.c b/net/netfilter/xt_limit.c index 88215dca19c..7dcfe8602c8 100644 --- a/net/netfilter/xt_limit.c +++ b/net/netfilter/xt_limit.c @@ -65,7 +65,7 @@ static DEFINE_SPINLOCK(limit_lock); #define CREDITS_PER_JIFFY POW2_BELOW32(MAX_CPJ) static bool -limit_mt(const struct sk_buff *skb, const struct xt_match_param *par) +limit_mt(const struct sk_buff *skb, const struct xt_action_param *par) { const struct xt_rateinfo *r = par->matchinfo; struct xt_limit_priv *priv = r->master; diff --git a/net/netfilter/xt_mac.c b/net/netfilter/xt_mac.c index b971ce93773..36c49644ce3 100644 --- a/net/netfilter/xt_mac.c +++ b/net/netfilter/xt_mac.c @@ -25,7 +25,8 @@ MODULE_DESCRIPTION("Xtables: MAC address match"); MODULE_ALIAS("ipt_mac"); MODULE_ALIAS("ip6t_mac"); -static bool mac_mt(const struct sk_buff *skb, const struct xt_match_param *par) +static bool mac_mt(const struct sk_buff *skb, + const struct xt_action_param *par) { const struct xt_mac_info *info = par->matchinfo; bool ret; diff --git a/net/netfilter/xt_mark.c b/net/netfilter/xt_mark.c index 035c468a004..3c8347076d5 100644 --- a/net/netfilter/xt_mark.c +++ b/net/netfilter/xt_mark.c @@ -25,7 +25,7 @@ MODULE_ALIAS("ipt_MARK"); MODULE_ALIAS("ip6t_MARK"); static unsigned int -mark_tg(struct sk_buff *skb, const struct xt_target_param *par) +mark_tg(struct sk_buff *skb, const struct xt_action_param *par) { const struct xt_mark_tginfo2 *info = par->targinfo; @@ -34,7 +34,7 @@ mark_tg(struct sk_buff *skb, const struct xt_target_param *par) } static bool -mark_mt(const struct sk_buff *skb, const struct xt_match_param *par) +mark_mt(const struct sk_buff *skb, const struct xt_action_param *par) { const struct xt_mark_mtinfo1 *info = par->matchinfo; diff --git a/net/netfilter/xt_multiport.c b/net/netfilter/xt_multiport.c index b21f9043224..3140fd4049f 100644 --- a/net/netfilter/xt_multiport.c +++ b/net/netfilter/xt_multiport.c @@ -72,7 +72,7 @@ ports_match_v1(const struct xt_multiport_v1 *minfo, } static bool -multiport_mt(const struct sk_buff *skb, const struct xt_match_param *par) +multiport_mt(const struct sk_buff *skb, const struct xt_action_param *par) { const __be16 *pptr; __be16 _ports[2]; diff --git a/net/netfilter/xt_osf.c b/net/netfilter/xt_osf.c index 8dcde13a078..37aa55860a9 100644 --- a/net/netfilter/xt_osf.c +++ b/net/netfilter/xt_osf.c @@ -193,8 +193,8 @@ static inline int xt_osf_ttl(const struct sk_buff *skb, const struct xt_osf_info return ip->ttl == f_ttl; } -static bool xt_osf_match_packet(const struct sk_buff *skb, - const struct xt_match_param *p) +static bool +xt_osf_match_packet(const struct sk_buff *skb, const struct xt_action_param *p) { const struct xt_osf_info *info = p->matchinfo; const struct iphdr *ip = ip_hdr(skb); diff --git a/net/netfilter/xt_owner.c b/net/netfilter/xt_owner.c index d24c76dffee..3dd1391d385 100644 --- a/net/netfilter/xt_owner.c +++ b/net/netfilter/xt_owner.c @@ -18,7 +18,7 @@ #include static bool -owner_mt(const struct sk_buff *skb, const struct xt_match_param *par) +owner_mt(const struct sk_buff *skb, const struct xt_action_param *par) { const struct xt_owner_match_info *info = par->matchinfo; const struct file *filp; diff --git a/net/netfilter/xt_physdev.c b/net/netfilter/xt_physdev.c index d0bdf3dd4d2..298cd290b06 100644 --- a/net/netfilter/xt_physdev.c +++ b/net/netfilter/xt_physdev.c @@ -22,7 +22,7 @@ MODULE_ALIAS("ip6t_physdev"); static bool -physdev_mt(const struct sk_buff *skb, const struct xt_match_param *par) +physdev_mt(const struct sk_buff *skb, const struct xt_action_param *par) { static const char nulldevname[IFNAMSIZ] __attribute__((aligned(sizeof(long)))); const struct xt_physdev_info *info = par->matchinfo; diff --git a/net/netfilter/xt_pkttype.c b/net/netfilter/xt_pkttype.c index 69da1d3a1d8..d95f2149df9 100644 --- a/net/netfilter/xt_pkttype.c +++ b/net/netfilter/xt_pkttype.c @@ -23,7 +23,7 @@ MODULE_ALIAS("ipt_pkttype"); MODULE_ALIAS("ip6t_pkttype"); static bool -pkttype_mt(const struct sk_buff *skb, const struct xt_match_param *par) +pkttype_mt(const struct sk_buff *skb, const struct xt_action_param *par) { const struct xt_pkttype_info *info = par->matchinfo; u_int8_t type; diff --git a/net/netfilter/xt_policy.c b/net/netfilter/xt_policy.c index 1fa239c1fb9..1abfc7ad427 100644 --- a/net/netfilter/xt_policy.c +++ b/net/netfilter/xt_policy.c @@ -110,7 +110,7 @@ match_policy_out(const struct sk_buff *skb, const struct xt_policy_info *info, } static bool -policy_mt(const struct sk_buff *skb, const struct xt_match_param *par) +policy_mt(const struct sk_buff *skb, const struct xt_action_param *par) { const struct xt_policy_info *info = par->matchinfo; int ret; diff --git a/net/netfilter/xt_quota.c b/net/netfilter/xt_quota.c index 7c95d69f6f0..e79e07c75da 100644 --- a/net/netfilter/xt_quota.c +++ b/net/netfilter/xt_quota.c @@ -23,7 +23,7 @@ MODULE_ALIAS("ip6t_quota"); static DEFINE_SPINLOCK(quota_lock); static bool -quota_mt(const struct sk_buff *skb, const struct xt_match_param *par) +quota_mt(const struct sk_buff *skb, const struct xt_action_param *par) { struct xt_quota_info *q = (void *)par->matchinfo; struct xt_quota_priv *priv = q->master; diff --git a/net/netfilter/xt_rateest.c b/net/netfilter/xt_rateest.c index 23805f8a444..53f7a4d12e1 100644 --- a/net/netfilter/xt_rateest.c +++ b/net/netfilter/xt_rateest.c @@ -15,7 +15,7 @@ static bool -xt_rateest_mt(const struct sk_buff *skb, const struct xt_match_param *par) +xt_rateest_mt(const struct sk_buff *skb, const struct xt_action_param *par) { const struct xt_rateest_match_info *info = par->matchinfo; struct gnet_stats_rate_est *r; diff --git a/net/netfilter/xt_realm.c b/net/netfilter/xt_realm.c index 484d1689bfd..b063c783901 100644 --- a/net/netfilter/xt_realm.c +++ b/net/netfilter/xt_realm.c @@ -22,7 +22,7 @@ MODULE_DESCRIPTION("Xtables: Routing realm match"); MODULE_ALIAS("ipt_realm"); static bool -realm_mt(const struct sk_buff *skb, const struct xt_match_param *par) +realm_mt(const struct sk_buff *skb, const struct xt_action_param *par) { const struct xt_realm_info *info = par->matchinfo; const struct dst_entry *dst = skb_dst(skb); diff --git a/net/netfilter/xt_recent.c b/net/netfilter/xt_recent.c index b88d63b9c76..503b7f199f1 100644 --- a/net/netfilter/xt_recent.c +++ b/net/netfilter/xt_recent.c @@ -224,7 +224,7 @@ static void recent_table_flush(struct recent_table *t) } static bool -recent_mt(const struct sk_buff *skb, const struct xt_match_param *par) +recent_mt(const struct sk_buff *skb, const struct xt_action_param *par) { struct net *net = dev_net(par->in ? par->in : par->out); struct recent_net *recent_net = recent_pernet(net); diff --git a/net/netfilter/xt_sctp.c b/net/netfilter/xt_sctp.c index c3694df5467..da4c3cb3140 100644 --- a/net/netfilter/xt_sctp.c +++ b/net/netfilter/xt_sctp.c @@ -114,7 +114,7 @@ match_packet(const struct sk_buff *skb, } static bool -sctp_mt(const struct sk_buff *skb, const struct xt_match_param *par) +sctp_mt(const struct sk_buff *skb, const struct xt_action_param *par) { const struct xt_sctp_info *info = par->matchinfo; const sctp_sctphdr_t *sh; diff --git a/net/netfilter/xt_socket.c b/net/netfilter/xt_socket.c index a9b16867e1f..2665e32d5db 100644 --- a/net/netfilter/xt_socket.c +++ b/net/netfilter/xt_socket.c @@ -88,7 +88,7 @@ extract_icmp_fields(const struct sk_buff *skb, static bool -socket_match(const struct sk_buff *skb, const struct xt_match_param *par, +socket_match(const struct sk_buff *skb, const struct xt_action_param *par, const struct xt_socket_mtinfo1 *info) { const struct iphdr *iph = ip_hdr(skb); @@ -174,13 +174,13 @@ socket_match(const struct sk_buff *skb, const struct xt_match_param *par, } static bool -socket_mt_v0(const struct sk_buff *skb, const struct xt_match_param *par) +socket_mt_v0(const struct sk_buff *skb, const struct xt_action_param *par) { return socket_match(skb, par, NULL); } static bool -socket_mt_v1(const struct sk_buff *skb, const struct xt_match_param *par) +socket_mt_v1(const struct sk_buff *skb, const struct xt_action_param *par) { return socket_match(skb, par, par->matchinfo); } diff --git a/net/netfilter/xt_state.c b/net/netfilter/xt_state.c index bb1271852d5..344cca66109 100644 --- a/net/netfilter/xt_state.c +++ b/net/netfilter/xt_state.c @@ -21,7 +21,7 @@ MODULE_ALIAS("ipt_state"); MODULE_ALIAS("ip6t_state"); static bool -state_mt(const struct sk_buff *skb, const struct xt_match_param *par) +state_mt(const struct sk_buff *skb, const struct xt_action_param *par) { const struct xt_state_info *sinfo = par->matchinfo; enum ip_conntrack_info ctinfo; diff --git a/net/netfilter/xt_statistic.c b/net/netfilter/xt_statistic.c index 5aeca1d023d..ee4540a2cf3 100644 --- a/net/netfilter/xt_statistic.c +++ b/net/netfilter/xt_statistic.c @@ -30,7 +30,7 @@ MODULE_ALIAS("ip6t_statistic"); static DEFINE_SPINLOCK(nth_lock); static bool -statistic_mt(const struct sk_buff *skb, const struct xt_match_param *par) +statistic_mt(const struct sk_buff *skb, const struct xt_action_param *par) { const struct xt_statistic_info *info = par->matchinfo; bool ret = info->flags & XT_STATISTIC_INVERT; diff --git a/net/netfilter/xt_string.c b/net/netfilter/xt_string.c index f6d5112175e..fd5dc5016a1 100644 --- a/net/netfilter/xt_string.c +++ b/net/netfilter/xt_string.c @@ -23,7 +23,7 @@ MODULE_ALIAS("ipt_string"); MODULE_ALIAS("ip6t_string"); static bool -string_mt(const struct sk_buff *skb, const struct xt_match_param *par) +string_mt(const struct sk_buff *skb, const struct xt_action_param *par) { const struct xt_string_info *conf = par->matchinfo; struct ts_state state; diff --git a/net/netfilter/xt_tcpmss.c b/net/netfilter/xt_tcpmss.c index 4809b34b10f..5c8a7b435d6 100644 --- a/net/netfilter/xt_tcpmss.c +++ b/net/netfilter/xt_tcpmss.c @@ -25,7 +25,7 @@ MODULE_ALIAS("ipt_tcpmss"); MODULE_ALIAS("ip6t_tcpmss"); static bool -tcpmss_mt(const struct sk_buff *skb, const struct xt_match_param *par) +tcpmss_mt(const struct sk_buff *skb, const struct xt_action_param *par) { const struct xt_tcpmss_match_info *info = par->matchinfo; const struct tcphdr *th; diff --git a/net/netfilter/xt_tcpudp.c b/net/netfilter/xt_tcpudp.c index efa2ede24ae..19c31d4c2ba 100644 --- a/net/netfilter/xt_tcpudp.c +++ b/net/netfilter/xt_tcpudp.c @@ -62,7 +62,8 @@ tcp_find_option(u_int8_t option, return invert; } -static bool tcp_mt(const struct sk_buff *skb, const struct xt_match_param *par) +static bool tcp_mt(const struct sk_buff *skb, + const struct xt_action_param *par) { const struct tcphdr *th; struct tcphdr _tcph; @@ -128,7 +129,8 @@ static int tcp_mt_check(const struct xt_mtchk_param *par) return (tcpinfo->invflags & ~XT_TCP_INV_MASK) ? -EINVAL : 0; } -static bool udp_mt(const struct sk_buff *skb, const struct xt_match_param *par) +static bool udp_mt(const struct sk_buff *skb, + const struct xt_action_param *par) { const struct udphdr *uh; struct udphdr _udph; diff --git a/net/netfilter/xt_time.c b/net/netfilter/xt_time.c index d8556fdda44..ffdb8fac0be 100644 --- a/net/netfilter/xt_time.c +++ b/net/netfilter/xt_time.c @@ -152,7 +152,7 @@ static void localtime_3(struct xtm *r, time_t time) } static bool -time_mt(const struct sk_buff *skb, const struct xt_match_param *par) +time_mt(const struct sk_buff *skb, const struct xt_action_param *par) { const struct xt_time_info *info = par->matchinfo; unsigned int packet_time; diff --git a/net/netfilter/xt_u32.c b/net/netfilter/xt_u32.c index d7c05f03a7e..f62797b1ddb 100644 --- a/net/netfilter/xt_u32.c +++ b/net/netfilter/xt_u32.c @@ -86,7 +86,8 @@ static bool u32_match_it(const struct xt_u32 *data, return true; } -static bool u32_mt(const struct sk_buff *skb, const struct xt_match_param *par) +static bool u32_mt(const struct sk_buff *skb, + const struct xt_action_param *par) { const struct xt_u32 *data = par->matchinfo; bool ret; diff --git a/net/sched/act_ipt.c b/net/sched/act_ipt.c index 03f80a0fa16..1f9595467c1 100644 --- a/net/sched/act_ipt.c +++ b/net/sched/act_ipt.c @@ -199,7 +199,7 @@ static int tcf_ipt(struct sk_buff *skb, struct tc_action *a, { int ret = 0, result = 0; struct tcf_ipt *ipt = a->priv; - struct xt_target_param par; + struct xt_action_param par; if (skb_cloned(skb)) { if (pskb_expand_head(skb, 0, 0, GFP_ATOMIC)) -- cgit v1.2.3 From 62fc8051083a334578c3f4b3488808f210b4565f Mon Sep 17 00:00:00 2001 From: Jan Engelhardt Date: Tue, 7 Jul 2009 20:42:08 +0200 Subject: netfilter: xtables: deconstify struct xt_action_param for matches In future, layer-3 matches will be an xt module of their own, and need to set the fragoff and thoff fields. Adding more pointers would needlessy increase memory requirements (esp. so for 64-bit, where pointers are wider). Signed-off-by: Jan Engelhardt --- include/linux/netfilter/x_tables.h | 2 +- net/bridge/netfilter/ebt_802_3.c | 2 +- net/bridge/netfilter/ebt_among.c | 2 +- net/bridge/netfilter/ebt_arp.c | 2 +- net/bridge/netfilter/ebt_ip.c | 2 +- net/bridge/netfilter/ebt_ip6.c | 2 +- net/bridge/netfilter/ebt_limit.c | 2 +- net/bridge/netfilter/ebt_mark_m.c | 2 +- net/bridge/netfilter/ebt_pkttype.c | 2 +- net/bridge/netfilter/ebt_stp.c | 2 +- net/bridge/netfilter/ebt_vlan.c | 2 +- net/ipv4/netfilter/ip_tables.c | 2 +- net/ipv4/netfilter/ipt_addrtype.c | 4 ++-- net/ipv4/netfilter/ipt_ah.c | 2 +- net/ipv4/netfilter/ipt_ecn.c | 3 +-- net/ipv6/netfilter/ip6_tables.c | 2 +- net/ipv6/netfilter/ip6t_ah.c | 3 +-- net/ipv6/netfilter/ip6t_eui64.c | 2 +- net/ipv6/netfilter/ip6t_frag.c | 2 +- net/ipv6/netfilter/ip6t_hbh.c | 2 +- net/ipv6/netfilter/ip6t_ipv6header.c | 2 +- net/ipv6/netfilter/ip6t_mh.c | 3 +-- net/ipv6/netfilter/ip6t_rt.c | 3 +-- net/netfilter/xt_cluster.c | 2 +- net/netfilter/xt_comment.c | 2 +- net/netfilter/xt_connbytes.c | 2 +- net/netfilter/xt_connlimit.c | 2 +- net/netfilter/xt_connmark.c | 2 +- net/netfilter/xt_conntrack.c | 6 +++--- net/netfilter/xt_dccp.c | 2 +- net/netfilter/xt_dscp.c | 7 +++---- net/netfilter/xt_esp.c | 3 +-- net/netfilter/xt_hashlimit.c | 2 +- net/netfilter/xt_helper.c | 2 +- net/netfilter/xt_hl.c | 6 ++---- net/netfilter/xt_iprange.c | 4 ++-- net/netfilter/xt_length.c | 4 ++-- net/netfilter/xt_limit.c | 2 +- net/netfilter/xt_mac.c | 3 +-- net/netfilter/xt_mark.c | 2 +- net/netfilter/xt_multiport.c | 2 +- net/netfilter/xt_osf.c | 2 +- net/netfilter/xt_owner.c | 2 +- net/netfilter/xt_physdev.c | 2 +- net/netfilter/xt_pkttype.c | 2 +- net/netfilter/xt_policy.c | 2 +- net/netfilter/xt_quota.c | 2 +- net/netfilter/xt_rateest.c | 2 +- net/netfilter/xt_realm.c | 2 +- net/netfilter/xt_recent.c | 2 +- net/netfilter/xt_sctp.c | 2 +- net/netfilter/xt_socket.c | 6 +++--- net/netfilter/xt_state.c | 2 +- net/netfilter/xt_statistic.c | 2 +- net/netfilter/xt_string.c | 2 +- net/netfilter/xt_tcpmss.c | 2 +- net/netfilter/xt_tcpudp.c | 6 ++---- net/netfilter/xt_time.c | 2 +- net/netfilter/xt_u32.c | 3 +-- 59 files changed, 70 insertions(+), 82 deletions(-) (limited to 'include/linux') diff --git a/include/linux/netfilter/x_tables.h b/include/linux/netfilter/x_tables.h index 6d1f9a63805..40c6a8d2a9e 100644 --- a/include/linux/netfilter/x_tables.h +++ b/include/linux/netfilter/x_tables.h @@ -288,7 +288,7 @@ struct xt_match { non-linear skb, using skb_header_pointer and skb_ip_make_writable. */ bool (*match)(const struct sk_buff *skb, - const struct xt_action_param *); + struct xt_action_param *); /* Called when user tries to insert an entry of this type. */ int (*checkentry)(const struct xt_mtchk_param *); diff --git a/net/bridge/netfilter/ebt_802_3.c b/net/bridge/netfilter/ebt_802_3.c index 4b0df00c82e..2a449b7ab8f 100644 --- a/net/bridge/netfilter/ebt_802_3.c +++ b/net/bridge/netfilter/ebt_802_3.c @@ -13,7 +13,7 @@ #include static bool -ebt_802_3_mt(const struct sk_buff *skb, const struct xt_action_param *par) +ebt_802_3_mt(const struct sk_buff *skb, struct xt_action_param *par) { const struct ebt_802_3_info *info = par->matchinfo; const struct ebt_802_3_hdr *hdr = ebt_802_3_hdr(skb); diff --git a/net/bridge/netfilter/ebt_among.c b/net/bridge/netfilter/ebt_among.c index c04f9461f73..8b84c581be3 100644 --- a/net/bridge/netfilter/ebt_among.c +++ b/net/bridge/netfilter/ebt_among.c @@ -129,7 +129,7 @@ static int get_ip_src(const struct sk_buff *skb, __be32 *addr) } static bool -ebt_among_mt(const struct sk_buff *skb, const struct xt_action_param *par) +ebt_among_mt(const struct sk_buff *skb, struct xt_action_param *par) { const struct ebt_among_info *info = par->matchinfo; const char *dmac, *smac; diff --git a/net/bridge/netfilter/ebt_arp.c b/net/bridge/netfilter/ebt_arp.c index 6203f4dea2e..cd457b891b2 100644 --- a/net/bridge/netfilter/ebt_arp.c +++ b/net/bridge/netfilter/ebt_arp.c @@ -16,7 +16,7 @@ #include static bool -ebt_arp_mt(const struct sk_buff *skb, const struct xt_action_param *par) +ebt_arp_mt(const struct sk_buff *skb, struct xt_action_param *par) { const struct ebt_arp_info *info = par->matchinfo; const struct arphdr *ah; diff --git a/net/bridge/netfilter/ebt_ip.c b/net/bridge/netfilter/ebt_ip.c index a0cde7442b5..23bca62d58d 100644 --- a/net/bridge/netfilter/ebt_ip.c +++ b/net/bridge/netfilter/ebt_ip.c @@ -25,7 +25,7 @@ struct tcpudphdr { }; static bool -ebt_ip_mt(const struct sk_buff *skb, const struct xt_action_param *par) +ebt_ip_mt(const struct sk_buff *skb, struct xt_action_param *par) { const struct ebt_ip_info *info = par->matchinfo; const struct iphdr *ih; diff --git a/net/bridge/netfilter/ebt_ip6.c b/net/bridge/netfilter/ebt_ip6.c index c451dc2ff82..50a46afc2bc 100644 --- a/net/bridge/netfilter/ebt_ip6.c +++ b/net/bridge/netfilter/ebt_ip6.c @@ -28,7 +28,7 @@ struct tcpudphdr { }; static bool -ebt_ip6_mt(const struct sk_buff *skb, const struct xt_action_param *par) +ebt_ip6_mt(const struct sk_buff *skb, struct xt_action_param *par) { const struct ebt_ip6_info *info = par->matchinfo; const struct ipv6hdr *ih6; diff --git a/net/bridge/netfilter/ebt_limit.c b/net/bridge/netfilter/ebt_limit.c index 760923f0806..517e78befcb 100644 --- a/net/bridge/netfilter/ebt_limit.c +++ b/net/bridge/netfilter/ebt_limit.c @@ -32,7 +32,7 @@ static DEFINE_SPINLOCK(limit_lock); #define CREDITS_PER_JIFFY POW2_BELOW32(MAX_CPJ) static bool -ebt_limit_mt(const struct sk_buff *skb, const struct xt_action_param *par) +ebt_limit_mt(const struct sk_buff *skb, struct xt_action_param *par) { struct ebt_limit_info *info = (void *)par->matchinfo; unsigned long now = jiffies; diff --git a/net/bridge/netfilter/ebt_mark_m.c b/net/bridge/netfilter/ebt_mark_m.c index de901634fa3..d98baefc4c7 100644 --- a/net/bridge/netfilter/ebt_mark_m.c +++ b/net/bridge/netfilter/ebt_mark_m.c @@ -13,7 +13,7 @@ #include static bool -ebt_mark_mt(const struct sk_buff *skb, const struct xt_action_param *par) +ebt_mark_mt(const struct sk_buff *skb, struct xt_action_param *par) { const struct ebt_mark_m_info *info = par->matchinfo; diff --git a/net/bridge/netfilter/ebt_pkttype.c b/net/bridge/netfilter/ebt_pkttype.c index 7ba67c4b677..496a5651530 100644 --- a/net/bridge/netfilter/ebt_pkttype.c +++ b/net/bridge/netfilter/ebt_pkttype.c @@ -13,7 +13,7 @@ #include static bool -ebt_pkttype_mt(const struct sk_buff *skb, const struct xt_action_param *par) +ebt_pkttype_mt(const struct sk_buff *skb, struct xt_action_param *par) { const struct ebt_pkttype_info *info = par->matchinfo; diff --git a/net/bridge/netfilter/ebt_stp.c b/net/bridge/netfilter/ebt_stp.c index 3cd6070a113..5b33a2e634a 100644 --- a/net/bridge/netfilter/ebt_stp.c +++ b/net/bridge/netfilter/ebt_stp.c @@ -120,7 +120,7 @@ static bool ebt_filter_config(const struct ebt_stp_info *info, } static bool -ebt_stp_mt(const struct sk_buff *skb, const struct xt_action_param *par) +ebt_stp_mt(const struct sk_buff *skb, struct xt_action_param *par) { const struct ebt_stp_info *info = par->matchinfo; const struct stp_header *sp; diff --git a/net/bridge/netfilter/ebt_vlan.c b/net/bridge/netfilter/ebt_vlan.c index e4ab62533c7..87b53b3a921 100644 --- a/net/bridge/netfilter/ebt_vlan.c +++ b/net/bridge/netfilter/ebt_vlan.c @@ -36,7 +36,7 @@ MODULE_LICENSE("GPL"); #define EXIT_ON_MISMATCH(_MATCH_,_MASK_) {if (!((info->_MATCH_ == _MATCH_)^!!(info->invflags & _MASK_))) return false; } static bool -ebt_vlan_mt(const struct sk_buff *skb, const struct xt_action_param *par) +ebt_vlan_mt(const struct sk_buff *skb, struct xt_action_param *par) { const struct ebt_vlan_info *info = par->matchinfo; const struct vlan_hdr *fp; diff --git a/net/ipv4/netfilter/ip_tables.c b/net/ipv4/netfilter/ip_tables.c index 3ab1b81e799..4e674f2824a 100644 --- a/net/ipv4/netfilter/ip_tables.c +++ b/net/ipv4/netfilter/ip_tables.c @@ -2138,7 +2138,7 @@ icmp_type_code_match(u_int8_t test_type, u_int8_t min_code, u_int8_t max_code, } static bool -icmp_match(const struct sk_buff *skb, const struct xt_action_param *par) +icmp_match(const struct sk_buff *skb, struct xt_action_param *par) { const struct icmphdr *ic; struct icmphdr _icmph; diff --git a/net/ipv4/netfilter/ipt_addrtype.c b/net/ipv4/netfilter/ipt_addrtype.c index 24ec548515e..db8bff0fb86 100644 --- a/net/ipv4/netfilter/ipt_addrtype.c +++ b/net/ipv4/netfilter/ipt_addrtype.c @@ -30,7 +30,7 @@ static inline bool match_type(struct net *net, const struct net_device *dev, } static bool -addrtype_mt_v0(const struct sk_buff *skb, const struct xt_action_param *par) +addrtype_mt_v0(const struct sk_buff *skb, struct xt_action_param *par) { struct net *net = dev_net(par->in ? par->in : par->out); const struct ipt_addrtype_info *info = par->matchinfo; @@ -48,7 +48,7 @@ addrtype_mt_v0(const struct sk_buff *skb, const struct xt_action_param *par) } static bool -addrtype_mt_v1(const struct sk_buff *skb, const struct xt_action_param *par) +addrtype_mt_v1(const struct sk_buff *skb, struct xt_action_param *par) { struct net *net = dev_net(par->in ? par->in : par->out); const struct ipt_addrtype_info_v1 *info = par->matchinfo; diff --git a/net/ipv4/netfilter/ipt_ah.c b/net/ipv4/netfilter/ipt_ah.c index 48a8293bc1d..c9d83dc2d6f 100644 --- a/net/ipv4/netfilter/ipt_ah.c +++ b/net/ipv4/netfilter/ipt_ah.c @@ -30,7 +30,7 @@ spi_match(u_int32_t min, u_int32_t max, u_int32_t spi, bool invert) return r; } -static bool ah_mt(const struct sk_buff *skb, const struct xt_action_param *par) +static bool ah_mt(const struct sk_buff *skb, struct xt_action_param *par) { struct ip_auth_hdr _ahdr; const struct ip_auth_hdr *ah; diff --git a/net/ipv4/netfilter/ipt_ecn.c b/net/ipv4/netfilter/ipt_ecn.c index 744d13ee296..b79dddc9edd 100644 --- a/net/ipv4/netfilter/ipt_ecn.c +++ b/net/ipv4/netfilter/ipt_ecn.c @@ -67,8 +67,7 @@ static inline bool match_tcp(const struct sk_buff *skb, return true; } -static bool ecn_mt(const struct sk_buff *skb, - const struct xt_action_param *par) +static bool ecn_mt(const struct sk_buff *skb, struct xt_action_param *par) { const struct ipt_ecn_info *info = par->matchinfo; diff --git a/net/ipv6/netfilter/ip6_tables.c b/net/ipv6/netfilter/ip6_tables.c index c3bc999a8bb..4549f8d6f88 100644 --- a/net/ipv6/netfilter/ip6_tables.c +++ b/net/ipv6/netfilter/ip6_tables.c @@ -2154,7 +2154,7 @@ icmp6_type_code_match(u_int8_t test_type, u_int8_t min_code, u_int8_t max_code, } static bool -icmp6_match(const struct sk_buff *skb, const struct xt_action_param *par) +icmp6_match(const struct sk_buff *skb, struct xt_action_param *par) { const struct icmp6hdr *ic; struct icmp6hdr _icmph; diff --git a/net/ipv6/netfilter/ip6t_ah.c b/net/ipv6/netfilter/ip6t_ah.c index 4fe71898381..c89887f35a4 100644 --- a/net/ipv6/netfilter/ip6t_ah.c +++ b/net/ipv6/netfilter/ip6t_ah.c @@ -36,8 +36,7 @@ spi_match(u_int32_t min, u_int32_t max, u_int32_t spi, bool invert) return r; } -static bool ah_mt6(const struct sk_buff *skb, - const struct xt_action_param *par) +static bool ah_mt6(const struct sk_buff *skb, struct xt_action_param *par) { struct ip_auth_hdr _ah; const struct ip_auth_hdr *ah; diff --git a/net/ipv6/netfilter/ip6t_eui64.c b/net/ipv6/netfilter/ip6t_eui64.c index 2fd2be1795e..f32fce34145 100644 --- a/net/ipv6/netfilter/ip6t_eui64.c +++ b/net/ipv6/netfilter/ip6t_eui64.c @@ -20,7 +20,7 @@ MODULE_LICENSE("GPL"); MODULE_AUTHOR("Andras Kis-Szabo "); static bool -eui64_mt6(const struct sk_buff *skb, const struct xt_action_param *par) +eui64_mt6(const struct sk_buff *skb, struct xt_action_param *par) { unsigned char eui64[8]; diff --git a/net/ipv6/netfilter/ip6t_frag.c b/net/ipv6/netfilter/ip6t_frag.c index 8401aa82ea0..fcc8c72f218 100644 --- a/net/ipv6/netfilter/ip6t_frag.c +++ b/net/ipv6/netfilter/ip6t_frag.c @@ -35,7 +35,7 @@ id_match(u_int32_t min, u_int32_t max, u_int32_t id, bool invert) } static bool -frag_mt6(const struct sk_buff *skb, const struct xt_action_param *par) +frag_mt6(const struct sk_buff *skb, struct xt_action_param *par) { struct frag_hdr _frag; const struct frag_hdr *fh; diff --git a/net/ipv6/netfilter/ip6t_hbh.c b/net/ipv6/netfilter/ip6t_hbh.c index d19d5cf47a3..f8aebc098d7 100644 --- a/net/ipv6/netfilter/ip6t_hbh.c +++ b/net/ipv6/netfilter/ip6t_hbh.c @@ -44,7 +44,7 @@ MODULE_ALIAS("ip6t_dst"); static struct xt_match hbh_mt6_reg[] __read_mostly; static bool -hbh_mt6(const struct sk_buff *skb, const struct xt_action_param *par) +hbh_mt6(const struct sk_buff *skb, struct xt_action_param *par) { struct ipv6_opt_hdr _optsh; const struct ipv6_opt_hdr *oh; diff --git a/net/ipv6/netfilter/ip6t_ipv6header.c b/net/ipv6/netfilter/ip6t_ipv6header.c index 8e88bb8311d..54bd9790603 100644 --- a/net/ipv6/netfilter/ip6t_ipv6header.c +++ b/net/ipv6/netfilter/ip6t_ipv6header.c @@ -27,7 +27,7 @@ MODULE_DESCRIPTION("Xtables: IPv6 header types match"); MODULE_AUTHOR("Andras Kis-Szabo "); static bool -ipv6header_mt6(const struct sk_buff *skb, const struct xt_action_param *par) +ipv6header_mt6(const struct sk_buff *skb, struct xt_action_param *par) { const struct ip6t_ipv6header_info *info = par->matchinfo; unsigned int temp; diff --git a/net/ipv6/netfilter/ip6t_mh.c b/net/ipv6/netfilter/ip6t_mh.c index 4a60788873f..eb1c3d65271 100644 --- a/net/ipv6/netfilter/ip6t_mh.c +++ b/net/ipv6/netfilter/ip6t_mh.c @@ -32,8 +32,7 @@ type_match(u_int8_t min, u_int8_t max, u_int8_t type, bool invert) return (type >= min && type <= max) ^ invert; } -static bool mh_mt6(const struct sk_buff *skb, - const struct xt_action_param *par) +static bool mh_mt6(const struct sk_buff *skb, struct xt_action_param *par) { struct ip6_mh _mh; const struct ip6_mh *mh; diff --git a/net/ipv6/netfilter/ip6t_rt.c b/net/ipv6/netfilter/ip6t_rt.c index 793c2720088..ee584693ee3 100644 --- a/net/ipv6/netfilter/ip6t_rt.c +++ b/net/ipv6/netfilter/ip6t_rt.c @@ -36,8 +36,7 @@ segsleft_match(u_int32_t min, u_int32_t max, u_int32_t id, bool invert) return r; } -static bool rt_mt6(const struct sk_buff *skb, - const struct xt_action_param *par) +static bool rt_mt6(const struct sk_buff *skb, struct xt_action_param *par) { struct ipv6_rt_hdr _route; const struct ipv6_rt_hdr *rh; diff --git a/net/netfilter/xt_cluster.c b/net/netfilter/xt_cluster.c index 67fc317118c..30b95a1c1c8 100644 --- a/net/netfilter/xt_cluster.c +++ b/net/netfilter/xt_cluster.c @@ -86,7 +86,7 @@ xt_cluster_is_multicast_addr(const struct sk_buff *skb, u_int8_t family) } static bool -xt_cluster_mt(const struct sk_buff *skb, const struct xt_action_param *par) +xt_cluster_mt(const struct sk_buff *skb, struct xt_action_param *par) { struct sk_buff *pskb = (struct sk_buff *)skb; const struct xt_cluster_match_info *info = par->matchinfo; diff --git a/net/netfilter/xt_comment.c b/net/netfilter/xt_comment.c index 1dbb3e13c05..5c861d2f21c 100644 --- a/net/netfilter/xt_comment.c +++ b/net/netfilter/xt_comment.c @@ -16,7 +16,7 @@ MODULE_ALIAS("ipt_comment"); MODULE_ALIAS("ip6t_comment"); static bool -comment_mt(const struct sk_buff *skb, const struct xt_action_param *par) +comment_mt(const struct sk_buff *skb, struct xt_action_param *par) { /* We always match */ return true; diff --git a/net/netfilter/xt_connbytes.c b/net/netfilter/xt_connbytes.c index 5e5cf15f011..73517835303 100644 --- a/net/netfilter/xt_connbytes.c +++ b/net/netfilter/xt_connbytes.c @@ -18,7 +18,7 @@ MODULE_ALIAS("ipt_connbytes"); MODULE_ALIAS("ip6t_connbytes"); static bool -connbytes_mt(const struct sk_buff *skb, const struct xt_action_param *par) +connbytes_mt(const struct sk_buff *skb, struct xt_action_param *par) { const struct xt_connbytes_info *sinfo = par->matchinfo; const struct nf_conn *ct; diff --git a/net/netfilter/xt_connlimit.c b/net/netfilter/xt_connlimit.c index 6eec1913298..f130fd9817b 100644 --- a/net/netfilter/xt_connlimit.c +++ b/net/netfilter/xt_connlimit.c @@ -173,7 +173,7 @@ static int count_them(struct net *net, } static bool -connlimit_mt(const struct sk_buff *skb, const struct xt_action_param *par) +connlimit_mt(const struct sk_buff *skb, struct xt_action_param *par) { struct net *net = dev_net(par->in ? par->in : par->out); const struct xt_connlimit_info *info = par->matchinfo; diff --git a/net/netfilter/xt_connmark.c b/net/netfilter/xt_connmark.c index b43cfc7f1bb..7278145e6a6 100644 --- a/net/netfilter/xt_connmark.c +++ b/net/netfilter/xt_connmark.c @@ -91,7 +91,7 @@ static void connmark_tg_destroy(const struct xt_tgdtor_param *par) } static bool -connmark_mt(const struct sk_buff *skb, const struct xt_action_param *par) +connmark_mt(const struct sk_buff *skb, struct xt_action_param *par) { const struct xt_connmark_mtinfo1 *info = par->matchinfo; enum ip_conntrack_info ctinfo; diff --git a/net/netfilter/xt_conntrack.c b/net/netfilter/xt_conntrack.c index 14a96f48001..39681f10291 100644 --- a/net/netfilter/xt_conntrack.c +++ b/net/netfilter/xt_conntrack.c @@ -113,7 +113,7 @@ ct_proto_port_check(const struct xt_conntrack_mtinfo2 *info, } static bool -conntrack_mt(const struct sk_buff *skb, const struct xt_action_param *par, +conntrack_mt(const struct sk_buff *skb, struct xt_action_param *par, u16 state_mask, u16 status_mask) { const struct xt_conntrack_mtinfo2 *info = par->matchinfo; @@ -191,7 +191,7 @@ conntrack_mt(const struct sk_buff *skb, const struct xt_action_param *par, } static bool -conntrack_mt_v1(const struct sk_buff *skb, const struct xt_action_param *par) +conntrack_mt_v1(const struct sk_buff *skb, struct xt_action_param *par) { const struct xt_conntrack_mtinfo1 *info = par->matchinfo; @@ -199,7 +199,7 @@ conntrack_mt_v1(const struct sk_buff *skb, const struct xt_action_param *par) } static bool -conntrack_mt_v2(const struct sk_buff *skb, const struct xt_action_param *par) +conntrack_mt_v2(const struct sk_buff *skb, struct xt_action_param *par) { const struct xt_conntrack_mtinfo2 *info = par->matchinfo; diff --git a/net/netfilter/xt_dccp.c b/net/netfilter/xt_dccp.c index 7a4d4e8edc1..cc2c2919439 100644 --- a/net/netfilter/xt_dccp.c +++ b/net/netfilter/xt_dccp.c @@ -96,7 +96,7 @@ match_option(u_int8_t option, const struct sk_buff *skb, unsigned int protoff, } static bool -dccp_mt(const struct sk_buff *skb, const struct xt_action_param *par) +dccp_mt(const struct sk_buff *skb, struct xt_action_param *par) { const struct xt_dccp_info *info = par->matchinfo; const struct dccp_hdr *dh; diff --git a/net/netfilter/xt_dscp.c b/net/netfilter/xt_dscp.c index 2133b509d15..64670fc5d0e 100644 --- a/net/netfilter/xt_dscp.c +++ b/net/netfilter/xt_dscp.c @@ -25,7 +25,7 @@ MODULE_ALIAS("ipt_tos"); MODULE_ALIAS("ip6t_tos"); static bool -dscp_mt(const struct sk_buff *skb, const struct xt_action_param *par) +dscp_mt(const struct sk_buff *skb, struct xt_action_param *par) { const struct xt_dscp_info *info = par->matchinfo; u_int8_t dscp = ipv4_get_dsfield(ip_hdr(skb)) >> XT_DSCP_SHIFT; @@ -34,7 +34,7 @@ dscp_mt(const struct sk_buff *skb, const struct xt_action_param *par) } static bool -dscp_mt6(const struct sk_buff *skb, const struct xt_action_param *par) +dscp_mt6(const struct sk_buff *skb, struct xt_action_param *par) { const struct xt_dscp_info *info = par->matchinfo; u_int8_t dscp = ipv6_get_dsfield(ipv6_hdr(skb)) >> XT_DSCP_SHIFT; @@ -54,8 +54,7 @@ static int dscp_mt_check(const struct xt_mtchk_param *par) return 0; } -static bool tos_mt(const struct sk_buff *skb, - const struct xt_action_param *par) +static bool tos_mt(const struct sk_buff *skb, struct xt_action_param *par) { const struct xt_tos_match_info *info = par->matchinfo; diff --git a/net/netfilter/xt_esp.c b/net/netfilter/xt_esp.c index 39caafff948..7c2d8002055 100644 --- a/net/netfilter/xt_esp.c +++ b/net/netfilter/xt_esp.c @@ -36,8 +36,7 @@ spi_match(u_int32_t min, u_int32_t max, u_int32_t spi, bool invert) return r; } -static bool esp_mt(const struct sk_buff *skb, - const struct xt_action_param *par) +static bool esp_mt(const struct sk_buff *skb, struct xt_action_param *par) { const struct ip_esp_hdr *eh; struct ip_esp_hdr _esp; diff --git a/net/netfilter/xt_hashlimit.c b/net/netfilter/xt_hashlimit.c index 700c21e0804..25a6e548ec2 100644 --- a/net/netfilter/xt_hashlimit.c +++ b/net/netfilter/xt_hashlimit.c @@ -516,7 +516,7 @@ hashlimit_init_dst(const struct xt_hashlimit_htable *hinfo, } static bool -hashlimit_mt(const struct sk_buff *skb, const struct xt_action_param *par) +hashlimit_mt(const struct sk_buff *skb, struct xt_action_param *par) { const struct xt_hashlimit_mtinfo1 *info = par->matchinfo; struct xt_hashlimit_htable *hinfo = info->hinfo; diff --git a/net/netfilter/xt_helper.c b/net/netfilter/xt_helper.c index e941bd26a51..9f4ab00c805 100644 --- a/net/netfilter/xt_helper.c +++ b/net/netfilter/xt_helper.c @@ -24,7 +24,7 @@ MODULE_ALIAS("ip6t_helper"); static bool -helper_mt(const struct sk_buff *skb, const struct xt_action_param *par) +helper_mt(const struct sk_buff *skb, struct xt_action_param *par) { const struct xt_helper_info *info = par->matchinfo; const struct nf_conn *ct; diff --git a/net/netfilter/xt_hl.c b/net/netfilter/xt_hl.c index 335c34a4fd1..7d12221ead8 100644 --- a/net/netfilter/xt_hl.c +++ b/net/netfilter/xt_hl.c @@ -25,8 +25,7 @@ MODULE_LICENSE("GPL"); MODULE_ALIAS("ipt_ttl"); MODULE_ALIAS("ip6t_hl"); -static bool ttl_mt(const struct sk_buff *skb, - const struct xt_action_param *par) +static bool ttl_mt(const struct sk_buff *skb, struct xt_action_param *par) { const struct ipt_ttl_info *info = par->matchinfo; const u8 ttl = ip_hdr(skb)->ttl; @@ -45,8 +44,7 @@ static bool ttl_mt(const struct sk_buff *skb, return false; } -static bool hl_mt6(const struct sk_buff *skb, - const struct xt_action_param *par) +static bool hl_mt6(const struct sk_buff *skb, struct xt_action_param *par) { const struct ip6t_hl_info *info = par->matchinfo; const struct ipv6hdr *ip6h = ipv6_hdr(skb); diff --git a/net/netfilter/xt_iprange.c b/net/netfilter/xt_iprange.c index 9578860a921..88f7c3511c7 100644 --- a/net/netfilter/xt_iprange.c +++ b/net/netfilter/xt_iprange.c @@ -17,7 +17,7 @@ #include static bool -iprange_mt4(const struct sk_buff *skb, const struct xt_action_param *par) +iprange_mt4(const struct sk_buff *skb, struct xt_action_param *par) { const struct xt_iprange_mtinfo *info = par->matchinfo; const struct iphdr *iph = ip_hdr(skb); @@ -68,7 +68,7 @@ iprange_ipv6_sub(const struct in6_addr *a, const struct in6_addr *b) } static bool -iprange_mt6(const struct sk_buff *skb, const struct xt_action_param *par) +iprange_mt6(const struct sk_buff *skb, struct xt_action_param *par) { const struct xt_iprange_mtinfo *info = par->matchinfo; const struct ipv6hdr *iph = ipv6_hdr(skb); diff --git a/net/netfilter/xt_length.c b/net/netfilter/xt_length.c index 842149b4122..176e5570a99 100644 --- a/net/netfilter/xt_length.c +++ b/net/netfilter/xt_length.c @@ -21,7 +21,7 @@ MODULE_ALIAS("ipt_length"); MODULE_ALIAS("ip6t_length"); static bool -length_mt(const struct sk_buff *skb, const struct xt_action_param *par) +length_mt(const struct sk_buff *skb, struct xt_action_param *par) { const struct xt_length_info *info = par->matchinfo; u_int16_t pktlen = ntohs(ip_hdr(skb)->tot_len); @@ -30,7 +30,7 @@ length_mt(const struct sk_buff *skb, const struct xt_action_param *par) } static bool -length_mt6(const struct sk_buff *skb, const struct xt_action_param *par) +length_mt6(const struct sk_buff *skb, struct xt_action_param *par) { const struct xt_length_info *info = par->matchinfo; const u_int16_t pktlen = ntohs(ipv6_hdr(skb)->payload_len) + diff --git a/net/netfilter/xt_limit.c b/net/netfilter/xt_limit.c index 7dcfe8602c8..32b7a579a03 100644 --- a/net/netfilter/xt_limit.c +++ b/net/netfilter/xt_limit.c @@ -65,7 +65,7 @@ static DEFINE_SPINLOCK(limit_lock); #define CREDITS_PER_JIFFY POW2_BELOW32(MAX_CPJ) static bool -limit_mt(const struct sk_buff *skb, const struct xt_action_param *par) +limit_mt(const struct sk_buff *skb, struct xt_action_param *par) { const struct xt_rateinfo *r = par->matchinfo; struct xt_limit_priv *priv = r->master; diff --git a/net/netfilter/xt_mac.c b/net/netfilter/xt_mac.c index 36c49644ce3..8160f6b1435 100644 --- a/net/netfilter/xt_mac.c +++ b/net/netfilter/xt_mac.c @@ -25,8 +25,7 @@ MODULE_DESCRIPTION("Xtables: MAC address match"); MODULE_ALIAS("ipt_mac"); MODULE_ALIAS("ip6t_mac"); -static bool mac_mt(const struct sk_buff *skb, - const struct xt_action_param *par) +static bool mac_mt(const struct sk_buff *skb, struct xt_action_param *par) { const struct xt_mac_info *info = par->matchinfo; bool ret; diff --git a/net/netfilter/xt_mark.c b/net/netfilter/xt_mark.c index 3c8347076d5..23345238711 100644 --- a/net/netfilter/xt_mark.c +++ b/net/netfilter/xt_mark.c @@ -34,7 +34,7 @@ mark_tg(struct sk_buff *skb, const struct xt_action_param *par) } static bool -mark_mt(const struct sk_buff *skb, const struct xt_action_param *par) +mark_mt(const struct sk_buff *skb, struct xt_action_param *par) { const struct xt_mark_mtinfo1 *info = par->matchinfo; diff --git a/net/netfilter/xt_multiport.c b/net/netfilter/xt_multiport.c index 3140fd4049f..52beb68256c 100644 --- a/net/netfilter/xt_multiport.c +++ b/net/netfilter/xt_multiport.c @@ -72,7 +72,7 @@ ports_match_v1(const struct xt_multiport_v1 *minfo, } static bool -multiport_mt(const struct sk_buff *skb, const struct xt_action_param *par) +multiport_mt(const struct sk_buff *skb, struct xt_action_param *par) { const __be16 *pptr; __be16 _ports[2]; diff --git a/net/netfilter/xt_osf.c b/net/netfilter/xt_osf.c index 37aa55860a9..4327e101c04 100644 --- a/net/netfilter/xt_osf.c +++ b/net/netfilter/xt_osf.c @@ -194,7 +194,7 @@ static inline int xt_osf_ttl(const struct sk_buff *skb, const struct xt_osf_info } static bool -xt_osf_match_packet(const struct sk_buff *skb, const struct xt_action_param *p) +xt_osf_match_packet(const struct sk_buff *skb, struct xt_action_param *p) { const struct xt_osf_info *info = p->matchinfo; const struct iphdr *ip = ip_hdr(skb); diff --git a/net/netfilter/xt_owner.c b/net/netfilter/xt_owner.c index 3dd1391d385..772d7389b33 100644 --- a/net/netfilter/xt_owner.c +++ b/net/netfilter/xt_owner.c @@ -18,7 +18,7 @@ #include static bool -owner_mt(const struct sk_buff *skb, const struct xt_action_param *par) +owner_mt(const struct sk_buff *skb, struct xt_action_param *par) { const struct xt_owner_match_info *info = par->matchinfo; const struct file *filp; diff --git a/net/netfilter/xt_physdev.c b/net/netfilter/xt_physdev.c index 298cd290b06..d7ca16b8b8d 100644 --- a/net/netfilter/xt_physdev.c +++ b/net/netfilter/xt_physdev.c @@ -22,7 +22,7 @@ MODULE_ALIAS("ip6t_physdev"); static bool -physdev_mt(const struct sk_buff *skb, const struct xt_action_param *par) +physdev_mt(const struct sk_buff *skb, struct xt_action_param *par) { static const char nulldevname[IFNAMSIZ] __attribute__((aligned(sizeof(long)))); const struct xt_physdev_info *info = par->matchinfo; diff --git a/net/netfilter/xt_pkttype.c b/net/netfilter/xt_pkttype.c index d95f2149df9..5b645cb598f 100644 --- a/net/netfilter/xt_pkttype.c +++ b/net/netfilter/xt_pkttype.c @@ -23,7 +23,7 @@ MODULE_ALIAS("ipt_pkttype"); MODULE_ALIAS("ip6t_pkttype"); static bool -pkttype_mt(const struct sk_buff *skb, const struct xt_action_param *par) +pkttype_mt(const struct sk_buff *skb, struct xt_action_param *par) { const struct xt_pkttype_info *info = par->matchinfo; u_int8_t type; diff --git a/net/netfilter/xt_policy.c b/net/netfilter/xt_policy.c index 1abfc7ad427..f23e97bb42d 100644 --- a/net/netfilter/xt_policy.c +++ b/net/netfilter/xt_policy.c @@ -110,7 +110,7 @@ match_policy_out(const struct sk_buff *skb, const struct xt_policy_info *info, } static bool -policy_mt(const struct sk_buff *skb, const struct xt_action_param *par) +policy_mt(const struct sk_buff *skb, struct xt_action_param *par) { const struct xt_policy_info *info = par->matchinfo; int ret; diff --git a/net/netfilter/xt_quota.c b/net/netfilter/xt_quota.c index e79e07c75da..b4f7dfea598 100644 --- a/net/netfilter/xt_quota.c +++ b/net/netfilter/xt_quota.c @@ -23,7 +23,7 @@ MODULE_ALIAS("ip6t_quota"); static DEFINE_SPINLOCK(quota_lock); static bool -quota_mt(const struct sk_buff *skb, const struct xt_action_param *par) +quota_mt(const struct sk_buff *skb, struct xt_action_param *par) { struct xt_quota_info *q = (void *)par->matchinfo; struct xt_quota_priv *priv = q->master; diff --git a/net/netfilter/xt_rateest.c b/net/netfilter/xt_rateest.c index 53f7a4d12e1..76a083184d8 100644 --- a/net/netfilter/xt_rateest.c +++ b/net/netfilter/xt_rateest.c @@ -15,7 +15,7 @@ static bool -xt_rateest_mt(const struct sk_buff *skb, const struct xt_action_param *par) +xt_rateest_mt(const struct sk_buff *skb, struct xt_action_param *par) { const struct xt_rateest_match_info *info = par->matchinfo; struct gnet_stats_rate_est *r; diff --git a/net/netfilter/xt_realm.c b/net/netfilter/xt_realm.c index b063c783901..459a7b256eb 100644 --- a/net/netfilter/xt_realm.c +++ b/net/netfilter/xt_realm.c @@ -22,7 +22,7 @@ MODULE_DESCRIPTION("Xtables: Routing realm match"); MODULE_ALIAS("ipt_realm"); static bool -realm_mt(const struct sk_buff *skb, const struct xt_action_param *par) +realm_mt(const struct sk_buff *skb, struct xt_action_param *par) { const struct xt_realm_info *info = par->matchinfo; const struct dst_entry *dst = skb_dst(skb); diff --git a/net/netfilter/xt_recent.c b/net/netfilter/xt_recent.c index 503b7f199f1..2808a7e3394 100644 --- a/net/netfilter/xt_recent.c +++ b/net/netfilter/xt_recent.c @@ -224,7 +224,7 @@ static void recent_table_flush(struct recent_table *t) } static bool -recent_mt(const struct sk_buff *skb, const struct xt_action_param *par) +recent_mt(const struct sk_buff *skb, struct xt_action_param *par) { struct net *net = dev_net(par->in ? par->in : par->out); struct recent_net *recent_net = recent_pernet(net); diff --git a/net/netfilter/xt_sctp.c b/net/netfilter/xt_sctp.c index da4c3cb3140..94d8b5deb2d 100644 --- a/net/netfilter/xt_sctp.c +++ b/net/netfilter/xt_sctp.c @@ -114,7 +114,7 @@ match_packet(const struct sk_buff *skb, } static bool -sctp_mt(const struct sk_buff *skb, const struct xt_action_param *par) +sctp_mt(const struct sk_buff *skb, struct xt_action_param *par) { const struct xt_sctp_info *info = par->matchinfo; const sctp_sctphdr_t *sh; diff --git a/net/netfilter/xt_socket.c b/net/netfilter/xt_socket.c index 2665e32d5db..3d54c236a1b 100644 --- a/net/netfilter/xt_socket.c +++ b/net/netfilter/xt_socket.c @@ -88,7 +88,7 @@ extract_icmp_fields(const struct sk_buff *skb, static bool -socket_match(const struct sk_buff *skb, const struct xt_action_param *par, +socket_match(const struct sk_buff *skb, struct xt_action_param *par, const struct xt_socket_mtinfo1 *info) { const struct iphdr *iph = ip_hdr(skb); @@ -174,13 +174,13 @@ socket_match(const struct sk_buff *skb, const struct xt_action_param *par, } static bool -socket_mt_v0(const struct sk_buff *skb, const struct xt_action_param *par) +socket_mt_v0(const struct sk_buff *skb, struct xt_action_param *par) { return socket_match(skb, par, NULL); } static bool -socket_mt_v1(const struct sk_buff *skb, const struct xt_action_param *par) +socket_mt_v1(const struct sk_buff *skb, struct xt_action_param *par) { return socket_match(skb, par, par->matchinfo); } diff --git a/net/netfilter/xt_state.c b/net/netfilter/xt_state.c index 344cca66109..e12e053d378 100644 --- a/net/netfilter/xt_state.c +++ b/net/netfilter/xt_state.c @@ -21,7 +21,7 @@ MODULE_ALIAS("ipt_state"); MODULE_ALIAS("ip6t_state"); static bool -state_mt(const struct sk_buff *skb, const struct xt_action_param *par) +state_mt(const struct sk_buff *skb, struct xt_action_param *par) { const struct xt_state_info *sinfo = par->matchinfo; enum ip_conntrack_info ctinfo; diff --git a/net/netfilter/xt_statistic.c b/net/netfilter/xt_statistic.c index ee4540a2cf3..96e62b8fd6b 100644 --- a/net/netfilter/xt_statistic.c +++ b/net/netfilter/xt_statistic.c @@ -30,7 +30,7 @@ MODULE_ALIAS("ip6t_statistic"); static DEFINE_SPINLOCK(nth_lock); static bool -statistic_mt(const struct sk_buff *skb, const struct xt_action_param *par) +statistic_mt(const struct sk_buff *skb, struct xt_action_param *par) { const struct xt_statistic_info *info = par->matchinfo; bool ret = info->flags & XT_STATISTIC_INVERT; diff --git a/net/netfilter/xt_string.c b/net/netfilter/xt_string.c index fd5dc5016a1..d3c48b14ab9 100644 --- a/net/netfilter/xt_string.c +++ b/net/netfilter/xt_string.c @@ -23,7 +23,7 @@ MODULE_ALIAS("ipt_string"); MODULE_ALIAS("ip6t_string"); static bool -string_mt(const struct sk_buff *skb, const struct xt_action_param *par) +string_mt(const struct sk_buff *skb, struct xt_action_param *par) { const struct xt_string_info *conf = par->matchinfo; struct ts_state state; diff --git a/net/netfilter/xt_tcpmss.c b/net/netfilter/xt_tcpmss.c index 5c8a7b435d6..f90728f6b8a 100644 --- a/net/netfilter/xt_tcpmss.c +++ b/net/netfilter/xt_tcpmss.c @@ -25,7 +25,7 @@ MODULE_ALIAS("ipt_tcpmss"); MODULE_ALIAS("ip6t_tcpmss"); static bool -tcpmss_mt(const struct sk_buff *skb, const struct xt_action_param *par) +tcpmss_mt(const struct sk_buff *skb, struct xt_action_param *par) { const struct xt_tcpmss_match_info *info = par->matchinfo; const struct tcphdr *th; diff --git a/net/netfilter/xt_tcpudp.c b/net/netfilter/xt_tcpudp.c index 19c31d4c2ba..dedde33c9db 100644 --- a/net/netfilter/xt_tcpudp.c +++ b/net/netfilter/xt_tcpudp.c @@ -62,8 +62,7 @@ tcp_find_option(u_int8_t option, return invert; } -static bool tcp_mt(const struct sk_buff *skb, - const struct xt_action_param *par) +static bool tcp_mt(const struct sk_buff *skb, struct xt_action_param *par) { const struct tcphdr *th; struct tcphdr _tcph; @@ -129,8 +128,7 @@ static int tcp_mt_check(const struct xt_mtchk_param *par) return (tcpinfo->invflags & ~XT_TCP_INV_MASK) ? -EINVAL : 0; } -static bool udp_mt(const struct sk_buff *skb, - const struct xt_action_param *par) +static bool udp_mt(const struct sk_buff *skb, struct xt_action_param *par) { const struct udphdr *uh; struct udphdr _udph; diff --git a/net/netfilter/xt_time.c b/net/netfilter/xt_time.c index ffdb8fac0be..79234bb19d0 100644 --- a/net/netfilter/xt_time.c +++ b/net/netfilter/xt_time.c @@ -152,7 +152,7 @@ static void localtime_3(struct xtm *r, time_t time) } static bool -time_mt(const struct sk_buff *skb, const struct xt_action_param *par) +time_mt(const struct sk_buff *skb, struct xt_action_param *par) { const struct xt_time_info *info = par->matchinfo; unsigned int packet_time; diff --git a/net/netfilter/xt_u32.c b/net/netfilter/xt_u32.c index f62797b1ddb..a95b50342db 100644 --- a/net/netfilter/xt_u32.c +++ b/net/netfilter/xt_u32.c @@ -86,8 +86,7 @@ static bool u32_match_it(const struct xt_u32 *data, return true; } -static bool u32_mt(const struct sk_buff *skb, - const struct xt_action_param *par) +static bool u32_mt(const struct sk_buff *skb, struct xt_action_param *par) { const struct xt_u32 *data = par->matchinfo; bool ret; -- cgit v1.2.3 From b4ba26119b06052888696491f614201817491a0d Mon Sep 17 00:00:00 2001 From: Jan Engelhardt Date: Tue, 7 Jul 2009 20:54:30 +0200 Subject: netfilter: xtables: change hotdrop pointer to direct modification Since xt_action_param is writable, let's use it. The pointer to 'bool hotdrop' always worried (8 bytes (64-bit) to write 1 byte!). Surprisingly results in a reduction in size: text data bss filename 5457066 692730 357892 vmlinux.o-prev 5456554 692730 357892 vmlinux.o Signed-off-by: Jan Engelhardt --- include/linux/netfilter/x_tables.h | 5 ++++- net/bridge/netfilter/ebtables.c | 5 ++--- net/ipv4/netfilter/arp_tables.c | 6 +++--- net/ipv4/netfilter/ip_tables.c | 9 ++++----- net/ipv4/netfilter/ipt_ah.c | 2 +- net/ipv4/netfilter/ipt_ecn.c | 2 +- net/ipv6/netfilter/ip6_tables.c | 11 +++++------ net/ipv6/netfilter/ip6t_ah.c | 4 ++-- net/ipv6/netfilter/ip6t_eui64.c | 2 +- net/ipv6/netfilter/ip6t_frag.c | 4 ++-- net/ipv6/netfilter/ip6t_hbh.c | 4 ++-- net/ipv6/netfilter/ip6t_mh.c | 4 ++-- net/ipv6/netfilter/ip6t_rt.c | 4 ++-- net/netfilter/xt_connlimit.c | 4 ++-- net/netfilter/xt_dccp.c | 4 ++-- net/netfilter/xt_esp.c | 2 +- net/netfilter/xt_hashlimit.c | 2 +- net/netfilter/xt_multiport.c | 2 +- net/netfilter/xt_recent.c | 2 +- net/netfilter/xt_sctp.c | 4 ++-- net/netfilter/xt_tcpmss.c | 2 +- net/netfilter/xt_tcpudp.c | 10 +++++----- 22 files changed, 47 insertions(+), 47 deletions(-) (limited to 'include/linux') diff --git a/include/linux/netfilter/x_tables.h b/include/linux/netfilter/x_tables.h index 40c6a8d2a9e..c2ee5d8550c 100644 --- a/include/linux/netfilter/x_tables.h +++ b/include/linux/netfilter/x_tables.h @@ -196,6 +196,9 @@ struct xt_counters_info { * @hook: hook number given packet came from * @family: Actual NFPROTO_* through which the function is invoked * (helpful when match->family == NFPROTO_UNSPEC) + * + * Fields written to by extensions: + * * @hotdrop: drop packet if we had inspection problems * Network namespace obtainable using dev_net(in/out) */ @@ -212,7 +215,7 @@ struct xt_action_param { unsigned int thoff; unsigned int hooknum; u_int8_t family; - bool *hotdrop; + bool hotdrop; }; /** diff --git a/net/bridge/netfilter/ebtables.c b/net/bridge/netfilter/ebtables.c index 290d43541d4..59ca00e40de 100644 --- a/net/bridge/netfilter/ebtables.c +++ b/net/bridge/netfilter/ebtables.c @@ -186,13 +186,12 @@ unsigned int ebt_do_table (unsigned int hook, struct sk_buff *skb, struct ebt_entries *chaininfo; const char *base; const struct ebt_table_info *private; - bool hotdrop = false; struct xt_action_param acpar; acpar.family = NFPROTO_BRIDGE; acpar.in = in; acpar.out = out; - acpar.hotdrop = &hotdrop; + acpar.hotdrop = false; acpar.hooknum = hook; read_lock_bh(&table->lock); @@ -216,7 +215,7 @@ unsigned int ebt_do_table (unsigned int hook, struct sk_buff *skb, if (EBT_MATCH_ITERATE(point, ebt_do_match, skb, &acpar) != 0) goto letscontinue; - if (hotdrop) { + if (acpar.hotdrop) { read_unlock_bh(&table->lock); return NF_DROP; } diff --git a/net/ipv4/netfilter/arp_tables.c b/net/ipv4/netfilter/arp_tables.c index 9e7d089f168..8cc56d26e93 100644 --- a/net/ipv4/netfilter/arp_tables.c +++ b/net/ipv4/netfilter/arp_tables.c @@ -260,7 +260,6 @@ unsigned int arpt_do_table(struct sk_buff *skb, static const char nulldevname[IFNAMSIZ] __attribute__((aligned(sizeof(long)))); unsigned int verdict = NF_DROP; const struct arphdr *arp; - bool hotdrop = false; struct arpt_entry *e, *back; const char *indev, *outdev; void *table_base; @@ -284,6 +283,7 @@ unsigned int arpt_do_table(struct sk_buff *skb, acpar.out = out; acpar.hooknum = hook; acpar.family = NFPROTO_ARP; + acpar.hotdrop = false; arp = arp_hdr(skb); do { @@ -345,10 +345,10 @@ unsigned int arpt_do_table(struct sk_buff *skb, else /* Verdict */ break; - } while (!hotdrop); + } while (!acpar.hotdrop); xt_info_rdunlock_bh(); - if (hotdrop) + if (acpar.hotdrop) return NF_DROP; else return verdict; diff --git a/net/ipv4/netfilter/ip_tables.c b/net/ipv4/netfilter/ip_tables.c index 4e674f2824a..607f89f16b7 100644 --- a/net/ipv4/netfilter/ip_tables.c +++ b/net/ipv4/netfilter/ip_tables.c @@ -308,7 +308,6 @@ ipt_do_table(struct sk_buff *skb, { static const char nulldevname[IFNAMSIZ] __attribute__((aligned(sizeof(long)))); const struct iphdr *ip; - bool hotdrop = false; /* Initializing verdict to NF_DROP keeps gcc happy. */ unsigned int verdict = NF_DROP; const char *indev, *outdev; @@ -330,7 +329,7 @@ ipt_do_table(struct sk_buff *skb, * match it. */ acpar.fragoff = ntohs(ip->frag_off) & IP_OFFSET; acpar.thoff = ip_hdrlen(skb); - acpar.hotdrop = &hotdrop; + acpar.hotdrop = false; acpar.in = in; acpar.out = out; acpar.family = NFPROTO_IPV4; @@ -432,7 +431,7 @@ ipt_do_table(struct sk_buff *skb, else /* Verdict */ break; - } while (!hotdrop); + } while (!acpar.hotdrop); xt_info_rdunlock_bh(); pr_debug("Exiting %s; resetting sp from %u to %u\n", __func__, *stackptr, origptr); @@ -440,7 +439,7 @@ ipt_do_table(struct sk_buff *skb, #ifdef DEBUG_ALLOW_ALL return NF_ACCEPT; #else - if (hotdrop) + if (acpar.hotdrop) return NF_DROP; else return verdict; #endif @@ -2154,7 +2153,7 @@ icmp_match(const struct sk_buff *skb, struct xt_action_param *par) * can't. Hence, no choice but to drop. */ duprintf("Dropping evil ICMP tinygram.\n"); - *par->hotdrop = true; + par->hotdrop = true; return false; } diff --git a/net/ipv4/netfilter/ipt_ah.c b/net/ipv4/netfilter/ipt_ah.c index c9d83dc2d6f..14a2aa8b8a1 100644 --- a/net/ipv4/netfilter/ipt_ah.c +++ b/net/ipv4/netfilter/ipt_ah.c @@ -46,7 +46,7 @@ static bool ah_mt(const struct sk_buff *skb, struct xt_action_param *par) * can't. Hence, no choice but to drop. */ pr_debug("Dropping evil AH tinygram.\n"); - *par->hotdrop = true; + par->hotdrop = true; return 0; } diff --git a/net/ipv4/netfilter/ipt_ecn.c b/net/ipv4/netfilter/ipt_ecn.c index b79dddc9edd..af6e9c77834 100644 --- a/net/ipv4/netfilter/ipt_ecn.c +++ b/net/ipv4/netfilter/ipt_ecn.c @@ -78,7 +78,7 @@ static bool ecn_mt(const struct sk_buff *skb, struct xt_action_param *par) if (info->operation & (IPT_ECN_OP_MATCH_ECE|IPT_ECN_OP_MATCH_CWR)) { if (ip_hdr(skb)->protocol != IPPROTO_TCP) return false; - if (!match_tcp(skb, info, par->hotdrop)) + if (!match_tcp(skb, info, &par->hotdrop)) return false; } diff --git a/net/ipv6/netfilter/ip6_tables.c b/net/ipv6/netfilter/ip6_tables.c index 4549f8d6f88..557fac9689c 100644 --- a/net/ipv6/netfilter/ip6_tables.c +++ b/net/ipv6/netfilter/ip6_tables.c @@ -337,7 +337,6 @@ ip6t_do_table(struct sk_buff *skb, struct xt_table *table) { static const char nulldevname[IFNAMSIZ] __attribute__((aligned(sizeof(long)))); - bool hotdrop = false; /* Initializing verdict to NF_DROP keeps gcc happy. */ unsigned int verdict = NF_DROP; const char *indev, *outdev; @@ -356,7 +355,7 @@ ip6t_do_table(struct sk_buff *skb, * things we don't know, ie. tcp syn flag or ports). If the * rule is also a fragment-specific rule, non-fragments won't * match it. */ - acpar.hotdrop = &hotdrop; + acpar.hotdrop = false; acpar.in = in; acpar.out = out; acpar.family = NFPROTO_IPV6; @@ -380,7 +379,7 @@ ip6t_do_table(struct sk_buff *skb, IP_NF_ASSERT(e); if (!ip6_packet_match(skb, indev, outdev, &e->ipv6, - &acpar.thoff, &acpar.fragoff, &hotdrop)) { + &acpar.thoff, &acpar.fragoff, &acpar.hotdrop)) { no_match: e = ip6t_next_entry(e); continue; @@ -447,7 +446,7 @@ ip6t_do_table(struct sk_buff *skb, else /* Verdict */ break; - } while (!hotdrop); + } while (!acpar.hotdrop); xt_info_rdunlock_bh(); *stackptr = origptr; @@ -455,7 +454,7 @@ ip6t_do_table(struct sk_buff *skb, #ifdef DEBUG_ALLOW_ALL return NF_ACCEPT; #else - if (hotdrop) + if (acpar.hotdrop) return NF_DROP; else return verdict; #endif @@ -2170,7 +2169,7 @@ icmp6_match(const struct sk_buff *skb, struct xt_action_param *par) * can't. Hence, no choice but to drop. */ duprintf("Dropping evil ICMP tinygram.\n"); - *par->hotdrop = true; + par->hotdrop = true; return false; } diff --git a/net/ipv6/netfilter/ip6t_ah.c b/net/ipv6/netfilter/ip6t_ah.c index c89887f35a4..89cccc5a9c9 100644 --- a/net/ipv6/netfilter/ip6t_ah.c +++ b/net/ipv6/netfilter/ip6t_ah.c @@ -48,13 +48,13 @@ static bool ah_mt6(const struct sk_buff *skb, struct xt_action_param *par) err = ipv6_find_hdr(skb, &ptr, NEXTHDR_AUTH, NULL); if (err < 0) { if (err != -ENOENT) - *par->hotdrop = true; + par->hotdrop = true; return false; } ah = skb_header_pointer(skb, ptr, sizeof(_ah), &_ah); if (ah == NULL) { - *par->hotdrop = true; + par->hotdrop = true; return false; } diff --git a/net/ipv6/netfilter/ip6t_eui64.c b/net/ipv6/netfilter/ip6t_eui64.c index f32fce34145..aab0706908c 100644 --- a/net/ipv6/netfilter/ip6t_eui64.c +++ b/net/ipv6/netfilter/ip6t_eui64.c @@ -27,7 +27,7 @@ eui64_mt6(const struct sk_buff *skb, struct xt_action_param *par) if (!(skb_mac_header(skb) >= skb->head && skb_mac_header(skb) + ETH_HLEN <= skb->data) && par->fragoff != 0) { - *par->hotdrop = true; + par->hotdrop = true; return false; } diff --git a/net/ipv6/netfilter/ip6t_frag.c b/net/ipv6/netfilter/ip6t_frag.c index fcc8c72f218..eda898fda6c 100644 --- a/net/ipv6/netfilter/ip6t_frag.c +++ b/net/ipv6/netfilter/ip6t_frag.c @@ -46,13 +46,13 @@ frag_mt6(const struct sk_buff *skb, struct xt_action_param *par) err = ipv6_find_hdr(skb, &ptr, NEXTHDR_FRAGMENT, NULL); if (err < 0) { if (err != -ENOENT) - *par->hotdrop = true; + par->hotdrop = true; return false; } fh = skb_header_pointer(skb, ptr, sizeof(_frag), &_frag); if (fh == NULL) { - *par->hotdrop = true; + par->hotdrop = true; return false; } diff --git a/net/ipv6/netfilter/ip6t_hbh.c b/net/ipv6/netfilter/ip6t_hbh.c index f8aebc098d7..59df051eaef 100644 --- a/net/ipv6/netfilter/ip6t_hbh.c +++ b/net/ipv6/netfilter/ip6t_hbh.c @@ -65,13 +65,13 @@ hbh_mt6(const struct sk_buff *skb, struct xt_action_param *par) NEXTHDR_HOP : NEXTHDR_DEST, NULL); if (err < 0) { if (err != -ENOENT) - *par->hotdrop = true; + par->hotdrop = true; return false; } oh = skb_header_pointer(skb, ptr, sizeof(_optsh), &_optsh); if (oh == NULL) { - *par->hotdrop = true; + par->hotdrop = true; return false; } diff --git a/net/ipv6/netfilter/ip6t_mh.c b/net/ipv6/netfilter/ip6t_mh.c index eb1c3d65271..0c90c66b199 100644 --- a/net/ipv6/netfilter/ip6t_mh.c +++ b/net/ipv6/netfilter/ip6t_mh.c @@ -47,14 +47,14 @@ static bool mh_mt6(const struct sk_buff *skb, struct xt_action_param *par) /* We've been asked to examine this packet, and we can't. Hence, no choice but to drop. */ pr_debug("Dropping evil MH tinygram.\n"); - *par->hotdrop = true; + par->hotdrop = true; return false; } if (mh->ip6mh_proto != IPPROTO_NONE) { pr_debug("Dropping invalid MH Payload Proto: %u\n", mh->ip6mh_proto); - *par->hotdrop = true; + par->hotdrop = true; return false; } diff --git a/net/ipv6/netfilter/ip6t_rt.c b/net/ipv6/netfilter/ip6t_rt.c index ee584693ee3..d8488c50a8e 100644 --- a/net/ipv6/netfilter/ip6t_rt.c +++ b/net/ipv6/netfilter/ip6t_rt.c @@ -52,13 +52,13 @@ static bool rt_mt6(const struct sk_buff *skb, struct xt_action_param *par) err = ipv6_find_hdr(skb, &ptr, NEXTHDR_ROUTING, NULL); if (err < 0) { if (err != -ENOENT) - *par->hotdrop = true; + par->hotdrop = true; return false; } rh = skb_header_pointer(skb, ptr, sizeof(_route), &_route); if (rh == NULL) { - *par->hotdrop = true; + par->hotdrop = true; return false; } diff --git a/net/netfilter/xt_connlimit.c b/net/netfilter/xt_connlimit.c index f130fd9817b..5c5b6b921b8 100644 --- a/net/netfilter/xt_connlimit.c +++ b/net/netfilter/xt_connlimit.c @@ -206,14 +206,14 @@ connlimit_mt(const struct sk_buff *skb, struct xt_action_param *par) if (connections < 0) { /* kmalloc failed, drop it entirely */ - *par->hotdrop = true; + par->hotdrop = true; return false; } return (connections > info->limit) ^ info->inverse; hotdrop: - *par->hotdrop = true; + par->hotdrop = true; return false; } diff --git a/net/netfilter/xt_dccp.c b/net/netfilter/xt_dccp.c index cc2c2919439..b63d2a3d80b 100644 --- a/net/netfilter/xt_dccp.c +++ b/net/netfilter/xt_dccp.c @@ -107,7 +107,7 @@ dccp_mt(const struct sk_buff *skb, struct xt_action_param *par) dh = skb_header_pointer(skb, par->thoff, sizeof(_dh), &_dh); if (dh == NULL) { - *par->hotdrop = true; + par->hotdrop = true; return false; } @@ -120,7 +120,7 @@ dccp_mt(const struct sk_buff *skb, struct xt_action_param *par) && DCCHECK(match_types(dh, info->typemask), XT_DCCP_TYPE, info->flags, info->invflags) && DCCHECK(match_option(info->option, skb, par->thoff, dh, - par->hotdrop), + &par->hotdrop), XT_DCCP_OPTION, info->flags, info->invflags); } diff --git a/net/netfilter/xt_esp.c b/net/netfilter/xt_esp.c index 7c2d8002055..171ba82b590 100644 --- a/net/netfilter/xt_esp.c +++ b/net/netfilter/xt_esp.c @@ -52,7 +52,7 @@ static bool esp_mt(const struct sk_buff *skb, struct xt_action_param *par) * can't. Hence, no choice but to drop. */ pr_debug("Dropping evil ESP tinygram.\n"); - *par->hotdrop = true; + par->hotdrop = true; return false; } diff --git a/net/netfilter/xt_hashlimit.c b/net/netfilter/xt_hashlimit.c index 25a6e548ec2..b46a8390896 100644 --- a/net/netfilter/xt_hashlimit.c +++ b/net/netfilter/xt_hashlimit.c @@ -562,7 +562,7 @@ hashlimit_mt(const struct sk_buff *skb, struct xt_action_param *par) return info->cfg.mode & XT_HASHLIMIT_INVERT; hotdrop: - *par->hotdrop = true; + par->hotdrop = true; return false; } diff --git a/net/netfilter/xt_multiport.c b/net/netfilter/xt_multiport.c index 52beb68256c..ac1d3c3d09e 100644 --- a/net/netfilter/xt_multiport.c +++ b/net/netfilter/xt_multiport.c @@ -87,7 +87,7 @@ multiport_mt(const struct sk_buff *skb, struct xt_action_param *par) * can't. Hence, no choice but to drop. */ pr_debug("Dropping evil offset=0 tinygram.\n"); - *par->hotdrop = true; + par->hotdrop = true; return false; } diff --git a/net/netfilter/xt_recent.c b/net/netfilter/xt_recent.c index 2808a7e3394..76aec6a4476 100644 --- a/net/netfilter/xt_recent.c +++ b/net/netfilter/xt_recent.c @@ -268,7 +268,7 @@ recent_mt(const struct sk_buff *skb, struct xt_action_param *par) goto out; e = recent_entry_init(t, &addr, par->family, ttl); if (e == NULL) - *par->hotdrop = true; + par->hotdrop = true; ret = !ret; goto out; } diff --git a/net/netfilter/xt_sctp.c b/net/netfilter/xt_sctp.c index 94d8b5deb2d..c04fcf385c5 100644 --- a/net/netfilter/xt_sctp.c +++ b/net/netfilter/xt_sctp.c @@ -128,7 +128,7 @@ sctp_mt(const struct sk_buff *skb, struct xt_action_param *par) sh = skb_header_pointer(skb, par->thoff, sizeof(_sh), &_sh); if (sh == NULL) { pr_debug("Dropping evil TCP offset=0 tinygram.\n"); - *par->hotdrop = true; + par->hotdrop = true; return false; } pr_debug("spt: %d\tdpt: %d\n", ntohs(sh->source), ntohs(sh->dest)); @@ -140,7 +140,7 @@ sctp_mt(const struct sk_buff *skb, struct xt_action_param *par) && ntohs(sh->dest) <= info->dpts[1], XT_SCTP_DEST_PORTS, info->flags, info->invflags) && SCCHECK(match_packet(skb, par->thoff + sizeof(sctp_sctphdr_t), - info, par->hotdrop), + info, &par->hotdrop), XT_SCTP_CHUNK_TYPES, info->flags, info->invflags); } diff --git a/net/netfilter/xt_tcpmss.c b/net/netfilter/xt_tcpmss.c index f90728f6b8a..c53d4d18ead 100644 --- a/net/netfilter/xt_tcpmss.c +++ b/net/netfilter/xt_tcpmss.c @@ -73,7 +73,7 @@ out: return info->invert; dropit: - *par->hotdrop = true; + par->hotdrop = true; return false; } diff --git a/net/netfilter/xt_tcpudp.c b/net/netfilter/xt_tcpudp.c index dedde33c9db..c14d4645daa 100644 --- a/net/netfilter/xt_tcpudp.c +++ b/net/netfilter/xt_tcpudp.c @@ -77,7 +77,7 @@ static bool tcp_mt(const struct sk_buff *skb, struct xt_action_param *par) */ if (par->fragoff == 1) { pr_debug("Dropping evil TCP offset=1 frag.\n"); - *par->hotdrop = true; + par->hotdrop = true; } /* Must not be a fragment. */ return false; @@ -90,7 +90,7 @@ static bool tcp_mt(const struct sk_buff *skb, struct xt_action_param *par) /* We've been asked to examine this packet, and we can't. Hence, no choice but to drop. */ pr_debug("Dropping evil TCP offset=0 tinygram.\n"); - *par->hotdrop = true; + par->hotdrop = true; return false; } @@ -108,13 +108,13 @@ static bool tcp_mt(const struct sk_buff *skb, struct xt_action_param *par) return false; if (tcpinfo->option) { if (th->doff * 4 < sizeof(_tcph)) { - *par->hotdrop = true; + par->hotdrop = true; return false; } if (!tcp_find_option(tcpinfo->option, skb, par->thoff, th->doff*4 - sizeof(_tcph), tcpinfo->invflags & XT_TCP_INV_OPTION, - par->hotdrop)) + &par->hotdrop)) return false; } return true; @@ -143,7 +143,7 @@ static bool udp_mt(const struct sk_buff *skb, struct xt_action_param *par) /* We've been asked to examine this packet, and we can't. Hence, no choice but to drop. */ pr_debug("Dropping evil UDP tinygram.\n"); - *par->hotdrop = true; + par->hotdrop = true; return false; } -- cgit v1.2.3 From 107e7be628821dcb78c43adce0331e8ddb40eabd Mon Sep 17 00:00:00 2001 From: Allan Stephens Date: Tue, 11 May 2010 14:30:08 +0000 Subject: tipc: Add support for "-s" configuration option Provide initial support for displaying overall TIPC status/statistics information at runtime. Currently, only version info for the TIPC kernel module is displayed. Signed-off-by: Allan Stephens Signed-off-by: Paul Gortmaker Signed-off-by: David S. Miller --- include/linux/tipc_config.h | 1 + net/tipc/config.c | 40 +++++++++++++++++++++++++++++++++++++++- net/tipc/core.c | 2 -- net/tipc/core.h | 3 +++ 4 files changed, 43 insertions(+), 3 deletions(-) (limited to 'include/linux') diff --git a/include/linux/tipc_config.h b/include/linux/tipc_config.h index 2bc6fa4adeb..9cde86c3241 100644 --- a/include/linux/tipc_config.h +++ b/include/linux/tipc_config.h @@ -74,6 +74,7 @@ #define TIPC_CMD_SHOW_NAME_TABLE 0x0005 /* tx name_tbl_query, rx ultra_string */ #define TIPC_CMD_SHOW_PORTS 0x0006 /* tx none, rx ultra_string */ #define TIPC_CMD_SHOW_LINK_STATS 0x000B /* tx link_name, rx ultra_string */ +#define TIPC_CMD_SHOW_STATS 0x000F /* tx unsigned, rx ultra_string */ #if 0 #define TIPC_CMD_SHOW_PORT_STATS 0x0008 /* tx port_ref, rx ultra_string */ diff --git a/net/tipc/config.c b/net/tipc/config.c index 7370241412c..961d1b09714 100644 --- a/net/tipc/config.c +++ b/net/tipc/config.c @@ -246,13 +246,48 @@ static void cfg_cmd_event(struct tipc_cmd_msg *msg, default: rv = tipc_cfg_cmd(msg, data, sz, (u32 *)&msg_sect[1].iov_len, orig); } - exit: +exit: rmsg.result_len = htonl(msg_sect[1].iov_len); rmsg.retval = htonl(rv); tipc_cfg_respond(msg_sect, 2u, orig); } #endif +#define MAX_STATS_INFO 2000 + +static struct sk_buff *tipc_show_stats(void) +{ + struct sk_buff *buf; + struct tlv_desc *rep_tlv; + struct print_buf pb; + int str_len; + u32 value; + + if (!TLV_CHECK(req_tlv_area, req_tlv_space, TIPC_TLV_UNSIGNED)) + return tipc_cfg_reply_error_string(TIPC_CFG_TLV_ERROR); + + value = ntohl(*(u32 *)TLV_DATA(req_tlv_area)); + if (value != 0) + return tipc_cfg_reply_error_string("unsupported argument"); + + buf = tipc_cfg_reply_alloc(TLV_SPACE(MAX_STATS_INFO)); + if (buf == NULL) + return NULL; + + rep_tlv = (struct tlv_desc *)buf->data; + tipc_printbuf_init(&pb, (char *)TLV_DATA(rep_tlv), MAX_STATS_INFO); + + tipc_printf(&pb, "TIPC version " TIPC_MOD_VER "\n"); + + /* Use additional tipc_printf()'s to return more info ... */ + + str_len = tipc_printbuf_validate(&pb); + skb_put(buf, TLV_SPACE(str_len)); + TLV_SET(rep_tlv, TIPC_TLV_ULTRA_STRING, NULL, str_len); + + return buf; +} + static struct sk_buff *cfg_enable_bearer(void) { struct tipc_bearer_config *args; @@ -536,6 +571,9 @@ struct sk_buff *tipc_cfg_do_cmd(u32 orig_node, u16 cmd, const void *request_area case TIPC_CMD_DUMP_LOG: rep_tlv_buf = tipc_log_dump(); break; + case TIPC_CMD_SHOW_STATS: + rep_tlv_buf = tipc_show_stats(); + break; case TIPC_CMD_SET_LINK_TOL: case TIPC_CMD_SET_LINK_PRI: case TIPC_CMD_SET_LINK_WINDOW: diff --git a/net/tipc/core.c b/net/tipc/core.c index 4e84c8431f3..b47d1842a97 100644 --- a/net/tipc/core.c +++ b/net/tipc/core.c @@ -49,8 +49,6 @@ #include "config.h" -#define TIPC_MOD_VER "2.0.0" - #ifndef CONFIG_TIPC_ZONES #define CONFIG_TIPC_ZONES 3 #endif diff --git a/net/tipc/core.h b/net/tipc/core.h index c58a1d16563..1e149f55f3e 100644 --- a/net/tipc/core.h +++ b/net/tipc/core.h @@ -59,6 +59,9 @@ #include #include + +#define TIPC_MOD_VER "2.0.0" + /* * TIPC sanity test macros */ -- cgit v1.2.3 From 8e1c298c01d4596fa2837913e531a93a791a7bec Mon Sep 17 00:00:00 2001 From: Allan Stephens Date: Tue, 11 May 2010 14:30:09 +0000 Subject: tipc: Update commenting in TIPC API Eliminate comments in TIPC's main API files that are either obsolete, incorrect, misleading, or unhelpful. It also adds in one new comment. Signed-off-by: Allan Stephens Signed-off-by: Paul Gortmaker Signed-off-by: David S. Miller --- include/linux/tipc.h | 6 +++--- include/net/tipc/tipc.h | 16 ++++++++-------- 2 files changed, 11 insertions(+), 11 deletions(-) (limited to 'include/linux') diff --git a/include/linux/tipc.h b/include/linux/tipc.h index 9536d8aeadf..181c8d0e6f7 100644 --- a/include/linux/tipc.h +++ b/include/linux/tipc.h @@ -107,7 +107,7 @@ static inline unsigned int tipc_node(__u32 addr) * Message importance levels */ -#define TIPC_LOW_IMPORTANCE 0 /* default */ +#define TIPC_LOW_IMPORTANCE 0 #define TIPC_MEDIUM_IMPORTANCE 1 #define TIPC_HIGH_IMPORTANCE 2 #define TIPC_CRITICAL_IMPORTANCE 3 @@ -182,7 +182,7 @@ struct sockaddr_tipc { struct tipc_name_seq nameseq; struct { struct tipc_name name; - __u32 domain; /* 0: own zone */ + __u32 domain; } name; } addr; }; @@ -200,7 +200,7 @@ struct sockaddr_tipc { */ #define TIPC_IMPORTANCE 127 /* Default: TIPC_LOW_IMPORTANCE */ -#define TIPC_SRC_DROPPABLE 128 /* Default: 0 (resend congested msg) */ +#define TIPC_SRC_DROPPABLE 128 /* Default: based on socket type */ #define TIPC_DEST_DROPPABLE 129 /* Default: based on socket type */ #define TIPC_CONN_TIMEOUT 130 /* Default: 8000 (ms) */ #define TIPC_NODE_RECVQ_DEPTH 131 /* Default: none (read only) */ diff --git a/include/net/tipc/tipc.h b/include/net/tipc/tipc.h index 9566608c88c..15af6dca0b4 100644 --- a/include/net/tipc/tipc.h +++ b/include/net/tipc/tipc.h @@ -2,7 +2,7 @@ * include/net/tipc/tipc.h: Main include file for TIPC users * * Copyright (c) 2003-2006, Ericsson AB - * Copyright (c) 2005, Wind River Systems + * Copyright (c) 2005,2010 Wind River Systems * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -126,7 +126,7 @@ int tipc_createport(unsigned int tipc_user, tipc_msg_event message_cb, tipc_named_msg_event named_message_cb, tipc_conn_msg_event conn_message_cb, - tipc_continue_event continue_event_cb,/* May be zero */ + tipc_continue_event continue_event_cb, u32 *portref); int tipc_deleteport(u32 portref); @@ -145,13 +145,13 @@ int tipc_set_portunreturnable(u32 portref, unsigned int isunreturnable); int tipc_publish(u32 portref, unsigned int scope, struct tipc_name_seq const *name_seq); int tipc_withdraw(u32 portref, unsigned int scope, - struct tipc_name_seq const *name_seq); /* 0: all */ + struct tipc_name_seq const *name_seq); int tipc_connect2port(u32 portref, struct tipc_portid const *port); int tipc_disconnect(u32 portref); -int tipc_shutdown(u32 ref); /* Sends SHUTDOWN msg */ +int tipc_shutdown(u32 ref); int tipc_isconnected(u32 portref, int *isconnected); @@ -176,7 +176,7 @@ int tipc_send_buf(u32 portref, int tipc_send2name(u32 portref, struct tipc_name const *name, - u32 domain, /* 0:own zone */ + u32 domain, unsigned int num_sect, struct iovec const *msg_sect); @@ -188,7 +188,7 @@ int tipc_send_buf2name(u32 portref, int tipc_forward2name(u32 portref, struct tipc_name const *name, - u32 domain, /*0: own zone */ + u32 domain, unsigned int section_count, struct iovec const *msg_sect, struct tipc_portid const *origin, @@ -228,14 +228,14 @@ int tipc_forward_buf2port(u32 portref, int tipc_multicast(u32 portref, struct tipc_name_seq const *seq, - u32 domain, /* 0:own zone */ + u32 domain, /* currently unused */ unsigned int section_count, struct iovec const *msg); #if 0 int tipc_multicast_buf(u32 portref, struct tipc_name_seq const *seq, - u32 domain, /* 0:own zone */ + u32 domain, void *buf, unsigned int size); #endif -- cgit v1.2.3 From 9f977fb7ae9ddf565b4800854212fb9a1ed6c2ea Mon Sep 17 00:00:00 2001 From: Octavian Purdila Date: Wed, 5 May 2010 00:26:55 +0000 Subject: sysctl: add proc_do_large_bitmap The new function can be used to read/write large bitmaps via /proc. A comma separated range format is used for compact output and input (e.g. 1,3-4,10-10). Writing into the file will first reset the bitmap then update it based on the given input. Signed-off-by: Octavian Purdila Signed-off-by: WANG Cong Cc: Eric W. Biederman Signed-off-by: David S. Miller --- include/linux/sysctl.h | 2 + kernel/sysctl.c | 161 +++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 163 insertions(+) (limited to 'include/linux') diff --git a/include/linux/sysctl.h b/include/linux/sysctl.h index f66014c90c9..7bb5cb64f3b 100644 --- a/include/linux/sysctl.h +++ b/include/linux/sysctl.h @@ -980,6 +980,8 @@ extern int proc_doulongvec_minmax(struct ctl_table *, int, void __user *, size_t *, loff_t *); extern int proc_doulongvec_ms_jiffies_minmax(struct ctl_table *table, int, void __user *, size_t *, loff_t *); +extern int proc_do_large_bitmap(struct ctl_table *, int, + void __user *, size_t *, loff_t *); /* * Register a set of sysctl names by calling register_sysctl_table diff --git a/kernel/sysctl.c b/kernel/sysctl.c index 4a976208de2..bcfb79e94ec 100644 --- a/kernel/sysctl.c +++ b/kernel/sysctl.c @@ -2049,6 +2049,16 @@ static size_t proc_skip_spaces(char **buf) return ret; } +static void proc_skip_char(char **buf, size_t *size, const char v) +{ + while (*size) { + if (**buf != v) + break; + (*size)--; + (*buf)++; + } +} + #define TMPBUFLEN 22 /** * proc_get_long - reads an ASCII formated integer from a user buffer @@ -2675,6 +2685,157 @@ static int proc_do_cad_pid(struct ctl_table *table, int write, return 0; } +/** + * proc_do_large_bitmap - read/write from/to a large bitmap + * @table: the sysctl table + * @write: %TRUE if this is a write to the sysctl file + * @buffer: the user buffer + * @lenp: the size of the user buffer + * @ppos: file position + * + * The bitmap is stored at table->data and the bitmap length (in bits) + * in table->maxlen. + * + * We use a range comma separated format (e.g. 1,3-4,10-10) so that + * large bitmaps may be represented in a compact manner. Writing into + * the file will clear the bitmap then update it with the given input. + * + * Returns 0 on success. + */ +int proc_do_large_bitmap(struct ctl_table *table, int write, + void __user *buffer, size_t *lenp, loff_t *ppos) +{ + int err = 0; + bool first = 1; + size_t left = *lenp; + unsigned long bitmap_len = table->maxlen; + unsigned long *bitmap = (unsigned long *) table->data; + unsigned long *tmp_bitmap = NULL; + char tr_a[] = { '-', ',', '\n' }, tr_b[] = { ',', '\n', 0 }, c; + + if (!bitmap_len || !left || (*ppos && !write)) { + *lenp = 0; + return 0; + } + + if (write) { + unsigned long page = 0; + char *kbuf; + + if (left > PAGE_SIZE - 1) + left = PAGE_SIZE - 1; + + page = __get_free_page(GFP_TEMPORARY); + kbuf = (char *) page; + if (!kbuf) + return -ENOMEM; + if (copy_from_user(kbuf, buffer, left)) { + free_page(page); + return -EFAULT; + } + kbuf[left] = 0; + + tmp_bitmap = kzalloc(BITS_TO_LONGS(bitmap_len) * sizeof(unsigned long), + GFP_KERNEL); + if (!tmp_bitmap) { + free_page(page); + return -ENOMEM; + } + proc_skip_char(&kbuf, &left, '\n'); + while (!err && left) { + unsigned long val_a, val_b; + bool neg; + + err = proc_get_long(&kbuf, &left, &val_a, &neg, tr_a, + sizeof(tr_a), &c); + if (err) + break; + if (val_a >= bitmap_len || neg) { + err = -EINVAL; + break; + } + + val_b = val_a; + if (left) { + kbuf++; + left--; + } + + if (c == '-') { + err = proc_get_long(&kbuf, &left, &val_b, + &neg, tr_b, sizeof(tr_b), + &c); + if (err) + break; + if (val_b >= bitmap_len || neg || + val_a > val_b) { + err = -EINVAL; + break; + } + if (left) { + kbuf++; + left--; + } + } + + while (val_a <= val_b) + set_bit(val_a++, tmp_bitmap); + + first = 0; + proc_skip_char(&kbuf, &left, '\n'); + } + free_page(page); + } else { + unsigned long bit_a, bit_b = 0; + + while (left) { + bit_a = find_next_bit(bitmap, bitmap_len, bit_b); + if (bit_a >= bitmap_len) + break; + bit_b = find_next_zero_bit(bitmap, bitmap_len, + bit_a + 1) - 1; + + if (!first) { + err = proc_put_char(&buffer, &left, ','); + if (err) + break; + } + err = proc_put_long(&buffer, &left, bit_a, false); + if (err) + break; + if (bit_a != bit_b) { + err = proc_put_char(&buffer, &left, '-'); + if (err) + break; + err = proc_put_long(&buffer, &left, bit_b, false); + if (err) + break; + } + + first = 0; bit_b++; + } + if (!err) + err = proc_put_char(&buffer, &left, '\n'); + } + + if (!err) { + if (write) { + if (*ppos) + bitmap_or(bitmap, bitmap, tmp_bitmap, bitmap_len); + else + memcpy(bitmap, tmp_bitmap, + BITS_TO_LONGS(bitmap_len) * sizeof(unsigned long)); + } + kfree(tmp_bitmap); + *lenp -= left; + *ppos += *lenp; + return 0; + } else { + kfree(tmp_bitmap); + return err; + } +} + #else /* CONFIG_PROC_FS */ int proc_dostring(struct ctl_table *table, int write, -- cgit v1.2.3 From a14462f1bd4d3962994f518459102000438665aa Mon Sep 17 00:00:00 2001 From: Jiri Pirko Date: Thu, 6 May 2010 01:33:53 +0000 Subject: net: adjust handle_macvlan to pass port struct to hook Now there's null check here and also again in the hook. Looking at bridge bits which are simmilar, port structure is rcu_dereferenced right away in handle_bridge and passed to hook. Looks nicer. Signed-off-by: Jiri Pirko Acked-by: Patrick McHardy Signed-off-by: David S. Miller --- drivers/net/macvlan.c | 8 ++------ include/linux/if_macvlan.h | 3 ++- net/core/dev.c | 10 +++++++--- 3 files changed, 11 insertions(+), 10 deletions(-) (limited to 'include/linux') diff --git a/drivers/net/macvlan.c b/drivers/net/macvlan.c index 9a939d828b4..1b78c0057a8 100644 --- a/drivers/net/macvlan.c +++ b/drivers/net/macvlan.c @@ -145,19 +145,15 @@ static void macvlan_broadcast(struct sk_buff *skb, } /* called under rcu_read_lock() from netif_receive_skb */ -static struct sk_buff *macvlan_handle_frame(struct sk_buff *skb) +static struct sk_buff *macvlan_handle_frame(struct macvlan_port *port, + struct sk_buff *skb) { const struct ethhdr *eth = eth_hdr(skb); - const struct macvlan_port *port; const struct macvlan_dev *vlan; const struct macvlan_dev *src; struct net_device *dev; unsigned int len; - port = rcu_dereference(skb->dev->macvlan_port); - if (port == NULL) - return skb; - if (is_multicast_ether_addr(eth->h_dest)) { src = macvlan_hash_lookup(port, eth->h_source); if (!src) diff --git a/include/linux/if_macvlan.h b/include/linux/if_macvlan.h index b78a712247d..9ea047aca79 100644 --- a/include/linux/if_macvlan.h +++ b/include/linux/if_macvlan.h @@ -85,6 +85,7 @@ extern netdev_tx_t macvlan_start_xmit(struct sk_buff *skb, struct net_device *dev); -extern struct sk_buff *(*macvlan_handle_frame_hook)(struct sk_buff *); +extern struct sk_buff *(*macvlan_handle_frame_hook)(struct macvlan_port *, + struct sk_buff *); #endif /* _LINUX_IF_MACVLAN_H */ diff --git a/net/core/dev.c b/net/core/dev.c index 3daee30a7c8..5cbba0927a8 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -2612,7 +2612,8 @@ static inline struct sk_buff *handle_bridge(struct sk_buff *skb, #endif #if defined(CONFIG_MACVLAN) || defined(CONFIG_MACVLAN_MODULE) -struct sk_buff *(*macvlan_handle_frame_hook)(struct sk_buff *skb) __read_mostly; +struct sk_buff *(*macvlan_handle_frame_hook)(struct macvlan_port *p, + struct sk_buff *skb) __read_mostly; EXPORT_SYMBOL_GPL(macvlan_handle_frame_hook); static inline struct sk_buff *handle_macvlan(struct sk_buff *skb, @@ -2620,14 +2621,17 @@ static inline struct sk_buff *handle_macvlan(struct sk_buff *skb, int *ret, struct net_device *orig_dev) { - if (skb->dev->macvlan_port == NULL) + struct macvlan_port *port; + + port = rcu_dereference(skb->dev->macvlan_port); + if (!port) return skb; if (*pt_prev) { *ret = deliver_skb(skb, *pt_prev, orig_dev); *pt_prev = NULL; } - return macvlan_handle_frame_hook(skb); + return macvlan_handle_frame_hook(port, skb); } #else #define handle_macvlan(skb, pt_prev, ret, orig_dev) (skb) -- cgit v1.2.3 From 3b098e2d7c693796cc4dffb07caa249fc0f70771 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Sat, 15 May 2010 23:57:10 -0700 Subject: net: Consistent skb timestamping With RPS inclusion, skb timestamping is not consistent in RX path. If netif_receive_skb() is used, its deferred after RPS dispatch. If netif_rx() is used, its done before RPS dispatch. This can give strange tcpdump timestamps results. I think timestamping should be done as soon as possible in the receive path, to get meaningful values (ie timestamps taken at the time packet was delivered by NIC driver to our stack), even if NAPI already can defer timestamping a bit (RPS can help to reduce the gap) Tom Herbert prefer to sample timestamps after RPS dispatch. In case sampling is expensive (HPET/acpi_pm on x86), this makes sense. Let admins switch from one mode to another, using a new sysctl, /proc/sys/net/core/netdev_tstamp_prequeue Its default value (1), means timestamps are taken as soon as possible, before backlog queueing, giving accurate timestamps. Setting a 0 value permits to sample timestamps when processing backlog, after RPS dispatch, to lower the load of the pre-RPS cpu. Signed-off-by: Eric Dumazet Signed-off-by: David S. Miller --- Documentation/sysctl/net.txt | 10 +++++++++ include/linux/netdevice.h | 1 + net/core/dev.c | 50 +++++++++++++++++++++++++++----------------- net/core/sysctl_net_core.c | 7 +++++++ 4 files changed, 49 insertions(+), 19 deletions(-) (limited to 'include/linux') diff --git a/Documentation/sysctl/net.txt b/Documentation/sysctl/net.txt index df38ef046f8..cbd05ffc606 100644 --- a/Documentation/sysctl/net.txt +++ b/Documentation/sysctl/net.txt @@ -84,6 +84,16 @@ netdev_max_backlog Maximum number of packets, queued on the INPUT side, when the interface receives packets faster than kernel can process them. +netdev_tstamp_prequeue +---------------------- + +If set to 0, RX packet timestamps can be sampled after RPS processing, when +the target CPU processes packets. It might give some delay on timestamps, but +permit to distribute the load on several cpus. + +If set to 1 (default), timestamps are sampled as soon as possible, before +queueing. + optmem_max ---------- diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index 69022d47d6f..c1b2341897c 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -2100,6 +2100,7 @@ extern const struct net_device_stats *dev_get_stats(struct net_device *dev); extern void dev_txq_stats_fold(const struct net_device *dev, struct net_device_stats *stats); extern int netdev_max_backlog; +extern int netdev_tstamp_prequeue; extern int weight_p; extern int netdev_set_master(struct net_device *dev, struct net_device *master); extern int skb_checksum_help(struct sk_buff *skb); diff --git a/net/core/dev.c b/net/core/dev.c index 5cbba0927a8..988e42912e7 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -1454,7 +1454,7 @@ void net_disable_timestamp(void) } EXPORT_SYMBOL(net_disable_timestamp); -static inline void net_timestamp(struct sk_buff *skb) +static inline void net_timestamp_set(struct sk_buff *skb) { if (atomic_read(&netstamp_needed)) __net_timestamp(skb); @@ -1462,6 +1462,12 @@ static inline void net_timestamp(struct sk_buff *skb) skb->tstamp.tv64 = 0; } +static inline void net_timestamp_check(struct sk_buff *skb) +{ + if (!skb->tstamp.tv64 && atomic_read(&netstamp_needed)) + __net_timestamp(skb); +} + /** * dev_forward_skb - loopback an skb to another netif * @@ -1508,9 +1514,9 @@ static void dev_queue_xmit_nit(struct sk_buff *skb, struct net_device *dev) #ifdef CONFIG_NET_CLS_ACT if (!(skb->tstamp.tv64 && (G_TC_FROM(skb->tc_verd) & AT_INGRESS))) - net_timestamp(skb); + net_timestamp_set(skb); #else - net_timestamp(skb); + net_timestamp_set(skb); #endif rcu_read_lock(); @@ -2201,6 +2207,7 @@ EXPORT_SYMBOL(dev_queue_xmit); =======================================================================*/ int netdev_max_backlog __read_mostly = 1000; +int netdev_tstamp_prequeue __read_mostly = 1; int netdev_budget __read_mostly = 300; int weight_p __read_mostly = 64; /* old backlog weight */ @@ -2465,8 +2472,8 @@ int netif_rx(struct sk_buff *skb) if (netpoll_rx(skb)) return NET_RX_DROP; - if (!skb->tstamp.tv64) - net_timestamp(skb); + if (netdev_tstamp_prequeue) + net_timestamp_check(skb); #ifdef CONFIG_RPS { @@ -2791,8 +2798,8 @@ static int __netif_receive_skb(struct sk_buff *skb) int ret = NET_RX_DROP; __be16 type; - if (!skb->tstamp.tv64) - net_timestamp(skb); + if (!netdev_tstamp_prequeue) + net_timestamp_check(skb); if (vlan_tx_tag_present(skb) && vlan_hwaccel_do_receive(skb)) return NET_RX_SUCCESS; @@ -2910,23 +2917,28 @@ out: */ int netif_receive_skb(struct sk_buff *skb) { + if (netdev_tstamp_prequeue) + net_timestamp_check(skb); + #ifdef CONFIG_RPS - struct rps_dev_flow voidflow, *rflow = &voidflow; - int cpu, ret; + { + struct rps_dev_flow voidflow, *rflow = &voidflow; + int cpu, ret; - rcu_read_lock(); + rcu_read_lock(); + + cpu = get_rps_cpu(skb->dev, skb, &rflow); - cpu = get_rps_cpu(skb->dev, skb, &rflow); + if (cpu >= 0) { + ret = enqueue_to_backlog(skb, cpu, &rflow->last_qtail); + rcu_read_unlock(); + } else { + rcu_read_unlock(); + ret = __netif_receive_skb(skb); + } - if (cpu >= 0) { - ret = enqueue_to_backlog(skb, cpu, &rflow->last_qtail); - rcu_read_unlock(); - } else { - rcu_read_unlock(); - ret = __netif_receive_skb(skb); + return ret; } - - return ret; #else return __netif_receive_skb(skb); #endif diff --git a/net/core/sysctl_net_core.c b/net/core/sysctl_net_core.c index dcc7d25996a..01eee5d984b 100644 --- a/net/core/sysctl_net_core.c +++ b/net/core/sysctl_net_core.c @@ -121,6 +121,13 @@ static struct ctl_table net_core_table[] = { .mode = 0644, .proc_handler = proc_dointvec }, + { + .procname = "netdev_tstamp_prequeue", + .data = &netdev_tstamp_prequeue, + .maxlen = sizeof(int), + .mode = 0644, + .proc_handler = proc_dointvec + }, { .procname = "message_cost", .data = &net_ratelimit_state.interval, -- cgit v1.2.3 From 7fee226ad2397b635e2fd565a59ca3ae08a164cd Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Tue, 11 May 2010 23:19:48 +0000 Subject: net: add a noref bit on skb dst Use low order bit of skb->_skb_dst to tell dst is not refcounted. Change _skb_dst to _skb_refdst to make sure all uses are catched. skb_dst() returns the dst, regardless of noref bit set or not, but with a lockdep check to make sure a noref dst is not given if current user is not rcu protected. New skb_dst_set_noref() helper to set an notrefcounted dst on a skb. (with lockdep check) skb_dst_drop() drops a reference only if skb dst was refcounted. skb_dst_force() helper is used to force a refcount on dst, when skb is queued and not anymore RCU protected. Use skb_dst_force() in __sk_add_backlog(), __dev_xmit_skb() if !IFF_XMIT_DST_RELEASE or skb enqueued on qdisc queue, in sock_queue_rcv_skb(), in __nf_queue(). Use skb_dst_force() in dev_requeue_skb(). Note: dst_use_noref() still dirties dst, we might transform it later to do one dirtying per jiffies. Signed-off-by: Eric Dumazet Signed-off-by: David S. Miller --- include/linux/skbuff.h | 58 ++++++++++++++++++++++++++++++++++++++++++++---- include/net/dst.h | 48 ++++++++++++++++++++++++++++++++++++--- include/net/sock.h | 13 ++++++----- net/core/dev.c | 3 +++ net/core/skbuff.c | 2 +- net/core/sock.c | 6 +++++ net/ipv4/icmp.c | 6 ++--- net/ipv4/ip_options.c | 9 ++++---- net/ipv4/netfilter.c | 6 ++--- net/ipv4/route.c | 2 +- net/netfilter/nf_queue.c | 2 ++ net/sched/sch_generic.c | 4 +++- 12 files changed, 134 insertions(+), 25 deletions(-) (limited to 'include/linux') diff --git a/include/linux/skbuff.h b/include/linux/skbuff.h index c9525bce80f..7cdfb4d5284 100644 --- a/include/linux/skbuff.h +++ b/include/linux/skbuff.h @@ -264,7 +264,7 @@ typedef unsigned char *sk_buff_data_t; * @transport_header: Transport layer header * @network_header: Network layer header * @mac_header: Link layer header - * @_skb_dst: destination entry + * @_skb_refdst: destination entry (with norefcount bit) * @sp: the security path, used for xfrm * @cb: Control buffer. Free for use by every layer. Put private vars here * @len: Length of actual data @@ -328,7 +328,7 @@ struct sk_buff { */ char cb[48] __aligned(8); - unsigned long _skb_dst; + unsigned long _skb_refdst; #ifdef CONFIG_XFRM struct sec_path *sp; #endif @@ -419,14 +419,64 @@ struct sk_buff { #include +/* + * skb might have a dst pointer attached, refcounted or not. + * _skb_refdst low order bit is set if refcount was _not_ taken + */ +#define SKB_DST_NOREF 1UL +#define SKB_DST_PTRMASK ~(SKB_DST_NOREF) + +/** + * skb_dst - returns skb dst_entry + * @skb: buffer + * + * Returns skb dst_entry, regardless of reference taken or not. + */ static inline struct dst_entry *skb_dst(const struct sk_buff *skb) { - return (struct dst_entry *)skb->_skb_dst; + /* If refdst was not refcounted, check we still are in a + * rcu_read_lock section + */ + WARN_ON((skb->_skb_refdst & SKB_DST_NOREF) && + !rcu_read_lock_held() && + !rcu_read_lock_bh_held()); + return (struct dst_entry *)(skb->_skb_refdst & SKB_DST_PTRMASK); } +/** + * skb_dst_set - sets skb dst + * @skb: buffer + * @dst: dst entry + * + * Sets skb dst, assuming a reference was taken on dst and should + * be released by skb_dst_drop() + */ static inline void skb_dst_set(struct sk_buff *skb, struct dst_entry *dst) { - skb->_skb_dst = (unsigned long)dst; + skb->_skb_refdst = (unsigned long)dst; +} + +/** + * skb_dst_set_noref - sets skb dst, without a reference + * @skb: buffer + * @dst: dst entry + * + * Sets skb dst, assuming a reference was not taken on dst + * skb_dst_drop() should not dst_release() this dst + */ +static inline void skb_dst_set_noref(struct sk_buff *skb, struct dst_entry *dst) +{ + WARN_ON(!rcu_read_lock_held() && !rcu_read_lock_bh_held()); + skb->_skb_refdst = (unsigned long)dst | SKB_DST_NOREF; +} + +/** + * skb_dst_is_noref - Test if skb dst isnt refcounted + * @skb: buffer + */ +static inline bool skb_dst_is_noref(const struct sk_buff *skb) +{ + return (skb->_skb_refdst & SKB_DST_NOREF) && skb_dst(skb); } static inline struct rtable *skb_rtable(const struct sk_buff *skb) diff --git a/include/net/dst.h b/include/net/dst.h index aac5a5fcfda..27207a13f2a 100644 --- a/include/net/dst.h +++ b/include/net/dst.h @@ -168,6 +168,12 @@ static inline void dst_use(struct dst_entry *dst, unsigned long time) dst->lastuse = time; } +static inline void dst_use_noref(struct dst_entry *dst, unsigned long time) +{ + dst->__use++; + dst->lastuse = time; +} + static inline struct dst_entry * dst_clone(struct dst_entry * dst) { @@ -177,11 +183,47 @@ struct dst_entry * dst_clone(struct dst_entry * dst) } extern void dst_release(struct dst_entry *dst); + +static inline void refdst_drop(unsigned long refdst) +{ + if (!(refdst & SKB_DST_NOREF)) + dst_release((struct dst_entry *)(refdst & SKB_DST_PTRMASK)); +} + +/** + * skb_dst_drop - drops skb dst + * @skb: buffer + * + * Drops dst reference count if a reference was taken. + */ static inline void skb_dst_drop(struct sk_buff *skb) { - if (skb->_skb_dst) - dst_release(skb_dst(skb)); - skb->_skb_dst = 0UL; + if (skb->_skb_refdst) { + refdst_drop(skb->_skb_refdst); + skb->_skb_refdst = 0UL; + } +} + +static inline void skb_dst_copy(struct sk_buff *nskb, const struct sk_buff *oskb) +{ + nskb->_skb_refdst = oskb->_skb_refdst; + if (!(nskb->_skb_refdst & SKB_DST_NOREF)) + dst_clone(skb_dst(nskb)); +} + +/** + * skb_dst_force - makes sure skb dst is refcounted + * @skb: buffer + * + * If dst is not yet refcounted, let's do it + */ +static inline void skb_dst_force(struct sk_buff *skb) +{ + if (skb_dst_is_noref(skb)) { + WARN_ON(!rcu_read_lock_held()); + skb->_skb_refdst &= ~SKB_DST_NOREF; + dst_clone(skb_dst(skb)); + } } /* Children define the path of the packet through the diff --git a/include/net/sock.h b/include/net/sock.h index aed16eb9db4..5697caf8cc7 100644 --- a/include/net/sock.h +++ b/include/net/sock.h @@ -600,12 +600,15 @@ static inline int sk_stream_memory_free(struct sock *sk) /* OOB backlog add */ static inline void __sk_add_backlog(struct sock *sk, struct sk_buff *skb) { - if (!sk->sk_backlog.tail) { - sk->sk_backlog.head = sk->sk_backlog.tail = skb; - } else { + /* dont let skb dst not refcounted, we are going to leave rcu lock */ + skb_dst_force(skb); + + if (!sk->sk_backlog.tail) + sk->sk_backlog.head = skb; + else sk->sk_backlog.tail->next = skb; - sk->sk_backlog.tail = skb; - } + + sk->sk_backlog.tail = skb; skb->next = NULL; } diff --git a/net/core/dev.c b/net/core/dev.c index cdcb9cbedf4..6c820650b80 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -2052,6 +2052,8 @@ static inline int __dev_xmit_skb(struct sk_buff *skb, struct Qdisc *q, * waiting to be sent out; and the qdisc is not running - * xmit the skb directly. */ + if (!(dev->priv_flags & IFF_XMIT_DST_RELEASE)) + skb_dst_force(skb); __qdisc_update_bstats(q, skb->len); if (sch_direct_xmit(skb, q, dev, txq, root_lock)) __qdisc_run(q); @@ -2060,6 +2062,7 @@ static inline int __dev_xmit_skb(struct sk_buff *skb, struct Qdisc *q, rc = NET_XMIT_SUCCESS; } else { + skb_dst_force(skb); rc = qdisc_enqueue_root(skb, q); qdisc_run(q); } diff --git a/net/core/skbuff.c b/net/core/skbuff.c index a9b0e1f7780..c543dd25243 100644 --- a/net/core/skbuff.c +++ b/net/core/skbuff.c @@ -520,7 +520,7 @@ static void __copy_skb_header(struct sk_buff *new, const struct sk_buff *old) new->transport_header = old->transport_header; new->network_header = old->network_header; new->mac_header = old->mac_header; - skb_dst_set(new, dst_clone(skb_dst(old))); + skb_dst_copy(new, old); new->rxhash = old->rxhash; #ifdef CONFIG_XFRM new->sp = secpath_get(old->sp); diff --git a/net/core/sock.c b/net/core/sock.c index 63530a03b8c..bf88a167c8f 100644 --- a/net/core/sock.c +++ b/net/core/sock.c @@ -307,6 +307,11 @@ int sock_queue_rcv_skb(struct sock *sk, struct sk_buff *skb) */ skb_len = skb->len; + /* we escape from rcu protected region, make sure we dont leak + * a norefcounted dst + */ + skb_dst_force(skb); + spin_lock_irqsave(&list->lock, flags); skb->dropcount = atomic_read(&sk->sk_drops); __skb_queue_tail(list, skb); @@ -1536,6 +1541,7 @@ static void __release_sock(struct sock *sk) do { struct sk_buff *next = skb->next; + WARN_ON_ONCE(skb_dst_is_noref(skb)); skb->next = NULL; sk_backlog_rcv(sk, skb); diff --git a/net/ipv4/icmp.c b/net/ipv4/icmp.c index f3d339f728b..d65e9215bcd 100644 --- a/net/ipv4/icmp.c +++ b/net/ipv4/icmp.c @@ -587,20 +587,20 @@ void icmp_send(struct sk_buff *skb_in, int type, int code, __be32 info) err = __ip_route_output_key(net, &rt2, &fl); else { struct flowi fl2 = {}; - struct dst_entry *odst; + unsigned long orefdst; fl2.fl4_dst = fl.fl4_src; if (ip_route_output_key(net, &rt2, &fl2)) goto relookup_failed; /* Ugh! */ - odst = skb_dst(skb_in); + orefdst = skb_in->_skb_refdst; /* save old refdst */ err = ip_route_input(skb_in, fl.fl4_dst, fl.fl4_src, RT_TOS(tos), rt2->u.dst.dev); dst_release(&rt2->u.dst); rt2 = skb_rtable(skb_in); - skb_dst_set(skb_in, odst); + skb_in->_skb_refdst = orefdst; /* restore old refdst */ } if (err) diff --git a/net/ipv4/ip_options.c b/net/ipv4/ip_options.c index 4c09a31fd14..3244133c24f 100644 --- a/net/ipv4/ip_options.c +++ b/net/ipv4/ip_options.c @@ -601,6 +601,7 @@ int ip_options_rcv_srr(struct sk_buff *skb) unsigned char *optptr = skb_network_header(skb) + opt->srr; struct rtable *rt = skb_rtable(skb); struct rtable *rt2; + unsigned long orefdst; int err; if (!opt->srr) @@ -624,16 +625,16 @@ int ip_options_rcv_srr(struct sk_buff *skb) } memcpy(&nexthop, &optptr[srrptr-1], 4); - rt = skb_rtable(skb); + orefdst = skb->_skb_refdst; skb_dst_set(skb, NULL); err = ip_route_input(skb, nexthop, iph->saddr, iph->tos, skb->dev); rt2 = skb_rtable(skb); if (err || (rt2->rt_type != RTN_UNICAST && rt2->rt_type != RTN_LOCAL)) { - ip_rt_put(rt2); - skb_dst_set(skb, &rt->u.dst); + skb_dst_drop(skb); + skb->_skb_refdst = orefdst; return -EINVAL; } - ip_rt_put(rt); + refdst_drop(orefdst); if (rt2->rt_type != RTN_LOCAL) break; /* Superfast 8) loopback forward */ diff --git a/net/ipv4/netfilter.c b/net/ipv4/netfilter.c index 82fb43c5c59..07de855e217 100644 --- a/net/ipv4/netfilter.c +++ b/net/ipv4/netfilter.c @@ -17,7 +17,7 @@ int ip_route_me_harder(struct sk_buff *skb, unsigned addr_type) const struct iphdr *iph = ip_hdr(skb); struct rtable *rt; struct flowi fl = {}; - struct dst_entry *odst; + unsigned long orefdst; unsigned int hh_len; unsigned int type; @@ -51,14 +51,14 @@ int ip_route_me_harder(struct sk_buff *skb, unsigned addr_type) if (ip_route_output_key(net, &rt, &fl) != 0) return -1; - odst = skb_dst(skb); + orefdst = skb->_skb_refdst; if (ip_route_input(skb, iph->daddr, iph->saddr, RT_TOS(iph->tos), rt->u.dst.dev) != 0) { dst_release(&rt->u.dst); return -1; } dst_release(&rt->u.dst); - dst_release(odst); + refdst_drop(orefdst); } if (skb_dst(skb)->error) diff --git a/net/ipv4/route.c b/net/ipv4/route.c index dea3f926425..705eccfb476 100644 --- a/net/ipv4/route.c +++ b/net/ipv4/route.c @@ -3033,7 +3033,7 @@ int ip_rt_dump(struct sk_buff *skb, struct netlink_callback *cb) continue; if (rt_is_expired(rt)) continue; - skb_dst_set(skb, dst_clone(&rt->u.dst)); + skb_dst_set_noref(skb, &rt->u.dst); if (rt_fill_info(net, skb, NETLINK_CB(cb->skb).pid, cb->nlh->nlmsg_seq, RTM_NEWROUTE, 1, NLM_F_MULTI) <= 0) { diff --git a/net/netfilter/nf_queue.c b/net/netfilter/nf_queue.c index 0b1103c0b1f..78b3cf9c519 100644 --- a/net/netfilter/nf_queue.c +++ b/net/netfilter/nf_queue.c @@ -9,6 +9,7 @@ #include #include #include +#include #include "nf_internals.h" @@ -170,6 +171,7 @@ static int __nf_queue(struct sk_buff *skb, dev_hold(physoutdev); } #endif + skb_dst_force(skb); afinfo->saveroute(skb, entry); status = qh->outfn(entry, queuenum); diff --git a/net/sched/sch_generic.c b/net/sched/sch_generic.c index a969b111bd7..a63029ef3ed 100644 --- a/net/sched/sch_generic.c +++ b/net/sched/sch_generic.c @@ -26,6 +26,7 @@ #include #include #include +#include /* Main transmission queue. */ @@ -40,6 +41,7 @@ static inline int dev_requeue_skb(struct sk_buff *skb, struct Qdisc *q) { + skb_dst_force(skb); q->gso_skb = skb; q->qstats.requeues++; q->q.qlen++; /* it's still part of the queue */ @@ -179,7 +181,7 @@ static inline int qdisc_restart(struct Qdisc *q) skb = dequeue_skb(q); if (unlikely(!skb)) return 0; - + WARN_ON_ONCE(skb_dst_is_noref(skb)); root_lock = qdisc_lock(q); dev = qdisc_dev(q); txq = netdev_get_tx_queue(dev, skb_get_queue_mapping(skb)); -- cgit v1.2.3 From 56e6943b902562e09d3e74126d8d8256b5ea17fb Mon Sep 17 00:00:00 2001 From: Wolfgang Grandegger Date: Mon, 17 May 2010 22:39:48 -0700 Subject: can: sja1000 platform data fixes The member "clock" of struct "sja1000_platform_data" is documented as "CAN bus oscillator frequency in Hz" but it's actually used as the CAN clock frequency, which is half of it. To avoid further confusion, this patch fixes it by renaming the member to "osc_freq". That way, also non mainline users will notice the change. The platform code for the relevant boards is updated accordingly. Furthermore, pre-defined values are now used for the members "ocr" and "cdr". Signed-off-by: Wolfgang Grandegger Acked-by: Marc Kleine-Budde Signed-off-by: David S. Miller --- arch/arm/mach-mx2/pcm970-baseboard.c | 6 +++--- arch/arm/mach-mx3/mach-pcm037.c | 6 +++--- drivers/net/can/sja1000/sja1000_platform.c | 3 ++- include/linux/can/platform/sja1000.h | 2 +- 4 files changed, 9 insertions(+), 8 deletions(-) (limited to 'include/linux') diff --git a/arch/arm/mach-mx2/pcm970-baseboard.c b/arch/arm/mach-mx2/pcm970-baseboard.c index 4aafd5b8b85..f490a406d57 100644 --- a/arch/arm/mach-mx2/pcm970-baseboard.c +++ b/arch/arm/mach-mx2/pcm970-baseboard.c @@ -201,9 +201,9 @@ static struct resource pcm970_sja1000_resources[] = { }; struct sja1000_platform_data pcm970_sja1000_platform_data = { - .clock = 16000000 / 2, - .ocr = 0x40 | 0x18, - .cdr = 0x40, + .osc_freq = 16000000, + .ocr = OCR_TX1_PULLDOWN | OCR_TX0_PUSHPULL, + .cdr = CDR_CBP, }; static struct platform_device pcm970_sja1000 = { diff --git a/arch/arm/mach-mx3/mach-pcm037.c b/arch/arm/mach-mx3/mach-pcm037.c index 2df1ec55a97..78ecd751549 100644 --- a/arch/arm/mach-mx3/mach-pcm037.c +++ b/arch/arm/mach-mx3/mach-pcm037.c @@ -530,9 +530,9 @@ static struct resource pcm970_sja1000_resources[] = { }; struct sja1000_platform_data pcm970_sja1000_platform_data = { - .clock = 16000000 / 2, - .ocr = 0x40 | 0x18, - .cdr = 0x40, + .osc_freq = 16000000, + .ocr = OCR_TX1_PULLDOWN | OCR_TX0_PUSHPULL, + .cdr = CDR_CBP, }; static struct platform_device pcm970_sja1000 = { diff --git a/drivers/net/can/sja1000/sja1000_platform.c b/drivers/net/can/sja1000/sja1000_platform.c index b65cabb361a..d9fadc489b3 100644 --- a/drivers/net/can/sja1000/sja1000_platform.c +++ b/drivers/net/can/sja1000/sja1000_platform.c @@ -111,7 +111,8 @@ static int sp_probe(struct platform_device *pdev) dev->irq = res_irq->start; priv->irq_flags = res_irq->flags & (IRQF_TRIGGER_MASK | IRQF_SHARED); priv->reg_base = addr; - priv->can.clock.freq = pdata->clock; + /* The CAN clock frequency is half the oscillator clock frequency */ + priv->can.clock.freq = pdata->osc_freq / 2; priv->ocr = pdata->ocr; priv->cdr = pdata->cdr; diff --git a/include/linux/can/platform/sja1000.h b/include/linux/can/platform/sja1000.h index 01ee2aeb048..96f8fcc78d7 100644 --- a/include/linux/can/platform/sja1000.h +++ b/include/linux/can/platform/sja1000.h @@ -26,7 +26,7 @@ #define OCR_TX_SHIFT 2 struct sja1000_platform_data { - u32 clock; /* CAN bus oscillator frequency in Hz */ + u32 osc_freq; /* CAN bus oscillator frequency in Hz */ u8 ocr; /* output control register */ u8 cdr; /* clock divider register */ -- cgit v1.2.3 From 57b610805ce92dbd79fc97509f80fa5391b99623 Mon Sep 17 00:00:00 2001 From: Scott Feldman Date: Mon, 17 May 2010 22:49:55 -0700 Subject: net: Add netlink support for virtual port management (was iovnl) Add new netdev ops ndo_{set|get}_vf_port to allow setting of port-profile on a netdev interface. Extends netlink socket RTM_SETLINK/ RTM_GETLINK with two new sub msgs called IFLA_VF_PORTS and IFLA_PORT_SELF (added to end of IFLA_cmd list). These are both nested atrtibutes using this layout: [IFLA_NUM_VF] [IFLA_VF_PORTS] [IFLA_VF_PORT] [IFLA_PORT_*], ... [IFLA_VF_PORT] [IFLA_PORT_*], ... ... [IFLA_PORT_SELF] [IFLA_PORT_*], ... These attributes are design to be set and get symmetrically. VF_PORTS is a list of VF_PORTs, one for each VF, when dealing with an SR-IOV device. PORT_SELF is for the PF of the SR-IOV device, in case it wants to also have a port-profile, or for the case where the VF==PF, like in enic patch 2/2 of this patch set. A port-profile is used to configure/enable the external switch virtual port backing the netdev interface, not to configure the host-facing side of the netdev. A port-profile is an identifier known to the switch. How port- profiles are installed on the switch or how available port-profiles are made know to the host is outside the scope of this patch. There are two types of port-profiles specs in the netlink msg. The first spec is for 802.1Qbg (pre-)standard, VDP protocol. The second spec is for devices that run a similar protocol as VDP but in firmware, thus hiding the protocol details. In either case, the specs have much in common and makes sense to define the netlink msg as the union of the two specs. For example, both specs have a notition of associating/deassociating a port-profile. And both specs require some information from the hypervisor manager, such as client port instance ID. The general flow is the port-profile is applied to a host netdev interface using RTM_SETLINK, the receiver of the RTM_SETLINK msg communicates with the switch, and the switch virtual port backing the host netdev interface is configured/enabled based on the settings defined by the port-profile. What those settings comprise, and how those settings are managed is again outside the scope of this patch, since this patch only deals with the first step in the flow. Signed-off-by: Scott Feldman Signed-off-by: Roopa Prabhu Signed-off-by: David S. Miller --- include/linux/if_link.h | 75 ++++++++++++++++++++ include/linux/netdevice.h | 8 +++ net/core/rtnetlink.c | 169 +++++++++++++++++++++++++++++++++++++++++++++- 3 files changed, 251 insertions(+), 1 deletion(-) (limited to 'include/linux') diff --git a/include/linux/if_link.h b/include/linux/if_link.h index c3af67fce3f..85c812db5a3 100644 --- a/include/linux/if_link.h +++ b/include/linux/if_link.h @@ -113,6 +113,8 @@ enum { IFLA_NUM_VF, /* Number of VFs if device is SR-IOV PF */ IFLA_VFINFO_LIST, IFLA_STATS64, + IFLA_VF_PORTS, + IFLA_PORT_SELF, __IFLA_MAX }; @@ -274,4 +276,77 @@ struct ifla_vf_info { __u32 qos; __u32 tx_rate; }; + +/* VF ports management section + * + * Nested layout of set/get msg is: + * + * [IFLA_NUM_VF] + * [IFLA_VF_PORTS] + * [IFLA_VF_PORT] + * [IFLA_PORT_*], ... + * [IFLA_VF_PORT] + * [IFLA_PORT_*], ... + * ... + * [IFLA_PORT_SELF] + * [IFLA_PORT_*], ... + */ + +enum { + IFLA_VF_PORT_UNSPEC, + IFLA_VF_PORT, /* nest */ + __IFLA_VF_PORT_MAX, +}; + +#define IFLA_VF_PORT_MAX (__IFLA_VF_PORT_MAX - 1) + +enum { + IFLA_PORT_UNSPEC, + IFLA_PORT_VF, /* __u32 */ + IFLA_PORT_PROFILE, /* string */ + IFLA_PORT_VSI_TYPE, /* 802.1Qbg (pre-)standard VDP */ + IFLA_PORT_INSTANCE_UUID, /* binary UUID */ + IFLA_PORT_HOST_UUID, /* binary UUID */ + IFLA_PORT_REQUEST, /* __u8 */ + IFLA_PORT_RESPONSE, /* __u16, output only */ + __IFLA_PORT_MAX, +}; + +#define IFLA_PORT_MAX (__IFLA_PORT_MAX - 1) + +#define PORT_PROFILE_MAX 40 +#define PORT_UUID_MAX 16 +#define PORT_SELF_VF -1 + +enum { + PORT_REQUEST_PREASSOCIATE = 0, + PORT_REQUEST_PREASSOCIATE_RR, + PORT_REQUEST_ASSOCIATE, + PORT_REQUEST_DISASSOCIATE, +}; + +enum { + PORT_VDP_RESPONSE_SUCCESS = 0, + PORT_VDP_RESPONSE_INVALID_FORMAT, + PORT_VDP_RESPONSE_INSUFFICIENT_RESOURCES, + PORT_VDP_RESPONSE_UNUSED_VTID, + PORT_VDP_RESPONSE_VTID_VIOLATION, + PORT_VDP_RESPONSE_VTID_VERSION_VIOALTION, + PORT_VDP_RESPONSE_OUT_OF_SYNC, + /* 0x08-0xFF reserved for future VDP use */ + PORT_PROFILE_RESPONSE_SUCCESS = 0x100, + PORT_PROFILE_RESPONSE_INPROGRESS, + PORT_PROFILE_RESPONSE_INVALID, + PORT_PROFILE_RESPONSE_BADSTATE, + PORT_PROFILE_RESPONSE_INSUFFICIENT_RESOURCES, + PORT_PROFILE_RESPONSE_ERROR, +}; + +struct ifla_port_vsi { + __u8 vsi_mgr_id; + __u8 vsi_type_id[3]; + __u8 vsi_type_version; + __u8 pad[3]; +}; + #endif /* _LINUX_IF_LINK_H */ diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index c1b2341897c..c3487a6bdf9 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -686,6 +686,9 @@ struct netdev_rx_queue { * int (*ndo_set_vf_tx_rate)(struct net_device *dev, int vf, int rate); * int (*ndo_get_vf_config)(struct net_device *dev, * int vf, struct ifla_vf_info *ivf); + * int (*ndo_set_vf_port)(struct net_device *dev, int vf, + * struct nlattr *port[]); + * int (*ndo_get_vf_port)(struct net_device *dev, int vf, struct sk_buff *skb); */ #define HAVE_NET_DEVICE_OPS struct net_device_ops { @@ -735,6 +738,11 @@ struct net_device_ops { int (*ndo_get_vf_config)(struct net_device *dev, int vf, struct ifla_vf_info *ivf); + int (*ndo_set_vf_port)(struct net_device *dev, + int vf, + struct nlattr *port[]); + int (*ndo_get_vf_port)(struct net_device *dev, + int vf, struct sk_buff *skb); #if defined(CONFIG_FCOE) || defined(CONFIG_FCOE_MODULE) int (*ndo_fcoe_enable)(struct net_device *dev); int (*ndo_fcoe_disable)(struct net_device *dev); diff --git a/net/core/rtnetlink.c b/net/core/rtnetlink.c index 66db1201da9..e4b9870e470 100644 --- a/net/core/rtnetlink.c +++ b/net/core/rtnetlink.c @@ -660,6 +660,31 @@ static inline int rtnl_vfinfo_size(const struct net_device *dev) return 0; } +static size_t rtnl_port_size(const struct net_device *dev) +{ + size_t port_size = nla_total_size(4) /* PORT_VF */ + + nla_total_size(PORT_PROFILE_MAX) /* PORT_PROFILE */ + + nla_total_size(sizeof(struct ifla_port_vsi)) + /* PORT_VSI_TYPE */ + + nla_total_size(PORT_UUID_MAX) /* PORT_INSTANCE_UUID */ + + nla_total_size(PORT_UUID_MAX) /* PORT_HOST_UUID */ + + nla_total_size(1) /* PROT_VDP_REQUEST */ + + nla_total_size(2); /* PORT_VDP_RESPONSE */ + size_t vf_ports_size = nla_total_size(sizeof(struct nlattr)); + size_t vf_port_size = nla_total_size(sizeof(struct nlattr)) + + port_size; + size_t port_self_size = nla_total_size(sizeof(struct nlattr)) + + port_size; + + if (!dev->netdev_ops->ndo_get_vf_port || !dev->dev.parent) + return 0; + if (dev_num_vf(dev->dev.parent)) + return port_self_size + vf_ports_size + + vf_port_size * dev_num_vf(dev->dev.parent); + else + return port_self_size; +} + static inline size_t if_nlmsg_size(const struct net_device *dev) { return NLMSG_ALIGN(sizeof(struct ifinfomsg)) @@ -680,9 +705,82 @@ static inline size_t if_nlmsg_size(const struct net_device *dev) + nla_total_size(1) /* IFLA_LINKMODE */ + nla_total_size(4) /* IFLA_NUM_VF */ + rtnl_vfinfo_size(dev) /* IFLA_VFINFO_LIST */ + + rtnl_port_size(dev) /* IFLA_VF_PORTS + IFLA_PORT_SELF */ + rtnl_link_get_size(dev); /* IFLA_LINKINFO */ } +static int rtnl_vf_ports_fill(struct sk_buff *skb, struct net_device *dev) +{ + struct nlattr *vf_ports; + struct nlattr *vf_port; + int vf; + int err; + + vf_ports = nla_nest_start(skb, IFLA_VF_PORTS); + if (!vf_ports) + return -EMSGSIZE; + + for (vf = 0; vf < dev_num_vf(dev->dev.parent); vf++) { + vf_port = nla_nest_start(skb, IFLA_VF_PORT); + if (!vf_port) { + nla_nest_cancel(skb, vf_ports); + return -EMSGSIZE; + } + NLA_PUT_U32(skb, IFLA_PORT_VF, vf); + err = dev->netdev_ops->ndo_get_vf_port(dev, vf, skb); + if (err) { +nla_put_failure: + nla_nest_cancel(skb, vf_port); + continue; + } + nla_nest_end(skb, vf_port); + } + + nla_nest_end(skb, vf_ports); + + return 0; +} + +static int rtnl_port_self_fill(struct sk_buff *skb, struct net_device *dev) +{ + struct nlattr *port_self; + int err; + + port_self = nla_nest_start(skb, IFLA_PORT_SELF); + if (!port_self) + return -EMSGSIZE; + + err = dev->netdev_ops->ndo_get_vf_port(dev, PORT_SELF_VF, skb); + if (err) { + nla_nest_cancel(skb, port_self); + return err; + } + + nla_nest_end(skb, port_self); + + return 0; +} + +static int rtnl_port_fill(struct sk_buff *skb, struct net_device *dev) +{ + int err; + + if (!dev->netdev_ops->ndo_get_vf_port || !dev->dev.parent) + return 0; + + err = rtnl_port_self_fill(skb, dev); + if (err) + return err; + + if (dev_num_vf(dev->dev.parent)) { + err = rtnl_vf_ports_fill(skb, dev); + if (err) + return err; + } + + return 0; +} + static int rtnl_fill_ifinfo(struct sk_buff *skb, struct net_device *dev, int type, u32 pid, u32 seq, u32 change, unsigned int flags) @@ -754,13 +852,15 @@ static int rtnl_fill_ifinfo(struct sk_buff *skb, struct net_device *dev, goto nla_put_failure; copy_rtnl_link_stats64(nla_data(attr), stats); + if (dev->dev.parent) + NLA_PUT_U32(skb, IFLA_NUM_VF, dev_num_vf(dev->dev.parent)); + if (dev->netdev_ops->ndo_get_vf_config && dev->dev.parent) { int i; struct nlattr *vfinfo, *vf; int num_vfs = dev_num_vf(dev->dev.parent); - NLA_PUT_U32(skb, IFLA_NUM_VF, num_vfs); vfinfo = nla_nest_start(skb, IFLA_VFINFO_LIST); if (!vfinfo) goto nla_put_failure; @@ -788,6 +888,10 @@ static int rtnl_fill_ifinfo(struct sk_buff *skb, struct net_device *dev, } nla_nest_end(skb, vfinfo); } + + if (rtnl_port_fill(skb, dev)) + goto nla_put_failure; + if (dev->rtnl_link_ops) { if (rtnl_link_fill(skb, dev) < 0) goto nla_put_failure; @@ -849,6 +953,8 @@ const struct nla_policy ifla_policy[IFLA_MAX+1] = { [IFLA_NET_NS_PID] = { .type = NLA_U32 }, [IFLA_IFALIAS] = { .type = NLA_STRING, .len = IFALIASZ-1 }, [IFLA_VFINFO_LIST] = {. type = NLA_NESTED }, + [IFLA_VF_PORTS] = { .type = NLA_NESTED }, + [IFLA_PORT_SELF] = { .type = NLA_NESTED }, }; EXPORT_SYMBOL(ifla_policy); @@ -870,6 +976,20 @@ static const struct nla_policy ifla_vf_policy[IFLA_VF_MAX+1] = { .len = sizeof(struct ifla_vf_tx_rate) }, }; +static const struct nla_policy ifla_port_policy[IFLA_PORT_MAX+1] = { + [IFLA_PORT_VF] = { .type = NLA_U32 }, + [IFLA_PORT_PROFILE] = { .type = NLA_STRING, + .len = PORT_PROFILE_MAX }, + [IFLA_PORT_VSI_TYPE] = { .type = NLA_BINARY, + .len = sizeof(struct ifla_port_vsi)}, + [IFLA_PORT_INSTANCE_UUID] = { .type = NLA_BINARY, + .len = PORT_UUID_MAX }, + [IFLA_PORT_HOST_UUID] = { .type = NLA_STRING, + .len = PORT_UUID_MAX }, + [IFLA_PORT_REQUEST] = { .type = NLA_U8, }, + [IFLA_PORT_RESPONSE] = { .type = NLA_U16, }, +}; + struct net *rtnl_link_get_net(struct net *src_net, struct nlattr *tb[]) { struct net *net; @@ -1089,6 +1209,53 @@ static int do_setlink(struct net_device *dev, struct ifinfomsg *ifm, } err = 0; + if (tb[IFLA_VF_PORTS]) { + struct nlattr *port[IFLA_PORT_MAX+1]; + struct nlattr *attr; + int vf; + int rem; + + err = -EOPNOTSUPP; + if (!ops->ndo_set_vf_port) + goto errout; + + nla_for_each_nested(attr, tb[IFLA_VF_PORTS], rem) { + if (nla_type(attr) != IFLA_VF_PORT) + continue; + err = nla_parse_nested(port, IFLA_PORT_MAX, + attr, ifla_port_policy); + if (err < 0) + goto errout; + if (!port[IFLA_PORT_VF]) { + err = -EOPNOTSUPP; + goto errout; + } + vf = nla_get_u32(port[IFLA_PORT_VF]); + err = ops->ndo_set_vf_port(dev, vf, port); + if (err < 0) + goto errout; + modified = 1; + } + } + err = 0; + + if (tb[IFLA_PORT_SELF]) { + struct nlattr *port[IFLA_PORT_MAX+1]; + + err = nla_parse_nested(port, IFLA_PORT_MAX, + tb[IFLA_PORT_SELF], ifla_port_policy); + if (err < 0) + goto errout; + + err = -EOPNOTSUPP; + if (ops->ndo_set_vf_port) + err = ops->ndo_set_vf_port(dev, PORT_SELF_VF, port); + if (err < 0) + goto errout; + modified = 1; + } + err = 0; + errout: if (err < 0 && modified && net_ratelimit()) printk(KERN_WARNING "A link change request failed with " -- cgit v1.2.3